added stub for inner functions
[swftools.git] / lib / as3 / parser.y
index 3a4cd59..9643008 100644 (file)
 %token<token> T_SHR ">>"
 
 %type <for_start> FOR_START
-%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT
+%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
 %type <token> VARCONST
 %type <code> CODE
 %type <code> CODEPIECE CODE_STATEMENT
 %type <value> MAYBEEXPRESSION
 %type <value> E DELETE
 %type <value> CONSTANT
-%type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
+%type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY 
+%type <value> INNERFUNCTION
 %type <token> USE_NAMESPACE
 %type <code> FOR_INIT
 %type <code> IMPORT
 // needed for "return" precedence:
 %nonassoc T_STRING T_REGEXP
 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
-%nonassoc "false" "true" "null" "undefined" "super"
+%nonassoc "false" "true" "null" "undefined" "super" "function"
+%nonassoc above_function
 
 
      
@@ -422,23 +424,50 @@ static void old_state()
     state = state->old;
     state_destroy(leaving);
 }
-void initialize_state()
+
+void initialize_parser()
 {
     global = rfx_calloc(sizeof(global_t));
-    new_state();
-
-    state->package = current_filename;
-
     global->file = abc_file_new();
     global->file->flags &= ~ABCFILE_LAZY;
     global->variable_count = 1;
-    
-    global->init = abc_initscript(global->file, 0);
+    global->init = abc_initscript(global->file);
     code_t*c = global->init->method->body->code;
-
     c = abc_getlocal_0(c);
     c = abc_pushscope(c);
-  
+    /*c = abc_findpropstrict(c, "[package]::trace");
+    c = abc_pushstring(c, "[entering global init function]");
+    c = abc_callpropvoid(c, "[package]::trace", 1);*/
+    global->init->method->body->code = c;
+}
+
+void initialize_file(char*filename)
+{
+    new_state();
+    state->package = filename;
+}
+void finish_file()
+{
+    if(!state || state->level!=1) {
+        syntaxerror("unexpected end of file");
+    }
+    state_destroy(state);state=0;
+}
+
+void* finish_parser()
+{
+    code_t*c = global->init->method->body->code;
+    /*c = abc_findpropstrict(c, "[package]::trace");
+      c = abc_pushstring(c, "[leaving global init function]");
+      c = abc_callpropvoid(c, "[package]::trace", 1);*/
+    c = abc_returnvoid(c);
+    global->init->method->body->code = c;
+    return global->file;
+}
+
+
+static void xx_scopetest() 
+{
     /* findpropstrict doesn't just return a scope object- it
        also makes it "active" somehow. Push local_0 on the
        scope stack and read it back with findpropstrict, it'll
@@ -464,29 +493,6 @@ void initialize_state()
     c = abc_getlocal_3(c);
     c = abc_kill(c, 3);
     c = abc_iftrue(c,xx);*/
-
-    c = abc_findpropstrict(c, "[package]::trace");
-    c = abc_pushstring(c, "[entering global init function]");
-    c = abc_callpropvoid(c, "[package]::trace", 1);
-    
-    global->init->method->body->code = c;
-}
-void* finalize_state()
-{
-    if(state->level!=1) {
-        syntaxerror("unexpected end of file");
-    }
-    abc_method_body_t*m = global->init->method->body;
-    //__ popscope(m);
-    
-    __ findpropstrict(m, "[package]::trace");
-    __ pushstring(m, "[leaving global init function]");
-    __ callpropvoid(m, "[package]::trace", 1);
-    __ returnvoid(m);
-
-    state_destroy(state);state=0;
-
-    return global->file;
 }
 
 
@@ -616,7 +622,7 @@ static void endpackage()
     old_state();
 }
 
-char*globalclass=0;
+char*as3_globalclass=0;
 static void startclass(int flags, char*classname, classinfo_t*extends, classinfo_list_t*implements, char interface)
 {
     if(state->cls) {
@@ -625,19 +631,10 @@ 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_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC))
         syntaxerror("invalid modifier(s)");
@@ -747,11 +744,11 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo
     __ setslot(m, slotindex);
 
     /* flash.display.MovieClip handling */
