From 8ac0963885bbc9367c82a09c70965d43a3336428 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Thu, 25 Oct 2012 14:38:27 +0000 Subject: [PATCH] combined some awk options into QSE_AWK_EXTRAKWS. deleted QSE_AWK_EXTRAOPS and enabled all new operators by default added === and !==. fixed a bug in printing the explicit concatenation operator(%%, %%=) improved @include handling --- qse/cmd/awk/awk.c | 12 +- qse/doc/page/awk.doc | 84 +++++++++- qse/include/qse/awk/awk.h | 40 ++--- qse/lib/awk/err.c | 8 +- qse/lib/awk/fnc.c | 11 +- qse/lib/awk/parse.c | 293 ++++++++++++++++------------------ qse/lib/awk/run.c | 121 ++++++++------ qse/lib/awk/run.h | 11 +- qse/lib/awk/tree.c | 16 +- qse/mod/awk/uci.c | 5 +- qse/regress/awk/regress.sh.in | 4 +- qse/samples/awk/awk04.c | 1 - qse/samples/awk/awk07.cpp | 2 +- qse/samples/awk/awk08.cpp | 3 +- qse/samples/awk/awk09.c | 2 +- qse/samples/awk/awk10.c | 2 - 16 files changed, 342 insertions(+), 273 deletions(-) diff --git a/qse/cmd/awk/awk.c b/qse/cmd/awk/awk.c index cc365bfb..aca61593 100644 --- a/qse/cmd/awk/awk.c +++ b/qse/cmd/awk/awk.c @@ -382,23 +382,19 @@ struct opttab_t { { QSE_T("implicit"), QSE_AWK_IMPLICIT, QSE_T("allow undeclared variables") }, { QSE_T("explicit"), QSE_AWK_EXPLICIT, QSE_T("allow declared variables(local,global)") }, - { QSE_T("extraops"), QSE_AWK_EXTRAOPS, QSE_T("enable extra operators(<<,>>,^^,\\)") }, + { QSE_T("extrakws"), QSE_AWK_EXTRAKWS, QSE_T("enable abort,reset,nextofile,OFILENAME,@include") }, { QSE_T("rio"), QSE_AWK_RIO, QSE_T("enable builtin I/O including getline & print") }, { QSE_T("rwpipe"), QSE_AWK_RWPIPE, QSE_T("allow a dual-directional pipe") }, { QSE_T("newline"), QSE_AWK_NEWLINE, QSE_T("enable a newline to terminate a statement") }, { QSE_T("striprecspc"), QSE_AWK_STRIPRECSPC, QSE_T("strip spaces in splitting a record") }, { QSE_T("stripstrspc"), QSE_AWK_STRIPSTRSPC, QSE_T("strip spaces in string-to-number conversion") }, - { QSE_T("nextofile"), QSE_AWK_NEXTOFILE, QSE_T("enable 'nextofile'") }, - { QSE_T("reset"), QSE_AWK_RESET, QSE_T("enable 'reset'") }, { QSE_T("crlf"), QSE_AWK_CRLF, QSE_T("use CRLF for a newline") }, { QSE_T("maptovar"), QSE_AWK_MAPTOVAR, QSE_T("allow a map to be assigned or returned") }, { QSE_T("pablock"), QSE_AWK_PABLOCK, QSE_T("enable pattern-action loop") }, { QSE_T("rexbound"), QSE_AWK_REXBOUND, QSE_T("enable {n,m} in a regular expression") }, { QSE_T("ncmponstr"), QSE_AWK_NCMPONSTR, QSE_T("perform numeric comparsion on numeric strings") }, { QSE_T("strictnaming"), QSE_AWK_STRICTNAMING, QSE_T("enable the strict naming rule") }, - { QSE_T("include"), QSE_AWK_INCLUDE, QSE_T("enable '@include'") }, { QSE_T("tolerant"), QSE_AWK_TOLERANT, QSE_T("make more fault-tolerant") }, - { QSE_T("abort"), QSE_AWK_ABORT, QSE_T("enable 'abort'") }, { QSE_NULL, 0, QSE_NULL } }; @@ -525,23 +521,19 @@ static int comparg (int argc, qse_char_t* argv[], struct arg_t* arg) { { QSE_T(":implicit"), QSE_T('\0') }, { QSE_T(":explicit"), QSE_T('\0') }, - { QSE_T(":extraops"), QSE_T('\0') }, + { QSE_T(":extrakws"), QSE_T('\0') }, { QSE_T(":rio"), QSE_T('\0') }, { QSE_T(":rwpipe"), QSE_T('\0') }, { QSE_T(":newline"), QSE_T('\0') }, { QSE_T(":striprecspc"), QSE_T('\0') }, { QSE_T(":stripstrspc"), QSE_T('\0') }, - { QSE_T(":nextofile"), QSE_T('\0') }, - { QSE_T(":reset"), QSE_T('\0') }, { QSE_T(":crlf"), QSE_T('\0') }, { QSE_T(":maptovar"), QSE_T('\0') }, { QSE_T(":pablock"), QSE_T('\0') }, { QSE_T(":rexbound"), QSE_T('\0') }, { QSE_T(":ncmponstr"), QSE_T('\0') }, { QSE_T(":strictnaming"), QSE_T('\0') }, - { QSE_T(":include"), QSE_T('\0') }, { QSE_T(":tolerant"), QSE_T('\0') }, - { QSE_T(":abort"), QSE_T('\0') }, { QSE_T(":call"), QSE_T('c') }, { QSE_T(":file"), QSE_T('f') }, diff --git a/qse/doc/page/awk.doc b/qse/doc/page/awk.doc index d11624cf..f13a2b35 100644 --- a/qse/doc/page/awk.doc +++ b/qse/doc/page/awk.doc @@ -3,7 +3,8 @@ @section awk_content CONTENTS - @ref awk_intro "INTRODUCTION" - @ref awk_lang "AWK LANGUAGE" -- @ref awk_ext "AWK LANGUAGE EXTENSIONS" + - @ref awk_litvar "LITERAL AND VARIABLE" + - @ref awk_ext_teq "TEQ OPERATOR" - @ref awk_ext_vardecl "VARIABLE DECLARATION" - @ref awk_ext_include "INCLUDE" - @ref awk_ext_print "EXTENDED PRINT/PRINTF" @@ -255,9 +256,74 @@ AWK has the following statement constructs. - printf - expression -@section awk_ext AWK LANGUAGE EXTENSIONS -Some language extensions are implemented and those can be enabled by setting -the corresponding options. +@subsection awk_litvar LITERAL AND VARIABLE + +Value type +- Scalar +-- String +-- Integer +-- Floating-Pointer number +- Hashed Map +- Regular expression + +Scalar values are immutable while a hashed map value is mutable. +A regular expression value is specially treated. + +A variable is tied to a value when it is assigned with a value. +If the variable is tied to a map value, it can't be assigned again. +You can use 'reset' to untie the variable from the value, and thus +restore the variable to the 'nil' state. + +.... + +@subsection awk_ext_teq TEQ OPERATOR + +The === operator compares two values and evaluates to a non-zero value +if both have the same internal type and the actual values are the same. +so 1 is not equal to 1.0 for the === operator. + +A map comparison for the === operator is a bit special. The contents of +the map is never inspected. Comparing two maps always result in inequality. + +However, if two variables points to the same map value, it can evaluate +to a non-zero value. This is possible if you allow assigning a map to +another non-map variable with #QSE_AWK_MAPTOVAR. In this case, a map +is not deep-copied but the reference to it is copied. + +@code +BEGIN { + a[10]=20; + b=a; + b[20]=40; + for (i in a) print i, a[i]; + print a===b; +} +@endcode + + +The === operator may be also useful when you want to indicate an error +with an uninitialized variable. The following code check if the function +returned a map. Since the variable 'nil' has never been assigned, its +internal type is 'NIL' and + +@code +function a () +{ + x[10] = 2; + return x; +} + +BEGIN { + t = a(); + if (t === nil) + print "nil"; + else + print "ok"; +} +@endcode. + +The !== operator is a negated form of the === operator. + @subsection awk_ext_vardecl VARIABLE DECLARATION @@ -303,6 +369,16 @@ blocks appear. To use \@include, you must turn on #QSE_AWK_INCLUDE. BEGIN { func_in_abc (); } @endcode +A semicolon is optional after the included file name. The following is the +same as the sample above. +@code +@include "abc.awk"; +BEGIN { func_in_abc(); } +@endcode + +If #QSE_AWK_NEWLINE is off, the semicolon is required. + + @subsection awk_ext_print EXTENDED PRINT/PRINTF When #QSE_AWK_TOLERANT is on, print and printf are treated as if they are function calls. In this mode, they return a negative number diff --git a/qse/include/qse/awk/awk.h b/qse/include/qse/awk/awk.h index 46b78ca5..d4f09147 100644 --- a/qse/include/qse/awk/awk.h +++ b/qse/include/qse/awk/awk.h @@ -460,6 +460,7 @@ struct qse_awk_sio_lxc_t }; typedef struct qse_awk_sio_lxc_t qse_awk_sio_lxc_t; +typedef struct qse_awk_sio_arg_t qse_awk_sio_arg_t; struct qse_awk_sio_arg_t { const qse_char_t* name; /**< [IN] name of I/O object */ @@ -477,9 +478,8 @@ struct qse_awk_sio_arg_t qse_size_t colm; qse_awk_sio_lxc_t last; - struct qse_awk_sio_arg_t* next; + qse_awk_sio_arg_t* next; }; -typedef struct qse_awk_sio_arg_t qse_awk_sio_arg_t; /** * The qse_awk_sio_impl_t type defines a source IO function @@ -892,23 +892,18 @@ enum qse_awk_trait_t /** * allows undeclared variables and implicit concatenation **/ - QSE_AWK_IMPLICIT = (1 << 0), + QSE_AWK_IMPLICIT = (1 << 0), /** * allows explicit variable declaration, the concatenation * operator, a period, and performs the parse-time function check. */ - QSE_AWK_EXPLICIT = (1 << 1), + QSE_AWK_EXPLICIT = (1 << 1), - /** - * supports extra operators: - * - @b <<, <<= left-shift - * - @b >>, >>= right-shiftt - * - @b ^^, ^^= xor - * - @b ~ bitwise-not - * - @b // idiv (get quotient) + /** + * enable abort,reset,nextofile,OFILENAME,@include. */ - QSE_AWK_EXTRAOPS = (1 << 2), + QSE_AWK_EXTRAKWS = (1 << 2), /** supports @b getline and @b print */ QSE_AWK_RIO = (1 << 3), @@ -951,12 +946,6 @@ enum qse_awk_trait_t */ QSE_AWK_STRIPSTRSPC = (1 << 7), - /** enables @b nextofile */ - QSE_AWK_NEXTOFILE = (1 << 8), - - /** enables @b reset */ - QSE_AWK_RESET = (1 << 9), - /** CR + LF by default */ QSE_AWK_CRLF = (1 << 10), @@ -1005,9 +994,6 @@ enum qse_awk_trait_t */ QSE_AWK_TOLERANT = (1 << 17), - /** enables @b abort */ - QSE_AWK_ABORT = (1 << 18), - /** * makes #qse_awk_t to behave compatibly with classical AWK * implementations @@ -1112,18 +1098,18 @@ enum qse_awk_errnum_t QSE_AWK_EARGTF, /**< too few arguments */ QSE_AWK_EARGTM, /**< too many arguments */ QSE_AWK_EFUNNF, /**< function '${0}' not found */ - QSE_AWK_ENOTIDX, /**< variable not indexable */ - QSE_AWK_ENOTDEL, /**< variable '${0}' not deletable */ + QSE_AWK_ENOTIDX, /**< not indexable */ + QSE_AWK_ENOTDEL, /**< '${0}' not deletable */ QSE_AWK_ENOTMAP, /**< value not a map */ QSE_AWK_ENOTMAPIN, /**< right-hand side of 'in' not a map */ QSE_AWK_ENOTMAPNILIN, /**< right-hand side of 'in' not a map nor nil */ QSE_AWK_ENOTREF, /**< value not referenceable */ QSE_AWK_ENOTASS, /**< value not assignable */ - QSE_AWK_EIDXVALASSMAP, /**< an indexed value cannot be assigned a map */ - QSE_AWK_EPOSVALASSMAP, /**< a positional cannot be assigned a map */ - QSE_AWK_EMAPTOSCALAR, /**< map '${0}' not assignable with a scalar */ + QSE_AWK_EIDXVALASSMAP, /**< indexed value cannot be assigned a map */ + QSE_AWK_EPOSVALASSMAP, /**< positional cannot be assigned a map */ + QSE_AWK_EMAPNA, /**< map '${0}' not assignable */ + QSE_AWK_EMAPPH, /**< map prohibited */ QSE_AWK_ESCALARTOMAP, /**< cannot change a scalar value to a map */ - QSE_AWK_EMAPNA, /**< map not allowed */ QSE_AWK_EVALTYPE, /**< invalid value type */ QSE_AWK_ERNEXTBEG, /**< 'next' called from BEGIN block */ QSE_AWK_ERNEXTEND, /**< 'next' called from END block */ diff --git a/qse/lib/awk/err.c b/qse/lib/awk/err.c index 2e8cf8c3..19ab6b50 100644 --- a/qse/lib/awk/err.c +++ b/qse/lib/awk/err.c @@ -109,8 +109,8 @@ const qse_char_t* qse_awk_dflerrstr (const qse_awk_t* awk, qse_awk_errnum_t errn QSE_T("too few arguments"), QSE_T("too many arguments"), QSE_T("function '${0}' not found"), - QSE_T("variable not indexable"), - QSE_T("variable '${0}' not deletable"), + QSE_T("not indexable"), + QSE_T("'${0}' not deletable"), QSE_T("value not a map"), QSE_T("right-hand side of the 'in' operator not a map"), QSE_T("right-hand side of the 'in' operator not a map nor nil"), @@ -118,9 +118,9 @@ const qse_char_t* qse_awk_dflerrstr (const qse_awk_t* awk, qse_awk_errnum_t errn QSE_T("value not assignable"), QSE_T("indexed value cannot be assigned a map"), QSE_T("positional value cannot be assigned a map"), - QSE_T("map '${0}' not assignable with a scalar"), + QSE_T("map '${0}' not assignable"), + QSE_T("map prohibited"), QSE_T("cannot change a scalar value to a map"), - QSE_T("map not allowed"), QSE_T("invalid value type"), QSE_T("'next' called from BEGIN block"), QSE_T("'next' called from END block"), diff --git a/qse/lib/awk/fnc.c b/qse/lib/awk/fnc.c index 627a889a..1ce4a0ba 100644 --- a/qse/lib/awk/fnc.c +++ b/qse/lib/awk/fnc.c @@ -652,9 +652,12 @@ static int fnc_split (qse_awk_rtx_t* run, const qse_awk_fnc_info_t* fi) if ((*a1_ref)->type != QSE_AWK_VAL_NIL && (*a1_ref)->type != QSE_AWK_VAL_MAP) { - /* cannot change a scalar value to a map */ - qse_awk_rtx_seterrnum (run, QSE_AWK_ESCALARTOMAP, QSE_NULL); - return -1; + if (!(run->awk->opt.trait & QSE_AWK_MAPTOVAR)) + { + /* cannot change a scalar value to a map */ + qse_awk_rtx_seterrnum (run, QSE_AWK_ESCALARTOMAP, QSE_NULL); + return -1; + } } if (a0->type == QSE_AWK_VAL_STR) @@ -976,7 +979,7 @@ static int __substitute (qse_awk_rtx_t* run, qse_long_t max_count) if ((*a2_ref)->type == QSE_AWK_VAL_MAP) { /* a map is not allowed as the third parameter */ - qse_awk_rtx_seterrnum (run, QSE_AWK_EMAPNA, QSE_NULL); + qse_awk_rtx_seterrnum (run, QSE_AWK_EMAPPH, QSE_NULL); goto oops; } diff --git a/qse/lib/awk/parse.c b/qse/lib/awk/parse.c index d109b7c4..51688c61 100644 --- a/qse/lib/awk/parse.c +++ b/qse/lib/awk/parse.c @@ -28,8 +28,8 @@ enum tok_t /* special token to direct the parser to include a file specified */ TOK_INCLUDE, - /* TOK_XXX_ASSNs should be in sync - * with assop in assign_to_opcode */ + /* TOK_XXX_ASSNs should be in sync with assop in assign_to_opcode. + * it also should be in the order as qse_awk_assop_type_t in run.h */ TOK_ASSN, TOK_PLUS_ASSN, TOK_MINUS_ASSN, @@ -46,12 +46,15 @@ enum tok_t TOK_BOR_ASSN, /* end of TOK_XXX_ASSN */ + TOK_TEQ, + TOK_TNE, TOK_EQ, TOK_NE, TOK_LE, TOK_LT, TOK_GE, TOK_GT, + TOK_MA, /* match */ TOK_NM, /* not match */ TOK_LNOT, /* logical negation ! */ TOK_PLUS, @@ -67,7 +70,7 @@ enum tok_t TOK_BOR, TOK_BXOR, TOK_BAND, - TOK_TILDE, /* used for unary bitwise-not and regex match */ + TOK_BNOT, /* used for unary bitwise-not and regex match */ TOK_RS, TOK_LS, TOK_IN, @@ -259,7 +262,7 @@ static kwent_t kwtab[] = * also keep it sorted by the first field for binary search */ { { QSE_T("BEGIN"), 5 }, TOK_BEGIN, QSE_AWK_PABLOCK }, { { QSE_T("END"), 3 }, TOK_END, QSE_AWK_PABLOCK }, - { { QSE_T("abort"), 5 }, TOK_ABORT, QSE_AWK_ABORT }, + { { QSE_T("abort"), 5 }, TOK_ABORT, QSE_AWK_EXTRAKWS }, { { QSE_T("break"), 5 }, TOK_BREAK, 0 }, { { QSE_T("continue"), 8 }, TOK_CONTINUE, 0 }, { { QSE_T("delete"), 6 }, TOK_DELETE, 0 }, @@ -272,14 +275,14 @@ static kwent_t kwtab[] = { { QSE_T("global"), 6 }, TOK_GLOBAL, QSE_AWK_EXPLICIT }, { { QSE_T("if"), 2 }, TOK_IF, 0 }, { { QSE_T("in"), 2 }, TOK_IN, 0 }, - { { QSE_T("include"), 7 }, TOK_INCLUDE, QSE_AWK_INCLUDE }, + { { QSE_T("include"), 7 }, TOK_INCLUDE, QSE_AWK_EXTRAKWS }, { { QSE_T("local"), 5 }, TOK_LOCAL, QSE_AWK_EXPLICIT }, { { QSE_T("next"), 4 }, TOK_NEXT, QSE_AWK_PABLOCK }, { { QSE_T("nextfile"), 8 }, TOK_NEXTFILE, QSE_AWK_PABLOCK }, - { { QSE_T("nextofile"), 9 }, TOK_NEXTOFILE, QSE_AWK_PABLOCK | QSE_AWK_NEXTOFILE }, + { { QSE_T("nextofile"), 9 }, TOK_NEXTOFILE, QSE_AWK_PABLOCK | QSE_AWK_EXTRAKWS }, { { QSE_T("print"), 5 }, TOK_PRINT, QSE_AWK_RIO }, { { QSE_T("printf"), 6 }, TOK_PRINTF, QSE_AWK_RIO }, - { { QSE_T("reset"), 5 }, TOK_RESET, QSE_AWK_RESET }, + { { QSE_T("reset"), 5 }, TOK_RESET, QSE_AWK_EXTRAKWS }, { { QSE_T("return"), 6 }, TOK_RETURN, 0 }, { { QSE_T("while"), 5 }, TOK_WHILE, 0 } }; @@ -319,7 +322,7 @@ static global_t gtab[] = { QSE_T("NR"), 2, QSE_AWK_PABLOCK }, /* current output file name */ - { QSE_T("OFILENAME"), 9, QSE_AWK_PABLOCK | QSE_AWK_NEXTOFILE }, + { QSE_T("OFILENAME"), 9, QSE_AWK_PABLOCK | QSE_AWK_EXTRAKWS }, /* output real-to-str conversion format for 'print' */ { QSE_T("OFMT"), 4, QSE_AWK_RIO }, @@ -385,12 +388,7 @@ static global_t gtab[] = qse_awk_seterror (awk, QSE_AWK_ENOERR, QSE_NULL, QSE_NULL) #define SETERR_TOK(awk,code) \ - do { \ - qse_cstr_t __ea; \ - __ea.len = QSE_STR_LEN((awk)->tok.name); \ - __ea.ptr = QSE_STR_PTR((awk)->tok.name); \ - qse_awk_seterror (awk, code, &__ea, &(awk)->tok.loc); \ - } while (0) + qse_awk_seterror (awk, code, QSE_STR_CSTR((awk)->tok.name), &(awk)->tok.loc) #define SETERR_COD(awk,code) \ qse_awk_seterror (awk, code, QSE_NULL, QSE_NULL) @@ -673,6 +671,46 @@ int qse_awk_parse (qse_awk_t* awk, qse_awk_sio_t* sio) return n; } +static int end_include (qse_awk_t* awk) +{ + int x; + qse_awk_sio_arg_t* cur; + + if (awk->sio.inp == &awk->sio.arg) return 0; /* no include */ + + /* if it is an included file, close it and + * retry to read a character from an outer file */ + + CLRERR (awk); + x = awk->sio.inf ( + awk, QSE_AWK_SIO_CLOSE, + awk->sio.inp, QSE_NULL, 0); + + /* if closing has failed, still destroy the + * sio structure first as normal and return + * the failure below. this way, the caller + * does not call QSE_AWK_SIO_CLOSE on + * awk->sio.inp again. */ + + cur = awk->sio.inp; + awk->sio.inp = awk->sio.inp->next; + + QSE_ASSERT (cur->name != QSE_NULL); + QSE_MMGR_FREE (awk->mmgr, cur); + awk->parse.depth.incl--; + + if (x != 0) + { + /* the failure mentioned above is returned here */ + if (ISNOERR(awk)) + SETERR_ARG (awk, QSE_AWK_ECLOSE, QSE_T(""), 5); + return -1; + } + + awk->sio.last = awk->sio.inp->last; + return 1; /* ended the included file successfully */ +} + static int begin_include (qse_awk_t* awk) { qse_ssize_t op; @@ -747,50 +785,21 @@ static int begin_include (qse_awk_t* awk) awk->sio.inp->line = 1; awk->sio.inp->colm = 1; - return 0; - -oops: - if (arg != QSE_NULL) QSE_MMGR_FREE (awk->mmgr, arg); - return -1; -} - -static int end_include (qse_awk_t* awk) -{ - int x; - qse_awk_sio_arg_t* cur; - - if (awk->sio.inp == &awk->sio.arg) return 0; /* no include */ - - /* if it is an included file, close it and - * retry to read a character from an outer file */ - - CLRERR (awk); - x = awk->sio.inf ( - awk, QSE_AWK_SIO_CLOSE, - awk->sio.inp, QSE_NULL, 0); - - /* if closing has failed, still destroy the - * sio structure first as normal and return - * the failure below. this way, the caller - * does not call QSE_AWK_SIO_CLOSE on - * awk->sio.inp again. */ - - cur = awk->sio.inp; - awk->sio.inp = awk->sio.inp->next; - - QSE_ASSERT (cur->name != QSE_NULL); - QSE_MMGR_FREE (awk->mmgr, cur); - awk->parse.depth.incl--; - - if (x != 0) + /* read in the first character in the included file. + * so the next call to get_token() sees the character read + * from this file. */ + if (get_char (awk) <= -1) { - /* the failure mentioned above is returned here */ - if (ISNOERR(awk)) - SETERR_ARG (awk, QSE_AWK_ECLOSE, QSE_T(""), 5); + end_include (awk); + /* since i've called end_include(), i don't go to oops */ return -1; } - return 1; /* ended the included file successfully */ + return 0; + +oops: + if (arg) QSE_MMGR_FREE (awk->mmgr, arg); + return -1; } static qse_awk_t* parse_progunit (qse_awk_t* awk) @@ -849,9 +858,10 @@ retry: } if (begin_include (awk) <= -1) return QSE_NULL; - - /* read the first meaningful token from the included file - * and recheck it by jumping to retry: */ + + /* i'm retrying to get the first top-level + * element as if parse_progunit() is called. + * so i need to skip NEWLINE tokens */ do { if (get_token(awk) <= -1) return QSE_NULL; @@ -898,6 +908,10 @@ retry: awk->parse.id.block = PARSE_BEGIN_BLOCK; if (parse_begin (awk) == QSE_NULL) return QSE_NULL; + + /* skip a semicolon after an action block if any */ + if (MATCH(awk,TOK_SEMICOLON) && + get_token (awk) <= -1) return QSE_NULL; } else if (MATCH(awk,TOK_END)) { @@ -926,6 +940,10 @@ retry: awk->parse.id.block = PARSE_END_BLOCK; if (parse_end (awk) == QSE_NULL) return QSE_NULL; + + /* skip a semicolon after an action block if any */ + if (MATCH(awk,TOK_SEMICOLON) && + get_token (awk) <= -1) return QSE_NULL; } else if (MATCH(awk,TOK_LBRACE)) { @@ -3629,8 +3647,7 @@ oops: return QSE_NULL; } -static qse_awk_nde_t* parse_logical_or ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_logical_or (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3641,8 +3658,7 @@ static qse_awk_nde_t* parse_logical_or ( return parse_binary (awk, xloc, 1, map, parse_logical_and); } -static qse_awk_nde_t* parse_logical_and ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_logical_and (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3653,8 +3669,7 @@ static qse_awk_nde_t* parse_logical_and ( return parse_binary (awk, xloc, 1, map, parse_in); } -static qse_awk_nde_t* parse_in ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_in (qse_awk_t* awk, const qse_awk_loc_t* xloc) { /* static binmap_t map[] = @@ -3708,21 +3723,19 @@ oops: return QSE_NULL; } -static qse_awk_nde_t* parse_regex_match ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_regex_match (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { - { TOK_TILDE, QSE_AWK_BINOP_MA }, - { TOK_NM, QSE_AWK_BINOP_NM }, - { TOK_EOF, 0 }, + { TOK_MA, QSE_AWK_BINOP_MA }, + { TOK_NM, QSE_AWK_BINOP_NM }, + { TOK_EOF, 0 }, }; return parse_binary (awk, xloc, 0, map, parse_bitwise_or); } -static qse_awk_nde_t* parse_bitwise_or ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_bitwise_or (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3733,8 +3746,7 @@ static qse_awk_nde_t* parse_bitwise_or ( return parse_binary (awk, xloc, 0, map, parse_bitwise_xor); } -static qse_awk_nde_t* parse_bitwise_xor ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_bitwise_xor (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3745,8 +3757,7 @@ static qse_awk_nde_t* parse_bitwise_xor ( return parse_binary (awk, xloc, 0, map, parse_bitwise_and); } -static qse_awk_nde_t* parse_bitwise_and ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_bitwise_and (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3757,11 +3768,12 @@ static qse_awk_nde_t* parse_bitwise_and ( return parse_binary (awk, xloc, 0, map, parse_equality); } -static qse_awk_nde_t* parse_equality ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_equality (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { + { TOK_TEQ, QSE_AWK_BINOP_TEQ }, + { TOK_TNE, QSE_AWK_BINOP_TNE }, { TOK_EQ, QSE_AWK_BINOP_EQ }, { TOK_NE, QSE_AWK_BINOP_NE }, { TOK_EOF, 0 } @@ -3770,8 +3782,7 @@ static qse_awk_nde_t* parse_equality ( return parse_binary (awk, xloc, 0, map, parse_relational); } -static qse_awk_nde_t* parse_relational ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_relational (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3782,12 +3793,10 @@ static qse_awk_nde_t* parse_relational ( { TOK_EOF, 0 } }; - return parse_binary (awk, xloc, 0, map, - ((awk->opt.trait & QSE_AWK_EXTRAOPS)? parse_shift: parse_concat)); + return parse_binary (awk, xloc, 0, map, parse_shift); } -static qse_awk_nde_t* parse_shift ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_shift (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3799,8 +3808,7 @@ static qse_awk_nde_t* parse_shift ( return parse_binary (awk, xloc, 0, map, parse_concat); } -static qse_awk_nde_t* parse_concat ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_concat (qse_awk_t* awk, const qse_awk_loc_t* xloc) { qse_awk_nde_t* left = QSE_NULL; qse_awk_nde_t* right = QSE_NULL; @@ -3853,8 +3861,7 @@ oops: return QSE_NULL; } -static qse_awk_nde_t* parse_additive ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_additive (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3866,8 +3873,7 @@ static qse_awk_nde_t* parse_additive ( return parse_binary (awk, xloc, 0, map, parse_multiplicative); } -static qse_awk_nde_t* parse_multiplicative ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_multiplicative (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -3882,8 +3888,7 @@ static qse_awk_nde_t* parse_multiplicative ( return parse_binary (awk, xloc, 0, map, parse_unary); } -static qse_awk_nde_t* parse_unary ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_unary (qse_awk_t* awk, const qse_awk_loc_t* xloc) { qse_awk_nde_t* left; qse_awk_loc_t uloc; @@ -3894,8 +3899,7 @@ static qse_awk_nde_t* parse_unary ( opcode = (MATCH(awk,TOK_PLUS))? QSE_AWK_UNROP_PLUS: (MATCH(awk,TOK_MINUS))? QSE_AWK_UNROP_MINUS: (MATCH(awk,TOK_LNOT))? QSE_AWK_UNROP_LNOT: - ((awk->opt.trait & QSE_AWK_EXTRAOPS) && MATCH(awk,TOK_TILDE))? - QSE_AWK_UNROP_BNOT: -1; + (MATCH(awk,TOK_BNOT))? QSE_AWK_UNROP_BNOT: -1; /*if (opcode <= -1) return parse_increment (awk);*/ if (opcode <= -1) return parse_exponent (awk, xloc); @@ -4023,8 +4027,7 @@ static qse_awk_nde_t* parse_unary ( } } -static qse_awk_nde_t* parse_exponent ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_exponent (qse_awk_t* awk, const qse_awk_loc_t* xloc) { static binmap_t map[] = { @@ -4035,8 +4038,7 @@ static qse_awk_nde_t* parse_exponent ( return parse_binary (awk, xloc, 0, map, parse_unary_exp); } -static qse_awk_nde_t* parse_unary_exp ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_unary_exp (qse_awk_t* awk, const qse_awk_loc_t* xloc) { qse_awk_nde_exp_t* nde; qse_awk_nde_t* left; @@ -4046,8 +4048,7 @@ static qse_awk_nde_t* parse_unary_exp ( opcode = (MATCH(awk,TOK_PLUS))? QSE_AWK_UNROP_PLUS: (MATCH(awk,TOK_MINUS))? QSE_AWK_UNROP_MINUS: (MATCH(awk,TOK_LNOT))? QSE_AWK_UNROP_LNOT: - ((awk->opt.trait & QSE_AWK_EXTRAOPS) && MATCH(awk,TOK_TILDE))? - QSE_AWK_UNROP_BNOT: -1; + (MATCH(awk,TOK_BNOT))? QSE_AWK_UNROP_BNOT: -1; if (opcode <= -1) return parse_increment (awk, xloc); @@ -4085,8 +4086,7 @@ static qse_awk_nde_t* parse_unary_exp ( return (qse_awk_nde_t*)nde; } -static qse_awk_nde_t* parse_increment ( - qse_awk_t* awk, const qse_awk_loc_t* xloc) +static qse_awk_nde_t* parse_increment (qse_awk_t* awk, const qse_awk_loc_t* xloc) { qse_awk_nde_exp_t* nde; qse_awk_nde_t* left; @@ -5805,16 +5805,18 @@ static int get_symbols (qse_awk_t* awk, qse_cint_t c, qse_awk_tok_t* tok) static struct ops_t ops[] = { + { QSE_T("==="), 3, TOK_TEQ, 0 }, { QSE_T("=="), 2, TOK_EQ, 0 }, { QSE_T("="), 1, TOK_ASSN, 0 }, + { QSE_T("!=="), 3, TOK_TNE, 0 }, { QSE_T("!="), 2, TOK_NE, 0 }, { QSE_T("!~"), 2, TOK_NM, 0 }, { QSE_T("!"), 1, TOK_LNOT, 0 }, - { QSE_T(">>="), 3, TOK_RS_ASSN, QSE_AWK_EXTRAOPS }, + { QSE_T(">>="), 3, TOK_RS_ASSN, 0 }, { QSE_T(">>"), 2, TOK_RS, 0 }, { QSE_T(">="), 2, TOK_GE, 0 }, { QSE_T(">"), 1, TOK_GT, 0 }, - { QSE_T("<<="), 3, TOK_LS_ASSN, QSE_AWK_EXTRAOPS }, + { QSE_T("<<="), 3, TOK_LS_ASSN, 0 }, { QSE_T("<<"), 2, TOK_LS, 0 }, { QSE_T("<="), 2, TOK_LE, 0 }, { QSE_T("<"), 1, TOK_LT, 0 }, @@ -5824,8 +5826,8 @@ static int get_symbols (qse_awk_t* awk, qse_cint_t c, qse_awk_tok_t* tok) { QSE_T("&&"), 2, TOK_LAND, 0 }, { QSE_T("&="), 2, TOK_BAND_ASSN, 0 }, { QSE_T("&"), 1, TOK_BAND, 0 }, - { QSE_T("^^="), 3, TOK_BXOR_ASSN, QSE_AWK_EXTRAOPS }, - { QSE_T("^^"), 2, TOK_BXOR, QSE_AWK_EXTRAOPS }, + { QSE_T("^^="), 3, TOK_BXOR_ASSN, 0 }, + { QSE_T("^^"), 2, TOK_BXOR, 0 }, { QSE_T("^="), 2, TOK_EXP_ASSN, 0 }, { QSE_T("^"), 1, TOK_EXP, 0 }, { QSE_T("++"), 2, TOK_PLUSPLUS, 0 }, @@ -5834,19 +5836,20 @@ static int get_symbols (qse_awk_t* awk, qse_cint_t c, qse_awk_tok_t* tok) { QSE_T("--"), 2, TOK_MINUSMINUS, 0 }, { QSE_T("-="), 2, TOK_MINUS_ASSN, 0 }, { QSE_T("-"), 1, TOK_MINUS, 0 }, - { QSE_T("**="), 3, TOK_EXP_ASSN, QSE_AWK_EXTRAOPS }, - { QSE_T("**"), 2, TOK_EXP, QSE_AWK_EXTRAOPS }, + { QSE_T("**="), 3, TOK_EXP_ASSN, 0 }, + { QSE_T("**"), 2, TOK_EXP, 0 }, { QSE_T("*="), 2, TOK_MUL_ASSN, 0 }, { QSE_T("*"), 1, TOK_MUL, 0 }, { QSE_T("/="), 2, TOK_DIV_ASSN, 0 }, { QSE_T("/"), 1, TOK_DIV, 0 }, - { QSE_T("\\="), 2, TOK_IDIV_ASSN, QSE_AWK_EXTRAOPS }, - { QSE_T("\\"), 1, TOK_IDIV, QSE_AWK_EXTRAOPS }, - { QSE_T("%%="), 3, TOK_CONCAT_ASSN, QSE_AWK_EXPLICIT }, - { QSE_T("%%"), 2, TOK_CONCAT, QSE_AWK_EXPLICIT }, + { QSE_T("\\="), 2, TOK_IDIV_ASSN, 0 }, + { QSE_T("\\"), 1, TOK_IDIV, 0 }, + { QSE_T("%%="), 3, TOK_CONCAT_ASSN, 0 }, + { QSE_T("%%"), 2, TOK_CONCAT, 0 }, { QSE_T("%="), 2, TOK_MOD_ASSN, 0 }, { QSE_T("%"), 1, TOK_MOD, 0 }, - { QSE_T("~"), 1, TOK_TILDE, 0 }, + { QSE_T("~~"), 2, TOK_BNOT, 0 }, + { QSE_T("~"), 1, TOK_MA, 0 }, { QSE_T("("), 1, TOK_LPAREN, 0 }, { QSE_T(")"), 1, TOK_RPAREN, 0 }, { QSE_T("{"), 1, TOK_LBRACE, 0 }, @@ -5898,6 +5901,7 @@ static int get_token_into (qse_awk_t* awk, qse_awk_tok_t* tok) { qse_cint_t c; int n; + int skip_semicolon_after_include = 0; retry: do @@ -5920,7 +5924,9 @@ retry: if (n <= -1) return -1; if (n >= 1) { - awk->sio.last = awk->sio.inp->last; + /*awk->sio.last = awk->sio.inp->last;*/ + /* mark that i'm retrying after end of an included file */ + skip_semicolon_after_include = 1; goto retry; } @@ -5977,50 +5983,6 @@ retry: QSE_STR_PTR(tok->name), QSE_STR_LEN(tok->name)); SET_TOKEN_TYPE (awk, tok, type); - -#if 0 - if (type == TOK_IDENT) - { - qse_awk_sio_lxc_t lc; - - while (c == QSE_T(':')); - { - lc = awk->sio.last; - GET_CHAR_TO (awk, c); - if (c == QSE_T(':')) - { - GET_CHAR_TO (awk, c); - if (c == QSE_T('_') || QSE_AWK_ISALPHA (awk, c)) - { - do - { - ADD_TOKEN_CHAR (awk, tok, c); - GET_CHAR_TO (awk, c); - } - while (c == QSE_T('_') || - QSE_AWK_ISALPHA (awk, c) || - QSE_AWK_ISDIGIT (awk, c)); - - /* this set_token_type may get executed - * more than necessary if there are many - * segments. but never mind */ - SET_TOKEN_TYPE (awk, tok, TOK_SEGIDENT); - } - else - { - /* TODO: return an error for the - * incomplete segmented identifier */ - } - } - else - { - unget_char (awk, &awk->sio.last); - awk->sio.last = lc; - break; - } - } - } -#endif } else if (c == QSE_T('\"')) { @@ -6044,11 +6006,26 @@ retry: else { qse_char_t cc = (qse_char_t)c; - SETERR_ARG_LOC ( - awk, QSE_AWK_ELXCHR, &cc, 1, &tok->loc); + SETERR_ARG_LOC (awk, QSE_AWK_ELXCHR, &cc, 1, &tok->loc); } return -1; } + + if (skip_semicolon_after_include && (tok->type == TOK_SEMICOLON || tok->type == TOK_NEWLINE)) + { + /* this handles the optional semicolon after the + * included file named as in @include "file-name"; */ + skip_semicolon_after_include = 0; + goto retry; + } + } + + if (skip_semicolon_after_include && !(awk->opt.trait & QSE_AWK_NEWLINE)) + { + /* semiclon has not been skipped yet and the + * newline option is not set. */ + qse_awk_seterror (awk, QSE_AWK_ESCOLON, QSE_STR_CSTR(tok->name), &tok->loc); + return -1; } return 0; diff --git a/qse/lib/awk/run.c b/qse/lib/awk/run.c index 9ffd728e..dac7ca71 100644 --- a/qse/lib/awk/run.c +++ b/qse/lib/awk/run.c @@ -161,6 +161,11 @@ static qse_awk_val_t* eval_binop_bxor ( qse_awk_rtx_t* run, qse_awk_val_t* left, qse_awk_val_t* right); static qse_awk_val_t* eval_binop_band ( qse_awk_rtx_t* run, qse_awk_val_t* left, qse_awk_val_t* right); + +static qse_awk_val_t* eval_binop_teq ( + qse_awk_rtx_t* run, qse_awk_val_t* left, qse_awk_val_t* right); +static qse_awk_val_t* eval_binop_tne ( + qse_awk_rtx_t* run, qse_awk_val_t* left, qse_awk_val_t* right); static qse_awk_val_t* eval_binop_eq ( qse_awk_rtx_t* run, qse_awk_val_t* left, qse_awk_val_t* right); static qse_awk_val_t* eval_binop_ne ( @@ -309,12 +314,12 @@ static int set_global ( /* once a variable becomes a map, * it cannot be changed to a scalar variable */ - if (var != QSE_NULL) + if (var) { /* global variable */ SETERR_ARGX_LOC ( rtx, - QSE_AWK_EMAPTOSCALAR, + QSE_AWK_EMAPNA, xstr_to_cstr(&var->id.name), &var->loc ); @@ -324,7 +329,7 @@ static int set_global ( /* qse_awk_rtx_setgbl has been called */ qse_cstr_t ea; ea.ptr = qse_awk_getgblname (rtx->awk, idx, &ea.len); - SETERR_ARGX (rtx, QSE_AWK_EMAPTOSCALAR, &ea); + SETERR_ARGX (rtx, QSE_AWK_EMAPNA, &ea); } return -1; @@ -656,7 +661,7 @@ int qse_awk_rtx_setofilename ( qse_awk_val_t* tmp; int n; - if (rtx->awk->opt.trait & QSE_AWK_NEXTOFILE) + if (rtx->awk->opt.trait & QSE_AWK_EXTRAKWS) { if (len == 0) tmp = qse_awk_val_zls; else @@ -2319,8 +2324,7 @@ static int run_return (qse_awk_rtx_t* run, qse_awk_nde_return_t* nde) /* cannot return a map */ qse_awk_rtx_refupval (run, val); qse_awk_rtx_refdownval (run, val); - - SETERR_LOC (run, QSE_AWK_EMAPNA, &nde->loc); + SETERR_LOC (run, QSE_AWK_EMAPPH, &nde->loc); return -1; } } @@ -2590,7 +2594,7 @@ static int run_delete_named (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var) return 0; } -static int run_delete_nonnamed (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var) +static int run_delete_unnamed (qse_awk_rtx_t* rtx, qse_awk_nde_var_t* var) { qse_awk_val_t* val; @@ -2693,7 +2697,7 @@ static int run_delete (qse_awk_rtx_t* rtx, qse_awk_nde_delete_t* nde) case QSE_AWK_NDE_GBLIDX: case QSE_AWK_NDE_LCLIDX: case QSE_AWK_NDE_ARGIDX: - return run_delete_nonnamed (rtx, var); + return run_delete_unnamed (rtx, var); default: QSE_ASSERTX ( @@ -3321,6 +3325,7 @@ static qse_awk_val_t* eval_assignment (qse_awk_rtx_t* run, qse_awk_nde_t* nde) qse_awk_val_t* val2, * tmp; static binop_func_t binop_func[] = { + /* this table must match qse_awk_assop_type_t in run.h */ QSE_NULL, /* QSE_AWK_ASSOP_NONE */ eval_binop_plus, eval_binop_minus, @@ -3455,13 +3460,12 @@ static qse_awk_val_t* do_assignment_scalar ( pair = qse_htb_search ( run->named, var->id.name.ptr, var->id.name.len); - if (pair != QSE_NULL && - ((qse_awk_val_t*)QSE_HTB_VPTR(pair))->type == QSE_AWK_VAL_MAP) + if (pair && ((qse_awk_val_t*)QSE_HTB_VPTR(pair))->type == QSE_AWK_VAL_MAP) { /* once a variable becomes a map, * it cannot be changed to a scalar variable */ SETERR_ARGX_LOC ( - run, QSE_AWK_EMAPTOSCALAR, + run, QSE_AWK_EMAPNA, xstr_to_cstr(&var->id.name), &var->loc); return QSE_NULL; } @@ -3495,7 +3499,7 @@ static qse_awk_val_t* do_assignment_scalar ( /* once the variable becomes a map, * it cannot be changed to a scalar variable */ SETERR_ARGX_LOC ( - run, QSE_AWK_EMAPTOSCALAR, + run, QSE_AWK_EMAPNA, xstr_to_cstr(&var->id.name), &var->loc); return QSE_NULL; } @@ -3514,7 +3518,7 @@ static qse_awk_val_t* do_assignment_scalar ( /* once the variable becomes a map, * it cannot be changed to a scalar variable */ SETERR_ARGX_LOC ( - run, QSE_AWK_EMAPTOSCALAR, + run, QSE_AWK_EMAPNA, xstr_to_cstr(&var->id.name), &var->loc); return QSE_NULL; } @@ -3718,6 +3722,8 @@ static qse_awk_val_t* eval_binary (qse_awk_rtx_t* run, qse_awk_nde_t* nde) eval_binop_bxor, eval_binop_band, + eval_binop_teq, + eval_binop_tne, eval_binop_eq, eval_binop_ne, eval_binop_gt, @@ -4338,28 +4344,67 @@ static int __cmp_val ( return func[left->type*4+right->type] (rtx, left, right); } -static qse_awk_val_t* eval_binop_eq ( - qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right) +static int teq_val (qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right) { int n; - if (left == right) + if (left == right) n = 1; + else if (left->type != right->type) n = 0; + else { - /* array comparison is not allowed but this special check - allows comparsion of an array with itself. let's not - care about this loophole. */ - return qse_awk_val_one; + switch (left->type) + { + case QSE_AWK_VAL_NIL: + n = 1; + break; + + case QSE_AWK_VAL_INT: + n = ((qse_awk_val_int_t*)left)->val == + ((qse_awk_val_int_t*)right)->val; + break; + + case QSE_AWK_VAL_FLT: + n = ((qse_awk_val_flt_t*)left)->val == + ((qse_awk_val_flt_t*)right)->val; + break; + + case QSE_AWK_VAL_STR: + n = qse_strxncmp ( + ((qse_awk_val_str_t*)left)->val.ptr, + ((qse_awk_val_str_t*)left)->val.len, + ((qse_awk_val_str_t*)right)->val.ptr, + ((qse_awk_val_str_t*)right)->val.len) == 0; + break; + + default: + /* map-x and map-y are always different regardless of + * their contents. however, if they are pointing to the + * same map value, it won't reach here but will be + * handled by the first check in this function */ + n = 0; + break; + } } -/* - if (left->type != right->type && - (left->type == QSE_AWK_VAL_MAP || right->type == QSE_AWK_VAL_MAP)) - { - return qse_awk_val_zero; - } -*/ + return n; +} - n = __cmp_val (rtx, left, right); +static qse_awk_val_t* eval_binop_teq ( + qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right) +{ + return teq_val (rtx, left, right)? qse_awk_val_one: qse_awk_val_zero; +} + +static qse_awk_val_t* eval_binop_tne ( + qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right) +{ + return teq_val (rtx, left, right)? qse_awk_val_zero: qse_awk_val_one; +} + +static qse_awk_val_t* eval_binop_eq ( + qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right) +{ + int n = __cmp_val (rtx, left, right); if (n == CMP_ERROR) return QSE_NULL; return (n == 0)? qse_awk_val_one: qse_awk_val_zero; } @@ -4367,25 +4412,7 @@ static qse_awk_val_t* eval_binop_eq ( static qse_awk_val_t* eval_binop_ne ( qse_awk_rtx_t* rtx, qse_awk_val_t* left, qse_awk_val_t* right) { - int n; - - if (left == right) - { - /* array comparison is not allowed but this special check - allows comparsion of an array with itself. let's not - care about this loophole. */ - return qse_awk_val_zero; - } - -/* - if (left->type != right->type && - (left->type == QSE_AWK_VAL_MAP || right->type == QSE_AWK_VAL_MAP)) - { - return qse_awk_val_one; - } -*/ - - n = __cmp_val (rtx, left, right); + int n = __cmp_val (rtx, left, right); if (n == CMP_ERROR) return QSE_NULL; return (n != 0)? qse_awk_val_one: qse_awk_val_zero; } diff --git a/qse/lib/awk/run.h b/qse/lib/awk/run.h index 3f3df00d..21be298d 100644 --- a/qse/lib/awk/run.h +++ b/qse/lib/awk/run.h @@ -24,7 +24,12 @@ enum qse_awk_assop_type_t { /* if you change this, you have to change assop_str in tree.c. - * synchronize it with binop_func of eval_assignment in run.c */ + * synchronize it wit: + * - binop_func in eval_assignment of run.c + * - assop in assing_to_opcode of parse.c + * - TOK_XXX_ASSN in tok_t in parse.c + * - assop_str in tree.c + */ QSE_AWK_ASSOP_NONE, QSE_AWK_ASSOP_PLUS, /* += */ QSE_AWK_ASSOP_MINUS, /* -= */ @@ -37,7 +42,7 @@ enum qse_awk_assop_type_t QSE_AWK_ASSOP_RS, /* >>= */ QSE_AWK_ASSOP_LS, /* <<= */ QSE_AWK_ASSOP_BAND, /* &= */ - QSE_AWK_ASSOP_BXOR, /* ^= */ + QSE_AWK_ASSOP_BXOR, /* ^^= */ QSE_AWK_ASSOP_BOR /* |= */ }; @@ -53,6 +58,8 @@ enum qse_awk_binop_type_t QSE_AWK_BINOP_BXOR, QSE_AWK_BINOP_BAND, + QSE_AWK_BINOP_TEQ, + QSE_AWK_BINOP_TNE, QSE_AWK_BINOP_EQ, QSE_AWK_BINOP_NE, QSE_AWK_BINOP_GT, diff --git a/qse/lib/awk/tree.c b/qse/lib/awk/tree.c index 876b2c89..5e7c468c 100644 --- a/qse/lib/awk/tree.c +++ b/qse/lib/awk/tree.c @@ -23,6 +23,7 @@ static const qse_char_t* assop_str[] = { + /* this table must match qse_awk_assop_type_t in run.h */ QSE_T("="), QSE_T("+="), QSE_T("-="), @@ -30,11 +31,12 @@ static const qse_char_t* assop_str[] = QSE_T("/="), QSE_T("\\="), QSE_T("%="), - QSE_T("**="), + QSE_T("**="), /* exponentation, also ^= */ + QSE_T("%%="), QSE_T(">>="), QSE_T("<<="), QSE_T("&="), - QSE_T("^="), + QSE_T("^^="), QSE_T("|=") }; @@ -45,9 +47,11 @@ static const qse_char_t* binop_str[][2] = { QSE_T("in"), QSE_T("in") }, { QSE_T("|"), QSE_T("|") }, - { QSE_T("^"), QSE_T("^") }, + { QSE_T("^^"), QSE_T("^^") }, { QSE_T("&"), QSE_T("&") }, + { QSE_T("==="), QSE_T("===") }, + { QSE_T("!=="), QSE_T("!==") }, { QSE_T("=="), QSE_T("==") }, { QSE_T("!="), QSE_T("!=") }, { QSE_T(">"), QSE_T(">") }, @@ -64,9 +68,9 @@ static const qse_char_t* binop_str[][2] = { QSE_T("/"), QSE_T("/") }, { QSE_T("\\"), QSE_T("\\") }, { QSE_T("%"), QSE_T("%") }, - { QSE_T("**"), QSE_T("**") }, + { QSE_T("**"), QSE_T("**") }, /* exponentation, also ^ */ - { QSE_T(" "), QSE_T(".") }, + { QSE_T(" "), QSE_T("%%") }, /* take note of this entry */ { QSE_T("~"), QSE_T("~") }, { QSE_T("!~"), QSE_T("!~") } }; @@ -76,7 +80,7 @@ static const qse_char_t* unrop_str[] = QSE_T("+"), QSE_T("-"), QSE_T("!"), - QSE_T("~") + QSE_T("~~") }; static const qse_char_t* incop_str[] = diff --git a/qse/mod/awk/uci.c b/qse/mod/awk/uci.c index 4fb4d42a..ca6d4f31 100644 --- a/qse/mod/awk/uci.c +++ b/qse/mod/awk/uci.c @@ -106,7 +106,7 @@ static void free_uctx_node (qse_awk_rtx_t* rtx, uctx_list_t* list, uctx_node_t* list->map.tab[node->id] = QSE_NULL; - uci_free_context (node->ctx); + if (node->ctx) uci_free_context (node->ctx); if (list->map.high == node->id + 1) { @@ -118,6 +118,7 @@ static void free_uctx_node (qse_awk_rtx_t* rtx, uctx_list_t* list, uctx_node_t* else { /* otherwise, chain the node to the free list */ + node->ctx = QSE_NULL; node->next = list->free; list->free = node; } @@ -135,7 +136,7 @@ static void free_uctx_node (qse_awk_rtx_t* rtx, uctx_list_t* list, uctx_node_t* { curnode = list->free; list->free = list->free->next; - uci_free_context (curnode->ctx); + QSE_ASSERT (curnode->ctx == QSE_NULL); QSE_MMGR_FREE (mmgr, curnode); } diff --git a/qse/regress/awk/regress.sh.in b/qse/regress/awk/regress.sh.in index fc7a4ab9..0bbcd05c 100755 --- a/qse/regress/awk/regress.sh.in +++ b/qse/regress/awk/regress.sh.in @@ -174,11 +174,11 @@ PROGS=" lang-045.awk!!!--newline=on -d- lang-046.awk!lang-046.dat2!!--newline=on -d- -vdatadir=@abs_srcdir@ -vdatafile=lang-046.dat1 lang-047.awk!!!--newline=on --tolerant=on -d- - lang-048.awk!!!--newline=on --extraops=on -d- + lang-048.awk!!!--newline=on -d- lang-049.awk!!!--newline=on -d- columnate.awk!passwd.dat!!--newline=on -F: - levenshtein-utests.awk!!!--newline=on --include=on + levenshtein-utests.awk!!!--newline=on --extrakws=on rcalc.awk!!!--newline=on -v target=89000 quicksort.awk!quicksort.dat!! quicksort2.awk!quicksort2.dat!!-vQSEAWK=\"${QSEAWK}\" -vSCRIPT_PATH=\"${SCRIPT_DIR}\" diff --git a/qse/samples/awk/awk04.c b/qse/samples/awk/awk04.c index bc948808..c5d30dc3 100644 --- a/qse/samples/awk/awk04.c +++ b/qse/samples/awk/awk04.c @@ -53,7 +53,6 @@ int main () /* don't allow BEGIN, END, pattern-action blocks */ opt &= ~QSE_AWK_PABLOCK; /* enable ** */ - opt |= QSE_AWK_EXTRAOPS; qse_awk_setopt (awk, QSE_AWK_TRAIT, &opt); diff --git a/qse/samples/awk/awk07.cpp b/qse/samples/awk/awk07.cpp index 2fcc5def..789cdffb 100644 --- a/qse/samples/awk/awk07.cpp +++ b/qse/samples/awk/awk07.cpp @@ -139,7 +139,7 @@ static int awk_main (int argc, qse_char_t* argv[]) awk.setTrait ( awk.getTrait() | QSE_AWK_MAPTOVAR | - QSE_AWK_RESET); + QSE_AWK_EXTRAKWS); if (ret >= 0) ret = run_awk (awk); if (ret <= -1) diff --git a/qse/samples/awk/awk08.cpp b/qse/samples/awk/awk08.cpp index 30b6b018..f93e9960 100644 --- a/qse/samples/awk/awk08.cpp +++ b/qse/samples/awk/awk08.cpp @@ -356,8 +356,7 @@ static int awk_main_2 (MyAwk& awk, int argc, qse_char_t* argv[]) cmdline_t cmdline; int n; - awk.setTrait (awk.getTrait() | QSE_AWK_INCLUDE | - QSE_AWK_MAPTOVAR | QSE_AWK_RWPIPE | QSE_AWK_EXTRAOPS); + awk.setTrait (awk.getTrait() | QSE_AWK_EXTRAKWS | QSE_AWK_MAPTOVAR | QSE_AWK_RWPIPE); // ARGV[0] if (awk.addArgument (QSE_T("awk08")) <= -1) diff --git a/qse/samples/awk/awk09.c b/qse/samples/awk/awk09.c index 4ed38d52..1d31a75d 100644 --- a/qse/samples/awk/awk09.c +++ b/qse/samples/awk/awk09.c @@ -64,7 +64,7 @@ int main () } qse_awk_getopt (awk, QSE_AWK_TRAIT, &opt); - opt |= QSE_AWK_NEXTOFILE; + opt |= QSE_AWK_EXTRAKWS; qse_awk_setopt (awk, QSE_AWK_TRAIT, &opt); psin.type = QSE_AWK_PARSESTD_STR; diff --git a/qse/samples/awk/awk10.c b/qse/samples/awk/awk10.c index c72f8c9c..5c099d60 100644 --- a/qse/samples/awk/awk10.c +++ b/qse/samples/awk/awk10.c @@ -59,8 +59,6 @@ int main () opt &= ~QSE_AWK_PABLOCK; /* can assign a map to a variable */ opt |= QSE_AWK_MAPTOVAR; - /* enable ** */ - opt |= QSE_AWK_EXTRAOPS; qse_awk_setopt (awk, QSE_AWK_TRAIT, &opt); psin.type = QSE_AWK_PARSESTD_STR;