%{ %line /* Copyright 1995, 1997 J"orn Rennecke */ /* declarations etc */ #include <stdio.h> #include <limits.h> #include "common.h" #include "alloc.h" #include "lex.h" #include "instrs.h" #include "compiler.h" #include "interpret.h" extern int current_line; extern int pragma_optimize; struct { uint32 length; struct string_concat **link; struct string_concat list; } current_string; p_int function_ix, n_fun_def, n_fun_redef, total_pcode; struct function **funblocks[N_FUNBLOCKS]; /* nonterminal 'inherit' depends on the order */ static struct ident inheritance_qualifiers[] = { { "variables", 9, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, &inheritance_qualifiers[1] }, { "functions", 9, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, 0 }, }; struct ident builtin_structs[] = { { "long", 4, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, &builtin_structs[1] }, { "quoted_array", 12, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, &builtin_structs[2] }, { "regexp", 6, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, &builtin_structs[3] }, { "space", 5, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, &builtin_structs[4] }, { "symbol", 6, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, &builtin_structs[5] }, { "term", 4, I_TYPE_GLOBAL, 0, 0,0, {{ -2, -2, -1, -1 }}, &inheritance_qualifiers[0] }, }; svalue inherit_file; static struct param { uint8 type; uint8 dummy[3]; struct ident *ident; } all_param[MAX_PARAM]; #define PARAM(n) (all_param[(n)]) static struct ident *identifier_limbo = 0; static int global_variable(struct ident *name); char error_nargs[] = %error_nargs ; char ce_error_nargs[] = %ce_error_nargs ; %} %efuns /* Include the tokens */ %union { %line struct ident *ident; p_int number; svalue constant; struct counted_string string; struct { p_int number; %if 0 int type; %endif } closure; #ifdef COMPILER_H struct expression expression; struct statement statement, multival; union node *statements; struct binode *local; struct { struct binode **link, *start; } scope; struct string_concat string_concat; #endif struct { struct type_list *list; uint32 length; } type_list; uint8 vtype; struct { int32 var, fun; } type_modifier; /* nonterminal 'inherit' casts this to inherit_types, * and uses int32* casts */ int32 inherit_types[2]; struct function *function; struct { int32 modifier; p_int basic_type; } global_var_decl; } %type <type_list> type_list %type <ident> YYF_IDENTIFIER %type <global_var_decl> global_var_declaration %type <function> function_signature %type <number> YYF_PARAM %type <ident> YYF_LOCAL %type <number> '=' YYF_ASSIGN YYF_LOR YYF_LAND '|' '^' '&' YYF_EQUALITY '~' '!' %type <number> YYF_COMPARE YYF_SHIFT '+' '-' YYF_DIV '*' YYF_ADDQ add_op %type <number> YYF_BASIC_TYPE YYF_CLOSURE_DECL YYF_VOID %type <number> basic_type opt_star no_star %type <number> YYF_TYPE_MODIFIER YYF_VAR_TYPE_MODIFIER YYF_FUN_TYPE_MODIFIER %type <number> YYF_VIRTUAL YYF_VARARGS %type <type_modifier> type_modifier inherit %type <number> opt_less %type <number> saveuse_rparen else saveuse_semi %type <constant> YYF_CONSTANT string constant stringified_ident %type <string> YYF_STRING %type <string_concat> string_concat %type <expression> lvalue var_indexable var_expr expression comma_expr %type <multival> multiconst m_v_constant m_kv_constant m_multiconst %type <multival> multival m_v_val arguments %type <expression> brac_expr for_expr for_cond local_declaration %type <statement> block statement %type <statements> statements %right '=' YYF_ASSIGN %right '?' %left YYF_LOR %left YYF_LAND %left '|' %left '^' %left '&' %left YYF_EQUALITY %left YYF_COMPARE '<' %left YYF_SHIFT %left '+' '-' %left YYF_DIV '*' %right '!' '~' %nonassoc YYF_ADDQ %nonassoc '[' YYF_ARROW YYF_QARROW %% start_symbol: program; basic_type: YYF_BASIC_TYPE | YYF_CLOSURE_DECL | YYF_STRUCT YYF_IDENTIFIER { %line struct ident *p; unsigned n; p = $2; n = p - &builtin_structs[0]; if (n < NELEM(builtin_structs)) { $$ = n + TYPE_STRUCT_BUILTIN; } else { /* FIXME */ } } ; %// type: basic_type | basic_type '*' { $$ = TYPE__ARRAY | $1; } ; %// type: basic_type opt_star { $$ = $1 | $2; } opt_star: { $$ = 0; } | '*' { $$ = TYPE__ARRAY; } no_star: { $$ = 0; } | '*' { yyerrorn(CE_VOIDARR); $$ = 0; } %// local_declaration should only be called from block. %// $-1 accumulates *all* local declarations that are topmost in this scope. local_declaration: basic_type opt_star YYF_IDENTIFIER { struct binode *n; struct ident *id; union ident_u u; %line n = alloc_tmpnode(); *$<scope>-1.link = n; $<scope>-1.link = &n->node[0].p; n->ntype = N_LOCAL; n->line = current_line; $$.node.leaf.type = LN_NIL; $$.length = 0; $$.vtype = $1; u.local.vtype = TYPE_VOID; id = NEW_IDENT($3, I_TYPE_LOCAL, u); n->node[1].id = id; if (id) { id->u.local.offset = -local_preallocated++; id->u.local.line = current_line; id->u.local.vtype = $1|$2; } } | basic_type opt_star YYF_IDENTIFIER '=' expression { struct binode *n; struct ident *id; union ident_u u; %line n = alloc_tmpnode(); *$<scope>-1.link = n; $<scope>-1.link = &n->node[0].p; n->ntype = N_LOCAL_INIT; n->line = current_line; $$.node = $5.node; $$.length = $5.length; $$.vtype = $1; u.local.vtype = TYPE_VOID; id = NEW_IDENT($3, I_TYPE_LOCAL, u); n->node[1].id = id; if (id) { id->u.local.offset = stack_use; id->u.local.line = current_line; id->u.local.vtype = $1|$2; } } | local_declaration ',' opt_star YYF_IDENTIFIER { struct binode *n; struct ident *id; union ident_u u; %line n = alloc_tmpnode(); *$<scope>-1.link = n; $<scope>-1.link = &n->node[0].p; n->ntype = N_LOCAL; n->line = current_line; $$.node.leaf.type = LN_NIL; $$.length = $1.length; $$.vtype = $1.vtype; u.local.vtype = TYPE_VOID; id = NEW_IDENT($4, I_TYPE_LOCAL, u); n->node[1].id = id; if (id) { id->u.local.offset = -local_preallocated++; id->u.local.line = current_line; id->u.local.vtype = $1.vtype|$3; } } | local_declaration ',' opt_star YYF_IDENTIFIER '=' expression { %line struct binode *n; struct ident *id; union ident_u u; n = alloc_tmpnode(); *$<scope>-1.link = n; $<scope>-1.link = &n->node[0].p; n->ntype = N_LOCAL_INIT; n->line = current_line; $$.node = $6.node; $$.length = $1.length + $6.length; $$.vtype = $1.vtype; u.local.vtype = TYPE_VOID; id = NEW_IDENT($4, I_TYPE_LOCAL, u); n->node[1].id = id; if (id) { id->u.local.offset = stack_use; id->u.local.line = current_line; id->u.local.vtype = $1.vtype|$3; } } ; type_modifier: { $$.var = $$.fun = 0; } | type_modifier YYF_TYPE_MODIFIER { $$.var = $1.var | $2; $$.fun = $1.fun | $2; } | type_modifier YYF_VAR_TYPE_MODIFIER { $$.var = $1.var | $2; $$.fun = $1.fun; } | type_modifier YYF_VIRTUAL { $$.var = $1.var | $2; $$.fun = $1.fun; } | type_modifier YYF_FUN_TYPE_MODIFIER { $$.var = $1.var; $$.fun = $1.fun | $2; } | type_modifier YYF_VARARGS { $$.var = $1.var; $$.fun = $1.fun | $2; } ; global_var_declaration: struct_declaration { /* FIXME */ } | type_modifier basic_type opt_star YYF_IDENTIFIER { declare_global_var($1.var, $2|$3, $4, SV_NULL); $$.modifier = $1.var; $$.basic_type = $2; } | type_modifier basic_type opt_star YYF_IDENTIFIER '=' constant { declare_global_var($1.var, $2|$3, $4, $6); $$.modifier = $1.var; $$.basic_type = $2; } | global_var_declaration ',' opt_star YYF_IDENTIFIER { declare_global_var($1.modifier, $1.basic_type|$3, $4, SV_NULL); $$ = $1; } | global_var_declaration ',' opt_star YYF_IDENTIFIER '=' constant { declare_global_var($1.modifier, $1.basic_type|$3, $4, $6); $$ = $1; } ; struct_member_decl: basic_type opt_star YYF_IDENTIFIER | struct_member_decl ',' opt_star YYF_IDENTIFIER { /* FIXME */ } ; struct_member_decls: /* empty */ | struct_member_decls struct_member_decl ';' { /* FIXME */ } ; struct_declaration: type_modifier YYF_STRUCT YYF_IDENTIFIER '{' struct_member_decls '}' { /* FIXME */ } ; formal_arg: basic_type opt_star | YYF_VARARGS basic_type opt_star | basic_type opt_star YYF_IDENTIFIER { struct ident *id = $3; union ident_u u; %line if (n_param > MAX_PARAM) { yyerrorn(CE_NPARAM, make_string(id->name, id->namelen)); break; } PARAM(n_param).type = $1|$2; u.param = -1; id = NEW_IDENT(id, I_TYPE_PARAM, u); if (id->u.param >= 0) yyerrorn(CE_DUPPAR, make_string(id->name, id->namelen)); id->u.param = n_param; PARAM(n_param++).ident = id; } | YYF_VARARGS basic_type opt_star YYF_IDENTIFIER { struct ident *id = $4; union ident_u u; %line if (n_param > MAX_PARAM) { yyerrorn(CE_NPARAM, make_string(id->name, id->namelen)); break; } PARAM(n_param).type = $1|$2|$3; u.param = -1; id = NEW_IDENT(id, I_TYPE_PARAM, u); if (id->u.param >= 0) yyerrorn(CE_DUPPAR, make_string(id->name, id->namelen)); id->u.param = n_param; PARAM(n_param++).ident = id; } ; type_list: formal_arg { %line struct type_list *tl = ALLOC_ANODE(*tl); $$.list = tl; $$.length = 1; /* tl->type = $1 | $2; */ tl->next = 0; } | type_list ',' formal_arg { %line struct type_list *tl = ALLOC_ANODE(*tl); $$.list = tl; $$.length = $1.length + 1; /* tl->type = $3 | $4; */ tl->next = $1.list; } ; function_signature: type_modifier YYF_VOID no_star YYF_IDENTIFIER '(' ')' { $$ = declare_lfun ($1.fun, $2|$3, $4, 0); } | type_modifier basic_type opt_star YYF_IDENTIFIER '(' ')' { $$ = declare_lfun ($1.fun, $2|$3, $4, 0); } | type_modifier YYF_VOID no_star YYF_IDENTIFIER '(' type_list ')' { $$ = declare_lfun ($1.fun, $2|$3, $4, $6.list); } | type_modifier basic_type opt_star YYF_IDENTIFIER '(' type_list ')' { $$ = declare_lfun ($1.fun, $2|$3, $4, $6.list); } add_op: '+' | '-' ; %// The actual concatenation is suspended till we are ready with the %// curent sequence. This way, we don't suffer quadratic time behaviour %// when a recursive macro runs wild and we wait for the maximum macro %// expansion count to be reached. string_concat: YYF_STRING YYF_STRING { %line struct string_concat *l; current_string.length = $1.len + $2.len; current_string.list.start = $1.start; l = alloc_tmpnode(); current_string.link = &l->next; current_string.list.next = l; if (l) { l->start = $2.start; } else { current_string.length = $1.len; current_string.link = ¤t_string.list.next; } } | string_concat YYF_STRING { %line struct string_concat *l; l = alloc_tmpnode(); if (!l) break; current_string.length += $2.len; *current_string.link = l; current_string.link = &l->next; l->start = $2.start; } ; // similar code in lex.c::cook_string() string: YYF_STRING { %line $$ = concat_strings($1.start, $1.len, 0); } | string_concat { %line *current_string.link = 0; $$ = concat_strings( current_string.list.start, current_string.length, current_string.list.next ); } ; constant: YYF_CONSTANT | string | '+' constant %prec '~' { $$ = $2; } | '~' constant { goto const_monop; } | '!' constant { goto const_monop; } | '-' constant %prec '~' { $1 = F_NEGATE; const_monop: *++inter_sp = $2; $$ = immediate_efun_call($1, 1); } | constant '|' constant { goto const_const_binop; } | constant '^' constant { goto const_const_binop; } | constant '&' constant { goto const_const_binop; } | constant YYF_EQUALITY constant { goto const_const_binop; } | constant YYF_COMPARE constant { goto const_const_binop; } | constant '<' constant { goto const_const_binop; } | constant YYF_SHIFT constant { goto const_const_binop; } | constant YYF_DIV constant { goto const_const_binop; } | constant '*' constant { goto const_const_binop; } | constant add_op constant %prec '+' { const_const_binop: *++inter_sp = $1; *++inter_sp = $3; $$ = immediate_efun_call($2, 2); } | '(' constant ')' { $$ = $2; } | '(' '{' '}' ')' { ASSIGN_SVALUE_NO_FREE(&$$, NIL_ARRAY); } | '(' '{' multiconst opt_comma '}' ')' { %line $$ = multiconst_array(&$3); } | '(' '[' ']' ')' { $$ = allocate_mapping(0, 0, inter_fp->object); } | '(' '[' m_multiconst opt_comma ']' ')' { $$ = multiconst_mapping(&$3); } | YYF_CLOSURE_DECL function_signature block { /* FIXME */ } ; multiconst: constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0].p = 0; n->node[1].sv = $1; $$.node = n; $$.length = 1; } | multiconst ',' constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0] = $1.node; n->node[1].sv = $3; $$.node = n; $$.length = $1.length + 1; } ; multival: var_expr { struct binode *n; %line n = alloc_tmpnode(); n->node[0] = 0; n->node[1] = $1.node; n->ntype = N_MULTIVAL; n->opr = $1.vtype; $$.node.p = n; $$.length = $1.length; } | multiconst ',' var_expr { multiconst_multival(&$1); goto concat_multival; } | multival ',' expression { struct binode *n; %line concat_multival: n = alloc_tmpnode(); n->node[0] = $1.node; n->node[1] = $3.node; n->ntype = N_MULTIVAL; n->opr = $3.vtype; $$.node.p = n; $$.length = $1.length + $3.length; } ; m_v_constant: constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0].p = 0; n->node[1].sv = $1; $$.node = n; $$.length = 1; } | m_v_constant ';' constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0] = $1.node; n->node[1].sv = $3; $$.node = n; $$.length = $1.length + 1; } ; m_kv_constant: constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0].sv = $1; n->node[1].p = 0; $$.node = n; $$.length = 0; } | constant ':' m_v_constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0].sv = $1; n->node[1] = $3.node; $$.node = n; $$.length = $3.length; } m_multiconst: m_kv_constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0].p = 0; n->node[1] = $1.node; $$.node = n; $$.length = $1.length; } | m_multiconst ',' m_kv_constant { struct binode *n; %line n = alloc_tmpnode(); n->ntype = N_MULTICONST; n->node[0] = $1.node; n->node[1] = $3.node; $$.node = n; $$.length = $1.length; } m_v_val: var_expr { struct binode *n; %line n = alloc_tmpnode(); n->node[0] = 0; n->node[1] = $1.node; n->ntype = N_MULTIVAL; n->opr = $1.vtype; $$.node.p = n; $$.length = $1.length; } | m_v_constant ';' var_expr { multiconst_multival(&$1); goto concat_m_v_val; } | m_v_val ';' expression { struct binode *n; %line concat_m_v_val: n = alloc_tmpnode(); n->node[0] = $1.node; n->node[1] = $3.node; n->ntype = N_MULTIVAL; n->opr = $3.vtype; $$.node.p = n; $$.length = $1.length + $3.length; } m_kv_val: var_expr | constant ':' m_v_val { constant_expression(&$1); goto var_k_var_v; } | var_expr ':' m_v_constant { /* FIXME */ goto var_k_var_v; } | var_expr ':' m_v_val { var_k_var_v: /* FIXME */ } m_multival: m_kv_val | m_multiconst ',' m_kv_val | m_multival ',' m_kv_constant | m_multival ',' m_kv_val opt_less: { $$ = 0; } | '<' { $$ = 1; } ; opt_comma: | ',' ; lvalue: YYF_PARAM { %line $$.node.leaf.type = LN_PARAM; $$.node.leaf.n.u = $1; /* the actual length can only be determined when code is generated, * although 2 is likely. */ $$.length = 2; $$.vtype = PARAM($1).type; } | YYF_LOCAL { int n = $1->u.local.offset; %line $$.node.leaf.type = LN_LOCAL; $$.node.leaf.n.s = n; n += stack_use; $$.length = 2 + (n > 0xff); $$.vtype = $1->u.local.vtype; } | YYF_IDENTIFIER { %line int n = global_variable($1); struct var_decl *var = GVARIABLE(n); $$.node.leaf.type = LN_GLOBAL; $$.node.leaf.n.u = n; $$.length = 2 + (var->ix > 0xff); $$.vtype = var->type; } | constant '[' opt_less constant ']' %prec '[' { constant_expression(&$1); goto var_const_index; } | var_indexable '[' opt_less constant ']' %prec '[' { %line struct binode *n; var_const_index: n = alloc_node(); $$.node.p = n; if (!$3 && !($4.i & ~0x1fffe)) { n->ntype = N_LV_UNARY_CST; n->node[0].leaf.type = LN_INT; n->node[0].leaf.n.u = $4.i >> 1; if (!($4.i & ~0x1fe)) { n->opr = ULV_CINDEX; $$.length = $1.length + 2; } else { n->opr = ULV_SINDEX; $$.length = $1.length + 3; } } else { n->ntype = N_LV_BINOP; n->opr = $3 ? ULV_RINDEX : ULV_INDEX; $$.length = $1.length + constant_node($4, &n->node[0]) + 1; } n->line = current_line; n->node[1] = $1.node; if ($1.vtype & TYPE__ARRAY) { $$.vtype = $1.vtype & ~TYPE__ARRAY; } else if ($1.vtype == TYPE_STRING) { $$.vtype = TYPE_NUMBER; } else { if ($$.vtype != TYPE_MAPPING) bad_type(1, 0/*FIXME*/); $$.vtype = TYPE_ANY; } } | var_indexable '[' opt_less var_expr ']' %prec '[' { %line struct binode *n; var_var_index: n = alloc_node(); $$.node.p = n; n->ntype = N_LV_BINOP; n->opr = $3 ? ULV_RINDEX : ULV_INDEX; n->line = current_line; n->node[0] = $4.node; n->node[1] = $1.node; $$.length = $4.length + $1.length + 1; if ($1.vtype & TYPE__ARRAY) { $$.vtype = $1.vtype & ~TYPE__ARRAY; } else if ($1.vtype == TYPE_STRING) { $$.vtype = TYPE_NUMBER; } else { if ($$.vtype != TYPE_MAPPING) bad_type(1, 0/*FIXME*/); $$.vtype = TYPE_ANY; } } | constant '[' opt_less var_expr ']' %prec '[' { constant_expression(&$1); goto var_var_index; } | constant '[' opt_less expression ',' expression ']' %prec '[' { /* FIXME */ } | var_indexable '[' opt_less expression ',' expression ']' %prec '[' { %line if ($3) yyerrorn(CE_SYNTAX); /* FIXME */ } | constant '[' opt_less expression YYF_RANGE opt_less expression ']' %prec '[' { constant_expression(&$1); goto var_range; } | var_indexable '[' opt_less expression YYF_RANGE opt_less expression ']' %prec '[' { struct binode *nd; int code; %line var_range: if ($3) if ($6) code = F_RR_RANGE; else code = F_RN_RANGE; else if ($6) code = F_NR_RANGE; else code = F_RANGE; nd = ALLOC_NNODE(1 + 3); nd->ntype = N_EFUN; nd->line = current_line; nd->node[0].efun.code = code; nd->node[0].efun.narg = 3; nd->node[1] = $1.node; nd->node[2] = $4.node; nd->node[3] = $7.node; $$.node = nd; $$.length = $1.length + $4.length +$7.length + 1 + (code > 0xff); if (! ($1.vtype & TYPE__ARRAY) && $1.vtype != TYPE_STRING && $1.vtype != TYPE_ANY) bad_type(1, code); if ($4.vtype != TYPE_NUMBER && $4.vtype != TYPE_ANY) bad_type(2, code); if ($7.vtype != TYPE_NUMBER && $7.vtype != TYPE_ANY) bad_type(3, code); $$.vtype = $1.vtype; } | constant '[' opt_less expression YYF_RANGE ']' %prec '[' { constant_expression(&$1); goto var_range2; } | var_indexable '[' opt_less expression YYF_RANGE ']' %prec '[' { struct binode *nd; int code; %line var_range2: if ($3) code = F_R_RANGE2; else code = F_RANGE2; nd = ALLOC_NNODE(1 + 2); nd->ntype = N_EFUN; nd->line = current_line; nd->node[0].efun.code = code; nd->node[0].efun.narg = 2; nd->node[1] = $1.node; nd->node[2] = $4.node; $$.node = nd; $$.length = $1.length + $4.length + 1 + (code > 0xff); if (! ($1.vtype & TYPE__ARRAY) && $1.vtype != TYPE_STRING && $1.vtype != TYPE_ANY) bad_type(1, code); if ($4.vtype != TYPE_NUMBER && $4.vtype != TYPE_ANY) bad_type(2, code); $$.vtype = $1.vtype; } | constant YYF_ARROW YYF_IDENTIFIER { /* FIXME */ } | var_indexable YYF_ARROW YYF_IDENTIFIER { /* * Might be a member of a user defined structure. Convert it * to an indexing with a constant index. * For this to happen, $1 must be of a known structure type. * Else $3 should be a visible auto variable: access to this * variable in an object. */ /* FIXME */ } ; arguments: /* empty */ { $$.node = 0; $$.length = 0; } | multival | multiconst { multiconst_multival(&$1); $$=$1; } ; var_indexable: lvalue | '(' var_expr ')' { $$ = $2; } | '(' comma_expr ')' { $$ = $2; /* FIXME: insert pops, account for their length. */ } | '(' '{' multival opt_comma '}' ')' {} | '(' '[' m_multival opt_comma ']' ')' {} | YYF_IDENTIFIER '(' arguments ')' { struct ident *id = $1; struct ident_global g = { -1, -1, -1, -1}; %line id = NEW_IDENT(id, I_TYPE_GLOBAL, (union ident_u)g); if (id->u.global.function >= 0) { lfun_call(id->u.global.function, $3, &$$); } else if (id->u.global.efun >= 0) { efun_call(id->u.global.efun, $3, &$$); } else { id = verify_declared_lfun(id); lfun_call(id->u.global.function, $3, &$$); } } | YYF_IDENTIFIER YYF_SCOPE YYF_IDENTIFIER '(' arguments ')' { /* FIXME */ } | { ASSIGN_SVALUE_NO_FREE(&$<constant>$, NIL_STRING); } YYF_SCOPE YYF_IDENTIFIER '(' arguments ')' { /* FIXME */ } | string YYF_SCOPE YYF_IDENTIFIER '(' arguments ')' { /* FIXME */ } | constant YYF_ARROW stringified_ident '(' arguments ')' { %line svalue ob = $1; if (SV_IS_NUMBER(ob) || (SV_TYPE(ob) != TYPE_OBJECT && !SV_IS_STRING(ob))) { } $<expression>1.length = constant_node(ob, &$<expression>1.node); member_call($<expression>1, $3, $5, &$$); } | var_indexable YYF_ARROW stringified_ident '(' arguments ')' { %line int type = $1.vtype; if (type != TYPE_OBJECT && type != TYPE_STRING && type != TYPE_ANY) { } member_call($1, $3, $5, &$$); } ; stringified_ident: YYF_IDENTIFIER { %line $$ = make_string($1->name, $1->namelen); if ($1->type == I_TYPE_UNKNOWN) free_shared_identifier($1); } ; var_expr: var_indexable | '&' lvalue { /* FIXME */ } | YYF_ADDQ lvalue %prec YYF_ADDQ { %line struct binode *n; if ($2.vtype != TYPE_NUMBER && $2.vtype != TYPE_ANY) bad_type(1, $1); n = alloc_mnode(); n->ntype = N_LV_UNARY; n->opr = $1 + ULV_PRE_DEC - ULV_DEC; $$.node.p = n; $$.length = $2.length + 1; $$.vtype = TYPE_NUMBER; n->node[0] = $2.node; } | lvalue YYF_ADDQ %prec YYF_ADDQ { %line struct binode *n; if ($1.vtype != TYPE_NUMBER && $1.vtype != TYPE_ANY) bad_type(1, $2); n = alloc_mnode(); n->ntype = N_LV_UNARY; n->opr = $2 + ULV_POST_DEC - ULV_DEC; $$.node.p = n; $$.length = $1.length + 1; $$.vtype = TYPE_NUMBER; n->node[0] = $1.node; } | '+' var_expr %prec '~' { $$ = $2; } | '-' var_expr %prec '~' { $1 = F_NEGATE; goto num_var_unary; } | '~' var_expr { %line num_var_unary: if ($2.vtype != TYPE_NUMBER && $2.vtype != TYPE_ANY) bad_type(1, $1); goto var_unary; } | '!' var_expr { %line struct binode *n; var_unary: n = alloc_mnode(); n->ntype = N_UNARY; n->opr = $1; n->line = current_line; n->node[0] = $2.node; $$.node.p = n; $$.length = $2.length + 1; $$.vtype = TYPE_NUMBER; } | var_expr YYF_DIV constant { goto var_const_mul; } | var_expr '*' constant { %line struct binode *n; p_int i; int op; var_const_mul: i = $3.i; op = $2; if (!(i & i-1)) { if (i > 2) { switch(op) { case F_MULTIPLY: op = F_LSH; i = ffs(i) - 2 << 1; break; case F_DIVIDE: op = F_RSH; i = ffs(i) - 2 << 1; break; case F_MOD: op = F_AND; i = i - 2; break; } } else if (i == 2) { if (op == F_MOD) { op = F_AND; i = 0; } else { $$ = $1; break; } } } if ($1.vtype != TYPE_NUMBER && $1.vtype != TYPE_ANY) bad_type(1, $2); if (i & 1) bad_type(2, $2); n = alloc_node(); n->ntype = N_BINOP; n->opr = op; n->line = current_line; n->node[0] = $1.node; $$.vtype = TYPE_NUMBER; $$.node.p = n; $$.length = $1.length + 1 + constant_node(i, &n->node[1]); } | constant YYF_DIV var_expr { goto const_var_binop; } | constant '*' var_expr { %line num_const_var_binop: if (!SV_IS_NUMBER($1)) bad_type(1, $2); if ($3.vtype != TYPE_NUMBER && $3.vtype != TYPE_ANY) bad_type(2, $2); $$.vtype = TYPE_NUMBER; goto const_var_binop; } | var_expr YYF_DIV var_expr { goto num_var_var_binop; } | var_expr '*' var_expr { %line num_var_var_binop: if ($1.vtype != TYPE_NUMBER && $1.vtype != TYPE_ANY) bad_type(1, $2); if ($3.vtype != TYPE_NUMBER && $3.vtype != TYPE_ANY) bad_type(2, $2); $$.vtype = TYPE_NUMBER; goto var_var_binop; } | constant add_op var_expr %prec '+' { %line struct binode *n; $$.vtype = TYPE_ANY; const_var_binop: n = alloc_node(); $$.node.p = n; $$.length = constant_node($1, &n->node[0]) + 1 + $3.length; n->node[1] = $3.node; n->opr = $2; n->ntype = N_BINOP; } | var_expr add_op constant %prec '+' { %line struct binode *n; $$.vtype = TYPE_ANY; var_const_binop: n = alloc_node(); $$.node.p = n; n->node[0] = $1.node; $$.length = $1.length + 1 + constant_node($3, &n->node[1]); n->opr = $2; n->ntype = N_BINOP; } | var_expr add_op var_expr %prec '+' { %line struct binode *n; $$.vtype = TYPE_ANY; var_var_binop: n = alloc_node(); $$.node.p = n; $$.length = $1.length + 1 + $3.length; n->node[0] = $1.node; n->node[1] = $3.node; n->opr = $2; n->ntype = N_BINOP; } | constant YYF_SHIFT var_expr { goto num_const_var_binop; } | var_expr YYF_SHIFT constant { num_var_const_binop: if ($1.vtype != TYPE_NUMBER && $1.vtype != TYPE_ANY) bad_type(1, $2); if (!SV_IS_NUMBER($3)) bad_type(2, $2); $$.vtype = TYPE_NUMBER; goto var_const_binop; } | var_expr YYF_SHIFT var_expr { goto num_var_var_binop; } | constant '<' var_expr { goto const_var_compare; } | constant YYF_COMPARE var_expr { const_var_compare: constant_expression(&$1); goto var_var_compare; } | var_expr '<' constant { goto var_const_compare; } | var_expr YYF_COMPARE constant { var_const_compare: constant_expression(&$3); goto var_var_compare; } | var_expr '<' var_expr { goto var_var_compare; } | var_expr YYF_COMPARE var_expr { var_var_compare: $$.vtype = TYPE_NUMBER; /* FIXME: Do type checks. */ goto var_var_binop; } | constant YYF_EQUALITY var_expr { %line $$.vtype = TYPE_NUMBER; goto const_var_binop; } | var_expr YYF_EQUALITY constant { %line $$.vtype = TYPE_NUMBER; goto var_const_binop; } | var_expr YYF_EQUALITY var_expr { %line $$.vtype = TYPE_NUMBER; goto var_var_binop; } | constant '&' var_expr { goto num_const_var_binop; } | var_expr '&' constant { goto num_var_const_binop; } | var_expr '&' var_expr { goto num_var_var_binop; } | constant '^' var_expr { goto num_const_var_binop; } | var_expr '^' constant { goto num_var_const_binop; } | var_expr '^' var_expr { goto num_var_var_binop; } | constant '|' var_expr { goto num_const_var_binop; } | var_expr '|' constant { goto num_var_const_binop; } | var_expr '|' var_expr { goto num_var_var_binop; } | constant YYF_LAND var_expr { %line if ($1.i) { FREE_SVALUE($1); $$ = $3; } else { $$.node.leaf.type = LN_INT; $$.node.leaf.n.u = 0; $$.length = 1; $$.vtype = TYPE_ANY; } } | var_expr YYF_LAND constant { constant_expression(&$3); goto var_var_lop; } | var_expr YYF_LAND var_expr { %line struct binode *n; $$.vtype = $3.vtype; var_var_lop: n = alloc_node(); $$.node.p = n; $$.length = $1.length + 2 + $3.length; n->node[0] = $1.node; n->node[1] = $3.node; n->opr = $2; n->ntype = N_LOP; if ($3.length > 254) { $$.length += 4; n->ntype = N_LLOP; n->opr = $2 == F_LAND ? F_XLBRANCH_ON_ZERO : F_XLBRANCH_ON_NON_ZERO; } } | constant YYF_LOR var_expr { %line svalue sv = $1; if (sv.i) { $$.vtype = comp_type(sv); $$.length = constant_node(sv, &$$.node); } else { $$ = $3; } } | var_expr YYF_LOR constant { constant_expression(&$3); goto var_var_lor; } | var_expr YYF_LOR var_expr { %line var_var_lor: $$.vtype = ($1.vtype != $3.vtype) ? TYPE_ANY : $1.vtype; goto var_var_lop; } | constant '?' expression ':' constant %prec '?' { svalue sv = $1; %line if (sv.i) { $$ = $3; FREE_SVALUE($5); } else { $$.vtype = comp_type($5); $$.length = constant_node($5, &$$.node); } FREE_SVALUE(sv); } | constant '?' expression ':' var_expr %prec '?' { svalue sv = $1; %line if (sv.i) $$ = $3; else $$ = $5; FREE_SVALUE(sv); } %// N_IF uses opr as a flag if expression return values should be popped. | var_expr '?' expression ':' constant %prec '?' { constant_expression(&$5); goto arith_if; } | var_expr '?' expression ':' var_expr %prec '?' { %line struct binode *n; int length, len1, len2; arith_if: n = ALLOC_NNODE(3); length = $5.length; len2 = 2 + (length > 254) + (length > 32765); length = $3.length + len2; len1 = 2 + (length > 254) + (length > 32765); $$.length = $1.length + len1 + $3.length + len2 + $5.length; $$.node.p = n; $$.vtype = $3.vtype == $5.vtype ? $3.vtype : TYPE_ANY; n->ntype = N_IF; n->opr = 0 + (len1 << 1) + (len2 << 4); n->node[0] = $1.node; n->node[1] = $3.node; n->node[2] = $5.node; } %// can't use opt_star here because this would clash with closure literals. | '(' '{' '*' basic_type '}' ')' var_expr %prec '~' { $$ = $7; /* FIXME */ } | '(' '{' basic_type '}' ')' var_expr %prec '~' { $$ = $6; /* FIXME */ } | lvalue YYF_ASSIGN constant %prec '=' { goto constant_assignment; } | lvalue '=' constant %prec '=' { %line constant_assignment: $<expression>3.length = constant_node($3, &$<expression>3.node); goto assignment; } | lvalue YYF_ASSIGN var_expr %prec '=' { goto assignment; } | lvalue '=' var_expr %prec '=' { %line struct binode *n; assignment: n = alloc_node(); $$.node.p = n; $$.length = $3.length + 1 + $1.length; n->node[0] = $3.node; n->node[1] = $1.node; n->opr = $2; n->ntype = N_LV_BINOP; } | constant YYF_QARROW YYF_IDENTIFIER { goto test_auto_var; } | var_indexable YYF_QARROW YYF_IDENTIFIER { test_auto_var: ; /* FIXME */ } ; comma_expr: var_expr ',' constant { %line struct binode *n; n = alloc_node(); $$.node.p = n; $$.length = $1.length + 1 + constant_node($3, &n->node[1]); $$.vtype = comp_type($3); n->node[0] = $1.node; n->ntype = N_SEQUENCE; } | var_expr ',' var_expr { goto comma_expr; } | var_expr ',' comma_expr { %line struct binode *n; comma_expr: n = alloc_node(); $$.node.p = n; $$.length = $1.length + 1 + $3.length; $$.vtype = $3.vtype; n->node[0] = $1.node; n->node[1] = $3.node; n->ntype = N_SEQUENCE; } ; %// expression can often not be used due to a bug in precedence usage: %// the precedence of the parent rule is ignored. expression: constant { $$.vtype = comp_type($1); $$.length = constant_node($1, &$$.node); } | var_expr ; for_cond: expression | comma_expr | { $$.node.leaf.type = LN_INT; $$.node.leaf.n.s = 1; $$.length = 1; } ; for_expr: var_expr | comma_expr | constant { FREE_SVALUE($1); $$.node.p = 0; $$.length = 0; } | /* empty */ { $$.node.p = 0; $$.length = 0; } ; brac_expr: expression | comma_expr ; saveuse_rparen: ')' { $$ = stack_use; } else: YYF_ELSE { $$ = stack_use; } saveuse_semi: ';' { $$ = stack_use; } statement: expression ';' { union node nd; %line expression_statement: nd = $1.node; if ($1.vtype != TYPE_NIL) { if (nd.i & 3) { nd = 0; $1.length = 0; } else switch(nd.p->ntype) { case N_LV_BINOP: switch(nd.p->opr) { case ULV_INDEX: case ULV_RINDEX: case ULV_MAP_CINDEX: stack_use++; } case N_LV_UNARY: break; default: stack_use++; } } $$.node = nd; $$.length = $1.length; } | comma_expr ';' { /* FIXME: might need actual pops. */ goto expression_statement; } | ';' { $$.node.p = 0; $$.length = 0; } | block %// N_IF uses opr as a flag if expression return values should be popped. | YYF_IF '(' brac_expr saveuse_rparen statement { %line struct binode *n; int length, len1; n = ALLOC_NNODE(3); length = $5.length + (stack_adjust(0, $4) - (uint8 *)0); len1 = 2 + (length > 254) + (length > 32765); $$.length = $3.length + len1 + length; $$.node = n; n->ntype = N_IF; n->opr = 1 + (len1 << 1); n->node[0] = $3.node; n->node[1] = $5.node; n->node[2].p = 0; } | YYF_IF '(' brac_expr saveuse_rparen statement else statement { %line struct binode *n; int length, len1, len2; n = ALLOC_NNODE(3); length = $7.length += stack_adjust(0, $6) - (uint8 *)0; len2 = (length > 0 ? 2 : 0) + (length > 254) + (length > 32765); length = $5.length + (stack_adjust(0, $4) - (uint8 *)0) + len2; len1 = 2 + (length > 254) + (length > 32765); $$.length = $3.length + len1 + length + $7.length; $$.node = n; n->ntype = N_IF; n->opr = 1 + (len1 << 1) + (len2 << 4); n->node[0] = $3.node; n->node[1] = $5.node; n->node[2] = len2 ? $7.node : (union node)(struct binode *)0; } | YYF_FOR '(' for_expr ';' for_cond saveuse_semi for_expr ')' statement { %line struct binode *n1, *n2, *n3; int len1, len2, length; /* $7 can't be simply appended to $9 because it is continue destination. */ length = stack_adjust(0, $6) - (uint8 *)0; length += $7.length + $9.length; len1 = 2 + (length > 254) + (length > 32765); length += $5.length; len2 = 2 + (length > 255) + (length > 32767); n1 = alloc_node(); $$.node.p = n1; $$.length = $3.length + len1 + length + len2; n1->ntype = N_SEQUENCE; n1->node[0] = $3.node; n2 = alloc_node(); n1->node[1].p = n2; n2->ntype = N_SEQUENCE; n2->node[1].p = 0; n3 = ALLOC_NNODE(3); n2->node[0].p = n3; n3->ntype = N_FOR; n3->opr = len1 + (len2 << 3); n3->node[0] = $9.node; n3->node[1] = $7.node; n3->node[2] = $5.node; } | YYF_WHILE '(' brac_expr saveuse_rparen statement { %line struct binode *n; int len1, len2, length; n = ALLOC_NNODE(3); $$.node.p = n; length = stack_adjust(0, $4) - (uint8 *)0; length += $5.length; len1 = 2 + (length > 254) + (length > 32765); length += $3.length; len2 = 2 + (length > 255) + (length > 32767); $$.length = length + (length > 255 ? 8 : 4); n->ntype = N_FOR; n->opr = len1 + (len2 << 3); n->node[0] = $5.node; n->node[1].p = 0; n->node[2] = $3.node; } | YYF_DO statement YYF_WHILE '(' brac_expr ')' ';' { %line struct binode *n; int length; n = alloc_node(); $$.node.p = n; length = $2.length + $5.length; $$.length = length + (length > 255 ? 4 : 2); n->ntype = N_DO; n->node[0] = $2.node; n->node[1] = $5.node; } | YYF_SWITCH '(' brac_expr ')' { } statement { %line $$ = $6; /* FIXME */ } | YYF_DEFAULT ':' { struct binode *n = alloc_mnode(); n->ntype = N_CASE_LABEL; n->opr = CASE_DEFAULT; $$.node = n; $$.length = 0; } | local_declaration ';' { $$.node = $1.node; $$.length = $1.length; } | YYF_RETURN ';' { struct binode *n; union node v; %line n = alloc_mnode(); n->ntype = N_RETURN; v.leaf.type = LN_INT; v.leaf.n.s = 0; n->node[0] = v; $$.node.p = n; $$.length = 1; stack_use = INT_MIN; } | YYF_RETURN comma_expr ';' { goto return_expr; } | YYF_RETURN expression ';' { struct binode *n; union node v; %line return_expr: v = $2.node; n = alloc_mnode(); n->ntype = N_RETURN; n->node[0] = v; $$.node.p = n; $$.length = (v.leaf.type != LN_INT || v.leaf.n.s != 0) ? $2.length + 1 : 1; stack_use = INT_MIN; } ; %// statements should only be used from block %// The code generator uses tail recursion elemination for the second %// subnode of N_SEQUENCE, thus place the larger subtree there. %// Need empty start so that local_decalration finds the scope correctly. statements: {} statement { %line struct binode *n; n = alloc_node(); n->ntype = N_SEQUENCE; $<statement>-1.node = n; $<statement>-1.length = $2.length; n->node[0] = $2.node; $$ = &n->node[1]; } | statements statement { %line struct binode *n; n = alloc_node(); n->ntype = N_SEQUENCE; $1->p = n; $<statement>-1.length += $2.length; n->node[0] = $2.node; $$ = &n->node[1]; } ; block: '{' '}' { $$.node.leaf.type = LN_NIL; $$.length = 0; } | '{' { $<scope>$.link = &$<scope>2.start; } statements '}' { %line struct binode *decl, *nxt_decl; $3->p = 0; *$<scope>2.link = 0; for (decl = $<scope>2.start; decl; decl = nxt_decl) { struct ident *id = decl->node[1].id; if (id->u.local.offset >= 0) stack_use++; free_shared_identifier(id); nxt_decl = decl->node[0].p; free_tmpnode(decl); } $$ = $<statement>1; } ; inherit: type_modifier YYF_INHERIT | type_modifier YYF_IDENTIFIER inherit { %line #define SIZE_INDEX(v,i) *(int32*)((char *)&v + i) struct ident *p; unsigned n; $$ = $3; p = $2; n = ((char *)p - (char *)&inheritance_qualifiers[0]) / (sizeof *p/sizeof $1.var); if (n < sizeof $1) { SIZE_INDEX($$, n) |= SIZE_INDEX($1, n); if (SIZE_INDEX($<inherit_types>$[2], -n) & TYPE__EXCL_MASK) yyerrorn(CE_TMODMISMATCH); } else { yyerrorn(CE_SYNTAX); if (p->type == I_TYPE_UNKNOWN) { p->type = I_TYPE_LIMBO; p->next_all = identifier_limbo; identifier_limbo = p; } } } ; inheritance: inherit string ';' { struct ident *p; while (p = identifier_limbo) { identifier_limbo = p->next; free_shared_identifier(p); } }; program: | program inheritance | program global_var_declaration ';' | program function_signature ';' { free_param(); } | program function_signature block { %line struct function *f = $2; n_fun_def++; if (!f->inherited) n_undefined_lfuns--; f->new_def = 1; f->num_arg = n_param; f->num_local = local_preallocated; /* Reserve one byte for a F_RETURN0 if we need it. */ if (! N_IS_VOLATILE($3.node)) $3.length++; total_pcode += $3.length; f->block = $3; free_param(); local_preallocated = 0; stack_use = 0; } ; %% %line static int global_variable(struct ident *p) { int r; if (p->type != I_TYPE_GLOBAL || (r = p->u.global.variable) < 0) { /* variable not declared */ yyerrorn(CE_VARNDECL, make_string(p->name, p->namelen)); return -1; } return r; } int proxy_efun(int function, int num_arg) { return -1; } static void free_param() { int n; for (n = n_param; n--; ) { free_shared_identifier(PARAM(n).ident); } n_param = 0; }