diff --git a/qse/include/qse/awk/awk.h b/qse/include/qse/awk/awk.h index b972fb13..a91e3a25 100644 --- a/qse/include/qse/awk/awk.h +++ b/qse/include/qse/awk/awk.h @@ -1,5 +1,5 @@ /* - * $Id: awk.h 213 2009-06-26 13:05:19Z hyunghwan.chung $ + * $Id: awk.h 214 2009-06-27 02:50:54Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. @@ -609,6 +609,7 @@ enum qse_awk_errnum_t QSE_AWK_EDUPLCL, /* duplicate local variable name */ QSE_AWK_EBADPAR, /* not a valid parameter name */ QSE_AWK_EBADVAR, /* not a valid variable name */ + QSE_AWK_EVARMS, /* variable name missing */ QSE_AWK_EUNDEF, /* undefined identifier */ QSE_AWK_ELVALUE, /* l-value required */ QSE_AWK_EGBLTM, /* too many global variables */ diff --git a/qse/lib/awk/err.c b/qse/lib/awk/err.c index 2bbbfdb6..ac8db27b 100644 --- a/qse/lib/awk/err.c +++ b/qse/lib/awk/err.c @@ -1,5 +1,5 @@ /* - * $Id: err.c 213 2009-06-26 13:05:19Z hyunghwan.chung $ + * $Id: err.c 214 2009-06-27 02:50:54Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. @@ -99,6 +99,7 @@ const qse_char_t* qse_awk_dflerrstr (qse_awk_t* awk, qse_awk_errnum_t errnum) QSE_T("duplicate local variable '${0}'"), QSE_T("'${0}' not a valid parameter name"), QSE_T("'${0}' not a valid variable name"), + QSE_T("variable name missing"), QSE_T("undefined identifier '${0}'"), QSE_T("l-value required"), QSE_T("too many global variables"), diff --git a/qse/lib/awk/parse.c b/qse/lib/awk/parse.c index aa232a1d..6a051cc3 100644 --- a/qse/lib/awk/parse.c +++ b/qse/lib/awk/parse.c @@ -1,5 +1,5 @@ /* - * $Id: parse.c 213 2009-06-26 13:05:19Z hyunghwan.chung $ + * $Id: parse.c 214 2009-06-27 02:50:54Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. @@ -387,10 +387,14 @@ static global_t gtab[] = qse_awk_seterror ((awk), (code), (line), &errarg); \ } while (0) +#define MATCH_TERMINATOR_NORMAL(awk) \ + (MATCH((awk),TOKEN_SEMICOLON) || MATCH((awk),TOKEN_NEWLINE)) + +#define MATCH_TERMINATOR_RBRACE(awk) \ + ((awk->option & QSE_AWK_NEWLINE) && MATCH((awk),TOKEN_RBRACE)) + #define MATCH_TERMINATOR(awk) \ - (MATCH((awk),TOKEN_SEMICOLON) || \ - MATCH((awk),TOKEN_NEWLINE) || \ - ((awk->option & QSE_AWK_NEWLINE) && MATCH((awk),TOKEN_RBRACE))) + (MATCH_TERMINATOR_NORMAL(awk) || MATCH_TERMINATOR_RBRACE(awk)) qse_size_t qse_awk_getmaxdepth (qse_awk_t* awk, int type) { @@ -592,7 +596,7 @@ exit_parse: static qse_awk_t* parse_progunit (qse_awk_t* awk) { /* - gbl xxx, xxxx; + global xxx, xxxx; BEGIN { action } END { action } pattern { action } @@ -1204,6 +1208,12 @@ static qse_awk_nde_t* parse_block ( { while (1) { + /* skip new lines before local declaration in a block*/ + while (MATCH(awk,TOKEN_NEWLINE)) + { + if (get_token(awk) <= -1) return QSE_NULL; + } + if (!MATCH(awk,TOKEN_LOCAL)) break; if (get_token(awk) <= -1) @@ -1624,6 +1634,14 @@ int qse_awk_delgbl ( static qse_awk_t* collect_globals (qse_awk_t* awk) { + if (MATCH(awk,TOKEN_NEWLINE)) + { + /* special check if the first name is on the + * same line when QSE_AWK_NEWLINE is on */ + SETERR (awk, QSE_AWK_EVARMS); + return QSE_NULL; + } + while (1) { if (!MATCH(awk,TOKEN_IDENT)) @@ -1640,7 +1658,19 @@ static qse_awk_t* collect_globals (qse_awk_t* awk) if (get_token(awk) <= -1) return QSE_NULL; - if (MATCH_TERMINATOR(awk)) break; + if (MATCH_TERMINATOR_NORMAL(awk)) + { + /* skip a terminator (;, ) */ + if (get_token(awk) <= -1) return QSE_NULL; + break; + } + + /* + * unlike collect_locals(), the right brace cannot + * terminate a global declaration as it can never be + * placed within a block. + * so do not perform MATCH_TERMINATOR_RBRACE(awk)) + */ if (!MATCH(awk,TOKEN_COMMA)) { @@ -1648,11 +1678,13 @@ static qse_awk_t* collect_globals (qse_awk_t* awk) return QSE_NULL; } - if (get_token(awk) <= -1) return QSE_NULL; + do + { + if (get_token(awk) <= -1) return QSE_NULL; + } + while (MATCH(awk,TOKEN_NEWLINE)); } - /* skip a semicolon */ - if (get_token(awk) <= -1) return QSE_NULL; return awk; } @@ -1660,11 +1692,19 @@ static qse_awk_t* collect_globals (qse_awk_t* awk) static qse_awk_t* collect_locals ( qse_awk_t* awk, qse_size_t nlcls, qse_bool_t istop) { - qse_xstr_t lcl; - qse_size_t n; + if (MATCH(awk,TOKEN_NEWLINE)) + { + /* special check if the first name is on the + * same line when QSE_AWK_NEWLINE is on */ + SETERR (awk, QSE_AWK_EVARMS); + return QSE_NULL; + } while (1) { + qse_xstr_t lcl; + qse_size_t n; + if (!MATCH(awk,TOKEN_IDENT)) { SETERRTOK (awk, QSE_AWK_EBADVAR); @@ -1741,7 +1781,18 @@ static qse_awk_t* collect_locals ( if (get_token(awk) <= -1) return QSE_NULL; - if (MATCH_TERMINATOR(awk)) break; + if (MATCH_TERMINATOR_NORMAL(awk)) + { + /* skip the terminator (;, ) */ + if (get_token(awk) <= -1) return QSE_NULL; + break; + } + + if (MATCH_TERMINATOR_RBRACE(awk)) + { + /* should not skip } */ + break; + } if (!MATCH(awk,TOKEN_COMMA)) { @@ -1749,12 +1800,13 @@ static qse_awk_t* collect_locals ( return QSE_NULL; } - if (get_token(awk) <= -1) return QSE_NULL; + do + { + if (get_token(awk) <= -1) return QSE_NULL; + } + while (MATCH(awk,TOKEN_NEWLINE)); } - /* skip a semicolon */ - if (get_token(awk) <= -1) return QSE_NULL; - return awk; } @@ -1924,18 +1976,20 @@ static qse_awk_nde_t* parse_statement_nb (qse_awk_t* awk, qse_size_t line) if (nde == QSE_NULL) return QSE_NULL; - /* check if a statement ends with a semicolon */ - if (MATCH_TERMINATOR(awk)) + if (MATCH_TERMINATOR_NORMAL(awk)) { - /* eat up the semicolon or a new line and read in the next token - * when QSE_AWK_NEWLINE is set, a statement may end with }. - * it should not be eaten up here. */ - if (!MATCH(awk,TOKEN_RBRACE) && get_token(awk) <= -1) + /* check if a statement ends with a semicolon or */ + if (get_token(awk) <= -1) { if (nde != QSE_NULL) qse_awk_clrpt (awk, nde); return QSE_NULL; } } + else if (MATCH_TERMINATOR_RBRACE(awk)) + { + /* do not skip the right brace as a statement terminator. + * is there anything to do here? */ + } else { if (nde != QSE_NULL) qse_awk_clrpt (awk, nde); @@ -4240,7 +4294,7 @@ static qse_awk_nde_t* parse_return (qse_awk_t* awk, qse_size_t line) nde->line = line; nde->next = QSE_NULL; - if (MATCH_TERMINATOR(awk)) + if (MATCH_TERMINATOR(awk)) { /* no return value */ val = QSE_NULL; @@ -5992,4 +6046,3 @@ int qse_awk_putsrcstrx ( return 0; } - diff --git a/qse/regress/awk/lang-001.awk b/qse/regress/awk/lang-001.awk new file mode 100644 index 00000000..5b8bb6eb --- /dev/null +++ b/qse/regress/awk/lang-001.awk @@ -0,0 +1,3 @@ +# cannot use function name as a parameter name +function f(f) { print f; } +BEGIN { f("hello"); } diff --git a/qse/regress/awk/lang-002.awk b/qse/regress/awk/lang-002.awk new file mode 100644 index 00000000..55bc7ad9 --- /dev/null +++ b/qse/regress/awk/lang-002.awk @@ -0,0 +1,7 @@ +function f(f) +{ + print f; + f("my hello"); +} + +BEGIN { f(10); } diff --git a/qse/regress/awk/lang-003.awk b/qse/regress/awk/lang-003.awk new file mode 100644 index 00000000..36b4ea59 --- /dev/null +++ b/qse/regress/awk/lang-003.awk @@ -0,0 +1,3 @@ +# should print 50 +function fn(f) { f = 20; } +BEGIN { f = 50; fn(100); print f; } diff --git a/qse/regress/awk/lang-004.awk b/qse/regress/awk/lang-004.awk new file mode 100644 index 00000000..93591f81 --- /dev/null +++ b/qse/regress/awk/lang-004.awk @@ -0,0 +1,3 @@ +# A function and a named variable cannot have the same name. +function a () { } +BEGIN { a = 20; } diff --git a/qse/regress/awk/lang-005.awk b/qse/regress/awk/lang-005.awk new file mode 100644 index 00000000..490fa372 --- /dev/null +++ b/qse/regress/awk/lang-005.awk @@ -0,0 +1,7 @@ +function a (a) { print a; } + +BEGIN { + local a; + a = 20; + a (1000); +} diff --git a/qse/regress/awk/lang-006.awk b/qse/regress/awk/lang-006.awk new file mode 100644 index 00000000..ff102b0c --- /dev/null +++ b/qse/regress/awk/lang-006.awk @@ -0,0 +1,2 @@ +global a; +function a () { } diff --git a/qse/regress/awk/lang-007.awk b/qse/regress/awk/lang-007.awk new file mode 100644 index 00000000..3cbc4953 --- /dev/null +++ b/qse/regress/awk/lang-007.awk @@ -0,0 +1,4 @@ +function fn () { a = 20; return a;} +global a; +BEGIN { a = 30; print fn (); print a; } + diff --git a/qse/regress/awk/lang-008.awk b/qse/regress/awk/lang-008.awk new file mode 100644 index 00000000..efbff863 --- /dev/null +++ b/qse/regress/awk/lang-008.awk @@ -0,0 +1,16 @@ +global x; +BEGIN { + x = 1; + { + local x; + x = 2; + { + local x; + x = 3; + print x; + } + print x; + } + print x; +} + diff --git a/qse/regress/awk/lang-009.awk b/qse/regress/awk/lang-009.awk new file mode 100644 index 00000000..992fdbc2 --- /dev/null +++ b/qse/regress/awk/lang-009.awk @@ -0,0 +1,9 @@ +function a (a) { print a; } + +BEGIN { + local a; + a = 20; +} + +END { a (1000); } + diff --git a/qse/regress/awk/regress.sh b/qse/regress/awk/regress.sh index 2f9e3c2b..ed4e4dc3 100755 --- a/qse/regress/awk/regress.sh +++ b/qse/regress/awk/regress.sh @@ -23,6 +23,7 @@ print_usage() ################### QSEAWK=${QSEAWK:=../../cmd/awk/qseawk} +TMPFILE="${TMPFILE:=./regress.temp}" PROGS=" cou-001.awk/cou.dat// @@ -81,6 +82,16 @@ PROGS=" emp-026.awk/emp.dat// emp-027.awk/emp.dat// + lang-001.awk///--implicit=off --explicit=on --newline=on -o- + lang-002.awk///--implicit=off --explicit=on --newline=on -o- + lang-003.awk///--implicit=off --explicit=on --newline=on -o- + lang-004.awk///--implicit=off --explicit=on --newline=on -o- + lang-005.awk///--implicit=off --explicit=on --newline=on -o- + lang-006.awk///--implicit=off --explicit=on --newline=on -o- + lang-007.awk///--implicit=off --explicit=on --newline=on -o- + lang-008.awk///--implicit=off --explicit=on --newline=on -o- + lang-009.awk/lang-009.awk//--implicit=off --explicit=on --newline=on -o- + quicksort.awk/quicksort.dat// quicksort2.awk/quicksort2.dat// asm.awk/asm.s/asm.dat/ @@ -96,13 +107,24 @@ PROGS=" exit 1; } -for prog in ${PROGS} +echo "${PROGS}" > "${TMPFILE}" + +while read prog do + [ -z "${prog}" ] && continue + script="`echo ${prog} | cut -d/ -f1`" datafile="`echo ${prog} | cut -d/ -f2`" redinfile="`echo ${prog} | cut -d/ -f3`" awkopts="`echo ${prog} | cut -d/ -f4`" + [ -z "${script}" ] && continue + [ -f "${script}" ] || + { + echo_so "${script} not found" + continue + } + if [ -n "${redinfile}" ] then echo_so "${QSEAWK} ${awkopts} -f ${script} ${datafile} < ${redinfile}" @@ -111,6 +133,9 @@ do echo_so "${QSEAWK} ${awkopts} -f ${script} ${datafile}" ${QSEAWK} ${awkopts} -f ${script} ${datafile} fi -done + +done < "${TMPFILE}" + +rm -f "${TMPFILE}" exit 0