added implementation for dict-style objects
[swftools.git] / lib / as3 / parser.y
index 402e2d9..f119828 100644 (file)
 %token<number_uint> T_SHORT
 %token<number_float> T_FLOAT
 
+%token<id> T_FOR "for"
+%token<id> T_WHILE "while"
+%token<id> T_DO "do"
+
 %token<token> KW_IMPLEMENTS
 %token<token> KW_NAMESPACE "namespace"
 %token<token> KW_PACKAGE "package"
@@ -81,7 +85,7 @@
 %token<token> KW_NATIVE
 %token<token> KW_FUNCTION "function"
 %token<token> KW_UNDEFINED "undefined"
-%token<token> KW_FOR "for"
+%token<token> KW_CONTINUE "continue"
 %token<token> KW_CLASS "class"
 %token<token> KW_CONST "const"
 %token<token> KW_SET "set"
@@ -94,7 +98,7 @@
 %token<token> KW_INTERFACE "interface"
 %token<token> KW_NULL "null"
 %token<token> KW_VAR "var"
-%token<token> KW_DYNAMIC
+%token<token> KW_DYNAMIC "dynamic"
 %token<token> KW_OVERRIDE
 %token<token> KW_FINAL
 %token<token> KW_GET "get"
 %token<token> KW_BOOLEAN "Boolean"
 %token<token> KW_UINT "uint"
 %token<token> KW_INT "int"
-%token<token> KW_WHILE "while"
 %token<token> KW_NUMBER "Number"
 %token<token> KW_STRING "String"
 %token<token> KW_DELETE "delete"
 %type <value> MAYBEEXPRESSION
 %type <value> E DELETE
 %type <value> CONSTANT
-%type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
+%type <code> FOR IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE
 %type <token> USE_NAMESPACE
 %type <code> FOR_INIT
 %type <token> IMPORT
 //%type <token> T_IDENTIFIER
 %type <token> MODIFIER
 %type <value> FUNCTIONCALL
-%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
+%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST
 
 // precedence: from low to high
 
 %left '/' '*' '%'
 %left plusplus_prefix minusminus_prefix '~' '!' "void" "delete" "typeof" //FIXME: *unary* + - should be here, too
 %left "--" "++" 
+%nonassoc below_curly
 %left '[' ']' '{' "new" '.' ".." "::"
 %nonassoc T_IDENTIFIER
 %left below_else
 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
 %nonassoc "false" "true" "null" "undefined" "super"
 
+
      
 %{
 
@@ -250,6 +255,7 @@ typedef struct _classstate {
     abc_class_t*abc;
     code_t*init;
     code_t*static_init;
+    char has_constructor;
 } classstate_t;
 
 typedef struct _methodstate {
@@ -481,7 +487,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo
     printf("\n");
     */
 
-    if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL))
+    if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
         syntaxerror("invalid modifier(s)");
 
     if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL))
@@ -527,8 +533,13 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo
 
     state->cls->abc = abc_class_new(global->file, &classname2, extends2);
     if(flags&FLAG_FINAL) abc_class_final(state->cls->abc);
