X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fas3%2Fparser.y;h=46ed6d2e8d47dbd999a31fb7a1349bf7c3e0dd6e;hb=26d27dff034cba4973ce545af4474fbe7e0e1866;hp=d2188ef3adca462869ab324366483baa6c335e20;hpb=ebda861534f85f6eb2d3b9c1346ee349520c91a5;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index d2188ef..46ed6d2 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -60,12 +60,13 @@ for_start_t for_start; abc_exception_t *exception; abc_exception_list_t *exception_list; + regexp_t regexp; } %token T_IDENTIFIER %token T_STRING -%token T_REGEXP +%token T_REGEXP %token T_EMPTY %token T_INT %token T_UINT @@ -137,6 +138,7 @@ %token T_NEE "!==" %token T_LE "<=" %token T_GE ">=" +%token T_ORBY "|=" %token T_DIVBY "/=" %token T_MODBY "%=" %token T_MULBY "*=" @@ -213,8 +215,8 @@ %right '?' ':' %left "||" %left "&&" -%nonassoc '|' -%nonassoc '^' +%left '|' +%left '^' %nonassoc '&' %nonassoc "==" "!=" "===" "!==" %nonassoc "is" "as" "in" @@ -289,9 +291,6 @@ typedef struct _methodstate { /* method data */ memberinfo_t*info; char late_binding; - /* code that needs to be executed at the start of - a method (like initializing local registers) */ - code_t*initcode; char is_constructor; char has_super; char is_global; @@ -301,7 +300,7 @@ typedef struct _methodstate { typedef struct _state { struct _state*old; int level; - + char*package; import_list_t*wildcard_imports; dict_t*imports; @@ -336,8 +335,7 @@ DECLARE_LIST(state); multiname_t m;\ namespace_t m##_ns;\ if(f) { \ - m##_ns.access = flags2access(f->flags); \ - m##_ns.name = ""; \ + m##_ns = flags2namespace(f->flags, ""); \ m.type = QNAME; \ m.ns = &m##_ns; \ m.namespace_set = 0; \ @@ -420,11 +418,8 @@ static void old_state() if(!state || !state->old) syntaxerror("invalid nesting"); state_t*leaving = state; + state = state->old; - /*if(state->method->initcode) { - printf("residual initcode\n"); - code_dump(state->method->initcode, 0, 0, "", stdout); - }*/ state_destroy(leaving); } void initialize_state() @@ -489,12 +484,121 @@ void* finalize_state() __ callpropvoid(m, "[package]::trace", 1); __ returnvoid(m); - state_destroy(state); + state_destroy(state);state=0; return global->file; } +typedef struct _variable { + int index; + classinfo_t*type; + char init; +} variable_t; + +static variable_t* find_variable(char*name) +{ + state_t* s = state; + while(s) { + variable_t*v = 0; + if(s->method) + v = dict_lookup(s->vars, name); + if(v) { + return v; + } + s = s->old; + } + return 0; +} +static variable_t* find_variable_safe(char*name) +{ + variable_t* v = find_variable(name); + if(!v) + syntaxerror("undefined variable: %s", name); + return v; +} +static char variable_exists(char*name) +{ + return dict_lookup(state->vars, name)!=0; +} +code_t*defaultvalue(code_t*c, classinfo_t*type); +static int new_variable(char*name, classinfo_t*type, char init) +{ + NEW(variable_t, v); + v->index = global->variable_count; + v->type = type; + v->init = init; + + dict_put(state->vars, name, v); + + return global->variable_count++; +} +#define TEMPVARNAME "__as3_temp__" +static int gettempvar() +{ + variable_t*v = find_variable(TEMPVARNAME); + if(v) + return v->index; + return new_variable(TEMPVARNAME, 0, 0); +} + +code_t* var_block(code_t*body) +{ + code_t*c = 0; + code_t*k = 0; + int t; + int num=0; + for(t=0;tvars->hashsize;t++) { + dictentry_t*e = state->vars->slots[t]; + while(e) { + variable_t*v = (variable_t*)e->data; + if(v->type && v->init) { + c = defaultvalue(c, v->type); + c = abc_setlocal(c, v->index); + k = abc_kill(k, v->index); + num++; + } + e = e->next; + } + } + + if(k) { + code_t*x = body; + while(x) { + if(x->opcode== OPCODE___BREAK__ || + x->opcode== OPCODE___CONTINUE__) { + /* link kill code before break/continue */ + code_t*e = code_dup(k); + code_t*s = code_start(e); + s->prev = x->prev; + if(x->prev) { + x->prev->next = s; + } + e->next = x; + x->prev = e; + } + x = x->prev; + } + } + + c = code_append(c, body); + c = code_append(c, k); + return c; +} + +static code_t* wrap_function(code_t*c,code_t*header, code_t*body) +{ + c = code_append(c, header); + c = code_append(c, var_block(body)); + /* append return if necessary */ + if(!c || c->opcode != OPCODE_RETURNVOID && + c->opcode != OPCODE_RETURNVALUE) { + c = abc_returnvoid(c); + } + return c; +} + + static void startpackage(char*name) { new_state(); @@ -521,24 +625,15 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo new_state(); global->variable_count = 1; state->cls = rfx_calloc(sizeof(classstate_t)); + state->method = rfx_calloc(sizeof(methodstate_t)); // method state, for static constructor token_list_t*t=0; classinfo_list_t*mlist=0; - /*printf("entering class %s\n", name); - printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token);printf("\n"); - if(extends) - printf(" extends: %s.%s\n", extends->package, extends->name); - printf(" implements (%d): ", list_length(implements)); - for(mlist=implements;mlist;mlist=mlist->next) { - printf("%s ", mlist->classinfo?mlist->classinfo->name:0); - } - printf("\n"); - */ - if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC)) + if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC)) syntaxerror("invalid modifier(s)"); - if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL)) + if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) syntaxerror("public and internal not supported at the same time."); /* create the class name, together with the proper attributes */ @@ -653,18 +748,6 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo multiname_destroy(extends2); } -static code_t* wrap_function(code_t*c,code_t*initcode, code_t*body) -{ - c = code_append(c, initcode); - c = code_append(c, body); - /* append return if necessary */ - if(!c || c->opcode != OPCODE_RETURNVOID && - c->opcode != OPCODE_RETURNVALUE) { - c = abc_returnvoid(c); - } - return c; -} - static void endclass() { if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) { @@ -673,6 +756,13 @@ static void endclass() c = abc_constructsuper(c, 0); state->cls->init = code_append(state->cls->init, c); } + if(!state->method->late_binding) { + // class initialization code uses late binding + code_t*c = 0; + c = abc_getlocal_0(c); + c = abc_pushscope(c); + state->cls->static_init = code_append(c, state->cls->static_init); + } if(state->cls->init) { abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0); @@ -681,93 +771,13 @@ static void endclass() if(state->cls->static_init) { abc_method_t*m = abc_class_getstaticconstructor(state->cls->abc, 0); m->body->code = wrap_function(0, state->cls->static_init, m->body->code); - } else { - // handy for scope testing - /*code_t*c = 0; - c = abc_pop(c); - c = abc_pop(c); - abc_class_getstaticconstructor(state->cls->abc,0)->body->code = c;*/ } free(state->cls);state->cls=0; + free(state->method);state->method=0; old_state(); } -typedef struct _variable { - int index; - classinfo_t*type; -} variable_t; - -static variable_t* find_variable(char*name) -{ - state_t* s = state; - while(s) { - variable_t*v = 0; - if(s->method) - v = dict_lookup(s->vars, name); - if(v) { - return v; - } - s = s->old; - } - return 0; -} -static variable_t* find_variable_safe(char*name) -{ - variable_t* v = find_variable(name); - if(!v) - syntaxerror("undefined variable: %s", name); - return v; -} -static char variable_exists(char*name) -{ - return dict_lookup(state->vars, name)!=0; -} -code_t*defaultvalue(code_t*c, classinfo_t*type); -static int new_variable(char*name, classinfo_t*type, char init) -{ - NEW(variable_t, v); - v->index = global->variable_count; - v->type = type; - - dict_put(state->vars, name, v); - - if(init && state->method && type) { - /* if this is a typed variable: - push default value for type on stack at the very beginning of the - method, so that it always has that type regardless of the control - path */ - state->method->initcode = defaultvalue(state->method->initcode, type); - state->method->initcode = abc_setlocal(state->method->initcode, v->index); - } - return global->variable_count++; -} -#define TEMPVARNAME "__as3_temp__" -static int gettempvar() -{ - variable_t*v = find_variable(TEMPVARNAME); - if(v) - return v->index; - return new_variable(TEMPVARNAME, 0, 0); -} - -code_t* killvars(code_t*c) -{ - int t; - for(t=0;tvars->hashsize;t++) { - dictentry_t*e =state->vars->slots[t]; - while(e) { - variable_t*v = (variable_t*)e->data; - //do this always, otherwise register types don't match - //in the verifier when doing nested loops - //if(!TYPE_IS_BUILTIN_SIMPLE(type)) { - c = abc_kill(c, v->index); - e = e->next; - } - } - return c; -} - void check_code_for_break(code_t*c) { while(c) { @@ -802,17 +812,21 @@ static void check_constant_against_type(classinfo_t*t, constant_t*c) } } + static int flags2access(int flags) { int access = 0; if(flags&FLAG_PUBLIC) { - if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); access = ACCESS_PACKAGE; } else if(flags&FLAG_PRIVATE) { - if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); access = ACCESS_PRIVATE; } else if(flags&FLAG_PROTECTED) { - if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); + if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); access = ACCESS_PROTECTED; } else { access = ACCESS_PACKAGEINTERNAL; @@ -820,6 +834,7 @@ static int flags2access(int flags) return access; } + static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot) { memberinfo_t*minfo = 0; @@ -876,7 +891,7 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC; if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE; if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED; - if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL; + if(flags&FLAG_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL; if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE; return minfo; } @@ -884,14 +899,14 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type) { - if(state->method) { + if(state->method && state->method->info) { syntaxerror("not able to start another method scope"); } new_state(); global->variable_count = 0; state->method = rfx_calloc(sizeof(methodstate_t)); - state->method->initcode = 0; state->method->has_super = 0; + if(state->cls) { state->method->is_constructor = !strcmp(state->cls->info->name,name); state->cls->has_constructor |= state->method->is_constructor; @@ -925,7 +940,7 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam if(state->method->is_constructor) { f = abc_class_getconstructor(state->cls->abc, type2); } else if(!state->method->is_global) { - namespace_t mname_ns = {flags2access(flags), ""}; + namespace_t mname_ns = flags2namespace(flags, ""); multiname_t mname = {QNAME, &mname_ns, 0, name}; if(flags&FLAG_STATIC) @@ -934,7 +949,7 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam f = abc_class_method(state->cls->abc, type2, &mname); slot = f->trait->slot_id; } else { - namespace_t mname_ns = {flags2access(flags), state->package}; + namespace_t mname_ns = flags2namespace(flags, state->package); multiname_t mname = {QNAME, &mname_ns, 0, name}; f = abc_method_new(global->file, type2, 1); @@ -1012,12 +1027,25 @@ void continuejumpsto(code_t*c, char*name, code_t*jump) } } +#define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a))) +#define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a))) +#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b)) + classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op) { if(!type1 || !type2) return registry_getanytype(); if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2)) return registry_getanytype(); + + if(op=='+') { + if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) { + return TYPE_NUMBER; + } else { + return TYPE_ANY; + } + } + if(type1 == type2) return type1; return registry_getanytype(); @@ -1077,8 +1105,12 @@ code_t*defaultvalue(code_t*c, classinfo_t*type) c = abc_pushnan(c); } else if(TYPE_IS_BOOLEAN(type)) { c = abc_pushfalse(c); + } else if(!type) { + //c = abc_pushundefined(c); } else { c = abc_pushnull(c); + MULTINAME(m, type); + c = abc_coerce2(c, &m); } return c; } @@ -1131,6 +1163,26 @@ static classinfo_t* find_class(char*name) return 0; } +static char is_getlocal(code_t*c) +{ + if(!c || c->prev || c->next) + return 0; + return(c->opcode == OPCODE_GETLOCAL + || c->opcode == OPCODE_GETLOCAL_0 + || c->opcode == OPCODE_GETLOCAL_1 + || c->opcode == OPCODE_GETLOCAL_2 + || c->opcode == OPCODE_GETLOCAL_3); +} +static int getlocalnr(code_t*c) +{ + if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];} + else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;} + else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;} + else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;} + else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;} + else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode); +} + static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore) { /* converts this: @@ -1277,8 +1329,6 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r return c; } -#define IS_INT(a) (TYPE_IS_INT((a).t) || TYPE_IS_UINT((a).t)) -#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b)) %} @@ -1412,6 +1462,7 @@ MAYBEELSE: "else" CODEBLOCK {$$=$2;} //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;} IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { + $$ = code_new(); $$ = code_append($$, $4.c); code_t*myjmp,*myif = $$ = abc_iffalse($$, 0); @@ -1425,8 +1476,8 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { $$ = code_append($$, $7); myjmp->branch = $$ = abc_nop($$); } - - $$ = killvars($$);old_state(); + $$ = var_block($$); + old_state(); } FOR_INIT : {$$=code_new();} @@ -1458,7 +1509,8 @@ FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { continuejumpsto($$, $1.name, cont); myif->branch = out; - $$ = killvars($$);old_state(); + $$ = var_block($$); + old_state(); } FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { @@ -1495,16 +1547,17 @@ FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1.name, out); continuejumpsto($$, $1.name, loopstart); - $$ = killvars($$); - myif->branch = out; + $$ = var_block($$); old_state(); + free(tmp1name); free(tmp2name); } WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { + $$ = code_new(); code_t*myjmp = $$ = abc_jump($$, 0); @@ -1518,7 +1571,7 @@ WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + $$ = var_block($$); old_state(); } @@ -1532,7 +1585,8 @@ DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { code_t*out = $$ = abc_nop($$); breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + + $$ = var_block($$); old_state(); } @@ -1592,6 +1646,8 @@ SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' { } c=c->prev; } + + $$ = var_block($$); old_state(); } @@ -1602,34 +1658,36 @@ MAYBE_FINALLY: | FINALLY CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);} '{' CODE '}' { - code_t*c = 0; - int i = find_variable_safe($3)->index; - c = abc_setlocal(c, i); - c = code_append(c, $8); - c = abc_kill(c, i); - namespace_t name_ns = {ACCESS_PACKAGE, ""}; multiname_t name = {QNAME, &name_ns, 0, $3}; - + NEW(abc_exception_t, e) e->exc_type = sig2mname($4); e->var_name = multiname_clone(&name); - e->target = code_start(c); $$ = e; + + code_t*c = 0; + int i = find_variable_safe($3)->index; + e->target = c = abc_setlocal(0, i); + c = code_append(c, $8); + c = abc_kill(c, i); + + c = var_block(c); old_state(); + } CATCH_LIST: CATCH {$$=list_new();list_append($$,$1);} CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$,$2);} -TRY : "try" '{' CODE '}' CATCH_LIST MAYBE_FINALLY { - code_t*start = code_start($3); - $$=$3; +TRY : "try" '{' {new_state();} CODE '}' CATCH_LIST MAYBE_FINALLY { + code_t*start = code_start($4); + $$=$4; code_t*out = abc_nop(0); code_t*jmp = $$ = abc_jump($$, out); - abc_exception_list_t*l = $5; + abc_exception_list_t*l = $6; while(l) { abc_exception_t*e = l->abc_exception; e->from = start; @@ -1641,7 +1699,10 @@ TRY : "try" '{' CODE '}' CATCH_LIST MAYBE_FINALLY { $$ = code_append($$, out); jmp->branch = out; - list_concat(state->method->exceptions, $5); + list_concat(state->method->exceptions, $6); + + $$ = var_block($$); + old_state(); } /* ------------ throw ------------------------------- */ @@ -1710,7 +1771,7 @@ MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;} | KW_FINAL {$$=FLAG_FINAL;} | KW_OVERRIDE {$$=FLAG_OVERRIDE;} | KW_NATIVE {$$=FLAG_NATIVE;} - | KW_INTERNAL {$$=FLAG_INTERNAL;} + | KW_INTERNAL {$$=FLAG_PACKAGEINTERNAL;} EXTENDS : {$$=registry_getobjectclass();} EXTENDS : KW_EXTENDS QNAME {$$=$2;} @@ -1759,7 +1820,7 @@ IDECLARATION : "var" T_IDENTIFIER { } IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE { $1 |= FLAG_PUBLIC; - if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) { + if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) { syntaxerror("invalid method modifiers: interface methods always need to be public"); } startfunction(0,$1,$3,$4,&$6,$8); @@ -1902,7 +1963,7 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P c = abc_getlocal_0(c); c = abc_constructsuper(c, 0); } - c = wrap_function(c, state->method->initcode, $11); + c = wrap_function(c, 0, $11); endfunction(0,$1,$3,$4,&$6,$8,c); $$=0; } @@ -2094,13 +2155,31 @@ VOIDEXPRESSION : EXPRESSION %prec below_minus { // ----------------------- expression evaluation ------------------------------------- +//V : CONSTANT {$$ = 0;} E : CONSTANT +//V : VAR_READ %prec T_IDENTIFIER {$$ = 0;} E : VAR_READ %prec T_IDENTIFIER {$$ = $1;} +//V : NEW {$$ = $1.c;} E : NEW {$$ = $1;} +//V : DELETE {$$ = $1.c;} E : DELETE {$$ = $1;} -E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */ - $$.t = TYPE_ANY; - } + +E : T_REGEXP { + $$.c = 0; + namespace_t ns = {ACCESS_PACKAGE, ""}; + multiname_t m = {QNAME, &ns, 0, "RegExp"}; + if(!$1.options) { + $$.c = abc_getlex2($$.c, &m); + $$.c = abc_pushstring($$.c, $1.pattern); + $$.c = abc_construct($$.c, 1); + } else { + $$.c = abc_getlex2($$.c, &m); + $$.c = abc_pushstring($$.c, $1.pattern); + $$.c = abc_pushstring($$.c, $1.options); + $$.c = abc_construct($$.c, 2); + } + $$.t = TYPE_REGEXP; +} CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1); //MULTINAME(m, registry_getintclass()); @@ -2215,15 +2294,6 @@ E : E '|' E {$$.c = code_append($1.c,$3.c); $$.t = TYPE_INT; } -E : E '-' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1,$3)) { - $$.c = abc_subtract_i($$.c); - $$.t = TYPE_INT; - } else { - $$.c = abc_subtract($$.c); - $$.t = TYPE_NUMBER; - } - } E : E ">>" E {$$.c = code_append($1.c,$3.c); $$.c = abc_rshift($$.c); $$.t = TYPE_INT; @@ -2241,16 +2311,30 @@ E : E '/' E {$$.c = code_append($1.c,$3.c); $$.c = abc_divide($$.c); $$.t = TYPE_NUMBER; } -E : E '+' E {$$.c = code_append($1.c,$3.c); - $$.c = abc_add($$.c); - $$.t = TYPE_NUMBER; - } E : E '%' E {$$.c = code_append($1.c,$3.c); $$.c = abc_modulo($$.c); $$.t = TYPE_NUMBER; } +E : E '+' E {$$.c = code_append($1.c,$3.c); + if(BOTH_INT($1.t, $3.t)) { + $$.c = abc_add_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c = abc_add($$.c); + $$.t = join_types($1.t,$3.t,'+'); + } + } +E : E '-' E {$$.c = code_append($1.c,$3.c); + if(BOTH_INT($1.t,$3.t)) { + $$.c = abc_subtract_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c = abc_subtract($$.c); + $$.t = TYPE_NUMBER; + } + } E : E '*' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1,$3)) { + if(BOTH_INT($1.t,$3.t)) { $$.c = abc_multiply_i($$.c); $$.t = TYPE_INT; } else { @@ -2307,7 +2391,7 @@ E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too E : '-' E { $$=$2; - if(IS_INT($2)) { + if(IS_INT($2.t)) { $$.c=abc_negate_i($$.c); $$.t = TYPE_INT; } else { @@ -2359,7 +2443,7 @@ E : '{' MAYBE_EXPRPAIR_LIST '}' { E : E "*=" E { code_t*c = $3.c; - if(BOTH_INT($1,$3)) { + if(BOTH_INT($1.t,$3.t)) { c=abc_multiply_i(c); } else { c=abc_multiply(c); @@ -2399,25 +2483,32 @@ E : E "/=" E { $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; } +E : E "|=" E { + code_t*c = abc_bitor($3.c); + c=converttype(c, TYPE_INT, $1.t); + $$.c = toreadwrite($1.c, c, 0, 0); + $$.t = $1.t; + } E : E "+=" E { code_t*c = $3.c; - if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) { + + if(TYPE_IS_INT($1.t)) { c=abc_add_i(c); } else { c=abc_add(c); + c=converttype(c, join_types($1.t, $3.t, '+'), $1.t); } - c=converttype(c, join_types($1.t, $3.t, '+'), $1.t); $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; } E : E "-=" E { code_t*c = $3.c; - if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) { + if(TYPE_IS_INT($1.t)) { c=abc_subtract_i(c); } else { c=abc_subtract(c); + c=converttype(c, join_types($1.t, $3.t, '-'), $1.t); } - c=converttype(c, join_types($1.t, $3.t, '-'), $1.t); $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; @@ -2430,30 +2521,45 @@ E : E '=' E { code_t*c = 0; } E : E '?' E ':' E %prec below_assignment { + $$.t = join_types($3.t,$5.t,'?'); $$.c = $1.c; code_t*j1 = $$.c = abc_iffalse($$.c, 0); $$.c = code_append($$.c, $3.c); + $$.c = converttype($$.c, $3.t, $$.t); code_t*j2 = $$.c = abc_jump($$.c, 0); $$.c = j1->branch = abc_label($$.c); $$.c = code_append($$.c, $5.c); + $$.c = converttype($$.c, $3.t, $$.t); $$.c = j2->branch = abc_label($$.c); - $$.t = join_types($3.t,$5.t,'?'); } -// TODO: use inclocal where appropriate E : E "++" { code_t*c = 0; classinfo_t*type = $1.t; - if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { - c=abc_increment_i(c); - type = TYPE_INT; + if(is_getlocal($1.c) && TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t)) { + int nr = getlocalnr($1.c); + code_free($1.c);$1.c=0; + if(TYPE_IS_INT($1.t)) { + $$.c = abc_getlocal(0, nr); + $$.c = abc_inclocal_i($$.c, nr); + } else if(TYPE_IS_NUMBER($1.t)) { + $$.c = abc_getlocal(0, nr); + $$.c = abc_inclocal($$.c, nr); + } else syntaxerror("internal error"); } else { - c=abc_increment(c); - type = TYPE_NUMBER; + if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { + c=abc_increment_i(c); + type = TYPE_INT; + } else { + c=abc_increment(c); + type = TYPE_NUMBER; + } + c=converttype(c, type, $1.t); + $$.c = toreadwrite($1.c, c, 0, 1); + $$.t = $1.t; } - c=converttype(c, type, $1.t); - $$.c = toreadwrite($1.c, c, 0, 1); - $$.t = $1.t; } + +// TODO: use inclocal, like with ++ E : E "--" { code_t*c = 0; classinfo_t*type = $1.t; if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { @@ -2503,7 +2609,7 @@ E : "super" '.' T_IDENTIFIER if(!t) t = TYPE_OBJECT; memberinfo_t*f = registry_findmember(t, $3, 1); - namespace_t ns = {flags2access(f->flags), ""}; + namespace_t ns = flags2namespace(f->flags, ""); MEMBER_MULTINAME(m, f, $3); $$.c = 0; $$.c = abc_getlocal_0($$.c); @@ -2595,7 +2701,11 @@ VAR_READ : T_IDENTIFIER { MULTINAME(m, a); $$.c = abc_findpropstrict2($$.c, &m); $$.c = abc_getproperty2($$.c, &m); - $$.t = TYPE_FUNCTION(a->function); + if(a->function->kind == MEMBER_METHOD) { + $$.t = TYPE_FUNCTION(a->function); + } else { + $$.t = a->function->type; + } } else { if(a->slot) { $$.c = abc_getglobalscope($$.c);