/* Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "hawk-prv.h" #define FMT_EBADVAR HAWK_T("'%.*js' not a valid variable name") #define FMT_ECOMMA HAWK_T("comma expected in place of '%.*js'") #define FMT_ECOLON HAWK_T("colon expected in place of '%.*js'") #define FMT_EDUPLCL HAWK_T("duplicate local variable name - %.*js") #define FMT_EFUNNF HAWK_T("function '%.*js' not defined") #define FMT_EIDENT HAWK_T("identifier expected in place of '%.*js'") #define FMT_EINTLIT HAWK_T("integer literal expected in place of '%.*js'") #define FMT_EIONMNL HAWK_T("invalid I/O name of length %zu containing '\\0'") #define FMT_EKWFNC HAWK_T("keyword 'function' expected in place of '%.*js'") #define FMT_EKWIN HAWK_T("keyword 'in' expected in place of '%.*js'") #define FMT_EKWWHL HAWK_T("keyword 'while' expected in place of '%.*js'") #define FMT_EKWCASE HAWK_T("keyword 'case' expected in place of '%.*js'") #define FMT_EMULDFL HAWK_T("multiple '%.*js' labels") #define FMT_ELBRACE HAWK_T("left brace expected in place of '%.*js'") #define FMT_ELPAREN HAWK_T("left parenthesis expected in place of '%.*js'") #define FMT_ENOENT_GBL_HS HAWK_T("no such global variable - %.*hs") #define FMT_ENOENT_GBL_LS HAWK_T("no such global variable - %.*ls") #define FMT_ERBRACE HAWK_T("right brace expected in place of '%.*js'") #define FMT_ERBRACK HAWK_T("right bracket expected in place of '%.*js'") #define FMT_ERPAREN HAWK_T("right parenthesis expected in place of '%.*js'") #define FMT_ESCOLON HAWK_T("semicolon expected in place of '%.*js'") #define FMT_ESEGTL HAWK_T("segment '%.*js' too long") #define FMT_EUNDEF HAWK_T("undefined identifier '%.*js'") #define FMT_EXKWNR HAWK_T("'%.*js' not recognized") #define TOK_FLAGS_LPAREN_CLOSER (1 << 0) #define PARSE_BLOCK_FLAG_IS_TOP (1 << 0) #define MAX_MOD_NAME_LEN (64) enum tok_t { TOK_EOF, TOK_NEWLINE, /* TOK_XXX_ASSNs should be in sync with assop in assign_to_opcode. * it also should be in the order as hawk_assop_type_t in run.h */ TOK_ASSN, TOK_PLUS_ASSN, TOK_MINUS_ASSN, TOK_MUL_ASSN, TOK_DIV_ASSN, TOK_IDIV_ASSN, TOK_MOD_ASSN, TOK_EXP_ASSN, /* ^ - exponentiation */ TOK_CONCAT_ASSN, TOK_RS_ASSN, /* >> */ TOK_LS_ASSN, /* << */ TOK_BAND_ASSN, TOK_BXOR_ASSN, TOK_BOR_ASSN, /* end of TOK_XXX_ASSN */ TOK_TEQ, TOK_TNE, TOK_EQ, TOK_NE, TOK_LE, TOK_LT, TOK_GE, TOK_GT, TOK_TILDE, /* ~ - match or bitwise negation */ TOK_NM, /* !~ - not match */ TOK_LNOT, /* ! - logical negation */ TOK_PLUS, TOK_PLUSPLUS, TOK_MINUS, TOK_MINUSMINUS, TOK_MUL, TOK_DIV, TOK_IDIV, TOK_MOD, TOK_LOR, /* || */ TOK_LAND, /* && */ TOK_BOR, /* | */ TOK_PIPE_BD, /* |& - bidirectional pipe */ TOK_BXOR, /* ^^ - bitwise-xor */ TOK_BAND, /* & */ TOK_RS, TOK_LS, TOK_IN, TOK_EXP, TOK_CONCAT, /* %% */ TOK_LPAREN, TOK_RPAREN, TOK_LBRACE, TOK_RBRACE, TOK_LBRACK, TOK_RBRACK, TOK_DOLLAR, TOK_COMMA, TOK_SEMICOLON, TOK_COLON, TOK_DBLCOLON, TOK_QUEST, TOK_ELLIPSIS, TOK_DBLPERIOD, /* not used yet */ TOK_PERIOD, TOK_ATBRACK, /* @[ */ TOK_ATBRACE, /* @{ */ /* == begin reserved words == */ /* === extended reserved words === */ TOK_XARGC, TOK_XARGV, TOK_XABORT, TOK_XCONST, TOK_XFALSE, TOK_XGLOBAL, TOK_XINCLUDE, TOK_XINCLUDE_ONCE, TOK_XLOCAL, TOK_XPRAGMA, TOK_XRESET, TOK_XTRUE, /* === normal reserved words === */ TOK_BEGIN, TOK_END, TOK_FUNCTION, TOK_IF, TOK_ELSE, TOK_WHILE, TOK_FOR, TOK_DO, TOK_SWITCH, TOK_CASE, TOK_DEFAULT, TOK_BREAK, TOK_CONTINUE, TOK_RETURN, TOK_EXIT, TOK_DELETE, TOK_NEXT, TOK_NEXTFILE, TOK_NEXTOFILE, TOK_PRINT, TOK_PRINTF, TOK_GETBLINE, TOK_GETLINE, /* == end reserved words == */ TOK_IDENT, TOK_CHAR, TOK_BCHR, TOK_INT, TOK_FLT, TOK_STR, TOK_MBS, TOK_REX, TOK_XNIL, __TOKEN_COUNT__ }; enum { PARSE_GBL, 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; }; typedef struct nde_chain_t nde_chain_t; struct nde_chain_t { hawk_nde_t* head; hawk_nde_t* tail; }; enum nde_hard_bool_t { NDE_HARD_BOOL_UNKNOWN = -1, NDE_HARD_BOOL_FALSE = 0, NDE_HARD_BOOL_TRUE = 1 }; typedef enum nde_hard_bool_t nde_hard_bool_t; static int parse_progunit (hawk_t* hawk); static void adjust_static_globals (hawk_t* hawk); static hawk_oow_t find_global (hawk_t* hawk, const hawk_oocs_t* name); static hawk_t* collect_globals (hawk_t* hawk, nde_chain_t* init, int is_const); static hawk_t* collect_locals (hawk_t* hawk, hawk_oow_t nlcls, int flags, nde_chain_t* init, int is_const); static int append_init_stmts_to_init_tree (hawk_t* hawk, nde_chain_t* init, const hawk_loc_t* xloc); static hawk_nde_t* parse_function (hawk_t* hawk, int named); static hawk_nde_t* parse_begin (hawk_t* hawk); static hawk_nde_t* parse_end (hawk_t* hawk); static int parse_action_block (hawk_t* hawk, hawk_nde_t* ptn, int blockless); static hawk_nde_t* parse_block_dc (hawk_t* hawk, const hawk_loc_t* xloc, int flags); static hawk_nde_t* parse_statement (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_expr_withdc (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_expr_basic_withdc (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_logical_or (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_logical_and (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_in (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_regex_match (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_bitwise_or (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_bitwise_xor (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_bitwise_and (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_equality (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_relational (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_shift (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_concat (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_additive (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_multiplicative (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_unary (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_exponent (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_unary_exp (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_increment (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_primary (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_primary_ident (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_primary_literal (hawk_t* hawk, const hawk_loc_t* xloc); static hawk_nde_t* parse_hashidx (hawk_t* hawk, const hawk_oocs_t* name, const hawk_loc_t* xloc); static hawk_nde_t* parse_dotidx (hawk_t* hawk, const hawk_oocs_t* name, const hawk_loc_t* xloc); #if defined(HAWK_ENABLE_FUN_AS_VALUE) static hawk_nde_t* parse_xcall_postfix_on_expr (hawk_t* hawk, hawk_nde_t* base, const hawk_loc_t* xloc); #endif #define FNCALL_FLAG_NOARG (1 << 0) /* no argument */ #define FNCALL_FLAG_EXPR (1 << 1) #define FNCALL_FLAG_MAP (1 << 2) #define FNCALL_FLAG_DEFER_MODFNC (1 << 3) static hawk_nde_t* parse_fncall (hawk_t* hawk, const hawk_oocs_t* name, hawk_fnc_t* fnc, const hawk_loc_t* xloc, int flags, int closer_token); static hawk_nde_t* parse_primary_ident_segs (hawk_t* hawk, const hawk_loc_t* xloc, const hawk_oocs_t* full, const hawk_oocs_t segs[], int nsegs); static hawk_nde_t* make_deferred_modsym_node (hawk_t* hawk, const hawk_loc_t* xloc, const hawk_oocs_t* name); static int get_token (hawk_t* hawk); static int preget_token (hawk_t* hawk); static int get_rexstr (hawk_t* hawk, hawk_tok_t* tok); static int skip_spaces (hawk_t* hawk); static int skip_comment (hawk_t* hawk); static int classify_ident (hawk_t* hawk, const hawk_oocs_t* name); static int deparse (hawk_t* hawk); static hawk_htb_walk_t deparse_func (hawk_htb_t* map, hawk_htb_pair_t* pair, void* arg); static int put_oow_as_dec (hawk_t* hawk, hawk_oow_t v); static int put_int_as_dec (hawk_t* hawk, hawk_int_t v); static int put_flt_as_str (hawk_t* hawk, hawk_flt_t v); static int put_escaped_oochars (hawk_t* hawk, const hawk_ooch_t* ptr, hawk_oow_t len); static int put_escaped_bchars (hawk_t* hawk, const hawk_bch_t* ptr, hawk_oow_t len); static int put_newline (hawk_t* hawk); static int put_char (hawk_t* hawk, hawk_ooch_t c); static int flush_out (hawk_t* hawk); static hawk_mod_t* query_module (hawk_t* hawk, const hawk_oocs_t segs[], int nsegs, hawk_mod_sym_t* sym, hawk_rbt_t* sectab); typedef struct kwent_t kwent_t; struct kwent_t { hawk_oocs_t name; int type; int trait; /* the entry is valid when this option is set */ }; static kwent_t kwtab[] = { /* keep this table in sync with the kw_t enums in "parse-prv.h". * also keep it sorted by the first field for binary search. * there are extra keywords that are alias to the main entries * in classify_ident. e.g. "func". such aliases are not placed * here because of direct access by index in hawk_getkwname() */ { { HAWK_T("@abort"), 6 }, TOK_XABORT, 0 }, { { HAWK_T("@argc"), 5 }, TOK_XARGC, 0 }, { { HAWK_T("@argv"), 5 }, TOK_XARGV, 0 }, { { HAWK_T("@const"), 6 }, TOK_XCONST, 0 }, { { HAWK_T("@false"), 6 }, TOK_XFALSE, 0 }, { { HAWK_T("@global"), 7 }, TOK_XGLOBAL, 0 }, { { HAWK_T("@include"), 8 }, TOK_XINCLUDE, 0 }, { { HAWK_T("@include_once"), 13 }, TOK_XINCLUDE_ONCE, 0 }, { { HAWK_T("@local"), 6 }, TOK_XLOCAL, 0 }, { { HAWK_T("@nil"), 4 }, TOK_XNIL, 0 }, { { HAWK_T("@pragma"), 7 }, TOK_XPRAGMA, 0 }, { { HAWK_T("@reset"), 6 }, TOK_XRESET, 0 }, { { HAWK_T("@true"), 5 }, TOK_XTRUE, 0 }, { { HAWK_T("BEGIN"), 5 }, TOK_BEGIN, HAWK_PABLOCK }, { { HAWK_T("END"), 3 }, TOK_END, HAWK_PABLOCK }, { { HAWK_T("break"), 5 }, TOK_BREAK, 0 }, { { HAWK_T("case"), 4 }, TOK_CASE, 0 }, { { HAWK_T("continue"), 8 }, TOK_CONTINUE, 0 }, { { HAWK_T("default"), 7 }, TOK_DEFAULT, 0 }, { { HAWK_T("delete"), 6 }, TOK_DELETE, 0 }, { { HAWK_T("do"), 2 }, TOK_DO, 0 }, { { HAWK_T("else"), 4 }, TOK_ELSE, 0 }, { { HAWK_T("exit"), 4 }, TOK_EXIT, 0 }, { { HAWK_T("for"), 3 }, TOK_FOR, 0 }, { { HAWK_T("function"), 8 }, TOK_FUNCTION, 0 }, { { HAWK_T("getbline"), 8 }, TOK_GETBLINE, HAWK_RIO }, { { HAWK_T("getline"), 7 }, TOK_GETLINE, HAWK_RIO }, { { HAWK_T("if"), 2 }, TOK_IF, 0 }, { { HAWK_T("in"), 2 }, TOK_IN, 0 }, { { HAWK_T("next"), 4 }, TOK_NEXT, HAWK_PABLOCK }, { { HAWK_T("nextfile"), 8 }, TOK_NEXTFILE, HAWK_PABLOCK }, { { HAWK_T("nextofile"), 9 }, TOK_NEXTOFILE, HAWK_PABLOCK | HAWK_NEXTOFILE }, { { HAWK_T("print"), 5 }, TOK_PRINT, HAWK_RIO }, { { HAWK_T("printf"), 6 }, TOK_PRINTF, HAWK_RIO }, { { HAWK_T("return"), 6 }, TOK_RETURN, 0 }, { { HAWK_T("switch"), 6 }, TOK_SWITCH, 0 }, { { HAWK_T("while"), 5 }, TOK_WHILE, 0 } }; typedef struct global_t global_t; struct global_t { const hawk_ooch_t* name; hawk_oow_t namelen; int trait; }; static global_t gtab[] = { /* * this table must match the order of the hawk_gbl_id_t enumerators */ /* output real-to-str conversion format for other cases than 'print' */ { HAWK_T("CONVFMT"), 7, 0 }, /* current input file name */ { HAWK_T("FILENAME"), 8, HAWK_PABLOCK }, /* input record number in current file */ { HAWK_T("FNR"), 3, HAWK_PABLOCK }, /* input field separator */ { HAWK_T("FS"), 2, 0 }, /* ignore case in string comparison */ { HAWK_T("IGNORECASE"), 10, 0 }, /* number of fields in current input record * NF is also updated if you assign a value to $0. so it is not * associated with HAWK_PABLOCK */ { HAWK_T("NF"), 2, 0 }, /* input record number */ { HAWK_T("NR"), 2, HAWK_PABLOCK }, /* detect a numeric string */ { HAWK_T("NUMSTRDETECT"), 12, 0 }, /* current output file name */ { HAWK_T("OFILENAME"), 9, HAWK_PABLOCK | HAWK_NEXTOFILE }, /* output real-to-str conversion format for 'print' */ { HAWK_T("OFMT"), 4, HAWK_RIO }, /* output field separator for 'print' */ { HAWK_T("OFS"), 3, HAWK_RIO }, /* output record separator. used for 'print' and blockless output */ { HAWK_T("ORS"), 3, HAWK_RIO }, /* deterimines whether to set CLOEXEC when invoking an external program via a pipe */ { HAWK_T("PIPECLOEXEC"), 11, HAWK_RIO }, { HAWK_T("RLENGTH"), 7, 0 }, { HAWK_T("RS"), 2, 0 }, { HAWK_T("RSTART"), 6, 0 }, { HAWK_T("SCRIPTNAME"), 10, 0 }, /* it decides the field construction behavior when FS is a regular expression and * the field splitter is composed of whitespaces only. e.g) FS="[ \t]*"; * if set to a non-zero value, remove leading spaces and trailing spaces off a record * before field splitting. * if set to zero, leading spaces and trailing spaces result in 1 empty field respectively. * if not set, the behavior is dependent on the hawk->opt.trait & HAWK_STRIPRECSPC */ { HAWK_T("STRIPRECSPC"), 11, 0 }, { HAWK_T("STRIPSTRSPC"), 11, 0 }, { HAWK_T("SUBSEP"), 6, 0 } }; #define GET_CHAR(hawk) \ do { if (get_char(hawk) <= -1) return -1; } while(0) #define GET_CHAR_TO(hawk,c) \ do { \ if (get_char(hawk) <= -1) return -1; \ c = (hawk)->sio.last.c; \ } while(0) #define SET_TOKEN_TYPE(hawk,tok,code) \ do { (tok)->type = (code); } while (0) #define ADD_TOKEN_CHAR(hawk,tok,c) \ do { \ if (HAWK_UNLIKELY(hawk_ooecs_ccat((tok)->name,(c)) == (hawk_oow_t)-1)) \ { \ ADJERR_LOC(hawk, &(tok)->loc); \ return -1; \ } \ } while (0) #define ADD_TOKEN_STR(hawk,tok,s,l) \ do { \ if (HAWK_UNLIKELY(hawk_ooecs_ncat((tok)->name,(s),(l)) == (hawk_oow_t)-1)) \ { \ ADJERR_LOC(hawk, &(tok)->loc); \ return -1; \ } \ } while (0) #if defined(HAWK_OOCH_IS_BCH) # define ADD_TOKEN_UINT32(hawk,tok,c) \ do { \ if (c <= 0xFF) ADD_TOKEN_CHAR(hawk, tok, c); \ else \ { \ hawk_bch_t __xbuf[HAWK_MBLEN_MAX + 1]; \ hawk_oow_t __len, __i; \ __len = uc_to_utf8(c, __xbuf, HAWK_COUNTOF(__xbuf)); /* use utf8 all the time */ \ for (__i = 0; __i < __len; __i++) ADD_TOKEN_CHAR(hawk, tok, __xbuf[__i]); \ } \ } while (0) #else # define ADD_TOKEN_UINT32(hawk,tok,c) ADD_TOKEN_CHAR(hawk,tok,c); #endif #define MATCH(hawk,tok_type) ((hawk)->tok.type == (tok_type)) #define MATCH_RANGE(hawk,tok_type_start,tok_type_end) ((hawk)->tok.type >= (tok_type_start) && (hawk)->tok.type <= (tok_type_end)) #define MATCH_TERMINATOR_NORMAL(hawk) \ (MATCH((hawk),TOK_SEMICOLON) || MATCH((hawk),TOK_NEWLINE)) #define MATCH_TERMINATOR_RBRACE(hawk) \ ((hawk->opt.trait & HAWK_NEWLINE) && MATCH((hawk),TOK_RBRACE)) #define MATCH_TERMINATOR(hawk) \ (MATCH_TERMINATOR_NORMAL(hawk) || MATCH_TERMINATOR_RBRACE(hawk)) #define ADJERR_LOC(hawk,l) do { if (l) (hawk)->gem_.errloc = *(l); } while (0) #if defined(HAWK_OOCH_IS_BCH) static HAWK_INLINE hawk_oow_t uc_to_utf8 (hawk_uch_t uc, hawk_bch_t* buf, hawk_oow_t bsz) { static hawk_cmgr_t* utf8_cmgr = HAWK_NULL; if (!utf8_cmgr) utf8_cmgr = hawk_get_cmgr_by_id(HAWK_CMGR_UTF8); return utf8_cmgr->uctobc(uc, buf, bsz); } #endif static HAWK_INLINE int is_plain_var (hawk_nde_t* nde) { return nde->type == HAWK_NDE_GBL || nde->type == HAWK_NDE_LCL || nde->type == HAWK_NDE_ARG || nde->type == HAWK_NDE_NAMED; } static HAWK_INLINE int is_var (hawk_nde_t* nde) { return nde->type == HAWK_NDE_GBL || nde->type == HAWK_NDE_LCL || nde->type == HAWK_NDE_ARG || nde->type == HAWK_NDE_NAMED || nde->type == HAWK_NDE_GBLIDX || nde->type == HAWK_NDE_LCLIDX || nde->type == HAWK_NDE_ARGIDX || nde->type == HAWK_NDE_NAMEDIDX; } static HAWK_INLINE hawk_oow_t tok_len_for_nospace (const hawk_tok_t* tok) { hawk_oow_t len = HAWK_OOECS_LEN(tok->name); return (len > 0)? len: 1; } static HAWK_INLINE int is_nospace_lparen (hawk_t* hawk) { if (!MATCH(hawk, TOK_LPAREN)) return 0; return (hawk->tok.loc.line == hawk->ptok.loc.line && hawk->tok.loc.colm == hawk->ptok.loc.colm + tok_len_for_nospace(&hawk->ptok)); } static int get_char (hawk_t* hawk) { hawk_ooi_t n; if (hawk->sio.nungots > 0) { /* there are something in the unget buffer */ hawk->sio.last = hawk->sio.ungot[--hawk->sio.nungots]; return 0; } if (hawk->sio.inp->b.pos >= hawk->sio.inp->b.len) { n = hawk->sio.inf( hawk, HAWK_SIO_CMD_READ, hawk->sio.inp, hawk->sio.inp->b.buf, HAWK_COUNTOF(hawk->sio.inp->b.buf) ); if (n <= -1) return -1; if (n == 0) { hawk->sio.inp->last.c = HAWK_OOCI_EOF; hawk->sio.inp->last.line = hawk->sio.inp->line; hawk->sio.inp->last.colm = hawk->sio.inp->colm; hawk->sio.inp->last.file = hawk->sio.inp->path; hawk->sio.last = hawk->sio.inp->last; return 0; } hawk->sio.inp->b.pos = 0; hawk->sio.inp->b.len = n; } if (hawk->sio.inp->last.c == HAWK_T('\n')) { /* if the previous charater was a newline, * increment the line counter and reset column to 1. * incrementing it line number here instead of * updating inp->last causes the line number for * TOK_EOF to be the same line as the last newline. */ hawk->sio.inp->line++; hawk->sio.inp->colm = 1; } hawk->sio.inp->last.c = hawk->sio.inp->b.buf[hawk->sio.inp->b.pos++]; hawk->sio.inp->last.line = hawk->sio.inp->line; hawk->sio.inp->last.colm = hawk->sio.inp->colm++; hawk->sio.inp->last.file = hawk->sio.inp->path; hawk->sio.last = hawk->sio.inp->last; return 0; } static void unget_char (hawk_t* hawk, const hawk_sio_lxc_t* c) { /* Make sure that the unget buffer is large enough */ HAWK_ASSERT(hawk->sio.nungots < HAWK_COUNTOF(hawk->sio.ungot)); hawk->sio.ungot[hawk->sio.nungots++] = *c; } const hawk_ooch_t* hawk_getgblname (hawk_t* hawk, hawk_oow_t idx, hawk_oow_t* len) { HAWK_ASSERT(idx < HAWK_ARR_SIZE(hawk->parse.gbls)); *len = HAWK_ARR_DLEN(hawk->parse.gbls,idx); return HAWK_ARR_DPTR(hawk->parse.gbls,idx); } void hawk_getkwname (hawk_t* hawk, hawk_kwid_t id, hawk_oocs_t* s) { *s = kwtab[id].name; } static int parse (hawk_t* hawk) { int ret = -1; hawk_ooi_t op; HAWK_ASSERT(hawk->sio.inf != HAWK_NULL); op = hawk->sio.inf(hawk, HAWK_SIO_CMD_OPEN, hawk->sio.inp, HAWK_NULL, 0); if (op <= -1) { /* cannot open the source file. * it doesn't even have to call CLOSE */ return -1; } adjust_static_globals(hawk); /* get the first character and the first token */ if (get_char(hawk) <= -1 || get_token(hawk)) goto oops; while (1) { while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } if (MATCH(hawk,TOK_EOF)) break; if (parse_progunit(hawk) <= -1) goto oops; } if (!(hawk->opt.trait & HAWK_IMPLICIT)) { /* ensure that all functions called are defined in the EXPLICIT-only mode. * otherwise, the error detection will get delayed until run-time. */ hawk_htb_pair_t* p; hawk_htb_itr_t itr; p = hawk_htb_getfirstpair(hawk->parse.funs, &itr); while (p) { if (hawk_htb_search(hawk->tree.funs, HAWK_HTB_KPTR(p), HAWK_HTB_KLEN(p)) == HAWK_NULL) { hawk_nde_t* nde; /* see parse_fncall() for what is stored into hawk->tree.funs */ nde = (hawk_nde_t*)HAWK_HTB_VPTR(p); hawk_seterrfmt(hawk, &nde->loc, HAWK_EFUNNF, FMT_EFUNNF, HAWK_HTB_KLEN(p), HAWK_HTB_KPTR(p)); goto oops; } p = hawk_htb_getnextpair(hawk->parse.funs, &itr); } } if (hawk->parse.pragma.trait & HAWK_PEDANTIC) { hawk_oow_t i; for (i = hawk->tree.ngbls_base ; i < HAWK_ARR_SIZE(hawk->parse.gbls); i++) { const hawk_ptl_t* ptl; hawk_var_xinfo_t vxi; ptl = HAWK_ARR_DPTL(hawk->parse.gbls, i); HAWK_MEMCPY(&vxi, (const hawk_ooch_t*)ptl->ptr + ptl->len, HAWK_SIZEOF(vxi)); if (!vxi.used) { hawk_seterrbfmt(hawk, &vxi.loc, HAWK_EUNUSED, "unused global variable '%.*js'", ptl->len, ptl->ptr); goto oops; } } } HAWK_ASSERT(hawk->tree.ngbls == HAWK_ARR_SIZE(hawk->parse.gbls)); HAWK_ASSERT(hawk->sio.inp == &hawk->sio.arg); ret = 0; oops: if (ret <= -1) { /* an error occurred and control has reached here * probably, some included files might not have been * closed. close them */ while (hawk->sio.inp != &hawk->sio.arg) { hawk_sio_arg_t* prev; /* nothing much to do about a close error */ hawk->sio.inf(hawk, HAWK_SIO_CMD_CLOSE, hawk->sio.inp, HAWK_NULL, 0); prev = hawk->sio.inp->prev; HAWK_ASSERT(hawk->sio.inp->name != HAWK_NULL); hawk_freemem(hawk, hawk->sio.inp); hawk->sio.inp = prev; } } if (hawk->sio.inf(hawk, HAWK_SIO_CMD_CLOSE, hawk->sio.inp, HAWK_NULL, 0) != 0 && ret == 0) ret = -1; /* clear the parse tree partially constructed on error */ if (ret <= -1) hawk_clear(hawk); return ret; } hawk_ooch_t* hawk_addsionamewithuchars (hawk_t* hawk, const hawk_uch_t* ptr, hawk_oow_t len) { hawk_link_t* link; /* TODO: duplication check? */ #if defined(HAWK_OOCH_IS_BCH) hawk_oow_t bcslen, ucslen; ucslen = len; if (hawk_convutobchars(hawk, ptr, &ucslen, HAWK_NULL, &bcslen) <= -1) return HAWK_NULL; link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_bch_t) * (bcslen + 1)); if (HAWK_UNLIKELY(!link)) return HAWK_NULL; ucslen = len; bcslen = bcslen + 1; hawk_convutobchars(hawk, ptr, &ucslen, (hawk_bch_t*)(link + 1), &bcslen); ((hawk_bch_t*)(link + 1))[bcslen] = '\0'; #else link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_uch_t) * (len + 1)); if (HAWK_UNLIKELY(!link)) return HAWK_NULL; hawk_copy_uchars_to_ucstr_unlimited ((hawk_uch_t*)(link + 1), ptr, len); #endif link->link = hawk->sio_names; hawk->sio_names = link; return (hawk_ooch_t*)(link + 1); } hawk_ooch_t* hawk_addsionamewithbchars (hawk_t* hawk, const hawk_bch_t* ptr, hawk_oow_t len) { hawk_link_t* link; /* TODO: duplication check? */ #if defined(HAWK_OOCH_IS_BCH) link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_bch_t) * (len + 1)); if (HAWK_UNLIKELY(!link)) return HAWK_NULL; hawk_copy_bchars_to_bcstr_unlimited ((hawk_bch_t*)(link + 1), ptr, len); #else hawk_oow_t bcslen, ucslen; bcslen = len; if (hawk_convbtouchars(hawk, ptr, &bcslen, HAWK_NULL, &ucslen, 0) <= -1) return HAWK_NULL; link = (hawk_link_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*link) + HAWK_SIZEOF(hawk_uch_t) * (ucslen + 1)); if (HAWK_UNLIKELY(!link)) return HAWK_NULL; bcslen = len; ucslen = ucslen + 1; hawk_convbtouchars(hawk, ptr, &bcslen, (hawk_uch_t*)(link + 1), &ucslen, 0); ((hawk_uch_t*)(link + 1))[ucslen] = '\0'; #endif link->link = hawk->sio_names; hawk->sio_names = link; return (hawk_ooch_t*)(link + 1); } void hawk_clearsionames (hawk_t* hawk) { hawk_link_t* cur; while (hawk->sio_names) { cur = hawk->sio_names; hawk->sio_names = cur->link; hawk_freemem(hawk, cur); } } int hawk_parse (hawk_t* hawk, hawk_sio_cbs_t* sio) { int n; /* the source code istream must be provided */ HAWK_ASSERT(sio != HAWK_NULL); /* the source code input stream must be provided at least */ HAWK_ASSERT(sio->in != HAWK_NULL); if (!sio || !sio->in) { hawk_seterrnum(hawk, HAWK_NULL, HAWK_EINVAL); return -1; } HAWK_ASSERT(hawk->parse.depth.loop == 0); HAWK_ASSERT(hawk->parse.depth.expr == 0); hawk_clear(hawk); hawk_clearsionames(hawk); HAWK_MEMSET(&hawk->sio, 0, HAWK_SIZEOF(hawk->sio)); hawk->sio.inf = sio->in; hawk->sio.outf = sio->out; hawk->sio.last.c = HAWK_OOCI_EOF; /*hawk->sio.arg.name = HAWK_NULL; hawk->sio.arg.handle = HAWK_NULL; hawk->sio.arg.path = HAWK_NULL;*/ hawk->sio.arg.line = 1; hawk->sio.arg.colm = 1; hawk->sio.arg.pragma_trait = 0; hawk->sio.inp = &hawk->sio.arg; n = parse(hawk); if (n == 0 && hawk->sio.outf != HAWK_NULL) n = deparse(hawk); HAWK_ASSERT(hawk->parse.depth.loop == 0); HAWK_ASSERT(hawk->parse.depth.expr == 0); return n; } static int end_include (hawk_t* hawk) { int x; hawk_sio_arg_t* cur; if (hawk->sio.inp == &hawk->sio.arg) return 0; /* no include */ /* if it is an included file, close it and * retry to read a character from an outer file */ x = hawk->sio.inf(hawk, HAWK_SIO_CMD_CLOSE, hawk->sio.inp, HAWK_NULL, 0); /* if closing has failed, still destroy the * sio structure first as normal and return * the failure below. this way, the caller * does not call HAWK_SIO_CMD_CLOSE on * hawk->sio.inp again. */ cur = hawk->sio.inp; hawk->sio.inp = hawk->sio.inp->prev; HAWK_ASSERT(cur->name != HAWK_NULL); /* restore the pragma values */ hawk->parse.pragma.trait = cur->pragma_trait; hawk_freemem(hawk, cur); hawk->parse.depth.incl--; if (x != 0) { /* the failure mentioned above is returned here */ return -1; } hawk->sio.last = hawk->sio.inp->last; return 1; /* ended the included file successfully */ } static int ever_included (hawk_t* hawk, hawk_sio_arg_t* arg) { hawk_oow_t i; for (i = 0; i < hawk->parse.incl_hist.count; i++) { if (HAWK_MEMCMP(&hawk->parse.incl_hist.ptr[i * HAWK_SIZEOF(arg->unique_id)], arg->unique_id, HAWK_SIZEOF(arg->unique_id)) == 0) return 1; } return 0; } static int record_ever_included (hawk_t* hawk, hawk_sio_arg_t* arg) { if (hawk->parse.incl_hist.count >= hawk->parse.incl_hist.capa) { hawk_uint8_t* tmp; hawk_oow_t newcapa; newcapa = hawk->parse.incl_hist.capa + 128; tmp = (hawk_uint8_t*)hawk_reallocmem(hawk, hawk->parse.incl_hist.ptr, newcapa * HAWK_SIZEOF(arg->unique_id)); if (!tmp) return -1; hawk->parse.incl_hist.ptr = tmp; hawk->parse.incl_hist.capa = newcapa; } HAWK_MEMCPY(&hawk->parse.incl_hist.ptr[hawk->parse.incl_hist.count * HAWK_SIZEOF(arg->unique_id)], arg->unique_id, HAWK_SIZEOF(arg->unique_id)); hawk->parse.incl_hist.count++; return 0; } static int begin_include (hawk_t* hawk, int once) { hawk_sio_arg_t* arg = HAWK_NULL; hawk_ooch_t* sio_name; if (hawk_count_oocstr(HAWK_OOECS_PTR(hawk->tok.name)) != HAWK_OOECS_LEN(hawk->tok.name)) { /* a '\0' character included in the include file name. * we don't support such a file name */ hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EIONMNL, FMT_EIONMNL, HAWK_OOECS_LEN(hawk->tok.name)); return -1; } if (hawk->opt.includedirs.ptr) { /* include directory is set... */ /* TODO: search target files in these directories */ } /* store the include-file name into a list * and this list is not deleted after hawk_parse. * the errinfo.loc.file can point to the file name here. */ sio_name = hawk_addsionamewithoochars(hawk, HAWK_OOECS_PTR(hawk->tok.name), HAWK_OOECS_LEN(hawk->tok.name)); if (HAWK_UNLIKELY(!sio_name)) { ADJERR_LOC(hawk, &hawk->ptok.loc); goto oops; } arg = (hawk_sio_arg_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*arg)); if (HAWK_UNLIKELY(!arg)) { ADJERR_LOC(hawk, &hawk->ptok.loc); goto oops; } arg->name = sio_name; arg->line = 1; arg->colm = 1; /* let the argument's prev field point to the current */ arg->prev = hawk->sio.inp; if (hawk->sio.inf(hawk, HAWK_SIO_CMD_OPEN, arg, HAWK_NULL, 0) <= -1) { ADJERR_LOC(hawk, &hawk->tok.loc); goto oops; } /* store the pragma value */ arg->pragma_trait = hawk->parse.pragma.trait; /* but don't change hawk->parse.pragma.trait. it means the included file inherits * the existing progma values. hawk->parse.pragma.trait = (hawk->opt.trait & (HAWK_IMPLICIT | HAWK_MULTILINESTR | HAWK_PEDANTIC | HAWK_RWPIPE | HAWK_STRIPRECSPC | HAWK_STRIPSTRSPC)); */ /* update the current pointer after opening is successful */ hawk->sio.inp = arg; hawk->parse.depth.incl++; if (once && ever_included(hawk, arg)) { end_include(hawk); /* it has been included previously. don't include this file again. */ if (get_token(hawk) <= -1) return -1; /* skip the include file name */ if (MATCH(hawk, TOK_SEMICOLON) || MATCH(hawk, TOK_NEWLINE)) { if (get_token(hawk) <= -1) return -1; /* skip the semicolon */ } } else { /* read in the first character in the included file. * so the next call to get_token() sees the character read * from this file. */ if (record_ever_included(hawk, arg) <= -1 || get_char(hawk) <= -1 || get_token(hawk) <= -1) { end_include(hawk); /* i don't jump to oops since i've called * end_include() where hawk->sio.inp/arg is freed. */ return -1; } } return 0; oops: /* i don't need to free 'link' here since it's linked to hawk->sio_names * that's freed at the beginning of hawk_parse() or by hawk_close(). */ if (arg) hawk_freemem(hawk, arg); return -1; } static nde_hard_bool_t classify_nde_hard_bool_with_depth (hawk_nde_t* nde, int depth) { if (depth >= 256) return NDE_HARD_BOOL_UNKNOWN; /* i don't want to allow deep recursion. TODO: remove hard-coded value or remove recursion? */ switch (nde->type) { case HAWK_NDE_GRP: { hawk_nde_t* cur; nde_hard_bool_t hb, last; cur = ((hawk_nde_grp_t*)nde)->body; HAWK_ASSERT(cur != HAWK_NULL); last = NDE_HARD_BOOL_UNKNOWN; do { hb = classify_nde_hard_bool_with_depth(cur, depth + 1); if (hb == NDE_HARD_BOOL_UNKNOWN) return NDE_HARD_BOOL_UNKNOWN; last = hb; cur = cur->next; } while (cur); return last; } case HAWK_NDE_XFALSE: case HAWK_NDE_XNIL: return NDE_HARD_BOOL_FALSE; case HAWK_NDE_XTRUE: return NDE_HARD_BOOL_TRUE; case HAWK_NDE_INT: return (((hawk_nde_int_t*)nde)->val == 0)? NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE; case HAWK_NDE_FLT: return (((hawk_nde_flt_t*)nde)->val == 0.0)? NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE; case HAWK_NDE_STR: return (((hawk_nde_str_t*)nde)->len == 0)? NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE; case HAWK_NDE_MBS: return (((hawk_nde_mbs_t*)nde)->len == 0)? NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE; case HAWK_NDE_EXP_UNR: if (((hawk_nde_exp_t*)nde)->opcode == HAWK_UNROP_LNOT) { nde_hard_bool_t hb; hb = classify_nde_hard_bool_with_depth(((hawk_nde_exp_t*)nde)->left, depth + 1); if (hb == NDE_HARD_BOOL_UNKNOWN) return NDE_HARD_BOOL_UNKNOWN; return (hb == NDE_HARD_BOOL_TRUE)? NDE_HARD_BOOL_FALSE: NDE_HARD_BOOL_TRUE; } return NDE_HARD_BOOL_UNKNOWN; case HAWK_NDE_EXP_BIN: if (((hawk_nde_exp_t*)nde)->opcode == HAWK_BINOP_LOR || ((hawk_nde_exp_t*)nde)->opcode == HAWK_BINOP_LAND) { nde_hard_bool_t lh, rh; lh = classify_nde_hard_bool_with_depth(((hawk_nde_exp_t*)nde)->left, depth + 1); if (lh == NDE_HARD_BOOL_UNKNOWN) return NDE_HARD_BOOL_UNKNOWN; rh = classify_nde_hard_bool_with_depth(((hawk_nde_exp_t*)nde)->right, depth + 1); if (rh == NDE_HARD_BOOL_UNKNOWN) return NDE_HARD_BOOL_UNKNOWN; if (((hawk_nde_exp_t*)nde)->opcode == HAWK_BINOP_LOR) { return (lh == NDE_HARD_BOOL_TRUE || rh == NDE_HARD_BOOL_TRUE)? NDE_HARD_BOOL_TRUE: NDE_HARD_BOOL_FALSE; } return (lh == NDE_HARD_BOOL_TRUE && rh == NDE_HARD_BOOL_TRUE)? NDE_HARD_BOOL_TRUE: NDE_HARD_BOOL_FALSE; } return NDE_HARD_BOOL_UNKNOWN; default: return NDE_HARD_BOOL_UNKNOWN; } } static HAWK_INLINE int is_nde_hard_false (hawk_nde_t* nde) { return classify_nde_hard_bool_with_depth(nde, 0) == NDE_HARD_BOOL_FALSE; } static HAWK_INLINE int is_nde_hard_true (hawk_nde_t* nde) { return classify_nde_hard_bool_with_depth(nde, 0) == NDE_HARD_BOOL_TRUE; } static int is_nde_unconditional_terminator (hawk_nde_t* nde) { switch (nde->type) { case HAWK_NDE_EXIT: /* exit or @abort */ case HAWK_NDE_RETURN: /* return */ case HAWK_NDE_NEXT: case HAWK_NDE_NEXTFILE: return 1; /* TODO: recursion depth check? */ case HAWK_NDE_BLK: { hawk_nde_t* p; for (p = ((hawk_nde_blk_t*)nde)->body; p; p = p->next) { if (is_nde_unconditional_terminator(p)) return 1; } return 0; } case HAWK_NDE_IF: { hawk_nde_if_t* p = (hawk_nde_if_t*)nde; return p->else_part && is_nde_unconditional_terminator(p->else_part) && is_nde_unconditional_terminator(p->then_part); } default: return 0; } } static int is_nde_safe_to_erase (hawk_nde_t* nde) { HAWK_ASSERT(nde != HAWK_NULL); switch (nde->type) { case HAWK_NDE_NULL: case HAWK_NDE_CHAR: case HAWK_NDE_BCHR: case HAWK_NDE_INT: case HAWK_NDE_FLT: case HAWK_NDE_STR: case HAWK_NDE_MBS: case HAWK_NDE_REX: case HAWK_NDE_XNIL: case HAWK_NDE_XTRUE: case HAWK_NDE_XFALSE: case HAWK_NDE_XARGC: case HAWK_NDE_XARGV: case HAWK_NDE_FUN: case HAWK_NDE_NAMED: case HAWK_NDE_GBL: case HAWK_NDE_LCL: case HAWK_NDE_ARG: return 1; case HAWK_NDE_POS: { /* only $0, $1, $2, ... are erasable. * $i, $"abc", $-1, ... are not erasable */ hawk_nde_t* idx = ((hawk_nde_pos_t*)nde)->val; return idx->type == HAWK_NDE_INT && ((hawk_nde_int_t*)idx)->val >= 0; } case HAWK_NDE_XARGVIDX: { /* erasable if @argv is accessed with a literal positive integer index only */ hawk_nde_t* idx = ((hawk_nde_xargvidx_t*)nde)->pos; return idx->type == HAWK_NDE_INT && ((hawk_nde_int_t*)idx)->val >= 0; } case HAWK_NDE_EXP_UNR: return ((hawk_nde_exp_t*)nde)->opcode == HAWK_UNROP_LNOT && is_nde_safe_to_erase(((hawk_nde_exp_t*)nde)->left); case HAWK_NDE_EXP_BIN: return ((((hawk_nde_exp_t*)nde)->opcode == HAWK_BINOP_LOR) || (((hawk_nde_exp_t*)nde)->opcode == HAWK_BINOP_LAND)) && is_nde_safe_to_erase(((hawk_nde_exp_t*)nde)->left) && is_nde_safe_to_erase(((hawk_nde_exp_t*)nde)->right); case HAWK_NDE_CND: /* parse_basic_expr() has ternary expression folding if the test is hard true/false. * this part is still be reached if the test is not hard true/false */ return is_nde_safe_to_erase(((hawk_nde_cnd_t*)nde)->test) && is_nde_safe_to_erase(((hawk_nde_cnd_t*)nde)->left) && is_nde_safe_to_erase(((hawk_nde_cnd_t*)nde)->right); case HAWK_NDE_GRP: { hawk_nde_t* p; for (p = ((hawk_nde_grp_t*)nde)->body; p; p = p->next) { if (!is_nde_safe_to_erase(p)) return 0; } return 1; } default: return 0; } } static int parse_progunit (hawk_t* hawk) { /* @pragma .... @include "xxxx" @global xxx, xxxx; BEGIN { action } END { action } pattern { action } function name (parameter-list) { statement } */ HAWK_ASSERT(hawk->parse.depth.loop == 0); if (MATCH(hawk, TOK_XGLOBAL)) { hawk_oow_t ngbls; hawk_loc_t gloc; nde_chain_t init = { HAWK_NULL, HAWK_NULL }; hawk->parse.id.block = PARSE_GBL; gloc = hawk->tok.loc; if (get_token(hawk) <= -1) return -1; HAWK_ASSERT(hawk->tree.ngbls == HAWK_ARR_SIZE(hawk->parse.gbls)); ngbls = hawk->tree.ngbls; if (collect_globals(hawk, &init, 0) == HAWK_NULL) { hawk_arr_delete(hawk->parse.gbls, ngbls, HAWK_ARR_SIZE(hawk->parse.gbls) - ngbls); hawk->tree.ngbls = ngbls; if (init.head) hawk_clrpt(hawk, init.head); return -1; } if (init.head && append_init_stmts_to_init_tree(hawk, &init, &gloc) <= -1) { hawk_arr_delete(hawk->parse.gbls, ngbls, HAWK_ARR_SIZE(hawk->parse.gbls) - ngbls); hawk->tree.ngbls = ngbls; hawk_clrpt(hawk, init.head); return -1; } } else if (MATCH(hawk, TOK_XCONST)) { hawk_oow_t ngbls; hawk_loc_t cloc; nde_chain_t init = { HAWK_NULL, HAWK_NULL }; hawk->parse.id.block = PARSE_GBL; cloc = hawk->tok.loc; if (get_token(hawk) <= -1) return -1; HAWK_ASSERT(hawk->tree.ngbls == HAWK_ARR_SIZE(hawk->parse.gbls)); ngbls = hawk->tree.ngbls; if (collect_globals(hawk, &init, 1) == HAWK_NULL) /* @const at the top level */ { hawk_arr_delete(hawk->parse.gbls, ngbls, HAWK_ARR_SIZE(hawk->parse.gbls) - ngbls); hawk->tree.ngbls = ngbls; if (init.head) hawk_clrpt(hawk, init.head); return -1; } if (init.head && append_init_stmts_to_init_tree(hawk, &init, &cloc) <= -1) { hawk_arr_delete(hawk->parse.gbls, ngbls, HAWK_ARR_SIZE(hawk->parse.gbls) - ngbls); hawk->tree.ngbls = ngbls; hawk_clrpt(hawk, init.head); return -1; } } else if (MATCH(hawk, TOK_XINCLUDE) || MATCH(hawk, TOK_XINCLUDE_ONCE)) { int once; if (hawk->opt.depth.s.incl > 0 && hawk->parse.depth.incl >= hawk->opt.depth.s.incl) { hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EINCLTD); return -1; } once = MATCH(hawk, TOK_XINCLUDE_ONCE); if (get_token(hawk) <= -1) return -1; if (!MATCH(hawk, TOK_STR)) { hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EINCLSTR); return -1; } if (begin_include(hawk, once) <= -1) return -1; /* i just return without doing anything special * after having setting up the environment for file * inclusion. the loop in parse() proceeds to call * parse_progunit() */ } else if (MATCH(hawk, TOK_XPRAGMA)) { hawk_oocs_t name; int trait; if (get_token(hawk) <= -1) return -1; if (!MATCH(hawk, TOK_IDENT)) { hawk_seterrfmt(hawk, &hawk->ptok.loc, HAWK_EIDENT, HAWK_T("identifier expected for '@pragma'")); return -1; } name.len = HAWK_OOECS_LEN(hawk->tok.name); name.ptr = HAWK_OOECS_PTR(hawk->tok.name); if (hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("entry"), 0) == 0) { if (get_token(hawk) <= -1) return -1; if (!MATCH(hawk, TOK_IDENT)) { hawk_seterrfmt(hawk, &hawk->ptok.loc, HAWK_EIDENT, HAWK_T("function name expected for 'entry'")); return -1; } if (HAWK_OOECS_LEN(hawk->tok.name) >= HAWK_COUNTOF(hawk->parse.pragma.entry)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EFUNNAM, HAWK_T("entry function name too long")); return -1; } if (hawk->sio.inp == &hawk->sio.arg) { /* only the top level source */ if (hawk->parse.pragma.entry[0] != '\0') { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EEXIST, HAWK_T("@pragma entry already set")); return -1; } hawk_copy_oochars_to_oocstr(hawk->parse.pragma.entry, HAWK_COUNTOF(hawk->parse.pragma.entry), HAWK_OOECS_PTR(hawk->tok.name), HAWK_OOECS_LEN(hawk->tok.name)); } } /* NOTE: trait = is an intended assignment */ else if (((trait = HAWK_IMPLICIT) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("implicit"), 0) == 0) || ((trait = HAWK_DEFER_MODSYM) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("defermodsym"), 0) == 0) || ((trait = HAWK_MULTILINESTR) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("multilinestr"), 0) == 0) || ((trait = HAWK_PEDANTIC) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("pedantic"), 0) == 0) || ((trait = HAWK_RWPIPE) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("rwpipe"), 0) == 0) || ((trait = HAWK_XCALL) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("xcall"), 0) == 0)) { /* @pragma implicit on (default) * @pragma implicit off * @pragma defermodsym on * @pragma defermodsym off (defualt) * @pragma multilinestr on * @pragma multilinestr off (defualt) * @pragma pedantic on * @pragma pedantic off (default) * @pragma rwpipe on (default) * @pragma rwpipe off * @pragma xcall on * @pragma xcall off (default) * * The initial values of these pragmas are set in hawk_clear() * The pragma items defined in this block is compile-time only unlike * the items defined in the next block. */ hawk_oocs_t value; if (get_token(hawk) <= -1) return -1; if (!MATCH(hawk, TOK_IDENT)) { error_ident_on_off_expected_for_implicit: hawk_seterrfmt(hawk, &hawk->ptok.loc, HAWK_EIDENT, HAWK_T("identifier 'on' or 'off' expected for '%.*js'"), name.len, name.ptr); return -1; } value.len = HAWK_OOECS_LEN(hawk->tok.name); value.ptr = HAWK_OOECS_PTR(hawk->tok.name); if (hawk_comp_oochars_oocstr(value.ptr, value.len, HAWK_T("on"), 0) == 0) { hawk->parse.pragma.trait |= trait; } else if (hawk_comp_oochars_oocstr(value.ptr, value.len, HAWK_T("off"), 0) == 0) { hawk->parse.pragma.trait &= ~trait; } else { goto error_ident_on_off_expected_for_implicit; } } /* NOTE: trait = is an intended assignment */ else if (((trait = HAWK_STRIPRECSPC) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("striprecspc"), 0) == 0) || ((trait = HAWK_STRIPSTRSPC) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("stripstrspc"), 0) == 0) || ((trait = HAWK_PIPECLOEXEC) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("pipecloexec"), 0) == 0) || ((trait = HAWK_NUMSTRDETECT) && hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("numstrdetect"), 0) == 0)) { /* @pragma numstrdetect on * @pragma numstrdetect off * @pragma pipecloexec on * @pragma pipecloexec off * @pragma striprecspc on * @pragma striprecspc off * @pragma stripstrspc on * @pragma stripstrspc off * * The initial values of these pragmas are set in hawk_clear() * * The effect of the pragma items listed here are modified at runtime * with corresponding globals values. The interpreter uses these * macros to detemine the actual effect as defined in hawk-prv.h. * - HAWK_RTX_IS_NUMSTRDETECT_ON(rtx) * - HAWK_RTX_IS_PIPECLOEXEC_ON(rtx) * - HAWK_RTX_IS_STRIPRECSPC_ON(rtx) * - HAWK_RTX_IS_STRIPSTRSPC_ON(rtx) * In general, the runtime global variable value takes precedence * over the compile-time pragma specifier value. */ int is_on; hawk_oocs_t value; if (get_token(hawk) <= -1) return -1; if (!MATCH(hawk, TOK_IDENT)) { error_ident_on_off_expected_for_striprecspc: hawk_seterrfmt(hawk, &hawk->ptok.loc, HAWK_EIDENT, HAWK_T("identifier 'on' or 'off' expected for '%.*js'"), name.len, name.ptr); return -1; } value.len = HAWK_OOECS_LEN(hawk->tok.name); value.ptr = HAWK_OOECS_PTR(hawk->tok.name); if (hawk_comp_oochars_oocstr(value.ptr, value.len, HAWK_T("on"), 0) == 0) is_on = 1; else if (hawk_comp_oochars_oocstr(value.ptr, value.len, HAWK_T("off"), 0) == 0) is_on = 0; else goto error_ident_on_off_expected_for_striprecspc; if (hawk->sio.inp == &hawk->sio.arg) { /* only the top level source. ignore the specified pragma in other levels */ if (is_on) hawk->parse.pragma.trait |= trait; else hawk->parse.pragma.trait &= ~trait; } } /* --------------------------------------------------------------------- * the pragmas up to this point affect the parser * the following pragmas affect runtime * --------------------------------------------------------------------- */ else if (hawk_comp_oochars_oocstr(name.ptr, name.len, HAWK_T("stack_limit"), 0) == 0) { hawk_int_t sl; /* @pragma stack_limit 99999 */ if (get_token(hawk) <= -1) return -1; if (!MATCH(hawk, TOK_INT)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EINTLIT, FMT_EINTLIT, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } sl = hawk_oochars_to_int(HAWK_OOECS_PTR(hawk->tok.name), HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOCHARS_TO_INT_MAKE_OPTION(0, 0, 0), HAWK_NULL, HAWK_NULL); if (sl < HAWK_MIN_RTX_STACK_LIMIT) sl = HAWK_MIN_RTX_STACK_LIMIT; else if (sl > HAWK_MAX_RTX_STACK_LIMIT) sl = HAWK_MAX_RTX_STACK_LIMIT; /* take the specified value if it's greater than the existing value */ if (sl > hawk->parse.pragma.rtx_stack_limit) hawk->parse.pragma.rtx_stack_limit = sl; } else { hawk_seterrfmt(hawk, &hawk->ptok.loc, HAWK_EIDENT, HAWK_T("unknown @pragma identifier - %.*js"), name.len, name.ptr); return -1; } if (get_token(hawk) <= -1) return -1; if (MATCH(hawk,TOK_SEMICOLON) && get_token(hawk) <= -1) return -1; } else if (MATCH(hawk, TOK_FUNCTION)) { hawk->parse.id.block = PARSE_FUNCTION; if (parse_function(hawk, 1) == HAWK_NULL) return -1; } else if (MATCH(hawk, TOK_BEGIN)) { if (!(hawk->opt.trait & HAWK_PABLOCK)) /* pattern action block not allowed */ { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EKWFNC, FMT_EKWFNC, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } hawk->parse.id.block = PARSE_BEGIN; if (get_token(hawk) <= -1) return -1; if (MATCH(hawk, TOK_NEWLINE) || MATCH(hawk, TOK_EOF)) { /* when HAWK_NEWLINE is set, * BEGIN and { should be located on the same line */ hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EBLKBEG); return -1; } if (!MATCH(hawk, TOK_LBRACE)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELBRACE, FMT_ELBRACE, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } hawk->parse.id.block = PARSE_BEGIN_BLOCK; if (parse_begin(hawk) == HAWK_NULL) return -1; /* skip a semicolon after an action block if any */ if (MATCH(hawk, TOK_SEMICOLON) && get_token(hawk) <= -1) return -1; } else if (MATCH(hawk, TOK_END)) { if (!(hawk->opt.trait & HAWK_PABLOCK)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EKWFNC, FMT_EKWFNC, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } hawk->parse.id.block = PARSE_END; if (get_token(hawk) <= -1) return -1; if (MATCH(hawk, TOK_NEWLINE) || MATCH(hawk, TOK_EOF)) { /* when HAWK_NEWLINE is set, * END and { should be located on the same line */ hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EBLKEND); return -1; } if (!MATCH(hawk, TOK_LBRACE)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELBRACE, FMT_ELBRACE, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } hawk->parse.id.block = PARSE_END_BLOCK; if (parse_end(hawk) == HAWK_NULL) return -1; /* skip a semicolon after an action block if any */ if (MATCH(hawk,TOK_SEMICOLON) && get_token(hawk) <= -1) return -1; } else if (MATCH(hawk, TOK_LBRACE)) { /* patternless block */ if (!(hawk->opt.trait & HAWK_PABLOCK)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EKWFNC, FMT_EKWFNC, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } hawk->parse.id.block = PARSE_ACTION_BLOCK; if (parse_action_block(hawk, HAWK_NULL, 0) <= -1) return -1; /* skip a semicolon after an action block if any */ if (MATCH(hawk, TOK_SEMICOLON) && get_token(hawk) <= -1) return -1; } else { /* expressions /regular expression/ pattern && pattern pattern || pattern !pattern (pattern) pattern, pattern */ hawk_nde_t* ptn; hawk_loc_t eloc; if (!(hawk->opt.trait & HAWK_PABLOCK)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EKWFNC, FMT_EKWFNC, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } hawk->parse.id.block = PARSE_PATTERN; eloc = hawk->tok.loc; ptn = parse_expr_withdc(hawk, &eloc); if (ptn == HAWK_NULL) return -1; HAWK_ASSERT(ptn->next == HAWK_NULL); if (MATCH(hawk,TOK_COMMA)) { if (get_token(hawk) <= -1) { hawk_clrpt(hawk, ptn); return -1; } eloc = hawk->tok.loc; ptn->next = parse_expr_withdc(hawk, &eloc); if (ptn->next == HAWK_NULL) { hawk_clrpt(hawk, ptn); return -1; } } if (MATCH(hawk,TOK_NEWLINE) || MATCH(hawk,TOK_SEMICOLON) || MATCH(hawk,TOK_EOF)) { /* blockless pattern */ int eof; hawk_loc_t ploc; eof = MATCH(hawk,TOK_EOF); ploc = hawk->ptok.loc; hawk->parse.id.block = PARSE_ACTION_BLOCK; if (parse_action_block(hawk, ptn, 1) <= -1) { hawk_clrpt(hawk, ptn); return -1; } if (!eof) { if (get_token(hawk) <= -1) { /* 'ptn' has been added to the chain. * it doesn't have to be cleared here * as hawk_clear() does it */ /*hawk_clrpt(hawk, ptn);*/ return -1; } } if ((hawk->opt.trait & HAWK_RIO) != HAWK_RIO) { /* blockless pattern requires HAWK_RIO * to be ON because the implicit block is * "print $0" */ hawk_seterrnum(hawk, &ploc, HAWK_ENOSUP); return -1; } } else { /* parse the action block */ if (!MATCH(hawk,TOK_LBRACE)) { hawk_clrpt(hawk, ptn); hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELBRACE, FMT_ELBRACE, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } hawk->parse.id.block = PARSE_ACTION_BLOCK; if (parse_action_block(hawk, ptn, 0) <= -1) { hawk_clrpt(hawk, ptn); return -1; } /* skip a semicolon after an action block if any */ if (MATCH(hawk,TOK_SEMICOLON) && get_token(hawk) <= -1) return -1; } } return 0; } static hawk_nde_t* parse_function (hawk_t* hawk, int named) { hawk_oocs_t name = { HAWK_NULL, 0 }; hawk_oocs_t saved_fun_name; hawk_nde_t* body = HAWK_NULL; hawk_fun_t* fun = HAWK_NULL; hawk_ooi_t org_fun_level; hawk_oow_t lcl_base; hawk_oow_t param_base; hawk_ooch_t* argspec = HAWK_NULL; hawk_oow_t argspeccapa = 0; hawk_oow_t argspeclen = 0; hawk_oow_t nargs, g; int variadic = 0; int has_ref_arg = 0; hawk_htb_pair_t* pair; hawk_loc_t xloc; int rederr; const hawk_ooch_t* redobj; hawk_loc_t fun_loc; fun_loc = hawk->tok.loc; /* make sure that parameter table is empty */ /* remember the number of parameters when parse_function is called. * this base number may not be 0 if parse_function has been called for a function literal * while the outer functions have at least one argument in total */ /*HAWK_ASSERT(HAWK_ARR_SIZE(hawk->parse.params) == 0); this assertion is not valid any more*/ lcl_base = HAWK_ARR_SIZE(hawk->parse.lcls); param_base = HAWK_ARR_SIZE(hawk->parse.params); org_fun_level = hawk->parse.fun_level++; if (hawk->parse.fun_level >= HAWK_COUNTOF(hawk->parse.param_bases)) { /* this may happen for function literals */ hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EBLKNST, HAWK_T("function nesting too deep")); goto oops; } hawk->parse.lcl_bases[hawk->parse.fun_level] = lcl_base; hawk->parse.param_bases[hawk->parse.fun_level] = param_base; hawk->parse.lcl_base = lcl_base; hawk->parse.param_base = param_base; /* eat up the keyword 'function' and get the next token */ HAWK_ASSERT(MATCH(hawk,TOK_FUNCTION)); if (get_token(hawk) <= -1) goto oops; if (named) { hawk_oocs_t fnt; /* file name token */ fnt.ptr = HAWK_OOECS_PTR(hawk->tok.name); fnt.len = HAWK_OOECS_LEN(hawk->tok.name); /* check if an identifier is in place */ if (!MATCH(hawk,TOK_IDENT)) { /* cannot find a valid identifier for a function name */ hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EFUNNAM, HAWK_T("'%.*js' not a valid function name"), fnt.len, fnt.ptr); goto oops; } /* note that i'm assigning to rederr in the 'if' conditions below. * i'm not checking equality */ /* check if it is a builtin function */ if ((hawk_findfncwithoocs(hawk, &fnt) != HAWK_NULL && (rederr = HAWK_EFNCRED, redobj = HAWK_T("intrinsic function"))) || /* check if it has already been defined as a function */ (hawk_htb_search(hawk->tree.funs, fnt.ptr, fnt.len) != HAWK_NULL && (rederr = HAWK_EFUNRED, redobj = HAWK_T("function"))) || /* check if it conflicts with a named variable */ (hawk_htb_search(hawk->parse.named, fnt.ptr, fnt.len) != HAWK_NULL && (rederr = HAWK_EVARRED, redobj = HAWK_T("variable"))) || /* check if it coincides to be a global variable name */ (((g = find_global(hawk, &fnt)) != HAWK_ARR_NIL) && (rederr = HAWK_EGBLRED, redobj = HAWK_T("global variable")))) { hawk_seterrfmt(hawk, &hawk->tok.loc, rederr, HAWK_T("%js '%.*js' redefined"), redobj, fnt.len, fnt.ptr); goto oops; } /* duplicate the name before it's overridden by get_token() */ name.ptr = hawk_dupoochars(hawk, fnt.ptr, fnt.len); if (HAWK_UNLIKELY(!name.ptr)) { ADJERR_LOC(hawk, &hawk->tok.loc); goto oops; } name.len = fnt.len; /* get the next token */ if (get_token(hawk) <= -1) goto oops; } /* match a left parenthesis */ if (!MATCH(hawk,TOK_LPAREN)) { /* named - a function name is not followed by a left parenthesis. * unnamed - the function keyword is not followed by a left parenthesis*/ hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } /* get the next token */ if (get_token(hawk) <= -1) goto oops; /* read parameter list */ if (MATCH(hawk,TOK_RPAREN)) { /* no function parameter found. get the next token */ if (get_token(hawk) <= -1) goto oops; } else { while (1) { hawk_ooch_t* pa; hawk_oow_t pal; if (MATCH(hawk, TOK_ELLIPSIS)) { /* this must be the last parameter. the variadic part doesn't support reference */ /* this must be the last token before the parenthesis if given. * function xxx (...) * function xxx (a ...) * function xxx (a, b ...) */ if (get_token(hawk) <= -1) goto oops; if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } variadic = 1; break; } nargs = HAWK_ARR_SIZE(hawk->parse.params) - hawk->parse.param_base; if (MATCH(hawk, TOK_BAND)) /* &arg */ { /* pass-by-reference argument */ if (nargs >= argspeccapa) { hawk_oow_t i, newcapa = HAWK_ALIGN_POW2(nargs + 2, 64); argspec = hawk_reallocmem(hawk, argspec, newcapa * HAWK_SIZEOF(*argspec)); if (HAWK_UNLIKELY(!argspec)) goto oops; for (i = argspeccapa; i < newcapa; i++) argspec[i] = HAWK_T(' '); argspeccapa = newcapa; } argspec[nargs] = 'r'; if (get_token(hawk) <= -1) goto oops; has_ref_arg = 1; } if (!MATCH(hawk,TOK_IDENT)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EBADPAR, HAWK_T("'%.*js' not a valid parameter name"), HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } pa = HAWK_OOECS_PTR(hawk->tok.name); pal = HAWK_OOECS_LEN(hawk->tok.name); /* NOTE: the following is not a conflict. * so the parameter is not checked against * global variables. * global x; * function f (x) { print x; } * x in print x is a parameter */ if (name.ptr) /* named */ { /* check if a parameter conflicts with the function * name or other parameters */ if ((hawk->opt.trait & HAWK_STRICTNAMING) && hawk_comp_oochars(pa, pal, name.ptr, name.len, 0) == 0) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EDUPPAR, HAWK_T("conflicting parameter name '%.*js' with function name"), pal, pa); goto oops; } } if (hawk_arr_search(hawk->parse.params, hawk->parse.param_base, HAWK_ARR_SIZE(hawk->parse.params), pa, pal) != HAWK_ARR_NIL) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EDUPPAR, HAWK_T("duplicate parameter name '%.*js'"), pal, pa); goto oops; } /* push the parameter to the parameter list */ if (nargs >= HAWK_MAX_PARAMS) { hawk_seterrnum(hawk, &hawk->tok.loc, HAWK_EPARTM); goto oops; } /* the insertion position is HAWK_ARR_SIZE(hawk->parse.params). * it must not be nargs which is relative to outer functions defined */ if (hawk_arr_insert(hawk->parse.params, HAWK_ARR_SIZE(hawk->parse.params), pa, pal) == HAWK_ARR_NIL) { ADJERR_LOC(hawk, &hawk->tok.loc); goto oops; } if (get_token(hawk) <= -1) goto oops; /* no ... present after the variable name */ if (MATCH(hawk,TOK_RPAREN)) break; if (!MATCH(hawk,TOK_COMMA)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ECOMMA, FMT_ECOMMA, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk,TOK_NEWLINE)); } if (argspec) { /* argspec is set to a non-null value if there is a least 1 reference parameter encountered. * when it's set, nargs is also updated. * * nargs is the number taken before the current argument word was added to the parse.params array. * so the actual number of arguments is nargs + 1 */ argspeclen = nargs + 1; argspec[argspeclen] = '\0'; } if (get_token(hawk) <= -1) goto oops; } /* function body can be placed on a different line * from a function name and the parameters even if * HAWK_NEWLINE is set. note TOK_NEWLINE is * available only when the option is set. */ while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } /* check if the function body starts with a left brace */ if (!MATCH(hawk,TOK_LBRACE)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELBRACE, FMT_ELBRACE, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; /* remember the current function name so that the body parser * can know the name of the current function being parsed */ saved_fun_name = hawk->tree.cur_fun; hawk->tree.cur_fun = name; /* actual function body */ xloc = hawk->ptok.loc; body = parse_block_dc(hawk, &xloc, 1); /* clear the current function name remembered */ hawk->tree.cur_fun = saved_fun_name; if (!body) goto oops; /* TODO: study furthur if the parameter names should be saved * for some reasons - might be needed for better deparsing output */ nargs = HAWK_ARR_SIZE(hawk->parse.params) - param_base; /* parameter names are not required anymore. clear them */ /*hawk_arr_clear(hawk->parse.params);*/ hawk_arr_setsize(hawk->parse.params, param_base); hawk->parse.fun_level--; if (hawk->parse.fun_level >= 0) { hawk->parse.lcl_base = hawk->parse.lcl_bases[hawk->parse.fun_level]; hawk->parse.param_base = hawk->parse.param_bases[hawk->parse.fun_level]; } else { hawk->parse.lcl_base = 0; hawk->parse.param_base = 0; } fun = (hawk_fun_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*fun)); if (HAWK_UNLIKELY(!fun)) { ADJERR_LOC(hawk, &hawk->tok.loc); goto oops; } /*fun->name.ptr = HAWK_NULL;*/ /* function name is set below */ fun->name.len = 0; fun->nargs = nargs; fun->variadic = variadic; fun->hasrefarg = has_ref_arg; fun->argspec = argspec; fun->argspeclen = argspeclen; fun->body = (hawk_nde_t*)body; if (name.ptr) /* named */ { pair = hawk_htb_insert(hawk->tree.funs, name.ptr, name.len, fun, 0); if (HAWK_UNLIKELY(!pair)) { /* if hawk_htb_insert() fails for other reasons than memory * shortage, there should be implementaion errors as duplicate * functions are detected earlier in this function */ ADJERR_LOC(hawk, &hawk->tok.loc); goto oops; } /* do some trick to save a string. make it back-point at the key part * of the pair */ fun->name.ptr = HAWK_HTB_KPTR(pair); fun->name.len = HAWK_HTB_KLEN(pair); hawk_freemem(hawk, name.ptr); /* remove an undefined function call entry from the parse.fun table */ hawk_htb_delete(hawk->parse.funs, fun->name.ptr, name.len); } else /* unnamed */ { hawk_nde_fun_t* funval = HAWK_NULL; /*fun->name.ptr = HAWK_T(""); TODO: is this needed? */ funval = (hawk_nde_fun_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*funval)); if (HAWK_UNLIKELY(!funval)) { ADJERR_LOC(hawk, &hawk->tok.loc); goto oops; } funval->type = HAWK_NDE_FUN; funval->loc = fun_loc; funval->name.ptr = HAWK_NULL; funval->name.len = 0; funval->funptr = fun; if (hawk_arr_pushstack(hawk->tree.ifuns, fun, 0) == HAWK_ARR_NIL) { ADJERR_LOC(hawk, &hawk->tok.loc); /* since i'm limiting funval within this 'else' block, * i need to free it here, not after the 'oops' label */ hawk_freemem(hawk, funval); goto oops; } body = (hawk_nde_t*)funval; } return body; oops: if (body) hawk_clrpt(hawk, body); if (argspec) hawk_freemem(hawk, argspec); if (fun) hawk_freemem(hawk, fun); if (name.ptr) hawk_freemem(hawk, name.ptr); if (hawk->parse.fun_level > org_fun_level) { /* NOTE there is no setsize on hawk->parse.lcls because * it's handled by parse_blocks as necessary */ hawk_arr_setsize(hawk->parse.params, param_base); hawk->parse.fun_level--; if (hawk->parse.fun_level >= 0) { hawk->parse.lcl_base = hawk->parse.lcl_bases[hawk->parse.fun_level]; hawk->parse.param_base = hawk->parse.param_bases[hawk->parse.fun_level]; } else { hawk->parse.lcl_base = 0; hawk->parse.param_base = 0; } } return HAWK_NULL; } static hawk_nde_t* parse_begin (hawk_t* hawk) { hawk_nde_t* nde; hawk_loc_t xloc; xloc = hawk->tok.loc; HAWK_ASSERT(MATCH(hawk,TOK_LBRACE)); if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_block_dc(hawk, &xloc, 1); if (HAWK_UNLIKELY(!nde)) return HAWK_NULL; if (!hawk->tree.begin) hawk->tree.begin = nde; else hawk->tree.begin_tail->next = nde; hawk->tree.begin_tail = nde; return nde; } static hawk_nde_t* parse_end (hawk_t* hawk) { hawk_nde_t* nde; hawk_loc_t xloc; xloc = hawk->tok.loc; HAWK_ASSERT(MATCH(hawk,TOK_LBRACE)); if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_block_dc(hawk, &xloc, 1); if (HAWK_UNLIKELY(!nde)) return HAWK_NULL; if (!hawk->tree.end) hawk->tree.end = nde; else hawk->tree.end_tail->next = nde; hawk->tree.end_tail = nde; return nde; } static int parse_action_block (hawk_t* hawk, hawk_nde_t* ptn, int blockless) { hawk_nde_t* nde; hawk_chain_t* chain; hawk_loc_t xloc; xloc = hawk->tok.loc; if (blockless) nde = HAWK_NULL; else { HAWK_ASSERT(MATCH(hawk,TOK_LBRACE)); if (get_token(hawk) <= -1) return -1; nde = parse_block_dc(hawk, &xloc, 1); if (HAWK_UNLIKELY(!nde)) return -1; } chain = (hawk_chain_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*chain)); if (HAWK_UNLIKELY(!chain)) { hawk_clrpt(hawk, nde); ADJERR_LOC(hawk, &xloc); return -1; } chain->pattern = ptn; chain->action = nde; chain->next = HAWK_NULL; if (hawk->tree.chain == HAWK_NULL) { hawk->tree.chain = chain; hawk->tree.chain_tail = chain; hawk->tree.chain_size++; } else { hawk->tree.chain_tail->next = chain; hawk->tree.chain_tail = chain; hawk->tree.chain_size++; } return 0; } static int parse_block_head (hawk_t* hawk, int flags, nde_chain_t* inits) { nde_chain_t local_inits = { HAWK_NULL, HAWK_NULL }; hawk_oow_t nlcls_outer; nlcls_outer = HAWK_ARR_SIZE(hawk->parse.lcls); /* local variable declarations */ while (1) { /* skip new lines before local declaration in a block*/ while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } if (MATCH(hawk,TOK_XINCLUDE) || MATCH(hawk, TOK_XINCLUDE_ONCE)) { /* @include ... */ int once; if (hawk->opt.depth.s.incl > 0 && hawk->parse.depth.incl >= hawk->opt.depth.s.incl) { hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EINCLTD); goto oops; } once = MATCH(hawk, TOK_XINCLUDE_ONCE); if (get_token(hawk) <= -1) goto oops; if (!MATCH(hawk,TOK_STR)) { hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EINCLSTR); goto oops; } if (begin_include(hawk, once) <= -1) goto oops; } else if (MATCH(hawk,TOK_XLOCAL)) { nde_chain_t init = { HAWK_NULL, HAWK_NULL }; /* @local ... */ if (get_token(hawk) <= -1) { hawk_arr_delete(hawk->parse.lcls, nlcls_outer, HAWK_ARR_SIZE(hawk->parse.lcls) - nlcls_outer); goto oops; } if (collect_locals(hawk, nlcls_outer, flags, &init, 0) == HAWK_NULL) { hawk_arr_delete(hawk->parse.lcls, nlcls_outer, HAWK_ARR_SIZE(hawk->parse.lcls) - nlcls_outer); goto oops; } if (init.head) { if (local_inits.head == HAWK_NULL) local_inits.head = init.head; else local_inits.tail->next = init.head; local_inits.tail = init.tail; } } else if (MATCH(hawk,TOK_XCONST)) { nde_chain_t init = { HAWK_NULL, HAWK_NULL }; /* @const ... */ if (get_token(hawk) <= -1) { hawk_arr_delete(hawk->parse.lcls, nlcls_outer, HAWK_ARR_SIZE(hawk->parse.lcls) - nlcls_outer); goto oops; } if (collect_locals(hawk, nlcls_outer, flags, &init, 1) == HAWK_NULL) { hawk_arr_delete(hawk->parse.lcls, nlcls_outer, HAWK_ARR_SIZE(hawk->parse.lcls) - nlcls_outer); goto oops; } if (init.head) { if (local_inits.head == HAWK_NULL) local_inits.head = init.head; else local_inits.tail->next = init.head; local_inits.tail = init.tail; } } else break; } *inits = local_inits; return 0; oops: if (local_inits.head) hawk_clrpt(hawk, local_inits.head); return -1; } static hawk_nde_t* parse_block (hawk_t* hawk, const hawk_loc_t* xloc, int flags) { hawk_nde_t* head, * curr, * nde; hawk_nde_blk_t* block; hawk_oow_t nlcls_outer, nlcls_max, tmp; nde_chain_t local_inits = { HAWK_NULL, HAWK_NULL }; int dead_code; nlcls_outer = HAWK_ARR_SIZE(hawk->parse.lcls); nlcls_max = hawk->parse.nlcls_max; /* block head */ if (parse_block_head(hawk, flags, &local_inits) <= -1) return HAWK_NULL; /* block body */ head = local_inits.head; curr = local_inits.tail; dead_code = 0; while (1) { /* skip new lines within a block */ while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } /* if EOF is met before the right brace, this is an error */ if (MATCH(hawk,TOK_EOF)) { hawk_seterrnum(hawk, &hawk->tok.loc, HAWK_EEOF); goto oops; } /* end the block when the right brace is met */ if (MATCH(hawk,TOK_RBRACE)) { if (get_token(hawk) <= -1) goto oops; break; } else if (MATCH(hawk, TOK_XINCLUDE) || MATCH(hawk, TOK_XINCLUDE_ONCE)) { int once; if (hawk->opt.depth.s.incl > 0 && hawk->parse.depth.incl >= hawk->opt.depth.s.incl) { hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EINCLTD); goto oops; } once = MATCH(hawk, TOK_XINCLUDE_ONCE); if (get_token(hawk) <= -1) goto oops; if (!MATCH(hawk,TOK_STR)) { hawk_seterrnum(hawk, &hawk->ptok.loc, HAWK_EINCLSTR); goto oops; } if (begin_include(hawk, once) <= -1) goto oops; } else { /* parse an actual statement in a block */ hawk_loc_t sloc; sloc = hawk->tok.loc; nde = parse_statement(hawk, &sloc); if (HAWK_UNLIKELY(!nde)) goto oops; /* remove unnecessary statements such as adjacent * null statements */ if (nde->type == HAWK_NDE_NULL) { hawk_clrpt(hawk, nde); continue; } if (nde->type == HAWK_NDE_BLK && ((hawk_nde_blk_t*)nde)->body == HAWK_NULL) { hawk_clrpt(hawk, nde); continue; } if (dead_code) { hawk_clrpt(hawk, nde); continue; } if (curr == HAWK_NULL) head = nde; else curr->next = nde; curr = nde; if (is_nde_unconditional_terminator(nde)) dead_code = 1; } } if (hawk->parse.pragma.trait & HAWK_PEDANTIC) { hawk_oow_t i; for (i = nlcls_outer ; i < HAWK_ARR_SIZE(hawk->parse.lcls); i++) { const hawk_ptl_t* ptl; hawk_var_xinfo_t vxi; ptl = HAWK_ARR_DPTL(hawk->parse.lcls, i); HAWK_MEMCPY(&vxi, (const hawk_ooch_t*)ptl->ptr + ptl->len, HAWK_SIZEOF(vxi)); if (!vxi.used) { hawk_seterrbfmt(hawk, &vxi.loc, HAWK_EUNUSED, "unused local variable '%.*js'", ptl->len, ptl->ptr); goto oops; } } } block = (hawk_nde_blk_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*block)); if (HAWK_UNLIKELY(!block)) { ADJERR_LOC(hawk, xloc); goto oops; } tmp = HAWK_ARR_SIZE(hawk->parse.lcls); if (tmp > hawk->parse.nlcls_max) hawk->parse.nlcls_max = tmp; /* remove all lcls to move them up to the top level */ hawk_arr_delete(hawk->parse.lcls, nlcls_outer, tmp - nlcls_outer); /* adjust the number of lcls for a block without any statements */ /* if (head == HAWK_NULL) tmp = 0; */ block->type = HAWK_NDE_BLK; block->loc = *xloc; block->body = head; block->org_nlcls = tmp - nlcls_outer; /* number of locals defined in this block */ block->outer_nlcls = nlcls_outer - hawk->parse.lcl_base; /* number of locals defined in outer blocks */ #if 1 /* 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 the outermost block */ if (flags & PARSE_BLOCK_FLAG_IS_TOP) { /* if there is no nested function literals, the following condition must be true. HAWK_ASSERT(nlcls_outer == 0 && nlcls_max == 0); with the intruduction of function literlas, it isn't true any more. */ block->nlcls = hawk->parse.nlcls_max - nlcls_outer; hawk->parse.nlcls_max = nlcls_max; /* restore */ } else { block->nlcls = 0; } #else /* no migration */ block->nlcls = block->org_nlcls; #endif return (hawk_nde_t*)block; oops: hawk_arr_delete(hawk->parse.lcls, nlcls_outer, HAWK_ARR_SIZE(hawk->parse.lcls) - nlcls_outer); hawk_clrpt(hawk, head); return HAWK_NULL; } static hawk_nde_t* parse_block_dc (hawk_t* hawk, const hawk_loc_t* xloc, int flags) { hawk_nde_t* nde; /* perform the depth check before calling parse_block() */ if (hawk->opt.depth.s.block_parse > 0 && hawk->parse.depth.block >= hawk->opt.depth.s.block_parse) { hawk_seterrnum(hawk, xloc, HAWK_EBLKNST); return HAWK_NULL; } hawk->parse.depth.block++; nde = parse_block(hawk, xloc, flags); hawk->parse.depth.block--; return nde; } int hawk_initgbls (hawk_t* hawk) { int id; /* hawk_initgbls is not generic-purpose. call this from * hawk_open only. */ HAWK_ASSERT(hawk->tree.ngbls_base == 0 && hawk->tree.ngbls == 0); hawk->tree.ngbls_base = 0; hawk->tree.ngbls = 0; for (id = HAWK_MIN_GBL_ID; id <= HAWK_MAX_GBL_ID; id++) { hawk_oow_t g; g = hawk_arr_insert(hawk->parse.gbls, HAWK_ARR_SIZE(hawk->parse.gbls), (hawk_ooch_t*)gtab[id].name, gtab[id].namelen); if (g == HAWK_ARR_NIL) return -1; HAWK_ASSERT((int)g == id); hawk->tree.ngbls_base++; hawk->tree.ngbls++; } HAWK_ASSERT(hawk->tree.ngbls_base == HAWK_MAX_GBL_ID - HAWK_MIN_GBL_ID + 1); return 0; } static void adjust_static_globals (hawk_t* hawk) { int id; HAWK_ASSERT(hawk->tree.ngbls_base >= HAWK_MAX_GBL_ID - HAWK_MAX_GBL_ID + 1); for (id = HAWK_MIN_GBL_ID; id <= HAWK_MAX_GBL_ID; id++) { if ((hawk->opt.trait & gtab[id].trait) != gtab[id].trait) { HAWK_ARR_DLEN(hawk->parse.gbls,id) = 0; } else { HAWK_ARR_DLEN(hawk->parse.gbls,id) = gtab[id].namelen; } } } static hawk_oow_t get_global (hawk_t* hawk, const hawk_oocs_t* name) { hawk_oow_t i; hawk_arr_t* gbls = hawk->parse.gbls; for (i = HAWK_ARR_SIZE(gbls); i > 0; ) { i--; if (hawk_comp_oochars(HAWK_ARR_DPTR(gbls,i), HAWK_ARR_DLEN(gbls,i), name->ptr, name->len, 0) == 0) return i; } return HAWK_ARR_NIL; } static hawk_oow_t get_or_add_named (hawk_t* hawk, const hawk_oocs_t* name) { hawk_htb_pair_t* pair; hawk_oow_t idx; pair = hawk_htb_search(hawk->parse.named, name->ptr, name->len); if (pair) { idx = (hawk_oow_t)HAWK_HTB_VPTR(pair); return idx; } idx = HAWK_HTB_SIZE(hawk->parse.named); pair = hawk_htb_upsert(hawk->parse.named, name->ptr, name->len, (void*)idx, 0); if (!pair) return (hawk_oow_t)-1; return idx; } static hawk_oow_t find_global (hawk_t* hawk, const hawk_oocs_t* name) { hawk_oow_t i; hawk_arr_t* gbls = hawk->parse.gbls; for (i = 0; i < HAWK_ARR_SIZE(gbls); i++) { if (hawk_comp_oochars(HAWK_ARR_DPTR(gbls,i), HAWK_ARR_DLEN(gbls,i), name->ptr, name->len, 0) == 0) return i; } return HAWK_ARR_NIL; } static int add_global (hawk_t* hawk, const hawk_oocs_t* name, hawk_loc_t* xloc, int disabled) { hawk_oow_t ngbls; hawk_oow_t n; /* check if it is a keyword */ if (classify_ident(hawk, name) != TOK_IDENT) { hawk_seterrfmt(hawk, xloc, HAWK_EKWRED, HAWK_T("unable to add global '%.*js' - keyword '%.*js' redefined"), name->len, name->ptr, name->len, name->ptr); return -1; } /* check if it conflicts with a builtin function name */ if (hawk_findfncwithoocs(hawk, name) != HAWK_NULL) { hawk_seterrfmt(hawk, xloc, HAWK_EFNCRED, HAWK_T("unable to add global '%.*js' - intrinsic function '%.*js' redefined"), name->len, name->ptr, name->len, name->ptr); return -1; } /* check if it conflicts with a function name */ if (hawk_htb_search(hawk->tree.funs, name->ptr, name->len) != HAWK_NULL || /* check if it conflicts with a function name caught in the function call table */ hawk_htb_search(hawk->parse.funs, name->ptr, name->len) != HAWK_NULL) { hawk_seterrfmt(hawk, xloc, HAWK_EFUNRED, HAWK_T("unable to add global '%.*js' - function '%.*js' redefined"), name->len, name->ptr, name->len, name->ptr); return -1; } /* check if it conflicts with other global variable names */ if (find_global(hawk, name) != HAWK_ARR_NIL) { hawk_seterrfmt(hawk, xloc, HAWK_EDUPGBL, HAWK_T("unable to add global '%.*js' - duplicate global variable name '%.*js'"), name->len, name->ptr, name->len, name->ptr); return -1; } #if 0 /* TODO: need to check if it conflicts with a named variable to * disallow such a program shown below (IMPLICIT & EXPLICIT on) * BEGIN {X=20; x(); x(); x(); print X} * @global X; * function x() { print X++; } */ if (hawk_htb_search(hawk->parse.named, name, len) != HAWK_NULL) { hawk_seterrfmt(hawk, xloc, HAWK_EVARRED, HAWK_T("variable '%.*js' redefined"), len, name); return -1; } #endif ngbls = HAWK_ARR_SIZE(hawk->parse.gbls); if (ngbls >= HAWK_MAX_GBLS) { hawk_seterrfmt(hawk, xloc, HAWK_EGBLTM, HAWK_T("unable to add global '%.*js' - too many globals"), name->len, name->ptr); return -1; } n = hawk_arr_insert(hawk->parse.gbls, HAWK_ARR_SIZE(hawk->parse.gbls), (hawk_ooch_t*)name->ptr, name->len); if (n == HAWK_ARR_NIL) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, xloc, hawk_geterrnum(hawk), HAWK_T("unable to add global '%.*js' - %js"), name->len, name->ptr, bem); return -1; } else if (hawk->parse.pragma.trait & HAWK_PEDANTIC) /* a variable collected when pendatic on will xinfo */ { const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; /* remember location information of each local variable collected */ ptl = HAWK_ARR_DPTL(hawk->parse.gbls, n); /* n is the position of the inserted name */ vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); if (xloc) vxi->loc = *xloc; else vxi->used = 1; /* if xloc is not given, it's not declared with @global. set used to 1 for simple check */ } HAWK_ASSERT(ngbls == HAWK_ARR_SIZE(hawk->parse.gbls) - 1); /* the disabled item is inserted normally but * the name length is reset to zero. */ if (disabled) HAWK_ARR_DLEN(hawk->parse.gbls,ngbls) = 0; hawk->tree.ngbls = HAWK_ARR_SIZE(hawk->parse.gbls); HAWK_ASSERT(ngbls == hawk->tree.ngbls - 1); /* return the id which is the index to the gbl table. */ return (int)ngbls; } int hawk_addgblwithbcstr (hawk_t* hawk, const hawk_bch_t* name) { int n; hawk_bcs_t ncs; if (hawk->tree.ngbls > hawk->tree.ngbls_base) { /* this function is not allowed after hawk_parse() is called */ hawk_seterrbfmt(hawk, HAWK_NULL, HAWK_EPERM, "not permitted to add global '%hs'", name); return -1; } ncs.ptr = (hawk_bch_t*)name; ncs.len = hawk_count_bcstr(name); if (ncs.len <= 0) { hawk_seterrbfmt(hawk, HAWK_NULL, HAWK_EINVAL, "blank global name not permitted"); return -1; } #if defined(HAWK_OOCH_IS_BCH) n = add_global(hawk, &ncs, HAWK_NULL, 0); #else { hawk_ucs_t wcs; wcs.ptr = hawk_dupbtoucstr(hawk, ncs.ptr, &wcs.len, 0); if (HAWK_UNLIKELY(!wcs.ptr)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, hawk_geterrnum(hawk), HAWK_T("unable to add global '%hs' - %js"), name, bem); return -1; } n = add_global(hawk, &wcs, HAWK_NULL, 0); hawk_freemem(hawk, wcs.ptr); } #endif /* update the count of the static globals. * the total global count has been updated inside add_global. */ if (n >= 0) hawk->tree.ngbls_base++; return n; } int hawk_addgblwithucstr (hawk_t* hawk, const hawk_uch_t* name) { int n; hawk_ucs_t ncs; if (hawk->tree.ngbls > hawk->tree.ngbls_base) { /* this function is not allowed after hawk_parse is called */ hawk_seterrbfmt(hawk, HAWK_NULL, HAWK_EPERM, "not permitted to add global '%ls'", name); return -1; } ncs.ptr = (hawk_uch_t*)name; ncs.len = hawk_count_ucstr(name); if (ncs.len <= 0) { hawk_seterrbfmt(hawk, HAWK_NULL, HAWK_EINVAL, "blank global name not permitted"); return -1; } #if defined(HAWK_OOCH_IS_BCH) { hawk_bcs_t mbs; mbs.ptr = hawk_duputobcstr(hawk, ncs.ptr, &mbs.len); if (HAWK_UNLIKELY(!mbs.ptr)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, hawk_geterrnum(hawk), HAWK_T("unable to add global '%ls' - %js"), name, bem); return -1; } n = add_global(hawk, &mbs, HAWK_NULL, 0); hawk_freemem(hawk, mbs.ptr); } #else n = add_global(hawk, &ncs, HAWK_NULL, 0); #endif /* update the count of the static globals. * the total global count has been updated inside add_global. */ if (n >= 0) hawk->tree.ngbls_base++; return n; } #define HAWK_NUM_STATIC_GBLS \ (HAWK_MAX_GBL_ID-HAWK_MIN_GBL_ID+1) int hawk_delgblwithbcstr (hawk_t* hawk, const hawk_bch_t* name) { hawk_oow_t n; hawk_bcs_t ncs; hawk_ucs_t wcs; ncs.ptr = (hawk_bch_t*)name; ncs.len = hawk_count_bcstr(name); if (hawk->tree.ngbls > hawk->tree.ngbls_base) { /* this function is not allow after hawk_parse is called */ hawk_seterrnum(hawk, HAWK_NULL, HAWK_EPERM); return -1; } #if defined(HAWK_OOCH_IS_BCH) n = hawk_arr_search(hawk->parse.gbls, HAWK_NUM_STATIC_GBLS, HAWK_ARR_SIZE(hawk->parse.gbls), ncs.ptr, ncs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_HS, ncs.len, ncs.ptr); return -1; } #else wcs.ptr = hawk_dupbtoucstr(hawk, ncs.ptr, &wcs.len, 0); if (HAWK_UNLIKELY(!wcs.ptr)) return -1; n = hawk_arr_search(hawk->parse.gbls, HAWK_NUM_STATIC_GBLS, HAWK_ARR_SIZE(hawk->parse.gbls), wcs.ptr, wcs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_LS, wcs.len, wcs.ptr); hawk_freemem(hawk, wcs.ptr); return -1; } hawk_freemem(hawk, wcs.ptr); #endif /* invalidate the name if deletion is requested. * this approach does not delete the entry. * if hawk_delgbl() is called with the same name * again, the entry will be appended again. * never call this funciton unless it is really required. */ /* hawk->parse.gbls.buf[n].name.ptr[0] = HAWK_T('\0'); hawk->parse.gbls.buf[n].name.len = 0; */ n = hawk_arr_uplete(hawk->parse.gbls, n, 1); HAWK_ASSERT(n == 1); return 0; } int hawk_delgblwithucstr (hawk_t* hawk, const hawk_uch_t* name) { hawk_oow_t n; hawk_ucs_t ncs; #if defined(HAWK_OOCH_IS_BCH) hawk_bcs_t mbs; #endif ncs.ptr = (hawk_uch_t*)name; ncs.len = hawk_count_ucstr(name); if (hawk->tree.ngbls > hawk->tree.ngbls_base) { /* this function is not allow after hawk_parse is called */ hawk_seterrnum(hawk, HAWK_NULL, HAWK_EPERM); return -1; } #if defined(HAWK_OOCH_IS_BCH) mbs.ptr = hawk_duputobcstr(hawk, ncs.ptr, &mbs.len); if (HAWK_UNLIKELY(!mbs.ptr)) return -1; n = hawk_arr_search(hawk->parse.gbls, HAWK_NUM_STATIC_GBLS, HAWK_ARR_SIZE(hawk->parse.gbls), mbs.ptr, mbs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_HS, mbs.len, mbs.ptr); hawk_freemem(hawk, mbs.ptr); return -1; } hawk_freemem(hawk, mbs.ptr); #else n = hawk_arr_search(hawk->parse.gbls, HAWK_NUM_STATIC_GBLS, HAWK_ARR_SIZE(hawk->parse.gbls), ncs.ptr, ncs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_LS, ncs.len, ncs.ptr); return -1; } #endif /* invalidate the name if deletion is requested. * this approach does not delete the entry. * if hawk_delgbl() is called with the same name * again, the entry will be appended again. * never call this funciton unless it is really required. */ /* hawk->parse.gbls.buf[n].name.ptr[0] = HAWK_T('\0'); hawk->parse.gbls.buf[n].name.len = 0; */ n = hawk_arr_uplete(hawk->parse.gbls, n, 1); HAWK_ASSERT(n == 1); return 0; } int hawk_findgblwithbcstr (hawk_t* hawk, const hawk_bch_t* name, int inc_builtins) { hawk_oow_t n; hawk_bcs_t ncs; hawk_ucs_t wcs; ncs.ptr = (hawk_bch_t*)name; ncs.len = hawk_count_bcstr(name); #if defined(HAWK_OOCH_IS_BCH) n = hawk_arr_search(hawk->parse.gbls, (inc_builtins? 0: HAWK_NUM_STATIC_GBLS), HAWK_ARR_SIZE(hawk->parse.gbls), ncs.ptr, ncs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_HS, ncs.len, ncs.ptr); return -1; } #else wcs.ptr = hawk_dupbtoucstr(hawk, ncs.ptr, &wcs.len, 0); if (HAWK_UNLIKELY(!wcs.ptr)) return -1; n = hawk_arr_search(hawk->parse.gbls, (inc_builtins? 0: HAWK_NUM_STATIC_GBLS), HAWK_ARR_SIZE(hawk->parse.gbls), wcs.ptr, wcs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_LS, wcs.len, wcs.ptr); hawk_freemem(hawk, wcs.ptr); return -1; } hawk_freemem(hawk, wcs.ptr); #endif return (int)n; } int hawk_findgblwithucstr (hawk_t* hawk, const hawk_uch_t* name, int inc_builtins) { hawk_oow_t n; hawk_ucs_t ncs; #if defined(HAWK_OOCH_IS_BCH) hawk_bcs_t mbs; #endif ncs.ptr = (hawk_uch_t*)name; ncs.len = hawk_count_ucstr(name); #if defined(HAWK_OOCH_IS_BCH) mbs.ptr = hawk_duputobcstr(hawk, ncs.ptr, &mbs.len); if (HAWK_UNLIKELY(!mbs.ptr)) return -1; n = hawk_arr_search(hawk->parse.gbls, (inc_builtins? 0: HAWK_NUM_STATIC_GBLS), HAWK_ARR_SIZE(hawk->parse.gbls), mbs.ptr, mbs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_HS, mbs.len, mbs.ptr); hawk_freemem(hawk, mbs.ptr); return -1; } hawk_freemem(hawk, mbs.ptr); #else n = hawk_arr_search(hawk->parse.gbls, (inc_builtins? 0: HAWK_NUM_STATIC_GBLS), HAWK_ARR_SIZE(hawk->parse.gbls), ncs.ptr, ncs.len); if (n == HAWK_ARR_NIL) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, FMT_ENOENT_GBL_LS, ncs.len, ncs.ptr); return -1; } #endif return (int)n; } static hawk_nde_t* make_init_assignment ( hawk_t* hawk, const hawk_oocs_t* name, hawk_nde_type_t var_type, hawk_oow_t idxa, hawk_nde_t* value, const hawk_loc_t* xloc, int is_const) { hawk_nde_var_t* var; hawk_nde_ass_t* ass; hawk_ooch_t* name_dup; var = (hawk_nde_var_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*var)); if (HAWK_UNLIKELY(!var)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } ass = (hawk_nde_ass_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*ass)); if (HAWK_UNLIKELY(!ass)) { hawk_freemem(hawk, var); ADJERR_LOC(hawk, xloc); return HAWK_NULL; } name_dup = hawk_dupoochars(hawk, name->ptr, name->len); if (HAWK_UNLIKELY(!name_dup)) { hawk_freemem(hawk, ass); hawk_freemem(hawk, var); ADJERR_LOC(hawk, xloc); return HAWK_NULL; } var->type = var_type; var->loc = *xloc; var->id.name.ptr = name_dup; var->id.name.len = name->len; var->id.idxa = idxa; var->idx = HAWK_NULL; var->is_const = is_const; ass->type = HAWK_NDE_ASS; ass->loc = *xloc; ass->next = HAWK_NULL; ass->opcode = HAWK_ASSOP_NONE; ass->left = (hawk_nde_t*)var; ass->right = value; /* indicate this is initialization, not assignment after initialization. * e.g. @local x = 99 */ ass->is_init = 1; return (hawk_nde_t*)ass; } static int append_init_stmt_to_init_chain (nde_chain_t* init, hawk_nde_t* stmt) { /* append a single init assignment statement to the given list */ HAWK_ASSERT(stmt != HAWK_NULL); HAWK_ASSERT(stmt->next == HAWK_NULL); if (!init->head) init->head = stmt; else init->tail->next = stmt; init->tail = stmt; return 0; } static int append_init_stmts_to_init_tree (hawk_t* hawk, nde_chain_t* init, const hawk_loc_t* xloc) { HAWK_ASSERT(init->head != HAWK_NULL); if (!hawk->tree.init) { hawk_nde_blk_t* blk; /* create a block node to hold the list of the init statements */ blk = (hawk_nde_blk_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*blk)); if (HAWK_UNLIKELY(!blk)) { ADJERR_LOC(hawk, xloc); return -1; } blk->type = HAWK_NDE_BLK; blk->loc = *xloc; blk->body = init->head; blk->nlcls = 0; blk->org_nlcls = 0; blk->outer_nlcls = 0; blk->next = HAWK_NULL; /* let it be accessible via hawk->tree.init */ hawk->tree.init = (hawk_nde_t*)blk; } else { /* chain it at the back of the list pointed to by * hawk->tree.init.body set first at the if block above. */ hawk->tree.init_tail->next = init->head; } hawk->tree.init_tail = init->tail; return 0; } static hawk_t* collect_globals (hawk_t* hawk, nde_chain_t* init, int is_const) { nde_chain_t new_init = { HAWK_NULL, HAWK_NULL }; if (MATCH(hawk,TOK_NEWLINE)) { /* special check if the first name is on the * same line when HAWK_NEWLINE is on */ hawk_seterrnum(hawk, HAWK_NULL, HAWK_EVARMS); return HAWK_NULL; } while (1) { hawk_oocs_t name; hawk_oow_t idxa; hawk_loc_t vloc; if (!MATCH(hawk,TOK_IDENT)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EBADVAR, FMT_EBADVAR, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } name = *HAWK_OOECS_OOCS(hawk->tok.name); vloc = hawk->tok.loc; idxa = add_global(hawk, &name, &hawk->tok.loc, 0); if ((int)idxa <= -1) goto oops; if (get_token(hawk) <= -1) goto oops; if (MATCH(hawk, TOK_ASSN)) { /* initialization on the same line */ const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; hawk_nde_t* init; hawk_nde_t* ass; hawk_loc_t eloc; if (get_token(hawk) <= -1) goto oops; eloc = hawk->tok.loc; init = parse_expr_basic_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!init)) goto oops; ass = make_init_assignment(hawk, &name, HAWK_NDE_GBL, idxa, init, &vloc, is_const); if (HAWK_UNLIKELY(!ass)) { hawk_clrpt(hawk, init); goto oops; } append_init_stmt_to_init_chain(&new_init, ass); ptl = HAWK_ARR_DPTL(hawk->parse.gbls, idxa); vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); vxi->is_const = is_const; /* mark it as a constant */ vxi->used = 1; /* TODO: RETHINK - i mark it as used as long as it is initialized */ } else if (is_const) { /* a constant must have an initializer */ hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EASSIGN, HAWK_T("assignment expression expected in place of '%.*js'"), HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (MATCH_TERMINATOR_NORMAL(hawk)) { /* skip a terminator (;, ) */ if (get_token(hawk) <= -1) goto oops; break; } if (!MATCH(hawk,TOK_COMMA)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ECOMMA, FMT_ECOMMA, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk,TOK_NEWLINE)); } if (new_init.head) { if (init->head == HAWK_NULL) init->head = new_init.head; else init->tail->next = new_init.head; init->tail = new_init.tail; } return hawk; oops: if (new_init.head) hawk_clrpt(hawk, new_init.head); return HAWK_NULL; } static hawk_t* collect_locals (hawk_t* hawk, hawk_oow_t nlcls, int flags, nde_chain_t* init, int is_const) { nde_chain_t new_init = { HAWK_NULL, HAWK_NULL }; if (MATCH(hawk,TOK_NEWLINE)) { /* special check if the first name is on the * same line when HAWK_NEWLINE is on */ hawk_seterrnum(hawk, HAWK_NULL, HAWK_EVARMS); return HAWK_NULL; } while (1) { hawk_oocs_t lcl; hawk_oow_t idxa; hawk_oow_t n; hawk_loc_t vloc; if (!MATCH(hawk,TOK_IDENT)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EBADVAR, FMT_EBADVAR, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } lcl = *HAWK_OOECS_OOCS(hawk->tok.name); vloc = hawk->tok.loc; /* check if it conflicts with a builtin function name * function f() { local length; } */ if (hawk_findfncwithoocs(hawk, &lcl) != HAWK_NULL) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EFNCRED, HAWK_T("intrinsic function '%.*js' redefined"), lcl.len, lcl.ptr); goto oops; } if (flags & PARSE_BLOCK_FLAG_IS_TOP) { /* check if it conflicts with a parameter name. * the first level declaration is treated as the same * scope as the parameter list */ n = hawk_arr_search(hawk->parse.params, hawk->parse.param_base, HAWK_ARR_SIZE(hawk->parse.params), lcl.ptr, lcl.len); if (n != HAWK_ARR_NIL) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EPARRED, HAWK_T("parameter '%.*js' redefined"), lcl.len, lcl.ptr); goto oops; } } if (hawk->opt.trait & HAWK_STRICTNAMING) { /* check if it conflicts with the owning function */ if (hawk->tree.cur_fun.ptr != HAWK_NULL) { if (hawk_comp_oochars(lcl.ptr, lcl.len, hawk->tree.cur_fun.ptr, hawk->tree.cur_fun.len, 0) == 0) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EFUNRED, HAWK_T("function '%.*js' redefined"), lcl.len, lcl.ptr); goto oops; } } } /* check if it conflicts with other local variable names */ n = hawk_arr_search(hawk->parse.lcls, nlcls, HAWK_ARR_SIZE(hawk->parse.lcls), lcl.ptr, lcl.len); if (n != HAWK_ARR_NIL) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EDUPLCL, FMT_EDUPLCL, lcl.len, lcl.ptr); goto oops; } /* check if it conflicts with global variable names */ n = find_global(hawk, &lcl); if (n != HAWK_ARR_NIL) { if (n < hawk->tree.ngbls_base) { /* it is a conflict only if it is one of a static global variable */ hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EDUPLCL, FMT_EDUPLCL, lcl.len, lcl.ptr); goto oops; } } if (HAWK_ARR_SIZE(hawk->parse.lcls) >= HAWK_MAX_LCLS) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELCLTM, HAWK_T("too many local variables defined - %.*js"), lcl.len, lcl.ptr); goto oops; } idxa = hawk_arr_insert(hawk->parse.lcls, HAWK_ARR_SIZE(hawk->parse.lcls), lcl.ptr, lcl.len); if (idxa == HAWK_ARR_NIL) { ADJERR_LOC(hawk, &hawk->tok.loc); goto oops; } else if (hawk->parse.pragma.trait & HAWK_PEDANTIC) { const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; /* remember location information of each local variable collected */ ptl = HAWK_ARR_DPTL(hawk->parse.lcls, idxa); vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); vxi->loc = hawk->tok.loc; } if (get_token(hawk) <= -1) goto oops; if (MATCH(hawk, TOK_ASSN)) { const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; hawk_nde_t* init; hawk_nde_t* ass; hawk_loc_t eloc; if (get_token(hawk) <= -1) goto oops; eloc = hawk->tok.loc; init = parse_expr_basic_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!init)) goto oops; ass = make_init_assignment(hawk, &lcl, HAWK_NDE_LCL, idxa - hawk->parse.lcl_base, init, &vloc, is_const); if (HAWK_UNLIKELY(!ass)) { hawk_clrpt(hawk, init); goto oops; } append_init_stmt_to_init_chain(&new_init, ass); ptl = HAWK_ARR_DPTL(hawk->parse.lcls, idxa); vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); vxi->is_const = is_const; /* mark it as a constant */ vxi->used = 1; /* mark it as used to escape the pedantic check as long as it is initialized */ } else if (is_const) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EASSIGN, HAWK_T("assignment expression expected in place of '%.*js'"), HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (MATCH_TERMINATOR_NORMAL(hawk)) { /* skip the terminator (;, ) */ if (get_token(hawk) <= -1) goto oops; break; } if (MATCH_TERMINATOR_RBRACE(hawk)) { /* should not skip } */ break; } if (!MATCH(hawk,TOK_COMMA)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ECOMMA, FMT_ECOMMA, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk,TOK_NEWLINE)); } if (new_init.head) { if (init->head == HAWK_NULL) init->head = new_init.head; else init->tail->next = new_init.head; init->tail = new_init.tail; } return hawk; oops: if (new_init.head) hawk_clrpt(hawk, new_init.head); return HAWK_NULL; } static hawk_nde_t* make_null_nde (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* null_nde; null_nde = (hawk_nde_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*null_nde)); if (HAWK_UNLIKELY(!null_nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } null_nde->type = HAWK_NDE_NULL; if (xloc) null_nde->loc = *xloc; null_nde->next = HAWK_NULL; return null_nde; } static hawk_nde_t* parse_if (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* test = HAWK_NULL; hawk_nde_t* then_part = HAWK_NULL; hawk_nde_t* else_part = HAWK_NULL; hawk_nde_if_t* nde; hawk_loc_t eloc, tloc; if (!MATCH(hawk,TOK_LPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return HAWK_NULL; } if (get_token(hawk) <= -1) return HAWK_NULL; eloc = hawk->tok.loc; test = parse_expr_withdc(hawk, &eloc); if (test == HAWK_NULL) goto oops; if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; tloc = hawk->tok.loc; then_part = parse_statement(hawk, &tloc); if (then_part == HAWK_NULL) goto oops; /* skip any new lines before the else block */ while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } if (MATCH(hawk,TOK_ELSE)) { if (get_token(hawk) <= -1) goto oops; { hawk_loc_t eloc; eloc = hawk->tok.loc; else_part = parse_statement(hawk, &eloc); if (else_part == HAWK_NULL) goto oops; } } if (is_nde_hard_false(test)) { /* fold if (0) or if (@false) or its variants */ if (else_part) { hawk_clrpt(hawk, test); hawk_clrpt(hawk, then_part); return else_part; } else { hawk_nde_t* null_nde; null_nde = make_null_nde(hawk, xloc); if (HAWK_UNLIKELY(!null_nde)) goto oops; /* [BE CAREFUL] * Don't forget reset test and then_part to HAWK_NULL if the control * skips the immediate return below and reaches further down */ hawk_clrpt(hawk, test); hawk_clrpt(hawk, then_part); return null_nde; } } else if (is_nde_hard_true(test)) { /* fold the else part for if (1) or if (@true) or its variants */ hawk_clrpt(hawk, test); if (else_part) hawk_clrpt(hawk, else_part); return then_part; } nde = (hawk_nde_if_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = HAWK_NDE_IF; nde->loc = *xloc; nde->test = test; nde->then_part = then_part; nde->else_part = else_part; return (hawk_nde_t*)nde; oops: if (else_part) hawk_clrpt(hawk, else_part); if (then_part) hawk_clrpt(hawk, then_part); if (test) hawk_clrpt(hawk, test); return HAWK_NULL; } static hawk_nde_case_t* alloc_nde_case (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_case_t* nde; nde = (hawk_nde_case_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = HAWK_NDE_CASE; nde->loc = *xloc; nde->val = HAWK_NULL; nde->action = HAWK_NULL; return nde; oops: return HAWK_NULL; } static hawk_nde_t* parse_switch (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_switch_t* nde; hawk_nde_t* test = HAWK_NULL; /* test to switch */ hawk_nde_t* case_val = HAWK_NULL; /* value after case */ hawk_nde_t* case_first = HAWK_NULL; hawk_nde_t* case_last = HAWK_NULL; hawk_nde_t* default_part = HAWK_NULL; hawk_loc_t eloc; if (!MATCH(hawk,TOK_LPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return HAWK_NULL; } if (get_token(hawk) <= -1) return HAWK_NULL; eloc = hawk->tok.loc; test = parse_expr_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!test)) goto oops; if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } if (!MATCH(hawk,TOK_LBRACE)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELBRACE, FMT_ELBRACE, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; while (!MATCH(hawk, TOK_RBRACE)) { while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } if (MATCH(hawk, TOK_CASE) || MATCH(hawk, TOK_DEFAULT)) { hawk_nde_case_t* vv; hawk_nde_t* action_first = HAWK_NULL; hawk_nde_t* action_last = HAWK_NULL; if (MATCH(hawk, TOK_CASE)) { if (get_token(hawk) <= -1) goto oops; eloc = hawk->tok.loc; case_val = parse_primary_literal(hawk, &eloc); if (HAWK_UNLIKELY(!case_val)) goto oops; } else /* MATCH(hawk, TOK_DEFAULT) */ { if (default_part) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EMULDFL, FMT_EMULDFL, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } case_val = HAWK_NULL; if (get_token(hawk) <= -1) goto oops; } if (!MATCH(hawk, TOK_COLON)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ECOLON, FMT_ECOLON, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } while (!MATCH(hawk, TOK_CASE) && !MATCH(hawk, TOK_DEFAULT) && !MATCH(hawk, TOK_RBRACE)) { hawk_nde_t* v; eloc = hawk->tok.loc; hawk->parse.depth.swtch++; v = parse_statement(hawk, &eloc); hawk->parse.depth.swtch--; if (!v) { if (action_first) hawk_clrpt(hawk, action_first); goto oops; } if (!action_first) { action_first = v; action_last = v; } else if (v->type == HAWK_NDE_NULL && v->type == action_last->type) { /* skip a successive null statement */ hawk_freemem(hawk, v); } else { action_last->next = v; action_last = v; } while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } } vv = alloc_nde_case(hawk, &eloc); if (HAWK_UNLIKELY(!vv)) { if (action_first) hawk_clrpt(hawk, action_first); goto oops; } vv->val = case_val; vv->action = action_first; if (!case_val) default_part = (hawk_nde_t*)vv; else case_val = HAWK_NULL; if (!case_first) { case_first = (hawk_nde_t*)vv; case_last = (hawk_nde_t*)vv; } else { case_last->next = (hawk_nde_t*)vv; case_last = (hawk_nde_t*)vv; } } else { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EKWCASE, FMT_EKWCASE, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } if (!MATCH(hawk, TOK_RBRACE)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERBRACE, FMT_ERBRACE, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; nde = (hawk_nde_switch_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = HAWK_NDE_SWITCH; nde->loc = *xloc; nde->test = test; nde->case_part = case_first; nde->default_part = default_part; return (hawk_nde_t*)nde; oops: if (case_val) hawk_clrpt(hawk, case_val); if (case_first) hawk_clrpt(hawk, case_first); /*if (default_part) hawk_clrpt(hawk, default_part); no need to crear it as it is in the case_first chanin */ if (test) hawk_clrpt(hawk, test); return HAWK_NULL; } static hawk_nde_t* parse_while (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* test = HAWK_NULL; hawk_nde_t* body = HAWK_NULL; hawk_nde_while_t* nde; hawk_loc_t ploc; if (!MATCH(hawk,TOK_LPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; ploc = hawk->tok.loc; test = parse_expr_withdc(hawk, &ploc); if (HAWK_UNLIKELY(!test)) goto oops; if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; ploc = hawk->tok.loc; body = parse_statement(hawk, &ploc); if (HAWK_UNLIKELY(!body)) goto oops; if (is_nde_hard_false(test)) { /* fold while(0) and while(@false) into a null statement. */ hawk_nde_t* null_nde; null_nde = make_null_nde(hawk, xloc); if (HAWK_UNLIKELY(!null_nde)) goto oops; hawk_clrpt(hawk, body); body = HAWK_NULL; hawk_clrpt(hawk, test); test = HAWK_NULL; return null_nde; } nde = (hawk_nde_while_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = HAWK_NDE_WHILE; nde->loc = *xloc; nde->test = test; nde->body = body; return (hawk_nde_t*)nde; oops: if (body) hawk_clrpt(hawk, body); if (test) hawk_clrpt(hawk, test); return HAWK_NULL; } static hawk_nde_t* parse_for (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* init = HAWK_NULL, * test = HAWK_NULL; hawk_nde_t* incr = HAWK_NULL, * body = HAWK_NULL; hawk_nde_forin_t* nde_forin; hawk_nde_for_t* nde_for; hawk_loc_t ploc; if (!MATCH(hawk,TOK_LPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return HAWK_NULL; } if (get_token(hawk) <= -1) return HAWK_NULL; if (!MATCH(hawk,TOK_SEMICOLON)) { /* 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_forin = MATCH(hawk,TOK_LPAREN); ploc = hawk->tok.loc; init = parse_expr_withdc(hawk, &ploc); if (HAWK_UNLIKELY(!init)) goto oops; if (!no_forin && init->type == HAWK_NDE_EXP_BIN && ((hawk_nde_exp_t*)init)->opcode == HAWK_BINOP_IN && #if defined(HAWK_ENABLE_GC) is_var(((hawk_nde_exp_t*)init)->left)) #else is_plain_var(((hawk_nde_exp_t*)init)->left)) #endif { /* switch to forin - for (x in y) */ if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; ploc = hawk->tok.loc; body = parse_statement(hawk, &ploc); if (HAWK_UNLIKELY(!body)) goto oops; nde_forin = (hawk_nde_forin_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde_forin)); if (HAWK_UNLIKELY(!nde_forin)) { ADJERR_LOC(hawk, xloc); goto oops; } nde_forin->type = HAWK_NDE_FORIN; nde_forin->loc = *xloc; nde_forin->test = init; nde_forin->body = body; return (hawk_nde_t*)nde_forin; } if (!MATCH(hawk,TOK_SEMICOLON)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ESCOLON, FMT_ESCOLON, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } do { if (get_token(hawk) <= -1) goto oops; /* skip new lines after the first semicolon */ } while (MATCH(hawk,TOK_NEWLINE)); if (!MATCH(hawk,TOK_SEMICOLON)) { ploc = hawk->tok.loc; test = parse_expr_withdc(hawk, &ploc); if (HAWK_UNLIKELY(!test)) goto oops; if (!MATCH(hawk,TOK_SEMICOLON)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ESCOLON, FMT_ESCOLON, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } do { if (get_token(hawk) <= -1) goto oops; /* skip new lines after the second semicolon */ } while (MATCH(hawk,TOK_NEWLINE)); if (!MATCH(hawk,TOK_RPAREN)) { { hawk_loc_t eloc; eloc = hawk->tok.loc; incr = parse_expr_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!incr)) goto oops; } if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } if (get_token(hawk) <= -1) goto oops; ploc = hawk->tok.loc; body = parse_statement(hawk, &ploc); if (body == HAWK_NULL) goto oops; if (test) { if (is_nde_hard_false(test)) { if (test) hawk_clrpt(hawk, test); if (incr) hawk_clrpt(hawk, incr); if (body) hawk_clrpt(hawk, body); if (init) return init; return make_null_nde(hawk, xloc); } else if (is_nde_hard_true(test)) { hawk_clrpt(hawk, test); test = HAWK_NULL; } } if (init && is_nde_safe_to_erase(init)) { hawk_clrpt(hawk, init); init = HAWK_NULL; } if (incr && is_nde_safe_to_erase(incr)) { hawk_clrpt(hawk, incr); incr = HAWK_NULL; } nde_for = (hawk_nde_for_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde_for)); if (HAWK_UNLIKELY(!nde_for)) { ADJERR_LOC(hawk, xloc); goto oops; } nde_for->type = HAWK_NDE_FOR; nde_for->loc = *xloc; nde_for->init = init; nde_for->test = test; nde_for->incr = incr; nde_for->body = body; return (hawk_nde_t*)nde_for; oops: if (init) hawk_clrpt(hawk, init); if (test) hawk_clrpt(hawk, test); if (incr) hawk_clrpt(hawk, incr); if (body) hawk_clrpt(hawk, body); return HAWK_NULL; } static hawk_nde_t* parse_dowhile (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* test = HAWK_NULL; hawk_nde_t* body = HAWK_NULL; hawk_nde_while_t* nde; hawk_loc_t ploc; HAWK_ASSERT(hawk->ptok.type == TOK_DO); ploc = hawk->tok.loc; body = parse_statement(hawk, &ploc); if (HAWK_UNLIKELY(!body)) goto oops; while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) goto oops; } if (!MATCH(hawk,TOK_WHILE)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EKWWHL, FMT_EKWWHL, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; if (!MATCH(hawk,TOK_LPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; ploc = hawk->tok.loc; test = parse_expr_withdc(hawk, &ploc); if (HAWK_UNLIKELY(!test)) goto oops; if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; nde = (hawk_nde_while_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = HAWK_NDE_DOWHILE; nde->loc = *xloc; nde->test = test; nde->body = body; return (hawk_nde_t*)nde; oops: if (body) hawk_clrpt(hawk, body); if (test) hawk_clrpt(hawk, test); HAWK_ASSERT(nde == HAWK_NULL); return HAWK_NULL; } static hawk_nde_t* parse_break (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_break_t* nde; HAWK_ASSERT(hawk->ptok.type == TOK_BREAK); if (hawk->parse.depth.loop <= 0 && hawk->parse.depth.swtch <= 0) { hawk_seterrnum(hawk, xloc, HAWK_EBREAK); return HAWK_NULL; } nde = (hawk_nde_break_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_BREAK; nde->loc = *xloc; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_continue (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_continue_t* nde; HAWK_ASSERT(hawk->ptok.type == TOK_CONTINUE); if (hawk->parse.depth.loop <= 0) { hawk_seterrnum(hawk, xloc, HAWK_ECONTINUE); return HAWK_NULL; } nde = (hawk_nde_continue_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_CONTINUE; nde->loc = *xloc; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_return (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_return_t* nde; hawk_nde_t* val; HAWK_ASSERT(hawk->ptok.type == TOK_RETURN); nde = (hawk_nde_return_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_RETURN; nde->loc = *xloc; if (MATCH_TERMINATOR(hawk)) { /* no return value */ val = HAWK_NULL; } else { hawk_loc_t eloc; eloc = hawk->tok.loc; val = parse_expr_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!val)) { hawk_freemem(hawk, nde); return HAWK_NULL; } } nde->val = val; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_exit (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_exit_t* nde; hawk_nde_t* val; HAWK_ASSERT(hawk->ptok.type == TOK_EXIT || hawk->ptok.type == TOK_XABORT); nde = (hawk_nde_exit_t*) hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (nde == HAWK_NULL) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_EXIT; nde->loc = *xloc; nde->abort = (hawk->ptok.type == TOK_XABORT); if (MATCH_TERMINATOR(hawk)) { /* no exit code */ val = HAWK_NULL; } else { hawk_loc_t eloc; eloc = hawk->tok.loc; val = parse_expr_withdc(hawk, &eloc); if (val == HAWK_NULL) { hawk_freemem(hawk, nde); return HAWK_NULL; } } nde->val = val; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_next (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_next_t* nde; HAWK_ASSERT(hawk->ptok.type == TOK_NEXT); if (hawk->parse.id.block == PARSE_BEGIN_BLOCK) { hawk_seterrnum(hawk, xloc, HAWK_ENEXTBEG); return HAWK_NULL; } if (hawk->parse.id.block == PARSE_END_BLOCK) { hawk_seterrnum(hawk, xloc, HAWK_ENEXTEND); return HAWK_NULL; } nde = (hawk_nde_next_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_NEXT; nde->loc = *xloc; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_nextfile (hawk_t* hawk, const hawk_loc_t* xloc, int out) { hawk_nde_nextfile_t* nde; if (!out && hawk->parse.id.block == PARSE_BEGIN_BLOCK) { hawk_seterrnum(hawk, xloc, HAWK_ENEXTFBEG); return HAWK_NULL; } if (!out && hawk->parse.id.block == PARSE_END_BLOCK) { hawk_seterrnum(hawk, xloc, HAWK_ENEXTFEND); return HAWK_NULL; } nde = (hawk_nde_nextfile_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_NEXTFILE; nde->loc = *xloc; nde->out = out; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_delete (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_delete_t* nde; hawk_nde_t* var = HAWK_NULL; hawk_loc_t dloc; hawk_nde_type_t type; int inparen = 0; HAWK_ASSERT(hawk->ptok.type == TOK_DELETE || hawk->ptok.type == TOK_XRESET); type = (hawk->ptok.type == TOK_DELETE)? HAWK_NDE_DELETE: HAWK_NDE_RESET; if (MATCH(hawk,TOK_LPAREN)) { if (get_token(hawk) <= -1) goto oops; inparen = 1; } if (!MATCH(hawk,TOK_IDENT)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EIDENT, FMT_EIDENT, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } dloc = hawk->tok.loc; var = parse_primary_ident(hawk, &dloc); if (HAWK_UNLIKELY(!var)) goto oops; if ((type == HAWK_NDE_DELETE && !is_var(var)) || (type == HAWK_NDE_RESET && !is_plain_var(var))) { hawk_seterrnum(hawk, &dloc, HAWK_EBADARG); goto oops; } if (inparen) { if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; } nde = (hawk_nde_delete_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = type; nde->loc = *xloc; nde->var = var; return (hawk_nde_t*)nde; oops: if (var) hawk_clrpt(hawk, var); return HAWK_NULL; } static hawk_nde_t* parse_print (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_print_t* nde; hawk_nde_t* args = HAWK_NULL; hawk_nde_t* out = HAWK_NULL; hawk_out_type_t out_type; hawk_nde_type_t type; hawk_loc_t eloc; HAWK_ASSERT(hawk->ptok.type == TOK_PRINT || hawk->ptok.type == TOK_PRINTF); type = (hawk->ptok.type == TOK_PRINT)? HAWK_NDE_PRINT: HAWK_NDE_PRINTF; if (!MATCH_TERMINATOR(hawk) && !MATCH(hawk,TOK_GT) && !MATCH(hawk,TOK_RS) && !MATCH(hawk,TOK_BOR) && !MATCH(hawk,TOK_PIPE_BD)) { hawk_nde_t* args_tail; hawk_nde_t* tail_prev; int in_parens = 0, gm_in_parens = 0; hawk_oow_t opening_lparen_seq; if (MATCH(hawk,TOK_LPAREN)) { /* just remember the sequence number of the left * parenthesis before calling parse_expr_withdc() * that eventually calls parse_primary_lparen() */ opening_lparen_seq = hawk->parse.lparen_seq; in_parens = 1; /* maybe. not confirmed yet */ /* print and printf provide weird syntaxs. * * 1. print 10, 20; * 2. print (10, 20); * 3. print (10,20,30) in a; * 4. print ((10,20,30) in a); * * Due to case 3, i can't consume LPAREN * here and expect RPAREN later. */ } eloc = hawk->tok.loc; args = parse_expr_withdc(hawk, &eloc); if (args == HAWK_NULL) goto oops; args_tail = args; tail_prev = HAWK_NULL; if (args->type != HAWK_NDE_GRP) { /* args->type == HAWK_NDE_GRP when print (a, b, c) * args->type != HAWK_NDE_GRP when print a, b, c */ hawk_oow_t group_opening_lparen_seq; while (MATCH(hawk,TOK_COMMA)) { do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk,TOK_NEWLINE)); /* if it's grouped, i must check if the last group member * is enclosed in parentheses. * * i set the condition to false whenever i see * a new group member. */ gm_in_parens = 0; if (MATCH(hawk,TOK_LPAREN)) { group_opening_lparen_seq = hawk->parse.lparen_seq; gm_in_parens = 1; /* maybe */ } eloc = hawk->tok.loc; args_tail->next = parse_expr_withdc(hawk, &eloc); if (args_tail->next == HAWK_NULL) goto oops; tail_prev = args_tail; args_tail = args_tail->next; if (gm_in_parens == 1 && hawk->ptok.type == TOK_RPAREN && hawk->parse.lparen_last_closed == group_opening_lparen_seq) { /* confirm that the last group seen so far * is parenthesized */ gm_in_parens = 2; } } } /* print 1 > 2 would print 1 to the file named 2. * print (1 > 2) would print (1 > 2) on the console * * think of all these... there are many more possible combinations. * * print ((10,20,30) in a) > "x"; * print ((10,20,30) in a) * print ((10,20,30) in a) > ("x"); * print ((10,20,30) in a) > (("x")); * function abc() { return "abc"; } BEGIN { print (1 > abc()); } * function abc() { return "abc"; } BEGIN { print 1 > abc(); } * print 1, 2, 3 > 4; * print (1, 2, 3) > 4; * print ((1, 2, 3) > 4); * print 1, 2, 3 > 4 + 5; * print 1, 2, (3 > 4) > 5; * print 1, 2, (3 > 4) > 5 + 6; */ if (in_parens == 1 && hawk->ptok.type == TOK_RPAREN && (hawk->ptok.flags & TOK_FLAGS_LPAREN_CLOSER) && hawk->parse.lparen_last_closed == opening_lparen_seq) { in_parens = 2; /* confirmed */ } if (in_parens != 2 && gm_in_parens != 2 && args_tail->type == HAWK_NDE_EXP_BIN) { /* check if the expression ends with an output redirector * and take out the part after the redirector and make it * the output target */ int i; hawk_nde_exp_t* ep = (hawk_nde_exp_t*)args_tail; static struct { int opc; int out; int opt; } tab[] = { { HAWK_BINOP_GT, HAWK_OUT_FILE, 0 }, { HAWK_BINOP_RS, HAWK_OUT_APFILE, 0 }, { HAWK_BINOP_BOR, HAWK_OUT_PIPE, 0 }, /* |& doesn't oeverlap with other binary operators. { HAWK_BINOP_PIPE_BD, HAWK_OUT_RWPIPE, HAWK_RWPIPE } */ }; for (i = 0; i < HAWK_COUNTOF(tab); i++) { if (ep->opcode == tab[i].opc) { hawk_nde_t* tmp; if (tab[i].opt && !(hawk->opt.trait & tab[i].opt)) break; tmp = args_tail; if (tail_prev) tail_prev->next = ep->left; else args = ep->left; out = ep->right; out_type = tab[i].out; hawk_freemem(hawk, tmp); break; } } } } if (!out) { out_type = MATCH(hawk,TOK_GT)? HAWK_OUT_FILE: MATCH(hawk,TOK_RS)? HAWK_OUT_APFILE: MATCH(hawk,TOK_BOR)? HAWK_OUT_PIPE: ((hawk->opt.trait & HAWK_RWPIPE) && MATCH(hawk,TOK_PIPE_BD))? HAWK_OUT_RWPIPE: HAWK_OUT_CONSOLE; if (out_type != HAWK_OUT_CONSOLE) { if (get_token(hawk) <= -1) goto oops; eloc = hawk->tok.loc; out = parse_expr_withdc(hawk, &eloc); if (out == HAWK_NULL) goto oops; } } if (type == HAWK_NDE_PRINTF && !args) { hawk_seterrnum(hawk, xloc, HAWK_ENOARG); goto oops; } nde = (hawk_nde_print_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = type; nde->loc = *xloc; nde->args = args; nde->out_type = out_type; nde->out = out; return (hawk_nde_t*)nde; oops: if (args) hawk_clrpt(hawk, args); if (out) hawk_clrpt(hawk, out); return HAWK_NULL; } static hawk_nde_t* parse_statement_nb (hawk_t* hawk, const hawk_loc_t* xloc) { /* parse a non-block statement */ hawk_nde_t* nde; /* keywords that don't require any terminating semicolon */ if (MATCH(hawk,TOK_IF)) { if (get_token(hawk) <= -1) return HAWK_NULL; return parse_if(hawk, xloc); } else if (MATCH(hawk, TOK_SWITCH)) { if (get_token(hawk) <= -1) return HAWK_NULL; return parse_switch(hawk, xloc); } else if (MATCH(hawk,TOK_WHILE)) { if (get_token(hawk) <= -1) return HAWK_NULL; hawk->parse.depth.loop++; nde = parse_while(hawk, xloc); hawk->parse.depth.loop--; return nde; } else if (MATCH(hawk,TOK_FOR)) { if (get_token(hawk) <= -1) return HAWK_NULL; hawk->parse.depth.loop++; nde = parse_for(hawk, xloc); hawk->parse.depth.loop--; return nde; } /* keywords that require a terminating semicolon */ if (MATCH(hawk,TOK_DO)) { if (get_token(hawk) <= -1) return HAWK_NULL; hawk->parse.depth.loop++; nde = parse_dowhile(hawk, xloc); hawk->parse.depth.loop--; return nde; } else if (MATCH(hawk,TOK_BREAK)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_break(hawk, xloc); } else if (MATCH(hawk,TOK_CONTINUE)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_continue(hawk, xloc); } else if (MATCH(hawk,TOK_RETURN)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_return(hawk, xloc); } else if (MATCH(hawk,TOK_EXIT) || MATCH(hawk,TOK_XABORT)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_exit(hawk, xloc); } else if (MATCH(hawk,TOK_NEXT)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_next(hawk, xloc); } else if (MATCH(hawk,TOK_NEXTFILE)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_nextfile(hawk, xloc, 0); } else if (MATCH(hawk,TOK_NEXTOFILE)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_nextfile(hawk, xloc, 1); } else if (MATCH(hawk,TOK_DELETE) || MATCH(hawk,TOK_XRESET)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_delete(hawk, xloc); } else if (!(hawk->opt.trait & HAWK_TOLERANT)) { /* in the non-tolerant mode, we treat print and printf * as a separate statement */ if (MATCH(hawk,TOK_PRINT) || MATCH(hawk,TOK_PRINTF)) { if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_print(hawk, xloc); } else nde = parse_expr_withdc(hawk, xloc); } else { nde = parse_expr_withdc(hawk, xloc); } if (HAWK_UNLIKELY(!nde)) return HAWK_NULL; if (MATCH_TERMINATOR_NORMAL(hawk)) { /* check if a statement ends with a semicolon or */ if (get_token(hawk) <= -1) { if (nde) hawk_clrpt(hawk, nde); return HAWK_NULL; } } else if (MATCH_TERMINATOR_RBRACE(hawk)) { /* do not skip the right brace as a statement terminator. * is there anything to do here? */ } else { if (nde) hawk_clrpt(hawk, nde); hawk_seterrbfmt(hawk, &hawk->tok.loc, HAWK_ESTMEND, "statement not ending with a semicolon and got '%.*js'", HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return HAWK_NULL; } return nde; } static hawk_nde_t* parse_statement (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* nde; /* skip new lines before a statement */ while (MATCH(hawk,TOK_NEWLINE)) { if (get_token(hawk) <= -1) return HAWK_NULL; } if (MATCH(hawk,TOK_SEMICOLON)) { /* null statement */ nde = make_null_nde(hawk, xloc); if (HAWK_UNLIKELY(!nde)) return HAWK_NULL; if (get_token(hawk) <= -1) { hawk_clrpt(hawk, nde); return HAWK_NULL; } } else if (MATCH(hawk,TOK_LBRACE)) { /* a block statemnt { ... } */ hawk_loc_t tloc; tloc = hawk->ptok.loc; if (get_token(hawk) <= -1) return HAWK_NULL; nde = parse_block_dc(hawk, &tloc, 0); } else { /* the statement id held in hawk->parse.id.stmt 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; hawk_loc_t tloc; old_id = hawk->parse.id.stmt; tloc = hawk->tok.loc; /* set the current statement id */ hawk->parse.id.stmt = hawk->tok.type; /* proceed parsing the statement */ nde = parse_statement_nb(hawk, &tloc); /* restore the statement id saved previously */ hawk->parse.id.stmt = old_id; } if (nde && is_nde_safe_to_erase(nde)) { hawk_nde_t* tmp; tmp = make_null_nde(hawk, &nde->loc); hawk_clrpt(hawk, nde); if (HAWK_UNLIKELY(!tmp)) return HAWK_NULL; nde = tmp; } return nde; } static int assign_to_opcode (hawk_t* hawk) { /* synchronize it with hawk_assop_type_t in run.h */ static int assop[] = { HAWK_ASSOP_NONE, HAWK_ASSOP_PLUS, HAWK_ASSOP_MINUS, HAWK_ASSOP_MUL, HAWK_ASSOP_DIV, HAWK_ASSOP_IDIV, HAWK_ASSOP_MOD, HAWK_ASSOP_EXP, HAWK_ASSOP_CONCAT, HAWK_ASSOP_RS, HAWK_ASSOP_LS, HAWK_ASSOP_BAND, HAWK_ASSOP_BXOR, HAWK_ASSOP_BOR }; if (hawk->tok.type >= TOK_ASSN && hawk->tok.type <= TOK_BOR_ASSN) { return assop[hawk->tok.type - TOK_ASSN]; } return -1; } static hawk_nde_t* parse_expr_basic (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* nde, * n1, * n2; nde = parse_logical_or(hawk, xloc); if (!nde) return HAWK_NULL; if (MATCH(hawk,TOK_QUEST)) /* ternary operator */ { hawk_loc_t eloc; hawk_nde_cnd_t* cnd; if (get_token(hawk) <= -1) { hawk_clrpt(hawk, nde); return HAWK_NULL; } eloc = hawk->tok.loc; n1 = parse_expr_withdc(hawk, &eloc); if (!n1) { hawk_clrpt(hawk, nde); return HAWK_NULL; } if (!MATCH(hawk,TOK_COLON)) { hawk_clrpt(hawk, nde); hawk_clrpt(hawk, n1); hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ECOLON, FMT_ECOLON, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return HAWK_NULL; } if (get_token(hawk) <= -1) { hawk_clrpt(hawk, nde); hawk_clrpt(hawk, n1); return HAWK_NULL; } eloc = hawk->tok.loc; n2 = parse_expr_withdc(hawk, &eloc); if (!n2) { hawk_clrpt(hawk, nde); hawk_clrpt(hawk, n1); return HAWK_NULL; } if (is_nde_hard_true(nde)) { hawk_clrpt(hawk, nde); hawk_clrpt(hawk, n2); return n1; } else if (is_nde_hard_false(nde)) { hawk_clrpt(hawk, nde); hawk_clrpt(hawk, n1); return n2; } cnd = (hawk_nde_cnd_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*cnd)); if (HAWK_UNLIKELY(!cnd)) { hawk_clrpt(hawk, nde); hawk_clrpt(hawk, n1); hawk_clrpt(hawk, n2); ADJERR_LOC(hawk, xloc); return HAWK_NULL; } cnd->type = HAWK_NDE_CND; cnd->loc = *xloc; cnd->next = HAWK_NULL; cnd->test = nde; cnd->left = n1; cnd->right = n2; nde = (hawk_nde_t*)cnd; } return nde; } static hawk_nde_t* parse_expr (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* x, * y; hawk_nde_ass_t* nde; int opcode; x = parse_expr_basic(hawk, xloc); if (x == HAWK_NULL) return HAWK_NULL; opcode = assign_to_opcode(hawk); if (opcode <= -1) { /* no assignment operator found. */ return x; } HAWK_ASSERT(x->next == HAWK_NULL); if (!is_var(x) && x->type != HAWK_NDE_POS) { hawk_clrpt(hawk, x); hawk_seterrnum(hawk, xloc, HAWK_EASSIGN); return HAWK_NULL; } if (get_token(hawk) <= -1) { hawk_clrpt(hawk, x); return HAWK_NULL; } { hawk_loc_t eloc; eloc = hawk->tok.loc; y = parse_expr_withdc(hawk, &eloc); } if (y == HAWK_NULL) { hawk_clrpt(hawk, x); return HAWK_NULL; } nde = (hawk_nde_ass_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (nde == HAWK_NULL) { hawk_clrpt(hawk, x); hawk_clrpt(hawk, y); ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_ASS; nde->loc = *xloc; nde->next = HAWK_NULL; nde->opcode = opcode; nde->left = x; nde->right = y; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_expr_withdc (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* nde; /* perform depth check before parsing expression */ if (hawk->opt.depth.s.expr_parse > 0 && hawk->parse.depth.expr >= hawk->opt.depth.s.expr_parse) { hawk_seterrnum(hawk, xloc, HAWK_EEXPRNST); return HAWK_NULL; } hawk->parse.depth.expr++; nde = parse_expr(hawk, xloc); hawk->parse.depth.expr--; return nde; } static hawk_nde_t* parse_expr_basic_withdc (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* nde; if (hawk->opt.depth.s.expr_parse > 0 && hawk->parse.depth.expr >= hawk->opt.depth.s.expr_parse) { hawk_seterrnum(hawk, xloc, HAWK_EEXPRNST); return HAWK_NULL; } hawk->parse.depth.expr++; nde = parse_expr_basic(hawk, xloc); hawk->parse.depth.expr--; return nde; } #define INT_BINOP_INT(x,op,y) \ (((hawk_nde_int_t*)x)->val op ((hawk_nde_int_t*)y)->val) #define INT_BINOP_FLT(x,op,y) \ (((hawk_nde_int_t*)x)->val op ((hawk_nde_flt_t*)y)->val) #define FLT_BINOP_INT(x,op,y) \ (((hawk_nde_flt_t*)x)->val op ((hawk_nde_int_t*)y)->val) #define FLT_BINOP_FLT(x,op,y) \ (((hawk_nde_flt_t*)x)->val op ((hawk_nde_flt_t*)y)->val) union folded_t { hawk_int_t l; hawk_flt_t r; }; typedef union folded_t folded_t; static HAWK_INLINE int is_nde_numeric_literal (hawk_nde_t* nde) { return nde->type == HAWK_NDE_INT || nde->type == HAWK_NDE_FLT; } static HAWK_INLINE hawk_int_t get_nde_literal_as_int (hawk_nde_t* nde) { return (nde->type == HAWK_NDE_INT)? ((hawk_nde_int_t*)nde)->val: (hawk_int_t)((hawk_nde_flt_t*)nde)->val; } static HAWK_INLINE hawk_flt_t get_nde_literal_as_flt (hawk_nde_t* nde) { return (nde->type == HAWK_NDE_INT)? (hawk_flt_t)((hawk_nde_int_t*)nde)->val: ((hawk_nde_flt_t*)nde)->val; } static HAWK_INLINE int is_safe_shift_count (hawk_int_t n) { return n >= 0 && n < HAWK_BITSOF(hawk_int_t); } static int fold_constants_for_binop (hawk_t* hawk, hawk_nde_t* left, hawk_nde_t* right, int opcode, folded_t* folded) { hawk_int_t li, ri; hawk_flt_t lf, rf; int both_int; if (!is_nde_numeric_literal(left) || !is_nde_numeric_literal(right)) return -1; li = get_nde_literal_as_int(left); ri = get_nde_literal_as_int(right); lf = get_nde_literal_as_flt(left); rf = get_nde_literal_as_flt(right); both_int = (left->type == HAWK_NDE_INT && right->type == HAWK_NDE_INT); switch (opcode) { case HAWK_BINOP_PLUS: if (both_int) { folded->l = li + ri; return HAWK_NDE_INT; } folded->r = lf + rf; return HAWK_NDE_FLT; case HAWK_BINOP_MINUS: if (both_int) { folded->l = li - ri; return HAWK_NDE_INT; } folded->r = lf - rf; return HAWK_NDE_FLT; case HAWK_BINOP_MUL: if (both_int) { folded->l = li * ri; return HAWK_NDE_INT; } folded->r = lf * rf; return HAWK_NDE_FLT; case HAWK_BINOP_DIV: if (both_int) { if (ri == 0) { hawk_seterrnum(hawk, HAWK_NULL, HAWK_EDIVBY0); return -2; } if ((li % ri) == 0) { folded->l = li / ri; return HAWK_NDE_INT; } folded->r = (hawk_flt_t)li / (hawk_flt_t)ri; return HAWK_NDE_FLT; } folded->r = lf / rf; return HAWK_NDE_FLT; case HAWK_BINOP_IDIV: if (both_int && ri == 0) { hawk_seterrnum(hawk, HAWK_NULL, HAWK_EDIVBY0); return -2; } folded->l = (both_int)? (li / ri): (hawk_int_t)(lf / rf); return HAWK_NDE_INT; case HAWK_BINOP_MOD: if (both_int) { if (ri == 0) { hawk_seterrnum(hawk, HAWK_NULL, HAWK_EDIVBY0); return -2; } folded->l = li % ri; return HAWK_NDE_INT; } folded->r = hawk->prm.math.mod(hawk, lf, rf); return HAWK_NDE_FLT; case HAWK_BINOP_EXP: if (both_int) { if (ri >= 0) { hawk_int_t v = 1; hawk_int_t n = ri; while (n-- > 0) v *= li; folded->l = v; return HAWK_NDE_INT; } if (li == 0) { hawk_seterrnum(hawk, HAWK_NULL, HAWK_EDIVBY0); return -2; } { hawk_flt_t v = 1.0; hawk_int_t n = -ri; while (n-- > 0) v /= li; folded->r = v; return HAWK_NDE_FLT; } } if (right->type == HAWK_NDE_INT) { if (ri >= 0) { hawk_flt_t v = 1.0; hawk_int_t n = ri; while (n-- > 0) v *= lf; folded->r = v; return HAWK_NDE_FLT; } if (lf == 0.0) { hawk_seterrnum(hawk, HAWK_NULL, HAWK_EDIVBY0); return -2; } { hawk_flt_t v = 1.0; hawk_int_t n = -ri; while (n-- > 0) v /= lf; folded->r = v; return HAWK_NDE_FLT; } } folded->r = hawk->prm.math.pow(hawk, lf, rf); return HAWK_NDE_FLT; case HAWK_BINOP_LS: if (!is_safe_shift_count(ri)) return -1; folded->l = li << ri; return HAWK_NDE_INT; case HAWK_BINOP_RS: if (!is_safe_shift_count(ri)) return -1; folded->l = li >> ri; return HAWK_NDE_INT; case HAWK_BINOP_BAND: folded->l = li & ri; return HAWK_NDE_INT; case HAWK_BINOP_BXOR: folded->l = li ^ ri; return HAWK_NDE_INT; case HAWK_BINOP_BOR: folded->l = li | ri; return HAWK_NDE_INT; case HAWK_BINOP_TEQ: if (left->type != right->type) folded->l = 0; else if (both_int) folded->l = (li == ri); else folded->l = (lf == rf); return HAWK_NDE_INT; case HAWK_BINOP_TNE: if (left->type != right->type) folded->l = 1; else if (both_int) folded->l = (li != ri); else folded->l = (lf != rf); return HAWK_NDE_INT; case HAWK_BINOP_EQ: folded->l = (both_int)? (li == ri): (lf == rf); return HAWK_NDE_INT; case HAWK_BINOP_NE: folded->l = (both_int)? (li != ri): (lf != rf); return HAWK_NDE_INT; case HAWK_BINOP_GT: folded->l = (both_int)? (li > ri): (lf > rf); return HAWK_NDE_INT; case HAWK_BINOP_GE: folded->l = (both_int)? (li >= ri): (lf >= rf); return HAWK_NDE_INT; case HAWK_BINOP_LT: folded->l = (both_int)? (li < ri): (lf < rf); return HAWK_NDE_INT; case HAWK_BINOP_LE: folded->l = (both_int)? (li <= ri): (lf <= rf); return HAWK_NDE_INT; default: return -1; } } static hawk_nde_t* new_exp_bin_node ( hawk_t* hawk, const hawk_loc_t* loc, int opcode, hawk_nde_t* left, hawk_nde_t* right) { hawk_nde_exp_t* tmp; tmp = (hawk_nde_exp_t*) hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); if (tmp) { tmp->type = HAWK_NDE_EXP_BIN; tmp->loc = *loc; tmp->opcode = opcode; tmp->left = left; tmp->right = right; } else ADJERR_LOC(hawk, loc); return (hawk_nde_t*)tmp; } static hawk_nde_t* new_int_node (hawk_t* hawk, hawk_int_t lv, const hawk_loc_t* loc) { hawk_nde_int_t* tmp; tmp = (hawk_nde_int_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); if (HAWK_LIKELY(tmp)) { tmp->type = HAWK_NDE_INT; tmp->loc = *loc; tmp->val = lv; } else ADJERR_LOC(hawk, loc); return (hawk_nde_t*)tmp; } static hawk_nde_t* new_flt_node (hawk_t* hawk, hawk_flt_t rv, const hawk_loc_t* loc) { hawk_nde_flt_t* tmp; tmp = (hawk_nde_flt_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); if (HAWK_LIKELY(tmp)) { tmp->type = HAWK_NDE_FLT; tmp->loc = *loc; tmp->val = rv; } else ADJERR_LOC(hawk, loc); return (hawk_nde_t*)tmp; } static hawk_nde_t* new_xbool_node (hawk_t* hawk, int bv, const hawk_loc_t* loc) { hawk_nde_xnil_t* tmp; tmp = (hawk_nde_xnil_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); if (HAWK_LIKELY(tmp)) { tmp->type = bv? HAWK_NDE_XTRUE: HAWK_NDE_XFALSE; tmp->loc = *loc; } else ADJERR_LOC(hawk, loc); return (hawk_nde_t*)tmp; } static hawk_nde_t* new_str_node_with_oocsarr (hawk_t* hawk, const hawk_oocs_t* oocsarr, const hawk_loc_t* loc) { hawk_nde_str_t* tmp; tmp = (hawk_nde_str_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); if (HAWK_UNLIKELY(!tmp)) { ADJERR_LOC(hawk, loc); return HAWK_NULL; } tmp->type = HAWK_NDE_STR; tmp->loc = *loc; tmp->ptr = hawk_dupoocsarr(hawk, oocsarr, &tmp->len); if (!HAWK_UNLIKELY(tmp)) { hawk_freemem(hawk, tmp); ADJERR_LOC(hawk, loc); return HAWK_NULL; } return (hawk_nde_t*)tmp; } static hawk_nde_t* new_mbs_node_with_bcsarr (hawk_t* hawk, const hawk_bcs_t* bcsarr, const hawk_loc_t* loc) { hawk_nde_mbs_t* tmp; tmp = (hawk_nde_mbs_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); if (!HAWK_UNLIKELY(tmp)) { ADJERR_LOC(hawk, loc); return HAWK_NULL; } tmp->type = HAWK_NDE_MBS; tmp->loc = *loc; tmp->ptr = hawk_dupbcsarr(hawk, bcsarr, &tmp->len); if (!HAWK_UNLIKELY(tmp)) { hawk_freemem(hawk, tmp); ADJERR_LOC(hawk, loc); return HAWK_NULL; } return (hawk_nde_t*)tmp; } static HAWK_INLINE void update_int_node (hawk_t* hawk, hawk_nde_int_t* node, hawk_int_t lv) { node->val = lv; if (node->str) { hawk_freemem(hawk, node->str); node->str = HAWK_NULL; node->len = 0; } } static HAWK_INLINE void update_flt_node (hawk_t* hawk, hawk_nde_flt_t* node, hawk_flt_t rv) { node->val = rv; if (node->str) { hawk_freemem(hawk, node->str); node->str = HAWK_NULL; node->len = 0; } } static int get_nde_wide_concat_literal (hawk_nde_t* nde, hawk_oocs_t* str, hawk_ooch_t* ch) { switch (nde->type) { case HAWK_NDE_CHAR: *ch = ((hawk_nde_char_t*)nde)->val; str->ptr = ch; str->len = 1; return 1; case HAWK_NDE_STR: str->ptr = ((hawk_nde_str_t*)nde)->ptr; str->len = ((hawk_nde_str_t*)nde)->len; return 1; default: return 0; } } static int get_nde_mbs_concat_literal (hawk_nde_t* nde, hawk_bcs_t* str, hawk_bch_t* ch) { switch (nde->type) { case HAWK_NDE_BCHR: *ch = ((hawk_nde_bchr_t*)nde)->val; str->ptr = ch; str->len = 1; return 1; case HAWK_NDE_MBS: str->ptr = ((hawk_nde_mbs_t*)nde)->ptr; str->len = ((hawk_nde_mbs_t*)nde)->len; return 1; default: return 0; } } static int fold_constants_for_concat ( hawk_t* hawk, hawk_nde_t* left, hawk_nde_t* right, const hawk_loc_t* loc, hawk_nde_t** folded) { hawk_ooch_t lwch[1], rwch[1]; hawk_bch_t lbch[1], rbch[1]; hawk_oocs_t wstr[3]; hawk_bcs_t bstr[3]; *folded = HAWK_NULL; if (get_nde_wide_concat_literal(left, &wstr[0], lwch) && get_nde_wide_concat_literal(right, &wstr[1], rwch)) { hawk_nde_t* nx; wstr[2].ptr = HAWK_NULL; wstr[2].len = 0; nx = new_str_node_with_oocsarr(hawk, wstr, loc); if (HAWK_UNLIKELY(!nx)) return -1; *folded = nx; return 1; } if (get_nde_mbs_concat_literal(left, &bstr[0], lbch) && get_nde_mbs_concat_literal(right, &bstr[1], rbch)) { hawk_nde_t* nx; bstr[2].ptr = HAWK_NULL; bstr[2].len = 0; nx = new_mbs_node_with_bcsarr(hawk, bstr, loc); if (HAWK_UNLIKELY(!nx)) return -1; *folded = nx; return 1; } return 0; /* not folded */ } static hawk_nde_t* new_or_fold_logical_bin_node ( hawk_t* hawk, const hawk_loc_t* loc, int opcode, hawk_nde_t* left, hawk_nde_t* right) { nde_hard_bool_t lh, rh; int lv; hawk_nde_t* tmp; lh = classify_nde_hard_bool_with_depth(left, 0); if (opcode == HAWK_BINOP_LAND && lh == NDE_HARD_BOOL_FALSE) lv = 0; else if (opcode == HAWK_BINOP_LOR && lh == NDE_HARD_BOOL_TRUE) lv = 1; else { rh = classify_nde_hard_bool_with_depth(right, 0); if (lh != NDE_HARD_BOOL_UNKNOWN && rh != NDE_HARD_BOOL_UNKNOWN) { lv = (opcode == HAWK_BINOP_LAND)? (lh == NDE_HARD_BOOL_TRUE && rh == NDE_HARD_BOOL_TRUE): (lh == NDE_HARD_BOOL_TRUE || rh == NDE_HARD_BOOL_TRUE); } else { tmp = new_exp_bin_node(hawk, loc, opcode, left, right); if (HAWK_UNLIKELY(!tmp)) { hawk_clrpt(hawk, right); hawk_clrpt(hawk, left); } return tmp; } } hawk_clrpt(hawk, right); hawk_clrpt(hawk, left); return new_xbool_node(hawk, lv, loc); } static hawk_nde_t* parse_binary ( hawk_t* hawk, const hawk_loc_t* xloc, int skipnl, const binmap_t* binmap, hawk_nde_t*(*next_level_func)(hawk_t*,const hawk_loc_t*)) { hawk_nde_t* left = HAWK_NULL; hawk_nde_t* right = HAWK_NULL; hawk_loc_t rloc; left = next_level_func(hawk, xloc); if (HAWK_UNLIKELY(!left)) goto oops; do { const binmap_t* p = binmap; int matched = 0; int opcode, fold; folded_t folded; while (p->token != TOK_EOF) { if (MATCH(hawk,p->token)) { opcode = p->binop; matched = 1; break; } p++; } if (!matched) break; do { if (get_token(hawk) <= -1) goto oops; } while (skipnl && MATCH(hawk,TOK_NEWLINE)); rloc = hawk->tok.loc; right = next_level_func(hawk, &rloc); if (right == HAWK_NULL) goto oops; fold = fold_constants_for_binop(hawk, left, right, opcode, &folded); switch (fold) { case HAWK_NDE_INT: if (fold == left->type) { hawk_clrpt(hawk, right); right = HAWK_NULL; update_int_node(hawk, (hawk_nde_int_t*)left, folded.l); } else if (fold == right->type) { hawk_clrpt(hawk, left); update_int_node(hawk, (hawk_nde_int_t*)right, folded.l); left = right; right = HAWK_NULL; } else { hawk_clrpt(hawk, right); right = HAWK_NULL; hawk_clrpt(hawk, left); left = HAWK_NULL; left = new_int_node(hawk, folded.l, xloc); if (left == HAWK_NULL) goto oops; } break; case HAWK_NDE_FLT: if (fold == left->type) { hawk_clrpt(hawk, right); right = HAWK_NULL; update_flt_node(hawk, (hawk_nde_flt_t*)left, folded.r); } else if (fold == right->type) { hawk_clrpt(hawk, left); update_flt_node(hawk, (hawk_nde_flt_t*)right, folded.r); left = right; right = HAWK_NULL; } else { hawk_clrpt(hawk, right); right = HAWK_NULL; hawk_clrpt(hawk, left); left = HAWK_NULL; left = new_flt_node(hawk, folded.r, xloc); if (left == HAWK_NULL) goto oops; } break; case -2: goto oops; default: { hawk_nde_t* tmp; tmp = new_exp_bin_node(hawk, xloc, opcode, left, right); if (tmp == HAWK_NULL) goto oops; left = tmp; right = HAWK_NULL; break; } } } while (1); return left; oops: if (right) hawk_clrpt(hawk, right); if (left) hawk_clrpt(hawk, left); return HAWK_NULL; } static hawk_nde_t* parse_logical_or (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* left = HAWK_NULL; hawk_nde_t* right = HAWK_NULL; hawk_nde_t* tmp; hawk_loc_t rloc; left = parse_logical_and(hawk, xloc); if (HAWK_UNLIKELY(!left)) goto oops; while (MATCH(hawk, TOK_LOR)) { do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk, TOK_NEWLINE)); rloc = hawk->tok.loc; right = parse_logical_and(hawk, &rloc); if (HAWK_UNLIKELY(!right)) goto oops; tmp = new_or_fold_logical_bin_node(hawk, xloc, HAWK_BINOP_LOR, left, right); if (HAWK_UNLIKELY(!tmp)) { left = HAWK_NULL; right = HAWK_NULL; goto oops; } left = tmp; right = HAWK_NULL; } return left; oops: if (right) hawk_clrpt(hawk, right); if (left) hawk_clrpt(hawk, left); return HAWK_NULL; } static hawk_nde_t* parse_logical_and (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* left = HAWK_NULL; hawk_nde_t* right = HAWK_NULL; hawk_nde_t* tmp; hawk_loc_t rloc; left = parse_in(hawk, xloc); if (HAWK_UNLIKELY(!left)) goto oops; while (MATCH(hawk, TOK_LAND)) { do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk, TOK_NEWLINE)); rloc = hawk->tok.loc; right = parse_in(hawk, &rloc); if (HAWK_UNLIKELY(!right)) goto oops; tmp = new_or_fold_logical_bin_node(hawk, xloc, HAWK_BINOP_LAND, left, right); if (HAWK_UNLIKELY(!tmp)) { left = HAWK_NULL; right = HAWK_NULL; goto oops; } left = tmp; right = HAWK_NULL; } return left; oops: if (right) hawk_clrpt(hawk, right); if (left) hawk_clrpt(hawk, left); return HAWK_NULL; } static hawk_nde_t* parse_in (hawk_t* hawk, const hawk_loc_t* xloc) { /* static binmap_t map[] = { { TOK_IN, HAWK_BINOP_IN }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_regex_match); */ hawk_nde_t* left = HAWK_NULL; hawk_nde_t* right = HAWK_NULL; hawk_loc_t rloc; left = parse_regex_match(hawk, xloc); if (left == HAWK_NULL) goto oops; do { hawk_nde_t* tmp; if (!MATCH(hawk,TOK_IN)) break; if (get_token(hawk) <= -1) goto oops; rloc = hawk->tok.loc; right = parse_regex_match(hawk, &rloc); if (HAWK_UNLIKELY(!right)) goto oops; #if defined(HAWK_ENABLE_GC) if (!is_var(right) && right->type != HAWK_NDE_XARGV) #else if (!is_plain_var(right) && right->type != HAWK_NDE_XARGV) #endif { hawk_seterrnum(hawk, &rloc, HAWK_ENOTVAR); goto oops; } tmp = new_exp_bin_node(hawk, xloc, HAWK_BINOP_IN, left, right); if (HAWK_UNLIKELY(!left)) goto oops; left = tmp; right = HAWK_NULL; } while (1); return left; oops: if (right) hawk_clrpt(hawk, right); if (left) hawk_clrpt(hawk, left); return HAWK_NULL; } static hawk_nde_t* parse_regex_match (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_TILDE, HAWK_BINOP_MA }, { TOK_NM, HAWK_BINOP_NM }, { TOK_EOF, 0 }, }; return parse_binary(hawk, xloc, 0, map, parse_bitwise_or); } static hawk_nde_t* parse_bitwise_or (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_BOR, HAWK_BINOP_BOR }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_bitwise_xor); } static hawk_nde_t* parse_bitwise_xor (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_BXOR, HAWK_BINOP_BXOR }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_bitwise_and); } static hawk_nde_t* parse_bitwise_and (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_BAND, HAWK_BINOP_BAND }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_equality); } static hawk_nde_t* parse_equality (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_TEQ, HAWK_BINOP_TEQ }, { TOK_TNE, HAWK_BINOP_TNE }, { TOK_EQ, HAWK_BINOP_EQ }, { TOK_NE, HAWK_BINOP_NE }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_relational); } static hawk_nde_t* parse_relational (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_GT, HAWK_BINOP_GT }, { TOK_GE, HAWK_BINOP_GE }, { TOK_LT, HAWK_BINOP_LT }, { TOK_LE, HAWK_BINOP_LE }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_shift); } static hawk_nde_t* parse_shift (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_LS, HAWK_BINOP_LS }, { TOK_RS, HAWK_BINOP_RS }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_concat); } static hawk_nde_t* parse_concat (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* left = HAWK_NULL; hawk_nde_t* right = HAWK_NULL; hawk_nde_t* folded = HAWK_NULL; hawk_loc_t rloc; left = parse_additive(hawk, xloc); if (HAWK_UNLIKELY(!left)) goto oops; do { hawk_nde_t* tmp; if (MATCH(hawk,TOK_CONCAT)) { if (get_token(hawk) <= -1) goto oops; } else if (hawk->opt.trait & HAWK_BLANKCONCAT) { /* * [NOTE] * TOK_TILDE has been commented out in the if condition below because * BINOP_MA has lower precedence than concatenation and it is not certain * the tilde is an unary bitwise negation operator at this phase. * You may use (~10) rather than ~10 after concatenation to avoid confusion. */ if (MATCH(hawk,TOK_LPAREN) || MATCH(hawk,TOK_DOLLAR) || /* unary operators */ MATCH(hawk,TOK_PLUS) || MATCH(hawk,TOK_MINUS) || MATCH(hawk,TOK_LNOT) || /* MATCH(hawk,TOK_TILDE) ||*/ /* increment operators */ MATCH(hawk,TOK_PLUSPLUS) || MATCH(hawk,TOK_MINUSMINUS) || ((hawk->opt.trait & HAWK_TOLERANT) && (hawk->tok.type == TOK_PRINT || hawk->tok.type == TOK_PRINTF)) || hawk->tok.type >= TOK_GETLINE) { /* proceed to handle concatenation expression */ /* nothing to to here. just fall through */ } else break; } else break; rloc = hawk->tok.loc; right = parse_additive(hawk, &rloc); if (HAWK_UNLIKELY(!right)) goto oops; switch (fold_constants_for_concat(hawk, left, right, xloc, &folded)) { case 1: hawk_clrpt(hawk, right); hawk_clrpt(hawk, left); tmp = folded; folded = HAWK_NULL; break; case 0: tmp = new_exp_bin_node(hawk, xloc, HAWK_BINOP_CONCAT, left, right); if (HAWK_UNLIKELY(!tmp)) goto oops; break; default: goto oops; } left = tmp; right = HAWK_NULL; } while (1); return left; oops: if (folded) hawk_clrpt(hawk, folded); if (right) hawk_clrpt(hawk, right); if (left) hawk_clrpt(hawk, left); return HAWK_NULL; } static hawk_nde_t* parse_additive (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_PLUS, HAWK_BINOP_PLUS }, { TOK_MINUS, HAWK_BINOP_MINUS }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_multiplicative); } static hawk_nde_t* parse_multiplicative (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_MUL, HAWK_BINOP_MUL }, { TOK_DIV, HAWK_BINOP_DIV }, { TOK_IDIV, HAWK_BINOP_IDIV }, { TOK_MOD, HAWK_BINOP_MOD }, /* { TOK_EXP, HAWK_BINOP_EXP }, */ { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_unary); } static hawk_nde_t* parse_unary (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* left; hawk_loc_t uloc; int opcode; int fold; folded_t folded; opcode = (MATCH(hawk,TOK_PLUS))? HAWK_UNROP_PLUS: (MATCH(hawk,TOK_MINUS))? HAWK_UNROP_MINUS: (MATCH(hawk,TOK_LNOT))? HAWK_UNROP_LNOT: (MATCH(hawk,TOK_TILDE))? HAWK_UNROP_BNOT: -1; /*if (opcode <= -1) return parse_increment(hawk);*/ if (opcode <= -1) return parse_exponent(hawk, xloc); if (hawk->opt.depth.s.expr_parse > 0 && hawk->parse.depth.expr >= hawk->opt.depth.s.expr_parse) { hawk_seterrnum(hawk, xloc, HAWK_EEXPRNST); return HAWK_NULL; } if (get_token(hawk) <= -1) return HAWK_NULL; hawk->parse.depth.expr++; uloc = hawk->tok.loc; left = parse_unary(hawk, &uloc); hawk->parse.depth.expr--; if (left == HAWK_NULL) return HAWK_NULL; if (opcode == HAWK_UNROP_LNOT) { /* handle the logical negation operator(!) first */ nde_hard_bool_t hb; hb = classify_nde_hard_bool_with_depth(left, 0); if (hb != NDE_HARD_BOOL_UNKNOWN) { hawk_clrpt(hawk, left); return new_xbool_node(hawk, hb == NDE_HARD_BOOL_FALSE, xloc); } } fold = -1; if (left->type == HAWK_NDE_INT) { fold = HAWK_NDE_INT; switch (opcode) { case HAWK_UNROP_PLUS: folded.l = ((hawk_nde_int_t*)left)->val; break; case HAWK_UNROP_MINUS: folded.l = -((hawk_nde_int_t*)left)->val; break; #if 0 case HAWK_UNROP_LNOT: folded.l = !((hawk_nde_int_t*)left)->val; break; #endif case HAWK_UNROP_BNOT: folded.l = ~((hawk_nde_int_t*)left)->val; break; default: fold = -1; break; } } else if (left->type == HAWK_NDE_FLT) { fold = HAWK_NDE_FLT; switch (opcode) { case HAWK_UNROP_PLUS: folded.r = ((hawk_nde_flt_t*)left)->val; break; case HAWK_UNROP_MINUS: folded.r = -((hawk_nde_flt_t*)left)->val; break; #if 0 case HAWK_UNROP_LNOT: folded.l = !((hawk_nde_flt_t*)left)->val; fold = HAWK_NDE_INT; break; #endif case HAWK_UNROP_BNOT: folded.l = ~((hawk_int_t)((hawk_nde_flt_t*)left)->val); fold = HAWK_NDE_INT; break; default: fold = -1; break; } } switch (fold) { case HAWK_NDE_INT: if (left->type == fold) { update_int_node(hawk, (hawk_nde_int_t*)left, folded.l); return left; } else { HAWK_ASSERT(left->type == HAWK_NDE_FLT); hawk_clrpt(hawk, left); return new_int_node(hawk, folded.l, xloc); } case HAWK_NDE_FLT: if (left->type == fold) { update_flt_node(hawk, (hawk_nde_flt_t*)left, folded.r); return left; } else { HAWK_ASSERT(left->type == HAWK_NDE_INT); hawk_clrpt(hawk, left); return new_flt_node(hawk, folded.r, xloc); } default: { hawk_nde_exp_t* nde; nde = (hawk_nde_exp_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (nde == HAWK_NULL) { hawk_clrpt(hawk, left); ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_EXP_UNR; nde->loc = *xloc; nde->opcode = opcode; nde->left = left; /*nde->right = HAWK_NULL;*/ return (hawk_nde_t*)nde; } } } static hawk_nde_t* parse_exponent (hawk_t* hawk, const hawk_loc_t* xloc) { static binmap_t map[] = { { TOK_EXP, HAWK_BINOP_EXP }, { TOK_EOF, 0 } }; return parse_binary(hawk, xloc, 0, map, parse_unary_exp); } static hawk_nde_t* parse_unary_exp (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_exp_t* nde; hawk_nde_t* left; hawk_loc_t uloc; int opcode; opcode = (MATCH(hawk,TOK_PLUS))? HAWK_UNROP_PLUS: (MATCH(hawk,TOK_MINUS))? HAWK_UNROP_MINUS: (MATCH(hawk,TOK_LNOT))? HAWK_UNROP_LNOT: (MATCH(hawk,TOK_TILDE))? HAWK_UNROP_BNOT: -1; /* ~ in the unary context is a bitwise-not operator */ if (opcode <= -1) return parse_increment(hawk, xloc); if (hawk->opt.depth.s.expr_parse > 0 && hawk->parse.depth.expr >= hawk->opt.depth.s.expr_parse) { hawk_seterrnum(hawk, xloc, HAWK_EEXPRNST); return HAWK_NULL; } if (get_token(hawk) <= -1) return HAWK_NULL; hawk->parse.depth.expr++; uloc = hawk->tok.loc; left = parse_unary(hawk, &uloc); hawk->parse.depth.expr--; if (left == HAWK_NULL) return HAWK_NULL; nde = (hawk_nde_exp_t*) hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (nde == HAWK_NULL) { hawk_clrpt(hawk, left); ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_EXP_UNR; nde->loc = *xloc; nde->opcode = opcode; nde->left = left; nde->right = HAWK_NULL; return (hawk_nde_t*)nde; } static hawk_nde_t* parse_increment (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_exp_t* nde; hawk_nde_t* left; int type, opcode, opcode1, opcode2; hawk_loc_t ploc; /* check for prefix increment operator */ opcode1 = MATCH(hawk,TOK_PLUSPLUS)? HAWK_INCOP_PLUS: MATCH(hawk,TOK_MINUSMINUS)? HAWK_INCOP_MINUS: -1; if (opcode1 != -1) { /* there is a prefix increment operator */ if (get_token(hawk) <= -1) return HAWK_NULL; } ploc = hawk->tok.loc; left = parse_primary(hawk, &ploc); if (HAWK_UNLIKELY(!left)) return HAWK_NULL; /* check for postfix increment operator */ opcode2 = MATCH(hawk,TOK_PLUSPLUS)? HAWK_INCOP_PLUS: MATCH(hawk,TOK_MINUSMINUS)? HAWK_INCOP_MINUS: -1; if (!(hawk->opt.trait & HAWK_BLANKCONCAT)) { if (opcode1 != -1 && opcode2 != -1) { /* both prefix and postfix increment operator. * not allowed */ hawk_clrpt(hawk, left); hawk_seterrnum(hawk, xloc, HAWK_EPREPST); return HAWK_NULL; } } if (opcode1 == -1 && opcode2 == -1) { /* no increment operators */ return left; } if (opcode1 != -1) { /* prefix increment operator. * ignore a potential postfix operator */ type = HAWK_NDE_EXP_INCPRE; opcode = opcode1; } else /*if (opcode2 != -1)*/ { HAWK_ASSERT(opcode2 != -1); /* postfix increment operator */ type = HAWK_NDE_EXP_INCPST; opcode = opcode2; /* let's do it later if (get_token(hawk) <= -1) { hawk_clrpt(hawk, left); return HAWK_NULL; } */ } if (!is_var(left) && left->type != HAWK_NDE_POS) { if (type == HAWK_NDE_EXP_INCPST) { /* For an expression like 1 ++y, * left is 1. so we leave ++ for y. */ return left; } else { hawk_clrpt(hawk, left); hawk_seterrnum(hawk, xloc, HAWK_EINCDECOPR); return HAWK_NULL; } } if (type == HAWK_NDE_EXP_INCPST) { /* consume the postfix operator */ if (get_token(hawk) <= -1) { hawk_clrpt(hawk, left); return HAWK_NULL; } } nde = (hawk_nde_exp_t*) hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (nde == HAWK_NULL) { hawk_clrpt(hawk, left); ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = type; nde->loc = *xloc; nde->opcode = opcode; nde->left = left; nde->right = HAWK_NULL; return (hawk_nde_t*)nde; } #define FNTYPE_UNKNOWN 0 #define FNTYPE_FNC 1 #define FNTYPE_FUN 2 static HAWK_INLINE int isfunname (hawk_t* hawk, const hawk_oocs_t* name, hawk_fun_t** fun) { hawk_htb_pair_t* pair; /* check if it is an hawk function being processed currently */ if (hawk->tree.cur_fun.ptr) { if (hawk_comp_oochars(hawk->tree.cur_fun.ptr, hawk->tree.cur_fun.len, name->ptr, name->len, 0) == 0) { /* the current function begin parsed */ return FNTYPE_FUN; } } /* check the funtion name in the function table */ pair = hawk_htb_search(hawk->tree.funs, name->ptr, name->len); if (pair) { /* one of the functions defined previously */ if (fun) { *fun = (hawk_fun_t*)HAWK_HTB_VPTR(pair); HAWK_ASSERT(*fun != HAWK_NULL); } return FNTYPE_FUN; } /* check if it is a function not resolved so far */ if (hawk_htb_search(hawk->parse.funs, name->ptr, name->len)) { /* one of the function calls not resolved so far. */ return FNTYPE_FUN; } return FNTYPE_UNKNOWN; } static HAWK_INLINE int isfnname (hawk_t* hawk, const hawk_oocs_t* name) { if (hawk_findfncwithoocs(hawk, name) != HAWK_NULL) { /* implicit function */ return FNTYPE_FNC; } return isfunname(hawk, name, HAWK_NULL); } static hawk_nde_t* parse_primary_char (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_char_t* nde; nde = (hawk_nde_char_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_CHAR; nde->loc = *xloc; nde->val = HAWK_OOECS_CHAR(hawk->tok.name, 0); if (get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_bchr (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_bchr_t* nde; nde = (hawk_nde_bchr_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_BCHR; nde->loc = *xloc; nde->val = HAWK_OOECS_CHAR(hawk->tok.name, 0); if (get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_int (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_int_t* nde; /* create the node for the literal */ nde = (hawk_nde_int_t*)new_int_node( hawk, hawk_oochars_to_int (HAWK_OOECS_PTR(hawk->tok.name), HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOCHARS_TO_INT_MAKE_OPTION(0, 0, 0), HAWK_NULL, HAWK_NULL), xloc ); if (HAWK_UNLIKELY(!nde)) return HAWK_NULL; HAWK_ASSERT(HAWK_OOECS_LEN(hawk->tok.name) == hawk_count_oocstr(HAWK_OOECS_PTR(hawk->tok.name))); /* remember the literal in the original form */ nde->len = HAWK_OOECS_LEN(hawk->tok.name); nde->str = hawk_dupoocs(hawk, HAWK_OOECS_OOCS(hawk->tok.name)); if (!nde->str || get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); if (nde->str) hawk_freemem(hawk, nde->str); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_flt (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_flt_t* nde; /* create the node for the literal */ nde = (hawk_nde_flt_t*)new_flt_node( hawk, hawk_oochars_to_flt(HAWK_OOECS_PTR(hawk->tok.name), HAWK_OOECS_LEN(hawk->tok.name), HAWK_NULL, 0), xloc ); if (HAWK_UNLIKELY(!nde)) return HAWK_NULL; HAWK_ASSERT( HAWK_OOECS_LEN(hawk->tok.name) == hawk_count_oocstr(HAWK_OOECS_PTR(hawk->tok.name))); /* remember the literal in the original form */ nde->len = HAWK_OOECS_LEN(hawk->tok.name); nde->str = hawk_dupoocs(hawk, HAWK_OOECS_OOCS(hawk->tok.name)); if (!nde->str || get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); if (nde->str) hawk_freemem(hawk, nde->str); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_str (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_str_t* nde; nde = (hawk_nde_str_t*) hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (nde == HAWK_NULL) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_STR; nde->loc = *xloc; nde->len = HAWK_OOECS_LEN(hawk->tok.name); nde->ptr = hawk_dupoocs(hawk, HAWK_OOECS_OOCS(hawk->tok.name)); if (nde->ptr == HAWK_NULL || get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); if (nde->ptr) hawk_freemem(hawk, nde->ptr); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_mbs (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_mbs_t* nde; nde = (hawk_nde_mbs_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_MBS; nde->loc = *xloc; #if defined(HAWK_OOCH_IS_BCH) nde->len = HAWK_OOECS_LEN(hawk->tok.name); nde->ptr = hawk_dupoocs(hawk, HAWK_OOECS_OOCS(hawk->tok.name)); if (!nde->ptr) { ADJERR_LOC(hawk, xloc); goto oops; } #else { /* the MBS token doesn't include a character greater than 0xFF in hawk->tok.name * even though it is a unicode string. simply copy over without conversion */ nde->len = HAWK_OOECS_LEN(hawk->tok.name); nde->ptr = hawk_allocmem(hawk, (nde->len + 1) * HAWK_SIZEOF(*nde->ptr)); if (!nde->ptr) { ADJERR_LOC(hawk, xloc); goto oops; } hawk_copy_uchars_to_bchars (nde->ptr, HAWK_OOECS_PTR(hawk->tok.name), nde->len); nde->ptr[nde->len] = '\0'; } #endif if (get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); if (nde->ptr) hawk_freemem(hawk, nde->ptr); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_rex (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_rex_t* nde; /* the regular expression is tokenized here because * of the context-sensitivity of the slash symbol. * if TOK_DIV is seen as a primary, it tries to compile * it as a regular expression */ hawk_ooecs_clear(hawk->tok.name); if (MATCH(hawk,TOK_DIV_ASSN) && hawk_ooecs_ccat(hawk->tok.name, HAWK_T('=')) == (hawk_oow_t)-1) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } SET_TOKEN_TYPE(hawk, &hawk->tok, TOK_REX); if (get_rexstr(hawk, &hawk->tok) <= -1) return HAWK_NULL; HAWK_ASSERT(MATCH(hawk,TOK_REX)); nde = (hawk_nde_rex_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (nde == HAWK_NULL) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_REX; nde->loc = *xloc; nde->str.len = HAWK_OOECS_LEN(hawk->tok.name); nde->str.ptr = hawk_dupoocs(hawk, HAWK_OOECS_OOCS(hawk->tok.name)); if (nde->str.ptr == HAWK_NULL) goto oops; if (hawk_buildrex(hawk, HAWK_OOECS_PTR(hawk->tok.name), HAWK_OOECS_LEN(hawk->tok.name), &nde->code[0], &nde->code[1]) <= -1) { ADJERR_LOC(hawk, xloc); goto oops; } if (get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); if (nde->code[0]) hawk_freerex(hawk, nde->code[0], nde->code[1]); if (nde->str.ptr) hawk_freemem(hawk, nde->str.ptr); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_positional (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_pos_t* nde; hawk_loc_t ploc; nde = (hawk_nde_pos_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = HAWK_NDE_POS; nde->loc = *xloc; if (get_token(hawk) <= -1) return HAWK_NULL; ploc = hawk->tok.loc; nde->val = parse_primary(hawk, &ploc); if (HAWK_UNLIKELY(!nde->val)) goto oops; return (hawk_nde_t*)nde; oops: if (nde) { if (nde->val) hawk_clrpt(hawk, nde->val); hawk_freemem(hawk, nde); } return HAWK_NULL; } static hawk_nde_t* _parse_primary_array_or_map (hawk_t* hawk, const hawk_loc_t* xloc, const hawk_oocs_t* full, const hawk_oocs_t segs[], int nsegs, int closer_token) { /* called for @[ or @{ handling. the caller of this function passes "hawk::array" or * "hawk::map" via "full'. */ hawk_mod_t* mod; hawk_mod_sym_t sym; hawk_fnc_t fnc; mod = query_module(hawk, segs, nsegs, &sym, HAWK_NULL); if (HAWK_UNLIKELY(!mod)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } if (sym.type != HAWK_MOD_FNC || ((hawk->opt.trait & sym.u.fnc_.trait) != sym.u.fnc_.trait)) { hawk_seterrfmt(hawk, xloc, HAWK_EUNDEF, FMT_EUNDEF, full->len, full->ptr); return HAWK_NULL; } HAWK_MEMSET(&fnc, 0, HAWK_SIZEOF(fnc)); fnc.name.ptr = full->ptr; fnc.name.len = full->len; fnc.spec = sym.u.fnc_; fnc.mod = mod; /* convert it to a function call */ return parse_fncall(hawk, full, &fnc, xloc, (closer_token == TOK_RBRACE? FNCALL_FLAG_MAP: 0), closer_token); } static hawk_nde_t* parse_primary_array (hawk_t* hawk, const hawk_loc_t* xloc) { /* treat it as if it's hawk::array() */ hawk_oocs_t segs[2]; hawk_oocs_t full; hawk_nde_t* nde; segs[0].ptr = HAWK_T("hawk"); segs[0].len = 4; segs[1].ptr = HAWK_T("array"); segs[1].len = 5; full.len = 11; full.ptr = hawk_allocmem(hawk, HAWK_SIZEOF(*full.ptr) * 12); if (HAWK_UNLIKELY(!full.ptr)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ENOMEM, HAWK_T("unable to allocate internal name for @[ - %js"), bem); return HAWK_NULL; } hawk_copy_oochars_to_oocstr_unlimited(&full.ptr[0], HAWK_T("hawk::array"), 11); nde = _parse_primary_array_or_map(hawk, xloc, &full, segs, 2, TOK_RBRACK); if (HAWK_UNLIKELY(!nde)) hawk_freemem(hawk, full.ptr); return nde; } static hawk_nde_t* parse_primary_map (hawk_t* hawk, const hawk_loc_t* xloc) { /* treat it as if it's hawk::map() */ hawk_oocs_t segs[2]; hawk_oocs_t full; hawk_nde_t* nde; segs[0].ptr = HAWK_T("hawk"); segs[0].len = 4; segs[1].ptr = HAWK_T("map"); segs[1].len = 3; full.len = 9; full.ptr = hawk_allocmem(hawk, HAWK_SIZEOF(*full.ptr) * 10); if (HAWK_UNLIKELY(!full.ptr)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ENOMEM, HAWK_T("unable to allocate internal name for @{ - %js"), bem); return HAWK_NULL; } hawk_copy_oochars_to_oocstr_unlimited(&full.ptr[0], HAWK_T("hawk::map"), 9); nde = _parse_primary_array_or_map(hawk, xloc, &full, segs, 2, TOK_RBRACE); if (HAWK_UNLIKELY(!nde)) hawk_freemem(hawk, full.ptr); return nde; } static hawk_nde_t* parse_primary_lparen (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* nde; hawk_nde_t* last; hawk_loc_t eloc; hawk_oow_t opening_lparen_seq; opening_lparen_seq = hawk->parse.lparen_seq++; /* eat up the left parenthesis */ if (get_token(hawk) <= -1) return HAWK_NULL; /* parse the sub-expression inside the parentheses */ eloc = hawk->tok.loc; nde = parse_expr_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!nde)) return HAWK_NULL; /* parse subsequent expressions separated by a comma, if any */ last = nde; HAWK_ASSERT(last->next == HAWK_NULL); while (MATCH(hawk,TOK_COMMA)) { hawk_nde_t* tmp; do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk,TOK_NEWLINE)); eloc = hawk->tok.loc; tmp = parse_expr_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!tmp)) goto oops; HAWK_ASSERT(tmp->next == HAWK_NULL); last->next = tmp; last = tmp; } /* ----------------- */ /* check for the closing parenthesis */ if (!MATCH(hawk,TOK_RPAREN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERPAREN, FMT_ERPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } /* remember the sequence number of the left parenthesis * that's been just closed by the matching right parenthesis */ hawk->parse.lparen_last_closed = opening_lparen_seq; hawk->tok.flags |= TOK_FLAGS_LPAREN_CLOSER; /* indicate that this RPAREN is closing LPAREN */ if (get_token(hawk) <= -1) goto oops; /* check if it is a chained node */ if (nde->next) { /* if so, it is an expression group */ /* (expr1, expr2, expr2) */ hawk_nde_grp_t* tmp; if ((hawk->parse.id.stmt != TOK_PRINT && hawk->parse.id.stmt != TOK_PRINTF) || hawk->parse.depth.expr != 1) { if (!(hawk->opt.trait & HAWK_TOLERANT) && !MATCH(hawk,TOK_IN)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EKWIN, FMT_EKWIN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } tmp = (hawk_nde_grp_t*)hawk_callocmem(hawk, HAWK_SIZEOF(hawk_nde_grp_t)); if (HAWK_UNLIKELY(!tmp)) { ADJERR_LOC(hawk, xloc); goto oops; } tmp->type = HAWK_NDE_GRP; tmp->loc = *xloc; tmp->body = nde; nde = (hawk_nde_t*)tmp; } /* ----------------- */ return nde; oops: if (nde) hawk_clrpt(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_getline (hawk_t* hawk, const hawk_loc_t* xloc, int mbs) { /* parse the statement-level getline. * getline after the pipe symbols(|,||) is parsed * by parse_primary(). */ hawk_nde_getline_t* nde; hawk_loc_t ploc; nde = (hawk_nde_getline_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) goto oops; nde->type = HAWK_NDE_GETLINE; nde->mbs = mbs; nde->loc = *xloc; nde->in_type = HAWK_IN_CONSOLE; if (get_token(hawk) <= -1) goto oops; if (MATCH(hawk,TOK_IDENT) || MATCH(hawk,TOK_DOLLAR)) { /* getline var * getline $XXX */ if ((hawk->opt.trait & HAWK_BLANKCONCAT) && MATCH(hawk,TOK_IDENT)) { /* i need to perform some precheck on if the identifier is * really a variable */ if (preget_token(hawk) <= -1) goto oops; if (hawk->ntok.type == TOK_DBLCOLON) goto novar; if (hawk->ntok.type == TOK_LPAREN) { if (hawk->ntok.loc.line == hawk->tok.loc.line && hawk->ntok.loc.colm == hawk->tok.loc.colm + HAWK_OOECS_LEN(hawk->tok.name)) { /* it's in the form of a function call since * there is no spaces between the identifier * and the left parenthesis. */ goto novar; } } if (isfnname(hawk, HAWK_OOECS_OOCS(hawk->tok.name)) != FNTYPE_UNKNOWN) goto novar; } ploc = hawk->tok.loc; nde->var = parse_primary(hawk, &ploc); if (HAWK_UNLIKELY(!nde->var)) goto oops; if (!is_var(nde->var) && nde->var->type != HAWK_NDE_POS) { /* this is 'getline' followed by an expression probably. * getline a() * getline sys::WNOHANG */ hawk_seterrnum(hawk, &ploc, HAWK_EBADARG); goto oops; } } novar: if (MATCH(hawk, TOK_LT)) { /* getline [var] < file */ if (get_token(hawk) <= -1) goto oops; ploc = hawk->tok.loc; /* TODO: is this correct? */ /*nde->in = parse_expr_withdc(hawk, &ploc);*/ nde->in = parse_primary(hawk, &ploc); if (HAWK_UNLIKELY(!nde->in)) goto oops; nde->in_type = HAWK_IN_FILE; } return (hawk_nde_t*)nde; oops: if (nde) { if (nde->in) hawk_clrpt(hawk, nde->in); if (nde->var) hawk_clrpt(hawk, nde->var); hawk_freemem(hawk, nde); } return HAWK_NULL; } static hawk_nde_t* parse_primary_xnil (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_xnil_t* nde; nde = (hawk_nde_xnil_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_XNIL; nde->loc = *xloc; if (get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_xtrue (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_xnil_t* nde; nde = (hawk_nde_xnil_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_XTRUE; nde->loc = *xloc; if (get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_xfalse (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_xnil_t* nde; nde = (hawk_nde_xnil_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_XFALSE; nde->loc = *xloc; if (get_token(hawk) <= -1) goto oops; return (hawk_nde_t*)nde; oops: HAWK_ASSERT(nde != HAWK_NULL); hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_primary_xarg (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* nde = HAWK_NULL; hawk_nde_t* pos = HAWK_NULL; int nde_type; /* @argc, @argv, @argv[] look like the standard ARGC and ARGV but form special constructs * for function arguments. this is useful when combined with the variadic function arguments. * it can't be used in assignment, delete, reset, in many other context, however. * when used in the main entry point function, @argc is the number of arguments to the * function but ARGC is the number of arguments including the program name so ARGC is greater * than @argc by 1. */ HAWK_ASSERT(hawk->tok.type == TOK_XARGV || hawk->tok.type == TOK_XARGC); if (hawk->tok.type == TOK_XARGV) { /* @argv */ nde_type = HAWK_NDE_XARGV; if (get_token(hawk) <= -1) goto oops; if (MATCH(hawk,TOK_LBRACK)) /* @argv[, expecting to see @argv[expr] */ { hawk_loc_t eloc; if (get_token(hawk) <= -1) goto oops; eloc = hawk->tok.loc; pos = parse_expr_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!pos)) goto oops; if (!MATCH(hawk,TOK_RBRACK)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERBRACK, FMT_ERBRACK, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (get_token(hawk) <= -1) goto oops; } } else { nde_type = HAWK_NDE_XARGC; if (get_token(hawk) <= -1) goto oops; } if (pos) { hawk_nde_xargvidx_t* xargvidx; xargvidx = (hawk_nde_xargvidx_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*xargvidx)); if (HAWK_UNLIKELY(!xargvidx)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } xargvidx->type = HAWK_NDE_XARGVIDX; xargvidx->loc = *xloc; xargvidx->pos = pos; nde = (hawk_nde_t*)xargvidx; } else { nde = hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = nde_type; nde->loc = *xloc; } return (hawk_nde_t*)nde; oops: if (nde) hawk_freemem(hawk, nde); /* tricky to call hawk_clrpt() on nde due to pos */ if (pos) hawk_clrpt(hawk, pos); return HAWK_NULL; } static hawk_nde_t* parse_primary_literal (hawk_t* hawk, const hawk_loc_t* xloc) { switch (hawk->tok.type) { case TOK_CHAR: return parse_primary_char(hawk, xloc); case TOK_BCHR: return parse_primary_bchr(hawk, xloc); case TOK_INT: return parse_primary_int(hawk, xloc); case TOK_FLT: return parse_primary_flt(hawk, xloc); case TOK_STR: return parse_primary_str(hawk, xloc); case TOK_MBS: return parse_primary_mbs(hawk, xloc); default: { hawk_tok_t* xtok; xtok = MATCH(hawk,TOK_NEWLINE)? &hawk->ptok: &hawk->tok; hawk_seterrfmt(hawk, &xtok->loc, HAWK_EEXPRNR, HAWK_T("literal expression not recognized around '%.*js'"), HAWK_OOECS_LEN(xtok->name), HAWK_OOECS_PTR(xtok->name)); return HAWK_NULL; } } } static hawk_nde_t* parse_primary_nopipe (hawk_t* hawk, const hawk_loc_t* xloc) { switch (hawk->tok.type) { case TOK_IDENT: return parse_primary_ident(hawk, xloc); case TOK_ATBRACK: /* @[ */ return parse_primary_array(hawk, xloc); case TOK_ATBRACE: /* @{ */ return parse_primary_map(hawk, xloc); case TOK_CHAR: return parse_primary_char(hawk, xloc); case TOK_BCHR: return parse_primary_bchr(hawk, xloc); case TOK_INT: return parse_primary_int(hawk, xloc); case TOK_FLT: return parse_primary_flt(hawk, xloc); case TOK_STR: return parse_primary_str(hawk, xloc); case TOK_MBS: return parse_primary_mbs(hawk, xloc); case TOK_DIV: case TOK_DIV_ASSN: return parse_primary_rex(hawk, xloc); case TOK_DOLLAR: return parse_primary_positional(hawk, xloc); case TOK_LPAREN: return parse_primary_lparen(hawk, xloc); case TOK_GETBLINE: return parse_primary_getline(hawk, xloc, 1); case TOK_GETLINE: return parse_primary_getline(hawk, xloc, 0); case TOK_FUNCTION: #if defined(HAWK_ENABLE_FUN_AS_VALUE) return parse_function(hawk, 0); #else hawk_seterrfmt(hawk, xloc, HAWK_EFUNNAM, HAWK_T("function literal not supported")); return HAWK_NULL; #endif case TOK_XNIL: return parse_primary_xnil(hawk, xloc); case TOK_XTRUE: return parse_primary_xtrue(hawk, xloc); case TOK_XFALSE: return parse_primary_xfalse(hawk, xloc); case TOK_XARGV: case TOK_XARGC: return parse_primary_xarg(hawk, xloc); default: { /* in the tolerant mode, we treat print and printf * as a function like getline */ if ((hawk->opt.trait & HAWK_TOLERANT) && (MATCH(hawk,TOK_PRINT) || MATCH(hawk,TOK_PRINTF))) { if (get_token(hawk) <= -1) return HAWK_NULL; return parse_print(hawk, xloc); } /* valid expression introducer is expected */ if (MATCH(hawk,TOK_NEWLINE)) { hawk_seterrfmt(hawk, &hawk->ptok.loc, HAWK_EEXPRNR, HAWK_T("unexpected newline after '%.*js'"), HAWK_OOECS_LEN(hawk->ptok.name), HAWK_OOECS_PTR(hawk->ptok.name)); } else { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EEXPRNR, HAWK_T("expression not recognized around '%.*js'"), HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); } return HAWK_NULL; } } } static hawk_nde_t* parse_primary (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* left; hawk_nde_getline_t* nde; hawk_nde_t* var = HAWK_NULL; hawk_loc_t ploc; left = parse_primary_nopipe(hawk, xloc); if (!left) goto oops; /* handle the piping part */ do { int intype = -1; int mbs; if (hawk->opt.trait & HAWK_RIO) { if (MATCH(hawk,TOK_BOR)) { intype = HAWK_IN_PIPE; } else if (MATCH(hawk,TOK_PIPE_BD) && (hawk->parse.pragma.trait & HAWK_RWPIPE)) { intype = HAWK_IN_RWPIPE; } } if (intype == -1) break; if (preget_token(hawk) <= -1) goto oops; if (hawk->ntok.type == TOK_GETBLINE) mbs = 1; else if (hawk->ntok.type == TOK_GETLINE) mbs = 0; else break; /* consume ntok('getline') */ get_token(hawk); /* no error check needed as it's guaranteeded to succeed for preget_token() above */ /* get the next token */ if (get_token(hawk) <= -1) goto oops; /* TODO: is this correct? */ if (MATCH(hawk,TOK_IDENT) || MATCH(hawk,TOK_DOLLAR)) { /* command | getline var * command || getline var */ if ((hawk->opt.trait & HAWK_BLANKCONCAT) && MATCH(hawk,TOK_IDENT)) { /* i need to perform some precheck on if the identifier is * really a variable */ if (preget_token(hawk) <= -1) goto oops; if (hawk->ntok.type == TOK_DBLCOLON) goto novar; if (hawk->ntok.type == TOK_LPAREN) { if (hawk->ntok.loc.line == hawk->tok.loc.line && hawk->ntok.loc.colm == hawk->tok.loc.colm + HAWK_OOECS_LEN(hawk->tok.name)) { /* it's in the form of a function call since * there is no spaces between the identifier * and the left parenthesis. */ goto novar; } } if (isfnname(hawk, HAWK_OOECS_OOCS(hawk->tok.name)) != FNTYPE_UNKNOWN) goto novar; } ploc = hawk->tok.loc; var = parse_primary(hawk, &ploc); if (!var) goto oops; if (!is_var(var) && var->type != HAWK_NDE_POS) { /* fucntion a() {} * print ("ls -laF" | getline a()) */ hawk_seterrnum(hawk, &ploc, HAWK_EBADARG); goto oops; } } novar: nde = (hawk_nde_getline_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); goto oops; } nde->type = HAWK_NDE_GETLINE; nde->loc = *xloc; nde->var = var; nde->mbs = mbs; nde->in_type = intype; nde->in = left; left = (hawk_nde_t*)nde; var = HAWK_NULL; } while (1); return left; oops: if (var) hawk_clrpt(hawk, var); hawk_clrpt(hawk, left); return HAWK_NULL; } static hawk_nde_t* parse_variable (hawk_t* hawk, const hawk_loc_t* xloc, hawk_nde_type_t type, const hawk_oocs_t* name, hawk_oow_t idxa, int is_const) { hawk_nde_var_t* nde; int is_fcv = 0; if (MATCH(hawk,TOK_LPAREN)) { #if defined(HAWK_ENABLE_FUN_AS_VALUE) /* if (MATCH(hawk,TOK_LPAREN) && (!(hawk->opt.trait & HAWK_BLANKCONCAT) || (hawk->tok.loc.line == xloc->line && hawk->tok.loc.colm == xloc->colm + name->len))) */ if (hawk->tok.loc.line == xloc->line && hawk->tok.loc.colm == xloc->colm + name->len) { is_fcv = 1; } else #endif if (!(hawk->opt.trait & HAWK_BLANKCONCAT)) { /* if concatenation by blanks is not allowed, the explicit * concatenation operator(%%) must be used. so it is obvious * that it is a function call, which is illegal for a variable. * if implicit, "var_xxx (1)" may be concatenation of * the value of var_xxx and 1. */ /* a variable is not a function */ hawk_seterrfmt(hawk, xloc, HAWK_EFUNNAM, HAWK_T("'%.*js' not a valid function name"), name->len, name->ptr); return HAWK_NULL; } } nde = (hawk_nde_var_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = type; nde->loc = *xloc; /*nde->id.name.ptr = HAWK_NULL;*/ nde->id.name.ptr = name->ptr; nde->id.name.len = name->len; nde->id.idxa = idxa; nde->idx = HAWK_NULL; nde->is_const = is_const; #if defined(HAWK_ENABLE_FUN_AS_VALUE) if (!is_fcv) return (hawk_nde_t*)nde; return parse_fncall(hawk, (const hawk_oocs_t*)nde, HAWK_NULL, xloc, FNCALL_FLAG_EXPR, TOK_RPAREN); #else return (hawk_nde_t*)nde; #endif } static int dup_ident_and_get_next (hawk_t* hawk, const hawk_loc_t* xloc, hawk_oocs_t* name, int max) { int nsegs = 0; HAWK_ASSERT(MATCH(hawk,TOK_IDENT)); do { name[nsegs].ptr = HAWK_OOECS_PTR(hawk->tok.name); name[nsegs].len = HAWK_OOECS_LEN(hawk->tok.name); /* duplicate the identifier */ name[nsegs].ptr = hawk_dupoochars(hawk, name[nsegs].ptr, name[nsegs].len); if (!name[nsegs].ptr) { ADJERR_LOC(hawk, xloc); goto oops; } nsegs++; if (get_token(hawk) <= -1) goto oops; if (!MATCH(hawk,TOK_DBLCOLON)) break; if (get_token(hawk) <= -1) goto oops; /* the identifier after :: * allow reserved words as well since i view the whole name(mod::ident) * as one segment. however, i don't want the identifier part to begin * with @. some extended keywords begin with @ like @include. * TOK_XGLOBAL to TOK_XRESET are excuded from the check for that reason. */ if (!MATCH(hawk, TOK_IDENT) && !(MATCH_RANGE(hawk, TOK_BEGIN, TOK_GETLINE))) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EIDENT, FMT_EIDENT, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } if (nsegs >= max) { hawk_seterrnum(hawk, xloc, HAWK_ESEGTM); goto oops; } } while (1); return nsegs; oops: while (nsegs > 0) hawk_freemem(hawk, name[--nsegs].ptr); return -1; } #if defined(HAWK_ENABLE_FUN_AS_VALUE) static hawk_nde_t* parse_fun_as_value (hawk_t* hawk, const hawk_oocs_t* name, const hawk_loc_t* xloc, hawk_fun_t* funptr) { hawk_nde_fun_t* nde; /* create the node for the literal */ nde = (hawk_nde_fun_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_FUN; nde->loc = *xloc; nde->name.ptr = name->ptr; nde->name.len = name->len; nde->funptr = funptr; return (hawk_nde_t*)nde; } #endif static hawk_nde_t* parse_primary_ident_noseg (hawk_t* hawk, const hawk_loc_t* xloc, const hawk_oocs_t* name) { hawk_fnc_t* fnc; hawk_oow_t idxa; hawk_nde_t* nde = HAWK_NULL; /* check if name is an intrinsic function name */ fnc = hawk_findfncwithoocs(hawk, name); if (fnc) { if (MATCH(hawk,TOK_LPAREN) || fnc->dfl0) { if (fnc->spec.arg.min > fnc->spec.arg.max) { /* this intrinsic function is located in the specificed module. * convert the function call to a module call. i do this to * exclude some instrinsic functions from the main engine. * e.g) sin -> math::sin * cos -> math::cos */ hawk_oocs_t segs[2]; HAWK_ASSERT(fnc->spec.arg.spec != HAWK_NULL); segs[0].ptr = (hawk_ooch_t*)fnc->spec.arg.spec; segs[0].len = hawk_count_oocstr(fnc->spec.arg.spec); segs[1] = *name; return parse_primary_ident_segs(hawk, xloc, name, segs, 2); } /* fnc->dfl0 means that the function can be called without (). e.g. length */ nde = parse_fncall(hawk, name, fnc, xloc, ((!MATCH(hawk,TOK_LPAREN) && fnc->dfl0)? FNCALL_FLAG_NOARG: 0), TOK_RPAREN); } else { /* an intrinsic function should be in the form of the function call */ hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); } } /* now we know that name is a normal identifier. */ else if (MATCH(hawk,TOK_LBRACK)) { nde = parse_hashidx(hawk, name, xloc); } else if (MATCH(hawk,TOK_PERIOD)) { nde = parse_dotidx(hawk, name, xloc); } else if ((idxa = hawk_arr_rsearch(hawk->parse.lcls, hawk->parse.lcl_base, HAWK_ARR_SIZE(hawk->parse.lcls), name->ptr, name->len)) != HAWK_ARR_NIL) { /* local variable */ const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; /* note an extra space has been reserved in each slot value * when initializing hawk->parser.lcls. the inline_slot_xtnsize field * was set to the size of hawk_var_xinfo_t */ ptl = HAWK_ARR_DPTL(hawk->parse.lcls, idxa); vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); if (hawk->parse.pragma.trait & HAWK_PEDANTIC) vxi->used = 1; nde = parse_variable(hawk, xloc, HAWK_NDE_LCL, name, idxa - hawk->parse.lcl_base, vxi->is_const); } else if ((idxa = hawk_arr_search(hawk->parse.params, hawk->parse.param_base, HAWK_ARR_SIZE(hawk->parse.params), name->ptr, name->len)) != HAWK_ARR_NIL) { /* parameter */ nde = parse_variable(hawk, xloc, HAWK_NDE_ARG, name, idxa - hawk->parse.param_base, 0); } else if ((idxa = get_global(hawk, name)) != HAWK_ARR_NIL) { /* global variable */ const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; /* note an extra space has been reserved in each slot value * when initializing hawk->parser.gbls. the inline_slot_xtnsize field * was set to the size of hawk_var_xinfo_t */ ptl = HAWK_ARR_DPTL(hawk->parse.gbls, idxa); vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); if (hawk->parse.pragma.trait & HAWK_PEDANTIC) vxi->used = 1; nde = parse_variable(hawk, xloc, HAWK_NDE_GBL, name, idxa, vxi->is_const); } else { int fntype; hawk_fun_t* funptr = HAWK_NULL; fntype = isfunname(hawk, name, &funptr); if (fntype) { HAWK_ASSERT(fntype == FNTYPE_FUN); if (MATCH(hawk,TOK_LPAREN)) { /* must be a function name */ HAWK_ASSERT(hawk_htb_search(hawk->parse.named, name->ptr, name->len) == HAWK_NULL); nde = parse_fncall(hawk, name, HAWK_NULL, xloc, 0, TOK_RPAREN); } else { /* function name appeared without (). used as a value without invocation */ #if defined(HAWK_ENABLE_FUN_AS_VALUE) nde = parse_fun_as_value(hawk, name, xloc, funptr); #else hawk_seterrfmt(hawk, xloc, HAWK_EFUNRED, HAWK_T("function '%.*js' redefined"), name->len, name->ptr); #endif } } /*else if (hawk->opt.trait & HAWK_IMPLICIT) */ else if (hawk->parse.pragma.trait & HAWK_IMPLICIT) { /* if the name is followed by ( without spaces, * it's considered a function call though the name * has not been seen/resolved. * * it is a function call so long as it's followed * by a left parenthesis if concatenation by blanks * (HAWK_BLANKCONCAT) is not allowed. */ int is_fncall_var = 0; if (MATCH(hawk,TOK_LPAREN) && (!(hawk->opt.trait & HAWK_BLANKCONCAT) || (hawk->tok.loc.line == xloc->line && hawk->tok.loc.colm == xloc->colm + name->len))) { /* it is a function call to an undefined function yet */ if (hawk_htb_search(hawk->parse.named, name->ptr, name->len) != HAWK_NULL) { /* the function call conflicts with a named variable */ #if defined(HAWK_ENABLE_FUN_AS_VALUE) is_fncall_var = 1; goto named_var; #else hawk_seterrfmt(hawk, xloc, HAWK_EVARRED, HAWK_T("variable '%.*js' redefined"), name->len, name->ptr); #endif } else { nde = parse_fncall(hawk, name, HAWK_NULL, xloc, 0, TOK_RPAREN); } } else { hawk_nde_var_t* tmp; #if defined(HAWK_ENABLE_FUN_AS_VALUE) named_var: #endif /* if there is a space between the name and the left parenthesis * while the name is not resolved to anything, we treat the space * as concatention by blanks. so we handle the name as a named * variable. */ tmp = (hawk_nde_var_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*tmp)); if (HAWK_UNLIKELY(!tmp)) ADJERR_LOC(hawk, xloc); else { hawk_oow_t named_idxa; /* collect unique instances of a named variable * for reference */ named_idxa = get_or_add_named(hawk, name); if (named_idxa == (hawk_oow_t)-1) { ADJERR_LOC(hawk, xloc); hawk_freemem(hawk, tmp); } else { tmp->type = HAWK_NDE_NAMED; tmp->loc = *xloc; tmp->id.name.ptr = name->ptr; tmp->id.name.len = name->len; tmp->id.idxa = named_idxa; tmp->idx = HAWK_NULL; nde = (hawk_nde_t*)tmp; #if defined(HAWK_ENABLE_FUN_AS_VALUE) if (is_fncall_var) nde = parse_fncall(hawk, (const hawk_oocs_t*)nde, HAWK_NULL, xloc, FNCALL_FLAG_EXPR, TOK_RPAREN); #endif } } } } else { if (MATCH(hawk,TOK_LPAREN)) { /* it is a function call as the name is followed * by ( with/without spaces and implicit variables are disabled. */ nde = parse_fncall(hawk, name, HAWK_NULL, xloc, 0, TOK_RPAREN); } else { /* undefined variable */ hawk_seterrfmt(hawk, xloc, HAWK_EUNDEF, FMT_EUNDEF, name->len, name->ptr); } } } #if defined(HAWK_ENABLE_FUN_AS_VALUE) /* * Traditionally, a(10)(20) is concatenation of the return value of 'a(10)' and '20'. * If the 'xcall' pragma is on, the parser changes the behavior to treated it as * chained function call. although the parser doesn't allow the general chains of * array/map index notation and function call notation (e.g. a()[20](1)[1][1](20, 30)), * this part is mainly to support at least one-level function call to a map element * in the dot notation(e.g. a.b.c(20, 30) ) */ if ((hawk->parse.pragma.trait & HAWK_XCALL) && nde) { hawk_nde_t* xcall; xcall = parse_xcall_postfix_on_expr(hawk, nde, xloc); if (!xcall) hawk_clrpt(hawk, nde); /* OWNERSHIP COMPLEXITY - SEE comments below */ return xcall; } #endif if (!nde) { /* the caller of this function(parse_primary_ident), originally, freed the name * if this function returned failure. after adding parse_call_postfix_on_expr() * there arised a ownership issue because the intermediate nde is already complete * before parse_xcall_postfix_on_expr() fails. so if the xcall post fix is not * involved, this function frees the name pointer. when xcall is involved, * another return path above marked OWNERSHIP COMPLEXITY frees the entire * intermediate parse tree pointed to by nde and returns there, not coming here. */ hawk_freemem(hawk, name->ptr); } return nde; } static hawk_nde_t* make_deferred_modsym_node (hawk_t* hawk, const hawk_loc_t* xloc, const hawk_oocs_t* name) { hawk_nde_modsym_t* nde; nde = (hawk_nde_modsym_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_MODSYM; nde->loc = *xloc; nde->name.ptr = name->ptr; nde->name.len = name->len; nde->cache_type = -1; /* not cached */ return (hawk_nde_t*)nde; } static hawk_nde_t* parse_primary_ident_segs (hawk_t* hawk, const hawk_loc_t* xloc, const hawk_oocs_t* full, const hawk_oocs_t segs[], int nsegs) { /* parse xxx::yyy */ /* the caller must ensure that full->ptr points to an allocated(or duplicated) * heap memory block. this function passes the pointer as is to the fnc node. * when the fnc node is freed, the data block pointed to by it is freed. * the caller must free it if this function returns failure or the returned * node type is neither fnc nor deferred modsym as the ownership doesn't get tranferred. */ hawk_nde_t* nde = HAWK_NULL; hawk_mod_t* mod; hawk_mod_sym_t sym; hawk_fnc_t fnc; /*#define DEBUG_DEFER_MODSYM*/ #if defined(DEBUG_DEFER_MODSYM) /* uncondition defer for easier debugging/testing */ if (hawk->parse.pragma.trait & HAWK_DEFER_MODSYM) goto defer; #endif mod = query_module(hawk, segs, nsegs, &sym, HAWK_NULL); if (!mod) { if (hawk->parse.pragma.trait & HAWK_DEFER_MODSYM) { #if defined(DEBUG_DEFER_MODSYM) defer: #endif if (MATCH(hawk, TOK_LPAREN)) { HAWK_MEMSET(&fnc, 0, HAWK_SIZEOF(fnc)); fnc.name.ptr = full->ptr; fnc.name.len = full->len; return parse_fncall(hawk, full, &fnc, xloc, FNCALL_FLAG_DEFER_MODFNC, TOK_RPAREN); } /* since query_module() is executed first, in most cases, the deferred symbol * must end up unresolved at run-time if ever evaluated. but this is to not break * programs referencing unresolved symbols at compile time */ return make_deferred_modsym_node(hawk, xloc, full); } ADJERR_LOC(hawk, xloc); } else { switch (sym.type) { case HAWK_MOD_FNC: if ((hawk->opt.trait & sym.u.fnc_.trait) != sym.u.fnc_.trait) { hawk_seterrfmt(hawk, xloc, HAWK_EUNDEF, FMT_EUNDEF, full->len, full->ptr); break; } if (MATCH(hawk,TOK_LPAREN)) { HAWK_MEMSET(&fnc, 0, HAWK_SIZEOF(fnc)); fnc.name.ptr = full->ptr; fnc.name.len = full->len; fnc.spec = sym.u.fnc_; fnc.mod = mod; nde = parse_fncall(hawk, full, &fnc, xloc, 0, TOK_RPAREN); } else { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELPAREN, FMT_ELPAREN, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); } break; case HAWK_MOD_INT: if ((hawk->opt.trait & sym.u.int_.trait) != sym.u.int_.trait) { hawk_seterrfmt(hawk, xloc, HAWK_EUNDEF, FMT_EUNDEF, full->len, full->ptr); break; } nde = new_int_node(hawk, sym.u.int_.val, xloc); /* i don't remember the symbol in the original form */ break; case HAWK_MOD_FLT: if ((hawk->opt.trait & sym.u.flt_.trait) != sym.u.flt_.trait) { hawk_seterrfmt(hawk, xloc, HAWK_EUNDEF, FMT_EUNDEF, full->len, full->ptr); break; } nde = new_flt_node(hawk, sym.u.flt_.val, xloc); /* i don't remember the symbol in the original form */ break; default: /* TODO: support MOD_VAR */ hawk_seterrfmt(hawk, xloc, HAWK_EUNDEF, FMT_EUNDEF, full->len, full->ptr); break; } } /* LIMITATION * xcall doesn't apply here for now. * not only here, it doesn't apply after @[] and @{}, also. * TODO: general xcall.. function call or array/map indexing after any expressions/statements */ return nde; } static hawk_nde_t* parse_primary_ident (hawk_t* hawk, const hawk_loc_t* xloc) { hawk_nde_t* nde = HAWK_NULL; hawk_oocs_t name[2]; /* TODO: support more than 2 segments??? */ int nsegs; HAWK_ASSERT(MATCH(hawk,TOK_IDENT)); nsegs = dup_ident_and_get_next(hawk, xloc, name, HAWK_COUNTOF(name)); if (nsegs <= -1) return HAWK_NULL; if (nsegs <= 1) { nde = parse_primary_ident_noseg(hawk, xloc, &name[0]); /* * it must free the name upon failure as shown below. but it * is freed inside parse_primary_ident_noseg() for ownership issues. * parse_primary_ident() and parase_primary_ident_noseg() are * tightly coupled in this regards. if (HAWK_UNLIKELY(!nde)) hawk_freemem(hawk, name[0].ptr); */ } else { hawk_oocs_t full; /* full name including :: */ hawk_oow_t capa; int i; for (capa = 0, i = 0; i < nsegs; i++) capa += name[i].len + 2; /* +2 for :: */ full.ptr = hawk_allocmem(hawk, HAWK_SIZEOF(*full.ptr) * (capa + 1)); if (HAWK_LIKELY(full.ptr)) { capa = hawk_copy_oochars_to_oocstr_unlimited(&full.ptr[0], name[0].ptr, name[0].len); for (i = 1; i < nsegs; i++) { capa += hawk_copy_oocstr_unlimited(&full.ptr[capa], HAWK_T("::")); capa += hawk_copy_oochars_to_oocstr_unlimited(&full.ptr[capa], name[i].ptr, name[i].len); } full.ptr[capa] = HAWK_T('\0'); full.len = capa; nde = parse_primary_ident_segs(hawk, xloc, &full, name, nsegs); if (!nde || (nde->type != HAWK_NDE_FNCALL_FNC && nde->type != HAWK_NDE_MODSYM)) { /* the FNC and deferred MODSYM nodes take the full name but other * nodes don't. so i need to free it. i know it's ugly. */ hawk_freemem(hawk, full.ptr); } } else { /* error number is set in hawk_allocmem */ ADJERR_LOC(hawk, xloc); } /* i don't need the name segments */ while (nsegs > 0) hawk_freemem(hawk, name[--nsegs].ptr); } return nde; } static hawk_nde_t* make_str_node_from_ident (hawk_t* hawk, const hawk_ooecs_t* name, const hawk_loc_t* xloc) { hawk_nde_str_t* nde; nde = (hawk_nde_str_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } nde->type = HAWK_NDE_STR; nde->loc = *xloc; nde->len = HAWK_OOECS_LEN(name); nde->ptr = hawk_dupoocs(hawk, HAWK_OOECS_OOCS(name)); if (HAWK_UNLIKELY(!nde->ptr)) { hawk_freemem(hawk, nde); return HAWK_NULL; } return (hawk_nde_t*)nde; } static int parse_single_idx (hawk_t* hawk, nde_chain_t* idx) { nde_chain_t newidx = { HAWK_NULL, HAWK_NULL }; if (MATCH(hawk, TOK_LBRACK)) { hawk_nde_t* tmp; hawk_loc_t eloc; /* [] may contain a grouped index separated by a comma. * for example, a[1,3,"kk"]. looping is required */ do { if (get_token(hawk) <= -1) goto oops; eloc = hawk->tok.loc; tmp = parse_expr_withdc(hawk, &eloc); if (HAWK_UNLIKELY(!tmp)) goto oops; if (!newidx.head) newidx.head = tmp; else newidx.tail->next = tmp; newidx.tail = tmp; } while (MATCH(hawk, TOK_COMMA)); if (!MATCH(hawk, TOK_RBRACK)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ERBRACK, FMT_ERBRACK, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } else { hawk_nde_t* tmp; hawk_loc_t eloc; HAWK_ASSERT(MATCH(hawk, TOK_PERIOD)); if (get_token(hawk) <= -1) goto oops; eloc = hawk->tok.loc; if (!MATCH(hawk, TOK_IDENT)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EIDENT, FMT_EIDENT, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } tmp = make_str_node_from_ident(hawk, hawk->tok.name, &eloc); if (HAWK_UNLIKELY(!tmp)) goto oops; if (!newidx.head) newidx.head = tmp; else newidx.tail->next = tmp; newidx.tail = tmp; } if (get_token(hawk) <= -1) goto oops; /* append it the outer index chain */ if (!idx->head) idx->head = newidx.head; else idx->tail->next = newidx.head; idx->tail = newidx.tail; return 0; oops: if (newidx.head) hawk_clrpt(hawk, newidx.head); return -1; } static hawk_nde_t* make_hashidx_access_node (hawk_t* hawk, const hawk_oocs_t* name, const hawk_loc_t* xloc, hawk_nde_t* idx) { hawk_nde_var_t* nde; hawk_oow_t idxa; nde = (hawk_nde_var_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*nde)); if (HAWK_UNLIKELY(!nde)) { ADJERR_LOC(hawk, xloc); return HAWK_NULL; } /* search the local variable list */ idxa = hawk_arr_rsearch(hawk->parse.lcls, hawk->parse.lcl_base, HAWK_ARR_SIZE(hawk->parse.lcls), name->ptr, name->len); if (idxa != HAWK_ARR_NIL) { nde->type = HAWK_NDE_LCLIDX; nde->loc = *xloc; /*nde->id.name = HAWK_NULL; */ nde->id.name.ptr = name->ptr; nde->id.name.len = name->len; nde->id.idxa = idxa - hawk->parse.lcl_base; nde->idx = idx; { const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; ptl = HAWK_ARR_DPTL(hawk->parse.lcls, idxa); vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); nde->is_const = vxi->is_const; if (hawk->parse.pragma.trait & HAWK_PEDANTIC) { vxi->used = 1; if (xloc) vxi->loc = *xloc; } } return (hawk_nde_t*)nde; } /* search the parameter name list */ idxa = hawk_arr_search(hawk->parse.params, hawk->parse.param_base, HAWK_ARR_SIZE(hawk->parse.params), name->ptr, name->len); if (idxa != HAWK_ARR_NIL) { nde->type = HAWK_NDE_ARGIDX; nde->loc = *xloc; /*nde->id.name = HAWK_NULL; */ nde->id.name.ptr = name->ptr; nde->id.name.len = name->len; nde->id.idxa = idxa - hawk->parse.param_base; nde->idx = idx; return (hawk_nde_t*)nde; } /* gets the global variable index */ idxa = get_global(hawk, name); if (idxa != HAWK_ARR_NIL) { nde->type = HAWK_NDE_GBLIDX; nde->loc = *xloc; /*nde->id.name = HAWK_NULL;*/ nde->id.name.ptr = name->ptr; nde->id.name.len = name->len; nde->id.idxa = idxa; nde->idx = idx; { const hawk_ptl_t* ptl; hawk_var_xinfo_t* vxi; ptl = HAWK_ARR_DPTL(hawk->parse.gbls, idxa); vxi = (hawk_var_xinfo_t*)((hawk_ooch_t*)ptl->ptr + ptl->len); nde->is_const = vxi->is_const; if (hawk->parse.pragma.trait & HAWK_PEDANTIC) { vxi->used = 1; if (xloc) vxi->loc = *xloc; } } return (hawk_nde_t*)nde; } /*if (hawk->opt.trait & HAWK_IMPLICIT) */ if (hawk->parse.pragma.trait & HAWK_IMPLICIT) { int fnname = isfnname(hawk, name); switch (fnname) { case FNTYPE_FNC: hawk_seterrfmt(hawk, xloc, HAWK_EFNCRED, HAWK_T("intrinsic function '%.*js' redefined"), name->len, name->ptr); goto exit_func; case FNTYPE_FUN: hawk_seterrfmt(hawk, xloc, HAWK_EFUNRED, HAWK_T("function '%.*js' redefined"), name->len, name->ptr); goto exit_func; } HAWK_ASSERT(fnname == 0); nde->type = HAWK_NDE_NAMEDIDX; nde->loc = *xloc; nde->id.name.ptr = name->ptr; nde->id.name.len = name->len; nde->id.idxa = get_or_add_named(hawk, name); if (nde->id.idxa == (hawk_oow_t)-1) { ADJERR_LOC(hawk, xloc); goto exit_func; } nde->idx = idx; return (hawk_nde_t*)nde; } /* -- ERROR down here -- */ /* undefined variable */ hawk_seterrfmt(hawk, xloc, HAWK_EUNDEF, FMT_EUNDEF, name->len, name->ptr); exit_func: /* didn't take over 'name' and 'idx'. just free the node structure * using hawk_freemem(). no call to hawk_clrpt() */ hawk_freemem(hawk, nde); return HAWK_NULL; } static hawk_nde_t* parse_idx_chain (hawk_t* hawk, const hawk_loc_t* xloc) { nde_chain_t idx = { HAWK_NULL, HAWK_NULL }; if (parse_single_idx(hawk, &idx) <= -1) goto oops; HAWK_ASSERT(idx.head != HAWK_NULL); #if defined(HAWK_ENABLE_GC) while (MATCH(hawk, TOK_LBRACK) || MATCH(hawk, TOK_PERIOD)) { hawk_nde_t* splitter; /* additional index - a[10][20] or a.b.c ... * use the NULL node as a splitter */ splitter = make_null_nde(hawk, xloc); if (HAWK_UNLIKELY(!splitter)) goto oops; HAWK_ASSERT(idx.tail != HAWK_NULL); idx.tail->next = splitter; idx.tail = splitter; /* splitter is already chained. no need seperate clearing upon failure */ if (parse_single_idx(hawk, &idx) <= -1) goto oops; } #endif return idx.head; oops: if (idx.head) hawk_clrpt(hawk, idx.head); return HAWK_NULL; } #if defined(HAWK_ENABLE_FUN_AS_VALUE) static hawk_nde_t* parse_xcall_postfix_on_expr (hawk_t* hawk, hawk_nde_t* base, const hawk_loc_t* xloc) { if (is_nospace_lparen(hawk) && (hawk->ptok.type == TOK_IDENT || hawk->ptok.type == TOK_RBRACK)) { /* it isn't nice for a parser to check if the previous token is an identifier or the * right bracket. but due to some weird concatenation by blank, it performs parsing * in this dirty way, rather than support general expression before () or []. * * the parser supports one-level function call notation after an array/map element * * - (a.b.c)(1) is concatenation of a.b.c and 1 * - a.b.c(1) is a valid xcall * - a["b"]["c"](1) is a valid xcall also * - a.b.c(1)(2) is concatenation of a.b.c(1) and 2 * - a(20)(10) is concatenation of a(20) and 10 */ hawk_loc_t cloc; hawk_nde_t* call; cloc = base->loc; call = parse_fncall(hawk, (const hawk_oocs_t*)base, HAWK_NULL, &cloc, FNCALL_FLAG_EXPR, TOK_RPAREN); if (!call) return HAWK_NULL; base = call; } return base; } #endif static hawk_nde_t* parse_hashidx_common (hawk_t* hawk, const hawk_oocs_t* name, const hawk_loc_t* xloc) { hawk_nde_t* idx; hawk_nde_t* h; idx = parse_idx_chain(hawk, xloc); if (HAWK_UNLIKELY(!idx)) return HAWK_NULL; h = make_hashidx_access_node(hawk, name, xloc, idx); if (HAWK_UNLIKELY(!h)) hawk_clrpt(hawk, idx); return h; } static hawk_nde_t* parse_hashidx (hawk_t* hawk, const hawk_oocs_t* name, const hawk_loc_t* xloc) { HAWK_ASSERT(MATCH(hawk, TOK_LBRACK)); return parse_hashidx_common(hawk, name, xloc); } static hawk_nde_t* parse_dotidx (hawk_t* hawk, const hawk_oocs_t* name, const hawk_loc_t* xloc) { HAWK_ASSERT(MATCH(hawk, TOK_PERIOD)); return parse_hashidx_common(hawk, name, xloc); } static hawk_nde_t* parse_fncall (hawk_t* hawk, const hawk_oocs_t* name, hawk_fnc_t* fnc, const hawk_loc_t* xloc, int flags, int closer_token) { hawk_nde_t* head, * curr, * nde; hawk_nde_fncall_t* call; hawk_oow_t nargs; hawk_loc_t eloc; head = curr = HAWK_NULL; call = HAWK_NULL; nargs = 0; if (flags & FNCALL_FLAG_NOARG) goto make_node; if (get_token(hawk) <= -1) goto oops; if (MATCH(hawk, closer_token)) { /* no parameters to the function call */ if (get_token(hawk) <= -1) goto oops; } else { /* parse function parameters */ while (1) { eloc = hawk->tok.loc; nde = parse_expr_withdc(hawk, &eloc); if (!nde) goto oops; if (!head) head = nde; else curr->next = nde; curr = nde; nargs++; if (MATCH(hawk, closer_token)) { if ((flags & FNCALL_FLAG_MAP) && (nargs & 1)) { /* a value part is missing after colon inside @{} */ goto colon_expected; } if (get_token(hawk) <= -1) goto oops; break; } if ((flags & FNCALL_FLAG_MAP) && (nargs & 1)) { /* inside @{}, * - it expects a colon after each key * - after value, a comma is expected */ if (!MATCH(hawk, TOK_COLON)) { colon_expected: hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ECOLON, FMT_ECOLON, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } else { if (!MATCH(hawk, TOK_COMMA)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ECOMMA, FMT_ECOMMA, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); goto oops; } } do { if (get_token(hawk) <= -1) goto oops; } while (MATCH(hawk,TOK_NEWLINE)); } } make_node: call = (hawk_nde_fncall_t*)hawk_callocmem(hawk, HAWK_SIZEOF(*call)); if (HAWK_UNLIKELY(!call)) { ADJERR_LOC(hawk, xloc); goto oops; } if (flags & FNCALL_FLAG_EXPR) { /* special case. "name" is not of the const hawk_oocs_t* type. * it points to the node directly */ call->type = HAWK_NDE_FNCALL_EXPR; call->loc = *xloc; call->u.expr.callable = (hawk_nde_t*)name; /* name is a pointer to a callable expression node */ call->args = head; call->nargs = nargs; } else if (fnc) { call->type = HAWK_NDE_FNCALL_FNC; call->loc = *xloc; call->u.fnc.info.name.ptr = name->ptr; call->u.fnc.info.name.len = name->len; call->u.fnc.info.mod = fnc->mod; call->u.fnc.spec = fnc->spec; call->u.fnc.flags = 0; call->args = head; call->nargs = nargs; if (flags & FNCALL_FLAG_DEFER_MODFNC) { call->u.fnc.flags |= HAWK_NDE_FNCALL_FNC_DEFERRED_MODFNC; /* when deferred, the argument count check isn't possible. * we join the argument count check blocks with "else if" below. */ } else if (nargs > call->u.fnc.spec.arg.max) { hawk_seterrbfmt(hawk, xloc, HAWK_EARGTM, "too many arguments to %.*js", name->len, name->ptr); goto oops; } else if (nargs < call->u.fnc.spec.arg.min) { hawk_seterrbfmt(hawk, xloc, HAWK_EARGTF, "too few arguments to %.*js", name->len, name->ptr); goto oops; } } else { call->type = HAWK_NDE_FNCALL_FUN; call->loc = *xloc; call->u.fun.name.ptr = name->ptr; call->u.fun.name.len = name->len; call->args = head; call->nargs = nargs; /* store a non-builtin function call into the hawk->parse.funs table */ if (!hawk_htb_upsert(hawk->parse.funs, name->ptr, name->len, call, 0)) { ADJERR_LOC(hawk, xloc); goto oops; } } return (hawk_nde_t*)call; oops: if (call) hawk_freemem(hawk, call); /* destroy the call node itself */ if (head) hawk_clrpt(hawk, head); /* clear the argument list separately from the call node */ return HAWK_NULL; } static int get_number (hawk_t* hawk, hawk_tok_t* tok) { hawk_ooci_t c; HAWK_ASSERT(HAWK_OOECS_LEN(tok->name) == 0); SET_TOKEN_TYPE(hawk, tok, TOK_INT); c = hawk->sio.last.c; if (c == '0') { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); if (c == 'x' || c == 'X') { /* hexadecimal number */ do { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } while (hawk_is_ooch_xdigit(c)); return 0; } else if (c == 'o' || c == 'O') { /* octal number */ ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); goto octal; } else if (c == 'b' || c == 'B') { /* binary number */ do { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } while (c == '0' || c == '1'); return 0; } else if (c != '.') { /* octal number */ octal: while (c >= '0' && c <= '7') { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } if (c == '8' || c == '9') { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_ELXCHR, HAWK_T("invalid digit '%jc'"), (hawk_ooch_t)c); return -1; } return 0; } } while (hawk_is_ooch_digit(c)) { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } if (c == '.') { /* floating-point number */ SET_TOKEN_TYPE(hawk, tok, TOK_FLT); ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); while (hawk_is_ooch_digit(c)) { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } } if (c == 'E' || c == 'e') { SET_TOKEN_TYPE(hawk, tok, TOK_FLT); ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); if (c == '+' || c == '-') { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } while (hawk_is_ooch_digit(c)) { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } } return 0; } /* i think allowing only up to 2 hexadigits is more useful though it * may break compatibilty with other hawk implementations. If you want * more than 2, define HEX_DIGIT_LIMIT_FOR_X to HAWK_TYPE_MAX(hawk_oow_t). */ /*#define HEX_DIGIT_LIMIT_FOR_X (HAWK_TYPE_MAX(hawk_oow_t))*/ #define HEX_DIGIT_LIMIT_FOR_X (2) static int get_string ( hawk_t* hawk, hawk_ooch_t end_char, hawk_ooch_t esc_char, int keep_esc_char, int byte_only, hawk_oow_t preescaped, hawk_tok_t* tok) { hawk_ooci_t c; hawk_oow_t escaped = preescaped; hawk_oow_t digit_count = 0; hawk_uint32_t c_acc = 0; while (1) { GET_CHAR_TO(hawk, c); if (c == HAWK_OOCI_EOF) { hawk_seterrnum(hawk, &hawk->tok.loc, HAWK_ESTRNC); return -1; } #if defined(HAWK_OOCH_IS_BCH) /* nothing extra to handle byte_only */ #else if (byte_only && c != '\\' && !HAWK_BYTE_PRINTABLE(c)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EMBSCHR, HAWK_T("invalid byte character '%jc'"), (hawk_ooch_t)c); return -1; } #endif if (escaped == 3) { if (c >= HAWK_T('0') && c <= HAWK_T('7')) { c_acc = c_acc * 8 + c - HAWK_T('0'); digit_count++; if (digit_count >= escaped) { /* should i limit the max to 0xFF/0377? if (c_acc > 0377) c_acc = 0377; */ ADD_TOKEN_UINT32(hawk, tok, c_acc); escaped = 0; } continue; } else { if (digit_count == 1 && end_char == HAWK_T('/')) { /* inside a regular expression, it's likely a backreference */ hawk_ooch_t oc = c_acc + HAWK_T('0'); ADD_TOKEN_CHAR(hawk, tok, esc_char); ADD_TOKEN_CHAR(hawk, tok, oc); } else { ADD_TOKEN_UINT32(hawk, tok, c_acc); } escaped = 0; } } else if (escaped == HEX_DIGIT_LIMIT_FOR_X || escaped == 4 || escaped == 8) { if (c >= HAWK_T('0') && c <= HAWK_T('9')) { c_acc = c_acc * 16 + c - HAWK_T('0'); digit_count++; if (digit_count >= escaped) { ADD_TOKEN_UINT32(hawk, tok, c_acc); escaped = 0; } continue; } else if (c >= HAWK_T('A') && c <= HAWK_T('F')) { c_acc = c_acc * 16 + c - HAWK_T('A') + 10; digit_count++; if (digit_count >= escaped) { ADD_TOKEN_UINT32(hawk, tok, c_acc); escaped = 0; } continue; } else if (c >= HAWK_T('a') && c <= HAWK_T('f')) { c_acc = c_acc * 16 + c - HAWK_T('a') + 10; digit_count++; if (digit_count >= escaped) { ADD_TOKEN_UINT32(hawk, tok, c_acc); escaped = 0; } continue; } else { if (digit_count == 0) { hawk_ooch_t ec; ec = (escaped == HEX_DIGIT_LIMIT_FOR_X)? HAWK_T('x'): (escaped == 4)? HAWK_T('u'): HAWK_T('U'); /* no valid character after the escaper. * keep the escaper as it is. consider this input: * \xGG * 'c' is at the first G. this part is to restore the * \x part. since \x is not followed by any hexadecimal * digits, it's literally 'x' */ ADD_TOKEN_CHAR(hawk, tok, ec); } else ADD_TOKEN_UINT32(hawk, tok, c_acc); escaped = 0; /* carry on to handle the current character */ } } else if (escaped == 99) { escaped = 0; if (c == '\n') continue; /* backslash \r \n */ } /* -------------------------------------- */ if (escaped == 0) { if (c == end_char) { /* terminating quote */ /*GET_CHAR_TO(hawk, c);*/ GET_CHAR(hawk); break; } else if (c == esc_char) { escaped = 1; continue; } else if (!(hawk->parse.pragma.trait & HAWK_MULTILINESTR) && (c == '\n' || c == '\r')) { hawk_seterrnum(hawk, &hawk->tok.loc, HAWK_ESTRNC); return -1; } } else if (escaped == 1) { if (c == '\n') { /* line continuation - a backslash at the end of line */ escaped = 0; continue; } else if (c == '\r') { escaped = 99; continue; } if (c == HAWK_T('n')) c = HAWK_T('\n'); else if (c == HAWK_T('r')) c = HAWK_T('\r'); else if (c == HAWK_T('t')) c = HAWK_T('\t'); else if (c == HAWK_T('f')) c = HAWK_T('\f'); else if (c == HAWK_T('b')) c = HAWK_T('\b'); else if (c == HAWK_T('v')) c = HAWK_T('\v'); else if (c == HAWK_T('a')) c = HAWK_T('\a'); else if (c >= HAWK_T('0') && c <= HAWK_T('7')) { /* treat it as an octal notation first and * check if it's a backreference between \1 and \7 inclusive * in the `if (escaped == 3)` block. */ escaped = 3; digit_count = 1; c_acc = c - HAWK_T('0'); continue; } else if (c == HAWK_T('x')) { escaped = HEX_DIGIT_LIMIT_FOR_X; digit_count = 0; c_acc = 0; continue; } else if (!byte_only && c == HAWK_T('u')) { /* in the MCHAR mode, the \u letter will get converted to UTF-8 sequences. * see ADD_TOKEN_UINT32(). */ escaped = 4; digit_count = 0; c_acc = 0; continue; } else if (!byte_only && c == HAWK_T('U')) { /* in the MCHAR mode, the \u letter will get converted to UTF-8 sequences * see ADD_TOKEN_UINT32(). */ escaped = 8; digit_count = 0; c_acc = 0; continue; } else if (keep_esc_char) { /* if the following character doesn't compose a proper * escape sequence, keep the escape character. * an unhandled escape sequence can be handled * outside this function since the escape character * is preserved.*/ ADD_TOKEN_CHAR(hawk, tok, esc_char); } escaped = 0; } ADD_TOKEN_CHAR(hawk, tok, c); } return 0; } static int get_rexstr (hawk_t* hawk, hawk_tok_t* tok) { if (hawk->sio.last.c == HAWK_T('/')) { /* handle an empty regular expression. * * this condition is met when the input is //. * the first / has been tokenized to TOK_DIV already. * if TOK_DIV is seen as a primary, this function is called. * as the token buffer has been cleared by the caller and * the token type is set to TOK_REX, this function can * just return after reading the next character. * see parse_primary_rex(). */ GET_CHAR(hawk); return 0; } else { hawk_oow_t preescaped = 0; if (hawk->sio.last.c == HAWK_T('\\')) { /* for input like /\//, this condition is met. * the initial escape character is added when the * second charater is handled in get_string() */ preescaped = 1; } else { /* add other initial characters here as get_string() * begins with reading the next character */ ADD_TOKEN_CHAR(hawk, tok, hawk->sio.last.c); } return get_string(hawk, HAWK_T('/'), HAWK_T('\\'), 1, 0, preescaped, tok); } } static int get_raw_string (hawk_t* hawk, hawk_ooch_t end_char, int byte_only, hawk_tok_t* tok) { hawk_ooci_t c; while (1) { GET_CHAR_TO(hawk, c); if (c == HAWK_OOCI_EOF) { hawk_seterrnum(hawk, &hawk->tok.loc, HAWK_ESTRNC); return -1; } #if defined(HAWK_OOCH_IS_BCH) /* nothing extra to handle byte_only */ #else if (byte_only && c != '\\' && !HAWK_BYTE_PRINTABLE(c)) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EMBSCHR, HAWK_T("invalid byte character '%jc'"), (hawk_ooch_t)c); return -1; } #endif if (c == end_char) { /* terminating quote */ GET_CHAR(hawk); break; } ADD_TOKEN_CHAR(hawk, tok, c); } return 0; } static int skip_spaces (hawk_t* hawk) { hawk_ooci_t c = hawk->sio.last.c; if (hawk->opt.trait & HAWK_NEWLINE) { do { while (c != HAWK_T('\n') && hawk_is_ooch_space(c)) GET_CHAR_TO(hawk, c); if (c == HAWK_T('\\')) { hawk_sio_lxc_t bs; hawk_sio_lxc_t cr; int hascr = 0; bs = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == HAWK_T('\r')) { hascr = 1; cr = hawk->sio.last; GET_CHAR_TO(hawk, c); } if (c == HAWK_T('\n')) { GET_CHAR_TO(hawk, c); continue; } else { /* push back the last character */ unget_char(hawk, &hawk->sio.last); /* push CR if any */ if (hascr) unget_char(hawk, &cr); /* restore the orginal backslash */ hawk->sio.last = bs; } } break; } while (1); } else { while (hawk_is_ooch_space(c)) GET_CHAR_TO(hawk, c); } return 0; } static int skip_comment (hawk_t* hawk) { hawk_ooci_t c = hawk->sio.last.c; hawk_sio_lxc_t lc; if (c == HAWK_T('#')) { /* skip up to \n */ do { GET_CHAR_TO(hawk, c); } while (c != HAWK_T('\n') && c != HAWK_OOCI_EOF); if (!(hawk->opt.trait & HAWK_NEWLINE)) GET_CHAR(hawk); return 1; /* comment by # */ } /* handle c-style comment */ if (c != HAWK_T('/')) return 0; /* not a comment */ /* save the last character */ lc = hawk->sio.last; /* read a new character */ GET_CHAR_TO(hawk, c); if (c == HAWK_T('*')) { do { GET_CHAR_TO(hawk, c); if (c == HAWK_OOCI_EOF) { hawk_loc_t loc; loc.line = hawk->sio.inp->line; loc.colm = hawk->sio.inp->colm; loc.file = hawk->sio.inp->path; hawk_seterrnum(hawk, &loc, HAWK_ECMTNC); return -1; } if (c == HAWK_T('*')) { GET_CHAR_TO(hawk, c); if (c == HAWK_OOCI_EOF) { hawk_loc_t loc; loc.line = hawk->sio.inp->line; loc.colm = hawk->sio.inp->colm; loc.file = hawk->sio.inp->path; hawk_seterrnum(hawk, &loc, HAWK_ECMTNC); return -1; } if (c == HAWK_T('/')) { /*GET_CHAR_TO(hawk, c);*/ GET_CHAR(hawk); break; } } } while (1); return 1; /* c-style comment */ } /* unget '*' */ unget_char(hawk, &hawk->sio.last); /* restore the previous state */ hawk->sio.last = lc; return 0; } static int get_symbols (hawk_t* hawk, hawk_ooci_t c, hawk_tok_t* tok) { struct ops_t { const hawk_ooch_t* str; hawk_oow_t len; int tid; int trait; }; static struct ops_t ops[] = { { HAWK_T("==="), 3, TOK_TEQ, 0 }, { HAWK_T("=="), 2, TOK_EQ, 0 }, { HAWK_T("="), 1, TOK_ASSN, 0 }, { HAWK_T("!=="), 3, TOK_TNE, 0 }, { HAWK_T("!="), 2, TOK_NE, 0 }, { HAWK_T("!~"), 2, TOK_NM, 0 }, { HAWK_T("!"), 1, TOK_LNOT, 0 }, { HAWK_T(">>="), 3, TOK_RS_ASSN, 0 }, { HAWK_T(">>"), 2, TOK_RS, 0 }, { HAWK_T(">="), 2, TOK_GE, 0 }, { HAWK_T(">"), 1, TOK_GT, 0 }, { HAWK_T("<<="), 3, TOK_LS_ASSN, 0 }, { HAWK_T("<<"), 2, TOK_LS, 0 }, { HAWK_T("<="), 2, TOK_LE, 0 }, { HAWK_T("<"), 1, TOK_LT, 0 }, { HAWK_T("|&"), 2, TOK_PIPE_BD, 0 }, { HAWK_T("||"), 2, TOK_LOR, 0 }, { HAWK_T("|="), 2, TOK_BOR_ASSN, 0 }, { HAWK_T("|"), 1, TOK_BOR, 0 }, { HAWK_T("&&"), 2, TOK_LAND, 0 }, { HAWK_T("&="), 2, TOK_BAND_ASSN, 0 }, { HAWK_T("&"), 1, TOK_BAND, 0 }, { HAWK_T("^^="), 3, TOK_BXOR_ASSN, 0 }, { HAWK_T("^^"), 2, TOK_BXOR, 0 }, { HAWK_T("^="), 2, TOK_EXP_ASSN, 0 }, { HAWK_T("^"), 1, TOK_EXP, 0 }, { HAWK_T("++"), 2, TOK_PLUSPLUS, 0 }, { HAWK_T("+="), 2, TOK_PLUS_ASSN, 0 }, { HAWK_T("+"), 1, TOK_PLUS, 0 }, { HAWK_T("--"), 2, TOK_MINUSMINUS, 0 }, { HAWK_T("-="), 2, TOK_MINUS_ASSN, 0 }, { HAWK_T("-"), 1, TOK_MINUS, 0 }, { HAWK_T("**="), 3, TOK_EXP_ASSN, 0 }, { HAWK_T("**"), 2, TOK_EXP, 0 }, { HAWK_T("*="), 2, TOK_MUL_ASSN, 0 }, { HAWK_T("*"), 1, TOK_MUL, 0 }, { HAWK_T("/="), 2, TOK_DIV_ASSN, 0 }, { HAWK_T("/"), 1, TOK_DIV, 0 }, { HAWK_T("\\="), 2, TOK_IDIV_ASSN, 0 }, { HAWK_T("\\"), 1, TOK_IDIV, 0 }, { HAWK_T("%%="), 3, TOK_CONCAT_ASSN, 0 }, { HAWK_T("%%"), 2, TOK_CONCAT, 0 }, { HAWK_T("%="), 2, TOK_MOD_ASSN, 0 }, { HAWK_T("%"), 1, TOK_MOD, 0 }, { HAWK_T("~"), 1, TOK_TILDE, 0 }, { HAWK_T("("), 1, TOK_LPAREN, 0 }, { HAWK_T(")"), 1, TOK_RPAREN, 0 }, { HAWK_T("{"), 1, TOK_LBRACE, 0 }, { HAWK_T("}"), 1, TOK_RBRACE, 0 }, { HAWK_T("["), 1, TOK_LBRACK, 0 }, { HAWK_T("]"), 1, TOK_RBRACK, 0 }, { HAWK_T("$"), 1, TOK_DOLLAR, 0 }, { HAWK_T(","), 1, TOK_COMMA, 0 }, { HAWK_T(";"), 1, TOK_SEMICOLON, 0 }, { HAWK_T("::"), 2, TOK_DBLCOLON, 0 }, { HAWK_T(":"), 1, TOK_COLON, 0 }, { HAWK_T("?"), 1, TOK_QUEST, 0 }, { HAWK_T("..."), 3, TOK_ELLIPSIS, 0 }, { HAWK_T(".."), 2, TOK_DBLPERIOD, 0 }, { HAWK_T("."), 1, TOK_PERIOD, 0 }, { HAWK_NULL, 0, 0, 0 } }; struct ops_t* p; int idx = 0; /* note that the loop below is not generaic enough. * you must keep the operators strings in a particular order */ for (p = ops; p->str != HAWK_NULL; ) { if (p->trait == 0 || (hawk->opt.trait & p->trait)) { if (p->str[idx] == HAWK_T('\0')) { ADD_TOKEN_STR(hawk, tok, p->str, p->len); SET_TOKEN_TYPE(hawk, tok, p->tid); return 1; } if (c == p->str[idx]) { idx++; GET_CHAR_TO(hawk, c); continue; } } p++; } return 0; } static int get_token_into (hawk_t* hawk, hawk_tok_t* tok) { hawk_ooci_t c; int n; int skip_semicolon_after_include = 0; retry: do { if (skip_spaces(hawk) <= -1) return -1; if ((n = skip_comment(hawk)) <= -1) return -1; } while (n >= 1); hawk_ooecs_clear(tok->name); tok->flags = 0; tok->loc.file = hawk->sio.last.file; tok->loc.line = hawk->sio.last.line; tok->loc.colm = hawk->sio.last.colm; c = hawk->sio.last.c; if (c == HAWK_OOCI_EOF) { n = end_include(hawk); if (n <= -1) return -1; if (n >= 1) { /*hawk->sio.last = hawk->sio.inp->last;*/ /* mark that i'm retrying after end of an included file */ skip_semicolon_after_include = 1; goto retry; } ADD_TOKEN_STR(hawk, tok, HAWK_T(""), 5); SET_TOKEN_TYPE(hawk, tok, TOK_EOF); } else if (c == HAWK_T('\n')) { /*ADD_TOKEN_CHAR(hawk, tok, HAWK_T('\n'));*/ ADD_TOKEN_STR(hawk, tok, HAWK_T(""), 4); SET_TOKEN_TYPE(hawk, tok, TOK_NEWLINE); GET_CHAR(hawk); } else if (hawk_is_ooch_digit(c)/*|| c == HAWK_T('.')*/) { if (get_number(hawk, tok) <= -1) return -1; } else if (c == HAWK_T('.')) { hawk_sio_lxc_t lc; lc = hawk->sio.last; GET_CHAR_TO(hawk, c); unget_char(hawk, &hawk->sio.last); hawk->sio.last = lc; if (hawk_is_ooch_digit(c)) { /* for a token such as .123 */ if (get_number(hawk, tok) <= -1) return -1; } else { c = HAWK_T('.'); goto try_get_symbols; } } else if (c == HAWK_T('@')) { int type; GET_CHAR_TO(hawk, c); if (c == '[' || c == '{') /* syntatic sugar for array/map composition */ { int tt; ADD_TOKEN_CHAR(hawk, tok, '@'); ADD_TOKEN_CHAR(hawk, tok, c); tt = (c == '['? TOK_ATBRACK: TOK_ATBRACE); SET_TOKEN_TYPE(hawk, tok, tt); GET_CHAR(hawk); } else if (c != '_' && !hawk_is_ooch_alpha(c)) { /* this extended keyword is empty, * not followed by a valid word */ hawk_seterrnum(hawk, &(hawk)->tok.loc, HAWK_EXKWEM); return -1; } else if (c == 'B' || c == 'b') { hawk_sio_lxc_t pc1 = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == 'C' || c == 'c') { hawk_sio_lxc_t pc2 = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == '\"' || c == '\'') { /* byte character - @BC"X", @BC"x", @BC'X', @bc'X' */ SET_TOKEN_TYPE(hawk, tok, TOK_BCHR); if (get_string(hawk, c, '\\', 0, 1, 0, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid byte-character token")); return -1; } } else { unget_char(hawk, &hawk->sio.last); unget_char(hawk, &pc2); hawk->sio.last = pc1; c = pc1.c; goto process_at_identifier; } } else if (c == '\"') { /* @B"XXX", @b"XXX" - byte string */ SET_TOKEN_TYPE(hawk, tok, TOK_MBS); if (get_string(hawk, c, HAWK_T('\\'), 0, 1, 0, tok) <= -1) return -1; } else if (c == '\'') { /* @B'X' @b'x' - byte character */ SET_TOKEN_TYPE(hawk, tok, TOK_BCHR); if (get_string(hawk, c, '\\', 0, 1, 0, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid byte-character token")); return -1; } } else { unget_char(hawk, &hawk->sio.last); hawk->sio.last = pc1; c = pc1.c; goto process_at_identifier; } } else if (c == 'C' || c == 'c') { hawk_sio_lxc_t pc1 = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == '\"' || c == '\'') { /* @C"X" @c"x" @C'X' @c'x' - character. alternative notation to 'X' */ SET_TOKEN_TYPE(hawk, tok, TOK_CHAR); if (get_string(hawk, c, '\\', 0, 0, 0, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid character token")); return -1; } } else { unget_char(hawk, &hawk->sio.last); hawk->sio.last = pc1; c = pc1.c; goto process_at_identifier; } } else if (c == 'R' || c == 'r') { hawk_sio_lxc_t pc1 = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == 'B' || c == 'b') { hawk_sio_lxc_t pc2 = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == 'C' || c == 'c') { hawk_sio_lxc_t pc3 = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == '\"' || c == '\'') { /* @rbc"X", @rbc"x" - raw byte character */ SET_TOKEN_TYPE(hawk, tok, TOK_BCHR); if (get_raw_string(hawk, c, 1, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid raw byte-character token")); return -1; } } else { unget_char(hawk, &hawk->sio.last); unget_char(hawk, &pc3); unget_char(hawk, &pc2); hawk->sio.last = pc1; c = pc1.c; goto process_at_identifier; } } else if (c == '\"') { /* @RB"X" - raw byte string */ SET_TOKEN_TYPE(hawk, tok, TOK_MBS); if (get_raw_string(hawk, c, 1, tok) <= -1) return -1; } else if (c == '\'') { /* @RB'X' - raw byte character */ SET_TOKEN_TYPE(hawk, tok, TOK_BCHR); if (get_raw_string(hawk, c, 1, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid raw byte-character token")); return -1; } } else { unget_char(hawk, &hawk->sio.last); unget_char(hawk, &pc2); hawk->sio.last = pc1; c = pc1.c; goto process_at_identifier; } } else if (c == 'C' || c == 'c') { hawk_sio_lxc_t pc2 = hawk->sio.last; GET_CHAR_TO(hawk, c); if (c == '\"' || c == '\'') { /* @RC"X", @RC"x" - raw character */ SET_TOKEN_TYPE(hawk, tok, TOK_CHAR); if (get_raw_string(hawk, c, 0, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid raw character token")); return -1; } } else { unget_char(hawk, &hawk->sio.last); unget_char(hawk, &pc2); hawk->sio.last = pc1; c = pc1.c; goto process_at_identifier; } } else if (c == '\"') { /* @R"X", @r"X" - raw string */ SET_TOKEN_TYPE(hawk, tok, TOK_STR); if (get_raw_string(hawk, c, 0, tok) <= -1) return -1; } else if (c == '\'') { /* @R"x", @R'x' - raw character */ SET_TOKEN_TYPE(hawk, tok, TOK_CHAR); if (get_raw_string(hawk, c, 0, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid raw character token")); return -1; } } else { unget_char(hawk, &hawk->sio.last); hawk->sio.last = pc1; c = pc1.c; goto process_at_identifier; } } else { process_at_identifier: ADD_TOKEN_CHAR(hawk, tok, HAWK_T('@')); /* expect normal identifier starting with an alphabet */ do { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } while (c == HAWK_T('_') || hawk_is_ooch_alpha(c) || hawk_is_ooch_digit(c)); if (hawk_comp_oochars_bcstr(HAWK_OOECS_PTR(tok->name), HAWK_OOECS_LEN(tok->name), "@SCRIPTNAME", 0) == 0) { /* special parser-level word @SCRIPTNAME. substitute an actual value for it */ if (HAWK_UNLIKELY(hawk_ooecs_cpy(tok->name, (tok->loc.file? (const hawk_ooch_t*)tok->loc.file: (const hawk_ooch_t*)HAWK_T(""))) == (hawk_oow_t)-1)) return -1; SET_TOKEN_TYPE(hawk, tok, TOK_STR); } else if (hawk_comp_oochars_bcstr(HAWK_OOECS_PTR(tok->name), HAWK_OOECS_LEN(tok->name), "@SCRIPTLINE", 0) == 0) { /* special parser-level word @SCRIPTLINE. subsitute an actual value for it */ if (HAWK_UNLIKELY(hawk_ooecs_fmt(tok->name, HAWK_T("%zu"), tok->loc.line) == (hawk_oow_t)-1)) return -1; SET_TOKEN_TYPE(hawk, tok, TOK_INT); } else if (hawk_comp_oochars_bcstr(HAWK_OOECS_PTR(tok->name), HAWK_OOECS_LEN(tok->name), "@UCH_ON", 0) == 0) { /* special parser-level word @SCRIPTLINE. subsitute an actual value for it */ #if defined(HAWK_OOCH_IS_BCH) if (HAWK_UNLIKELY(hawk_ooecs_fmt(tok->name, HAWK_T("%zu"), 0) == (hawk_oow_t)-1)) return -1; #else if (HAWK_UNLIKELY(hawk_ooecs_fmt(tok->name, HAWK_T("%zu"), 1) == (hawk_oow_t)-1)) return -1; #endif SET_TOKEN_TYPE(hawk, tok, TOK_INT); } else if (hawk_comp_oochars_bcstr(HAWK_OOECS_PTR(tok->name), HAWK_OOECS_LEN(tok->name), "@BCH_ON", 0) == 0) { /* special parser-level word @SCRIPTLINE. subsitute an actual value for it */ #if defined(HAWK_OOCH_IS_BCH) if (HAWK_UNLIKELY(hawk_ooecs_fmt(tok->name, HAWK_T("%zu"), 1) == (hawk_oow_t)-1)) return -1; #else if (HAWK_UNLIKELY(hawk_ooecs_fmt(tok->name, HAWK_T("%zu"), 0) == (hawk_oow_t)-1)) return -1; #endif SET_TOKEN_TYPE(hawk, tok, TOK_INT); } else { type = classify_ident(hawk, HAWK_OOECS_OOCS(tok->name)); if (type == TOK_IDENT) { hawk_seterrfmt(hawk, &hawk->tok.loc, HAWK_EXKWNR, FMT_EXKWNR, HAWK_OOECS_LEN(hawk->tok.name), HAWK_OOECS_PTR(hawk->tok.name)); return -1; } SET_TOKEN_TYPE(hawk, tok, type); } } } else if (c == '_' || hawk_is_ooch_alpha(c)) { int type; /* identifier */ do { ADD_TOKEN_CHAR(hawk, tok, c); GET_CHAR_TO(hawk, c); } while (c == '_' || hawk_is_ooch_alpha(c) || hawk_is_ooch_digit(c)); type = classify_ident(hawk, HAWK_OOECS_OOCS(tok->name)); SET_TOKEN_TYPE(hawk, tok, type); } else if (c == '\"') { /* double-quoted string */ SET_TOKEN_TYPE(hawk, tok, TOK_STR); if (get_string(hawk, c, '\\', 0, 0, 0, tok) <= -1) return -1; } else if (c == '\'') { SET_TOKEN_TYPE(hawk, tok, TOK_CHAR); if (get_string(hawk, c, '\\', 0, 0, 0, tok) <= -1) return -1; if (HAWK_OOECS_LEN(tok->name) != 1) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid character token")); return -1; } } else { try_get_symbols: n = get_symbols(hawk, c, tok); if (n <= -1) return -1; if (n == 0) { /* not handled yet */ if (c == HAWK_T('\0')) { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid character '\\0'")); } else { hawk_seterrfmt(hawk, &tok->loc, HAWK_ELXCHR, HAWK_T("invalid character '%jc'"), (hawk_ooch_t)c); } return -1; } if (skip_semicolon_after_include && (tok->type == TOK_SEMICOLON || tok->type == TOK_NEWLINE)) { /* this handles the optional semicolon after the * included file named as in @include "file-name"; */ skip_semicolon_after_include = 0; goto retry; } } if (skip_semicolon_after_include && !(hawk->opt.trait & HAWK_NEWLINE)) { /* semiclon has not been skipped yet and the newline option is not set. */ hawk_seterrfmt(hawk, &tok->loc, HAWK_ESCOLON, FMT_ESCOLON, HAWK_OOECS_LEN(tok->name), HAWK_OOECS_PTR(tok->name)); return -1; } return 0; } static int get_token (hawk_t* hawk) { hawk->ptok.type = hawk->tok.type; hawk->ptok.flags = hawk->tok.flags; hawk->ptok.loc.file = hawk->tok.loc.file; hawk->ptok.loc.line = hawk->tok.loc.line; hawk->ptok.loc.colm = hawk->tok.loc.colm; hawk_ooecs_swap(hawk->ptok.name, hawk->tok.name); if (HAWK_OOECS_LEN(hawk->ntok.name) > 0) { hawk->tok.type = hawk->ntok.type; hawk->tok.flags = hawk->ntok.flags; hawk->tok.loc.file = hawk->ntok.loc.file; hawk->tok.loc.line = hawk->ntok.loc.line; hawk->tok.loc.colm = hawk->ntok.loc.colm; hawk_ooecs_swap(hawk->tok.name, hawk->ntok.name); hawk_ooecs_clear(hawk->ntok.name); return 0; } return get_token_into(hawk, &hawk->tok); } static int preget_token (hawk_t* hawk) { /* LIMITATION: no more than one token can be pre-read in a row without consumption. */ if (HAWK_OOECS_LEN(hawk->ntok.name) > 0) { /* you can't read more than 1 token in advance. * * if there is a token already read in, it is just * retained. * * parsing an expression like '$0 | a' causes this * funtion to be called before get_token() consumes the * pre-read token. * * Because the expression like this * print $1 | getline x; * must be parsed as * print $(1 | getline x); * preget_token() is called from parse_primary(). * * For the expression '$0 | $2', * 1) parse_primary() calls parse_primary_positional() if $ is encountered. * 2) parse_primary_positional() calls parse_primary() recursively for the positional part after $. * 3) parse_primary() in #2 calls preget_token() * 4) parse_primary() in #1 also calls preget_token(). * * this block is reached because no token is consumed between #3 and #4. * * in short, it happens if getline doesn't doesn't follow | after the positional. * $1 | $2 * $1 | abc + 20 */ return 0; } else { /* if there is no token pre-read, we get a new * token and place it to hawk->ntok. */ return get_token_into(hawk, &hawk->ntok); } } static int classify_ident (hawk_t* hawk, const hawk_oocs_t* name) { /* perform binary search */ /* declaring left, right, mid to be the int type is ok * because we know kwtab is small enough. */ int left, right, mid; /* extra alias keywords which are not part of the main kwtab */ if (hawk_comp_oochars_bcstr(name->ptr, name->len, "func", 0) == 0) return TOK_FUNCTION; /* search in the main kwtab */ left = 0; right = HAWK_COUNTOF(kwtab) - 1; while (left <= right) { int n; kwent_t* kwp; /*mid = (left + right) / 2;*/ mid = left + (right - left) / 2; kwp = &kwtab[mid]; n = hawk_comp_oochars(kwp->name.ptr, kwp->name.len, name->ptr, name->len, 0); if (n > 0) { /* if left, right, mid were of hawk_oow_t, * you would need the following line. if (mid == 0) break; */ right = mid - 1; } else if (n < 0) left = mid + 1; else { if ((hawk->opt.trait & kwp->trait) != kwp->trait) break; return kwp->type; } } return TOK_IDENT; } int hawk_isvalidident (hawk_t* hawk, const hawk_ooch_t* name) { hawk_ooch_t c; hawk_oocs_t cs; cs.ptr = (hawk_ooch_t*)name; if ((c = *name) == '_' || hawk_is_ooch_alpha(c)) { do { c = *++name; } while (c == '_' || hawk_is_ooch_alpha(c) || hawk_is_ooch_digit(c)); if (c != '\0') return 0; cs.len = name - cs.ptr; return classify_ident(hawk, &cs) == TOK_IDENT; } return 0; } static int put_oow_as_dec (hawk_t* hawk, hawk_oow_t v) { hawk_ooch_t tmp[HAWK_SIZEOF(v) * 8 + 2]; hawk_oow_t n; n = hawk_uint_to_oocstr((hawk_uint_t)v, 10, HAWK_NULL, tmp, HAWK_COUNTOF(tmp)); if (HAWK_UNLIKELY(n == (hawk_oow_t)-1)) return -1; return hawk_putsrcoochars(hawk, tmp, n); } static int put_int_as_dec (hawk_t* hawk, hawk_int_t v) { hawk_ooch_t tmp[HAWK_SIZEOF(v) * 8 + 2]; hawk_oow_t n; n = hawk_int_to_oocstr(v, 10, HAWK_NULL, tmp, HAWK_COUNTOF(tmp)); if (HAWK_UNLIKELY(n == (hawk_oow_t)-1)) return -1; return hawk_putsrcoochars(hawk, tmp, n); } static int put_flt_as_str (hawk_t* hawk, hawk_flt_t v) { hawk_ooch_t tmp[96]; hawk_oow_t n; #if defined(HAWK_USE_FLTMAX) n = hawk_fmttooocstr(hawk, tmp, HAWK_COUNTOF(tmp), HAWK_T("%jjf"), &v); #else n = hawk_fmttooocstr(hawk, tmp, HAWK_COUNTOF(tmp), HAWK_T("%zf"), v); #endif if (HAWK_UNLIKELY(n == (hawk_oow_t)-1)) return -1; return hawk_putsrcoochars(hawk, tmp, n); } static int put_escaped_oochars (hawk_t* hawk, const hawk_ooch_t* ptr, hawk_oow_t len) { hawk_oow_t i; for (i = 0; i < len; i++) { switch (ptr[i]) { case HAWK_T('\n'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\n")) <= -1) return -1; break; case HAWK_T('\r'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\r")) <= -1) return -1; break; case HAWK_T('\t'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\t")) <= -1) return -1; break; case HAWK_T('\f'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\f")) <= -1) return -1; break; case HAWK_T('\b'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\b")) <= -1) return -1; break; case HAWK_T('\v'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\v")) <= -1) return -1; break; case HAWK_T('\a'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\a")) <= -1) return -1; break; case HAWK_T('\0'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\0")) <= -1) return -1; break; case HAWK_T('\"'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\\"")) <= -1) return -1; break; case HAWK_T('\\'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\\\")) <= -1) return -1; break; default: if (hawk_putsrcoochars(hawk, &ptr[i], 1) <= -1) return -1; break; } } return 0; } static int put_escaped_bchars (hawk_t* hawk, const hawk_bch_t* ptr, hawk_oow_t len) { hawk_oow_t i; for (i = 0; i < len; i++) { switch (ptr[i]) { case HAWK_BT('\n'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\n")) <= -1) return -1; break; case HAWK_BT('\r'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\r")) <= -1) return -1; break; case HAWK_BT('\t'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\t")) <= -1) return -1; break; case HAWK_BT('\f'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\f")) <= -1) return -1; break; case HAWK_BT('\b'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\b")) <= -1) return -1; break; case HAWK_BT('\v'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\v")) <= -1) return -1; break; case HAWK_BT('\a'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\a")) <= -1) return -1; break; case HAWK_BT('\0'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\0")) <= -1) return -1; break; case HAWK_BT('\"'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\\"")) <= -1) return -1; break; case HAWK_BT('\\'): if (hawk_putsrcoocstr(hawk, HAWK_T("\\\\")) <= -1) return -1; break; default: { #if defined(HAWK_OOCH_IS_BCH) hawk_ooch_t oc = (hawk_bchu_t)ptr[i]; if (hawk_putsrcoochars(hawk, &oc, 1) <= -1) return -1; #else hawk_ooch_t oc = (hawk_bchu_t)ptr[i]; if (HAWK_BYTE_PRINTABLE((hawk_bchu_t)ptr[i])) { if (hawk_putsrcoochars(hawk, &oc, 1) <= -1) return -1; } else { hawk_bch_t xbuf[3]; hawk_ooch_t xc; hawk_byte_to_bcstr((hawk_bchu_t)ptr[i], xbuf, HAWK_COUNTOF(xbuf), 16, '0'); if (hawk_putsrcoocstr(hawk, HAWK_T("\\x")) <= -1) return -1; xc = (hawk_bchu_t)xbuf[0]; if (hawk_putsrcoochars(hawk, &xc, 1) <= -1) return -1; xc = (hawk_bchu_t)xbuf[1]; if (hawk_putsrcoochars(hawk, &xc, 1) <= -1) return -1; } #endif break; } } } return 0; } static int put_newline (hawk_t* hawk) { if (hawk->opt.trait & HAWK_CRLF) { if (put_char(hawk, HAWK_T('\r')) <= -1) return -1; } return put_char(hawk, HAWK_T('\n')); } struct deparse_func_t { hawk_t* hawk; hawk_ooch_t* tmp; hawk_oow_t tmp_len; int ret; }; static int deparse (hawk_t* hawk) { hawk_nde_t* nde; hawk_nde_t* ndetab[2]; hawk_chain_t* chain; hawk_ooch_t tmp[HAWK_SIZEOF(hawk_oow_t) * 8 + 32]; struct deparse_func_t df; int n = 0; int i; hawk_ooi_t op; hawk_oocs_t kw; HAWK_ASSERT(hawk->sio.outf != HAWK_NULL); HAWK_MEMSET(&hawk->sio.arg, 0, HAWK_SIZEOF(hawk->sio.arg)); op = hawk->sio.outf(hawk, HAWK_SIO_CMD_OPEN, &hawk->sio.arg, HAWK_NULL, 0); if (op <= -1) return -1; #define EXIT_DEPARSE() do { n = -1; goto exit_deparse; } while(0) if (hawk->parse.pragma.rtx_stack_limit > 0 && hawk->parse.pragma.rtx_stack_limit != hawk->opt.rtx_stack_limit) { hawk_oow_t len; len = hawk_int_to_oocstr((hawk_int_t)hawk->parse.pragma.rtx_stack_limit, 10, HAWK_NULL, tmp, HAWK_COUNTOF(tmp)); if (hawk_putsrcoocstr(hawk, HAWK_T("@pragma stack_limit ")) <= -1 || hawk_putsrcoochars(hawk, tmp, len) <= -1 || hawk_putsrcoocstr(hawk, HAWK_T(";\n")) <= -1) EXIT_DEPARSE(); } if (hawk->tree.ngbls > hawk->tree.ngbls_base) { hawk_oow_t i, len; HAWK_ASSERT(hawk->tree.ngbls > 0); hawk_getkwname(hawk, HAWK_KWID_XGLOBAL, &kw); if (hawk_putsrcoochars(hawk, kw.ptr, kw.len) <= -1 || hawk_putsrcoocstr(hawk, HAWK_T(" ")) <= -1) EXIT_DEPARSE(); for (i = hawk->tree.ngbls_base; i < hawk->tree.ngbls - 1; i++) { if (!(hawk->opt.trait & HAWK_IMPLICIT)) { /* use the actual name if no named variable is allowed */ if (hawk_putsrcoochars(hawk, HAWK_ARR_DPTR(hawk->parse.gbls, i), HAWK_ARR_DLEN(hawk->parse.gbls, i)) <= -1) EXIT_DEPARSE(); } else { len = hawk_int_to_oocstr((hawk_int_t)i, 10, HAWK_T("__g"), tmp, HAWK_COUNTOF(tmp)); HAWK_ASSERT(len != (hawk_oow_t)-1); if (hawk_putsrcoochars(hawk, tmp, len) <= -1) EXIT_DEPARSE(); } if (hawk_putsrcoocstr(hawk, HAWK_T(", ")) <= -1) EXIT_DEPARSE(); } if (!(hawk->opt.trait & HAWK_IMPLICIT)) { /* use the actual name if no named variable is allowed */ if (hawk_putsrcoochars(hawk, HAWK_ARR_DPTR(hawk->parse.gbls,i), HAWK_ARR_DLEN(hawk->parse.gbls,i)) <= -1) EXIT_DEPARSE(); } else { len = hawk_int_to_oocstr((hawk_int_t)i, 10, HAWK_T("__g"), tmp, HAWK_COUNTOF(tmp)); HAWK_ASSERT(len != (hawk_oow_t)-1); if (hawk_putsrcoochars(hawk, tmp, len) <= -1) EXIT_DEPARSE(); } if (hawk->opt.trait & HAWK_CRLF) { if (hawk_putsrcoocstr(hawk, HAWK_T(";\r\n\r\n")) <= -1) EXIT_DEPARSE(); } else { if (hawk_putsrcoocstr(hawk, HAWK_T(";\n\n")) <= -1) EXIT_DEPARSE(); } } df.hawk = hawk; df.tmp = tmp; df.tmp_len = HAWK_COUNTOF(tmp); df.ret = 0; hawk_htb_walk(hawk->tree.funs, deparse_func, &df); if (df.ret <= -1) EXIT_DEPARSE(); ndetab[0] = hawk->tree.init; /* initialization of @global/@const */ ndetab[1] = hawk->tree.begin; /* real BEGIN block */ for (i = 0; i < HAWK_COUNTOF(ndetab); i++) { for (nde = ndetab[i]; nde != HAWK_NULL; nde = nde->next) { if (i == 0) { /* @__init is not a real name. * it's for display only. */ if (hawk_putsrcoochars(hawk, HAWK_T("@__init"), 7) <= -1) EXIT_DEPARSE(); } else { hawk_oocs_t kw; hawk_getkwname(hawk, HAWK_KWID_BEGIN, &kw); if (hawk_putsrcoochars(hawk, kw.ptr, kw.len) <= -1) EXIT_DEPARSE(); } if (hawk_putsrcoocstr(hawk, HAWK_T(" ")) <= -1) EXIT_DEPARSE(); if (hawk_prnnde(hawk, nde) <= -1) EXIT_DEPARSE(); if (hawk->opt.trait & HAWK_CRLF) { if (put_char(hawk, HAWK_T('\r')) <= -1) EXIT_DEPARSE(); } if (put_char(hawk, HAWK_T('\n')) <= -1) EXIT_DEPARSE(); } } chain = hawk->tree.chain; while (chain) { if (chain->pattern) { if (hawk_prnptnpt(hawk, chain->pattern) <= -1) EXIT_DEPARSE(); } if (!chain->action) { /* blockless pattern */ if (hawk->opt.trait & HAWK_CRLF) { if (put_char(hawk, HAWK_T('\r')) <= -1) EXIT_DEPARSE(); } if (put_char(hawk, HAWK_T('\n')) <= -1) EXIT_DEPARSE(); } else { if (chain->pattern) { if (put_char(hawk, HAWK_T(' ')) <= -1) EXIT_DEPARSE(); } if (hawk_prnpt(hawk, chain->action) <= -1) EXIT_DEPARSE(); } if (hawk->opt.trait & HAWK_CRLF) { if (put_char(hawk, HAWK_T('\r')) <= -1) EXIT_DEPARSE(); } if (put_char(hawk, HAWK_T('\n')) <= -1) EXIT_DEPARSE(); chain = chain->next; } for (nde = hawk->tree.end; nde != HAWK_NULL; nde = nde->next) { hawk_oocs_t kw; hawk_getkwname(hawk, HAWK_KWID_END, &kw); if (hawk_putsrcoochars(hawk, kw.ptr, kw.len) <= -1) EXIT_DEPARSE(); if (hawk_putsrcoocstr(hawk, HAWK_T(" ")) <= -1) EXIT_DEPARSE(); if (hawk_prnnde(hawk, nde) <= -1) EXIT_DEPARSE(); /* if (hawk->opt.trait & HAWK_CRLF) { if (put_char(hawk, HAWK_T('\r')) <= -1) EXIT_DEPARSE(); } if (put_char(hawk, HAWK_T('\n')) <= -1) EXIT_DEPARSE(); */ } if (flush_out(hawk) <= -1) EXIT_DEPARSE(); exit_deparse: if (hawk->sio.outf(hawk, HAWK_SIO_CMD_CLOSE, &hawk->sio.arg, HAWK_NULL, 0) != 0 && n == 0) n = -1; return n; } static hawk_htb_walk_t deparse_func (hawk_htb_t* map, hawk_htb_pair_t* pair, void* arg) { struct deparse_func_t* df = (struct deparse_func_t*)arg; hawk_fun_t* fun = (hawk_fun_t*)HAWK_HTB_VPTR(pair); hawk_oow_t i, n; hawk_oocs_t kw; HAWK_ASSERT(hawk_comp_oochars(HAWK_HTB_KPTR(pair), HAWK_HTB_KLEN(pair), fun->name.ptr, fun->name.len, 0) == 0); #define PUT_C(x,c) \ if (put_char(x->hawk,c)==-1) { \ x->ret = -1; return HAWK_HTB_WALK_STOP; \ } #define PUT_S(x,str) \ if (hawk_putsrcoocstr(x->hawk,str) <= -1) { \ x->ret = -1; return HAWK_HTB_WALK_STOP; \ } #define PUT_SX(x,str,len) \ if (hawk_putsrcoochars(x->hawk, str, len) <= -1) { \ x->ret = -1; return HAWK_HTB_WALK_STOP; \ } /* the keyword 'function' */ hawk_getkwname (df->hawk, HAWK_KWID_FUNCTION, &kw); PUT_SX(df, kw.ptr, kw.len); /* function name */ PUT_C(df, HAWK_T(' ')); PUT_SX(df, fun->name.ptr, fun->name.len); PUT_S(df, HAWK_T("(")); /* argument list */ for (i = 0; i < fun->nargs; ) { if (fun->argspec && i < fun->argspeclen && fun->argspec[i] == 'r') PUT_S(df, HAWK_T("&")); n = hawk_int_to_oocstr(i++, 10, HAWK_T("__p"), df->tmp, df->tmp_len); HAWK_ASSERT(n != (hawk_oow_t)-1); PUT_SX (df, df->tmp, n); if (i >= fun->nargs) break; PUT_S(df, HAWK_T(", ")); } if (fun->variadic) { if (fun->nargs > 0) PUT_S(df, HAWK_T(", ")); PUT_S(df, HAWK_T("...")); } PUT_S(df, HAWK_T(") ")); if (hawk_prnpt(df->hawk, fun->body) <= -1) return -1; if (df->hawk->opt.trait & HAWK_CRLF) { PUT_C(df, HAWK_T('\r')); } PUT_C(df, HAWK_T('\n')); return HAWK_HTB_WALK_FORWARD; #undef PUT_C #undef PUT_S #undef PUT_SX } static int put_char (hawk_t* hawk, hawk_ooch_t c) { hawk->sio.arg.b.buf[hawk->sio.arg.b.len++] = c; if (hawk->sio.arg.b.len >= HAWK_COUNTOF(hawk->sio.arg.b.buf)) { if (flush_out(hawk) <= -1) return -1; } return 0; } static int flush_out (hawk_t* hawk) { hawk_ooi_t n; while (hawk->sio.arg.b.pos < hawk->sio.arg.b.len) { n = hawk->sio.outf( hawk, HAWK_SIO_CMD_WRITE, &hawk->sio.arg, &hawk->sio.arg.b.buf[hawk->sio.arg.b.pos], hawk->sio.arg.b.len - hawk->sio.arg.b.pos ); if (n <= 0) return -1; hawk->sio.arg.b.pos += n; } hawk->sio.arg.b.pos = 0; hawk->sio.arg.b.len = 0; return 0; } int hawk_putsrcoocstr (hawk_t* hawk, const hawk_ooch_t* str) { while (*str != '\0') { if (put_char(hawk, *str) <= -1) return -1; str++; } return 0; } int hawk_putsrcoochars (hawk_t* hawk, const hawk_ooch_t* str, hawk_oow_t len) { const hawk_ooch_t* end = str + len; while (str < end) { if (put_char(hawk, *str) <= -1) return -1; str++; } return 0; } #if defined(HAWK_ENABLE_STATIC_MODULE) /* let's hardcode module information */ #include "mod-hawk.h" #include "mod-math.h" #include "mod-str.h" #include "mod-sys.h" #if defined(HAWK_ENABLE_MOD_FFI_STATIC) #include "../mod/mod-ffi.h" #endif #if defined(HAWK_ENABLE_MOD_MEMC_STATIC) #include "../mod/mod-memc.h" #endif #if defined(HAWK_ENABLE_MOD_MYSQL_STATIC) #include "../mod/mod-mysql.h" #endif #if defined(HAWK_ENABLE_MOD_JSON_STATIC) #include "../mod/mod-json.h" #endif #if defined(HAWK_ENABLE_MOD_SED_STATIC) #include "../mod/mod-sed.h" #endif #if defined(HAWK_ENABLE_MOD_SQLITE_STATIC) #include "../mod/mod-sqlite.h" #endif #if defined(HAWK_ENABLE_MOD_UCI_STATIC) #include "../mod/mod-uci.h" #endif /* * if modules are linked statically into the main hawk module, * this table is used to find the entry point of the modules. * you must update this table if you add more modules */ static hawk_mod_desc_t static_modtab[] = { #if defined(HAWK_ENABLE_MOD_FFI_STATIC) { HAWK_T("ffi"), hawk_mod_ffi }, #endif { HAWK_T("hawk"), hawk_mod_hawk }, { HAWK_T("math"), hawk_mod_math }, #if defined(HAWK_ENABLE_MOD_MEMC_STATIC) { HAWK_T("memc"), hawk_mod_memc }, #endif #if defined(HAWK_ENABLE_MOD_MYSQL_STATIC) { HAWK_T("mysql"), hawk_mod_mysql }, #endif #if defined(HAWK_ENABLE_MOD_JSON_STATIC) { HAWK_T("json"), hawk_mod_json }, #endif #if defined(HAWK_ENABLE_MOD_SED_STATIC) { HAWK_T("sed"), hawk_mod_sed }, #endif #if defined(HAWK_ENABLE_MOD_SQLITE_STATIC) { HAWK_T("sqlite"), hawk_mod_sqlite }, #endif { HAWK_T("str"), hawk_mod_str }, { HAWK_T("sys"), hawk_mod_sys }, #if defined(HAWK_ENABLE_MOD_UCI_STATIC) { HAWK_T("uci"), hawk_mod_uci }, #endif }; #endif static hawk_mod_t* query_module (hawk_t* hawk, const hawk_oocs_t segs[], int nsegs, hawk_mod_sym_t* sym, hawk_rbt_t* sectab) { hawk_rbt_pair_t* pair; hawk_mod_data_t* mdp; hawk_rbt_t* modtab; int n; HAWK_ASSERT(nsegs == 2); modtab = hawk->modtab; if (sectab) { /* search in the secondary table first */ pair = hawk_rbt_search(sectab, segs[0].ptr, segs[0].len); if (pair) { mdp = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair); goto done; } /* when a module is loaded for the first time, * the information must be stored in the secondary table * when it's not HAWK_NULL */ modtab = sectab; } /* find in the shared table. note that it searches in hawk->modtab explicitly. * it's wrong to search in modtab because it can get changed to sectab when it * is given */ pair = hawk_rbt_search(hawk->modtab, segs[0].ptr, segs[0].len); if (pair) { mdp = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair); /* this flows to the done lable */ } else { hawk_mod_data_t md; hawk_mod_load_t load = HAWK_NULL; hawk_mod_spec_t spec; hawk_oow_t buflen; /*hawk_ooch_t buf[MAX_MOD_NAME_LEN + 12] = HAWK_T("_hawk_mod_");*/ /* maximum module name length is 64. 15 is decomposed to 13 + 1 + 1. * 10 for _hawk_mod_ * 1 for _ at the end when hawk_mod_xxx_ is attempted. * 1 for the terminating '\0' */ hawk_ooch_t buf[MAX_MOD_NAME_LEN + 12]; /* the terminating null isn't needed in buf here */ HAWK_MEMCPY(buf, HAWK_T("_hawk_mod_"), HAWK_SIZEOF(hawk_ooch_t) * 10); if (segs[0].len > HAWK_COUNTOF(buf) - 15) { /* module name too long */ hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ESEGTL, FMT_ESEGTL, segs[0].len, segs[0].ptr); return HAWK_NULL; } #if defined(HAWK_ENABLE_STATIC_MODULE) /* attempt to find a statically linked module */ /* TODO: binary search ... */ for (n = 0; n < HAWK_COUNTOF(static_modtab); n++) { if (hawk_comp_oochars_oocstr(segs[0].ptr, segs[0].len, static_modtab[n].name, 0) == 0) { load = static_modtab[n].load; break; } } #endif if (!load) { /* check in the user-added static module table */ hawk_htb_pair_t* pair; pair = hawk_htb_search(hawk->static_mods, segs[0].ptr, segs[0].len); if (pair) load = (hawk_mod_load_t)pair->val.ptr; } if (load) { /* found the module in the staic module table */ HAWK_MEMSET(&md, 0, HAWK_SIZEOF(md)); /* Note md.handle is HAWK_NULL for a static module */ /* i copy-insert 'md' into the table before calling 'load'. * to pass the same address to load(), query(), etc */ pair = hawk_rbt_insert(modtab, segs[0].ptr, segs[0].len, &md, HAWK_SIZEOF(md)); if (HAWK_UNLIKELY(!pair)) return HAWK_NULL; mdp = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair); if (load(&mdp->mod, hawk) <= -1) { hawk_rbt_delete(modtab, segs[0].ptr, segs[0].len); return HAWK_NULL; } goto done; } /* attempt to find an external module */ HAWK_MEMSET(&spec, 0, HAWK_SIZEOF(spec)); spec.prefix = (hawk->opt.mod[1].len > 0)? (const hawk_ooch_t*)hawk->opt.mod[1].ptr: (const hawk_ooch_t*)HAWK_T(HAWK_DEFAULT_MODPREFIX); spec.postfix = (hawk->opt.mod[2].len > 0)? (const hawk_ooch_t*)hawk->opt.mod[2].ptr: (const hawk_ooch_t*)HAWK_T(HAWK_DEFAULT_MODPOSTFIX); spec.name = segs[0].ptr; /* the caller must ensure that this segment is null-terminated */ if (!hawk->prm.modopen || !hawk->prm.modgetsym || !hawk->prm.modclose) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_EINVAL, HAWK_T("module callbacks not set properly")); goto open_fail; } spec.libdir = (hawk->opt.mod[0].len > 0)? (const hawk_ooch_t*)hawk->opt.mod[0].ptr: (const hawk_ooch_t*)HAWK_T(HAWK_DEFAULT_MODLIBDIRS); do { #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) # define LIBDIR_SEPARATOR ';' #else # define LIBDIR_SEPARATOR ':' #endif hawk_ooch_t* colon; colon = hawk_find_oochar_in_oocstr(spec.libdir, LIBDIR_SEPARATOR); if (colon) *colon = '\0'; HAWK_MEMSET(&md, 0, HAWK_SIZEOF(md)); md.handle = hawk->prm.modopen(hawk, &spec); if (!colon) break; *colon = LIBDIR_SEPARATOR; spec.libdir = colon + 1; #undef LIBDIR_SEPARATOR } while (!md.handle); if (!md.handle) { const hawk_ooch_t* bem; open_fail: bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("'%js%js%js' for module '%js' not found - %js"), (spec.prefix? (const hawk_ooch_t*)spec.prefix: (const hawk_ooch_t*)HAWK_T("")), spec.name, (spec.postfix? (const hawk_ooch_t*)spec.postfix: (const hawk_ooch_t*)HAWK_T("")), spec.name, bem); return HAWK_NULL; } buflen = hawk_copy_oocstr_unlimited(&buf[10], segs[0].ptr); /* attempt hawk_mod_xxx */ load = hawk->prm.modgetsym(hawk, md.handle, &buf[1]); if (!load) { /* attempt _hawk_mod_xxx */ load = hawk->prm.modgetsym(hawk, md.handle, &buf[0]); if (!load) { hawk_seterrnum(hawk, HAWK_NULL, HAWK_ENOERR); /* attempt hawk_mod_xxx_ */ buf[10 + buflen] = HAWK_T('_'); buf[10 + buflen + 1] = HAWK_T('\0'); load = hawk->prm.modgetsym(hawk, md.handle, &buf[1]); if (!load) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("module symbol '%.*js' not found - %js"), (10 + buflen), &buf[1], bem); hawk->prm.modclose(hawk, md.handle); return HAWK_NULL; } } } /* i copy-insert 'md' into the table before calling 'load'. * to pass the same address to load(), query(), etc */ pair = hawk_rbt_insert(modtab, segs[0].ptr, segs[0].len, &md, HAWK_SIZEOF(md)); if (HAWK_UNLIKELY(!pair)) { hawk->prm.modclose(hawk, md.handle); return HAWK_NULL; } mdp = (hawk_mod_data_t*)HAWK_RBT_VPTR(pair); if (load(&mdp->mod, hawk) <= -1) { hawk_rbt_delete(modtab, segs[0].ptr, segs[0].len); hawk->prm.modclose(hawk, mdp->handle); return HAWK_NULL; } } done: hawk_seterrnum(hawk, HAWK_NULL, HAWK_ENOERR); n = mdp->mod.query(&mdp->mod, hawk, segs[1].ptr, sym); if (n <= -1) { const hawk_ooch_t* olderrmsg = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, HAWK_ENOENT, HAWK_T("unable to find '%.*js' in module '%.*js' - %js"), segs[1].len, segs[1].ptr, segs[0].len, segs[0].ptr, olderrmsg); return HAWK_NULL; } return &mdp->mod; } int hawk_addstaticmodwithbcstr (hawk_t* hawk, const hawk_bch_t* name, hawk_mod_load_t load) { hawk_htb_pair_t* pair; #if defined(HAWK_OOCH_IS_BCH) pair = hawk_htb_insert(hawk->static_mods, (hawk_bch_t*)name, hawk_count_bcstr(name), load, 0); #else { hawk_ucs_t wcs; wcs.ptr = hawk_dupbtoucstr(hawk, name, &wcs.len, 0); if (HAWK_UNLIKELY(!wcs.ptr)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, hawk_geterrnum(hawk), HAWK_T("unable to add static module '%hs' - %js"), name, bem); return -1; } pair = hawk_htb_insert(hawk->static_mods, wcs.ptr, wcs.len, load, 0); hawk_freemem(hawk, wcs.ptr); } #endif if (HAWK_UNLIKELY(!pair)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, hawk_geterrnum(hawk), HAWK_T("unable to add static module '%js' - %js"), name, bem); return -1; } return 0; } int hawk_addstaticmodwithucstr (hawk_t* hawk, const hawk_uch_t* name, hawk_mod_load_t load) { hawk_htb_pair_t* pair; #if defined(HAWK_OOCH_IS_BCH) { hawk_bcs_t mbs; mbs.ptr = hawk_duputobcstr(hawk, name, &mbs.len); if (HAWK_UNLIKELY(!mbs.ptr)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, hawk_geterrnum(hawk), HAWK_T("unable to add static module '%ls' - %js"), name, bem); return -1; } pair = hawk_htb_insert(hawk->static_mods, mbs.ptr, mbs.len, load, 0); hawk_freemem(hawk, mbs.ptr); } #else pair = hawk_htb_insert(hawk->static_mods, (hawk_uch_t*)name, hawk_count_ucstr(name), load, 0); #endif if (HAWK_UNLIKELY(!pair)) { const hawk_ooch_t* bem = hawk_backuperrmsg(hawk); hawk_seterrfmt(hawk, HAWK_NULL, hawk_geterrnum(hawk), HAWK_T("unable to add static module '%js' - %js"), name, bem); return -1; } return 0; } hawk_mod_t* hawk_querymodulewithoocs (hawk_t* hawk, const hawk_oocs_t* name, hawk_mod_sym_t* sym, hawk_rbt_t* sectab) { const hawk_ooch_t* dc; hawk_oocs_t segs[2]; hawk_ooch_t modname[MAX_MOD_NAME_LEN + 1]; /* the logic here must match hawk_rtx_querymodulewithoocs() in run.c */ dc = hawk_find_oochars_in_oochars(name->ptr, name->len, HAWK_T("::"), 2, 0); if (!dc) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_EINVAL, HAWK_T("invalid module name - %.*js"), name->len, name->ptr); return HAWK_NULL; } segs[0].len = dc - name->ptr; if (segs[0].len >= HAWK_COUNTOF(modname)) { hawk_seterrfmt(hawk, HAWK_NULL, HAWK_EINVAL, HAWK_T("module name too long - %.*js"), name->len, name->ptr); return HAWK_NULL; } segs[0].ptr = modname; hawk_copy_oochars_to_oocstr(modname, HAWK_COUNTOF(modname), name->ptr, segs[0].len); segs[1].len = name->len - segs[0].len - 2; segs[1].ptr = (hawk_ooch_t*)name->ptr + segs[0].len + 2; return query_module(hawk, segs, 2, sym, sectab); } hawk_mod_t* hawk_querymodulewithname (hawk_t* hawk, const hawk_ooch_t* name, hawk_mod_sym_t* sym, hawk_rbt_t* sectab) { hawk_oocs_t oocs; oocs.ptr = (hawk_ooch_t*)name; oocs.len = hawk_count_oocstr(name); return hawk_querymodulewithoocs(hawk, &oocs, sym, sectab); }