-    if(flags&FLAG_DYNAMIC) abc_class_sealed(state->cls->abc);
-    if(interface) abc_class_interface(state->cls->abc);
+    if(!(flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
+    if(interface) {
+        state->cls->info->flags |= CLASS_INTERFACE;
+        abc_class_interface(state->cls->abc);
+    }
+
+    abc_class_protectedNS(state->cls->abc, classname);
 
     for(mlist=implements;mlist;mlist=mlist->next) {
         MULTINAME(m, mlist->classinfo);
@@ -594,29 +605,40 @@ 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->init) {
-        if(!state->cls->abc->constructor) {
-            abc_method_t*m = abc_class_constructor(state->cls->abc, 0);
-            m->body->code = code_append(m->body->code, state->cls->init);
-            m->body->code = abc_returnvoid(m->body->code);
-        } else {
-            code_t*c = state->cls->abc->constructor->body->code;
-            c = code_append(state->cls->init, c);
-            state->cls->abc->constructor->body->code = c;
+    if(!state->cls->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) {
+        code_t*c = 0;
+        c = abc_getlocal_0(c);
+        c = abc_constructsuper(c, 0);
+        state->cls->init = code_append(state->cls->init, c);
+    }
 
-        }
+    if(state->cls->init) {
+        abc_method_t*m = abc_class_getconstructor(state->cls->abc, 0);
+        m->body->code = wrap_function(0, state->cls->init, m->body->code);
     }
     if(state->cls->static_init) {
-        if(!state->cls->abc->static_constructor) {
-            abc_method_t*m = abc_class_staticconstructor(state->cls->abc, 0);
-            m->body->code = code_append(m->body->code, state->cls->static_init);
-            m->body->code = abc_returnvoid(m->body->code);
-        } else {
-            state->cls->abc->static_constructor->body->code = 
-                code_append(state->cls->static_init, state->cls->abc->static_constructor->body->code);
-        }
+        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;*/
     }
 
     old_state();
@@ -690,6 +712,21 @@ code_t* killvars(code_t*c)
     return c;
 }
 
+void check_code_for_break(code_t*c)
+{
+    while(c) {
+        if(c->opcode == OPCODE___BREAK__) {
+            char*name = string_cstr(c->data[0]);
+            syntaxerror("Unresolved \"break %s\"", name);
+        }
+        if(c->opcode == OPCODE___CONTINUE__) {
+            char*name = string_cstr(c->data[0]);
+            syntaxerror("Unresolved \"continue %s\"", name);
+        }
+        c=c->prev;
+    }
+}
+
 
 static void check_constant_against_type(classinfo_t*t, constant_t*c)
 {
@@ -784,6 +821,8 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n
     state->method->initcode = 0;
     state->method->is_constructor = !strcmp(state->cls->info->name,name);
     state->method->has_super = 0;
+    
+    state->cls->has_constructor |= state->method->is_constructor;
 
     global->variable_count = 0;
 
@@ -809,7 +848,7 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam
     multiname_t*type2 = sig2mname(return_type);
     int slot = 0;
     if(state->method->is_constructor) {
-        f = abc_class_constructor(state->cls->abc, type2);
+        f = abc_class_getconstructor(state->cls->abc, type2);
     } else {
         if(flags&FLAG_STATIC)
             f = abc_class_staticmethod(state->cls->abc, type2, &mname);
@@ -839,7 +878,13 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam
             syntaxerror("non-optional parameter not allowed after optional parameters");
         }
     }
-    f->body->code = body;
+    check_code_for_break(body);
+
+    if(f->body)
+        f->body->code = body;
+    else //interface
+        if(body)
+            syntaxerror("interface methods can't have a method body");
         
     old_state();
 }
@@ -851,16 +896,30 @@ char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
     return 1; // FIXME
 }
 
-void breakjumpsto(code_t*c, code_t*jump) 
+void breakjumpsto(code_t*c, char*name, code_t*jump) 
 {
-    while(c->prev) 
-        c=c->prev;
     while(c) {
         if(c->opcode == OPCODE___BREAK__) {
-            c->opcode = OPCODE_JUMP;
-            c->branch = jump;
+            string_t*name2 = c->data[0];
+            if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+                c->opcode = OPCODE_JUMP;
+                c->branch = jump;
+            }
         }
-        c = c->next;
+        c=c->prev;
+    }
+}
+void continuejumpsto(code_t*c, char*name, code_t*jump) 
+{
+    while(c) {
+        if(c->opcode == OPCODE___CONTINUE__) {
+            string_t*name2 = c->data[0];
+            if(!name2->len || !strncmp(name2->str, name, name2->len)) {
+                c->opcode = OPCODE_JUMP;
+                c->branch = jump;
+            }
+        }
+        c = c->prev;
     }
 }
 
@@ -888,19 +947,13 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
         return abc_coerce2(c, &m);
     }
     
