X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=lib%2Fas3%2Fparser.y;h=3a4cd5937eb9e1c49dae3cde2b0b419ebcd0abae;hb=c9356868b99bede23aed167c0f1d3ed22c6d3710;hp=959f76da472b8921877ef2f7f63a5993758e3dfd;hpb=1476da7c7f16895f76a6b9ab44c2414b6b8f1943;p=swftools.git diff --git a/lib/as3/parser.y b/lib/as3/parser.y index 959f76d..3a4cd59 100644 --- a/lib/as3/parser.y +++ b/lib/as3/parser.y @@ -58,12 +58,15 @@ char*id; constant_t*constant; for_start_t for_start; + abc_exception_t *exception; + abc_exception_list_t *exception_list; + regexp_t regexp; } %token T_IDENTIFIER %token T_STRING -%token T_REGEXP +%token T_REGEXP %token T_EMPTY %token T_INT %token T_UINT @@ -76,16 +79,16 @@ %token T_DO "do" %token T_SWITCH "switch" -%token KW_IMPLEMENTS +%token KW_IMPLEMENTS "implements" %token KW_NAMESPACE "namespace" %token KW_PACKAGE "package" -%token KW_PROTECTED -%token KW_PUBLIC -%token KW_PRIVATE +%token KW_PROTECTED "protected" +%token KW_PUBLIC "public" +%token KW_PRIVATE "private" %token KW_USE "use" -%token KW_INTERNAL +%token KW_INTERNAL "internal" %token KW_NEW "new" -%token KW_NATIVE +%token KW_NATIVE "native" %token KW_FUNCTION "function" %token KW_UNDEFINED "undefined" %token KW_CONTINUE "continue" @@ -95,7 +98,9 @@ %token KW_CASE "case" %token KW_SET "set" %token KW_VOID "void" -%token KW_STATIC +%token KW_THROW "throw" +%token KW_STATIC "static" +%token KW_WITH "with" %token KW_INSTANCEOF "instanceof" %token KW_IMPORT "import" %token KW_RETURN "return" @@ -104,13 +109,13 @@ %token KW_NULL "null" %token KW_VAR "var" %token KW_DYNAMIC "dynamic" -%token KW_OVERRIDE -%token KW_FINAL +%token KW_OVERRIDE "override" +%token KW_FINAL "final" %token KW_EACH "each" %token KW_GET "get" %token KW_TRY "try" %token KW_SUPER "super" -%token KW_EXTENDS +%token KW_EXTENDS "extends" %token KW_FALSE "false" %token KW_TRUE "true" %token KW_BOOLEAN "Boolean" @@ -133,6 +138,7 @@ %token T_NEE "!==" %token T_LE "<=" %token T_GE ">=" +%token T_ORBY "|=" %token T_DIVBY "/=" %token T_MODBY "%=" %token T_MULBY "*=" @@ -156,23 +162,25 @@ %type X_IDENTIFIER PACKAGE FOR_IN_INIT %type VARCONST %type CODE -%type CODEPIECE -%type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH -%type PACKAGE_DECLARATION -%type FUNCTION_DECLARATION -%type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST -%type CLASS_DECLARATION -%type NAMESPACE_DECLARATION -%type INTERFACE_DECLARATION +%type CODEPIECE CODE_STATEMENT +%type CODEBLOCK MAYBECODE MAYBE_CASE_LIST CASE_LIST DEFAULT CASE SWITCH WITH +%type PACKAGE_DECLARATION SLOT_DECLARATION +%type FUNCTION_DECLARATION PACKAGE_INITCODE +%type VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST THROW +%type CATCH +%type CATCH_LIST +%type CLASS_DECLARATION +%type NAMESPACE_DECLARATION +%type INTERFACE_DECLARATION %type VOIDEXPRESSION %type EXPRESSION NONCOMMAEXPRESSION %type MAYBEEXPRESSION %type E DELETE %type CONSTANT -%type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE +%type FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY %type USE_NAMESPACE %type FOR_INIT -%type IMPORT +%type IMPORT %type MAYBETYPE %type GETSET %type PARAM @@ -207,8 +215,8 @@ %right '?' ':' %left "||" %left "&&" -%nonassoc '|' -%nonassoc '^' +%left '|' +%left '^' %nonassoc '&' %nonassoc "==" "!=" "===" "!==" %nonassoc "is" "as" "in" @@ -283,17 +291,16 @@ typedef struct _methodstate { /* method data */ memberinfo_t*info; char late_binding; - /* code that needs to be executed at the start of - a method (like initializing local registers) */ - code_t*initcode; char is_constructor; char has_super; + char is_global; + abc_exception_list_t*exceptions; } methodstate_t; typedef struct _state { struct _state*old; int level; - + char*package; import_list_t*wildcard_imports; dict_t*imports; @@ -301,6 +308,8 @@ typedef struct _state { classstate_t*cls; methodstate_t*method; + + char*exception_name; dict_t*vars; } state_t; @@ -326,8 +335,7 @@ DECLARE_LIST(state); multiname_t m;\ namespace_t m##_ns;\ if(f) { \ - m##_ns.access = flags2access(f->flags); \ - m##_ns.name = ""; \ + m##_ns = flags2namespace(f->flags, ""); \ m.type = QNAME; \ m.ns = &m##_ns; \ m.namespace_set = 0; \ @@ -359,11 +367,6 @@ static namespace_list_t nl2 = {&ns2,&nl3}; static namespace_list_t nl1 = {&ns1,&nl2}; static namespace_set_t nopackage_namespace_set = {&nl1}; -static void init_globals() -{ - global = rfx_calloc(sizeof(global_t)); -} - static void new_state() { NEW(state_t, s); @@ -376,7 +379,7 @@ static void new_state() state = s; state->level++; state->has_own_imports = 0; - state->vars = dict_new(); + state->vars = dict_new(); state->old = oldstate; } static void state_has_imports() @@ -415,20 +418,20 @@ static void old_state() if(!state || !state->old) syntaxerror("invalid nesting"); state_t*leaving = state; + state = state->old; - /*if(state->method->initcode) { - printf("residual initcode\n"); - code_dump(state->method->initcode, 0, 0, "", stdout); - }*/ state_destroy(leaving); } void initialize_state() { - init_globals(); + 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); code_t*c = global->init->method->body->code; @@ -481,20 +484,127 @@ void* finalize_state() __ callpropvoid(m, "[package]::trace", 1); __ returnvoid(m); - state_destroy(state); + state_destroy(state);state=0; return global->file; } +typedef struct _variable { + int index; + classinfo_t*type; + char init; +} variable_t; + +static variable_t* find_variable(char*name) +{ + state_t* s = state; + while(s) { + variable_t*v = 0; + if(s->method) + v = dict_lookup(s->vars, name); + if(v) { + return v; + } + s = s->old; + } + return 0; +} +static variable_t* find_variable_safe(char*name) +{ + variable_t* v = find_variable(name); + if(!v) + syntaxerror("undefined variable: %s", name); + return v; +} +static char variable_exists(char*name) +{ + return dict_lookup(state->vars, name)!=0; +} +code_t*defaultvalue(code_t*c, classinfo_t*type); +static int new_variable(char*name, classinfo_t*type, char init) +{ + NEW(variable_t, v); + v->index = global->variable_count; + v->type = type; + v->init = init; + + dict_put(state->vars, name, v); + + return global->variable_count++; +} +#define TEMPVARNAME "__as3_temp__" +static int gettempvar() +{ + variable_t*v = find_variable(TEMPVARNAME); + if(v) + return v->index; + return new_variable(TEMPVARNAME, 0, 0); +} + +code_t* var_block(code_t*body) +{ + code_t*c = 0; + code_t*k = 0; + int t; + int num=0; + for(t=0;tvars->hashsize;t++) { + dictentry_t*e = state->vars->slots[t]; + while(e) { + variable_t*v = (variable_t*)e->data; + if(v->type && v->init) { + c = defaultvalue(c, v->type); + c = abc_setlocal(c, v->index); + k = abc_kill(k, v->index); + num++; + } + e = e->next; + } + } + + if(k) { + code_t*x = body; + while(x) { + if(x->opcode== OPCODE___BREAK__ || + x->opcode== OPCODE___CONTINUE__) { + /* link kill code before break/continue */ + code_t*e = code_dup(k); + code_t*s = code_start(e); + s->prev = x->prev; + if(x->prev) { + x->prev->next = s; + } + e->next = x; + x->prev = e; + } + x = x->prev; + } + } + + c = code_append(c, body); + c = code_append(c, k); + return c; +} + +static code_t* wrap_function(code_t*c,code_t*header, code_t*body) +{ + c = code_append(c, header); + c = code_append(c, var_block(body)); + /* append return if necessary */ + if(!c || c->opcode != OPCODE_RETURNVOID && + c->opcode != OPCODE_RETURNVALUE) { + c = abc_returnvoid(c); + } + return c; +} + + static void startpackage(char*name) { - if(state->package) { - syntaxerror("Packages can not be nested."); - } new_state(); /*printf("entering package \"%s\"\n", name);*/ state->package = strdup(name); + global->variable_count = 1; } static void endpackage() { @@ -513,6 +623,7 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo syntaxerror("inner classes now allowed"); } new_state(); + global->variable_count = 1; state->cls = rfx_calloc(sizeof(classstate_t)); token_list_t*t=0; @@ -528,10 +639,10 @@ static void startclass(int flags, char*classname, classinfo_t*extends, classinfo printf("\n"); */ - if(flags&~(FLAG_INTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC)) + if(flags&~(FLAG_PACKAGEINTERNAL|FLAG_PUBLIC|FLAG_FINAL|FLAG_DYNAMIC)) syntaxerror("invalid modifier(s)"); - if((flags&(FLAG_PUBLIC|FLAG_INTERNAL)) == (FLAG_PUBLIC|FLAG_INTERNAL)) + if((flags&(FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) == (FLAG_PUBLIC|FLAG_PACKAGEINTERNAL)) syntaxerror("public and internal not supported at the same time."); /* create the class name, together with the proper attributes */ @@ -646,18 +757,6 @@ 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->has_constructor && !(state->cls->info->flags&CLASS_INTERFACE)) { @@ -686,80 +785,6 @@ static void endclass() old_state(); } -typedef struct _variable { - int index; - classinfo_t*type; -} variable_t; - -static variable_t* find_variable(char*name) -{ - state_t* s = state; - while(s) { - variable_t*v = 0; - if(s->method) - v = dict_lookup(s->vars, name); - if(v) { - return v; - } - s = s->old; - } - return 0; -} -static variable_t* find_variable_safe(char*name) -{ - variable_t* v = find_variable(name); - if(!v) - syntaxerror("undefined variable: %s", name); - return v; -} -static char variable_exists(char*name) -{ - return dict_lookup(state->vars, name)!=0; -} -code_t*defaultvalue(code_t*c, classinfo_t*type); -static int new_variable(char*name, classinfo_t*type, char init) -{ - NEW(variable_t, v); - v->index = global->variable_count; - v->type = type; - dict_put(state->vars, name, v); - - if(init && state->method && type) { - /* if this is a typed variable: - push default value for type on stack at the very beginning of the - method, so that it always has that type regardless of the control - path */ - state->method->initcode = defaultvalue(state->method->initcode, type); - state->method->initcode = abc_setlocal(state->method->initcode, v->index); - } - return global->variable_count++; -} -#define TEMPVARNAME "__as3_temp__" -static int gettempvar() -{ - variable_t*v = find_variable(TEMPVARNAME); - if(v) - return v->index; - return new_variable(TEMPVARNAME, 0, 0); -} - -code_t* killvars(code_t*c) -{ - int t; - for(t=0;tvars->hashsize;t++) { - dictentry_t*e =state->vars->slots[t]; - while(e) { - variable_t*v = (variable_t*)e->data; - //do this always, otherwise register types don't match - //in the verifier when doing nested loops - //if(!TYPE_IS_BUILTIN_SIMPLE(type)) { - c = abc_kill(c, v->index); - e = e->next; - } - } - return c; -} - void check_code_for_break(code_t*c) { while(c) { @@ -794,10 +819,38 @@ static void check_constant_against_type(classinfo_t*t, constant_t*c) } } + +static int flags2access(int flags) +{ + int access = 0; + if(flags&FLAG_PUBLIC) { + if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); + access = ACCESS_PACKAGE; + } else if(flags&FLAG_PRIVATE) { + if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); + access = ACCESS_PRIVATE; + } else if(flags&FLAG_PROTECTED) { + if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_PACKAGEINTERNAL)) + syntaxerror("invalid combination of access levels"); + access = ACCESS_PROTECTED; + } else { + access = ACCESS_PACKAGEINTERNAL; + } + return access; +} + + static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*name, params_t*params, classinfo_t*return_type, int slot) { memberinfo_t*minfo = 0; - if(getset != KW_GET && getset != KW_SET) { + if(!state->cls) { + //package method + minfo = memberinfo_register_global(flags2access(flags), state->package, name, MEMBER_METHOD); + minfo->return_type = return_type; + } else if(getset != KW_GET && getset != KW_SET) { + //class method if((minfo = registry_findmember(state->cls->info, name, 0))) { if(minfo->parent == state->cls->info) { syntaxerror("class already contains a member/method called '%s'", name); @@ -814,6 +867,7 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na // to actually store these //state->minfo->slot = state->method->abc->method->trait->slot_id; } else { + //class getter/setter int gs = getset==KW_GET?MEMBER_GET:MEMBER_SET; classinfo_t*type=0; if(getset == KW_GET) @@ -844,29 +898,11 @@ static memberinfo_t*registerfunction(enum yytokentype getset, int flags, char*na if(flags&FLAG_PUBLIC) minfo->flags |= FLAG_PUBLIC; if(flags&FLAG_PRIVATE) minfo->flags |= FLAG_PRIVATE; if(flags&FLAG_PROTECTED) minfo->flags |= FLAG_PROTECTED; - if(flags&FLAG_INTERNAL) minfo->flags |= FLAG_INTERNAL; + if(flags&FLAG_PACKAGEINTERNAL) minfo->flags |= FLAG_PACKAGEINTERNAL; if(flags&FLAG_OVERRIDE) minfo->flags |= FLAG_OVERRIDE; return minfo; } -static int flags2access(int flags) -{ - int access = 0; - if(flags&FLAG_PUBLIC) { - if(access&(FLAG_PRIVATE|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PACKAGE; - } else if(flags&FLAG_PRIVATE) { - if(access&(FLAG_PUBLIC|FLAG_PROTECTED|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PRIVATE; - } else if(flags&FLAG_PROTECTED) { - if(access&(FLAG_PUBLIC|FLAG_PRIVATE|FLAG_INTERNAL)) syntaxerror("invalid combination of access levels"); - access = ACCESS_PROTECTED; - } else { - access = ACCESS_PACKAGEINTERNAL; - } - return access; -} - static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type) { @@ -874,17 +910,24 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n syntaxerror("not able to start another method scope"); } new_state(); + global->variable_count = 0; state->method = rfx_calloc(sizeof(methodstate_t)); - 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; + if(state->cls) { + state->method->is_constructor = !strcmp(state->cls->info->name,name); + state->cls->has_constructor |= state->method->is_constructor; + + new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0); + } else { + state->method->is_global = 1; + state->method->late_binding = 1; // for global methods, always push local_0 on the scope stack + + new_variable("globalscope", 0, 0); + } /* state->vars is initialized by state_new */ - if(new_variable((flags&FLAG_STATIC)?"class":"this", state->cls->info, 0)!=0) syntaxerror("Internal error"); + param_list_t*p=0; for(p=params->list;p;p=p->next) { new_variable(p->param->name, p->param->type, 0); @@ -897,21 +940,28 @@ static void startfunction(token_t*ns, int flags, enum yytokentype getset, char*n static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*name, params_t*params, classinfo_t*return_type, code_t*body) { - namespace_t mname_ns = {flags2access(flags), ""}; - multiname_t mname = {QNAME, &mname_ns, 0, name}; - abc_method_t*f = 0; multiname_t*type2 = sig2mname(return_type); int slot = 0; if(state->method->is_constructor) { f = abc_class_getconstructor(state->cls->abc, type2); - } else { + } else if(!state->method->is_global) { + namespace_t mname_ns = flags2namespace(flags, ""); + multiname_t mname = {QNAME, &mname_ns, 0, name}; + if(flags&FLAG_STATIC) f = abc_class_staticmethod(state->cls->abc, type2, &mname); else f = abc_class_method(state->cls->abc, type2, &mname); slot = f->trait->slot_id; + } else { + namespace_t mname_ns = flags2namespace(flags, state->package); + multiname_t mname = {QNAME, &mname_ns, 0, name}; + + f = abc_method_new(global->file, type2, 1); + trait_t*t = trait_new_method(&global->init->traits, multiname_clone(&mname), f); + //abc_code_t*c = global->init->method->body->code; } //flash doesn't seem to allow us to access function slots //state->method->info->slot = slot; @@ -938,11 +988,13 @@ static void endfunction(token_t*ns, int flags, enum yytokentype getset, char*nam } check_code_for_break(body); - if(f->body) + if(f->body) { f->body->code = body; - else //interface + f->body->exceptions = state->method->exceptions; + } else { //interface if(body) syntaxerror("interface methods can't have a method body"); + } free(state->method);state->method=0; old_state(); @@ -982,12 +1034,25 @@ void continuejumpsto(code_t*c, char*name, code_t*jump) } } +#define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a))) +#define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a))) +#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b)) + classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op) { if(!type1 || !type2) return registry_getanytype(); if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2)) return registry_getanytype(); + + if(op=='+') { + if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) { + return TYPE_NUMBER; + } else { + return TYPE_ANY; + } + } + if(type1 == type2) return type1; return registry_getanytype(); @@ -1047,8 +1112,12 @@ code_t*defaultvalue(code_t*c, classinfo_t*type) c = abc_pushnan(c); } else if(TYPE_IS_BOOLEAN(type)) { c = abc_pushfalse(c); + } else if(!type) { + //c = abc_pushundefined(c); } else { c = abc_pushnull(c); + MULTINAME(m, type); + c = abc_coerce2(c, &m); } return c; } @@ -1068,14 +1137,15 @@ static classinfo_t* find_class(char*name) classinfo_t*c=0; c = registry_findclass(state->package, name); + if(c) return c; /* try explicit imports */ dictentry_t* e = dict_get_slot(state->imports, name); + if(c) return c; while(e) { - if(c) - break; if(!strcmp(e->key, name)) { c = (classinfo_t*)e->data; + if(c) return c; } e = e->next; } @@ -1083,18 +1153,21 @@ static classinfo_t* find_class(char*name) /* 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); + if(c) return c; l = l->next; } /* try global package */ - if(!c) { - c = registry_findclass("", name); - } - return c; + c = registry_findclass("", name); + if(c) return c; + + /* try local "filename" package */ + c = registry_findclass(current_filename, name); + if(c) return c; + + return 0; } static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore) @@ -1147,6 +1220,8 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r prefix = abc_getlocal(prefix, temp); prefix = abc_swap(prefix); prefix = abc_getlocal(prefix, temp); + if(!use_temp_var); + prefix = abc_kill(prefix, temp); } use_temp_var = 1; } else { @@ -1234,14 +1309,13 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r c = abc_setlocal(c, temp); c = code_append(c, write); c = abc_getlocal(c, temp); + c = abc_kill(c, temp); } } 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)) %} @@ -1250,47 +1324,72 @@ static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char r /* ------------ code blocks / statements ---------------- */ -PROGRAM: MAYBECODE { - /* todo: do something with this code if we're outside a function */ - if($1) - warning("ignored code"); -} +PROGRAM: MAYBE_PROGRAM_CODE_LIST + +MAYBE_PROGRAM_CODE_LIST: | PROGRAM_CODE_LIST +PROGRAM_CODE_LIST: PROGRAM_CODE + | PROGRAM_CODE_LIST PROGRAM_CODE + +PROGRAM_CODE: PACKAGE_DECLARATION + | INTERFACE_DECLARATION + | CLASS_DECLARATION + | FUNCTION_DECLARATION + | SLOT_DECLARATION + | PACKAGE_INITCODE + | ';' + +MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST +INPACKAGE_CODE_LIST: INPACKAGE_CODE + | INPACKAGE_CODE_LIST INPACKAGE_CODE + +INPACKAGE_CODE: INTERFACE_DECLARATION + | CLASS_DECLARATION + | FUNCTION_DECLARATION + | SLOT_DECLARATION + | PACKAGE_INITCODE + | ';' MAYBECODE: CODE {$$=$1;} MAYBECODE: {$$=code_new();} -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*/} -CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/} -CODEPIECE: INTERFACE_DECLARATION {$$=code_new();} -CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/} -CODEPIECE: ';' {$$=code_new();} -CODEPIECE: VARIABLE_DECLARATION {$$=$1} -CODEPIECE: VOIDEXPRESSION {$$=$1} -CODEPIECE: FOR {$$=$1} -CODEPIECE: FOR_IN {$$=$1} -CODEPIECE: WHILE {$$=$1} -CODEPIECE: DO_WHILE {$$=$1} -CODEPIECE: SWITCH {$$=$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();} +CODE: CODE CODEPIECE {$$=code_append($1,$2);} +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 +CODE_STATEMENT: DO_WHILE +CODE_STATEMENT: SWITCH +CODE_STATEMENT: IF +CODE_STATEMENT: WITH +CODE_STATEMENT: TRY + +// code which may appear anywhere +CODEPIECE: ';' {$$=0;} +CODEPIECE: VARIABLE_DECLARATION +CODEPIECE: CODE_STATEMENT +CODEPIECE: BREAK +CODEPIECE: CONTINUE +CODEPIECE: RETURN +CODEPIECE: THROW + +CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=0;} +CODEPIECE: USE_NAMESPACE {/*TODO*/$$=0;} CODEBLOCK : '{' CODE '}' {$$=$2;} CODEBLOCK : '{' '}' {$$=0;} CODEBLOCK : CODEPIECE ';' {$$=$1;} CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;} +/* ------------ package init code ------------------- */ + +PACKAGE_INITCODE: CODE_STATEMENT { + if($1) warning("code ignored"); +} + /* ------------ variables --------------------------- */ MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;} @@ -1350,6 +1449,7 @@ MAYBEELSE: "else" CODEBLOCK {$$=$2;} //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;} IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { + $$ = code_new(); $$ = code_append($$, $4.c); code_t*myjmp,*myif = $$ = abc_iffalse($$, 0); @@ -1363,8 +1463,8 @@ IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE { $$ = code_append($$, $7); myjmp->branch = $$ = abc_nop($$); } - - $$ = killvars($$);old_state(); + $$ = var_block($$); + old_state(); } FOR_INIT : {$$=code_new();} @@ -1396,7 +1496,8 @@ FOR : FOR_START FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK { continuejumpsto($$, $1.name, cont); myif->branch = out; - $$ = killvars($$);old_state(); + $$ = var_block($$); + old_state(); } FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { @@ -1433,16 +1534,17 @@ FOR_IN : FOR_START FOR_IN_INIT "in" EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1.name, out); continuejumpsto($$, $1.name, loopstart); - $$ = killvars($$); - myif->branch = out; + $$ = var_block($$); old_state(); + free(tmp1name); free(tmp2name); } WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { + $$ = code_new(); code_t*myjmp = $$ = abc_jump($$, 0); @@ -1456,7 +1558,7 @@ WHILE : T_WHILE '(' {new_state();} EXPRESSION ')' CODEBLOCK { breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + $$ = var_block($$); old_state(); } @@ -1470,7 +1572,8 @@ DO_WHILE : T_DO {new_state();} CODEBLOCK "while" '(' EXPRESSION ')' { code_t*out = $$ = abc_nop($$); breakjumpsto($$, $1, out); continuejumpsto($$, $1, cont); - $$ = killvars($$); + + $$ = var_block($$); old_state(); } @@ -1530,9 +1633,89 @@ SWITCH : T_SWITCH '(' {new_state();} E ')' '{' MAYBE_CASE_LIST '}' { } c=c->prev; } + + $$ = var_block($$); old_state(); } +/* ------------ try / catch /finally ---------------- */ + +FINALLY: "finally" '{' CODE '}' +MAYBE_FINALLY: | FINALLY + +CATCH: "catch" '(' T_IDENTIFIER MAYBETYPE ')' {new_state();state->exception_name=$3;new_variable($3, $4, 0);} + '{' CODE '}' { + namespace_t name_ns = {ACCESS_PACKAGE, ""}; + multiname_t name = {QNAME, &name_ns, 0, $3}; + + NEW(abc_exception_t, e) + e->exc_type = sig2mname($4); + e->var_name = multiname_clone(&name); + $$ = e; + + code_t*c = 0; + int i = find_variable_safe($3)->index; + e->target = c = abc_setlocal(0, i); + c = code_append(c, $8); + c = abc_kill(c, i); + + c = var_block(c); + old_state(); + +} + +CATCH_LIST: CATCH {$$=list_new();list_append($$,$1);} +CATCH_LIST: CATCH_LIST CATCH {$$=$1;list_append($$,$2);} + +TRY : "try" '{' {new_state();} CODE '}' CATCH_LIST MAYBE_FINALLY { + code_t*start = code_start($4); + $$=$4; + + code_t*out = abc_nop(0); + code_t*jmp = $$ = abc_jump($$, out); + + abc_exception_list_t*l = $6; + while(l) { + abc_exception_t*e = l->abc_exception; + e->from = start; + e->to = jmp; + $$ = code_append($$, e->target); + $$ = abc_jump($$, out); + l = l->next; + } + $$ = code_append($$, out); + jmp->branch = out; + + list_concat(state->method->exceptions, $6); + + $$ = var_block($$); + old_state(); +} + +/* ------------ throw ------------------------------- */ + +THROW : "throw" EXPRESSION { + $$=$2.c; + $$=abc_throw($$); +} +THROW : "throw" %prec prec_none { + if(!state->exception_name) + syntaxerror("re-throw only possible within a catch block"); + variable_t*v = find_variable(state->exception_name); + $$=code_new(); + $$=abc_getlocal($$, v->index); + $$=abc_throw($$); +} + +/* ------------ with -------------------------------- */ + +WITH : "with" '(' EXPRESSION ')' CODEBLOCK { + $$ = $3.c; + $$ = abc_pushscope($$); + $$ = code_append($$, $5); + $$ = abc_popscope($$); +} + /* ------------ packages and imports ---------------- */ X_IDENTIFIER: T_IDENTIFIER @@ -1541,8 +1724,8 @@ X_IDENTIFIER: T_IDENTIFIER PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,".",$3);free($1);$1=0;} PACKAGE: X_IDENTIFIER {$$=strdup($1);} -PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBECODE '}' {endpackage()} -PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBECODE '}' {endpackage()} +PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2);free($2);$2=0;} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;} +PACKAGE_DECLARATION : "package" '{' {startpackage("")} MAYBE_INPACKAGE_CODE_LIST '}' {endpackage();$$=0;} IMPORT : "import" QNAME { classinfo_t*c = $2; @@ -1575,7 +1758,7 @@ MODIFIER : KW_PUBLIC {$$=FLAG_PUBLIC;} | KW_FINAL {$$=FLAG_FINAL;} | KW_OVERRIDE {$$=FLAG_OVERRIDE;} | KW_NATIVE {$$=FLAG_NATIVE;} - | KW_INTERNAL {$$=FLAG_INTERNAL;} + | KW_INTERNAL {$$=FLAG_PACKAGEINTERNAL;} EXTENDS : {$$=registry_getobjectclass();} EXTENDS : KW_EXTENDS QNAME {$$=$2;} @@ -1589,36 +1772,42 @@ IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;} CLASS_DECLARATION : MAYBE_MODIFIERS "class" T_IDENTIFIER EXTENDS IMPLEMENTS_LIST '{' {startclass($1,$3,$4,$5, 0);} - MAYBE_DECLARATION_LIST - '}' {endclass();} + MAYBE_CLASS_BODY + '}' {endclass();$$=0;} INTERFACE_DECLARATION : MAYBE_MODIFIERS "interface" T_IDENTIFIER EXTENDS_LIST '{' {startclass($1,$3,0,$4,1);} - MAYBE_IDECLARATION_LIST - '}' {endclass();} + MAYBE_INTERFACE_BODY + '}' {endclass();$$=0;} /* ------------ classes and interfaces (body) -------------- */ -MAYBE_DECLARATION_LIST : -MAYBE_DECLARATION_LIST : DECLARATION_LIST -DECLARATION_LIST : DECLARATION -DECLARATION_LIST : DECLARATION_LIST DECLARATION -DECLARATION : ';' -DECLARATION : SLOT_DECLARATION -DECLARATION : FUNCTION_DECLARATION - -MAYBE_IDECLARATION_LIST : -MAYBE_IDECLARATION_LIST : IDECLARATION_LIST -IDECLARATION_LIST : IDECLARATION -IDECLARATION_LIST : IDECLARATION_LIST IDECLARATION +MAYBE_CLASS_BODY : +MAYBE_CLASS_BODY : CLASS_BODY +CLASS_BODY : CLASS_BODY_ITEM +CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM +CLASS_BODY_ITEM : ';' +CLASS_BODY_ITEM : SLOT_DECLARATION +CLASS_BODY_ITEM : FUNCTION_DECLARATION + +CLASS_BODY_ITEM : CODE_STATEMENT { + code_t*c = state->cls->static_init; + c = code_append(c, $1); + state->cls->static_init = c; +} + +MAYBE_INTERFACE_BODY : +MAYBE_INTERFACE_BODY : INTERFACE_BODY +INTERFACE_BODY : IDECLARATION +INTERFACE_BODY : INTERFACE_BODY IDECLARATION IDECLARATION : ';' IDECLARATION : "var" T_IDENTIFIER { syntaxerror("variable declarations not allowed in interfaces"); } IDECLARATION : MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')' MAYBETYPE { $1 |= FLAG_PUBLIC; - if($1&(FLAG_PRIVATE|FLAG_INTERNAL|FLAG_PROTECTED)) { + if($1&(FLAG_PRIVATE|FLAG_PACKAGEINTERNAL|FLAG_PROTECTED)) { syntaxerror("invalid method modifiers: interface methods always need to be public"); } startfunction(0,$1,$3,$4,&$6,$8); @@ -1631,45 +1820,58 @@ VARCONST: "var" | "const" SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION { int flags = $1; - memberinfo_t* info = memberinfo_register(state->cls->info, $3, MEMBER_SLOT); + memberinfo_t* info = state->cls? + memberinfo_register(state->cls->info, $3, MEMBER_SLOT): + memberinfo_register_global(flags2access($1), state->package, $3, MEMBER_SLOT); + info->type = $4; info->flags = flags; - trait_t*t=0; + /* slot name */ namespace_t mname_ns = {flags2access(flags), ""}; multiname_t mname = {QNAME, &mname_ns, 0, $3}; - - if(!(flags&FLAG_STATIC)) { - if($4) { - MULTINAME(m, $4); - t=abc_class_slot(state->cls->abc, &mname, &m); - } else { - t=abc_class_slot(state->cls->abc, &mname, 0); - } - info->slot = t->slot_id; + + trait_list_t**traits; + code_t**code; + if(!state->cls) { + // global variable + traits = &global->init->traits; + code = &global->init->method->body->code; + } else if(flags&FLAG_STATIC) { + // static variable + traits = &state->cls->abc->static_traits; + code = &state->cls->static_init; } else { - if($4) { - MULTINAME(m, $4); - t=abc_class_staticslot(state->cls->abc, &mname, &m); - } else { - t=abc_class_staticslot(state->cls->abc, &mname, 0); - } - info->slot = t->slot_id; + // instance variable + traits = &state->cls->abc->traits; + code = &state->cls->init; + } + + trait_t*t=0; + if($4) { + MULTINAME(m, $4); + t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0); + } else { + t = trait_new_member(traits, 0, multiname_clone(&mname), 0); } + info->slot = t->slot_id; + + /* initalization code (if needed) */ + code_t*c = 0; if($5.c && !is_pushundefined($5.c)) { - code_t*c = 0; c = abc_getlocal_0(c); c = code_append(c, $5.c); c = converttype(c, $5.t, $4); c = abc_setslot(c, t->slot_id); - if(!(flags&FLAG_STATIC)) - state->cls->init = code_append(state->cls->init, c); - else - state->cls->static_init = code_append(state->cls->static_init, c); } + + *code = code_append(*code, c); + if($2==KW_CONST) { t->kind= TRAIT_CONST; } + + $$=0; } /* ------------ constants -------------------------------------- */ @@ -1748,8 +1950,9 @@ FUNCTION_DECLARATION: MAYBE_MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_P c = abc_getlocal_0(c); c = abc_constructsuper(c, 0); } - c = wrap_function(c, state->method->initcode, $11); + c = wrap_function(c, 0, $11); endfunction(0,$1,$3,$4,&$6,$8,c); + $$=0; } /* ------------- package + class ids --------------- */ @@ -1939,13 +2142,31 @@ VOIDEXPRESSION : EXPRESSION %prec below_minus { // ----------------------- expression evaluation ------------------------------------- +//V : CONSTANT {$$ = 0;} E : CONSTANT +//V : VAR_READ %prec T_IDENTIFIER {$$ = 0;} E : VAR_READ %prec T_IDENTIFIER {$$ = $1;} +//V : NEW {$$ = $1.c;} E : NEW {$$ = $1;} +//V : DELETE {$$ = $1.c;} E : DELETE {$$ = $1;} -E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */ - $$.t = TYPE_ANY; - } + +E : T_REGEXP { + $$.c = 0; + namespace_t ns = {ACCESS_PACKAGE, ""}; + multiname_t m = {QNAME, &ns, 0, "RegExp"}; + if(!$1.options) { + $$.c = abc_getlex2($$.c, &m); + $$.c = abc_pushstring($$.c, $1.pattern); + $$.c = abc_construct($$.c, 1); + } else { + $$.c = abc_getlex2($$.c, &m); + $$.c = abc_pushstring($$.c, $1.pattern); + $$.c = abc_pushstring($$.c, $1.options); + $$.c = abc_construct($$.c, 2); + } + $$.t = TYPE_REGEXP; +} CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1); //MULTINAME(m, registry_getintclass()); @@ -2061,7 +2282,7 @@ E : E '|' E {$$.c = code_append($1.c,$3.c); } E : E '-' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1,$3)) { + if(BOTH_INT($1.t,$3.t)) { $$.c = abc_subtract_i($$.c); $$.t = TYPE_INT; } else { @@ -2086,16 +2307,21 @@ 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.t, $3.t)) { + $$.c = abc_add_i($$.c); + $$.t = TYPE_INT; + } else { + $$.c = abc_add($$.c); + $$.t = join_types($1.t,$3.t,'+'); + } + } E : E '*' E {$$.c = code_append($1.c,$3.c); - if(BOTH_INT($1,$3)) { + if(BOTH_INT($1.t,$3.t)) { $$.c = abc_multiply_i($$.c); $$.t = TYPE_INT; } else { @@ -2152,7 +2378,7 @@ E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too E : '-' E { $$=$2; - if(IS_INT($2)) { + if(IS_INT($2.t)) { $$.c=abc_negate_i($$.c); $$.t = TYPE_INT; } else { @@ -2204,7 +2430,7 @@ E : '{' MAYBE_EXPRPAIR_LIST '}' { E : E "*=" E { code_t*c = $3.c; - if(BOTH_INT($1,$3)) { + if(BOTH_INT($1.t,$3.t)) { c=abc_multiply_i(c); } else { c=abc_multiply(c); @@ -2244,6 +2470,12 @@ E : E "/=" E { $$.c = toreadwrite($1.c, c, 0, 0); $$.t = $1.t; } +E : E "|=" E { + code_t*c = abc_bitor($3.c); + c=converttype(c, TYPE_INT, $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)) { @@ -2348,7 +2580,7 @@ E : "super" '.' T_IDENTIFIER if(!t) t = TYPE_OBJECT; memberinfo_t*f = registry_findmember(t, $3, 1); - namespace_t ns = {flags2access(f->flags), ""}; + namespace_t ns = flags2namespace(f->flags, ""); MEMBER_MULTINAME(m, f, $3); $$.c = 0; $$.c = abc_getlocal_0($$.c); @@ -2404,7 +2636,7 @@ VAR_READ : T_IDENTIFIER { $$.t = v->type; /* look at current class' members */ - } else if((f = registry_findmember(state->cls->info, $1, 1))) { + } else if(state->cls && (f = registry_findmember(state->cls->info, $1, 1))) { // $1 is a function in this class int var_is_static = (f->flags&FLAG_STATIC); int i_am_static = ((state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC); @@ -2473,9 +2705,9 @@ VAR_READ : T_IDENTIFIER { // ----------------- namespaces ------------------------------------------------- -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=$2;} -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=$2;} -NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=$2;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER {$$=0;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_IDENTIFIER {$$=0;} +NAMESPACE_DECLARATION : MAYBE_MODIFIERS "namespace" T_IDENTIFIER '=' T_STRING {$$=0;} -USE_NAMESPACE : "use" "namespace" T_IDENTIFIER +USE_NAMESPACE : "use" "namespace" T_IDENTIFIER {$$=0;}