added typeof, void expressions
[swftools.git] / lib / as3 / parser.y
index f6ca060..7415c96 100644 (file)
 %token<token> KW_NEW "new"
 %token<token> KW_NATIVE
 %token<token> KW_FUNCTION "function"
+%token<token> KW_UNDEFINED "undefined"
 %token<token> KW_FOR "for"
 %token<token> KW_CLASS "class"
 %token<token> KW_CONST "const"
 %token<token> KW_SET "set"
+%token<token> KW_VOID "void"
 %token<token> KW_STATIC
 %token<token> KW_IMPORT "import"
 %token<token> KW_RETURN "return"
+%token<token> KW_TYPEOF "typeof"
 %token<token> KW_INTERFACE "interface"
 %token<token> KW_NULL "null"
 %token<token> KW_VAR "var"
 %token<token> KW_WHILE "while"
 %token<token> KW_NUMBER "Number"
 %token<token> KW_STRING "String"
+%token<token> KW_DELETE "delete"
 %token<token> KW_IF "if"
 %token<token> KW_ELSE  "else"
 %token<token> KW_BREAK   "break"
 %token<token> T_EQEQ "=="
 %token<token> T_EQEQEQ "==="
 %token<token> T_NE "!="
+%token<token> T_NEE "!=="
 %token<token> T_LE "<="
 %token<token> T_GE ">="
 %token<token> T_DIVBY "/=" 
 %token<token> T_SHL "<<"
 %token<token> T_USHR ">>>"
 %token<token> T_SHR ">>"
-%token<token> T_SEMICOLON ';'
-%token<token> T_STAR '*'
-%token<token> T_DOT '.'
 
 %type <id> X_IDENTIFIER PACKAGE
 %type <token> VARCONST
 %type <code> VOIDEXPRESSION
 %type <value> EXPRESSION NONCOMMAEXPRESSION
 %type <value> MAYBEEXPRESSION
-%type <value> E
+%type <value> E DELETE
 %type <value> CONSTANT
 %type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
 %type <token> USE_NAMESPACE
 %type <value_list> 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);
 }
 
@@ -997,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];
@@ -1092,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))
 
 %}
 
@@ -1102,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);}
@@ -1189,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;}
 
@@ -1402,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) ------- */
 
@@ -1483,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}
@@ -1579,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);
 }
@@ -1595,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;
                                 }
@@ -1636,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;
                    }
 
@@ -1664,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;
@@ -1703,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_bitxor($$.c);
+             $$.t = TYPE_INT;
+            }
+
+E : E '|' E {$$.c = code_append($1.c,$3.c);
+             $$.c = abc_bitor($$.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;
+            }
+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);$$.c=abc_coerce_a($$.c);
-             $$.t = join_types($1.t, $3.t, '%');
+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);$$.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);
+             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
-E : E "is" E
-E : '(' E ')' {$$=$2;}
-E : '-' E {$$=$2;}
+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;
@@ -1726,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);
@@ -1739,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);
@@ -1799,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;
@@ -1827,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);
@@ -1841,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);
@@ -1857,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) {
@@ -1946,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};