X-Git-Url: http://git.asbjorn.it/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=7415c961ea3921b317a9237edcd21d99d71b3bcf;hb=631dcf0673b8eadb969dd418613002261953a456;hp=08900d19f4a9d0ffb3664930c1c88bb40e5be289;hpb=4d69b9f6d19e31a9f9215b1f127dc56b24c559f5;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index 08900d1..7415c96 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -80,13 +80,16 @@ %token KW_NEW "new" %token KW_NATIVE %token KW_FUNCTION "function" +%token KW_UNDEFINED "undefined" %token KW_FOR "for" %token KW_CLASS "class" %token KW_CONST "const" %token KW_SET "set" +%token KW_VOID "void" %token KW_STATIC %token KW_IMPORT "import" %token KW_RETURN "return" +%token KW_TYPEOF "typeof" %token KW_INTERFACE "interface" %token KW_NULL "null" %token KW_VAR "var" @@ -103,6 +106,7 @@ %token KW_WHILE "while" %token KW_NUMBER "Number" %token KW_STRING "String" +%token KW_DELETE "delete" %token KW_IF "if" %token KW_ELSE "else" %token KW_BREAK "break" @@ -112,6 +116,7 @@ %token T_EQEQ "==" %token T_EQEQEQ "===" %token T_NE "!=" +%token T_NEE "!==" %token T_LE "<=" %token T_GE ">=" %token T_DIVBY "/=" @@ -132,9 +137,6 @@ %token T_SHL "<<" %token T_USHR ">>>" %token T_SHR ">>" -%token T_SEMICOLON ';' -%token T_STAR '*' -%token T_DOT '.' %type X_IDENTIFIER PACKAGE %type VARCONST @@ -150,7 +152,7 @@ %type VOIDEXPRESSION %type EXPRESSION NONCOMMAEXPRESSION %type MAYBEEXPRESSION -%type E +%type E DELETE %type CONSTANT %type FOR IF WHILE MAYBEELSE BREAK RETURN %type USE_NAMESPACE @@ -180,45 +182,39 @@ %type MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES // precedence: from low to high -// http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000012.html %left prec_none + +%left below_semicolon +%left ';' +%left ',' +%nonassoc below_assignment // for ?:, contrary to spec +%right '=' "*=" "/=" "%=" "+=" "-=" "<<=" ">>=" ">>>=" "&=" "^=" "|=" %right '?' ':' -%right '=' "/=" "%=" "*=" "+=" "-=" ">>=" "<<=" ">>>=" %left "||" %left "&&" %nonassoc '|' %nonassoc '^' %nonassoc '&' -%nonassoc "!=" "==" "===" "<=" '<' ">=" '>' // TODO: support "a < b < c" syntax? -%nonassoc "is" -%left prec_belowminus -%left '-' -%left '+' -%left "<<" -%left ">>>" -%left ">>" -%left '%' -%left '/' -%left '*' -%left '!' -%left '~' -%left "--" "++" -%left '[' -%nonassoc "as" -%left '.' ".." "::" +%nonassoc "==" "!=" "===" "!==" +%nonassoc "is" "as" +%nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax? +%left "<<" ">>" ">>>" +%left below_minus +%left '-' '+' +%left '/' '*' '%' +%left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too +%left "--" "++" +%left '[' ']' '{' "new" '.' ".." "::" %nonassoc T_IDENTIFIER -%left below_semicolon -%left ';' +%left below_else %nonassoc "else" %left '(' // needed for "return" precedence: %nonassoc T_STRING T_REGEXP %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT -%nonassoc "new" "false" "true" "null" - -%left prec_highest +%nonassoc "false" "true" "null" "undefined" %{ @@ -903,6 +899,8 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to) } if(TYPE_IS_FUNCTION(from) && TYPE_IS_FUNCTION(to)) return c; + if(TYPE_IS_CLASS(from) && TYPE_IS_CLASS(to)) + return c; syntaxerror("can't convert type %s to %s", from->name, to->name); } @@ -928,6 +926,40 @@ void parserassert(int b) if(!b) syntaxerror("internal error: assertion failed"); } +static classinfo_t* find_class(char*name) +{ + classinfo_t*c=0; + + c = registry_findclass(state->package, name); + + /* try explicit imports */ + dictentry_t* e = dict_get_slot(state->imports, name); + while(e) { + if(c) + break; + if(!strcmp(e->key, name)) { + c = (classinfo_t*)e->data; + } + e = e->next; + } + + /* try package.* imports */ + import_list_t*l = state->wildcard_imports; + while(l) { + if(c) + break; + //printf("does package %s contain a class %s?\n", l->import->package, name); + c = registry_findclass(l->import->package, name); + l = l->next; + } + + /* try global package */ + if(!c) { + c = registry_findclass("", name); + } + return c; +} + static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore) { /* converts this: @@ -963,12 +995,26 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r write->opcode = OPCODE_SETPROPERTY; multiname_t*m = (multiname_t*)r->data[0]; write->data[0] = multiname_clone(m); - if(m->type != QNAME && m->type != MULTINAME) - syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)"); - if(!justassign) { - prefix = abc_dup(prefix); // we need the object, too + if(m->type == QNAME || m->type == MULTINAME) { + if(!justassign) { + prefix = abc_dup(prefix); // we need the object, too + } + use_temp_var = 1; + } else if(m->type == MULTINAMEL) { + if(!justassign) { + /* dupping two values on the stack requires 5 operations and one register- + couldn't adobe just have given us a dup2? */ + int temp = gettempvar(); + prefix = abc_setlocal(prefix, temp); + prefix = abc_dup(prefix); + prefix = abc_getlocal(prefix, temp); + prefix = abc_swap(prefix); + prefix = abc_getlocal(prefix, temp); + } + use_temp_var = 1; + } else { + syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)"); } - use_temp_var = 1; } else if(r->opcode == OPCODE_GETSLOT) { write->opcode = OPCODE_SETSLOT; write->data[0] = r->data[0]; @@ -1058,6 +1104,8 @@ 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)) %} @@ -1068,7 +1116,7 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r PROGRAM: MAYBECODE -MAYBECODE: CODE {$$=$1;} +MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/} MAYBECODE: {$$=code_new();} CODE: CODE CODEPIECE {$$=code_append($1,$2);} @@ -1155,7 +1203,7 @@ ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION /* ------------ control flow ------------------------- */ -MAYBEELSE: %prec prec_none {$$ = code_new();} +MAYBEELSE: %prec below_else {$$ = code_new();} MAYBEELSE: "else" CODEBLOCK {$$=$2;} //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;} @@ -1368,9 +1416,9 @@ STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);} STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);} STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);} //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);} -STATICCONSTANT : KW_TRUE {$$ = constant_new_true($1);} -STATICCONSTANT : KW_FALSE {$$ = constant_new_false($1);} -STATICCONSTANT : KW_NULL {$$ = constant_new_null($1);} +STATICCONSTANT : "true" {$$ = constant_new_true($1);} +STATICCONSTANT : "false" {$$ = constant_new_false($1);} +STATICCONSTANT : "null" {$$ = constant_new_null($1);} /* ------------ classes and interfaces (body, functions) ------- */ @@ -1432,34 +1480,7 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P CLASS: T_IDENTIFIER { /* try current package */ - $$ = registry_findclass(state->package, $1); - - /* try explicit imports */ - dictentry_t* e = dict_get_slot(state->imports, $1); - while(e) { - if($$) - break; - if(!strcmp(e->key, $1)) { - $$ = (classinfo_t*)e->data; - } - e = e->next; - } - - /* try package.* imports */ - import_list_t*l = state->wildcard_imports; - while(l) { - if($$) - break; - //printf("does package %s contain a class %s?\n", l->import->package, $1); - $$ = registry_findclass(l->import->package, $1); - l = l->next; - } - - /* try global package */ - if(!$$) { - $$ = registry_findclass("", $1); - } - + $$ = find_class($1); if(!$$) syntaxerror("Could not find class %s\n", $1); } @@ -1476,16 +1497,18 @@ QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);} TYPE : QNAME {$$=$1;} | '*' {$$=registry_getanytype();} + /* | "String" {$$=registry_getstringclass();} | "int" {$$=registry_getintclass();} | "uint" {$$=registry_getuintclass();} | "Boolean" {$$=registry_getbooleanclass();} | "Number" {$$=registry_getnumberclass();} + */ MAYBETYPE: ':' TYPE {$$=$2;} MAYBETYPE: {$$=0;} -/* ----------function calls, constructor calls ------ */ +/* ----------function calls, delete, constructor calls ------ */ MAYBE_PARAM_VALUES : %prec prec_none {$$=0;} MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2} @@ -1505,8 +1528,12 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { MULTINAME(m, $2); $$.c = code_new(); - /* TODO: why do we have to *find* our own classes? */ - $$.c = abc_findpropstrict2($$.c, &m); + if($2->slot) { + $$.c = abc_getglobalscope($$.c); + $$.c = abc_getslot($$.c, $2->slot); + } else { + $$.c = abc_findpropstrict2($$.c, &m); + } typedcode_list_t*l = $3; int len = 0; @@ -1515,7 +1542,10 @@ NEW : "new" CLASS MAYBE_PARAM_VALUES { l = l->next; len ++; } - $$.c = abc_constructprop2($$.c, &m, len); + if($2->slot) + $$.c = abc_construct($$.c, len); + else + $$.c = abc_constructprop2($$.c, &m, len); $$.t = $2; } @@ -1565,15 +1595,35 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' { memberinfo_t*f = 0; - if(TYPE_IS_FUNCTION($1.t) && - (f = registry_findmember($1.t, "call"))) { - $$.t = f->return_type; + if(TYPE_IS_FUNCTION($1.t) && $1.t->function) { + $$.t = $1.t->function->return_type; } else { $$.c = abc_coerce_a($$.c); $$.t = TYPE_ANY; } } +DELETE: "delete" E { + $$.c = $2.c; + if($$.c->opcode == OPCODE_COERCE_A) { + $$.c = code_cutlast($$.c); + } + multiname_t*name = 0; + if($$.c->opcode == OPCODE_GETPROPERTY) { + $$.c->opcode = OPCODE_DELETEPROPERTY; + } else if($$.c->opcode == OPCODE_GETSLOT) { + int slot = (int)(ptroff_t)$$.c->data[0]; + multiname_t*name = abc_class_find_slotid(state->cls,slot)->name; + $$.c = code_cutlast($$.c); + $$.c = abc_deleteproperty2($$.c, name); + } else { + $$.c = abc_getlocal_0($$.c); + MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, ""); + $$.c = abc_deleteproperty2($$.c, &m); + } + $$.t = TYPE_BOOLEAN; +} + RETURN: "return" %prec prec_none { $$ = abc_returnvoid(0); } @@ -1581,23 +1631,25 @@ RETURN: "return" EXPRESSION { $$ = $2.c; $$ = abc_returnvalue($$); } + // ----------------------- expression types ------------------------------------- -NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;} -EXPRESSION : E %prec prec_belowminus {$$ = $1;} -EXPRESSION : EXPRESSION ',' E %prec prec_belowminus { +NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;} +EXPRESSION : E %prec below_minus {$$ = $1;} +EXPRESSION : EXPRESSION ',' E %prec below_minus { $$.c = $1.c; $$.c = cut_last_push($$.c); $$.c = code_append($$.c,$3.c); $$.t = $3.t; } -VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=cut_last_push($1.c);} +VOIDEXPRESSION : EXPRESSION %prec below_minus {$$=cut_last_push($1.c);} // ----------------------- expression evaluation ------------------------------------- E : CONSTANT E : VAR_READ %prec T_IDENTIFIER {$$ = $1;} E : NEW {$$ = $1;} +E : DELETE {$$ = $1;} E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */ $$.t = TYPE_ANY; } @@ -1622,13 +1674,16 @@ CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1); CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1); $$.t = TYPE_STRING; } -CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0); +CONSTANT : "undefined" {$$.c = abc_pushundefined(0); + $$.t = TYPE_ANY; + } +CONSTANT : "true" {$$.c = abc_pushtrue(0); $$.t = TYPE_BOOLEAN; } -CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0); +CONSTANT : "false" {$$.c = abc_pushfalse(0); $$.t = TYPE_BOOLEAN; } -CONSTANT : KW_NULL {$$.c = abc_pushnull(0); +CONSTANT : "null" {$$.c = abc_pushnull(0); $$.t = TYPE_NULL; } @@ -1650,6 +1705,9 @@ E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c); } E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c); $$.t = TYPE_BOOLEAN; + } +E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c); + $$.t = TYPE_BOOLEAN; } E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c); $$.t = TYPE_BOOLEAN; @@ -1689,22 +1747,115 @@ E : '!' E {$$.c=$2.c; $$.t = TYPE_BOOLEAN; } -E : E '-' E -E : E '/' E -E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c); - $$.t = join_types($1.t, $3.t, '+'); +E : '~' E {$$.c=$2.c; + $$.c = abc_bitnot($$.c); + $$.t = TYPE_INT; + } + +E : E '&' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_bitand($$.c); + $$.t = TYPE_INT; } -E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c); - $$.t = join_types($1.t, $3.t, '%'); + +E : E '^' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_bitxor($$.c); + $$.t = TYPE_INT; } -E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c); - $$.t = join_types($1.t, $3.t, '*'); + +E : E '|' E {$$.c = code_append($1.c,$3.c); + $$.c = abc_bitor($$.c); + $$.t = TYPE_INT; } -E : E "as" E -E : E "is" E -E : '(' E ')' {$$=$2;} -E : '-' E {$$=$2;} +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; + } +E : E ">>>" E {$$.c = code_append($1.c,$3.c); + $$.c = abc_urshift($$.c); + $$.t = TYPE_INT; + } +E : E "<<" E {$$.c = code_append($1.c,$3.c); + $$.c = abc_lshift($$.c); + $$.t = TYPE_INT; + } + +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,$3)) { + $$.c = abc_multiply_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c = abc_multiply($$.c); + $$.t = TYPE_NUMBER; + } + } + +E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate + if(use_astype && TYPE_IS_CLASS($3.t)) { + MULTINAME(m,$3.t->cls); + $$.c = abc_astype2($1.c, &m); + $$.t = $3.t->cls; + } else { + $$.c = code_append($1.c, $3.c); + $$.c = abc_astypelate($$.c); + $$.t = TYPE_ANY; + } + } + +E : E "is" E {$$.c = code_append($1.c, $3.c); + $$.c = abc_istypelate($$.c); + $$.t = TYPE_BOOLEAN; + } + +E : "typeof" '(' E ')' { + $$.c = $3.c; + $$.c = abc_typeof($$.c); + $$.t = TYPE_STRING; + } + +E : "void" E { + $$.c = cut_last_push($2.c); + $$.c = abc_pushundefined($$.c); + $$.t = TYPE_ANY; + } + +E : "void" { $$.c = abc_pushundefined(0); + $$.t = TYPE_ANY; + } + +E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too + +E : '-' E { + $$=$2; + if(IS_INT($2)) { + $$.c=abc_negate_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c=abc_negate($$.c); + $$.t = TYPE_NUMBER; + } +} E : E '[' E ']' { $$.c = $1.c; @@ -1712,11 +1863,12 @@ E : E '[' E ']' { MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, ""); $$.c = abc_getproperty2($$.c, &m); + $$.t = 0; // array elements have unknown type } E : E "*=" E { code_t*c = $3.c; - if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) { + if(BOTH_INT($1,$3)) { c=abc_multiply_i(c); } else { c=abc_multiply(c); @@ -1725,6 +1877,7 @@ E : E "*=" E { $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; } + E : E "%=" E { code_t*c = abc_modulo($3.c); c=converttype(c, join_types($1.t, $3.t, '%'), $1.t); @@ -1785,6 +1938,17 @@ E : E '=' E { code_t*c = 0; $$.t = $1.t; } +E : E '?' E ':' E %prec below_assignment { + $$.c = $1.c; + code_t*j1 = $$.c = abc_iffalse($$.c, 0); + $$.c = code_append($$.c, $3.c); + code_t*j2 = $$.c = abc_jump($$.c, 0); + $$.c = j1->branch = abc_label($$.c); + $$.c = code_append($$.c, $5.c); + $$.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; @@ -1813,7 +1977,7 @@ E : E "--" { code_t*c = 0; $$.t = $1.t; } -E : "++" E { code_t*c = 0; +E : "++" %prec plusplus_prefix E { code_t*c = 0; classinfo_t*type = $2.t; if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { c=abc_increment_i(c); @@ -1827,7 +1991,7 @@ E : "++" E { code_t*c = 0; $$.t = $2.t; } -E : "--" E { code_t*c = 0; +E : "--" %prec minusminus_prefix E { code_t*c = 0; classinfo_t*type = $2.t; if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) { c=abc_decrement_i(c); @@ -1843,10 +2007,21 @@ E : "--" E { code_t*c = 0; E : E '.' T_IDENTIFIER {$$.c = $1.c; - if($$.t) { - memberinfo_t*f = registry_findmember($$.t, $3); + classinfo_t*t = $1.t; + char is_static = 0; + if(TYPE_IS_CLASS(t)) { + memberinfo_t*m = registry_findmember($1.t, "prototype"); + if(!m) syntaxerror("identifier '%s' not found in anonymous class", $3); + t = m->type; + is_static = 1; + } + if(t) { + memberinfo_t*f = registry_findmember(t, $3); + char noslot = 0; + if(f && !is_static != !(f->flags&FLAG_STATIC)) + noslot=1; - if(f && f->slot) { + if(f && f->slot && !noslot) { $$.c = abc_getslot($$.c, f->slot); } else { if(f) { @@ -1923,8 +2098,8 @@ VAR_READ : T_IDENTIFIER { $$.t = f->type; } - /* look at classes in the current package */ - } else if((a = registry_findclass(state->package, $1))) { + /* look at classes in the current package and imported classes */ + } else if((a = find_class($1))) { if(a->slot) { $$.c = abc_getglobalscope($$.c); $$.c = abc_getslot($$.c, a->slot); @@ -1932,14 +2107,12 @@ VAR_READ : T_IDENTIFIER { MULTINAME(m, a); $$.c = abc_getlex2($$.c, &m); } - /* this is not entirely correct (this is the class itself, - not an object of this class) */ - $$.t = a; + $$.t = TYPE_CLASS(a); /* unknown object, let the avm2 resolve it */ } else { if(strcmp($1,"trace")) - warning("Couldn't resolve %s, doing late binding", $1); + warning("Couldn't resolve '%s', doing late binding", $1); state->late_binding = 1; multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};