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;
60 %token<token> T_IDENTIFIER
61 %token<string> T_STRING
62 %token<token> T_REGEXP
64 %token<number_int> T_INT
65 %token<number_uint> T_UINT
66 %token<number_uint> T_BYTE
67 %token<number_uint> T_SHORT
68 %token<number_float> T_FLOAT
70 %token<token> KW_IMPLEMENTS
71 %token<token> KW_NAMESPACE "namespace"
72 %token<token> KW_PACKAGE "package"
73 %token<token> KW_PROTECTED
74 %token<token> KW_PUBLIC
75 %token<token> KW_PRIVATE
76 %token<token> KW_USE "use"
77 %token<token> KW_INTERNAL
78 %token<token> KW_NEW "new"
79 %token<token> KW_NATIVE
80 %token<token> KW_FUNCTION "function"
81 %token<token> KW_FOR "for"
82 %token<token> KW_CLASS "class"
83 %token<token> KW_CONST "const"
84 %token<token> KW_SET "set"
85 %token<token> KW_STATIC
86 %token<token> KW_IMPORT "import"
87 %token<token> KW_RETURN "return"
88 %token<token> KW_INTERFACE "interface"
89 %token<token> KW_NULL "null"
90 %token<token> KW_VAR "var"
91 %token<token> KW_DYNAMIC
92 %token<token> KW_OVERRIDE
93 %token<token> KW_FINAL
94 %token<token> KW_GET "get"
95 %token<token> KW_EXTENDS
96 %token<token> KW_FALSE "false"
97 %token<token> KW_TRUE "true"
98 %token<token> KW_BOOLEAN "Boolean"
99 %token<token> KW_UINT "uint"
100 %token<token> KW_INT "int"
101 %token<token> KW_WHILE "while"
102 %token<token> KW_NUMBER "Number"
103 %token<token> KW_STRING "String"
104 %token<token> KW_IF "if"
105 %token<token> KW_ELSE "else"
106 %token<token> KW_BREAK "break"
107 %token<token> KW_IS "is"
108 %token<token> KW_AS "as"
110 %token<token> T_EQEQ "=="
111 %token<token> T_EQEQEQ "==="
112 %token<token> T_NE "!="
113 %token<token> T_LE "<="
114 %token<token> T_GE ">="
115 %token<token> T_DIVBY "/="
116 %token<token> T_MODBY "%="
117 %token<token> T_PLUSBY "+="
118 %token<token> T_MINUSBY "-="
119 %token<token> T_SHRBY ">>="
120 %token<token> T_SHLBY "<<="
121 %token<token> T_USHRBY ">>>="
122 %token<token> T_OROR "||"
123 %token<token> T_ANDAND "&&"
124 %token<token> T_COLONCOLON "::"
125 %token<token> T_MINUSMINUS "--"
126 %token<token> T_PLUSPLUS "++"
127 %token<token> T_DOTDOT ".."
128 %token<token> T_DOTDOTDOT "..."
129 %token<token> T_SHL "<<"
130 %token<token> T_USHR ">>>"
131 %token<token> T_SHR ">>"
132 %token<token> T_SEMICOLON ';'
133 %token<token> T_STAR '*'
134 %token<token> T_DOT '.'
136 %type <token> X_IDENTIFIER VARCONST
138 %type <code> CODEPIECE
139 %type <code> CODEBLOCK MAYBECODE
140 %type <token> PACKAGE_DECLARATION
141 %type <token> FUNCTION_DECLARATION
142 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
143 %type <token> CLASS_DECLARATION
144 %type <token> NAMESPACE_DECLARATION
145 %type <token> INTERFACE_DECLARATION
146 %type <code> VOIDEXPRESSION
147 %type <value> EXPRESSION NONCOMMAEXPRESSION
148 %type <value> MAYBEEXPRESSION
150 %type <value> CONSTANT
151 %type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
152 %type <token> USE_NAMESPACE
153 %type <code> FOR_INIT
155 %type <classinfo> MAYBETYPE
158 %type <params> PARAM_LIST
159 %type <params> MAYBE_PARAM_LIST
160 %type <token> MODIFIERS
161 %type <token> MODIFIER_LIST
162 %type <constant> STATICCONSTANT MAYBESTATICCONSTANT
163 %type <classinfo_list> IMPLEMENTS_LIST
164 %type <classinfo> EXTENDS
165 %type <classinfo_list> EXTENDS_LIST
166 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
167 %type <classinfo_list> QNAME_LIST
168 %type <classinfo> TYPE
170 //%type <token> VARIABLE
171 %type <value> VAR_READ
173 //%type <token> T_IDENTIFIER
174 %type <token> MODIFIER
175 %type <token> PACKAGE
176 %type <value> FUNCTIONCALL
177 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
179 // precedence: from low to high
180 // http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000012.html
195 %nonassoc "!=" "==" "===" "<=" '<' ">=" '>' // TODO: support "a < b < c" syntax?
197 %left prec_belowminus
212 %nonassoc T_IDENTIFIER
213 %left below_semicolon
218 // needed for "return" precedence:
219 %nonassoc T_STRING T_REGEXP
220 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
221 %nonassoc "new" "false" "true" "null"
228 static int yyerror(char*s)
230 syntaxerror("%s", s);
232 static token_t* concat2(token_t* t1, token_t* t2)
235 int l1 = strlen(t1->text);
236 int l2 = strlen(t2->text);
237 t->text = malloc(l1+l2+1);
238 memcpy(t->text , t1->text, l1);
239 memcpy(t->text+l1, t2->text, l2);
243 static token_t* concat3(token_t* t1, token_t* t2, token_t* t3)
246 int l1 = strlen(t1->text);
247 int l2 = strlen(t2->text);
248 int l3 = strlen(t3->text);
249 t->text = malloc(l1+l2+l3+1);
250 memcpy(t->text , t1->text, l1);
251 memcpy(t->text+l1, t2->text, l2);
252 memcpy(t->text+l1+l2, t3->text, l3);
253 t->text[l1+l2+l3] = 0;
256 static char* concat3str(const char* t1, const char* t2, const char* t3)
261 char*text = malloc(l1+l2+l3+1);
262 memcpy(text , t1, l1);
263 memcpy(text+l1, t2, l2);
264 memcpy(text+l1+l2, t3, l3);
269 typedef struct _import {
273 DECLARE_LIST(import);
275 typedef struct _state {
283 /* code that needs to be executed at the start of
284 a method (like initializing local registers) */
287 import_list_t*wildcard_imports;
289 char has_own_imports;
295 code_t*cls_static_init;
305 typedef struct _global {
309 static global_t*global = 0;
310 static state_t* state = 0;
314 #define MULTINAME(m,x) multiname_t m;namespace_t m##_ns;registry_fill_multiname(&m, &m##_ns, x);
316 /* warning: list length of namespace set is undefined */
317 #define MULTINAME_LATE(m, access, package) \
318 namespace_t m##_ns = {access, package}; \
319 namespace_set_t m##_nsset; \
320 namespace_list_t m##_l;m##_l.next = 0; \
321 m##_nsset.namespaces = &m##_l; \
322 m##_nsset = m##_nsset; \
323 m##_l.namespace = &m##_ns; \
324 multiname_t m = {MULTINAMEL, 0, &m##_nsset, 0};
326 static state_list_t*state_stack=0;
328 static void init_globals()
330 global = rfx_calloc(sizeof(global_t));
333 static void new_state()
336 NEW(state_list_t, sl);
338 state_t*oldstate = state;
340 memcpy(s, state, sizeof(state_t)); //shallow copy
341 sl->next = state_stack;
344 s->imports = dict_new();
349 state->vars = dict_new();
351 state->has_own_imports = 0;
353 static void state_has_imports()
355 state->wildcard_imports = list_clone(state->wildcard_imports);
356 state->imports = dict_clone(state->imports);
357 state->has_own_imports = 1;
360 static void old_state()
362 if(!state_stack || !state_stack->next)
363 syntaxerror("invalid nesting");
364 state_t*oldstate = state;
365 state_list_t*old = state_stack;
366 state_stack = state_stack->next;
368 state = state_stack->state;
369 /*if(state->initcode) {
370 printf("residual initcode\n");
371 code_dump(state->initcode, 0, 0, "", stdout);
373 if(oldstate->has_own_imports) {
374 list_free(oldstate->wildcard_imports);
375 dict_destroy(oldstate->imports);oldstate->imports=0;
377 state->initcode = code_append(state->initcode, oldstate->initcode);
379 void initialize_state()
384 state->file = abc_file_new();
385 state->file->flags &= ~ABCFILE_LAZY;
387 state->init = abc_initscript(state->file, 0, 0);
388 code_t*c = state->init->method->body->code;
390 c = abc_getlocal_0(c);
391 c = abc_pushscope(c);
393 /* findpropstrict doesn't just return a scope object- it
394 also makes it "active" somehow. Push local_0 on the
395 scope stack and read it back with findpropstrict, it'll
396 contain properties like "trace". Trying to find the same
397 property on a "vanilla" local_0 yields only a "undefined" */
398 //c = abc_findpropstrict(c, "[package]::trace");
400 /*c = abc_getlocal_0(c);
401 c = abc_findpropstrict(c, "[package]::trace");
403 c = abc_setlocal_1(c);
405 c = abc_pushbyte(c, 0);
406 c = abc_setlocal_2(c);
408 code_t*xx = c = abc_label(c);
409 c = abc_findpropstrict(c, "[package]::trace");
410 c = abc_pushstring(c, "prop:");
411 c = abc_hasnext2(c, 1, 2);
413 c = abc_setlocal_3(c);
414 c = abc_callpropvoid(c, "[package]::trace", 2);
415 c = abc_getlocal_3(c);
417 c = abc_iftrue(c,xx);*/
419 c = abc_findpropstrict(c, "[package]::trace");
420 c = abc_pushstring(c, "[entering global init function]");
421 c = abc_callpropvoid(c, "[package]::trace", 1);
423 state->init->method->body->code = c;
425 void* finalize_state()
427 if(state->level!=1) {
428 syntaxerror("unexpected end of file");
430 abc_method_body_t*m = state->init->method->body;
433 __ findpropstrict(m, "[package]::trace");
434 __ pushstring(m, "[leaving global init function]");
435 __ callpropvoid(m, "[package]::trace", 1);
441 static void startpackage(token_t*t)
444 syntaxerror("Packages can not be nested.");
447 char*name = t?t->text:"";
448 /*printf("entering package \"%s\"\n", name);*/
449 state->package = name;
451 static void endpackage()
453 /*printf("leaving package \"%s\"\n", state->package);*/
458 static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, classinfo_list_t*implements, char interface)
461 syntaxerror("inner classes now allowed");
464 char*classname = name->text;
467 classinfo_list_t*mlist=0;
468 /*printf("entering class %s\n", name->text);
469 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token->text);printf("\n");
471 printf(" extends: %s.%s\n", extends->package, extends->name);
472 printf(" implements (%d): ", list_length(implements));
473 for(mlist=implements;mlist;mlist=mlist->next) {
474 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
479 char public=0,internal=0,final=0,sealed=1;
480 for(t=modifiers->tokens;t;t=t->next) {
481 if(t->token->type == KW_INTERNAL) {
482 /* the programmer is being explicit-
483 being internal is the default anyway */
485 } else if(t->token->type == KW_PUBLIC) {
487 } else if(t->token->type == KW_FINAL) {
490 syntaxerror("modifier \"%s\" not supported in class declaration", t->token->text);
494 syntaxerror("public and internal not supported at the same time.");
496 /* create the class name, together with the proper attributes */
500 if(!public && !state->package) {
501 access = ACCESS_PRIVATE; package = current_filename;
502 } else if(!public && state->package) {
503 access = ACCESS_PACKAGEINTERNAL; package = state->package;
504 } else if(state->package) {
505 access = ACCESS_PACKAGE; package = state->package;
507 syntaxerror("public classes only allowed inside a package");
510 if(registry_findclass(package, classname)) {
511 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
514 state->clsinfo = classinfo_register(access, package, classname);
516 MULTINAME(classname2,state->clsinfo);
518 multiname_t*extends2 = sig2mname(extends);
521 state->cls_init = abc_getlocal_0(state->cls_init);
522 state->cls_init = abc_constructsuper(state->cls_init, 0);
525 state->cls = abc_class_new(state->file, &classname2, extends2);
526 if(final) abc_class_final(state->cls);
527 if(sealed) abc_class_sealed(state->cls);
528 if(interface) abc_class_interface(state->cls);
530 for(mlist=implements;mlist;mlist=mlist->next) {
531 MULTINAME(m, mlist->classinfo);
532 abc_class_add_interface(state->cls, &m);
535 /* now write the construction code for this class */
536 int slotindex = abc_initscript_addClassTrait(state->init, &classname2, state->cls);
538 abc_method_body_t*m = state->init->method->body;
539 __ getglobalscope(m);
540 classinfo_t*s = extends;
545 //TODO: take a look at the current scope stack, maybe
546 // we can re-use something
551 multiname_t*s2 = sig2mname(s);
553 multiname_destroy(s2);
555 __ pushscope(m); count++;
556 m->code = m->code->prev->prev; // invert
558 /* continue appending after last op end */
559 while(m->code && m->code->next) m->code = m->code->next;
561 /* TODO: if this is one of *our* classes, we can also
562 do a getglobalscope/getslot <nr> (which references
563 the init function's slots) */
565 __ getlex2(m, extends2);
567 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
568 stack is not the superclass */
569 __ pushscope(m);count++;
572 /* notice: we get a verify error #1107 if the top element on the scope
573 stack is not the global object */
575 __ pushscope(m);count++;
577 __ newclass(m,state->cls);
581 __ setslot(m, slotindex);
583 /* flash.display.MovieClip handling */
584 if(!globalclass && public && classinfo_equals(registry_getMovieClip(),extends)) {
585 if(state->package && state->package[0]) {
586 globalclass = concat3str(state->package, ".", classname);
588 globalclass = strdup(classname);
591 multiname_destroy(extends2);
594 static void endclass()
596 if(state->cls_init) {
597 if(!state->cls->constructor) {
598 abc_method_body_t*m = abc_class_constructor(state->cls, 0, 0);
599 m->code = code_append(m->code, state->cls_init);
600 m->code = abc_returnvoid(m->code);
602 code_t*c = state->cls->constructor->body->code;
603 c = code_append(state->cls_init, c);
604 state->cls->constructor->body->code = c;
608 if(state->cls_static_init) {
609 if(!state->cls->static_constructor) {
610 abc_method_body_t*m = abc_class_staticconstructor(state->cls, 0, 0);
611 m->code = state->cls_static_init;
613 state->cls->static_constructor->body->code =
614 code_append(state->cls_static_init, state->cls->static_constructor->body->code);
621 static token_t* empty_token()
629 void extend(token_t*list, token_t*add) {
630 list_append(list->tokens,add);
632 list->text = add->text;
634 void extend_s(token_t*list, char*seperator, token_t*add) {
635 list_append(list->tokens,add);
636 char*t1 = list->text;
642 list->text = malloc(l1+l2+l3+1);
643 strcpy(list->text, t1);
644 strcpy(list->text+l1, t2);
645 strcpy(list->text+l1+l2, t3);
646 list->text[l1+l2+l3]=0;
649 typedef struct _variable {
654 static int find_variable(char*name, classinfo_t**m)
656 state_list_t* s = state_stack;
658 variable_t*v = dict_lookup(s->state->vars, name);
669 static int find_variable_safe(char*name, classinfo_t**m)
671 int i = find_variable(name, m);
673 syntaxerror("undefined variable: %s", name);
676 static char variable_exists(char*name)
678 return dict_lookup(state->vars, name)!=0;
680 static int new_variable(char*name, classinfo_t*type)
683 v->index = global->variable_count;
685 dict_put(state->vars, name, v);
686 return global->variable_count++;
688 #define TEMPVARNAME "__as3_temp__"
689 static int gettempvar()
691 int i = find_variable(TEMPVARNAME, 0);
693 return new_variable(TEMPVARNAME, 0);
699 code_t* killvars(code_t*c)
702 for(t=0;t<state->vars->hashsize;t++) {
703 dictentry_t*e =state->vars->slots[t];
705 variable_t*v = (variable_t*)e->data;
706 //do this always, otherwise register types don't match
707 //in the verifier when doing nested loops
708 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
709 c = abc_kill(c, v->index);
717 static void check_constant_against_type(classinfo_t*t, constant_t*c)
719 #define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
720 if(TYPE_IS_NUMBER(t)) {
721 xassert(c->type == CONSTANT_FLOAT
722 || c->type == CONSTANT_INT
723 || c->type == CONSTANT_UINT);
724 } else if(TYPE_IS_UINT(t)) {
725 xassert(c->type == CONSTANT_UINT ||
726 (c->type == CONSTANT_INT && c->i>0));
727 } else if(TYPE_IS_INT(t)) {
728 xassert(c->type == CONSTANT_INT);
729 } else if(TYPE_IS_BOOLEAN(t)) {
730 xassert(c->type == CONSTANT_TRUE
731 || c->type == CONSTANT_FALSE);
735 static void startfunction(token_t*ns, token_t*mod, token_t*getset, token_t*name,
736 params_t*params, classinfo_t*type)
740 global->variable_count = 0;
741 state->function = name->text;
744 syntaxerror("not able to start another method scope");
747 multiname_t*type2 = sig2mname(type);
748 if(!strcmp(state->clsinfo->name,name->text)) {
749 state->m = abc_class_constructor(state->cls, type2, 0);
751 state->minfo = memberinfo_register(state->clsinfo, name->text, MEMBER_METHOD);
752 state->minfo->return_type = type;
753 state->m = abc_class_method(state->cls, type2, name->text, 0);
754 // getslot on a member slot only returns "undefined", so no need
755 // to actually store these
756 //state->minfo->slot = state->m->method->trait->slot_id;
758 if(getset->type == KW_GET) {
759 state->m->method->trait->kind = TRAIT_GETTER;
761 if(getset->type == KW_SET) {
762 state->m->method->trait->kind = TRAIT_SETTER;
764 if(params->varargs) {
765 state->m->method->flags |= METHOD_NEED_REST;
770 for(p=params->list;p;p=p->next) {
771 if(params->varargs && !p->next) {
772 break; //varargs: omit last parameter in function signature
774 multiname_t*m = sig2mname(p->param->type);
775 list_append(state->m->method->parameters, m);
776 if(p->param->value) {
777 check_constant_against_type(p->param->type, p->param->value);
778 opt=1;list_append(state->m->method->optional_parameters, p->param->value);
780 syntaxerror("non-optional parameter not allowed after optional parameters");
784 /* state->vars is initialized by state_new */
785 if(new_variable("this", state->clsinfo)!=0) syntaxerror("Internal error");
787 for(p=params->list;p;p=p->next) {
788 new_variable(p->param->name, p->param->type);
791 static void endfunction(code_t*body)
794 if(state->late_binding) {
795 c = abc_getlocal_0(c);
796 c = abc_pushscope(c);
798 c = code_append(c, state->initcode);
799 c = code_append(c, body);
801 /* append return if necessary */
802 if(!c || c->opcode != OPCODE_RETURNVOID &&
803 c->opcode != OPCODE_RETURNVALUE)
804 c = abc_returnvoid(c);
806 if(state->m->code) syntaxerror("internal error");
813 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
818 void breakjumpsto(code_t*c, code_t*jump)
823 if(c->opcode == OPCODE___BREAK__) {
824 c->opcode = OPCODE_JUMP;
831 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
834 return registry_getanytype();
835 if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
836 return registry_getanytype();
839 return registry_getanytype();
841 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
846 /*TODO: can omit this if from is zero? */
847 return abc_coerce_a(c);
849 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
850 MULTINAME(m, TYPE_UINT);
851 return abc_coerce2(c, &m);
853 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
854 MULTINAME(m, TYPE_INT);
855 return abc_coerce2(c, &m);
860 code_t*defaultvalue(code_t*c, classinfo_t*type)
862 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
863 c = abc_pushbyte(c, 0);
864 } else if(TYPE_IS_BOOLEAN(type)) {
865 c = abc_pushfalse(c);
872 char is_pushundefined(code_t*c)
874 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
877 void parserassert(int b)
879 if(!b) syntaxerror("internal error: assertion failed");
882 static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign)
886 [prefix code] [read instruction]
890 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
893 if(in && in->opcode == OPCODE_COERCE_A) {
894 in = code_cutlast(in);
897 syntaxerror("internal error");
899 /* chop off read instruction */
903 prefix = r->prev;r->prev = 0;
909 /* generate the write instruction, and maybe append a dup to the prefix code */
910 code_t* write = abc_nop(0);
911 if(r->opcode == OPCODE_GETPROPERTY) {
912 write->opcode = OPCODE_SETPROPERTY;
913 multiname_t*m = (multiname_t*)r->data[0];
914 write->data[0] = multiname_clone(m);
916 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
918 prefix = abc_dup(prefix); // we need the object, too
920 } else if(r->opcode == OPCODE_GETSLOT) {
921 write->opcode = OPCODE_SETSLOT;
922 write->data[0] = r->data[0];
924 prefix = abc_dup(prefix); // we need the object, too
926 } else if(r->opcode == OPCODE_GETLOCAL) {
927 write->opcode = OPCODE_SETLOCAL;
928 write->data[0] = r->data[0];
929 } else if(r->opcode == OPCODE_GETLOCAL_0) {
930 write->opcode = OPCODE_SETLOCAL_0;
931 } else if(r->opcode == OPCODE_GETLOCAL_1) {
932 write->opcode = OPCODE_SETLOCAL_1;
933 } else if(r->opcode == OPCODE_GETLOCAL_2) {
934 write->opcode = OPCODE_SETLOCAL_2;
935 } else if(r->opcode == OPCODE_GETLOCAL_3) {
936 write->opcode = OPCODE_SETLOCAL_3;
938 code_dump(r, 0, 0, "", stdout);
939 syntaxerror("illegal lvalue: can't assign a value to this expression");
946 /* with getproperty/getslot, we have to be extra careful not
947 to execute the read code twice, as it might have side-effects
948 (e.g. if the property is in fact a setter/getter combination)
950 So read the value, modify it, and write it again,
951 using prefix only once and making sure (by using a temporary
952 register) that the return value is what we just wrote */
954 c = code_append(c, prefix);
955 c = code_append(c, r);
956 c = code_append(c, middlepart);
958 c = abc_setlocal(c, temp);
959 c = code_append(c, write);
960 c = abc_getlocal(c, temp);
961 c = abc_kill(c, temp);
963 /* if we're allowed to execute the read code twice *and*
964 the middlepart doesn't modify the code, things are easier.
966 code_t* r2 = code_dup(r);
967 //c = code_append(c, prefix);
968 parserassert(!prefix);
969 c = code_append(c, r);
970 c = code_append(c, middlepart);
971 c = code_append(c, write);
972 c = code_append(c, r2);
975 /* even smaller version: overwrite the value without reading
978 c = code_append(c, prefix);
981 c = code_append(c, middlepart);
982 c = code_append(c, write);
983 c = code_append(c, r);
995 /* ------------ code blocks / statements ---------------- */
999 MAYBECODE: CODE {$$=$1;}
1000 MAYBECODE: {$$=code_new();}
1002 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
1003 CODE: CODEPIECE {$$=$1;}
1005 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
1006 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
1007 CODEPIECE: FUNCTION_DECLARATION {$$=code_new();/*enters a scope*/}
1008 CODEPIECE: INTERFACE_DECLARATION {$$=code_new();}
1009 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
1010 CODEPIECE: ';' {$$=code_new();}
1011 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
1012 CODEPIECE: VOIDEXPRESSION {$$=$1}
1013 CODEPIECE: FOR {$$=$1}
1014 CODEPIECE: WHILE {$$=$1}
1015 CODEPIECE: BREAK {$$=$1}
1016 CODEPIECE: RETURN {$$=$1}
1017 CODEPIECE: IF {$$=$1}
1018 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
1019 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
1021 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
1022 CODEBLOCK : CODEPIECE ';' {$$=$1;}
1023 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
1025 /* ------------ variables --------------------------- */
1027 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
1028 | {$$.c=abc_pushundefined(0);
1032 VAR : "const" | "var"
1033 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
1035 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
1036 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
1038 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
1040 if(variable_exists($2->text))
1041 syntaxerror("Variable %s already defined", $2->text);
1043 if(!is_subtype_of($4.t, $3)) {
1044 syntaxerror("Can't convert %s to %s", $4.t->name,
1048 int index = new_variable($2->text, $3);
1051 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1053 $$ = converttype($$, $4.t, $3);
1054 $$ = abc_setlocal($$, index);
1056 $$ = defaultvalue(0, $3);
1057 $$ = abc_setlocal($$, index);
1060 /* if this is a typed variable:
1061 push default value for type on stack */
1063 state->initcode = defaultvalue(state->initcode, $3);
1064 state->initcode = abc_setlocal(state->initcode, index);
1067 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
1069 $$ = abc_coerce_a($$);
1070 $$ = abc_setlocal($$, index);
1076 /* that's the default for a local register, anyway
1078 state->initcode = abc_pushundefined(state->initcode);
1079 state->initcode = abc_setlocal(state->initcode, index);
1081 printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
1084 /* ------------ control flow ------------------------- */
1086 MAYBEELSE: %prec prec_none {$$ = code_new();}
1087 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
1088 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
1090 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
1091 $$ = state->initcode;state->initcode=0;
1093 $$ = code_append($$, $4.c);
1094 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
1096 $$ = code_append($$, $6);
1098 myjmp = $$ = abc_jump($$, 0);
1100 myif->branch = $$ = abc_label($$);
1102 $$ = code_append($$, $7);
1103 myjmp->branch = $$ = abc_label($$);
1106 $$ = killvars($$);old_state();
1109 FOR_INIT : {$$=code_new();}
1110 FOR_INIT : VARIABLE_DECLARATION
1111 FOR_INIT : VOIDEXPRESSION
1113 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
1114 $$ = state->initcode;state->initcode=0;
1116 $$ = code_append($$, $4);
1117 code_t*loopstart = $$ = abc_label($$);
1118 $$ = code_append($$, $6.c);
1119 code_t*myif = $$ = abc_iffalse($$, 0);
1120 $$ = code_append($$, $10);
1121 $$ = code_append($$, $8);
1122 $$ = abc_jump($$, loopstart);
1123 code_t*out = $$ = abc_label($$);
1124 breakjumpsto($$, out);
1127 $$ = killvars($$);old_state();
1130 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1131 $$ = state->initcode;state->initcode=0;
1133 code_t*myjmp = $$ = abc_jump($$, 0);
1134 code_t*loopstart = $$ = abc_label($$);
1135 $$ = code_append($$, $6);
1136 myjmp->branch = $$ = abc_label($$);
1137 $$ = code_append($$, $4.c);
1138 $$ = abc_iftrue($$, loopstart);
1139 code_t*out = $$ = abc_label($$);
1140 breakjumpsto($$, out);
1142 $$ = killvars($$);old_state();
1146 $$ = abc___break__(0);
1149 /* ------------ packages and imports ---------------- */
1151 X_IDENTIFIER: T_IDENTIFIER
1154 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,$2,$3);}
1155 PACKAGE: X_IDENTIFIER {$$=$1;}
1157 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1158 PACKAGE_DECLARATION : "package" '{' {startpackage(0)} MAYBECODE '}' {endpackage()}
1160 IMPORT : "import" QNAME {
1163 syntaxerror("Couldn't import class\n");
1164 state_has_imports();
1165 dict_put(state->imports, c->name, c);
1168 IMPORT : "import" PACKAGE '.' '*' {
1170 i->package = $2->text;
1171 state_has_imports();
1172 list_append(state->wildcard_imports, i);
1176 /* ------------ classes and interfaces (header) -------------- */
1178 MODIFIERS : {$$=empty_token();}
1179 MODIFIERS : MODIFIER_LIST {$$=$1}
1180 MODIFIER_LIST : MODIFIER MODIFIER_LIST {extend($2,$1);$$=$2;}
1181 MODIFIER_LIST : MODIFIER {$$=empty_token();extend($$,$1);}
1182 MODIFIER : KW_PUBLIC | KW_PRIVATE | KW_PROTECTED | KW_STATIC | KW_DYNAMIC | KW_FINAL | KW_OVERRIDE | KW_NATIVE | KW_INTERNAL
1184 EXTENDS : {$$=registry_getobjectclass();}
1185 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1187 EXTENDS_LIST : {$$=list_new();}
1188 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1190 IMPLEMENTS_LIST : {$$=list_new();}
1191 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1193 CLASS_DECLARATION : MODIFIERS "class" T_IDENTIFIER
1194 EXTENDS IMPLEMENTS_LIST
1195 '{' {startclass($1,$3,$4,$5, 0);}
1196 MAYBE_DECLARATION_LIST
1199 INTERFACE_DECLARATION : MODIFIERS "interface" T_IDENTIFIER
1201 '{' {startclass($1,$3,0,$4,1);}
1202 MAYBE_IDECLARATION_LIST
1205 /* ------------ classes and interfaces (body) -------------- */
1207 MAYBE_DECLARATION_LIST :
1208 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1209 DECLARATION_LIST : DECLARATION
1210 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1212 DECLARATION : SLOT_DECLARATION
1213 DECLARATION : FUNCTION_DECLARATION
1215 /* ------------ classes and interfaces (body, slots ) ------- */
1217 VARCONST: "var" | "const"
1218 SLOT_DECLARATION: MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1220 memberinfo_t* info = memberinfo_register(state->clsinfo, $3->text, MEMBER_SLOT);
1226 t=abc_class_slot(state->cls, $3->text, &m);
1228 t=abc_class_slot(state->cls, $3->text, 0);
1230 if($2->type==KW_CONST) {
1231 t->kind= TRAIT_CONST;
1233 info->slot = t->slot_id;
1234 if($5.c && !is_pushundefined($5.c)) {
1236 c = abc_getlocal_0(c);
1237 c = code_append(c, $5.c);
1238 c = converttype(c, $5.t, $4);
1239 c = abc_setslot(c, t->slot_id);
1240 //c = abc_setproperty(c, $3->text);
1241 state->cls_init = code_append(state->cls_init, c);
1245 /* ------------ constants -------------------------------------- */
1247 MAYBESTATICCONSTANT: {$$=0;}
1248 MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
1250 STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
1251 STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
1252 STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
1253 STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
1254 STATICCONSTANT : T_STRING {$$ = constant_new_string($1);}
1255 //STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
1256 STATICCONSTANT : KW_TRUE {$$ = constant_new_true($1);}
1257 STATICCONSTANT : KW_FALSE {$$ = constant_new_false($1);}
1258 STATICCONSTANT : KW_NULL {$$ = constant_new_null($1);}
1260 /* ------------ classes and interfaces (body, functions) ------- */
1262 // non-vararg version
1264 memset(&$$,0,sizeof($$));
1266 MAYBE_PARAM_LIST: PARAM_LIST {
1271 MAYBE_PARAM_LIST: "..." PARAM {
1272 memset(&$$,0,sizeof($$));
1274 list_append($$.list, $2);
1276 MAYBE_PARAM_LIST: PARAM_LIST ',' "..." PARAM {
1279 list_append($$.list, $4);
1283 PARAM_LIST: PARAM_LIST ',' PARAM {
1285 list_append($$.list, $3);
1288 memset(&$$,0,sizeof($$));
1289 list_append($$.list, $1);
1291 PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
1292 $$ = malloc(sizeof(param_t));
1297 PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
1298 $$ = malloc(sizeof(param_t));
1299 $$->name=$1->text;$$->type = TYPE_ANY;
1302 FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1303 MAYBETYPE '{' {startfunction(0,$1,$3,$4,&$6,$8)} MAYBECODE '}'
1305 if(!state->m) syntaxerror("internal error: undefined function");
1309 /* ------------- package + class ids --------------- */
1311 CLASS: T_IDENTIFIER {
1313 /* try current package */
1314 $$ = registry_findclass(state->package, $1->text);
1316 /* try explicit imports */
1317 dictentry_t* e = dict_get_slot(state->imports, $1->text);
1321 if(!strcmp(e->key, $1->text)) {
1322 $$ = (classinfo_t*)e->data;
1327 /* try package.* imports */
1328 import_list_t*l = state->wildcard_imports;
1332 //printf("does package %s contain a class %s?\n", l->import->package, $1->text);
1333 $$ = registry_findclass(l->import->package, $1->text);
1337 /* try global package */
1339 $$ = registry_findclass("", $1->text);
1342 if(!$$) syntaxerror("Could not find class %s\n", $1->text);
1345 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1346 $$ = registry_findclass($1->text, $3->text);
1347 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1->text, $3->text);
1350 QNAME: PACKAGEANDCLASS
1354 /* ----------function calls, constructor calls ------ */
1356 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1357 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1359 MAYBE_EXPRESSION_LIST : {$$=0;}
1360 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1361 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1362 typedcode_t*t = malloc(sizeof(typedcode_t));
1364 list_append($$, t);}
1365 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1366 typedcode_t*t = malloc(sizeof(typedcode_t));
1368 list_append($$, t);}
1370 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1374 /* TODO: why do we have to *find* our own classes? */
1375 $$.c = abc_findpropstrict2($$.c, &m);
1377 typedcode_list_t*l = $3;
1380 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1384 $$.c = abc_constructprop2($$.c, &m, len);
1388 /* TODO: use abc_call (for calling local variables),
1389 abc_callstatic (for calling own methods)
1392 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1393 typedcode_list_t*l = $3;
1395 code_t*paramcode = 0;
1397 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1403 if($$.c->opcode == OPCODE_COERCE_A) {
1404 $$.c = code_cutlast($$.c);
1408 multiname_t*name = 0;
1409 if($$.c->opcode == OPCODE_GETPROPERTY) {
1410 name = multiname_clone($$.c->data[0]);
1411 $$.c = code_cutlast($$.c);
1412 $$.c = code_append($$.c, paramcode);
1413 $$.c = abc_callproperty2($$.c, name, len);
1414 } else if($$.c->opcode == OPCODE_GETSLOT) {
1415 int slot = (int)(ptroff_t)$$.c->data[0];
1416 trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME
1417 if(t->kind!=TRAIT_METHOD) {
1418 //flash allows to assign closures to members.
1419 //syntaxerror("not a function");
1422 $$.c = code_cutlast($$.c);
1423 $$.c = code_append($$.c, paramcode);
1424 //$$.c = abc_callmethod($$.c, t->method, len); //#1051 illegal early access binding
1425 $$.c = abc_callproperty2($$.c, name, len);
1427 $$.c = abc_getlocal_0($$.c);
1428 $$.c = code_append($$.c, paramcode);
1429 $$.c = abc_call($$.c, len);
1434 if(TYPE_IS_FUNCTION($1.t) &&
1435 (f = registry_findmember($1.t, "call"))) {
1436 $$.t = f->return_type;
1438 $$.c = abc_coerce_a($$.c);
1443 RETURN: "return" %prec prec_none {
1444 $$ = abc_returnvoid(0);
1446 RETURN: "return" EXPRESSION {
1448 $$ = abc_returnvalue($$);
1450 // ----------------------- expression types -------------------------------------
1452 NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;}
1453 EXPRESSION : E %prec prec_belowminus {$$ = $1;}
1454 EXPRESSION : EXPRESSION ',' E %prec prec_belowminus {
1456 $$.c = cut_last_push($$.c);
1457 $$.c = code_append($$.c,$3.c);
1460 VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=cut_last_push($1.c);}
1462 // ----------------------- expression evaluation -------------------------------------
1465 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1467 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1471 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1472 $$.t = TYPE_BOOLEAN;
1474 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1475 $$.t = TYPE_BOOLEAN;
1477 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1478 $$.t = TYPE_BOOLEAN;
1480 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1481 $$.t = TYPE_BOOLEAN;
1483 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1484 $$.t = TYPE_BOOLEAN;
1486 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1487 $$.t = TYPE_BOOLEAN;
1489 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1490 $$.t = TYPE_BOOLEAN;
1493 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1495 $$.c = converttype($$.c, $1.t, $$.t);
1496 $$.c = abc_dup($$.c);
1497 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1498 $$.c = cut_last_push($$.c);
1499 $$.c = code_append($$.c,$3.c);
1500 $$.c = converttype($$.c, $3.t, $$.t);
1501 code_t*label = $$.c = abc_label($$.c);
1502 jmp->branch = label;
1505 $$.t = join_types($1.t, $3.t, 'A');
1506 /*printf("%08x:\n",$1.t);
1507 code_dump($1.c, 0, 0, "", stdout);
1508 printf("%08x:\n",$3.t);
1509 code_dump($3.c, 0, 0, "", stdout);
1510 printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
1512 $$.c = converttype($$.c, $1.t, $$.t);
1513 $$.c = abc_dup($$.c);
1514 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1515 $$.c = cut_last_push($$.c);
1516 $$.c = code_append($$.c,$3.c);
1517 $$.c = converttype($$.c, $3.t, $$.t);
1518 code_t*label = $$.c = abc_label($$.c);
1519 jmp->branch = label;
1522 E : '!' E {$$.c=$2.c;
1523 $$.c = abc_not($$.c);
1524 $$.t = TYPE_BOOLEAN;
1529 E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c);
1530 $$.t = join_types($1.t, $3.t, '+');
1532 E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c);
1533 $$.t = join_types($1.t, $3.t, '%');
1535 E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c);
1536 $$.t = join_types($1.t, $3.t, '*');
1541 E : '(' E ')' {$$=$2;}
1546 $$.c = code_append($$.c, $3.c);
1548 MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
1549 $$.c = abc_getproperty2($$.c, &m);
1554 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1559 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1561 $$.c = toreadwrite($1.c, c, 0);
1564 E : E "-=" E { code_t*c = $3.c;
1565 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1566 c=abc_subtract_i(c);
1570 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
1572 $$.c = toreadwrite($1.c, c, 0);
1575 E : E '=' E { code_t*c = 0;
1576 c = code_append(c, $3.c);
1577 c = converttype(c, $3.t, $1.t);
1578 $$.c = toreadwrite($1.c, c, 1);
1582 // TODO: use inclocal where appropriate
1583 E : E "++" { code_t*c = 0;
1584 classinfo_t*type = $1.t;
1585 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1586 c=abc_increment_i(c);
1591 c=converttype(c, type, $1.t);
1592 $$.c = toreadwrite($1.c, c, 0);
1595 E : E "--" { code_t*c = 0;
1596 classinfo_t*type = $1.t;
1597 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1598 c=abc_increment_i(c);
1603 c=converttype(c, type, $1.t);
1604 $$.c = toreadwrite($1.c, c, 0);
1608 E : "++" E { code_t*c = 0;
1609 classinfo_t*type = $2.t;
1610 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1611 c=abc_increment_i(c);
1616 c=converttype(c, type, $2.t);
1617 $$.c = toreadwrite($2.c, c, 0);
1621 E : E '.' T_IDENTIFIER
1624 memberinfo_t*f = registry_findmember($$.t, $3->text);
1627 $$.c = abc_getslot($$.c, f->slot);
1629 namespace_t ns = {$$.t->access, ""}; // needs to be "", not $$.t->package
1630 multiname_t m = {QNAME, &ns, 0, $3->text};
1631 $$.c = abc_getproperty2($$.c, &m);
1633 /* determine type */
1635 if(f->kind == MEMBER_METHOD) {
1636 $$.t = TYPE_FUNCTION(f);
1641 $$.c = abc_coerce_a($$.c);
1642 $$.t = registry_getanytype();
1645 namespace_t ns = {ACCESS_PACKAGE, ""};
1646 multiname_t m = {QNAME, &ns, 0, $3->text};
1647 $$.c = abc_getproperty2($$.c, &m);
1648 $$.c = abc_coerce_a($$.c);
1649 $$.t = registry_getanytype();
1653 VAR_READ : T_IDENTIFIER {
1658 if((i = find_variable($1->text, &$$.t)) >= 0) {
1659 // $1 is a local variable
1660 $$.c = abc_getlocal($$.c, i);
1661 } else if((f = registry_findmember(state->clsinfo, $1->text))) {
1662 // $1 is a function in this class
1663 if(f->kind == MEMBER_METHOD) {
1664 $$.t = TYPE_FUNCTION(f);
1669 $$.c = abc_getlocal_0($$.c);
1670 $$.c = abc_getslot($$.c, f->slot);
1672 namespace_t ns = {state->clsinfo->access, ""};
1673 multiname_t m = {QNAME, &ns, 0, $1->text};
1674 $$.c = abc_getlocal_0($$.c);
1675 $$.c = abc_getproperty2($$.c, &m);
1678 // let the avm2 resolve $1
1679 if(strcmp($1->text,"trace"))
1680 warning("Couldn't resolve %s, doing late binding", $1->text);
1681 state->late_binding = 1;
1684 $$.c = abc_findpropstrict($$.c, $1->text);
1685 $$.c = abc_getproperty($$.c, $1->text);
1690 // ------------------------------------------------------------------------------
1693 TYPE : QNAME {$$=$1;}
1694 | '*' {$$=registry_getanytype();}
1695 | "String" {$$=registry_getstringclass();}
1696 | "int" {$$=registry_getintclass();}
1697 | "uint" {$$=registry_getuintclass();}
1698 | "Boolean" {$$=registry_getbooleanclass();}
1699 | "Number" {$$=registry_getnumberclass();}
1701 MAYBETYPE: ':' TYPE {$$=$2;}
1704 //FUNCTION_HEADER: NAMESPACE MODIFIERS T_FUNCTION GETSET T_IDENTIFIER '(' PARAMS ')'
1705 FUNCTION_HEADER: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1708 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER
1709 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_IDENTIFIER
1710 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_STRING
1712 //NAMESPACE : {$$=empty_token();}
1713 //NAMESPACE : T_IDENTIFIER {$$=$1};
1715 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1716 //MULTINAME(m, registry_getintclass());
1717 //$$.c = abc_coerce2($$.c, &m); // FIXME
1720 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1723 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1726 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1729 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1732 CONSTANT : T_STRING {$$.c = abc_pushstring(0, $1);
1735 CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
1736 $$.t = TYPE_BOOLEAN;
1738 CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
1739 $$.t = TYPE_BOOLEAN;
1741 CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
1745 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
1748 //VARIABLE : T_IDENTIFIER
1749 //VARIABLE : VARIABLE '.' T_IDENTIFIER
1750 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
1751 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
1752 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
1753 //VARIABLE : VARIABLE '[' EXPRESSION ']' // unqualified expression
1755 GETSET : "get" {$$=$1;}
1757 | {$$=empty_token();}
1759 IDECLARATION : VARIABLE_DECLARATION
1760 IDECLARATION : FUNCTION_DECLARATION
1762 //IDENTIFIER_LIST : T_IDENTIFIER ',' IDENTIFIER_LIST {extend($3,$1);$$=$3;}
1763 //IDENTIFIER_LIST : T_IDENTIFIER {$$=empty_token();extend($$,$1);}
1765 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1766 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1769 MAYBE_IDECLARATION_LIST :
1770 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1771 IDECLARATION_LIST : IDECLARATION
1772 IDECLARATION_LIST : IDECLARATION_LIST FUNCTION_HEADER
1775 // 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
1776 // syntactic keywords: each get set namespace include dynamic final native override static