-    if(!globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
+    if(!as3_globalclass && (flags&FLAG_PUBLIC) && classinfo_equals(registry_getMovieClip(),extends)) {
         if(state->package && state->package[0]) {
-            globalclass = concat3(state->package, ".", classname);
+            as3_globalclass = concat3(state->package, ".", classname);
         } else {
-            globalclass = strdup(classname);
+            as3_globalclass = strdup(classname);
         }
     }
     multiname_destroy(extends2);
@@ -765,6 +762,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);
@@ -773,15 +777,10 @@ 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();
 }
 
@@ -906,7 +905,7 @@ 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();
@@ -1170,6 +1169,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:
@@ -1357,7 +1376,6 @@ CODE: CODEPIECE {$$=$1;}
 
 // code which also may appear outside a method
 CODE_STATEMENT: IMPORT 
-CODE_STATEMENT: VOIDEXPRESSION 
 CODE_STATEMENT: FOR 
 CODE_STATEMENT: FOR_IN 
 CODE_STATEMENT: WHILE 
@@ -1366,11 +1384,12 @@ CODE_STATEMENT: SWITCH
 CODE_STATEMENT: IF
 CODE_STATEMENT: WITH
 CODE_STATEMENT: TRY
+CODE_STATEMENT: VOIDEXPRESSION 
 
 // code which may appear anywhere
 CODEPIECE: ';' {$$=0;}
-CODEPIECE: VARIABLE_DECLARATION
 CODEPIECE: CODE_STATEMENT
+CODEPIECE: VARIABLE_DECLARATION
 CODEPIECE: BREAK
 CODEPIECE: CONTINUE
 CODEPIECE: RETURN
@@ -1387,7 +1406,7 @@ CODEBLOCK :  CODEPIECE %prec below_semicolon {$$=$1;}
 /* ------------ package init code ------------------- */
 
 PACKAGE_INITCODE: CODE_STATEMENT {
-    if($1) warning("code ignored");
+    if($1) as3_warning("code ignored");
 }
 
 /* ------------ variables --------------------------- */
@@ -1470,6 +1489,8 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
 FOR_INIT : {$$=code_new();}
 FOR_INIT : VARIABLE_DECLARATION
 FOR_INIT : VOIDEXPRESSION
+
+// TODO: why doesn't an %prec above_identifier resolve the r-r conflict here?
 FOR_IN_INIT : "var" T_IDENTIFIER MAYBETYPE {
     $$=$2;new_variable($2,$3,1);
 }
@@ -1745,7 +1766,7 @@ IMPORT : "import" PACKAGE '.' '*' {
 
 /* ------------ classes and interfaces (header) -------------- */
 
-MAYBE_MODIFIERS : {$$=0;}
+MAYBE_MODIFIERS : %prec above_function {$$=0;}
 MAYBE_MODIFIERS : MODIFIER_LIST {$$=$1}
 MODIFIER_LIST : MODIFIER               {$$=$1;}
 MODIFIER_LIST : MODIFIER_LIST MODIFIER {$$=$1|$2;}
@@ -1955,6 +1976,14 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P
     $$=0;
 }
 
+MAYBE_IDENTIFIER: T_IDENTIFIER
+MAYBE_IDENTIFIER: {$$=0;}
+INNERFUNCTION: "function" MAYBE_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE '{' MAYBECODE '}'
+{
+    syntaxerror("nested functions not supported yet");
+}
+
+
 /* ------------- package + class ids --------------- */
 
 CLASS: T_IDENTIFIER {
@@ -2142,6 +2171,7 @@ VOIDEXPRESSION : EXPRESSION %prec below_minus {
 
 // ----------------------- expression evaluation -------------------------------------
 
+E : INNERFUNCTION %prec prec_none {$$ = $1;}
 //V : CONSTANT                    {$$ = 0;}
 E : CONSTANT
 //V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
@@ -2281,15 +2311,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.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);
              $$.c = abc_rshift($$.c);
              $$.t = TYPE_INT;
@@ -2320,6 +2341,15 @@ E : E '+' E {$$.c = code_append($1.c,$3.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.t,$3.t)) {
                 $$.c = abc_multiply_i($$.c);
@@ -2478,23 +2508,24 @@ E : E "|=" E {
               }
 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;
@@ -2507,30 +2538,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)) {
@@ -2672,7 +2718,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);
@@ -2687,7 +2737,7 @@ VAR_READ : T_IDENTIFIER {
     /* unknown object, let the avm2 resolve it */
     } else {
         if(strcmp($1,"trace"))
-            warning("Couldn't resolve '%s', doing late binding", $1);
+            as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
         state->method->late_binding = 1;
                 
         multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};