/* * $Id: parse.c,v 1.247 2007-03-02 11:14:34 bacon Exp $ * * {License} */ #include enum { TOKEN_EOF, TOKEN_NEWLINE, /* TOKEN_XXX_ASSIGNs should in sync * with __assop in __assign_to_opcode */ TOKEN_ASSIGN, TOKEN_PLUS_ASSIGN, TOKEN_MINUS_ASSIGN, TOKEN_MUL_ASSIGN, TOKEN_DIV_ASSIGN, TOKEN_IDIV_ASSIGN, TOKEN_MOD_ASSIGN, TOKEN_EXP_ASSIGN, TOKEN_EQ, TOKEN_NE, TOKEN_LE, TOKEN_LT, TOKEN_GE, TOKEN_GT, TOKEN_NM, /* not match */ TOKEN_NOT, TOKEN_PLUS, TOKEN_PLUSPLUS, TOKEN_MINUS, TOKEN_MINUSMINUS, TOKEN_MUL, TOKEN_DIV, TOKEN_IDIV, TOKEN_MOD, TOKEN_LOR, TOKEN_LAND, TOKEN_BOR, TOKEN_BXOR, TOKEN_BAND, TOKEN_BORAND, TOKEN_TILDE, /* used for unary bitwise-not and regex match */ TOKEN_RSHIFT, TOKEN_LSHIFT, TOKEN_IN, TOKEN_EXP, TOKEN_LPAREN, TOKEN_RPAREN, TOKEN_LBRACE, TOKEN_RBRACE, TOKEN_LBRACK, TOKEN_RBRACK, TOKEN_DOLLAR, TOKEN_COMMA, TOKEN_PERIOD, TOKEN_SEMICOLON, TOKEN_COLON, TOKEN_QUEST, TOKEN_BEGIN, TOKEN_END, TOKEN_FUNCTION, TOKEN_LOCAL, TOKEN_GLOBAL, TOKEN_IF, TOKEN_ELSE, TOKEN_WHILE, TOKEN_FOR, TOKEN_DO, TOKEN_BREAK, TOKEN_CONTINUE, TOKEN_RETURN, TOKEN_EXIT, TOKEN_NEXT, TOKEN_NEXTFILE, TOKEN_NEXTINFILE, TOKEN_NEXTOFILE, TOKEN_DELETE, TOKEN_PRINT, TOKEN_PRINTF, TOKEN_GETLINE, TOKEN_IDENT, TOKEN_INT, TOKEN_REAL, TOKEN_STR, TOKEN_REX, __TOKEN_COUNT__ }; enum { PARSE_GLOBAL, PARSE_FUNCTION, PARSE_BEGIN, PARSE_END, PARSE_BEGIN_BLOCK, PARSE_END_BLOCK, PARSE_PATTERN, PARSE_ACTION_BLOCK }; enum { PARSE_LOOP_NONE, PARSE_LOOP_WHILE, PARSE_LOOP_FOR, PARSE_LOOP_DOWHILE }; typedef struct __binmap_t __binmap_t; struct __binmap_t { int token; int binop; }; static int __parse (ase_awk_t* awk); static ase_awk_t* __parse_progunit (ase_awk_t* awk); static ase_awk_t* __collect_globals (ase_awk_t* awk); static ase_awk_t* __add_builtin_globals (ase_awk_t* awk); static ase_awk_t* __add_global ( ase_awk_t* awk, const ase_char_t* name, ase_size_t len, ase_size_t line, int force); static ase_awk_t* __collect_locals (ase_awk_t* awk, ase_size_t nlocals); static ase_awk_nde_t* __parse_function (ase_awk_t* awk); static ase_awk_nde_t* __parse_begin (ase_awk_t* awk); static ase_awk_nde_t* __parse_end (ase_awk_t* awk); static ase_awk_chain_t* __parse_pattern_block ( ase_awk_t* awk, ase_awk_nde_t* ptn, ase_bool_t blockless); static ase_awk_nde_t* __parse_block (ase_awk_t* awk, ase_size_t line, ase_bool_t is_top); static ase_awk_nde_t* __parse_block_dc (ase_awk_t* awk, ase_size_t line, ase_bool_t is_top); static ase_awk_nde_t* __parse_statement (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_statement_nb (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_expression (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_expression0 (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_basic_expr (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_binary_expr ( ase_awk_t* awk, ase_size_t line, const __binmap_t* binmap, ase_awk_nde_t*(*next_level_func)(ase_awk_t*,ase_size_t)); static ase_awk_nde_t* __parse_logical_or (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_logical_and (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_in (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_regex_match (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_bitwise_or (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_bitwise_or_with_extio (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_bitwise_xor (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_bitwise_and (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_equality (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_relational (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_shift (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_concat (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_additive (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_multiplicative (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_unary (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_exponent (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_unary_exp (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_increment (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_primary (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_primary_ident (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_hashidx ( ase_awk_t* awk, ase_char_t* name, ase_size_t name_len, ase_size_t line); static ase_awk_nde_t* __parse_fncall ( ase_awk_t* awk, ase_char_t* name, ase_size_t name_len, ase_awk_bfn_t* bfn, ase_size_t line); static ase_awk_nde_t* __parse_if (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_while (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_for (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_dowhile (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_break (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_continue (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_return (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_exit (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_next (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_nextfile (ase_awk_t* awk, ase_size_t line, int out); static ase_awk_nde_t* __parse_delete (ase_awk_t* awk, ase_size_t line); static ase_awk_nde_t* __parse_print (ase_awk_t* awk, ase_size_t line, int type); static int __get_token (ase_awk_t* awk); static int __get_number (ase_awk_t* awk); static int __get_charstr (ase_awk_t* awk); static int __get_rexstr (ase_awk_t* awk); static int __get_string ( ase_awk_t* awk, ase_char_t end_char, ase_char_t esc_char, ase_bool_t keep_esc_char); static int __get_char (ase_awk_t* awk); static int __unget_char (ase_awk_t* awk, ase_cint_t c); static int __skip_spaces (ase_awk_t* awk); static int __skip_comment (ase_awk_t* awk); static int __classify_ident ( ase_awk_t* awk, const ase_char_t* name, ase_size_t len); static int __assign_to_opcode (ase_awk_t* awk); static int __is_plain_var (ase_awk_nde_t* nde); static int __is_var (ase_awk_nde_t* nde); static int __deparse (ase_awk_t* awk); static int __deparse_func (ase_awk_pair_t* pair, void* arg); static int __put_char (ase_awk_t* awk, ase_char_t c); static int __flush (ase_awk_t* awk); struct __kwent { const ase_char_t* name; ase_size_t name_len; int type; int valid; /* the entry is valid when this option is set */ }; static struct __kwent __kwtab[] = { /* operators */ { ASE_T("in"), 2, TOKEN_IN, 0 }, /* top-level block starters */ { ASE_T("BEGIN"), 5, TOKEN_BEGIN, 0 }, { ASE_T("END"), 3, TOKEN_END, 0 }, { ASE_T("function"), 8, TOKEN_FUNCTION, 0 }, { ASE_T("func"), 4, TOKEN_FUNCTION, 0 }, /* keywords for variable declaration */ { ASE_T("local"), 5, TOKEN_LOCAL, ASE_AWK_EXPLICIT }, { ASE_T("global"), 6, TOKEN_GLOBAL, ASE_AWK_EXPLICIT }, /* keywords that start statements excluding expression statements */ { ASE_T("if"), 2, TOKEN_IF, 0 }, { ASE_T("else"), 4, TOKEN_ELSE, 0 }, { ASE_T("while"), 5, TOKEN_WHILE, 0 }, { ASE_T("for"), 3, TOKEN_FOR, 0 }, { ASE_T("do"), 2, TOKEN_DO, 0 }, { ASE_T("break"), 5, TOKEN_BREAK, 0 }, { ASE_T("continue"), 8, TOKEN_CONTINUE, 0 }, { ASE_T("return"), 6, TOKEN_RETURN, 0 }, { ASE_T("exit"), 4, TOKEN_EXIT, 0 }, { ASE_T("next"), 4, TOKEN_NEXT, 0 }, { ASE_T("nextfile"), 8, TOKEN_NEXTFILE, 0 }, { ASE_T("nextofile"), 9, TOKEN_NEXTOFILE, ASE_AWK_NEXTOFILE }, { ASE_T("delete"), 6, TOKEN_DELETE, 0 }, { ASE_T("print"), 5, TOKEN_PRINT, ASE_AWK_EXTIO }, { ASE_T("printf"), 6, TOKEN_PRINTF, ASE_AWK_EXTIO }, /* keywords that can start an expression */ { ASE_T("getline"), 7, TOKEN_GETLINE, ASE_AWK_EXTIO }, { ASE_NULL, 0, 0, 0 } }; typedef struct global_t global_t; struct global_t { const ase_char_t* name; ase_size_t name_len; int valid; }; static global_t gtab[] = { { ASE_T("ARGC"), 4, 0 }, { ASE_T("ARGV"), 4, 0 }, { ASE_T("CONVFMT"), 7, 0 }, { ASE_T("ENVIRON"), 7, 0 }, { ASE_T("FILENAME"), 8, 0 }, { ASE_T("FNR"), 3, 0 }, { ASE_T("FS"), 2, 0 }, { ASE_T("IGNORECASE"), 10, 0 }, { ASE_T("NF"), 2, 0 }, { ASE_T("NR"), 2, 0 }, { ASE_T("OFILENAME"), 9, ASE_AWK_NEXTOFILE }, { ASE_T("OFMT"), 4, 0 }, { ASE_T("OFS"), 3, 0 }, { ASE_T("ORS"), 3, 0 }, { ASE_T("RLENGTH"), 7, 0 }, { ASE_T("RS"), 2, 0 }, { ASE_T("RSTART"), 6, 0 }, { ASE_T("SUBSEP"), 6, 0 }, { ASE_NULL, 0, 0 } }; #define GET_CHAR(awk) \ do { if (__get_char (awk) == -1) return -1; } while(0) #define GET_CHAR_TO(awk,c) \ do { \ if (__get_char (awk) == -1) return -1; \ c = (awk)->src.lex.curc; \ } while(0) /* #define SET_TOKEN_TYPE(awk,code) \ do { \ (awk)->token.prev.type = (awk)->token.type; \ (awk)->token.type = (code); \ } while (0) */ #define SET_TOKEN_TYPE(awk,code) \ do { (awk)->token.type = (code); } while (0) #define ADD_TOKEN_CHAR(awk,c) \ do { \ if (ase_str_ccat(&(awk)->token.name,(c)) == (ase_size_t)-1) { \ ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, (awk)->token.line, ASE_NULL); \ return -1; \ } \ } while (0) #define MATCH(awk,token_type) ((awk)->token.type == (token_type)) #define SET_ERROR_0(awk,code,msg) \ do { \ if (MATCH(awk,TOKEN_EOF)) \ { \ ase_awk_seterror_old ( \ awk, ASE_AWK_EENDSRC, \ (awk)->token.prev.line, ASE_NULL); \ } \ else \ { \ (awk)->prmfns.misc.sprintf ( \ (awk)->prmfns.misc.custom_data, \ (awk)->errmsg, ASE_COUNTOF((awk)->errmsg), \ msg, \ ASE_STR_LEN(&(awk)->token.name), \ ASE_STR_BUF(&(awk)->token.name)); \ ase_awk_seterror_old ( \ awk, code, (awk)->token.line, \ (awk)->errmsg); \ } \ } while (0) ase_size_t ase_awk_getmaxdepth (ase_awk_t* awk, int type) { return (type == ASE_AWK_DEPTH_BLOCK_PARSE)? awk->parse.depth.max.block: (type == ASE_AWK_DEPTH_BLOCK_RUN)? awk->run.depth.max.block: (type == ASE_AWK_DEPTH_EXPR_PARSE)? awk->parse.depth.max.expr: (type == ASE_AWK_DEPTH_EXPR_RUN)? awk->run.depth.max.expr: (type == ASE_AWK_DEPTH_REX_BUILD)? awk->rex.depth.max.build: (type == ASE_AWK_DEPTH_REX_MATCH)? awk->rex.depth.max.match: 0; } void ase_awk_setmaxdepth (ase_awk_t* awk, int types, ase_size_t depth) { if (types & ASE_AWK_DEPTH_BLOCK_PARSE) { awk->parse.depth.max.block = depth; if (depth <= 0) awk->parse.parse_block = __parse_block; else awk->parse.parse_block = __parse_block_dc; } if (types & ASE_AWK_DEPTH_EXPR_PARSE) { awk->parse.depth.max.expr = depth; } if (types & ASE_AWK_DEPTH_BLOCK_RUN) { awk->run.depth.max.block = depth; } if (types & ASE_AWK_DEPTH_EXPR_RUN) { awk->run.depth.max.expr = depth; } if (types & ASE_AWK_DEPTH_REX_BUILD) { awk->rex.depth.max.build = depth; } if (types & ASE_AWK_DEPTH_REX_MATCH) { awk->rex.depth.max.match = depth; } } const ase_char_t* ase_awk_getglobalname ( ase_awk_t* awk, ase_size_t idx, ase_size_t* len) { *len = gtab[idx].name_len; return gtab[idx].name; } int ase_awk_parse (ase_awk_t* awk, ase_awk_srcios_t* srcios) { int n; ASE_AWK_ASSERTX (awk, srcios != ASE_NULL && srcios->in != ASE_NULL, "the source code input stream must be provided at least"); ASE_AWK_ASSERT (awk, awk->parse.depth.cur.loop == 0); ASE_AWK_ASSERT (awk, awk->parse.depth.cur.expr == 0); ase_awk_clear (awk); ase_memcpy (&awk->src.ios, srcios, ASE_SIZEOF(awk->src.ios)); n = __parse (awk); ASE_AWK_ASSERT (awk, awk->parse.depth.cur.loop == 0); ASE_AWK_ASSERT (awk, awk->parse.depth.cur.expr == 0); return n; } static int __parse (ase_awk_t* awk) { int n = 0, op; ASE_AWK_ASSERT (awk, awk->src.ios.in != ASE_NULL); op = awk->src.ios.in ( ASE_AWK_IO_OPEN, awk->src.ios.custom_data, ASE_NULL, 0); if (op <= -1) { /* cannot open the source file. * it doesn't even have to call CLOSE */ ase_awk_seterror_old ( awk, ASE_AWK_ESINOP, 0, ASE_T("cannot open the source input")); return -1; } if (__add_builtin_globals (awk) == ASE_NULL) { n = -1; goto exit_parse; } /* the user io handler for the source code input returns 0 when * it doesn't have any files to open. this is the same condition * as the source code file is empty. so it will perform the parsing * when op is positive, which means there are something to parse */ if (op > 0) { /* get the first character */ if (__get_char(awk) == -1) { n = -1; goto exit_parse; } /* get the first token */ if (__get_token(awk) == -1) { n = -1; goto exit_parse; } while (1) { if (MATCH(awk,TOKEN_EOF)) break; if (MATCH(awk,TOKEN_NEWLINE)) continue; if (__parse_progunit (awk) == ASE_NULL) { n = -1; goto exit_parse; } } } awk->tree.nglobals = ase_awk_tab_getsize(&awk->parse.globals); if (awk->src.ios.out != ASE_NULL) { if (__deparse (awk) == -1) { n = -1; goto exit_parse; } } exit_parse: if (awk->src.ios.in ( ASE_AWK_IO_CLOSE, awk->src.ios.custom_data, ASE_NULL, 0) != 0) { if (n == 0) { /* this is to keep the earlier error above * that might be more critical than this */ ase_awk_seterror_old ( awk, ASE_AWK_ESINCL, 0, ASE_T("cannot close the source input")); n = -1; } } if (n == -1) ase_awk_clear (awk); else awk->tree.ok = 1; return n; } static ase_awk_t* __parse_progunit (ase_awk_t* awk) { /* pattern { action } function name (parameter-list) { statement } */ ASE_AWK_ASSERT (awk, awk->parse.depth.cur.loop == 0); if ((awk->option & ASE_AWK_EXPLICIT) && MATCH(awk,TOKEN_GLOBAL)) { ase_size_t nglobals; awk->parse.id.block = PARSE_GLOBAL; if (__get_token(awk) == -1) return ASE_NULL; nglobals = ase_awk_tab_getsize(&awk->parse.globals); if (__collect_globals (awk) == ASE_NULL) { ase_awk_tab_remove ( &awk->parse.globals, nglobals, ase_awk_tab_getsize(&awk->parse.globals) - nglobals); return ASE_NULL; } } else if (MATCH(awk,TOKEN_FUNCTION)) { awk->parse.id.block = PARSE_FUNCTION; if (__parse_function (awk) == ASE_NULL) return ASE_NULL; } else if (MATCH(awk,TOKEN_BEGIN)) { awk->parse.id.block = PARSE_BEGIN; if (__get_token(awk) == -1) return ASE_NULL; if ((awk->option & ASE_AWK_BLOCKLESS) && (MATCH(awk,TOKEN_NEWLINE) || MATCH(awk,TOKEN_EOF))) { /* when the blockless pattern is supported * BEGIN and { should be located on the same line */ ase_awk_seterror_old ( awk, ASE_AWK_EBLKBEG, awk->token.prev.line, ASE_T("BEGIN not followed by a left bracket on the same line")); return ASE_NULL; } if (!MATCH(awk,TOKEN_LBRACE)) { ase_awk_seterror_old ( awk, ASE_AWK_ELBRACE, awk->token.prev.line, ASE_T("BEGIN not followed by a left bracket")); return ASE_NULL; } awk->parse.id.block = PARSE_BEGIN_BLOCK; if (__parse_begin (awk) == ASE_NULL) return ASE_NULL; } else if (MATCH(awk,TOKEN_END)) { awk->parse.id.block = PARSE_END; if (__get_token(awk) == -1) return ASE_NULL; if ((awk->option & ASE_AWK_BLOCKLESS) && (MATCH(awk,TOKEN_NEWLINE) || MATCH(awk,TOKEN_EOF))) { /* when the blockless pattern is supported * END and { should be located on the same line */ ase_awk_seterror_old ( awk, ASE_AWK_EBLKEND, awk->token.prev.line, ASE_T("END not followed by a left bracket on the same line")); return ASE_NULL; } if (!MATCH(awk,TOKEN_LBRACE)) { ase_awk_seterror_old ( awk, ASE_AWK_ELBRACE, awk->token.prev.line, ASE_T("END not followed by a left bracket")); return ASE_NULL; } awk->parse.id.block = PARSE_END_BLOCK; if (__parse_end (awk) == ASE_NULL) return ASE_NULL; } else if (MATCH(awk,TOKEN_LBRACE)) { /* patternless block */ awk->parse.id.block = PARSE_ACTION_BLOCK; if (__parse_pattern_block ( awk, ASE_NULL, ase_false) == ASE_NULL) return ASE_NULL; } else { /* expressions /regular expression/ pattern && pattern pattern || pattern !pattern (pattern) pattern, pattern */ ase_awk_nde_t* ptn; awk->parse.id.block = PARSE_PATTERN; ptn = __parse_expression (awk, awk->token.line); if (ptn == ASE_NULL) return ASE_NULL; ASE_AWK_ASSERT (awk, ptn->next == ASE_NULL); if (MATCH(awk,TOKEN_COMMA)) { if (__get_token (awk) == -1) { ase_awk_clrpt (awk, ptn); return ASE_NULL; } ptn->next = __parse_expression (awk, awk->token.line); if (ptn->next == ASE_NULL) { ase_awk_clrpt (awk, ptn); return ASE_NULL; } } if ((awk->option & ASE_AWK_BLOCKLESS) && (MATCH(awk,TOKEN_NEWLINE) || MATCH(awk,TOKEN_EOF))) { /* blockless pattern */ ase_bool_t newline = MATCH(awk,TOKEN_NEWLINE); awk->parse.id.block = PARSE_ACTION_BLOCK; if (__parse_pattern_block ( awk, ptn, ase_true) == ASE_NULL) { ase_awk_clrpt (awk, ptn); return ASE_NULL; } if (newline) { if (__get_token (awk) == -1) { /* ptn has been added to the chain. * it doesn't have to be cleared here * as ase_awk_clear does it */ /*ase_awk_clrpt (awk, ptn);*/ return ASE_NULL; } } } else { /* parse the action block */ if (!MATCH(awk,TOKEN_LBRACE)) { ase_awk_clrpt (awk, ptn); ase_awk_seterror_old ( awk, ASE_AWK_ELBRACE, (MATCH(awk,TOKEN_EOF)? awk->token.prev.line: awk->token.line), ASE_T("not a valid start of a block")); return ASE_NULL; } awk->parse.id.block = PARSE_ACTION_BLOCK; if (__parse_pattern_block ( awk, ptn, ase_false) == ASE_NULL) { ase_awk_clrpt (awk, ptn); return ASE_NULL; } } } return awk; } static ase_awk_nde_t* __parse_function (ase_awk_t* awk) { ase_char_t* name; ase_char_t* name_dup; ase_size_t name_len; ase_awk_nde_t* body; ase_awk_afn_t* afn; ase_size_t nargs; ase_awk_pair_t* pair; int n; /* eat up the keyword 'function' and get the next token */ ASE_AWK_ASSERT (awk, MATCH(awk,TOKEN_FUNCTION)); if (__get_token(awk) == -1) return ASE_NULL; /* match a function name */ if (!MATCH(awk,TOKEN_IDENT)) { /* cannot find a valid identifier for a function name */ if (MATCH(awk,TOKEN_EOF)) { ase_awk_seterror_old ( awk, ASE_AWK_EIDENT, awk->token.prev.line, ASE_T("function definition without a name")); } else { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("'%.*s' not a valid function name"), ASE_STR_LEN(&awk->token.name), ASE_STR_BUF(&awk->token.name)); ase_awk_seterror_old ( awk, ASE_AWK_EIDENT, awk->token.line, awk->errmsg); } return ASE_NULL; } name = ASE_STR_BUF(&awk->token.name); name_len = ASE_STR_LEN(&awk->token.name); /* check if it is a builtin function */ if (ase_awk_getbfn (awk, name, name_len) != ASE_NULL) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("built-in function '%.*s' redefined"), name_len, name); ase_awk_seterror_old ( awk, ASE_AWK_EBFNRED, awk->token.line, awk->errmsg); return ASE_NULL; } if (ase_awk_map_get(&awk->tree.afns, name, name_len) != ASE_NULL) { /* the function is defined previously */ awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("function '%.*s' redefined"), name_len, name); ase_awk_seterror_old ( awk, ASE_AWK_EAFNRED, awk->token.line, awk->errmsg); return ASE_NULL; } if (awk->option & ASE_AWK_UNIQUEFN) { /* check if it coincides to be a global variable name */ ase_size_t g; g = ase_awk_tab_find (&awk->parse.globals, 0, name, name_len); if (g != (ase_size_t)-1) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("global variable '%.*s' redefined"), name_len, name); ase_awk_seterror_old ( awk, ASE_AWK_EGBLRED, awk->token.line, awk->errmsg); return ASE_NULL; } } /* clone the function name before it is overwritten */ name_dup = ase_strxdup (name, name_len, &awk->prmfns.mmgr); if (name_dup == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, awk->token.line, ASE_NULL); return ASE_NULL; } /* get the next token */ if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, name_dup); return ASE_NULL; } /* match a left parenthesis */ if (!MATCH(awk,TOKEN_LPAREN)) { /* a function name is not followed by a left parenthesis */ ASE_AWK_FREE (awk, name_dup); ase_awk_seterror_old ( awk, ASE_AWK_ELPAREN, awk->token.line, ASE_T("function name not followed by a left parenthesis")); return ASE_NULL; } /* get the next token */ if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, name_dup); return ASE_NULL; } /* make sure that parameter table is empty */ ASE_AWK_ASSERT (awk, ase_awk_tab_getsize(&awk->parse.params) == 0); /* read parameter list */ if (MATCH(awk,TOKEN_RPAREN)) { /* no function parameter found. get the next token */ if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, name_dup); return ASE_NULL; } } else { while (1) { ase_char_t* param; ase_size_t param_len; if (!MATCH(awk,TOKEN_IDENT)) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); if (MATCH(awk,TOKEN_EOF)) { ase_awk_seterror_old ( awk, ASE_AWK_EENDSRC, awk->token.prev.line, ASE_NULL); } else if (MATCH(awk,TOKEN_RPAREN)) { ase_awk_seterror_old ( awk, ASE_AWK_EIDENT, awk->token.prev.line, ASE_T("premature end of parameter list")); } else { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("'%.*s' not a valid parameter name"), ASE_STR_LEN(&awk->token.name), ASE_STR_BUF(&awk->token.name)); ase_awk_seterror_old ( awk, ASE_AWK_EIDENT, awk->token.line, awk->errmsg); } return ASE_NULL; } param = ASE_STR_BUF(&awk->token.name); param_len = ASE_STR_LEN(&awk->token.name); if (awk->option & ASE_AWK_UNIQUEFN) { /* check if a parameter conflicts with a function */ if (ase_strxncmp (name_dup, name_len, param, param_len) == 0 || ase_awk_map_get (&awk->tree.afns, param, param_len) != ASE_NULL) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("conflicting parameter '%.*s' with the function"), param_len, param); ase_awk_seterror_old ( awk, ASE_AWK_EDUPPAR, awk->token.line, awk->errmsg); return ASE_NULL; } /* NOTE: the following is not a conflict * global x; * function f (x) { print x; } * x in print x is a parameter */ } /* check if a parameter conflicts with other parameters */ if (ase_awk_tab_find ( &awk->parse.params, 0, param, param_len) != (ase_size_t)-1) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("duplicate parameter '%.*s'"), param_len, param); ase_awk_seterror_old ( awk, ASE_AWK_EDUPPAR, awk->token.line, awk->errmsg); return ASE_NULL; } /* push the parameter to the parameter list */ if (ase_awk_tab_getsize ( &awk->parse.params) >= ASE_AWK_MAX_PARAMS) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); ase_awk_seterror_old ( awk, ASE_AWK_EPARTM, awk->token.line, ASE_T("too many parameters in the parameter list")); return ASE_NULL; } if (ase_awk_tab_add ( &awk->parse.params, param, param_len) == (ase_size_t)-1) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, awk->token.line, ASE_NULL); return ASE_NULL; } if (__get_token (awk) == -1) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); return ASE_NULL; } if (MATCH(awk,TOKEN_RPAREN)) break; if (!MATCH(awk,TOKEN_COMMA)) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); SET_ERROR_0 (awk, ASE_AWK_ECOMMA, ASE_T("comma expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); return ASE_NULL; } } if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); return ASE_NULL; } } /* check if the function body starts with a left brace */ if (!MATCH(awk,TOKEN_LBRACE)) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); if (MATCH(awk,TOKEN_EOF)) { ase_awk_seterror_old ( awk, ASE_AWK_EENDSRC, awk->token.prev.line, ASE_NULL); } else { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("'%.*s' not a valid start of the function body"), ASE_STR_LEN(&awk->token.name), ASE_STR_BUF(&awk->token.name)); ase_awk_seterror_old ( awk, ASE_AWK_ELBRACE, awk->token.line, awk->errmsg); } return ASE_NULL; } if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); return ASE_NULL; } /* actual function body */ body = awk->parse.parse_block (awk, awk->token.prev.line, ase_true); if (body == ASE_NULL) { ASE_AWK_FREE (awk, name_dup); ase_awk_tab_clear (&awk->parse.params); return ASE_NULL; } /* TODO: study furthur if the parameter names should be saved * for some reasons - might be need for deparsing output */ nargs = ase_awk_tab_getsize (&awk->parse.params); /* parameter names are not required anymore. clear them */ ase_awk_tab_clear (&awk->parse.params); afn = (ase_awk_afn_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_afn_t)); if (afn == ASE_NULL) { ASE_AWK_FREE (awk, name_dup); ase_awk_clrpt (awk, body); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, awk->token.line, ASE_NULL); return ASE_NULL; } afn->name = ASE_NULL; /* function name set below */ afn->name_len = 0; afn->nargs = nargs; afn->body = body; n = ase_awk_map_putx (&awk->tree.afns, name_dup, name_len, afn, &pair); if (n < 0) { ASE_AWK_FREE (awk, name_dup); ase_awk_clrpt (awk, body); ASE_AWK_FREE (awk, afn); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, awk->token.line, ASE_NULL); return ASE_NULL; } /* duplicate functions should have been detected previously */ ASE_AWK_ASSERT (awk, n != 0); afn->name = pair->key; /* do some trick to save a string. */ afn->name_len = pair->key_len; ASE_AWK_FREE (awk, name_dup); return body; } static ase_awk_nde_t* __parse_begin (ase_awk_t* awk) { ase_awk_nde_t* nde; ASE_AWK_ASSERT (awk, MATCH(awk,TOKEN_LBRACE)); if (__get_token(awk) == -1) return ASE_NULL; nde = awk->parse.parse_block (awk, awk->token.prev.line, ase_true); if (nde == ASE_NULL) return ASE_NULL; awk->tree.begin = nde; return nde; } static ase_awk_nde_t* __parse_end (ase_awk_t* awk) { ase_awk_nde_t* nde; ASE_AWK_ASSERT (awk, MATCH(awk,TOKEN_LBRACE)); if (__get_token(awk) == -1) return ASE_NULL; nde = awk->parse.parse_block (awk, awk->token.prev.line, ase_true); if (nde == ASE_NULL) return ASE_NULL; awk->tree.end = nde; return nde; } static ase_awk_chain_t* __parse_pattern_block ( ase_awk_t* awk, ase_awk_nde_t* ptn, ase_bool_t blockless) { ase_awk_nde_t* nde; ase_awk_chain_t* chain; ase_size_t line = awk->token.line; if (blockless) nde = ASE_NULL; else { ASE_AWK_ASSERT (awk, MATCH(awk,TOKEN_LBRACE)); if (__get_token(awk) == -1) return ASE_NULL; nde = awk->parse.parse_block (awk, line, ase_true); if (nde == ASE_NULL) return ASE_NULL; } chain = (ase_awk_chain_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_chain_t)); if (chain == ASE_NULL) { ase_awk_clrpt (awk, nde); ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } chain->pattern = ptn; chain->action = nde; chain->next = ASE_NULL; if (awk->tree.chain == ASE_NULL) { awk->tree.chain = chain; awk->tree.chain_tail = chain; awk->tree.chain_size++; } else { awk->tree.chain_tail->next = chain; awk->tree.chain_tail = chain; awk->tree.chain_size++; } return chain; } static ase_awk_nde_t* __parse_block ( ase_awk_t* awk, ase_size_t line, ase_bool_t is_top) { ase_awk_nde_t* head, * curr, * nde; ase_awk_nde_blk_t* block; ase_size_t nlocals, nlocals_max, tmp; nlocals = ase_awk_tab_getsize(&awk->parse.locals); nlocals_max = awk->parse.nlocals_max; /* local variable declarations */ if (awk->option & ASE_AWK_EXPLICIT) { while (1) { if (!MATCH(awk,TOKEN_LOCAL)) break; if (__get_token(awk) == -1) { ase_awk_tab_remove ( &awk->parse.locals, nlocals, ase_awk_tab_getsize(&awk->parse.locals) - nlocals); return ASE_NULL; } if (__collect_locals(awk, nlocals) == ASE_NULL) { ase_awk_tab_remove ( &awk->parse.locals, nlocals, ase_awk_tab_getsize(&awk->parse.locals) - nlocals); return ASE_NULL; } } } /* block body */ head = ASE_NULL; curr = ASE_NULL; while (1) { if (MATCH(awk,TOKEN_EOF)) { ase_awk_tab_remove ( &awk->parse.locals, nlocals, ase_awk_tab_getsize(&awk->parse.locals) - nlocals); if (head != ASE_NULL) ase_awk_clrpt (awk, head); ase_awk_seterror_old ( awk, ASE_AWK_EENDSRC, awk->token.prev.line, ASE_NULL); return ASE_NULL; } if (MATCH(awk,TOKEN_RBRACE)) { if (__get_token(awk) == -1) { ase_awk_tab_remove ( &awk->parse.locals, nlocals, ase_awk_tab_getsize(&awk->parse.locals) - nlocals); if (head != ASE_NULL) ase_awk_clrpt (awk, head); return ASE_NULL; } break; } nde = __parse_statement (awk, awk->token.line); if (nde == ASE_NULL) { ase_awk_tab_remove ( &awk->parse.locals, nlocals, ase_awk_tab_getsize(&awk->parse.locals)-nlocals); if (head != ASE_NULL) ase_awk_clrpt (awk, head); return ASE_NULL; } /* remove unnecessary statements */ if (nde->type == ASE_AWK_NDE_NULL || (nde->type == ASE_AWK_NDE_BLK && ((ase_awk_nde_blk_t*)nde)->body == ASE_NULL)) continue; if (curr == ASE_NULL) head = nde; else curr->next = nde; curr = nde; } block = (ase_awk_nde_blk_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_blk_t)); if (block == ASE_NULL) { ase_awk_tab_remove ( &awk->parse.locals, nlocals, ase_awk_tab_getsize(&awk->parse.locals)-nlocals); ase_awk_clrpt (awk, head); ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } tmp = ase_awk_tab_getsize(&awk->parse.locals); if (tmp > awk->parse.nlocals_max) awk->parse.nlocals_max = tmp; ase_awk_tab_remove ( &awk->parse.locals, nlocals, tmp - nlocals); /* adjust the number of locals for a block without any statements */ /* if (head == ASE_NULL) tmp = 0; */ block->type = ASE_AWK_NDE_BLK; block->line = line; block->next = ASE_NULL; block->body = head; /* TODO: not only local variables but also nested blocks, unless it is part of other constructs such as if, can be promoted and merged to top-level block */ /* migrate all block-local variables to a top-level block */ if (is_top) { block->nlocals = awk->parse.nlocals_max - nlocals; awk->parse.nlocals_max = nlocals_max; } else { /*block->nlocals = tmp - nlocals;*/ block->nlocals = 0; } return (ase_awk_nde_t*)block; } static ase_awk_nde_t* __parse_block_dc ( ase_awk_t* awk, ase_size_t line, ase_bool_t is_top) { ase_awk_nde_t* nde; ASE_AWK_ASSERT (awk, awk->parse.depth.max.block > 0); if (awk->parse.depth.cur.block >= awk->parse.depth.max.block) { ase_awk_seterror_old ( awk, ASE_AWK_ERECUR, awk->token.prev.line, ASE_T("block nested too deeply")); return ASE_NULL; } awk->parse.depth.cur.block++; nde = __parse_block (awk, line, is_top); awk->parse.depth.cur.block--; return nde; } static ase_awk_t* __add_builtin_globals (ase_awk_t* awk) { global_t* p = gtab; ase_awk_t* tmp; awk->tree.nbglobals = 0; while (p->name != ASE_NULL) { if (p->valid != 0 && (awk->option & p->valid) == 0) { /* an invalid global variable are still added * to the global variable table with an empty name. * this is to prevent the run-time from looking up * the variable */ tmp =__add_global (awk, ASE_T(""), 0, 0, 1); } else { tmp =__add_global (awk, p->name, p->name_len, 0, 0); } if (tmp == ASE_NULL) return ASE_NULL; awk->tree.nbglobals++; p++; } return awk; } static ase_awk_t* __add_global ( ase_awk_t* awk, const ase_char_t* name, ase_size_t len, ase_size_t line, int force) { if (!force) { if (awk->option & ASE_AWK_UNIQUEFN) { /* check if it conflict with a builtin function name */ if (ase_awk_getbfn (awk, name, len) != ASE_NULL) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("built-in function '%.*s' redefined"), len, name); ase_awk_seterror_old (awk, ASE_AWK_EBFNRED, line, awk->errmsg); return ASE_NULL; } /* check if it conflict with a function name */ if (ase_awk_map_get ( &awk->tree.afns, name, len) != ASE_NULL) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("function '%.*s' redefined"), len, name); ase_awk_seterror_old (awk, ASE_AWK_EAFNRED, line, awk->errmsg); return ASE_NULL; } } /* check if it conflicts with other global variable names */ if (ase_awk_tab_find ( &awk->parse.globals, 0, name, len) != (ase_size_t)-1) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("duplicate global variable '%.*s'"), len, name); ase_awk_seterror_old (awk, ASE_AWK_EDUPGBL, line, awk->errmsg); return ASE_NULL; } } if (ase_awk_tab_getsize(&awk->parse.globals) >= ASE_AWK_MAX_GLOBALS) { ase_awk_seterror_old (awk, ASE_AWK_EGBLTM, line, ASE_NULL); return ASE_NULL; } if (ase_awk_tab_add (&awk->parse.globals, name, len) == (ase_size_t)-1) { ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } return awk; } static ase_awk_t* __collect_globals (ase_awk_t* awk) { while (1) { if (!MATCH(awk,TOKEN_IDENT)) { if (MATCH(awk,TOKEN_EOF)) { ase_awk_seterror_old ( awk, ASE_AWK_EENDSRC, awk->token.prev.line, ASE_NULL); return ASE_NULL; } else { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("'%.*s' not a valid variable name"), ASE_STR_LEN(&awk->token.name), ASE_STR_BUF(&awk->token.name)); ase_awk_seterror_old ( awk, ASE_AWK_EIDENT, awk->token.line, awk->errmsg); return ASE_NULL; } } if (__add_global ( awk, ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), awk->token.line, 0) == ASE_NULL) return ASE_NULL; if (__get_token(awk) == -1) return ASE_NULL; if (MATCH(awk,TOKEN_SEMICOLON)) break; if (!MATCH(awk,TOKEN_COMMA)) { SET_ERROR_0 (awk, ASE_AWK_ECOMMA, ASE_T("comma expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) return ASE_NULL; } /* skip a semicolon */ if (__get_token(awk) == -1) return ASE_NULL; return awk; } static ase_awk_t* __collect_locals (ase_awk_t* awk, ase_size_t nlocals) { ase_char_t* local; ase_size_t local_len; while (1) { if (!MATCH(awk,TOKEN_IDENT)) { if (MATCH(awk,TOKEN_EOF)) { ase_awk_seterror_old ( awk, ASE_AWK_EENDSRC, awk->token.prev.line, ASE_NULL); return ASE_NULL; } else { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("'%.*s' not a valid variable name"), ASE_STR_LEN(&awk->token.name), ASE_STR_BUF(&awk->token.name)); ase_awk_seterror_old ( awk, ASE_AWK_EIDENT, awk->token.line, awk->errmsg); return ASE_NULL; } } local = ASE_STR_BUF(&awk->token.name); local_len = ASE_STR_LEN(&awk->token.name); /* NOTE: it is not checked againt globals names */ if (awk->option & ASE_AWK_UNIQUEFN) { /* check if it conflict with a builtin function name */ if (ase_awk_getbfn (awk, local, local_len) != ASE_NULL) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("built-in function '%.*s' redefined"), local_len, local); ase_awk_seterror_old ( awk, ASE_AWK_EBFNRED, awk->token.line, awk->errmsg); return ASE_NULL; } /* check if it conflict with a function name */ if (ase_awk_map_get ( &awk->tree.afns, local, local_len) != ASE_NULL) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("function '%.*s' redefined"), local_len, local); ase_awk_seterror_old ( awk, ASE_AWK_EAFNRED, awk->token.line, awk->errmsg); return ASE_NULL; } } /* check if it conflicts with a paremeter name */ if (ase_awk_tab_find (&awk->parse.params, 0, local, local_len) != (ase_size_t)-1) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("parameter '%.*s' redefined"), local_len, local); ase_awk_seterror_old ( awk, ASE_AWK_EPARRED, awk->token.line, awk->errmsg); return ASE_NULL; } /* check if it conflicts with other local variable names */ if (ase_awk_tab_find (&awk->parse.locals, ((awk->option & ASE_AWK_SHADING)? nlocals: 0), local, local_len) != (ase_size_t)-1) { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("duplicate local variable '%.*s'"), local_len, local); ase_awk_seterror_old ( awk, ASE_AWK_EDUPLCL, awk->token.line, awk->errmsg); return ASE_NULL; } if (ase_awk_tab_getsize(&awk->parse.locals) >= ASE_AWK_MAX_LOCALS) { ase_awk_seterror_old ( awk, ASE_AWK_ELCLTM, awk->token.line, ASE_NULL); return ASE_NULL; } if (ase_awk_tab_add ( &awk->parse.locals, local, local_len) == (ase_size_t)-1) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, awk->token.line, ASE_NULL); return ASE_NULL; } if (__get_token(awk) == -1) return ASE_NULL; if (MATCH(awk,TOKEN_SEMICOLON)) break; if (!MATCH(awk,TOKEN_COMMA)) { SET_ERROR_0 (awk, ASE_AWK_ECOMMA, ASE_T("comma expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) return ASE_NULL; } /* skip a semicolon */ if (__get_token(awk) == -1) return ASE_NULL; return awk; } static ase_awk_nde_t* __parse_statement (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* nde; if (MATCH(awk,TOKEN_SEMICOLON)) { /* null statement */ nde = (ase_awk_nde_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_t)); if (nde == ASE_NULL) { ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_NULL; nde->line = line; nde->next = ASE_NULL; if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, nde); return ASE_NULL; } } else if (MATCH(awk,TOKEN_LBRACE)) { if (__get_token(awk) == -1) return ASE_NULL; nde = awk->parse.parse_block ( awk, awk->token.prev.line, ase_false); } else { /* the statement id held in awk->parse.id.stmnt denotes * the token id of the statement currently being parsed. * the current statement id is saved here because the * statement id can be changed in __parse_statement_nb. * it will, in turn, call __parse_statement which will * eventually change the statement id. */ int old_id = awk->parse.id.stmnt; /* set the current statement id */ awk->parse.id.stmnt = awk->token.type; /* proceed parsing the statement */ nde = __parse_statement_nb (awk, line); /* restore the statement id saved previously */ awk->parse.id.stmnt = old_id; } return nde; } static ase_awk_nde_t* __parse_statement_nb (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* nde; /* keywords that don't require any terminating semicolon */ if (MATCH(awk,TOKEN_IF)) { if (__get_token(awk) == -1) return ASE_NULL; return __parse_if (awk, line); } else if (MATCH(awk,TOKEN_WHILE)) { if (__get_token(awk) == -1) return ASE_NULL; awk->parse.depth.cur.loop++; nde = __parse_while (awk, line); awk->parse.depth.cur.loop--; return nde; } else if (MATCH(awk,TOKEN_FOR)) { if (__get_token(awk) == -1) return ASE_NULL; awk->parse.depth.cur.loop++; nde = __parse_for (awk, line); awk->parse.depth.cur.loop--; return nde; } /* keywords that require a terminating semicolon */ if (MATCH(awk,TOKEN_DO)) { if (__get_token(awk) == -1) return ASE_NULL; awk->parse.depth.cur.loop++; nde = __parse_dowhile (awk, line); awk->parse.depth.cur.loop--; return nde; } else if (MATCH(awk,TOKEN_BREAK)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_break (awk, line); } else if (MATCH(awk,TOKEN_CONTINUE)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_continue (awk, line); } else if (MATCH(awk,TOKEN_RETURN)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_return (awk, line); } else if (MATCH(awk,TOKEN_EXIT)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_exit (awk, line); } else if (MATCH(awk,TOKEN_NEXT)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_next (awk, line); } else if (MATCH(awk,TOKEN_NEXTFILE)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_nextfile (awk, line, 0); } else if (MATCH(awk,TOKEN_NEXTOFILE)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_nextfile (awk, line, 1); } else if (MATCH(awk,TOKEN_DELETE)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_delete (awk, line); } else if (MATCH(awk,TOKEN_PRINT)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_print (awk, line, ASE_AWK_NDE_PRINT); } else if (MATCH(awk,TOKEN_PRINTF)) { if (__get_token(awk) == -1) return ASE_NULL; nde = __parse_print (awk, line, ASE_AWK_NDE_PRINTF); } else { nde = __parse_expression (awk, line); } if (nde == ASE_NULL) return ASE_NULL; /* check if a statement ends with a semicolon */ if (!MATCH(awk,TOKEN_SEMICOLON)) { if (nde != ASE_NULL) ase_awk_clrpt (awk, nde); if (MATCH(awk,TOKEN_EOF)) { ase_awk_seterror_old ( awk, ASE_AWK_EENDSRC, awk->token.prev.line, ASE_NULL); } else { ase_awk_seterror_old ( awk, ASE_AWK_ESCOLON, awk->token.prev.line, ASE_T("statement not terminated with a semicolon")); } return ASE_NULL; } /* eat up the semicolon and read in the next token */ if (__get_token(awk) == -1) { if (nde != ASE_NULL) ase_awk_clrpt (awk, nde); return ASE_NULL; } return nde; } static ase_awk_nde_t* __parse_expression (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* nde; if (awk->parse.depth.max.expr > 0 && awk->parse.depth.cur.expr >= awk->parse.depth.max.expr) { ase_awk_seterror_old ( awk, ASE_AWK_ERECUR, line, ASE_T("expression nested too deeply")); return ASE_NULL; } awk->parse.depth.cur.expr++; nde = __parse_expression0 (awk, line); awk->parse.depth.cur.expr--; return nde; } static ase_awk_nde_t* __parse_expression0 (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* x, * y; ase_awk_nde_ass_t* nde; int opcode; x = __parse_basic_expr (awk, line); if (x == ASE_NULL) return ASE_NULL; opcode = __assign_to_opcode (awk); if (opcode == -1) { /* no assignment operator found. */ return x; } ASE_AWK_ASSERT (awk, x->next == ASE_NULL); if (!__is_var(x) && x->type != ASE_AWK_NDE_POS) { ase_awk_clrpt (awk, x); ase_awk_seterror_old ( awk, ASE_AWK_EASSIGN, line, ASE_T("invalid assignment expression")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, x); return ASE_NULL; } /*y = __parse_basic_expr (awk);*/ y = __parse_expression (awk, awk->token.line); if (y == ASE_NULL) { ase_awk_clrpt (awk, x); return ASE_NULL; } nde = (ase_awk_nde_ass_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_ass_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, x); ase_awk_clrpt (awk, y); ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_ASS; nde->line = line; nde->next = ASE_NULL; nde->opcode = opcode; nde->left = x; nde->right = y; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_basic_expr (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* nde, * n1, * n2; nde = __parse_logical_or (awk, line); if (nde == ASE_NULL) return ASE_NULL; if (MATCH(awk,TOKEN_QUEST)) { ase_awk_nde_cnd_t* tmp; if (__get_token(awk) == -1) return ASE_NULL; /*n1 = __parse_basic_expr (awk, awk->token.line);*/ n1 = __parse_expression (awk, awk->token.line); if (n1 == ASE_NULL) { ase_awk_clrpt (awk, nde); return ASE_NULL; } if (!MATCH(awk,TOKEN_COLON)) { if (MATCH(awk,TOKEN_EOF)) { ase_awk_seterror_old ( awk, ASE_AWK_EENDSRC, awk->token.prev.line, ASE_NULL); } else { ase_awk_seterror_old ( awk, ASE_AWK_ECOLON, awk->token.line, ASE_NULL); } return ASE_NULL; } if (__get_token(awk) == -1) return ASE_NULL; /*n2 = __parse_basic_expr (awk, awk->token.line);*/ n2 = __parse_expression (awk, awk->token.line); if (n2 == ASE_NULL) { ase_awk_clrpt (awk, nde); ase_awk_clrpt (awk, n1); return ASE_NULL; } tmp = (ase_awk_nde_cnd_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_cnd_t)); if (tmp == ASE_NULL) { ase_awk_clrpt (awk, nde); ase_awk_clrpt (awk, n1); ase_awk_clrpt (awk, n2); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } tmp->type = ASE_AWK_NDE_CND; tmp->line = line; tmp->next = ASE_NULL; tmp->test = nde; tmp->left = n1; tmp->right = n2; nde = (ase_awk_nde_t*)tmp; } return nde; } static ase_awk_nde_t* __parse_binary_expr ( ase_awk_t* awk, ase_size_t line, const __binmap_t* binmap, ase_awk_nde_t*(*next_level_func)(ase_awk_t*,ase_size_t)) { ase_awk_nde_exp_t* nde; ase_awk_nde_t* left, * right; int opcode; left = next_level_func (awk, line); if (left == ASE_NULL) return ASE_NULL; while (1) { const __binmap_t* p = binmap; ase_bool_t matched = ase_false; while (p->token != TOKEN_EOF) { if (MATCH(awk,p->token)) { opcode = p->binop; matched = ase_true; break; } p++; } if (!matched) break; if (__get_token(awk) == -1) { ase_awk_clrpt (awk, left); return ASE_NULL; } right = next_level_func (awk, awk->token.line); if (right == ASE_NULL) { ase_awk_clrpt (awk, left); return ASE_NULL; } nde = (ase_awk_nde_exp_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_exp_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, right); ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_EXP_BIN; nde->line = line; nde->next = ASE_NULL; nde->opcode = opcode; nde->left = left; nde->right = right; left = (ase_awk_nde_t*)nde; } return left; } static ase_awk_nde_t* __parse_logical_or (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_LOR, ASE_AWK_BINOP_LOR }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_logical_and); } static ase_awk_nde_t* __parse_logical_and (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_LAND, ASE_AWK_BINOP_LAND }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_in); } static ase_awk_nde_t* __parse_in (ase_awk_t* awk, ase_size_t line) { /* static __binmap_t map[] = { { TOKEN_IN, ASE_AWK_BINOP_IN }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_regex_match); */ ase_awk_nde_exp_t* nde; ase_awk_nde_t* left, * right; ase_size_t line2; left = __parse_regex_match (awk, line); if (left == ASE_NULL) return ASE_NULL; while (1) { if (!MATCH(awk,TOKEN_IN)) break; if (__get_token(awk) == -1) { ase_awk_clrpt (awk, left); return ASE_NULL; } line2 = awk->token.line; right = __parse_regex_match (awk, line2); if (right == ASE_NULL) { ase_awk_clrpt (awk, left); return ASE_NULL; } if (!__is_plain_var(right)) { ase_awk_clrpt (awk, right); ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOTVAR, line2, ASE_T("right-hand side of the 'in' operator not a variable")); return ASE_NULL; } nde = (ase_awk_nde_exp_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_exp_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, right); ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_EXP_BIN; nde->line = line; nde->next = ASE_NULL; nde->opcode = ASE_AWK_BINOP_IN; nde->left = left; nde->right = right; left = (ase_awk_nde_t*)nde; } return left; } static ase_awk_nde_t* __parse_regex_match (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_TILDE, ASE_AWK_BINOP_MA }, { TOKEN_NM, ASE_AWK_BINOP_NM }, { TOKEN_EOF, 0 }, }; return __parse_binary_expr (awk, line, map, __parse_bitwise_or); } static ase_awk_nde_t* __parse_bitwise_or (ase_awk_t* awk, ase_size_t line) { if (awk->option & ASE_AWK_EXTIO) { return __parse_bitwise_or_with_extio (awk, line); } else { static __binmap_t map[] = { { TOKEN_BOR, ASE_AWK_BINOP_BOR }, { TOKEN_EOF, 0 } }; return __parse_binary_expr ( awk, line, map, __parse_bitwise_xor); } } static ase_awk_nde_t* __parse_bitwise_or_with_extio (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* left, * right; left = __parse_bitwise_xor (awk, line); if (left == ASE_NULL) return ASE_NULL; while (1) { int in_type; if (MATCH(awk,TOKEN_BOR)) in_type = ASE_AWK_IN_PIPE; else if (MATCH(awk,TOKEN_BORAND)) in_type = ASE_AWK_IN_COPROC; else break; if (__get_token(awk) == -1) { ase_awk_clrpt (awk, left); return ASE_NULL; } if (MATCH(awk,TOKEN_GETLINE)) { ase_awk_nde_getline_t* nde; ase_awk_nde_t* var = ASE_NULL; /* piped getline */ if (__get_token(awk) == -1) { ase_awk_clrpt (awk, left); return ASE_NULL; } /* TODO: is this correct? */ if (MATCH(awk,TOKEN_IDENT)) { /* command | getline var */ var = __parse_primary (awk, awk->token.line); if (var == ASE_NULL) { ase_awk_clrpt (awk, left); return ASE_NULL; } } nde = (ase_awk_nde_getline_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_getline_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_GETLINE; nde->line = line; nde->next = ASE_NULL; nde->var = var; nde->in_type = in_type; nde->in = left; left = (ase_awk_nde_t*)nde; } else { ase_awk_nde_exp_t* nde; if (in_type == ASE_AWK_IN_COPROC) { ase_awk_clrpt (awk, left); /* TODO: support coprocess */ ase_awk_seterror_old ( awk, ASE_AWK_EGETLINE, line, ASE_T("coprocess not supported by getline")); return ASE_NULL; } right = __parse_bitwise_xor (awk, awk->token.line); if (right == ASE_NULL) { ase_awk_clrpt (awk, left); return ASE_NULL; } nde = (ase_awk_nde_exp_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_exp_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, right); ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_EXP_BIN; nde->line = line; nde->next = ASE_NULL; nde->opcode = ASE_AWK_BINOP_BOR; nde->left = left; nde->right = right; left = (ase_awk_nde_t*)nde; } } return left; } static ase_awk_nde_t* __parse_bitwise_xor (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_BXOR, ASE_AWK_BINOP_BXOR }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_bitwise_and); } static ase_awk_nde_t* __parse_bitwise_and (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_BAND, ASE_AWK_BINOP_BAND }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_equality); } static ase_awk_nde_t* __parse_equality (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_EQ, ASE_AWK_BINOP_EQ }, { TOKEN_NE, ASE_AWK_BINOP_NE }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_relational); } static ase_awk_nde_t* __parse_relational (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_GT, ASE_AWK_BINOP_GT }, { TOKEN_GE, ASE_AWK_BINOP_GE }, { TOKEN_LT, ASE_AWK_BINOP_LT }, { TOKEN_LE, ASE_AWK_BINOP_LE }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_shift); } static ase_awk_nde_t* __parse_shift (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_LSHIFT, ASE_AWK_BINOP_LSHIFT }, { TOKEN_RSHIFT, ASE_AWK_BINOP_RSHIFT }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_concat); } static ase_awk_nde_t* __parse_concat (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_exp_t* nde; ase_awk_nde_t* left, * right; left = __parse_additive (awk, line); if (left == ASE_NULL) return ASE_NULL; while (1) { if (MATCH(awk,TOKEN_PERIOD)) { if ((awk->option & ASE_AWK_EXPLICIT) == 0) break; if (__get_token(awk) == -1) return ASE_NULL; } else if (MATCH(awk,TOKEN_LPAREN) || MATCH(awk,TOKEN_DOLLAR) || awk->token.type >= TOKEN_GETLINE) { /* TODO: is the check above sufficient? */ if ((awk->option & ASE_AWK_IMPLICIT) == 0) break; } else break; right = __parse_additive (awk, awk->token.line); if (right == ASE_NULL) { ase_awk_clrpt (awk, left); return ASE_NULL; } nde = (ase_awk_nde_exp_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_exp_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, left); ase_awk_clrpt (awk, right); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_EXP_BIN; nde->line = line; nde->next = ASE_NULL; nde->opcode = ASE_AWK_BINOP_CONCAT; nde->left = left; nde->right = right; left = (ase_awk_nde_t*)nde; } return left; } static ase_awk_nde_t* __parse_additive (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_PLUS, ASE_AWK_BINOP_PLUS }, { TOKEN_MINUS, ASE_AWK_BINOP_MINUS }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_multiplicative); } static ase_awk_nde_t* __parse_multiplicative (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_MUL, ASE_AWK_BINOP_MUL }, { TOKEN_DIV, ASE_AWK_BINOP_DIV }, { TOKEN_IDIV, ASE_AWK_BINOP_IDIV }, { TOKEN_MOD, ASE_AWK_BINOP_MOD }, /* { TOKEN_EXP, ASE_AWK_BINOP_EXP }, */ { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_unary); } static ase_awk_nde_t* __parse_unary (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_exp_t* nde; ase_awk_nde_t* left; int opcode; opcode = (MATCH(awk,TOKEN_PLUS))? ASE_AWK_UNROP_PLUS: (MATCH(awk,TOKEN_MINUS))? ASE_AWK_UNROP_MINUS: (MATCH(awk,TOKEN_NOT))? ASE_AWK_UNROP_NOT: (MATCH(awk,TOKEN_TILDE))? ASE_AWK_UNROP_BNOT: -1; /*if (opcode == -1) return __parse_increment (awk);*/ if (opcode == -1) return __parse_exponent (awk, line); if (__get_token(awk) == -1) return ASE_NULL; if (awk->parse.depth.max.expr > 0 && awk->parse.depth.cur.expr >= awk->parse.depth.max.expr) { ase_awk_seterror_old ( awk, ASE_AWK_ERECUR, awk->token.line, ASE_T("expression nested too deeply")); return ASE_NULL; } awk->parse.depth.cur.expr++; left = __parse_unary (awk, awk->token.line); awk->parse.depth.cur.expr--; if (left == ASE_NULL) return ASE_NULL; nde = (ase_awk_nde_exp_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_exp_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_EXP_UNR; nde->line = line; nde->next = ASE_NULL; nde->opcode = opcode; nde->left = left; nde->right = ASE_NULL; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_exponent (ase_awk_t* awk, ase_size_t line) { static __binmap_t map[] = { { TOKEN_EXP, ASE_AWK_BINOP_EXP }, { TOKEN_EOF, 0 } }; return __parse_binary_expr (awk, line, map, __parse_unary_exp); } static ase_awk_nde_t* __parse_unary_exp (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_exp_t* nde; ase_awk_nde_t* left; int opcode; opcode = (MATCH(awk,TOKEN_PLUS))? ASE_AWK_UNROP_PLUS: (MATCH(awk,TOKEN_MINUS))? ASE_AWK_UNROP_MINUS: (MATCH(awk,TOKEN_NOT))? ASE_AWK_UNROP_NOT: (MATCH(awk,TOKEN_TILDE))? ASE_AWK_UNROP_BNOT: -1; if (opcode == -1) return __parse_increment (awk, line); if (__get_token(awk) == -1) return ASE_NULL; if (awk->parse.depth.max.expr > 0 && awk->parse.depth.cur.expr >= awk->parse.depth.max.expr) { ase_awk_seterror_old ( awk, ASE_AWK_ERECUR, awk->token.line, ASE_T("expression nested too deeply")); return ASE_NULL; } awk->parse.depth.cur.expr++; left = __parse_unary (awk, awk->token.line); awk->parse.depth.cur.expr--; if (left == ASE_NULL) return ASE_NULL; nde = (ase_awk_nde_exp_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_exp_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_EXP_UNR; nde->line = line; nde->next = ASE_NULL; nde->opcode = opcode; nde->left = left; nde->right = ASE_NULL; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_increment (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_exp_t* nde; ase_awk_nde_t* left; int type, opcode, opcode1, opcode2; /* check for prefix increment operator */ opcode1 = MATCH(awk,TOKEN_PLUSPLUS)? ASE_AWK_INCOP_PLUS: MATCH(awk,TOKEN_MINUSMINUS)? ASE_AWK_INCOP_MINUS: -1; if (opcode1 != -1) { if (__get_token(awk) == -1) return ASE_NULL; } left = __parse_primary (awk, line); if (left == ASE_NULL) return ASE_NULL; /* check for postfix increment operator */ opcode2 = MATCH(awk,TOKEN_PLUSPLUS)? ASE_AWK_INCOP_PLUS: MATCH(awk,TOKEN_MINUSMINUS)? ASE_AWK_INCOP_MINUS: -1; if (opcode1 != -1 && opcode2 != -1) { /* both prefix and postfix increment operator. * not allowed */ ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ELVALUE, line, ASE_T("both prefix and postfix increment/decrement operator present")); return ASE_NULL; } else if (opcode1 == -1 && opcode2 == -1) { /* no increment operators */ return left; } else if (opcode1 != -1) { /* prefix increment operator */ type = ASE_AWK_NDE_EXP_INCPRE; opcode = opcode1; } else if (opcode2 != -1) { /* postfix increment operator */ type = ASE_AWK_NDE_EXP_INCPST; opcode = opcode2; if (__get_token(awk) == -1) return ASE_NULL; } nde = (ase_awk_nde_exp_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_exp_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, left); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = type; nde->line = line; nde->next = ASE_NULL; nde->opcode = opcode; nde->left = left; nde->right = ASE_NULL; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_primary (ase_awk_t* awk, ase_size_t line) { if (MATCH(awk,TOKEN_IDENT)) { return __parse_primary_ident (awk, line); } else if (MATCH(awk,TOKEN_INT)) { ase_awk_nde_int_t* nde; nde = (ase_awk_nde_int_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_int_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_INT; nde->line = line; nde->next = ASE_NULL; nde->val = ase_awk_strxtolong (awk, ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), 0, ASE_NULL); nde->str = ase_strxdup ( ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), &awk->prmfns.mmgr); if (nde->str == ASE_NULL) { ASE_AWK_FREE (awk, nde); return ASE_NULL; } nde->len = ASE_STR_LEN(&awk->token.name); ASE_AWK_ASSERT (awk, ASE_STR_LEN(&awk->token.name) == ase_strlen(ASE_STR_BUF(&awk->token.name))); if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, nde->str); ASE_AWK_FREE (awk, nde); return ASE_NULL; } return (ase_awk_nde_t*)nde; } else if (MATCH(awk,TOKEN_REAL)) { ase_awk_nde_real_t* nde; nde = (ase_awk_nde_real_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_real_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_REAL; nde->line = line; nde->next = ASE_NULL; nde->val = ase_awk_strxtoreal (awk, ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), ASE_NULL); nde->str = ase_strxdup ( ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), &awk->prmfns.mmgr); if (nde->str == ASE_NULL) { ASE_AWK_FREE (awk, nde); return ASE_NULL; } nde->len = ASE_STR_LEN(&awk->token.name); ASE_AWK_ASSERT (awk, ASE_STR_LEN(&awk->token.name) == ase_strlen(ASE_STR_BUF(&awk->token.name))); if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, nde->str); ASE_AWK_FREE (awk, nde); return ASE_NULL; } return (ase_awk_nde_t*)nde; } else if (MATCH(awk,TOKEN_STR)) { ase_awk_nde_str_t* nde; nde = (ase_awk_nde_str_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_str_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_STR; nde->line = line; nde->next = ASE_NULL; nde->len = ASE_STR_LEN(&awk->token.name); nde->buf = ase_strxdup ( ASE_STR_BUF(&awk->token.name), nde->len, &awk->prmfns.mmgr); if (nde->buf == ASE_NULL) { ASE_AWK_FREE (awk, nde); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, nde->buf); ASE_AWK_FREE (awk, nde); return ASE_NULL; } return (ase_awk_nde_t*)nde; } else if (MATCH(awk,TOKEN_DIV)) { ase_awk_nde_rex_t* nde; int errnum; /* the regular expression is tokenized here because * of the context-sensitivity of the slash symbol */ SET_TOKEN_TYPE (awk, TOKEN_REX); ase_str_clear (&awk->token.name); if (__get_rexstr (awk) == -1) return ASE_NULL; ASE_AWK_ASSERT (awk, MATCH(awk,TOKEN_REX)); nde = (ase_awk_nde_rex_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_rex_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_REX; nde->line = line; nde->next = ASE_NULL; nde->len = ASE_STR_LEN(&awk->token.name); nde->buf = ase_strxdup ( ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), &awk->prmfns.mmgr); if (nde->buf == ASE_NULL) { ASE_AWK_FREE (awk, nde); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->code = ase_awk_buildrex (awk, ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), &errnum); if (nde->code == ASE_NULL) { ASE_AWK_FREE (awk, nde->buf); ASE_AWK_FREE (awk, nde); ase_awk_seterror_old ( awk, errnum, line, ASE_NULL); return ASE_NULL; } if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, nde->buf); ASE_AWK_FREE (awk, nde->code); ASE_AWK_FREE (awk, nde); return ASE_NULL; } return (ase_awk_nde_t*)nde; } else if (MATCH(awk,TOKEN_DOLLAR)) { ase_awk_nde_pos_t* nde; ase_awk_nde_t* prim; if (__get_token(awk)) return ASE_NULL; prim = __parse_primary (awk, awk->token.line); if (prim == ASE_NULL) return ASE_NULL; nde = (ase_awk_nde_pos_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_pos_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, prim); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_POS; nde->line = line; nde->next = ASE_NULL; nde->val = prim; return (ase_awk_nde_t*)nde; } else if (MATCH(awk,TOKEN_LPAREN)) { ase_awk_nde_t* nde; ase_awk_nde_t* last; /* eat up the left parenthesis */ if (__get_token(awk) == -1) return ASE_NULL; /* parse the sub-expression inside the parentheses */ nde = __parse_expression (awk, awk->token.line); if (nde == ASE_NULL) return ASE_NULL; /* parse subsequent expressions separated by a comma, if any */ last = nde; ASE_AWK_ASSERT (awk, last->next == ASE_NULL); while (MATCH(awk,TOKEN_COMMA)) { ase_awk_nde_t* tmp; if (__get_token(awk) == -1) { ase_awk_clrpt (awk, nde); return ASE_NULL; } tmp = __parse_expression (awk, awk->token.line); if (tmp == ASE_NULL) { ase_awk_clrpt (awk, nde); return ASE_NULL; } ASE_AWK_ASSERT (awk, tmp->next == ASE_NULL); last->next = tmp; last = tmp; } /* ----------------- */ /* check for the closing parenthesis */ if (!MATCH(awk,TOKEN_RPAREN)) { ase_awk_clrpt (awk, nde); SET_ERROR_0 (awk, ASE_AWK_ERPAREN, ASE_T("right parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, nde); return ASE_NULL; } /* check if it is a chained node */ if (nde->next != ASE_NULL) { /* if so, it is a expression group */ /* (expr1, expr2, expr2) */ ase_awk_nde_grp_t* tmp; if ((awk->parse.id.stmnt != TOKEN_PRINT && awk->parse.id.stmnt != TOKEN_PRINTF) || awk->parse.depth.cur.expr != 1) { if (!MATCH(awk,TOKEN_IN)) { ase_awk_clrpt (awk, nde); SET_ERROR_0 (awk, ASE_AWK_EIN, ASE_T("'in' expected in place of '%.*s'")); return ASE_NULL; } } tmp = (ase_awk_nde_grp_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_grp_t)); if (tmp == ASE_NULL) { ase_awk_clrpt (awk, nde); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } tmp->type = ASE_AWK_NDE_GRP; tmp->line = line; tmp->next = ASE_NULL; tmp->body = nde; nde = (ase_awk_nde_t*)tmp; } /* ----------------- */ return nde; } else if (MATCH(awk,TOKEN_GETLINE)) { ase_awk_nde_getline_t* nde; ase_awk_nde_t* var = ASE_NULL; ase_awk_nde_t* in = ASE_NULL; if (__get_token(awk) == -1) return ASE_NULL; if (MATCH(awk,TOKEN_IDENT)) { /* getline var */ var = __parse_primary (awk, awk->token.line); if (var == ASE_NULL) return ASE_NULL; } if (MATCH(awk, TOKEN_LT)) { /* getline [var] < file */ if (__get_token(awk) == -1) { if (var != ASE_NULL) ase_awk_clrpt (awk, var); return ASE_NULL; } /* TODO: is this correct? */ /*in = __parse_expression (awk);*/ in = __parse_primary (awk, awk->token.line); if (in == ASE_NULL) { if (var != ASE_NULL) ase_awk_clrpt (awk, var); return ASE_NULL; } } nde = (ase_awk_nde_getline_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_getline_t)); if (nde == ASE_NULL) { if (var != ASE_NULL) ase_awk_clrpt (awk, var); if (in != ASE_NULL) ase_awk_clrpt (awk, in); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_GETLINE; nde->line = line; nde->next = ASE_NULL; nde->var = var; nde->in_type = (in == ASE_NULL)? ASE_AWK_IN_CONSOLE: ASE_AWK_IN_FILE; nde->in = in; return (ase_awk_nde_t*)nde; } /* valid expression introducer is expected */ ase_awk_seterror_old ( awk, ASE_AWK_EEXPRES, (MATCH(awk,TOKEN_EOF)? awk->token.prev.line: line), ASE_T("invalid expression")); return ASE_NULL; } static ase_awk_nde_t* __parse_primary_ident (ase_awk_t* awk, ase_size_t line) { ase_char_t* name_dup; ase_size_t name_len; ase_awk_bfn_t* bfn; ASE_AWK_ASSERT (awk, MATCH(awk,TOKEN_IDENT)); name_dup = ase_strxdup ( ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name), &awk->prmfns.mmgr); if (name_dup == ASE_NULL) { ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } name_len = ASE_STR_LEN(&awk->token.name); if (__get_token(awk) == -1) { ASE_AWK_FREE (awk, name_dup); return ASE_NULL; } /* check if name_dup is a built-in function name */ bfn = ase_awk_getbfn (awk, name_dup, name_len); if (bfn != ASE_NULL) { ase_awk_nde_t* nde; if (!MATCH(awk,TOKEN_LPAREN)) { /* built-in function should be in the form * of the function call */ awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("function name '%.*s' without a left parenthesis"), name_len, name_dup); ASE_AWK_FREE (awk, name_dup); ase_awk_seterror_old (awk, ASE_AWK_ELPAREN, line, awk->errmsg); return ASE_NULL; } ASE_AWK_FREE (awk, name_dup); nde = __parse_fncall (awk, ASE_NULL, 0, bfn, line); return (ase_awk_nde_t*)nde; } /* now we know that name_dup is a normal identifier. */ if (MATCH(awk,TOKEN_LBRACK)) { ase_awk_nde_t* nde; nde = __parse_hashidx (awk, name_dup, name_len, line); if (nde == ASE_NULL) ASE_AWK_FREE (awk, name_dup); return (ase_awk_nde_t*)nde; } else if (MATCH(awk,TOKEN_LPAREN)) { /* function call */ ase_awk_nde_t* nde; nde = __parse_fncall (awk, name_dup, name_len, ASE_NULL, line); if (nde == ASE_NULL) ASE_AWK_FREE (awk, name_dup); return (ase_awk_nde_t*)nde; } else { /* normal variable */ ase_awk_nde_var_t* nde; ase_size_t idxa; nde = (ase_awk_nde_var_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_var_t)); if (nde == ASE_NULL) { ASE_AWK_FREE (awk, name_dup); ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } /* search the parameter name list */ idxa = ase_awk_tab_find ( &awk->parse.params, 0, name_dup, name_len); if (idxa != (ase_size_t)-1) { nde->type = ASE_AWK_NDE_ARG; nde->line = line; nde->next = ASE_NULL; /*nde->id.name = ASE_NULL;*/ nde->id.name = name_dup; nde->id.name_len = name_len; nde->id.idxa = idxa; nde->idx = ASE_NULL; return (ase_awk_nde_t*)nde; } /* search the local variable list */ idxa = ase_awk_tab_rrfind ( &awk->parse.locals, 0, name_dup, name_len); if (idxa != (ase_size_t)-1) { nde->type = ASE_AWK_NDE_LOCAL; nde->line = line; nde->next = ASE_NULL; /*nde->id.name = ASE_NULL;*/ nde->id.name = name_dup; nde->id.name_len = name_len; nde->id.idxa = idxa; nde->idx = ASE_NULL; return (ase_awk_nde_t*)nde; } /* search the global variable list */ idxa = ase_awk_tab_rrfind ( &awk->parse.globals, 0, name_dup, name_len); if (idxa != (ase_size_t)-1) { nde->type = ASE_AWK_NDE_GLOBAL; nde->line = line; nde->next = ASE_NULL; /*nde->id.name = ASE_NULL;*/ nde->id.name = name_dup; nde->id.name_len = name_len; nde->id.idxa = idxa; nde->idx = ASE_NULL; return (ase_awk_nde_t*)nde; } if (awk->option & ASE_AWK_IMPLICIT) { nde->type = ASE_AWK_NDE_NAMED; nde->line = line; nde->next = ASE_NULL; nde->id.name = name_dup; nde->id.name_len = name_len; nde->id.idxa = (ase_size_t)-1; nde->idx = ASE_NULL; return (ase_awk_nde_t*)nde; } awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("undefined identifier '%.*s'"), name_len, name_dup); /* undefined variable */ ASE_AWK_FREE (awk, name_dup); ASE_AWK_FREE (awk, nde); ase_awk_seterror_old (awk, ASE_AWK_EUNDEF, line, awk->errmsg); return ASE_NULL; } } static ase_awk_nde_t* __parse_hashidx ( ase_awk_t* awk, ase_char_t* name, ase_size_t name_len, ase_size_t line) { ase_awk_nde_t* idx, * tmp, * last; ase_awk_nde_var_t* nde; ase_size_t idxa; idx = ASE_NULL; last = ASE_NULL; do { if (__get_token(awk) == -1) { if (idx != ASE_NULL) ase_awk_clrpt (awk, idx); return ASE_NULL; } tmp = __parse_expression (awk, awk->token.line); if (tmp == ASE_NULL) { if (idx != ASE_NULL) ase_awk_clrpt (awk, idx); return ASE_NULL; } if (idx == ASE_NULL) { ASE_AWK_ASSERT (awk, last == ASE_NULL); idx = tmp; last = tmp; } else { last->next = tmp; last = tmp; } } while (MATCH(awk,TOKEN_COMMA)); ASE_AWK_ASSERT (awk, idx != ASE_NULL); if (!MATCH(awk,TOKEN_RBRACK)) { ase_awk_clrpt (awk, idx); SET_ERROR_0 (awk, ASE_AWK_ERBRACK, ASE_T("right bracket expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, idx); return ASE_NULL; } nde = (ase_awk_nde_var_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_var_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, idx); ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } /* search the parameter name list */ idxa = ase_awk_tab_find (&awk->parse.params, 0, name, name_len); if (idxa != (ase_size_t)-1) { nde->type = ASE_AWK_NDE_ARGIDX; nde->line = line; nde->next = ASE_NULL; /*nde->id.name = ASE_NULL; */ nde->id.name = name; nde->id.name_len = name_len; nde->id.idxa = idxa; nde->idx = idx; return (ase_awk_nde_t*)nde; } /* search the local variable list */ idxa = ase_awk_tab_rrfind(&awk->parse.locals, 0, name, name_len); if (idxa != (ase_size_t)-1) { nde->type = ASE_AWK_NDE_LOCALIDX; nde->line = line; nde->next = ASE_NULL; /*nde->id.name = ASE_NULL; */ nde->id.name = name; nde->id.name_len = name_len; nde->id.idxa = idxa; nde->idx = idx; return (ase_awk_nde_t*)nde; } /* search the global variable list */ idxa = ase_awk_tab_rrfind(&awk->parse.globals, 0, name, name_len); if (idxa != (ase_size_t)-1) { nde->type = ASE_AWK_NDE_GLOBALIDX; nde->line = line; nde->next = ASE_NULL; /*nde->id.name = ASE_NULL;*/ nde->id.name = name; nde->id.name_len = name_len; nde->id.idxa = idxa; nde->idx = idx; return (ase_awk_nde_t*)nde; } if (awk->option & ASE_AWK_IMPLICIT) { nde->type = ASE_AWK_NDE_NAMEDIDX; nde->line = line; nde->next = ASE_NULL; nde->id.name = name; nde->id.name_len = name_len; nde->id.idxa = (ase_size_t)-1; nde->idx = idx; return (ase_awk_nde_t*)nde; } /* undefined variable */ ase_awk_clrpt (awk, idx); ASE_AWK_FREE (awk, nde); awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("undefined identifier '%.*s'"), name_len, name); ase_awk_seterror_old (awk, ASE_AWK_EUNDEF, line, awk->errmsg); return ASE_NULL; } static ase_awk_nde_t* __parse_fncall ( ase_awk_t* awk, ase_char_t* name, ase_size_t name_len, ase_awk_bfn_t* bfn, ase_size_t line) { ase_awk_nde_t* head, * curr, * nde; ase_awk_nde_call_t* call; ase_size_t nargs; if (__get_token(awk) == -1) return ASE_NULL; head = curr = ASE_NULL; nargs = 0; if (MATCH(awk,TOKEN_RPAREN)) { /* no parameters to the function call */ if (__get_token(awk) == -1) return ASE_NULL; } else { /* parse function parameters */ while (1) { nde = __parse_expression (awk, awk->token.line); if (nde == ASE_NULL) { if (head != ASE_NULL) ase_awk_clrpt (awk, head); return ASE_NULL; } if (head == ASE_NULL) head = nde; else curr->next = nde; curr = nde; nargs++; if (MATCH(awk,TOKEN_RPAREN)) { if (__get_token(awk) == -1) { if (head != ASE_NULL) ase_awk_clrpt (awk, head); return ASE_NULL; } break; } if (!MATCH(awk,TOKEN_COMMA)) { if (head != ASE_NULL) ase_awk_clrpt (awk, head); SET_ERROR_0 (awk, ASE_AWK_ECOMMA, ASE_T("comma expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { if (head != ASE_NULL) ase_awk_clrpt (awk, head); return ASE_NULL; } } } call = (ase_awk_nde_call_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_call_t)); if (call == ASE_NULL) { if (head != ASE_NULL) ase_awk_clrpt (awk, head); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } if (bfn != ASE_NULL) { call->type = ASE_AWK_NDE_BFN; call->line = line; call->next = ASE_NULL; /*call->what.bfn = bfn; */ call->what.bfn.name.ptr = bfn->name.ptr; call->what.bfn.name.len = bfn->name.len; call->what.bfn.arg.min = bfn->arg.min; call->what.bfn.arg.max = bfn->arg.max; call->what.bfn.arg.spec = bfn->arg.spec; call->what.bfn.handler = bfn->handler; call->args = head; call->nargs = nargs; } else { call->type = ASE_AWK_NDE_AFN; call->line = line; call->next = ASE_NULL; call->what.afn.name.ptr = name; call->what.afn.name.len = name_len; call->args = head; call->nargs = nargs; } return (ase_awk_nde_t*)call; } static ase_awk_nde_t* __parse_if (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* test; ase_awk_nde_t* then_part; ase_awk_nde_t* else_part; ase_awk_nde_if_t* nde; if (!MATCH(awk,TOKEN_LPAREN)) { SET_ERROR_0 ( awk, ASE_AWK_ELPAREN, ASE_T("left parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) return ASE_NULL; test = __parse_expression (awk, awk->token.line); if (test == ASE_NULL) return ASE_NULL; if (!MATCH(awk,TOKEN_RPAREN)) { ase_awk_clrpt (awk, test); SET_ERROR_0 ( awk, ASE_AWK_ERPAREN, ASE_T("right parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, test); return ASE_NULL; } then_part = __parse_statement (awk, awk->token.line); if (then_part == ASE_NULL) { ase_awk_clrpt (awk, test); return ASE_NULL; } if (MATCH(awk,TOKEN_ELSE)) { if (__get_token(awk) == -1) { ase_awk_clrpt (awk, then_part); ase_awk_clrpt (awk, test); return ASE_NULL; } else_part = __parse_statement (awk, awk->token.prev.line); if (else_part == ASE_NULL) { ase_awk_clrpt (awk, then_part); ase_awk_clrpt (awk, test); return ASE_NULL; } } else else_part = ASE_NULL; nde = (ase_awk_nde_if_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_if_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, else_part); ase_awk_clrpt (awk, then_part); ase_awk_clrpt (awk, test); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_IF; nde->line = line; nde->next = ASE_NULL; nde->test = test; nde->then_part = then_part; nde->else_part = else_part; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_while (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* test, * body; ase_awk_nde_while_t* nde; if (!MATCH(awk,TOKEN_LPAREN)) { SET_ERROR_0 ( awk, ASE_AWK_ELPAREN, ASE_T("left parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) return ASE_NULL; test = __parse_expression (awk, awk->token.line); if (test == ASE_NULL) return ASE_NULL; if (!MATCH(awk,TOKEN_RPAREN)) { ase_awk_clrpt (awk, test); SET_ERROR_0 ( awk, ASE_AWK_ERPAREN, ASE_T("right parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, test); return ASE_NULL; } body = __parse_statement (awk, awk->token.line); if (body == ASE_NULL) { ase_awk_clrpt (awk, test); return ASE_NULL; } nde = (ase_awk_nde_while_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_while_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, body); ase_awk_clrpt (awk, test); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_WHILE; nde->line = line; nde->next = ASE_NULL; nde->test = test; nde->body = body; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_for (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* init, * test, * incr, * body; ase_awk_nde_for_t* nde; ase_awk_nde_foreach_t* nde2; if (!MATCH(awk,TOKEN_LPAREN)) { SET_ERROR_0 ( awk, ASE_AWK_ELPAREN, ASE_T("left parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) return ASE_NULL; if (MATCH(awk,TOKEN_SEMICOLON)) init = ASE_NULL; else { /* this line is very ugly. it checks the entire next * expression or the first element in the expression * is wrapped by a parenthesis */ int no_foreach = MATCH(awk,TOKEN_LPAREN); init = __parse_expression (awk, awk->token.line); if (init == ASE_NULL) return ASE_NULL; if (!no_foreach && init->type == ASE_AWK_NDE_EXP_BIN && ((ase_awk_nde_exp_t*)init)->opcode == ASE_AWK_BINOP_IN && __is_plain_var(((ase_awk_nde_exp_t*)init)->left)) { /* switch to foreach */ if (!MATCH(awk,TOKEN_RPAREN)) { ase_awk_clrpt (awk, init); SET_ERROR_0 ( awk, ASE_AWK_ERPAREN, ASE_T("right parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, init); return ASE_NULL; } body = __parse_statement (awk, awk->token.line); if (body == ASE_NULL) { ase_awk_clrpt (awk, init); return ASE_NULL; } nde2 = (ase_awk_nde_foreach_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_foreach_t)); if (nde2 == ASE_NULL) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, body); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde2->type = ASE_AWK_NDE_FOREACH; nde2->line = line; nde2->next = ASE_NULL; nde2->test = init; nde2->body = body; return (ase_awk_nde_t*)nde2; } if (!MATCH(awk,TOKEN_SEMICOLON)) { ase_awk_clrpt (awk, init); SET_ERROR_0 (awk, ASE_AWK_ESCOLON, ASE_T("semicolon expected in place of '%.*s'")); return ASE_NULL; } } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, init); return ASE_NULL; } if (MATCH(awk,TOKEN_SEMICOLON)) test = ASE_NULL; else { test = __parse_expression (awk, awk->token.line); if (test == ASE_NULL) { ase_awk_clrpt (awk, init); return ASE_NULL; } if (!MATCH(awk,TOKEN_SEMICOLON)) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, test); SET_ERROR_0 (awk, ASE_AWK_ESCOLON, ASE_T("semicolon expected in place of '%.*s'")); return ASE_NULL; } } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, test); return ASE_NULL; } if (MATCH(awk,TOKEN_RPAREN)) incr = ASE_NULL; else { incr = __parse_expression (awk, awk->token.line); if (incr == ASE_NULL) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, test); return ASE_NULL; } if (!MATCH(awk,TOKEN_RPAREN)) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, test); ase_awk_clrpt (awk, incr); SET_ERROR_0 ( awk, ASE_AWK_ERPAREN, ASE_T("right parenthesis expected in place of '%.*s'")); return ASE_NULL; } } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, test); ase_awk_clrpt (awk, incr); return ASE_NULL; } body = __parse_statement (awk, awk->token.line); if (body == ASE_NULL) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, test); ase_awk_clrpt (awk, incr); return ASE_NULL; } nde = (ase_awk_nde_for_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_for_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, init); ase_awk_clrpt (awk, test); ase_awk_clrpt (awk, incr); ase_awk_clrpt (awk, body); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_FOR; nde->line = line; nde->next = ASE_NULL; nde->init = init; nde->test = test; nde->incr = incr; nde->body = body; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_dowhile (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_t* test, * body; ase_awk_nde_while_t* nde; ASE_AWK_ASSERT (awk, awk->token.prev.type == TOKEN_DO); body = __parse_statement (awk, awk->token.line); if (body == ASE_NULL) return ASE_NULL; if (!MATCH(awk,TOKEN_WHILE)) { ase_awk_clrpt (awk, body); SET_ERROR_0 ( awk, ASE_AWK_EWHILE, ASE_T("'while' expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, body); return ASE_NULL; } if (!MATCH(awk,TOKEN_LPAREN)) { ase_awk_clrpt (awk, body); SET_ERROR_0 ( awk, ASE_AWK_ELPAREN, ASE_T("left parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, body); return ASE_NULL; } test = __parse_expression (awk, awk->token.line); if (test == ASE_NULL) { ase_awk_clrpt (awk, body); return ASE_NULL; } if (!MATCH(awk,TOKEN_RPAREN)) { ase_awk_clrpt (awk, body); ase_awk_clrpt (awk, test); SET_ERROR_0 ( awk, ASE_AWK_ERPAREN, ASE_T("right parenthesis expected in place of '%.*s'")); return ASE_NULL; } if (__get_token(awk) == -1) { ase_awk_clrpt (awk, body); ase_awk_clrpt (awk, test); return ASE_NULL; } nde = (ase_awk_nde_while_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_while_t)); if (nde == ASE_NULL) { ase_awk_clrpt (awk, body); ase_awk_clrpt (awk, test); ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_DOWHILE; nde->line = line; nde->next = ASE_NULL; nde->test = test; nde->body = body; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_break (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_break_t* nde; ASE_AWK_ASSERT (awk, awk->token.prev.type == TOKEN_BREAK); if (awk->parse.depth.cur.loop <= 0) { ase_awk_seterror_old ( awk, ASE_AWK_EBREAK, line, ASE_T("break statement outside a loop")); return ASE_NULL; } nde = (ase_awk_nde_break_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_break_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_BREAK; nde->line = line; nde->next = ASE_NULL; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_continue (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_continue_t* nde; ASE_AWK_ASSERT (awk, awk->token.prev.type == TOKEN_CONTINUE); if (awk->parse.depth.cur.loop <= 0) { ase_awk_seterror_old ( awk, ASE_AWK_ECONTINUE, line, ASE_T("continue statement outside a loop")); return ASE_NULL; } nde = (ase_awk_nde_continue_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_continue_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_CONTINUE; nde->line = line; nde->next = ASE_NULL; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_return (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_return_t* nde; ase_awk_nde_t* val; ASE_AWK_ASSERT (awk, awk->token.prev.type == TOKEN_RETURN); nde = (ase_awk_nde_return_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_return_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_RETURN; nde->line = line; nde->next = ASE_NULL; if (MATCH(awk,TOKEN_SEMICOLON)) { /* no return value */ val = ASE_NULL; } else { val = __parse_expression (awk, awk->token.line); if (val == ASE_NULL) { ASE_AWK_FREE (awk, nde); return ASE_NULL; } } nde->val = val; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_exit (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_exit_t* nde; ase_awk_nde_t* val; ASE_AWK_ASSERT (awk, awk->token.prev.type == TOKEN_EXIT); nde = (ase_awk_nde_exit_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_exit_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_EXIT; nde->line = line; nde->next = ASE_NULL; if (MATCH(awk,TOKEN_SEMICOLON)) { /* no exit code */ val = ASE_NULL; } else { val = __parse_expression (awk, awk->token.line); if (val == ASE_NULL) { ASE_AWK_FREE (awk, nde); return ASE_NULL; } } nde->val = val; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_next (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_next_t* nde; ASE_AWK_ASSERT (awk, awk->token.prev.type == TOKEN_NEXT); if (awk->parse.id.block == PARSE_BEGIN_BLOCK) { ase_awk_seterror_old ( awk, ASE_AWK_ENEXT, line, ASE_T("next statement in BEGIN block")); return ASE_NULL; } if (awk->parse.id.block == PARSE_END_BLOCK) { ase_awk_seterror_old ( awk, ASE_AWK_ENEXT, line, ASE_T("next statement in END block")); return ASE_NULL; } nde = (ase_awk_nde_next_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_next_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_NEXT; nde->line = line; nde->next = ASE_NULL; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_nextfile (ase_awk_t* awk, ase_size_t line, int out) { ase_awk_nde_nextfile_t* nde; if (awk->parse.id.block == PARSE_BEGIN_BLOCK) { ase_awk_seterror_old ( awk, ASE_AWK_ENEXTFILE, line, ASE_T("nextfile statement in BEGIN block")); return ASE_NULL; } if (awk->parse.id.block == PARSE_END_BLOCK) { ase_awk_seterror_old ( awk, ASE_AWK_ENEXTFILE, line, ASE_T("nextfile statement in END block")); return ASE_NULL; } nde = (ase_awk_nde_nextfile_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_nextfile_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_NEXTFILE; nde->line = line; nde->next = ASE_NULL; nde->out = out; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_delete (ase_awk_t* awk, ase_size_t line) { ase_awk_nde_delete_t* nde; ase_awk_nde_t* var; ASE_AWK_ASSERT (awk, awk->token.prev.type == TOKEN_DELETE); if (!MATCH(awk,TOKEN_IDENT)) { SET_ERROR_0 ( awk, ASE_AWK_EIDENT, ASE_T("identifier expected in place of '%.*s'")); return ASE_NULL; } var = __parse_primary_ident (awk, awk->token.line); if (var == ASE_NULL) return ASE_NULL; if (!__is_var (var)) { /* a normal identifier is expected */ ase_awk_clrpt (awk, var); ase_awk_seterror_old (awk, ASE_AWK_EIDENT, line, ASE_T("'delete' not followed by a normal variable")); return ASE_NULL; } nde = (ase_awk_nde_delete_t*) ASE_AWK_MALLOC ( awk, ASE_SIZEOF(ase_awk_nde_delete_t)); if (nde == ASE_NULL) { ase_awk_seterror_old ( awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } nde->type = ASE_AWK_NDE_DELETE; nde->line = line; nde->next = ASE_NULL; nde->var = var; return (ase_awk_nde_t*)nde; } static ase_awk_nde_t* __parse_print (ase_awk_t* awk, ase_size_t line, int type) { ase_awk_nde_print_t* nde; ase_awk_nde_t* args = ASE_NULL; ase_awk_nde_t* out = ASE_NULL; int out_type; if (!MATCH(awk,TOKEN_SEMICOLON) && !MATCH(awk,TOKEN_GT) && !MATCH(awk,TOKEN_RSHIFT) && !MATCH(awk,TOKEN_BOR) && !MATCH(awk,TOKEN_BORAND)) { ase_awk_nde_t* args_tail; ase_awk_nde_t* tail_prev; args = __parse_expression (awk, awk->token.line); if (args == ASE_NULL) return ASE_NULL; args_tail = args; tail_prev = ASE_NULL; if (args->type != ASE_AWK_NDE_GRP) { /* args->type == ASE_AWK_NDE_GRP when print (a, b, c) * args->type != ASE_AWK_NDE_GRP when print a, b, c */ while (MATCH(awk,TOKEN_COMMA)) { if (__get_token(awk) == -1) { ase_awk_clrpt (awk, args); return ASE_NULL; } args_tail->next = __parse_expression (awk, awk->token.line); if (args_tail->next == ASE_NULL) { ase_awk_clrpt (awk, args); return ASE_NULL; } tail_prev = args_tail; args_tail = args_tail->next; } } /* print 1 > 2 would print 1 to the file named 2. * print (1 > 2) would print (1 > 2) on the console */ if (awk->token.prev.type != TOKEN_RPAREN && args_tail->type == ASE_AWK_NDE_EXP_BIN) { ase_awk_nde_exp_t* ep = (ase_awk_nde_exp_t*)args_tail; if (ep->opcode == ASE_AWK_BINOP_GT) { ase_awk_nde_t* tmp = args_tail; if (tail_prev != ASE_NULL) tail_prev->next = ep->left; else args = ep->left; out = ep->right; out_type = ASE_AWK_OUT_FILE; ASE_AWK_FREE (awk, tmp); } else if (ep->opcode == ASE_AWK_BINOP_RSHIFT) { ase_awk_nde_t* tmp = args_tail; if (tail_prev != ASE_NULL) tail_prev->next = ep->left; else args = ep->left; out = ep->right; out_type = ASE_AWK_OUT_FILE_APPEND; ASE_AWK_FREE (awk, tmp); } else if (ep->opcode == ASE_AWK_BINOP_BOR) { ase_awk_nde_t* tmp = args_tail; if (tail_prev != ASE_NULL) tail_prev->next = ep->left; else args = ep->left; out = ep->right; out_type = ASE_AWK_OUT_PIPE; ASE_AWK_FREE (awk, tmp); } } } if (out == ASE_NULL) { out_type = MATCH(awk,TOKEN_GT)? ASE_AWK_OUT_FILE: MATCH(awk,TOKEN_RSHIFT)? ASE_AWK_OUT_FILE_APPEND: MATCH(awk,TOKEN_BOR)? ASE_AWK_OUT_PIPE: MATCH(awk,TOKEN_BORAND)? ASE_AWK_OUT_COPROC: ASE_AWK_OUT_CONSOLE; if (out_type != ASE_AWK_OUT_CONSOLE) { if (__get_token(awk) == -1) { if (args != ASE_NULL) ase_awk_clrpt (awk, args); return ASE_NULL; } out = __parse_expression (awk, awk->token.line); if (out == ASE_NULL) { if (args != ASE_NULL) ase_awk_clrpt (awk, args); return ASE_NULL; } } } nde = (ase_awk_nde_print_t*) ASE_AWK_MALLOC (awk, ASE_SIZEOF(ase_awk_nde_print_t)); if (nde == ASE_NULL) { if (args != ASE_NULL) ase_awk_clrpt (awk, args); if (out != ASE_NULL) ase_awk_clrpt (awk, out); ase_awk_seterror_old (awk, ASE_AWK_ENOMEM, line, ASE_NULL); return ASE_NULL; } ASE_AWK_ASSERTX (awk, type == ASE_AWK_NDE_PRINT || type == ASE_AWK_NDE_PRINTF, "the node type should be either ASE_AWK_NDE_PRINT or ASE_AWK_NDE_PRINTF"); if (type == ASE_AWK_NDE_PRINTF && args == ASE_NULL) { if (out != ASE_NULL) ase_awk_clrpt (awk, out); ase_awk_seterror_old ( awk, ASE_AWK_EPRINTFARG, line, ASE_T("printf not followed by any arguments")); return ASE_NULL; } nde->type = type; nde->line = line; nde->next = ASE_NULL; nde->args = args; nde->out_type = out_type; nde->out = out; return (ase_awk_nde_t*)nde; } static int __get_token (ase_awk_t* awk) { ase_cint_t c; ase_size_t line; int n; line = awk->token.line; awk->token.prev.type = awk->token.type; awk->token.prev.line = awk->token.line; awk->token.prev.column = awk->token.column; do { if (__skip_spaces(awk) == -1) return -1; if ((n = __skip_comment(awk)) == -1) return -1; } while (n == 1); ase_str_clear (&awk->token.name); awk->token.line = awk->src.lex.line; awk->token.column = awk->src.lex.column; if (line != 0 && (awk->option & ASE_AWK_BLOCKLESS) && (awk->parse.id.block == PARSE_PATTERN || awk->parse.id.block == PARSE_BEGIN || awk->parse.id.block == PARSE_END)) { if (awk->token.line != line) { SET_TOKEN_TYPE (awk, TOKEN_NEWLINE); return 0; } } c = awk->src.lex.curc; if (c == ASE_CHAR_EOF) { SET_TOKEN_TYPE (awk, TOKEN_EOF); } else if (ASE_AWK_ISDIGIT (awk, c)/*|| c == ASE_T('.')*/) { if (__get_number (awk) == -1) return -1; } else if (c == ASE_T('.')) { if (__get_char (awk) == -1) return -1; c = awk->src.lex.curc; if ((awk->option & ASE_AWK_EXPLICIT) == 0 && ASE_AWK_ISDIGIT (awk, c)) { awk->src.lex.curc = ASE_T('.'); if (__unget_char (awk, c) == -1) return -1; if (__get_number (awk) == -1) return -1; } else /*if (ASE_AWK_ISSPACE (awk, c) || c == ASE_CHAR_EOF)*/ { SET_TOKEN_TYPE (awk, TOKEN_PERIOD); ADD_TOKEN_CHAR (awk, ASE_T('.')); } /* else { ase_awk_seterror_old ( awk, ASE_AWK_ELXCHR, awk->token.line, ASE_T("floating point not followed by any valid digits")); return -1; } */ } else if (ASE_AWK_ISALPHA (awk, c) || c == ASE_T('_')) { int type; /* identifier */ do { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } while (ASE_AWK_ISALPHA (awk, c) || c == ASE_T('_') || ASE_AWK_ISDIGIT(awk,c)); type = __classify_ident (awk, ASE_STR_BUF(&awk->token.name), ASE_STR_LEN(&awk->token.name)); SET_TOKEN_TYPE (awk, type); } else if (c == ASE_T('\"')) { SET_TOKEN_TYPE (awk, TOKEN_STR); if (__get_charstr(awk) == -1) return -1; while (awk->option & ASE_AWK_STRCONCAT) { do { if (__skip_spaces(awk) == -1) return -1; if ((n = __skip_comment(awk)) == -1) return -1; } while (n == 1); c = awk->src.lex.curc; if (c != ASE_T('\"')) break; if (__get_charstr(awk) == -1) return -1; } } else if (c == ASE_T('=')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_EQ); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_ASSIGN); } } else if (c == ASE_T('!')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_NE); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('~')) { SET_TOKEN_TYPE (awk, TOKEN_NM); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_NOT); } } else if (c == ASE_T('>')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if ((awk->option & ASE_AWK_SHIFT) && c == ASE_T('>')) { SET_TOKEN_TYPE (awk, TOKEN_RSHIFT); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_GE); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_GT); } } else if (c == ASE_T('<')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if ((awk->option & ASE_AWK_SHIFT) && c == ASE_T('<')) { SET_TOKEN_TYPE (awk, TOKEN_LSHIFT); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_LE); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_LT); } } else if (c == ASE_T('|')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('|')) { SET_TOKEN_TYPE (awk, TOKEN_LOR); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if ((awk->option & ASE_AWK_COPROC) && c == ASE_T('&')) { SET_TOKEN_TYPE (awk, TOKEN_BORAND); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_BOR); } } else if (c == ASE_T('&')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('&')) { SET_TOKEN_TYPE (awk, TOKEN_LAND); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_BAND); } } else if (c == ASE_T('~')) { SET_TOKEN_TYPE (awk, TOKEN_TILDE); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('^')) { SET_TOKEN_TYPE (awk, TOKEN_BXOR); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('+')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('+')) { SET_TOKEN_TYPE (awk, TOKEN_PLUSPLUS); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_PLUS_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_PLUS); } } else if (c == ASE_T('-')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('-')) { SET_TOKEN_TYPE (awk, TOKEN_MINUSMINUS); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_MINUS_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_MINUS); } } else if (c == ASE_T('*')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_MUL_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('*')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_EXP_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_EXP); } } else { SET_TOKEN_TYPE (awk, TOKEN_MUL); } } else if (c == ASE_T('/')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_DIV_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if ((awk->option & ASE_AWK_IDIV) && c == ASE_T('/')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_IDIV_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_IDIV); } } else { SET_TOKEN_TYPE (awk, TOKEN_DIV); } } #if 0 /* TODO: is it a good idea to use a back-slash for * the idiv operator like BASIC? */ else if ((awk->option & ASE_AWK_IDIV) && c == ASE_T('\\')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_IDIV_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_IDIV); } } #endif else if (c == ASE_T('%')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('=')) { SET_TOKEN_TYPE (awk, TOKEN_MOD_ASSIGN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { SET_TOKEN_TYPE (awk, TOKEN_MOD); } } else if (c == ASE_T('(')) { SET_TOKEN_TYPE (awk, TOKEN_LPAREN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T(')')) { SET_TOKEN_TYPE (awk, TOKEN_RPAREN); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('{')) { SET_TOKEN_TYPE (awk, TOKEN_LBRACE); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('}')) { SET_TOKEN_TYPE (awk, TOKEN_RBRACE); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('[')) { SET_TOKEN_TYPE (awk, TOKEN_LBRACK); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T(']')) { SET_TOKEN_TYPE (awk, TOKEN_RBRACK); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('$')) { SET_TOKEN_TYPE (awk, TOKEN_DOLLAR); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T(',')) { SET_TOKEN_TYPE (awk, TOKEN_COMMA); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T(';')) { SET_TOKEN_TYPE (awk, TOKEN_SEMICOLON); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T(':')) { SET_TOKEN_TYPE (awk, TOKEN_COLON); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else if (c == ASE_T('?')) { SET_TOKEN_TYPE (awk, TOKEN_QUEST); ADD_TOKEN_CHAR (awk, c); GET_CHAR (awk); } else { awk->prmfns.misc.sprintf ( awk->prmfns.misc.custom_data, awk->errmsg, ASE_COUNTOF(awk->errmsg), ASE_T("invalid character '%c'"), c); ase_awk_seterror_old ( awk, ASE_AWK_ELXCHR, awk->token.line, awk->errmsg); return -1; } return 0; } static int __get_number (ase_awk_t* awk) { ase_cint_t c; ASE_AWK_ASSERT (awk, ASE_STR_LEN(&awk->token.name) == 0); SET_TOKEN_TYPE (awk, TOKEN_INT); c = awk->src.lex.curc; if (c == ASE_T('0')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('x') || c == ASE_T('X')) { /* hexadecimal number */ do { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } while (ASE_AWK_ISXDIGIT (awk, c)); return 0; } #if 0 else if (c == ASE_T('b') || c == ASE_T('B')) { /* binary number */ do { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } while (c == ASE_T('0') || c == ASE_T('1')); return 0; } #endif else if (c != '.') { /* octal number */ while (c >= ASE_T('0') && c <= ASE_T('7')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } return 0; } } while (ASE_AWK_ISDIGIT (awk, c)) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } if (c == ASE_T('.')) { /* floating-point number */ SET_TOKEN_TYPE (awk, TOKEN_REAL); ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); while (ASE_AWK_ISDIGIT (awk, c)) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } } if (c == ASE_T('E') || c == ASE_T('e')) { SET_TOKEN_TYPE (awk, TOKEN_REAL); ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); if (c == ASE_T('+') || c == ASE_T('-')) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } while (ASE_AWK_ISDIGIT (awk, c)) { ADD_TOKEN_CHAR (awk, c); GET_CHAR_TO (awk, c); } } return 0; } static int __get_charstr (ase_awk_t* awk) { if (awk->src.lex.curc != ASE_T('\"')) { /* the starting quote has been consumed before this function * has been called */ ADD_TOKEN_CHAR (awk, awk->src.lex.curc); } return __get_string (awk, ASE_T('\"'), ASE_T('\\'), ase_false); } static int __get_rexstr (ase_awk_t* awk) { if (awk->src.lex.curc == ASE_T('/')) { /* this part of the function is different from __get_charstr * because of the way this function is called */ GET_CHAR (awk); return 0; } else { ADD_TOKEN_CHAR (awk, awk->src.lex.curc); return __get_string (awk, ASE_T('/'), ASE_T('\\'), ase_true); } } static int __get_string ( ase_awk_t* awk, ase_char_t end_char, ase_char_t esc_char, ase_bool_t keep_esc_char) { ase_cint_t c; int escaped = 0; int digit_count = 0; ase_cint_t c_acc = 0; while (1) { GET_CHAR_TO (awk, c); if (c == ASE_CHAR_EOF) { ase_awk_seterror_old ( awk, ASE_AWK_EENDSTR, awk->token.line, ASE_T("string not closed with a quote")); return -1; } if (escaped == 3) { if (c >= ASE_T('0') && c <= ASE_T('7')) { c_acc = c_acc * 8 + c - ASE_T('0'); digit_count++; if (digit_count >= escaped) { ADD_TOKEN_CHAR (awk, c_acc); escaped = 0; } continue; } else { ADD_TOKEN_CHAR (awk, c_acc); escaped = 0; } } else if (escaped == 2 || escaped == 4 || escaped == 8) { if (c >= ASE_T('0') && c <= ASE_T('9')) { c_acc = c_acc * 16 + c - ASE_T('0'); digit_count++; if (digit_count >= escaped) { ADD_TOKEN_CHAR (awk, c_acc); escaped = 0; } continue; } else if (c >= ASE_T('A') && c <= ASE_T('F')) { c_acc = c_acc * 16 + c - ASE_T('A') + 10; digit_count++; if (digit_count >= escaped) { ADD_TOKEN_CHAR (awk, c_acc); escaped = 0; } continue; } else if (c >= ASE_T('a') && c <= ASE_T('f')) { c_acc = c_acc * 16 + c - ASE_T('a') + 10; digit_count++; if (digit_count >= escaped) { ADD_TOKEN_CHAR (awk, c_acc); escaped = 0; } continue; } else { ase_char_t rc; rc = (escaped == 2)? ASE_T('x'): (escaped == 4)? ASE_T('u'): ASE_T('U'); if (digit_count == 0) ADD_TOKEN_CHAR (awk, rc); else ADD_TOKEN_CHAR (awk, c_acc); escaped = 0; } } if (escaped == 0 && c == end_char) { /* terminating quote */ /*GET_CHAR_TO (awk, c);*/ GET_CHAR (awk); break; } if (escaped == 0 && c == esc_char) { escaped = 1; continue; } if (escaped == 1) { if (c == ASE_T('n')) c = ASE_T('\n'); else if (c == ASE_T('r')) c = ASE_T('\r'); else if (c == ASE_T('t')) c = ASE_T('\t'); else if (c == ASE_T('f')) c = ASE_T('\f'); else if (c == ASE_T('b')) c = ASE_T('\b'); else if (c == ASE_T('v')) c = ASE_T('\v'); else if (c == ASE_T('a')) c = ASE_T('\a'); else if (c >= ASE_T('0') && c <= ASE_T('7')) { escaped = 3; digit_count = 1; c_acc = c - ASE_T('0'); continue; } else if (c == ASE_T('x')) { escaped = 2; digit_count = 0; c_acc = 0; continue; } #ifdef ASE_CHAR_IS_WCHAR else if (c == ASE_T('u') && ASE_SIZEOF(ase_char_t) >= 2) { escaped = 4; digit_count = 0; c_acc = 0; continue; } else if (c == ASE_T('U') && ASE_SIZEOF(ase_char_t) >= 4) { escaped = 8; digit_count = 0; c_acc = 0; continue; } #endif else if (keep_esc_char) { ADD_TOKEN_CHAR (awk, esc_char); } escaped = 0; } ADD_TOKEN_CHAR (awk, c); } return 0; } static int __get_char (ase_awk_t* awk) { ase_ssize_t n; /*ase_char_t c;*/ if (awk->src.lex.ungotc_count > 0) { awk->src.lex.curc = awk->src.lex.ungotc[--awk->src.lex.ungotc_count]; awk->src.lex.line = awk->src.lex.ungotc_line[awk->src.lex.ungotc_count]; awk->src.lex.column = awk->src.lex.ungotc_column[awk->src.lex.ungotc_count]; return 0; } if (awk->src.shared.buf_pos >= awk->src.shared.buf_len) { n = awk->src.ios.in ( ASE_AWK_IO_READ, awk->src.ios.custom_data, awk->src.shared.buf, ASE_COUNTOF(awk->src.shared.buf)); if (n <= -1) { ase_awk_seterror_old ( awk, ASE_AWK_ESINRD, 0, ASE_T("cannot read the source input")); return -1; } if (n == 0) { awk->src.lex.curc = ASE_CHAR_EOF; return 0; } awk->src.shared.buf_pos = 0; awk->src.shared.buf_len = n; } awk->src.lex.curc = awk->src.shared.buf[awk->src.shared.buf_pos++]; if (awk->src.lex.curc == ASE_T('\n')) { awk->src.lex.line++; awk->src.lex.column = 1; } else awk->src.lex.column++; return 0; } static int __unget_char (ase_awk_t* awk, ase_cint_t c) { if (awk->src.lex.ungotc_count >= ASE_COUNTOF(awk->src.lex.ungotc)) { ase_awk_seterror_old ( awk, ASE_AWK_ELXUNG, awk->src.lex.line, ASE_NULL); return -1; } awk->src.lex.ungotc_line[awk->src.lex.ungotc_count] = awk->src.lex.line; awk->src.lex.ungotc_column[awk->src.lex.ungotc_count] = awk->src.lex.column; awk->src.lex.ungotc[awk->src.lex.ungotc_count++] = c; return 0; } static int __skip_spaces (ase_awk_t* awk) { ase_cint_t c = awk->src.lex.curc; while (ASE_AWK_ISSPACE (awk, c)) GET_CHAR_TO (awk, c); return 0; } static int __skip_comment (ase_awk_t* awk) { ase_cint_t c = awk->src.lex.curc; ase_size_t line, column; if (c == ASE_T('#')) { do { GET_CHAR_TO (awk, c); } while (c != ASE_T('\n') && c != ASE_CHAR_EOF); GET_CHAR (awk); return 1; /* comment by # */ } if (c != ASE_T('/')) return 0; /* not a comment */ line = awk->src.lex.line; column = awk->src.lex.column; GET_CHAR_TO (awk, c); if (c == ASE_T('*')) { do { GET_CHAR_TO (awk, c); if (c == ASE_CHAR_EOF) { ase_awk_seterror_old ( awk, ASE_AWK_EENDCMT, awk->src.lex.line, ASE_T("comment not properly closed")); return -1; } if (c == ASE_T('*')) { GET_CHAR_TO (awk, c); if (c == ASE_CHAR_EOF) { ase_awk_seterror_old ( awk, ASE_AWK_EENDCMT, awk->src.lex.line, ASE_T("comment not properly closed")); return -1; } if (c == ASE_T('/')) { /*GET_CHAR_TO (awk, c);*/ GET_CHAR (awk); break; } } } while (1); return 1; /* c-style comment */ } if (__unget_char (awk, c) == -1) return -1; /* error */ awk->src.lex.curc = ASE_T('/'); awk->src.lex.line = line; awk->src.lex.column = column; return 0; } static int __classify_ident ( ase_awk_t* awk, const ase_char_t* name, ase_size_t len) { struct __kwent* kwp; for (kwp = __kwtab; kwp->name != ASE_NULL; kwp++) { if (kwp->valid != 0 && (awk->option & kwp->valid) == 0) continue; if (ase_strxncmp (kwp->name, kwp->name_len, name, len) == 0) { return kwp->type; } } return TOKEN_IDENT; } static int __assign_to_opcode (ase_awk_t* awk) { static int __assop[] = { ASE_AWK_ASSOP_NONE, ASE_AWK_ASSOP_PLUS, ASE_AWK_ASSOP_MINUS, ASE_AWK_ASSOP_MUL, ASE_AWK_ASSOP_DIV, ASE_AWK_ASSOP_IDIV, ASE_AWK_ASSOP_MOD, ASE_AWK_ASSOP_EXP }; if (awk->token.type >= TOKEN_ASSIGN && awk->token.type <= TOKEN_EXP_ASSIGN) { return __assop[awk->token.type - TOKEN_ASSIGN]; } return -1; } static int __is_plain_var (ase_awk_nde_t* nde) { return nde->type == ASE_AWK_NDE_GLOBAL || nde->type == ASE_AWK_NDE_LOCAL || nde->type == ASE_AWK_NDE_ARG || nde->type == ASE_AWK_NDE_NAMED; } static int __is_var (ase_awk_nde_t* nde) { return nde->type == ASE_AWK_NDE_GLOBAL || nde->type == ASE_AWK_NDE_LOCAL || nde->type == ASE_AWK_NDE_ARG || nde->type == ASE_AWK_NDE_NAMED || nde->type == ASE_AWK_NDE_GLOBALIDX || nde->type == ASE_AWK_NDE_LOCALIDX || nde->type == ASE_AWK_NDE_ARGIDX || nde->type == ASE_AWK_NDE_NAMEDIDX; } struct __deparse_func_t { ase_awk_t* awk; ase_char_t* tmp; ase_size_t tmp_len; }; static int __deparse (ase_awk_t* awk) { ase_awk_chain_t* chain; ase_char_t tmp[ASE_SIZEOF(ase_size_t)*8 + 32]; struct __deparse_func_t df; int n = 0, op; ASE_AWK_ASSERT (awk, awk->src.ios.out != ASE_NULL); awk->src.shared.buf_len = 0; awk->src.shared.buf_pos = 0; op = awk->src.ios.out ( ASE_AWK_IO_OPEN, awk->src.ios.custom_data, ASE_NULL, 0); if (op <= -1) { ase_awk_seterror_old ( awk, ASE_AWK_ESOUTOP, 0, ASE_T("cannot open the source output")); return -1; } if (op == 0) { /* the result of the open operation indicates that the * file has been open but reached the end. so it has to * skip the entire deparsing procedure as it can't write * any single characters on such an io handler. but note * that this is not really an error for the parse and deparser. * * in fact, there are two ways to skip deparsing. * 1. set awk->src.ios.out to NULL. * 2. set awk->src.ios.out to a normal handler but * make it return 0 on the OPEN request. */ n = 0; goto exit_deparse; } #define EXIT_DEPARSE(num) \ do { \ n = -1; \ ase_awk_seterror_old (awk, num, 0, ASE_NULL); \ goto exit_deparse; \ } while(0) if (awk->tree.nglobals > awk->tree.nbglobals) { ase_size_t i/*, len*/; ASE_AWK_ASSERT (awk, awk->tree.nglobals > 0); if (ase_awk_putsrcstr (awk, ASE_T("global ")) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); for (i = awk->tree.nbglobals; i < awk->tree.nglobals - 1; i++) { /* len = ase_awk_longtostr ((ase_long_t)i, 10, ASE_T("__global"), tmp, ASE_COUNTOF(tmp)); ASE_AWK_ASSERT (awk, len != (ase_size_t)-1); if (ase_awk_putsrcstrx (awk, tmp, len) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); */ if (ase_awk_putsrcstrx (awk, awk->parse.globals.buf[i].name, awk->parse.globals.buf[i].name_len) == -1) { EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (ase_awk_putsrcstr (awk, ASE_T(", ")) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } /* len = ase_awk_longtostr ((ase_long_t)i, 10, ASE_T("__global"), tmp, ASE_COUNTOF(tmp)); ASE_AWK_ASSERT (awk, len != (ase_size_t)-1); if (ase_awk_putsrcstrx (awk, tmp, len) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); */ if (ase_awk_putsrcstrx (awk, awk->parse.globals.buf[i].name, awk->parse.globals.buf[i].name_len) == -1) { EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (awk->option & ASE_AWK_CRLF) { if (ase_awk_putsrcstr (awk, ASE_T(";\r\n\r\n")) == -1) { EXIT_DEPARSE (ASE_AWK_ESOUTWR); } } else { if (ase_awk_putsrcstr (awk, ASE_T(";\n\n")) == -1) { EXIT_DEPARSE (ASE_AWK_ESOUTWR); } } } df.awk = awk; df.tmp = tmp; df.tmp_len = ASE_COUNTOF(tmp); if (ase_awk_map_walk (&awk->tree.afns, __deparse_func, &df) == -1) { EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (awk->tree.begin != ASE_NULL) { if (ase_awk_putsrcstr (awk, ASE_T("BEGIN ")) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); if (ase_awk_prnpt (awk, awk->tree.begin) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); if (awk->option & ASE_AWK_CRLF) { if (__put_char (awk, ASE_T('\r')) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (__put_char (awk, ASE_T('\n')) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } chain = awk->tree.chain; while (chain != ASE_NULL) { if (chain->pattern != ASE_NULL) { if (ase_awk_prnptnpt (awk, chain->pattern) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (chain->action == ASE_NULL) { /* blockless pattern */ if (awk->option & ASE_AWK_CRLF) { if (__put_char (awk, ASE_T('\r')) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (__put_char (awk, ASE_T('\n')) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } else { if (chain->pattern != ASE_NULL) { if (__put_char (awk, ASE_T(' ')) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (ase_awk_prnpt (awk, chain->action) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (awk->option & ASE_AWK_CRLF) { if (__put_char (awk, ASE_T('\r')) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (__put_char (awk, ASE_T('\n')) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); chain = chain->next; } if (awk->tree.end != ASE_NULL) { if (ase_awk_putsrcstr (awk, ASE_T("END ")) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); if (ase_awk_prnpt (awk, awk->tree.end) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); } if (__flush (awk) == -1) EXIT_DEPARSE (ASE_AWK_ESOUTWR); exit_deparse: if (awk->src.ios.out ( ASE_AWK_IO_CLOSE, awk->src.ios.custom_data, ASE_NULL, 0) != 0) { if (n == 0) { ase_awk_seterror_old ( awk, ASE_AWK_ESOUTCL, 0, ASE_T("cannot close the source output")); n = -1; } } return n; } static int __deparse_func (ase_awk_pair_t* pair, void* arg) { struct __deparse_func_t* df = (struct __deparse_func_t*)arg; ase_awk_afn_t* afn = (ase_awk_afn_t*)pair->val; ase_size_t i, n; ASE_AWK_ASSERT (df->awk, ase_strxncmp ( pair->key, pair->key_len, afn->name, afn->name_len) == 0); if (ase_awk_putsrcstr (df->awk, ASE_T("function ")) == -1) return -1; if (ase_awk_putsrcstr (df->awk, afn->name) == -1) return -1; if (ase_awk_putsrcstr (df->awk, ASE_T(" (")) == -1) return -1; for (i = 0; i < afn->nargs; ) { n = ase_awk_longtostr (i++, 10, ASE_T("__param"), df->tmp, df->tmp_len); ASE_AWK_ASSERT (df->awk, n != (ase_size_t)-1); if (ase_awk_putsrcstrx (df->awk, df->tmp, n) == -1) return -1; if (i >= afn->nargs) break; if (ase_awk_putsrcstr (df->awk, ASE_T(", ")) == -1) return -1; } if (ase_awk_putsrcstr (df->awk, ASE_T(")")) == -1) return -1; if (df->awk->option & ASE_AWK_CRLF) { if (__put_char (df->awk, ASE_T('\r')) == -1) return -1; } if (__put_char (df->awk, ASE_T('\n')) == -1) return -1; if (ase_awk_prnpt (df->awk, afn->body) == -1) return -1; if (df->awk->option & ASE_AWK_CRLF) { if (__put_char (df->awk, ASE_T('\r')) == -1) return -1; } if (__put_char (df->awk, ASE_T('\n')) == -1) return -1; return 0; } static int __put_char (ase_awk_t* awk, ase_char_t c) { awk->src.shared.buf[awk->src.shared.buf_len++] = c; if (awk->src.shared.buf_len >= ASE_COUNTOF(awk->src.shared.buf)) { if (__flush (awk) == -1) return -1; } return 0; } static int __flush (ase_awk_t* awk) { ase_ssize_t n; ASE_AWK_ASSERT (awk, awk->src.ios.out != ASE_NULL); while (awk->src.shared.buf_pos < awk->src.shared.buf_len) { n = awk->src.ios.out ( ASE_AWK_IO_WRITE, awk->src.ios.custom_data, &awk->src.shared.buf[awk->src.shared.buf_pos], awk->src.shared.buf_len - awk->src.shared.buf_pos); if (n <= 0) return -1; awk->src.shared.buf_pos += n; } awk->src.shared.buf_pos = 0; awk->src.shared.buf_len = 0; return 0; } int ase_awk_putsrcstr (ase_awk_t* awk, const ase_char_t* str) { while (*str != ASE_T('\0')) { if (__put_char (awk, *str) == -1) return -1; str++; } return 0; } int ase_awk_putsrcstrx ( ase_awk_t* awk, const ase_char_t* str, ase_size_t len) { const ase_char_t* end = str + len; while (str < end) { if (__put_char (awk, *str) == -1) return -1; str++; } return 0; }