as3: compile-time constants now work
[swftools.git] / lib / as3 / parser.y
index 67f800d..633f0bb 100644 (file)
@@ -191,9 +191,10 @@ extern int a3_lex();
 %type <code> INTERFACE_DECLARATION
 %type <code> VOIDEXPRESSION
 %type <value> EXPRESSION NONCOMMAEXPRESSION
-%type <value> MAYBEEXPRESSION
+%type <node> MAYBEEXPRESSION
 %type <value> DELETE
 %type <node> E COMMA_EXPRESSION
+%type <node> VAR_READ
 %type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY 
 %type <value> INNERFUNCTION
 %type <code> USE_NAMESPACE
@@ -217,7 +218,7 @@ extern int a3_lex();
 
 %type <classinfo> TYPE
 //%type <token> VARIABLE
-%type <value> VAR_READ MEMBER
+%type <value> MEMBER
 %type <value> NEW
 //%type <token> T_IDENTIFIER
 %type <value> FUNCTIONCALL
@@ -686,9 +687,13 @@ static int new_variable(const char*name, classinfo_t*type, char init, char maybe
 int gettempvar()
 {
     variable_t*v = find_variable(state, TEMPVARNAME);
+    int i;
     if(v) 
-        return v->index;
-    return new_variable(TEMPVARNAME, 0, 0, 0);
+        i = v->index;
+    else
+        i = new_variable(TEMPVARNAME, 0, 0, 0);
+    parserassert(i);
+    return i;
 }
 
 static code_t* var_block(code_t*body) 
@@ -899,8 +904,6 @@ static void function_initvars(methodstate_t*m, params_t*params, int flags, char
             index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
         else
             index = new_variable("globalscope", 0, 0, 0);
-        if(index)
-            *(int*)0=0;
         parserassert(!index);
     }
 
@@ -992,6 +995,7 @@ static void startclass(modifiers_t* mod, char*classname, classinfo_t*extends, cl
         state->cls = rfx_calloc(sizeof(classstate_t));
         state->cls->init = rfx_calloc(sizeof(methodstate_t));
         state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
+        state->cls->static_init->variable_count=1;
         /* notice: we make no effort to initialize the top variable (local0) here,
            even though it has special meaning. We just rely on the facat
            that pass 1 won't do anything with variables */
@@ -1255,7 +1259,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c
         // not sure wether to look into superclasses here, too
         minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
         if(minfo) {
-            if(minfo->kind!=INFOTYPE_SLOT)
+            if(minfo->kind!=INFOTYPE_VAR)
                 syntaxerror("class already contains a method called '%s'", name);
             if(!(minfo->subtype & (SUBTYPE_GETSET)))
                 syntaxerror("class already contains a field called '%s'", name);
@@ -1276,7 +1280,7 @@ static methodinfo_t*registerfunction(enum yytokentype getset, modifiers_t*mod, c
             }*/
         } else {
             minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
-            minfo->kind = INFOTYPE_SLOT; //hack
+            minfo->kind = INFOTYPE_VAR; //hack
             minfo->subtype = gs;
             minfo->return_type = 0;
         }
@@ -1967,10 +1971,8 @@ CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER {
     }
 };
 
-MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
-                |                {$$.c=abc_pushundefined(0);
-                                  $$.t=TYPE_ANY;
-                                 }
+MAYBEEXPRESSION : '=' E {$$=$2;}
+                |       {$$=mkdummynode();}
 
 VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
 VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
@@ -1987,11 +1989,6 @@ PASS1
     new_variable($1, 0, 1, 0);
 PASS2
    
-    if(!is_subtype_of($3.t, $2)) {
-        syntaxerror("Can't convert %s to %s", $3.t->name, 
-                                              $2->name);
-    }
-
     char slot = 0;
     int index = 0;
     if(state->method->uses_slots) {
@@ -2010,21 +2007,25 @@ PASS2
 
     $$ = slot?abc_getscopeobject(0, 1):0;
     
+    typedcode_t v = node_read($3);
+    if(!is_subtype_of(v.t, $2)) {
+        syntaxerror("Can't convert %s to %s", v.t->name, $2->name);
+    }
     if($2) {
-        if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
-            $$ = code_append($$, $3.c);
-            $$ = converttype($$, $3.t, $2);
+        if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
+            $$ = code_append($$, v.c);
+            $$ = converttype($$, v.t, $2);
         } else {
-            code_free($3.c);
+            code_free(v.c);
             $$ = defaultvalue($$, $2);
         }
     } else {
-        if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
-            $$ = code_append($$, $3.c);
+        if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
+            $$ = code_append($$, v.c);
             $$ = abc_coerce_a($$);
         } else {
             // don't do anything
-            code_free($3.c);
+            code_free(v.c);
             code_free($$);
             $$ = 0;
             break;
@@ -2634,20 +2635,29 @@ PASS12
             t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
         }
         info->slot = t->slot_id;
-        
-        /* initalization code (if needed) */
-        code_t*c = 0;
-        if($3.c && !is_pushundefined($3.c)) {
-            c = abc_getlocal_0(c);
-            c = code_append(c, $3.c);
-            c = converttype(c, $3.t, $2);
-            c = abc_setslot(c, t->slot_id);
+       
+        constant_t cval = $3->type->eval($3);
+        if(cval.type!=CONSTANT_UNKNOWN) {
+            /* compile time constant */
+            t->value = malloc(sizeof(constant_t));
+            memcpy(t->value, &cval, sizeof(constant_t));
+            info->value = constant_clone(t->value);
+        } else {
+            typedcode_t v = node_read($3);
+            /* initalization code (if needed) */
+            code_t*c = 0;
+            if(v.c && !is_pushundefined(v.c)) {
+                c = abc_getlocal_0(c);
+                c = code_append(c, v.c);
+                c = converttype(c, v.t, $2);
+                c = abc_setslot(c, t->slot_id);
+            }
+            *code = code_append(*code, c);
         }
 
-        *code = code_append(*code, c);
-
         if(slotstate_varconst==KW_CONST) {
             t->kind= TRAIT_CONST;
+            info->flags |= FLAG_CONST;
         }
     }
 
@@ -2657,7 +2667,12 @@ PASS12
 /* ------------ constants -------------------------------------- */
 
 MAYBECONSTANT: {$$=0;}
-MAYBECONSTANT: '=' CONSTANT {$$=$2;}
+MAYBECONSTANT: '=' E {
+  $$ = malloc(sizeof(constant_t));
+  *$$ = node_eval($2);
+  if($$->type == CONSTANT_UNKNOWN)
+    syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)");
+}
 
 //CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
 CONSTANT : T_INT {$$ = constant_new_int($1);}
