3 Routines for compiling Flash2 AVM2 ABC Actionscript
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2008 Matthias Kramm <kramm@quiss.org>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
30 #include "tokenizer.h"
44 classinfo_t*classinfo;
45 classinfo_list_t*classinfo_list;
48 unsigned int number_uint;
52 typedcode_list_t*value_list;
59 %token<token> T_IDENTIFIER
60 %token<string> T_STRING
61 %token<token> T_REGEXP
63 %token<number_int> T_INT
64 %token<number_uint> T_UINT
65 %token<number_uint> T_BYTE
66 %token<number_uint> T_SHORT
67 %token<number_float> T_FLOAT
69 %token<token> KW_IMPLEMENTS
70 %token<token> KW_NAMESPACE "namespace"
71 %token<token> KW_PACKAGE "package"
72 %token<token> KW_PROTECTED
73 %token<token> KW_PUBLIC
74 %token<token> KW_PRIVATE
75 %token<token> KW_USE "use"
76 %token<token> KW_INTERNAL
77 %token<token> KW_NEW "new"
78 %token<token> KW_NATIVE
79 %token<token> KW_FUNCTION "function"
80 %token<token> KW_FOR "for"
81 %token<token> KW_CLASS "class"
82 %token<token> KW_CONST "const"
83 %token<token> KW_SET "set"
84 %token<token> KW_STATIC
85 %token<token> KW_IMPORT "import"
86 %token<token> KW_RETURN "return"
87 %token<token> KW_INTERFACE "interface"
88 %token<token> KW_NULL "null"
89 %token<token> KW_VAR "var"
90 %token<token> KW_DYNAMIC
91 %token<token> KW_OVERRIDE
92 %token<token> KW_FINAL
93 %token<token> KW_GET "get"
94 %token<token> KW_EXTENDS
95 %token<token> KW_FALSE "false"
96 %token<token> KW_TRUE "true"
97 %token<token> KW_BOOLEAN "Boolean"
98 %token<token> KW_UINT "uint"
99 %token<token> KW_INT "int"
100 %token<token> KW_WHILE "while"
101 %token<token> KW_NUMBER "Number"
102 %token<token> KW_STRING "String"
103 %token<token> KW_IF "if"
104 %token<token> KW_ELSE "else"
105 %token<token> KW_BREAK "break"
106 %token<token> KW_IS "is"
107 %token<token> KW_AS "as"
109 %token<token> T_EQEQ "=="
110 %token<token> T_EQEQEQ "==="
111 %token<token> T_NE "!="
112 %token<token> T_LE "<="
113 %token<token> T_GE ">="
114 %token<token> T_DIVBY "/="
115 %token<token> T_MODBY "%="
116 %token<token> T_PLUSBY "+="
117 %token<token> T_MINUSBY "-="
118 %token<token> T_SHRBY ">>="
119 %token<token> T_SHLBY "<<="
120 %token<token> T_USHRBY ">>>="
121 %token<token> T_OROR "||"
122 %token<token> T_ANDAND "&&"
123 %token<token> T_COLONCOLON "::"
124 %token<token> T_MINUSMINUS "--"
125 %token<token> T_PLUSPLUS "++"
126 %token<token> T_DOTDOT ".."
127 %token<token> T_DOTDOTDOT "..."
128 %token<token> T_SHL "<<"
129 %token<token> T_USHR ">>>"
130 %token<token> T_SHR ">>"
131 %token<token> T_SEMICOLON ';'
132 %token<token> T_STAR '*'
133 %token<token> T_DOT '.'
135 %type <token> X_IDENTIFIER VARCONST
137 %type <code> CODEPIECE
138 %type <code> CODEBLOCK MAYBECODE
139 %type <token> PACKAGE_DECLARATION
140 %type <token> FUNCTION_DECLARATION
141 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
142 %type <token> CLASS_DECLARATION
143 %type <token> NAMESPACE_DECLARATION
144 %type <token> INTERFACE_DECLARATION
145 %type <code> VOIDEXPRESSION
146 %type <value> EXPRESSION NONCOMMAEXPRESSION
147 %type <value> MAYBEEXPRESSION
149 %type <value> CONSTANT
150 %type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
151 %type <token> USE_NAMESPACE
152 %type <code> FOR_INIT
154 %type <classinfo> MAYBETYPE
157 %type <params> PARAM_LIST
158 %type <params> MAYBE_PARAM_LIST
159 %type <token> MODIFIERS
160 %type <token> MODIFIER_LIST
161 %type <classinfo_list> IMPLEMENTS_LIST
162 %type <classinfo> EXTENDS
163 %type <classinfo_list> EXTENDS_LIST
164 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
165 %type <classinfo_list> QNAME_LIST
166 %type <classinfo> TYPE
168 //%type <token> VARIABLE
169 %type <value> VAR_READ
171 //%type <token> T_IDENTIFIER
172 %type <token> MODIFIER
173 %type <token> PACKAGE
174 %type <value> FUNCTIONCALL
175 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
177 // precedence: from low to high
178 // http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000012.html
193 %nonassoc "!=" "==" "===" "<=" '<' ">=" '>' // TODO: support "a < b < c" syntax?
195 %left prec_belowminus
210 %nonassoc T_IDENTIFIER
211 %left below_semicolon
216 // needed for "return" precedence:
217 %nonassoc T_STRING T_REGEXP
218 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
219 %nonassoc "new" "false" "true" "null"
226 static int yyerror(char*s)
228 syntaxerror("%s", s);
230 static token_t* concat2(token_t* t1, token_t* t2)
233 int l1 = strlen(t1->text);
234 int l2 = strlen(t2->text);
235 t->text = malloc(l1+l2+1);
236 memcpy(t->text , t1->text, l1);
237 memcpy(t->text+l1, t2->text, l2);
241 static token_t* concat3(token_t* t1, token_t* t2, token_t* t3)
244 int l1 = strlen(t1->text);
245 int l2 = strlen(t2->text);
246 int l3 = strlen(t3->text);
247 t->text = malloc(l1+l2+l3+1);
248 memcpy(t->text , t1->text, l1);
249 memcpy(t->text+l1, t2->text, l2);
250 memcpy(t->text+l1+l2, t3->text, l3);
251 t->text[l1+l2+l3] = 0;
254 static char* concat3str(const char* t1, const char* t2, const char* t3)
259 char*text = malloc(l1+l2+l3+1);
260 memcpy(text , t1, l1);
261 memcpy(text+l1, t2, l2);
262 memcpy(text+l1+l2, t3, l3);
267 typedef struct _import {
271 DECLARE_LIST(import);
273 typedef struct _state {
281 /* code that needs to be executed at the start of
282 a method (like initializing local registers) */
285 import_list_t*wildcard_imports;
287 char has_own_imports;
293 code_t*cls_static_init;
304 static state_t* state = 0;
308 #define MULTINAME(m,x) multiname_t m;namespace_t m##_ns;registry_fill_multiname(&m, &m##_ns, x);
310 /* warning: list length of namespace set is undefined */
311 #define MULTINAME_LATE(m, access, package) \
312 namespace_t m##_ns = {access, package}; \
313 namespace_set_t m##_nsset; \
314 namespace_list_t m##_l;m##_l.next = 0; \
315 m##_nsset.namespaces = &m##_l; \
316 m##_nsset = m##_nsset; \
317 m##_l.namespace = &m##_ns; \
318 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
320 static state_list_t*state_stack=0;
322 static void new_state()
325 NEW(state_list_t, sl);
327 state_t*oldstate = state;
329 memcpy(s, state, sizeof(state_t)); //shallow copy
330 sl->next = state_stack;
333 s->local_var_base = array_length(oldstate->vars) + oldstate->local_var_base;
336 s->imports = dict_new();
341 state->vars = array_new();
343 state->has_own_imports = 0;
345 static void state_has_imports()
347 state->wildcard_imports = list_clone(state->wildcard_imports);
348 state->imports = dict_clone(state->imports);
349 state->has_own_imports = 1;
352 static void old_state()
354 if(!state_stack || !state_stack->next)
355 syntaxerror("invalid nesting");
356 state_t*oldstate = state;
357 state_list_t*old = state_stack;
358 state_stack = state_stack->next;
360 state = state_stack->state;
361 /*if(state->initcode) {
362 printf("residual initcode\n");
363 code_dump(state->initcode, 0, 0, "", stdout);
365 if(oldstate->has_own_imports) {
366 list_free(oldstate->wildcard_imports);
367 dict_destroy(oldstate->imports);oldstate->imports=0;
369 state->initcode = code_append(state->initcode, oldstate->initcode);
371 void initialize_state()
375 state->file = abc_file_new();
376 state->file->flags &= ~ABCFILE_LAZY;
378 state->init = abc_initscript(state->file, 0, 0);
379 code_t*c = state->init->method->body->code;
381 c = abc_getlocal_0(c);
382 c = abc_pushscope(c);
384 /* findpropstrict doesn't just return a scope object- it
385 also makes it "active" somehow. Push local_0 on the
386 scope stack and read it back with findpropstrict, it'll
387 contain properties like "trace". Trying to find the same
388 property on a "vanilla" local_0 yields only a "undefined" */
389 //c = abc_findpropstrict(c, "[package]::trace");
391 /*c = abc_getlocal_0(c);
392 c = abc_findpropstrict(c, "[package]::trace");
394 c = abc_setlocal_1(c);
396 c = abc_pushbyte(c, 0);
397 c = abc_setlocal_2(c);
399 code_t*xx = c = abc_label(c);
400 c = abc_findpropstrict(c, "[package]::trace");
401 c = abc_pushstring(c, "prop:");
402 c = abc_hasnext2(c, 1, 2);
404 c = abc_setlocal_3(c);
405 c = abc_callpropvoid(c, "[package]::trace", 2);
406 c = abc_getlocal_3(c);
408 c = abc_iftrue(c,xx);*/
410 c = abc_findpropstrict(c, "[package]::trace");
411 c = abc_pushstring(c, "[entering global init function]");
412 c = abc_callpropvoid(c, "[package]::trace", 1);
414 state->init->method->body->code = c;
416 void* finalize_state()
418 if(state->level!=1) {
419 syntaxerror("unexpected end of file");
421 abc_method_body_t*m = state->init->method->body;
424 __ findpropstrict(m, "[package]::trace");
425 __ pushstring(m, "[leaving global init function]");
426 __ callpropvoid(m, "[package]::trace", 1);
432 static void startpackage(token_t*t)
435 syntaxerror("Packages can not be nested.");
438 char*name = t?t->text:"";
439 /*printf("entering package \"%s\"\n", name);*/
440 state->package = name;
442 static void endpackage()
444 /*printf("leaving package \"%s\"\n", state->package);*/
449 static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, classinfo_list_t*implements, char interface)
452 syntaxerror("inner classes now allowed");
455 char*classname = name->text;
458 classinfo_list_t*mlist=0;
459 /*printf("entering class %s\n", name->text);
460 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token->text);printf("\n");
462 printf(" extends: %s.%s\n", extends->package, extends->name);
463 printf(" implements (%d): ", list_length(implements));
464 for(mlist=implements;mlist;mlist=mlist->next) {
465 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
470 char public=0,internal=0,final=0,sealed=1;
471 for(t=modifiers->tokens;t;t=t->next) {
472 if(t->token->type == KW_INTERNAL) {
473 /* the programmer is being explicit-
474 being internal is the default anyway */
476 } else if(t->token->type == KW_PUBLIC) {
478 } else if(t->token->type == KW_FINAL) {
481 syntaxerror("modifier \"%s\" not supported in class declaration", t->token->text);
485 syntaxerror("public and internal not supported at the same time.");
487 /* create the class name, together with the proper attributes */
491 if(!public && !state->package) {
492 access = ACCESS_PRIVATE; package = current_filename;
493 } else if(!public && state->package) {
494 access = ACCESS_PACKAGEINTERNAL; package = state->package;
495 } else if(state->package) {
496 access = ACCESS_PACKAGE; package = state->package;
498 syntaxerror("public classes only allowed inside a package");
501 if(registry_findclass(package, classname)) {
502 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
505 state->clsinfo = classinfo_register(access, package, classname);
507 MULTINAME(classname2,state->clsinfo);
509 multiname_t*extends2 = sig2mname(extends);
512 state->cls_init = abc_getlocal_0(state->cls_init);
513 state->cls_init = abc_constructsuper(state->cls_init, 0);
516 state->cls = abc_class_new(state->file, &classname2, extends2);
517 if(final) abc_class_final(state->cls);
518 if(sealed) abc_class_sealed(state->cls);
519 if(interface) abc_class_interface(state->cls);
521 for(mlist=implements;mlist;mlist=mlist->next) {
522 MULTINAME(m, mlist->classinfo);
523 abc_class_add_interface(state->cls, &m);
526 /* now write the construction code for this class */
527 int slotindex = abc_initscript_addClassTrait(state->init, &classname2, state->cls);
529 abc_method_body_t*m = state->init->method->body;
530 __ getglobalscope(m);
531 classinfo_t*s = extends;
536 //TODO: take a look at the current scope stack, maybe
537 // we can re-use something
542 multiname_t*s2 = sig2mname(s);
544 multiname_destroy(s2);
546 __ pushscope(m); count++;
547 m->code = m->code->prev->prev; // invert
549 /* continue appending after last op end */
550 while(m->code && m->code->next) m->code = m->code->next;
552 /* TODO: if this is one of *our* classes, we can also
553 do a getglobalscope/getslot <nr> (which references
554 the init function's slots) */
556 __ getlex2(m, extends2);
558 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
559 stack is not the superclass */
560 __ pushscope(m);count++;
563 /* notice: we get a verify error #1107 if the top element on the scope
564 stack is not the global object */
566 __ pushscope(m);count++;
568 __ newclass(m,state->cls);
572 __ setslot(m, slotindex);
574 /* flash.display.MovieClip handling */
575 if(!globalclass && public && classinfo_equals(registry_getMovieClip(),extends)) {
576 if(state->package && state->package[0]) {
577 globalclass = concat3str(state->package, ".", classname);
579 globalclass = strdup(classname);
582 multiname_destroy(extends2);
585 static void endclass()
587 if(state->cls_init) {
588 if(!state->cls->constructor) {
589 abc_method_body_t*m = abc_class_constructor(state->cls, 0, 0);
590 m->code = code_append(m->code, state->cls_init);
591 m->code = abc_returnvoid(m->code);
593 code_t*c = state->cls->constructor->body->code;
594 c = code_append(state->cls_init, c);
595 state->cls->constructor->body->code = c;
599 if(state->cls_static_init) {
600 if(!state->cls->static_constructor) {
601 abc_method_body_t*m = abc_class_staticconstructor(state->cls, 0, 0);
602 m->code = state->cls_static_init;
604 state->cls->static_constructor->body->code =
605 code_append(state->cls_static_init, state->cls->static_constructor->body->code);
611 static void startfunction(token_t*ns, token_t*mod, token_t*getset, token_t*name,
612 params_t*params, classinfo_t*type)
616 state->function = name->text;
619 syntaxerror("not able to start another method scope");
622 multiname_t*type2 = sig2mname(type);
623 if(!strcmp(state->clsinfo->name,name->text)) {
624 state->m = abc_class_constructor(state->cls, type2, 0);
626 state->minfo = memberinfo_register(state->clsinfo, name->text, MEMBER_METHOD);
627 state->m = abc_class_method(state->cls, type2, name->text, 0);
628 // getslot on a member slot only returns "undefined", so no need
629 // to actually store these
630 //state->minfo->slot = state->m->method->trait->slot_id;
632 if(getset->type == KW_GET) {
633 state->m->method->trait->kind = TRAIT_GETTER;
635 if(getset->type == KW_SET) {
636 state->m->method->trait->kind = TRAIT_SETTER;
638 if(params->varargs) {
639 state->m->method->flags |= METHOD_NEED_REST;
643 for(p=params->list;p;p=p->next) {
644 if(params->varargs && !p->next) {
645 break; //varargs: omit last parameter in function signature
647 multiname_t*m = sig2mname(p->param->type);
648 list_append(state->m->method->parameters, m);
651 /* state->vars is initialized by state_new */
652 array_append(state->vars, "this", state->clsinfo);
653 for(p=params->list;p;p=p->next) {
654 array_append(state->vars, p->param->name, 0);
657 static void endfunction(code_t*body)
660 if(state->late_binding) {
661 c = abc_getlocal_0(c);
662 c = abc_pushscope(c);
664 c = code_append(c, state->initcode);
665 c = code_append(c, body);
667 /* append return if necessary */
668 if(!c || c->opcode != OPCODE_RETURNVOID &&
669 c->opcode != OPCODE_RETURNVALUE)
670 c = abc_returnvoid(c);
672 if(state->m->code) syntaxerror("internal error");
678 static token_t* empty_token()
686 void extend(token_t*list, token_t*add) {
687 list_append(list->tokens,add);
689 list->text = add->text;
691 void extend_s(token_t*list, char*seperator, token_t*add) {
692 list_append(list->tokens,add);
693 char*t1 = list->text;
699 list->text = malloc(l1+l2+l3+1);
700 strcpy(list->text, t1);
701 strcpy(list->text+l1, t2);
702 strcpy(list->text+l1+l2, t3);
703 list->text[l1+l2+l3]=0;
706 static int find_variable(char*name, classinfo_t**m)
708 state_list_t* s = state_stack;
710 int i = array_find(s->state->vars, name);
713 *m = array_getvalue(s->state->vars, i);
715 return i + s->state->local_var_base;
721 static int find_variable_safe(char*name, classinfo_t**m)
723 int i = find_variable(name, m);
725 syntaxerror("undefined variable: %s", name);
728 static char variable_exists(char*name)
730 return array_contains(state->vars, name);
732 static int new_variable(char*name, classinfo_t*type)
734 return array_append(state->vars, name, type) + state->local_var_base;
736 #define TEMPVARNAME "__as3_temp__"
737 static int gettempvar()
739 int i = find_variable(TEMPVARNAME, 0);
741 return new_variable(TEMPVARNAME, 0);
747 code_t* killvars(code_t*c)
750 for(t=0;t<state->vars->num;t++) {
751 classinfo_t*type = array_getvalue(state->vars, t);
752 //do this always, otherwise register types don't match
753 //in the verifier when doing nested loops
754 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
755 c = abc_kill(c, t+state->local_var_base);
761 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
766 void breakjumpsto(code_t*c, code_t*jump)
771 if(c->opcode == OPCODE___BREAK__) {
772 c->opcode = OPCODE_JUMP;
779 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
782 return registry_getanytype();
783 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
784 return registry_getanytype();
787 return registry_getanytype();
789 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
794 /*TODO: can omit this if from is zero? */
795 return abc_coerce_a(c);
797 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
798 MULTINAME(m, TYPE_UINT);
799 return abc_coerce2(c, &m);
801 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
802 MULTINAME(m, TYPE_INT);
803 return abc_coerce2(c, &m);
808 code_t*defaultvalue(code_t*c, classinfo_t*type)
810 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
811 c = abc_pushbyte(c, 0);
812 } else if(TYPE_IS_BOOLEAN(type)) {
813 c = abc_pushfalse(c);
820 char is_pushundefined(code_t*c)
822 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
825 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign)
829 [prefix code] [read instruction]
833 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
836 if(in && in->opcode == OPCODE_COERCE_A) {
837 in = code_cutlast(in);
840 syntaxerror("internal error");
842 /* chop off read instruction */
846 prefix = r->prev;r->prev = 0;
852 /* generate the write instruction, and maybe append a dup to the prefix code */
853 code_t* write = abc_nop(0);
854 if(r->opcode == OPCODE_GETPROPERTY) {
855 write->opcode = OPCODE_SETPROPERTY;
856 multiname_t*m = (multiname_t*)r->data[0];
857 write->data[0] = multiname_clone(m);
859 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
861 prefix = abc_dup(prefix); // we need the object, too
862 } else if(r->opcode == OPCODE_GETSLOT) {
863 write->opcode = OPCODE_SETSLOT;
864 write->data[0] = r->data[0];
866 prefix = abc_dup(prefix); // we need the object, too
867 } else if(r->opcode == OPCODE_GETLOCAL) {
868 write->opcode = OPCODE_SETLOCAL;
869 write->data[0] = r->data[0];
870 } else if(r->opcode == OPCODE_GETLOCAL_0) {
871 write->opcode = OPCODE_SETLOCAL_0;
872 } else if(r->opcode == OPCODE_GETLOCAL_1) {
873 write->opcode = OPCODE_SETLOCAL_1;
874 } else if(r->opcode == OPCODE_GETLOCAL_2) {
875 write->opcode = OPCODE_SETLOCAL_2;
876 } else if(r->opcode == OPCODE_GETLOCAL_3) {
877 write->opcode = OPCODE_SETLOCAL_3;
879 code_dump(r, 0, 0, "", stdout);
880 syntaxerror("illegal lvalue: can't assign a value to this expression");
886 /* read value, modify it with middle part, and write it again,
887 using prefix only once and making sure (by using a temporary
888 register) that the return value is what we just wrote */
890 c = code_append(c, prefix);
891 c = code_append(c, r);
892 c = code_append(c, middlepart);
894 c = abc_setlocal(c, temp);
895 c = code_append(c, write);
896 c = abc_getlocal(c, temp);
897 c = abc_kill(c, temp);
899 /* simpler version: overwrite the value without reading
902 c = code_append(c, prefix);
905 c = code_append(c, middlepart);
906 c = code_append(c, write);
907 c = code_append(c, r);
919 /* ------------ code blocks / statements ---------------- */
923 MAYBECODE: CODE {$$=$1;}
924 MAYBECODE: {$$=code_new();}
926 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
927 CODE: CODEPIECE {$$=$1;}
929 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
930 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
931 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
932 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
933 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
934 CODEPIECE: ';' {$$=code_new();}
935 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
936 CODEPIECE: VOIDEXPRESSION {$$=$1}
937 CODEPIECE: FOR {$$=$1}
938 CODEPIECE: WHILE {$$=$1}
939 CODEPIECE: BREAK {$$=$1}
940 CODEPIECE: RETURN {$$=$1}
941 CODEPIECE: IF {$$=$1}
942 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
943 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
945 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
946 CODEBLOCK : CODEPIECE ';' {$$=$1;}
947 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
949 /* ------------ variables --------------------------- */
951 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
952 | {$$.c=abc_pushundefined(0);
956 VAR : "const" | "var"
957 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
959 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
960 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
962 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
964 if(variable_exists($2->text))
965 syntaxerror("Variable %s already defined", $2->text);
967 if(!is_subtype_of($4.t, $3)) {
968 syntaxerror("Can't convert %s to %s", $4.t->name,
972 int index = new_variable($2->text, $3);
975 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
977 $$ = converttype($$, $4.t, $3);
978 $$ = abc_setlocal($$, index);
980 $$ = defaultvalue(0, $3);
981 $$ = abc_setlocal($$, index);
984 /* push default value for type on stack */
985 state->initcode = defaultvalue(state->initcode, $3);
986 state->initcode = abc_setlocal(state->initcode, index);
988 /* only bother to actually set this variable if its syntax is either
993 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
995 $$ = abc_coerce_a($$);
996 $$ = abc_setlocal($$, index);
1002 /* that's the default for a local register, anyway
1004 state->initcode = abc_pushundefined(state->initcode);
1005 state->initcode = abc_setlocal(state->initcode, index);
1007 printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1010 /* ------------ control flow ------------------------- */
1012 MAYBEELSE: %prec prec_none {$$ = code_new();}
1013 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1014 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1016 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1017 $$ = state->initcode;state->initcode=0;
1019 $$ = code_append($$, $4.c);
1020 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1022 $$ = code_append($$, $6);
1024 myjmp = $$ = abc_jump($$, 0);
1026 myif->branch = $$ = abc_label($$);
1028 $$ = code_append($$, $7);
1029 myjmp->branch = $$ = abc_label($$);
1032 $$ = killvars($$);old_state();
1035 FOR_INIT : {$$=code_new();}
1036 FOR_INIT : VARIABLE_DECLARATION
1037 FOR_INIT : VOIDEXPRESSION
1039 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1040 $$ = state->initcode;state->initcode=0;
1042 $$ = code_append($$, $4);
1043 code_t*loopstart = $$ = abc_label($$);
1044 $$ = code_append($$, $6.c);
1045 code_t*myif = $$ = abc_iffalse($$, 0);
1046 $$ = code_append($$, $10);
1047 $$ = code_append($$, $8);
1048 $$ = abc_jump($$, loopstart);
1049 code_t*out = $$ = abc_label($$);
1050 breakjumpsto($$, out);
1053 $$ = killvars($$);old_state();
1056 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1057 $$ = state->initcode;state->initcode=0;
1059 code_t*myjmp = $$ = abc_jump($$, 0);
1060 code_t*loopstart = $$ = abc_label($$);
1061 $$ = code_append($$, $6);
1062 myjmp->branch = $$ = abc_label($$);
1063 $$ = code_append($$, $4.c);
1064 $$ = abc_iftrue($$, loopstart);
1065 code_t*out = $$ = abc_label($$);
1066 breakjumpsto($$, out);
1068 $$ = killvars($$);old_state();
1072 $$ = abc___break__(0);
1075 /* ------------ packages and imports ---------------- */
1077 X_IDENTIFIER: T_IDENTIFIER
1080 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,$2,$3);}
1081 PACKAGE: X_IDENTIFIER {$$=$1;}
1083 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1084 PACKAGE_DECLARATION : "package" '{' {startpackage(0)} MAYBECODE '}' {endpackage()}
1086 IMPORT : "import" QNAME {
1089 syntaxerror("Couldn't import class\n");
1090 state_has_imports();
1091 dict_put(state->imports, c->name, c);
1094 IMPORT : "import" PACKAGE '.' '*' {
1096 i->package = $2->text;
1097 state_has_imports();
1098 list_append(state->wildcard_imports, i);
1102 /* ------------ classes and interfaces (header) -------------- */
1104 MODIFIERS : {$$=empty_token();}
1105 MODIFIERS : MODIFIER_LIST {$$=$1}
1106 MODIFIER_LIST : MODIFIER MODIFIER_LIST {extend($2,$1);$$=$2;}
1107 MODIFIER_LIST : MODIFIER {$$=empty_token();extend($$,$1);}
1108 MODIFIER : KW_PUBLIC | KW_PRIVATE | KW_PROTECTED | KW_STATIC | KW_DYNAMIC | KW_FINAL | KW_OVERRIDE | KW_NATIVE | KW_INTERNAL
1110 EXTENDS : {$$=registry_getobjectclass();}
1111 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1113 EXTENDS_LIST : {$$=list_new();}
1114 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1116 IMPLEMENTS_LIST : {$$=list_new();}
1117 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1119 CLASS_DECLARATION : MODIFIERS "class" T_IDENTIFIER
1120 EXTENDS IMPLEMENTS_LIST
1121 '{' {startclass($1,$3,$4,$5, 0);}
1122 MAYBE_DECLARATION_LIST
1125 INTERFACE_DECLARATION : MODIFIERS "interface" T_IDENTIFIER
1127 '{' {startclass($1,$3,0,$4,1);}
1128 MAYBE_IDECLARATION_LIST
1131 /* ------------ classes and interfaces (body) -------------- */
1133 MAYBE_DECLARATION_LIST :
1134 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1135 DECLARATION_LIST : DECLARATION
1136 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1138 DECLARATION : SLOT_DECLARATION
1139 DECLARATION : FUNCTION_DECLARATION
1141 /* ------------ classes and interfaces (body, slots ) ------- */
1143 VARCONST: "var" | "const"
1144 SLOT_DECLARATION: MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1146 memberinfo_t* info = memberinfo_register(state->clsinfo, $3->text, MEMBER_SLOT);
1152 t=abc_class_slot(state->cls, $3->text, &m);
1154 t=abc_class_slot(state->cls, $3->text, 0);
1156 if($2->type==KW_CONST) {
1157 t->kind= TRAIT_CONST;
1159 info->slot = t->slot_id;
1160 if($5.c && !is_pushundefined($5.c)) {
1162 c = abc_getlocal_0(c);
1163 c = code_append(c, $5.c);
1164 c = converttype(c, $5.t, $4);
1165 c = abc_setslot(c, t->slot_id);
1166 //c = abc_setproperty(c, $3->text);
1167 state->cls_init = code_append(state->cls_init, c);
1171 /* ------------ classes and interfaces (body, functions) ------- */
1173 // non-vararg version
1175 memset(&$$,0,sizeof($$));
1177 MAYBE_PARAM_LIST: PARAM_LIST {
1182 MAYBE_PARAM_LIST: "..." PARAM {
1183 memset(&$$,0,sizeof($$));
1185 list_append($$.list, $2);
1187 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1190 list_append($$.list, $4);
1194 PARAM_LIST: PARAM_LIST ',' PARAM {
1196 list_append($$.list, $3);
1199 memset(&$$,0,sizeof($$));
1200 list_append($$.list, $1);
1202 PARAM: T_IDENTIFIER ':' TYPE {
1203 $$ = malloc(sizeof(param_t));
1204 $$->name=$1->text;$$->type = $3;
1206 PARAM: T_IDENTIFIER {
1207 $$ = malloc(sizeof(param_t));
1208 $$->name=$1->text;$$->type = TYPE_ANY;
1211 FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1212 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1214 if(!state->m) syntaxerror("internal error: undefined function");
1218 /* ------------- package + class ids --------------- */
1220 CLASS: T_IDENTIFIER {
1222 /* try current package */
1223 $$ = registry_findclass(state->package, $1->text);
1225 /* try explicit imports */
1226 dictentry_t* e = dict_get_slot(state->imports, $1->text);
1230 if(!strcmp(e->key, $1->text)) {
1231 $$ = (classinfo_t*)e->data;
1236 /* try package.* imports */
1237 import_list_t*l = state->wildcard_imports;
1241 //printf("does package %s contain a class %s?\n", l->import->package, $1->text);
1242 $$ = registry_findclass(l->import->package, $1->text);
1246 /* try global package */
1248 $$ = registry_findclass("", $1->text);
1251 if(!$$) syntaxerror("Could not find class %s\n", $1->text);
1254 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1255 $$ = registry_findclass($1->text, $3->text);
1256 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1->text, $3->text);
1259 QNAME: PACKAGEANDCLASS
1263 /* ----------function calls, constructor calls ------ */
1265 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1266 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1268 MAYBE_EXPRESSION_LIST : {$$=0;}
1269 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1270 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1271 typedcode_t*t = malloc(sizeof(typedcode_t));
1273 list_append($$, t);}
1274 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1275 typedcode_t*t = malloc(sizeof(typedcode_t));
1277 list_append($$, t);}
1279 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1283 /* TODO: why do we have to *find* our own classes? */
1284 $$.c = abc_findpropstrict2($$.c, &m);
1286 typedcode_list_t*l = $3;
1289 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1293 $$.c = abc_constructprop2($$.c, &m, len);
1297 /* TODO: use abc_call (for calling local variables),
1298 abc_callstatic (for calling own methods)
1301 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1302 typedcode_list_t*l = $3;
1304 code_t*paramcode = 0;
1306 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1312 if($$.c->opcode == OPCODE_COERCE_A) {
1313 $$.c = code_cutlast($$.c);
1317 multiname_t*name = 0;
1318 if($$.c->opcode == OPCODE_GETPROPERTY) {
1319 name = multiname_clone($$.c->data[0]);
1320 $$.c = code_cutlast($$.c);
1321 $$.c = code_append($$.c, paramcode);
1322 $$.c = abc_callproperty2($$.c, name, len);
1323 } else if($$.c->opcode == OPCODE_GETSLOT) {
1324 int slot = (int)(ptroff_t)$$.c->data[0];
1325 trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME
1326 if(t->kind!=TRAIT_METHOD) {
1327 //flash allows to assign closures to members.
1328 //syntaxerror("not a function");
1331 $$.c = code_cutlast($$.c);
1332 $$.c = code_append($$.c, paramcode);
1333 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1334 $$.c = abc_callproperty2($$.c, name, len);
1336 $$.c = abc_getlocal_0($$.c);
1337 $$.c = code_append($$.c, paramcode);
1338 $$.c = abc_call($$.c, len);
1342 if(TYPE_IS_FUNCTION($1.t) &&
1343 (f = registry_findmember($1.t, "__funcptr__"))) {
1344 $$.t = f->return_type;
1346 $$.c = abc_coerce_a($$.c);
1351 RETURN: "return" %prec prec_none {
1352 $$ = abc_returnvoid(0);
1354 RETURN: "return" EXPRESSION {
1356 $$ = abc_returnvalue($$);
1358 // ----------------------- expression types -------------------------------------
1360 NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;}
1361 EXPRESSION : E %prec prec_belowminus {$$ = $1;}
1362 EXPRESSION : EXPRESSION ',' E %prec prec_belowminus {
1364 $$.c = cut_last_push($$.c);
1365 $$.c = code_append($$.c,$3.c);
1368 VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=cut_last_push($1.c);}
1370 // ----------------------- expression evaluation -------------------------------------
1373 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1375 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1379 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1380 $$.t = TYPE_BOOLEAN;
1382 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1383 $$.t = TYPE_BOOLEAN;
1385 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1386 $$.t = TYPE_BOOLEAN;
1388 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1389 $$.t = TYPE_BOOLEAN;
1391 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1392 $$.t = TYPE_BOOLEAN;
1394 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1395 $$.t = TYPE_BOOLEAN;
1397 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1398 $$.t = TYPE_BOOLEAN;
1401 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1403 $$.c = converttype($$.c, $1.t, $$.t);
1404 $$.c = abc_dup($$.c);
1405 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1406 $$.c = cut_last_push($$.c);
1407 $$.c = code_append($$.c,$3.c);
1408 $$.c = converttype($$.c, $3.t, $$.t);
1409 code_t*label = $$.c = abc_label($$.c);
1410 jmp->branch = label;
1412 E : E "&&" E {$$.t = join_types($1.t, $3.t, 'A');
1414 $$.c = converttype($$.c, $1.t, $$.t);
1415 $$.c = abc_dup($$.c);
1416 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1417 $$.c = cut_last_push($$.c);
1418 $$.c = code_append($$.c,$3.c);
1419 $$.c = converttype($$.c, $3.t, $$.t);
1420 code_t*label = $$.c = abc_label($$.c);
1421 jmp->branch = label;
1424 E : '!' E {$$.c=$2.c;
1425 $$.c = abc_not($$.c);
1426 $$.t = TYPE_BOOLEAN;
1431 E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c);
1432 $$.t = join_types($1.t, $3.t, '+');
1434 E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c);
1435 $$.t = join_types($1.t, $3.t, '%');
1437 E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c);
1438 $$.t = join_types($1.t, $3.t, '*');
1443 E : '(' E ')' {$$=$2;}
1448 $$.c = code_append($$.c, $3.c);
1450 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
1451 $$.c = abc_getproperty2($$.c, &m);
1456 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1461 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1463 $$.c = toreadwrite($1.c, c, 0);
1466 E : E "-=" E { code_t*c = $3.c;
1467 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1468 c=abc_subtract_i(c);
1472 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
1474 $$.c = toreadwrite($1.c, c, 0);
1477 E : E '=' E { code_t*c = 0;
1478 c = code_append(c, $3.c);
1479 c = converttype(c, $3.t, $1.t);
1480 $$.c = toreadwrite($1.c, c, 1);
1484 // TODO: use inclocal where appropriate
1485 E : E "++" { code_t*c = 0;
1486 classinfo_t*type = $1.t;
1487 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1488 c=abc_increment_i(c);
1493 c=converttype(c, type, $1.t);
1494 $$.c = toreadwrite($1.c, c, 0);
1497 E : E "--" { code_t*c = 0;
1498 classinfo_t*type = $1.t;
1499 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1500 c=abc_increment_i(c);
1505 c=converttype(c, type, $1.t);
1506 $$.c = toreadwrite($1.c, c, 0);
1510 E : E '.' T_IDENTIFIER
1513 memberinfo_t*f = registry_findmember($$.t, $3->text);
1516 $$.c = abc_getslot($$.c, f->slot);
1518 namespace_t ns = {$$.t->access, ""}; // needs to be "", not $$.t->package
1519 multiname_t m = {QNAME, &ns, 0, $3->text};
1520 $$.c = abc_getproperty2($$.c, &m);
1522 /* determine type */
1524 if(f->kind == MEMBER_METHOD) {
1525 $$.t = TYPE_FUNCTION(f);
1530 $$.c = abc_coerce_a($$.c);
1531 $$.t = registry_getanytype();
1534 namespace_t ns = {ACCESS_PACKAGE, ""};
1535 multiname_t m = {QNAME, &ns, 0, $3->text};
1536 $$.c = abc_getproperty2($$.c, &m);
1537 $$.c = abc_coerce_a($$.c);
1538 $$.t = registry_getanytype();
1542 VAR_READ : T_IDENTIFIER {
1547 if((i = find_variable($1->text, &$$.t)) >= 0) {
1548 // $1 is a local variable
1549 $$.c = abc_getlocal($$.c, i);
1550 } else if((f = registry_findmember(state->clsinfo, $1->text))) {
1551 // $1 is a function in this class
1552 if(f->kind == MEMBER_METHOD) {
1553 $$.t = TYPE_FUNCTION(f);
1558 $$.c = abc_getlocal_0($$.c);
1559 $$.c = abc_getslot($$.c, f->slot);
1561 namespace_t ns = {state->clsinfo->access, ""};
1562 multiname_t m = {QNAME, &ns, 0, $1->text};
1563 $$.c = abc_getlocal_0($$.c);
1564 $$.c = abc_getproperty2($$.c, &m);
1567 // let the avm2 resolve $1
1568 if(strcmp($1->text,"trace"))
1569 warning("Couldn't resolve %s, doing late binding", $1->text);
1570 state->late_binding = 1;
1573 $$.c = abc_findpropstrict($$.c, $1->text);
1574 $$.c = abc_getproperty($$.c, $1->text);
1579 // ------------------------------------------------------------------------------
1582 TYPE : QNAME {$$=$1;}
1583 | '*' {$$=registry_getanytype();}
1584 | "String" {$$=registry_getstringclass();}
1585 | "int" {$$=registry_getintclass();}
1586 | "uint" {$$=registry_getuintclass();}
1587 | "Boolean" {$$=registry_getbooleanclass();}
1588 | "Number" {$$=registry_getnumberclass();}
1590 MAYBETYPE: ':' TYPE {$$=$2;}
1593 //FUNCTION_HEADER: NAMESPACE MODIFIERS T_FUNCTION GETSET T_IDENTIFIER '(' PARAMS ')'
1594 FUNCTION_HEADER: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1597 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER
1598 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_IDENTIFIER
1599 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_STRING
1601 //NAMESPACE : {$$=empty_token();}
1602 //NAMESPACE : T_IDENTIFIER {$$=$1};
1604 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1605 //MULTINAME(m, registry_getintclass());
1606 //$$.c = abc_coerce2($$.c, &m); // FIXME
1609 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1612 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1615 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1618 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1621 CONSTANT : T_STRING {$$.c = abc_pushstring(0, $1);
1624 CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
1625 $$.t = TYPE_BOOLEAN;
1627 CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
1628 $$.t = TYPE_BOOLEAN;
1630 CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
1634 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
1637 //VARIABLE : T_IDENTIFIER
1638 //VARIABLE : VARIABLE '.' T_IDENTIFIER
1639 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
1640 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
1641 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
1642 //VARIABLE : VARIABLE '[' EXPRESSION ']' // unqualified expression
1644 GETSET : "get" {$$=$1;}
1646 | {$$=empty_token();}
1648 IDECLARATION : VARIABLE_DECLARATION
1649 IDECLARATION : FUNCTION_DECLARATION
1651 //IDENTIFIER_LIST : T_IDENTIFIER ',' IDENTIFIER_LIST {extend($3,$1);$$=$3;}
1652 //IDENTIFIER_LIST : T_IDENTIFIER {$$=empty_token();extend($$,$1);}
1654 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1655 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1658 MAYBE_IDECLARATION_LIST :
1659 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1660 IDECLARATION_LIST : IDECLARATION
1661 IDECLARATION_LIST : IDECLARATION_LIST FUNCTION_HEADER
1664 // keywords: as break case catch class const continue default delete do else extends false finally for function if implements import in instanceof interface internal is native new null package private protected public return super switch this throw to true try typeof use var void while with
1665 // syntactic keywords: each get set namespace include dynamic final native override static