-    if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
-        return abc_coerce2(c, &m);
-    }
-    if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
-        return abc_coerce2(c, &m);
-    }
-    /* these are subject to overflow */
-    if(TYPE_IS_INT(from) && TYPE_IS_UINT(to)) {
-        return abc_coerce2(c, &m);
-    }
-    if(TYPE_IS_UINT(from) && TYPE_IS_INT(to)) {
+    if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
+       (TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
+        // allow conversion between number types
         return abc_coerce2(c, &m);
     }
+    //printf("%s.%s\n", from.package, from.name);
+    //printf("%s.%s\n", to.package, to.name);
 
     classinfo_t*supertype = from;
     while(supertype) {
@@ -911,7 +964,7 @@ code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
         int t=0;
         while(supertype->interfaces[t]) {
             if(supertype->interfaces[t]==to) {
-                // to type is one of from's interfaces
+                // target type is one of from's interfaces
                 return abc_coerce2(c, &m);
             }
             t++;
@@ -1142,10 +1195,14 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r
 PROGRAM: MAYBECODE
 
 MAYBECODE: CODE {$$=$1;/*TODO: do something with this code if we're not in a function*/}
-MAYBECODE:      {$$=code_new();}
+MAYBECODE: {$$=code_new();}
 
-CODE: CODE CODEPIECE {$$=code_append($1,$2);}
-CODE: CODEPIECE {$$=$1;}
+CODE: CODE CODEPIECE {
+    $$=code_append($1,$2);
+}
+CODE: CODEPIECE {
+    $$=$1;
+}
 
 CODEPIECE: PACKAGE_DECLARATION   {$$=code_new();/*enters a scope*/}
 CODEPIECE: CLASS_DECLARATION     {$$=code_new();/*enters a scope*/}
@@ -1157,13 +1214,16 @@ CODEPIECE: VARIABLE_DECLARATION  {$$=$1}
 CODEPIECE: VOIDEXPRESSION        {$$=$1}
 CODEPIECE: FOR                   {$$=$1}
 CODEPIECE: WHILE                 {$$=$1}
+CODEPIECE: DO_WHILE              {$$=$1}
 CODEPIECE: BREAK                 {$$=$1}
+CODEPIECE: CONTINUE              {$$=$1}
 CODEPIECE: RETURN                {$$=$1}
 CODEPIECE: IF                    {$$=$1}
 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
 CODEPIECE: USE_NAMESPACE         {/*TODO*/$$=code_new();}
 
-CODEBLOCK :  '{' MAYBECODE '}' {$$=$2;}
+CODEBLOCK :  '{' CODE '}' {$$=$2;}
+CODEBLOCK :  '{' '}'      {$$=0;}
 CODEBLOCK :  CODEPIECE ';'             {$$=$1;}
 CODEBLOCK :  CODEPIECE %prec below_semicolon {$$=$1;}
 
@@ -1241,10 +1301,10 @@ IF  : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
     if($7) {
         myjmp = $$ = abc_jump($$, 0);
     }
-    myif->branch = $$ = abc_label($$);
+    myif->branch = $$ = abc_nop($$);
     if($7) {
         $$ = code_append($$, $7);
-        myjmp->branch = $$ = abc_label($$);
+        myjmp->branch = $$ = abc_nop($$);
     }
     
     $$ = killvars($$);old_state();
@@ -1254,38 +1314,67 @@ FOR_INIT : {$$=code_new();}
 FOR_INIT : VARIABLE_DECLARATION
 FOR_INIT : VOIDEXPRESSION
 
-FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
+FOR : T_FOR '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
     $$ = code_new();
     $$ = code_append($$, $4);
     code_t*loopstart = $$ = abc_label($$);
     $$ = code_append($$, $6.c);
     code_t*myif = $$ = abc_iffalse($$, 0);
     $$ = code_append($$, $10);
+    code_t*cont = $$ = abc_nop($$);
     $$ = code_append($$, $8);
     $$ = abc_jump($$, loopstart);
-    code_t*out = $$ = abc_label($$);
-    breakjumpsto($$, out);
+    code_t*out = $$ = abc_nop($$);
+    breakjumpsto($$, $1, out);
+    continuejumpsto($$, $1, cont);
     myif->branch = out;
 
     $$ = killvars($$);old_state();
 }
 
-WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
+WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK {
     $$ = code_new();
+
     code_t*myjmp = $$ = abc_jump($$, 0);
     code_t*loopstart = $$ = abc_label($$);
     $$ = code_append($$, $6);
-    myjmp->branch = $$ = abc_label($$);
+    code_t*cont = $$ = abc_nop($$);
+    myjmp->branch = cont;
     $$ = code_append($$, $4.c);
     $$ = abc_iftrue($$, loopstart);
-    code_t*out = $$ = abc_label($$);
-    breakjumpsto($$, out);
+    code_t*out = $$ = abc_nop($$);
+    breakjumpsto($$, $1, out);
+    continuejumpsto($$, $1, cont);
 
-    $$ = killvars($$);old_state();
+    $$ = killvars($$);
+    old_state();
 }
 
-BREAK : "break" {
-    $$ = abc___break__(0);
+DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' {
+    $$ = code_new();
+    code_t*loopstart = $$ = abc_label($$);
+    $$ = code_append($$, $3);
+    code_t*cont = $$ = abc_nop($$);
+    $$ = code_append($$, $6.c);
+    $$ = abc_iftrue($$, loopstart);
+    code_t*out = $$ = abc_nop($$);
+    breakjumpsto($$, $1, out);
+    continuejumpsto($$, $1, cont);
+    $$ = killvars($$);
+    old_state();
+}
+
+BREAK : "break" %prec prec_none {
+    $$ = abc___break__(0, "");
+}
+BREAK : "break" T_IDENTIFIER {
+    $$ = abc___break__(0, $2);
+}
+CONTINUE : "continue" %prec prec_none {
+    $$ = abc___continue__(0, "");
+}
+CONTINUE : "continue" T_IDENTIFIER {
+    $$ = abc___continue__(0, $2);
 }
 
 /* ------------ packages and imports ---------------- */
@@ -1499,19 +1588,11 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P
         c = abc_pushscope(c);
     }
     if(state->method->is_constructor && !state->method->has_super) {
-        // generate default constructor
+        // call default constructor
         c = abc_getlocal_0(c);
         c = abc_constructsuper(c, 0);
     }
-
-    c = code_append(c, state->method->initcode);
-    c = code_append(c, $11);
-
-    /* append return if necessary */
-    if(!c || c->opcode != OPCODE_RETURNVOID && 
-             c->opcode != OPCODE_RETURNVALUE) {
-        c = abc_returnvoid(c);
-    }
+    c = wrap_function(c, state->method->initcode, $11);
     endfunction(0,$1,$3,$4,&$6,$8,c);
 }
 