@@ -2823,8 +2838,8 @@ CLASS_SPEC_LIST : CLASS_SPEC {PASS12 $$=list_new();list_append($$, $1);}
 CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
 
 TYPE : CLASS_SPEC {PASS12 $$=$1;}
-     | '*'        {PASS12 $$=registry_getanytype();}
-     | "void"     {PASS12 $$=registry_getanytype();}
+     | '*'        {PASS12 $$=TYPE_ANY;}
+     | "void"     {PASS12 $$=TYPE_ANY;}
     /*
      |  "String"  {$$=registry_getstringclass();}
      |  "int"     {$$=registry_getintclass();}
@@ -3032,11 +3047,11 @@ EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
 // ----------------------- expression evaluation -------------------------------------
 
 E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);}
-E : VAR_READ %prec T_IDENTIFIER   {$$ = mkcodenode($1);}
 E : MEMBER %prec '.'              {$$ = mkcodenode($1);}
 E : NEW                           {$$ = mkcodenode($1);}
 E : DELETE                        {$$ = mkcodenode($1);}
 E : FUNCTIONCALL                  {$$ = mkcodenode($1);}
+E : VAR_READ %prec T_IDENTIFIER   {$$ = $1;}
 
 E : CONSTANT { 
     $$ = mkconstnode($1);
@@ -3240,7 +3255,7 @@ MEMBER : E '.' T_IDENTIFIER {
         multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
         $$.c = abc_getproperty2($$.c, &m);
         $$.c = abc_coerce_a($$.c);
-        $$.t = registry_getanytype();
+        $$.t = TYPE_ANY;
     }
 }
 
@@ -3262,8 +3277,11 @@ VAR_READ : T_IDENTIFIER {
     as3_schedule_class_noerror(state->package, $1);
     PASS2
 
-    $$.t = 0;
-    $$.c = 0;
+    typedcode_t o;
+    o.t = 0;
+    o.c = 0;
+    $$ = 0;
+
     slotinfo_t*a = 0;
     memberinfo_t*f = 0;
 
@@ -3271,14 +3289,16 @@ VAR_READ : T_IDENTIFIER {
     /* look at variables */
     if((v = find_variable(state, $1))) {
         // $1 is a local variable
-        $$.c = abc_getlocal($$.c, v->index);
-        $$.t = v->type;
+        o.c = abc_getlocal(o.c, v->index);
+        o.t = v->type;
+        $$ = mkcodenode(o);
         break;
     }
     if((v = find_slot(state, $1))) {
-        $$.c = abc_getscopeobject($$.c, 1);
-        $$.c = abc_getslot($$.c, v->index);
-        $$.t = v->type;
+        o.c = abc_getscopeobject(o.c, 1);
+        o.c = abc_getslot(o.c, v->index);
+        o.t = v->type;
+        $$ = mkcodenode(o);
         break;
     }
 
