+DECLARATION : ';'
+DECLARATION : SLOT_DECLARATION
+DECLARATION : FUNCTION_DECLARATION
+
+/* ------------ classes and interfaces (body, slots ) ------- */
+
+VARCONST: "var" | "const"
+SLOT_DECLARATION: MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
+
+ memberinfo_t* info = memberinfo_register(state->clsinfo, $3->text, MEMBER_SLOT);
+ info->type = $4;
+
+ trait_t*t=0;
+ if($4) {
+ MULTINAME(m, $4);
+ t=abc_class_slot(state->cls, $3->text, &m);
+ } else {
+ t=abc_class_slot(state->cls, $3->text, 0);
+ }
+ if($2->type==KW_CONST) {
+ t->kind= TRAIT_CONST;
+ }
+ info->slot = t->slot_id;
+ 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);
+ //c = abc_setproperty(c, $3->text);
+ state->cls_init = code_append(state->cls_init, c);
+ }
+}
+
+/* ------------ constants -------------------------------------- */
+
+MAYBESTATICCONSTANT: {$$=0;}
+MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
+
+STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
+STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
+STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
+STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
+STATICCONSTANT : T_STRING {$$ = constant_new_string($1);}
+//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);}
+
+/* ------------ classes and interfaces (body, functions) ------- */
+
+// non-vararg version
+MAYBE_PARAM_LIST: {
+ memset(&$$,0,sizeof($$));
+}
+MAYBE_PARAM_LIST: PARAM_LIST {
+ $$=$1;
+}
+
+// vararg version
+MAYBE_PARAM_LIST: "..." PARAM {
+ memset(&$$,0,sizeof($$));
+ $$.varargs=1;
+ list_append($$.list, $2);
+}
+MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
+ $$ =$1;
+ $$.varargs=1;
+ list_append($$.list, $4);
+}
+
+// non empty
+PARAM_LIST: PARAM_LIST ',' PARAM {
+ $$ = $1;
+ list_append($$.list, $3);
+}
+PARAM_LIST: PARAM {
+ memset(&$$,0,sizeof($$));
+ list_append($$.list, $1);
+}
+PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
+ $$ = malloc(sizeof(param_t));
+ $$->name=$1->text;
+ $$->type = $3;
+ $$->value = $4;
+}
+PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
+ $$ = malloc(sizeof(param_t));
+ $$->name=$1->text;$$->type = TYPE_ANY;
+}
+
+FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
+ MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
+{
+ if(!state->m) syntaxerror("internal error: undefined function");
+ endfunction($11);
+}
+
+/* ------------- package + class ids --------------- */
+
+CLASS: T_IDENTIFIER {
+
+ /* try current package */
+ $$ = registry_findclass(state->package, $1->text);
+
+ /* try explicit imports */
+ dictentry_t* e = dict_get_slot(state->imports, $1->text);
+ while(e) {
+ if($$)
+ break;
+ if(!strcmp(e->key, $1->text)) {
+ $$ = (classinfo_t*)e->data;
+ }
+ e = e->next;
+ }
+
+ /* try package.* imports */
+ import_list_t*l = state->wildcard_imports;
+ while(l) {
+ if($$)
+ break;
+ //printf("does package %s contain a class %s?\n", l->import->package, $1->text);
+ $$ = registry_findclass(l->import->package, $1->text);
+ l = l->next;
+ }
+
+ /* try global package */
+ if(!$$) {
+ $$ = registry_findclass("", $1->text);
+ }
+
+ if(!$$) syntaxerror("Could not find class %s\n", $1->text);
+}
+
+PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
+ $$ = registry_findclass($1->text, $3->text);
+ if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1->text, $3->text);
+}
+
+QNAME: PACKAGEANDCLASS
+ | CLASS
+
+
+/* ----------function calls, constructor calls ------ */
+
+MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
+MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
+
+MAYBE_EXPRESSION_LIST : {$$=0;}
+MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
+EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
+ typedcode_t*t = malloc(sizeof(typedcode_t));
+ *t = $1;
+ list_append($$, t);}
+EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
+ typedcode_t*t = malloc(sizeof(typedcode_t));
+ *t = $3;
+ list_append($$, t);}
+
+NEW : "new" CLASS MAYBE_PARAM_VALUES {
+ MULTINAME(m, $2);
+ $$.c = code_new();
+
+ /* TODO: why do we have to *find* our own classes? */
+ $$.c = abc_findpropstrict2($$.c, &m);
+
+ typedcode_list_t*l = $3;
+ int len = 0;
+ while(l) {
+ $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
+ l = l->next;
+ len ++;
+ }
+ $$.c = abc_constructprop2($$.c, &m, len);
+ $$.t = $2;
+}
+
+/* TODO: use abc_call (for calling local variables),
+ abc_callstatic (for calling own methods)
+ call (for closures)
+*/
+FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
+ typedcode_list_t*l = $3;
+ int len = 0;
+ code_t*paramcode = 0;
+ while(l) {
+ paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
+ l = l->next;
+ len ++;
+ }
+
+ $$.c = $1.c;
+ if($$.c->opcode == OPCODE_COERCE_A) {
+ $$.c = code_cutlast($$.c);
+ }
+
+ $$.t = TYPE_ANY;
+ multiname_t*name = 0;
+ if($$.c->opcode == OPCODE_GETPROPERTY) {
+ name = multiname_clone($$.c->data[0]);
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_callproperty2($$.c, name, len);
+ } else if($$.c->opcode == OPCODE_GETSLOT) {
+ int slot = (int)(ptroff_t)$$.c->data[0];
+ trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME
+ if(t->kind!=TRAIT_METHOD) {
+ //flash allows to assign closures to members.
+ //syntaxerror("not a function");
+ }
+ name = t->name;
+ $$.c = code_cutlast($$.c);
+ $$.c = code_append($$.c, paramcode);
+ //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
+ $$.c = abc_callproperty2($$.c, name, len);
+ } else {
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = code_append($$.c, paramcode);
+ $$.c = abc_call($$.c, len);
+ }
+
+ memberinfo_t*f = 0;
+
+ if(TYPE_IS_FUNCTION($1.t) &&
+ (f = registry_findmember($1.t, "call"))) {
+ $$.t = f->return_type;
+ } else {
+ $$.c = abc_coerce_a($$.c);
+ $$.t = TYPE_ANY;
+ }
+}
+
+RETURN: "return" %prec prec_none {
+ $$ = abc_returnvoid(0);
+}
+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 {
+ $$.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);}
+
+// ----------------------- expression evaluation -------------------------------------
+
+E : CONSTANT
+E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
+E : NEW {$$ = $1;}
+E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
+ $$.t = TYPE_ANY;
+ }
+E : FUNCTIONCALL
+E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+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_equals($$.c);$$.c = abc_not($$.c);
+ $$.t = TYPE_BOOLEAN;
+ }
+
+E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
+ $$.c = $1.c;
+ $$.c = converttype($$.c, $1.t, $$.t);
+ $$.c = abc_dup($$.c);
+ code_t*jmp = $$.c = abc_iftrue($$.c, 0);
+ $$.c = cut_last_push($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.c = converttype($$.c, $3.t, $$.t);
+ code_t*label = $$.c = abc_label($$.c);
+ jmp->branch = label;
+ }
+E : E "&&" E {
+ $$.t = join_types($1.t, $3.t, 'A');
+ /*printf("%08x:\n",$1.t);
+ code_dump($1.c, 0, 0, "", stdout);
+ printf("%08x:\n",$3.t);
+ code_dump($3.c, 0, 0, "", stdout);
+ printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
+ $$.c = $1.c;
+ $$.c = converttype($$.c, $1.t, $$.t);
+ $$.c = abc_dup($$.c);
+ code_t*jmp = $$.c = abc_iffalse($$.c, 0);
+ $$.c = cut_last_push($$.c);
+ $$.c = code_append($$.c,$3.c);
+ $$.c = converttype($$.c, $3.t, $$.t);
+ code_t*label = $$.c = abc_label($$.c);
+ jmp->branch = label;
+ }
+
+E : '!' E {$$.c=$2.c;
+ $$.c = abc_not($$.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 '%' 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_multiply($$.c);$$.c=abc_coerce_a($$.c);
+ $$.t = join_types($1.t, $3.t, '*');
+ }
+
+E : E "as" E
+E : E "is" E
+E : '(' E ')' {$$=$2;}
+E : '-' E {$$=$2;}
+
+E : E '[' E ']' {
+ $$.c = $1.c;
+ $$.c = code_append($$.c, $3.c);
+
+ MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
+ $$.c = abc_getproperty2($$.c, &m);
+}
+
+E : E "+=" E {
+ code_t*c = $3.c;
+ if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
+ c=abc_add_i(c);
+ } else {
+ c=abc_add(c);
+ }
+ c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
+
+ $$.c = toreadwrite($1.c, c, 0);
+ $$.t = $1.t;
+ }
+E : E "-=" E { code_t*c = $3.c;
+ if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
+ c=abc_subtract_i(c);
+ } else {
+ c=abc_subtract(c);
+ }
+ c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
+
+ $$.c = toreadwrite($1.c, c, 0);
+ $$.t = $1.t;
+ }
+E : E '=' E { code_t*c = 0;
+ c = code_append(c, $3.c);
+ c = converttype(c, $3.t, $1.t);
+ $$.c = toreadwrite($1.c, c, 1);
+ $$.t = $1.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);
+ } else {
+ c=abc_increment(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $1.t);
+ $$.c = toreadwrite($1.c, c, 0);
+ $$.t = $1.t;
+ }
+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);
+ } else {
+ c=abc_increment(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $1.t);
+ $$.c = toreadwrite($1.c, c, 0);
+ $$.t = $1.t;
+ }
+
+E : "++" E { code_t*c = 0;
+ classinfo_t*type = $2.t;
+ if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
+ c=abc_increment_i(c);
+ } else {
+ c=abc_increment(c);
+ type = TYPE_NUMBER;
+ }
+ c=converttype(c, type, $2.t);
+ $$.c = toreadwrite($2.c, c, 0);
+ $$.t = $2.t;
+ }
+
+E : E '.' T_IDENTIFIER
+ {$$.c = $1.c;
+ if($$.t) {
+ memberinfo_t*f = registry_findmember($$.t, $3->text);
+
+ if(f && f->slot) {
+ $$.c = abc_getslot($$.c, f->slot);
+ } else {
+ namespace_t ns = {$$.t->access, ""}; // needs to be "", not $$.t->package
+ multiname_t m = {QNAME, &ns, 0, $3->text};
+ $$.c = abc_getproperty2($$.c, &m);
+ }
+ /* determine type */
+ if(f) {
+ if(f->kind == MEMBER_METHOD) {
+ $$.t = TYPE_FUNCTION(f);
+ } else {
+ $$.t = f->type;
+ }
+ } else {
+ $$.c = abc_coerce_a($$.c);
+ $$.t = registry_getanytype();
+ }
+ } else {
+ namespace_t ns = {ACCESS_PACKAGE, ""};
+ multiname_t m = {QNAME, &ns, 0, $3->text};
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.c = abc_coerce_a($$.c);
+ $$.t = registry_getanytype();
+ }
+ }
+
+VAR_READ : T_IDENTIFIER {
+ $$.t = 0;
+ $$.c = 0;
+ int i;
+ memberinfo_t*f = 0;
+ if((i = find_variable($1->text, &$$.t)) >= 0) {
+ // $1 is a local variable
+ $$.c = abc_getlocal($$.c, i);
+ } else if((f = registry_findmember(state->clsinfo, $1->text))) {
+ // $1 is a function in this class
+ if(f->kind == MEMBER_METHOD) {
+ $$.t = TYPE_FUNCTION(f);
+ } else {
+ $$.t = f->type;
+ }
+ if(f->slot>0) {
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getslot($$.c, f->slot);
+ } else {
+ namespace_t ns = {state->clsinfo->access, ""};
+ multiname_t m = {QNAME, &ns, 0, $1->text};
+ $$.c = abc_getlocal_0($$.c);
+ $$.c = abc_getproperty2($$.c, &m);
+ }
+ } else {
+ // let the avm2 resolve $1
+ if(strcmp($1->text,"trace"))
+ warning("Couldn't resolve %s, doing late binding", $1->text);
+ state->late_binding = 1;
+
+ $$.t = 0;
+ $$.c = abc_findpropstrict($$.c, $1->text);
+ $$.c = abc_getproperty($$.c, $1->text);
+ }
+}
+
+
+// ------------------------------------------------------------------------------
+
+
+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_HEADER: NAMESPACE MODIFIERS T_FUNCTION GETSET T_IDENTIFIER '(' PARAMS ')'
+FUNCTION_HEADER: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
+ MAYBETYPE
+
+NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER
+NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_IDENTIFIER
+NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_STRING
+
+//NAMESPACE : {$$=empty_token();}
+//NAMESPACE : T_IDENTIFIER {$$=$1};
+
+CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
+ //MULTINAME(m, registry_getintclass());
+ //$$.c = abc_coerce2($$.c, &m); // FIXME
+ $$.t = TYPE_INT;
+ }
+CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
+ $$.t = TYPE_INT;
+ }
+CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
+ $$.t = TYPE_INT;
+ }
+CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
+ $$.t = TYPE_UINT;
+ }
+CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
+ $$.t = TYPE_FLOAT;
+ }
+CONSTANT : T_STRING {$$.c = abc_pushstring(0, $1);
+ $$.t = TYPE_STRING;
+ }
+CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
+ $$.t = TYPE_BOOLEAN;
+ }
+CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
+ $$.t = TYPE_BOOLEAN;
+ }
+CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
+ $$.t = TYPE_NULL;
+ }
+
+USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
+
+
+//VARIABLE : T_IDENTIFIER
+//VARIABLE : VARIABLE '.' T_IDENTIFIER
+//VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
+//VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
+//VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
+//VARIABLE : VARIABLE '[' EXPRESSION ']' // unqualified expression
+
+GETSET : "get" {$$=$1;}
+ | "set" {$$=$1;}
+ | {$$=empty_token();}
+
+IDECLARATION : VARIABLE_DECLARATION
+IDECLARATION : FUNCTION_DECLARATION
+
+//IDENTIFIER_LIST : T_IDENTIFIER ',' IDENTIFIER_LIST {extend($3,$1);$$=$3;}
+//IDENTIFIER_LIST : T_IDENTIFIER {$$=empty_token();extend($$,$1);}
+
+QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
+QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
+