@@ -1537,6 +1618,7 @@ QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
 
 TYPE : QNAME      {$$=$1;}
      | '*'        {$$=registry_getanytype();}
+     | "void"     {$$=registry_getanytype();}
     /*
      |  "String"  {$$=registry_getstringclass();}
      |  "int"     {$$=registry_getintclass();}
@@ -1619,8 +1701,7 @@ FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
         int slot = (int)(ptroff_t)$$.c->data[0];
         trait_t*t = abc_class_find_slotid(state->cls->abc,slot);//FIXME
         if(t->kind!=TRAIT_METHOD) {
-            //flash allows to assign closures to members.
-            //syntaxerror("not a function");
+            //ok: flash allows to assign closures to members.
         }
         name = t->name;
         $$.c = code_cutlast($$.c);
@@ -1941,6 +2022,48 @@ E : E '[' E ']' {
   $$.t = 0; // array elements have unknown type
 }
 
+E : '[' MAYBE_EXPRESSION_LIST ']' {
+    $$.c = code_new();
+    typedcode_list_t*l = 0;
+    int len = 0;
+    for(l=$2;l;l=l->next) {
+        $$.c = code_append($$.c, l->typedcode->c);len++;
+    }
+    $$.c = abc_newarray($$.c, len);
+    $$.t = registry_getarrayclass();
+}
+
+MAYBE_EXPRPAIR_LIST : {$$=0;}
+MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1};
+
+EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+    typedcode_t*t1 = malloc(sizeof(typedcode_t));*t1 = $1;
+    typedcode_t*t2 = malloc(sizeof(typedcode_t));*t2 = $3;
+    $$ = 0;
+    list_append($$, t1);
+    list_append($$, t2);
+}
+EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+    $$=$1;
+    typedcode_t*t1 = malloc(sizeof(typedcode_t));*t1 = $3;
+    typedcode_t*t2 = malloc(sizeof(typedcode_t));*t2 = $5;
+    list_append($$, t1);
+    list_append($$, t2);
+}
+//MAYBECOMMA: ','
+//MAYBECOMMA:
+
+E : '{' MAYBE_EXPRPAIR_LIST '}' {
+    $$.c = code_new();
+    typedcode_list_t*l = 0;
+    int len = 0;
+    for(l=$2;l;l=l->next) {
+        $$.c = code_append($$.c, l->typedcode->c);len++;
+    }
+    $$.c = abc_newobject($$.c, len/2);
+    $$.t = registry_getobjectclass();
+}
+
 E : E "*=" E { 
                code_t*c = $3.c;
                if(BOTH_INT($1,$3)) {
@@ -2174,14 +2297,21 @@ VAR_READ : T_IDENTIFIER {
     
     /* 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);
-        } else {
+        if(a->flags & FLAG_METHOD) {
             MULTINAME(m, a);
-            $$.c = abc_getlex2($$.c, &m);
+            $$.c = abc_findpropstrict2($$.c, &m);
+            $$.c = abc_getproperty2($$.c, &m);
+            $$.t = TYPE_FUNCTION(a->function);
+        } else {
+            if(a->slot) {
+                $$.c = abc_getglobalscope($$.c);
+                $$.c = abc_getslot($$.c, a->slot);
+            } else {
+                MULTINAME(m, a);
+                $$.c = abc_getlex2($$.c, &m);
+            }
+            $$.t = TYPE_CLASS(a);
         }
-        $$.t = TYPE_CLASS(a);
 
     /* unknown object, let the avm2 resolve it */
     } else {