@@ -3287,53 +3307,70 @@ VAR_READ : T_IDENTIFIER {
     /* look at current class' members */
     if(!state->method->inner && 
         state->cls && 
-        (f = findmember_nsset(state->cls->info, $1, 1)) &&
-        (f->flags&FLAG_STATIC) >= i_am_static) 
+        (f = findmember_nsset(state->cls->info, $1, 1)))
     {
-        // $1 is a function in this class
+        // $1 is a member or attribute in this class
         int var_is_static = (f->flags&FLAG_STATIC);
 
-        if(f->kind == INFOTYPE_METHOD) {
-            $$.t = TYPE_FUNCTION(f);
-        } else {
-            $$.t = f->type;
-        }
-        if(var_is_static && !i_am_static) {
-        /* access to a static member from a non-static location.
-           do this via findpropstrict:
-           there doesn't seem to be any non-lookup way to access
-           static properties of a class */
-            state->method->late_binding = 1;
-            $$.t = f->type;
-            namespace_t ns = {f->access, f->package};
-            multiname_t m = {QNAME, &ns, 0, $1};
-            $$.c = abc_findpropstrict2($$.c, &m);
-            $$.c = abc_getproperty2($$.c, &m);
-            break;
-        } else if(f->slot>0) {
-            $$.c = abc_getlocal_0($$.c);
-            $$.c = abc_getslot($$.c, f->slot);
-            break;
-        } else {
-            namespace_t ns = {f->access, f->package};
-            multiname_t m = {QNAME, &ns, 0, $1};
-            $$.c = abc_getlocal_0($$.c);
-            $$.c = abc_getproperty2($$.c, &m);
-            break;
+        if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
+            /* if the variable is a constant (and we know what is evaluates to), we
+               can just use the value itself */
+            varinfo_t*v = (varinfo_t*)f;
+            if(v->value) {
+                $$ = mkconstnode(v->value);
+                break;
+            }
+        } 
+       
+        if(var_is_static >= i_am_static) {
+            if(f->kind == INFOTYPE_METHOD) {
+                o.t = TYPE_FUNCTION(f);
+            } else {
+                o.t = f->type;
+            }
+
+            if(var_is_static && !i_am_static) {
+            /* access to a static member from a non-static location.
+               do this via findpropstrict:
+               there doesn't seem to be any non-lookup way to access
+               static properties of a class */
+                state->method->late_binding = 1;
+                o.t = f->type;
+                namespace_t ns = {f->access, f->package};
+                multiname_t m = {QNAME, &ns, 0, $1};
+                o.c = abc_findpropstrict2(o.c, &m);
+                o.c = abc_getproperty2(o.c, &m);
+                $$ = mkcodenode(o);
+                break;
+            } else if(f->slot>0) {
+                o.c = abc_getlocal_0(o.c);
+                o.c = abc_getslot(o.c, f->slot);
+                $$ = mkcodenode(o);
+                break;
+            } else {
+                namespace_t ns = {f->access, f->package};
+                multiname_t m = {QNAME, &ns, 0, $1};
+                o.c = abc_getlocal_0(o.c);
+                o.c = abc_getproperty2(o.c, &m);
+                $$ = mkcodenode(o);
+                break;
+            }
         }
     } 
     
     /* look at actual classes, in the current package and imported */
     if((a = find_class($1))) {
-        $$ = push_class(a);
+        o = push_class(a);
+        $$ = mkcodenode(o);
         break;
     }
 
     /* look through package prefixes */
     if(dict_contains(state->import_toplevel_packages, $1) || 
        registry_ispackage($1)) {
-        $$.c = abc___pushpackage__($$.c, $1);
-        $$.t = 0;
+        o.c = abc___pushpackage__(o.c, $1);
+        o.t = 0;
+        $$ = mkcodenode(o); //?
         break;
     }
 
@@ -3345,9 +3382,11 @@ VAR_READ : T_IDENTIFIER {
                 
         multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
 
-        $$.t = 0;
-        $$.c = abc_findpropstrict2($$.c, &m);
-        $$.c = abc_getproperty2($$.c, &m);
+        o.t = 0;
+        o.c = abc_findpropstrict2(o.c, &m);
+        o.c = abc_getproperty2(o.c, &m);
+        $$ = mkcodenode(o);
+        break;
     }
 }
 
@@ -3409,7 +3448,7 @@ USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
             syntaxerror("Couldn't resolve namespace %s", $3->name);
     }
 
-    if(!s || s->kind != INFOTYPE_SLOT)
+    if(!s || s->kind != INFOTYPE_VAR)
         syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
     if(!s->value || !NS_TYPE(s->value->type))
         syntaxerror("%s.%s is not a namespace", $3->package, $3->name);