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;
54 param_list_t* param_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_SHL "<<"
128 %token<token> T_USHR ">>>"
129 %token<token> T_SHR ">>"
130 %token<token> T_SEMICOLON ';'
131 %token<token> T_STAR '*'
132 %token<token> T_DOT '.'
134 %type <token> X_IDENTIFIER VARCONST
136 %type <code> CODEPIECE
137 %type <code> CODEBLOCK MAYBECODE
138 %type <token> PACKAGE_DECLARATION
139 %type <token> FUNCTION_DECLARATION
140 %type <code> VARIABLE_DECLARATION ONE_VARIABLE VARIABLE_LIST
141 %type <token> CLASS_DECLARATION
142 %type <token> NAMESPACE_DECLARATION
143 %type <token> INTERFACE_DECLARATION
144 %type <code> VOIDEXPRESSION
145 %type <value> EXPRESSION NONCOMMAEXPRESSION
146 %type <value> MAYBEEXPRESSION
148 %type <value> CONSTANT
149 %type <code> FOR IF WHILE MAYBEELSE BREAK RETURN
150 %type <token> USE_NAMESPACE
151 %type <code> FOR_INIT
153 %type <classinfo> MAYBETYPE
156 %type <param_list> PARAM_LIST
157 %type <param_list> MAYBE_PARAM_LIST
158 %type <token> MODIFIERS
159 %type <token> MODIFIER_LIST
160 %type <classinfo_list> IMPLEMENTS_LIST
161 %type <classinfo> EXTENDS
162 %type <classinfo_list> EXTENDS_LIST
163 %type <classinfo> CLASS PACKAGEANDCLASS QNAME
164 %type <classinfo_list> QNAME_LIST
165 %type <classinfo> TYPE
167 //%type <token> VARIABLE
168 %type <value> VAR_READ
170 //%type <token> T_IDENTIFIER
171 %type <token> MODIFIER
172 %type <token> PACKAGE
173 %type <value> FUNCTIONCALL
174 %type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST MAYBE_PARAM_VALUES
176 // precedence: from low to high
177 // http://livedocs.adobe.com/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000012.html
192 %nonassoc "!=" "==" "===" "<=" '<' ">=" '>' // TODO: support "a < b < c" syntax?
194 %left prec_belowminus
209 %nonassoc T_IDENTIFIER
210 %left below_semicolon
215 // needed for "return" precedence:
216 %nonassoc T_STRING T_REGEXP
217 %nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
218 %nonassoc "new" "false" "true" "null"
225 static int yyerror(char*s)
227 syntaxerror("%s", s);
229 static token_t* concat2(token_t* t1, token_t* t2)
232 int l1 = strlen(t1->text);
233 int l2 = strlen(t2->text);
234 t->text = malloc(l1+l2+1);
235 memcpy(t->text , t1->text, l1);
236 memcpy(t->text+l1, t2->text, l2);
240 static token_t* concat3(token_t* t1, token_t* t2, token_t* t3)
243 int l1 = strlen(t1->text);
244 int l2 = strlen(t2->text);
245 int l3 = strlen(t3->text);
246 t->text = malloc(l1+l2+l3+1);
247 memcpy(t->text , t1->text, l1);
248 memcpy(t->text+l1, t2->text, l2);
249 memcpy(t->text+l1+l2, t3->text, l3);
250 t->text[l1+l2+l3] = 0;
253 static char* concat3str(const char* t1, const char* t2, const char* t3)
258 char*text = malloc(l1+l2+l3+1);
259 memcpy(text , t1, l1);
260 memcpy(text+l1, t2, l2);
261 memcpy(text+l1+l2, t3, l3);
266 typedef struct _import {
270 DECLARE_LIST(import);
272 typedef struct _state {
280 /* code that needs to be executed at the start of
281 a method (like initializing local registers) */
284 import_list_t*wildcard_imports;
286 char has_own_imports;
292 code_t*cls_static_init;
303 static state_t* state = 0;
307 #define MULTINAME(m,x) multiname_t m;namespace_t m##_ns;registry_fill_multiname(&m, &m##_ns, x);
309 static state_list_t*state_stack=0;
311 static void new_state()
314 NEW(state_list_t, sl);
316 state_t*oldstate = state;
318 memcpy(s, state, sizeof(state_t)); //shallow copy
319 sl->next = state_stack;
322 s->local_var_base = array_length(oldstate->vars) + oldstate->local_var_base;
325 s->imports = dict_new();
330 state->vars = array_new();
332 state->has_own_imports = 0;
334 static void state_has_imports()
336 state->wildcard_imports = list_clone(state->wildcard_imports);
337 state->imports = dict_clone(state->imports);
338 state->has_own_imports = 1;
341 static void old_state()
343 if(!state_stack || !state_stack->next)
344 syntaxerror("invalid nesting");
345 state_t*oldstate = state;
346 state_list_t*old = state_stack;
347 state_stack = state_stack->next;
349 state = state_stack->state;
350 /*if(state->initcode) {
351 printf("residual initcode\n");
352 code_dump(state->initcode, 0, 0, "", stdout);
354 if(oldstate->has_own_imports) {
355 list_free(oldstate->wildcard_imports);
356 dict_destroy(oldstate->imports);oldstate->imports=0;
358 state->initcode = code_append(state->initcode, oldstate->initcode);
360 void initialize_state()
364 state->file = abc_file_new();
365 state->file->flags &= ~ABCFILE_LAZY;
367 state->init = abc_initscript(state->file, 0, 0);
368 code_t*c = state->init->method->body->code;
370 c = abc_getlocal_0(c);
371 c = abc_pushscope(c);
373 /* findpropstrict doesn't just return a scope object- it
374 also makes it "active" somehow. Push local_0 on the
375 scope stack and read it back with findpropstrict, it'll
376 contain properties like "trace". Trying to find the same
377 property on a "vanilla" local_0 yields only a "undefined" */
378 //c = abc_findpropstrict(c, "[package]::trace");
380 /*c = abc_getlocal_0(c);
381 c = abc_findpropstrict(c, "[package]::trace");
383 c = abc_setlocal_1(c);
385 c = abc_pushbyte(c, 0);
386 c = abc_setlocal_2(c);
388 code_t*xx = c = abc_label(c);
389 c = abc_findpropstrict(c, "[package]::trace");
390 c = abc_pushstring(c, "prop:");
391 c = abc_hasnext2(c, 1, 2);
393 c = abc_setlocal_3(c);
394 c = abc_callpropvoid(c, "[package]::trace", 2);
395 c = abc_getlocal_3(c);
397 c = abc_iftrue(c,xx);*/
399 c = abc_findpropstrict(c, "[package]::trace");
400 c = abc_pushstring(c, "[entering global init function]");
401 c = abc_callpropvoid(c, "[package]::trace", 1);
403 state->init->method->body->code = c;
405 void* finalize_state()
407 if(state->level!=1) {
408 syntaxerror("unexpected end of file");
410 abc_method_body_t*m = state->init->method->body;
413 __ findpropstrict(m, "[package]::trace");
414 __ pushstring(m, "[leaving global init function]");
415 __ callpropvoid(m, "[package]::trace", 1);
421 static void startpackage(token_t*t)
424 syntaxerror("Packages can not be nested.");
427 char*name = t?t->text:"";
428 /*printf("entering package \"%s\"\n", name);*/
429 state->package = name;
431 static void endpackage()
433 /*printf("leaving package \"%s\"\n", state->package);*/
438 static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, classinfo_list_t*implements, char interface)
441 syntaxerror("inner classes now allowed");
444 char*classname = name->text;
447 classinfo_list_t*mlist=0;
448 /*printf("entering class %s\n", name->text);
449 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token->text);printf("\n");
451 printf(" extends: %s.%s\n", extends->package, extends->name);
452 printf(" implements (%d): ", list_length(implements));
453 for(mlist=implements;mlist;mlist=mlist->next) {
454 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
459 char public=0,internal=0,final=0,sealed=1;
460 for(t=modifiers->tokens;t;t=t->next) {
461 if(t->token->type == KW_INTERNAL) {
462 /* the programmer is being explicit-
463 being internal is the default anyway */
465 } else if(t->token->type == KW_PUBLIC) {
467 } else if(t->token->type == KW_FINAL) {
470 syntaxerror("modifier \"%s\" not supported in class declaration", t->token->text);
474 syntaxerror("public and internal not supported at the same time.");
476 /* create the class name, together with the proper attributes */
480 if(!public && !state->package) {
481 access = ACCESS_PRIVATE; package = current_filename;
482 } else if(!public && state->package) {
483 access = ACCESS_PACKAGEINTERNAL; package = state->package;
484 } else if(state->package) {
485 access = ACCESS_PACKAGE; package = state->package;
487 syntaxerror("public classes only allowed inside a package");
490 if(registry_findclass(package, classname)) {
491 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
494 state->clsinfo = classinfo_register(access, package, classname);
496 MULTINAME(classname2,state->clsinfo);
498 multiname_t*extends2 = sig2mname(extends);
501 state->cls_init = abc_getlocal_0(state->cls_init);
502 state->cls_init = abc_constructsuper(state->cls_init, 0);
505 state->cls = abc_class_new(state->file, &classname2, extends2);
506 if(final) abc_class_final(state->cls);
507 if(sealed) abc_class_sealed(state->cls);
508 if(interface) abc_class_interface(state->cls);
510 for(mlist=implements;mlist;mlist=mlist->next) {
511 MULTINAME(m, mlist->classinfo);
512 abc_class_add_interface(state->cls, &m);
515 /* now write the construction code for this class */
516 int slotindex = abc_initscript_addClassTrait(state->init, &classname2, state->cls);
518 abc_method_body_t*m = state->init->method->body;
519 __ getglobalscope(m);
520 classinfo_t*s = extends;
525 //TODO: take a look at the current scope stack, maybe
526 // we can re-use something
531 multiname_t*s2 = sig2mname(s);
533 multiname_destroy(s2);
535 __ pushscope(m); count++;
536 m->code = m->code->prev->prev; // invert
538 /* continue appending after last op end */
539 while(m->code && m->code->next) m->code = m->code->next;
541 /* TODO: if this is one of *our* classes, we can also
542 do a getglobalscope/getslot <nr> (which references
543 the init function's slots) */
545 __ getlex2(m, extends2);
547 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
548 stack is not the superclass */
549 __ pushscope(m);count++;
552 /* notice: we get a verify error #1107 if the top element on the scope
553 stack is not the global object */
555 __ pushscope(m);count++;
557 __ newclass(m,state->cls);
561 __ setslot(m, slotindex);
563 /* flash.display.MovieClip handling */
564 if(!globalclass && public && classinfo_equals(registry_getMovieClip(),extends)) {
565 if(state->package && state->package[0]) {
566 globalclass = concat3str(state->package, ".", classname);
568 globalclass = strdup(classname);
571 multiname_destroy(extends2);
574 static void endclass()
576 if(state->cls_init) {
577 if(!state->cls->constructor) {
578 abc_method_body_t*m = abc_class_constructor(state->cls, 0, 0);
579 m->code = code_append(m->code, state->cls_init);
580 m->code = abc_returnvoid(m->code);
582 code_t*c = state->cls->constructor->body->code;
583 c = code_append(state->cls_init, c);
584 state->cls->constructor->body->code = c;
588 if(state->cls_static_init) {
589 if(!state->cls->static_constructor) {
590 abc_method_body_t*m = abc_class_staticconstructor(state->cls, 0, 0);
591 m->code = state->cls_static_init;
593 state->cls->static_constructor->body->code =
594 code_append(state->cls_static_init, state->cls->static_constructor->body->code);
600 static void startfunction(token_t*ns, token_t*mod, token_t*getset, token_t*name,
601 param_list_t*params, classinfo_t*type)
605 state->function = name->text;
608 syntaxerror("not able to start another method scope");
611 multiname_t*type2 = sig2mname(type);
612 if(!strcmp(state->clsinfo->name,name->text)) {
613 state->m = abc_class_constructor(state->cls, type2, 0);
615 state->minfo = memberinfo_register(state->clsinfo, name->text, MEMBER_METHOD);
616 state->m = abc_class_method(state->cls, type2, name->text, 0);
617 state->minfo->slot = state->m->method->trait->slot_id;
619 if(getset->type == KW_GET) {
620 state->m->method->trait->kind = TRAIT_GETTER;
622 if(getset->type == KW_SET) {
623 state->m->method->trait->kind = TRAIT_SETTER;
627 for(p=params;p;p=p->next) {
628 multiname_t*m = sig2mname(p->param->type);
629 list_append(state->m->method->parameters, m);
632 /* state->vars is initialized by state_new */
633 array_append(state->vars, "this", 0);
634 for(p=params;p;p=p->next) {
635 array_append(state->vars, p->param->name, 0);
638 static void endfunction(code_t*body)
641 if(state->late_binding) {
642 c = abc_getlocal_0(c);
643 c = abc_pushscope(c);
645 c = code_append(c, state->initcode);
646 c = code_append(c, body);
647 c = abc_returnvoid(c);
649 if(state->m->code) syntaxerror("internal error");
655 static token_t* empty_token()
663 void extend(token_t*list, token_t*add) {
664 list_append(list->tokens,add);
666 list->text = add->text;
668 void extend_s(token_t*list, char*seperator, token_t*add) {
669 list_append(list->tokens,add);
670 char*t1 = list->text;
676 list->text = malloc(l1+l2+l3+1);
677 strcpy(list->text, t1);
678 strcpy(list->text+l1, t2);
679 strcpy(list->text+l1+l2, t3);
680 list->text[l1+l2+l3]=0;
683 static int find_variable(char*name, classinfo_t**m)
685 state_list_t* s = state_stack;
687 int i = array_find(s->state->vars, name);
690 *m = array_getvalue(s->state->vars, i);
692 return i + s->state->local_var_base;
698 static int find_variable_safe(char*name, classinfo_t**m)
700 int i = find_variable(name, m);
702 syntaxerror("undefined variable: %s", name);
705 static char variable_exists(char*name)
707 return array_contains(state->vars, name);
709 static int new_variable(char*name, classinfo_t*type)
711 return array_append(state->vars, name, type) + state->local_var_base;
713 #define TEMPVARNAME "__as3_temp__"
714 static int gettempvar()
716 int i = find_variable(TEMPVARNAME, 0);
718 return new_variable(TEMPVARNAME, 0);
724 code_t* killvars(code_t*c)
727 for(t=0;t<state->vars->num;t++) {
728 classinfo_t*type = array_getvalue(state->vars, t);
729 //do this always, otherwise register types don't match
730 //in the verifier when doing nested loops
731 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
732 c = abc_kill(c, t+state->local_var_base);
738 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
743 void breakjumpsto(code_t*c, code_t*jump)
748 if(c->opcode == OPCODE___BREAK__) {
749 c->opcode = OPCODE_JUMP;
756 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
758 return registry_getanytype(); // FIXME
760 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
765 /*TODO: can omit this if from is zero? */
766 return abc_coerce_a(c);
768 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
769 MULTINAME(m, TYPE_UINT);
770 return abc_coerce2(c, &m);
772 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
773 MULTINAME(m, TYPE_INT);
774 return abc_coerce2(c, &m);
779 code_t*defaultvalue(code_t*c, classinfo_t*type)
781 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
782 c = abc_pushbyte(c, 0);
783 } else if(TYPE_IS_BOOLEAN(type)) {
784 c = abc_pushfalse(c);
791 char is_pushundefined(code_t*c)
793 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
796 static code_t* toreadwrite(code_t*in, code_t*middlepart)
800 [prefix code] [read instruction]
804 [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
808 syntaxerror("internal error");
810 int temp = gettempvar();
812 /* chop off read instruction */
816 prefix = r->prev;r->prev = 0;
822 /* generate the write instruction, and maybe append a dup to the prefix code */
823 code_t* write = abc_nop(0);
824 if(r->opcode == OPCODE_GETPROPERTY) {
825 write->opcode = OPCODE_SETPROPERTY;
826 multiname_t*m = (multiname_t*)r->data[0];
827 write->data[0] = multiname_clone(m);
829 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
830 prefix = abc_dup(prefix); // we need the object, too
831 } else if(r->opcode == OPCODE_GETSLOT) {
832 write->opcode = OPCODE_SETSLOT;
833 write->data[0] = r->data[0];
834 prefix = abc_dup(prefix); // we need the object, too
835 } else if(r->opcode == OPCODE_GETLOCAL) {
836 write->opcode = OPCODE_SETLOCAL;
837 write->data[0] = r->data[0];
838 } else if(r->opcode == OPCODE_GETLOCAL_0) {
839 write->opcode = OPCODE_SETLOCAL_0;
840 } else if(r->opcode == OPCODE_GETLOCAL_1) {
841 write->opcode = OPCODE_SETLOCAL_1;
842 } else if(r->opcode == OPCODE_GETLOCAL_2) {
843 write->opcode = OPCODE_SETLOCAL_2;
844 } else if(r->opcode == OPCODE_GETLOCAL_3) {
845 write->opcode = OPCODE_SETLOCAL_3;
847 code_dump(r, 0, 0, "", stdout);
848 syntaxerror("illegal lvalue: can't assign a value to this expression");
852 c = code_append(c, prefix);
853 c = code_append(c, r);
854 c = code_append(c, middlepart);
856 c = abc_setlocal(c, temp);
857 c = code_append(c, write);
858 c = abc_getlocal(c, temp);
859 c = abc_kill(c, temp);
870 /* ------------ code blocks / statements ---------------- */
874 MAYBECODE: CODE {$$=$1;}
875 MAYBECODE: {$$=code_new();}
877 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
878 CODE: CODEPIECE {$$=$1;}
880 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
881 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
882 CODEPIECE: INTERFACE_DECLARATION {/*TODO*/$$=code_new();}
883 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
884 CODEPIECE: ';' {$$=code_new();}
885 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
886 CODEPIECE: VOIDEXPRESSION {$$=$1}
887 CODEPIECE: FOR {$$=$1}
888 CODEPIECE: WHILE {$$=$1}
889 CODEPIECE: BREAK {$$=$1}
890 CODEPIECE: RETURN {$$=$1}
891 CODEPIECE: IF {$$=$1}
892 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
893 CODEPIECE: FUNCTION_DECLARATION {/*TODO*/$$=code_new();}
894 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
896 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
897 CODEBLOCK : CODEPIECE ';' {$$=$1;}
898 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
900 /* ------------ variables --------------------------- */
902 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
903 | {$$.c=abc_pushundefined(0);
907 VAR : "const" | "var"
908 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
910 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
911 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
913 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
915 if(variable_exists($2->text))
916 syntaxerror("Variable %s already defined", $2->text);
918 if(!is_subtype_of($4.t, $3)) {
919 syntaxerror("Can't convert %s to %s", $4.t->name,
923 int index = new_variable($2->text, $3);
926 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
928 $$ = converttype($$, $4.t, $3);
929 $$ = abc_setlocal($$, index);
931 $$ = defaultvalue(0, $3);
932 $$ = abc_setlocal($$, index);
935 /* push default value for type on stack */
936 state->initcode = defaultvalue(state->initcode, $3);
937 state->initcode = abc_setlocal(state->initcode, index);
939 /* only bother to actually set this variable if its syntax is either
944 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
946 $$ = abc_coerce_a($$);
947 $$ = abc_setlocal($$, index);
953 /* that's the default for a local register, anyway
955 state->initcode = abc_pushundefined(state->initcode);
956 state->initcode = abc_setlocal(state->initcode, index);
958 printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
961 /* ------------ control flow ------------------------- */
963 MAYBEELSE: %prec prec_none {$$ = code_new();}
964 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
965 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
967 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
968 $$ = state->initcode;state->initcode=0;
970 $$ = code_append($$, $4.c);
971 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
973 $$ = code_append($$, $6);
975 myjmp = $$ = abc_jump($$, 0);
977 myif->branch = $$ = abc_label($$);
979 $$ = code_append($$, $7);
980 myjmp->branch = $$ = abc_label($$);
983 $$ = killvars($$);old_state();
986 FOR_INIT : {$$=code_new();}
987 FOR_INIT : VARIABLE_DECLARATION
988 FOR_INIT : VOIDEXPRESSION
990 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
991 $$ = state->initcode;state->initcode=0;
993 $$ = code_append($$, $4);
994 code_t*loopstart = $$ = abc_label($$);
995 $$ = code_append($$, $6.c);
996 code_t*myif = $$ = abc_iffalse($$, 0);
997 $$ = code_append($$, $10);
998 $$ = code_append($$, $8);
999 $$ = abc_jump($$, loopstart);
1000 code_t*out = $$ = abc_label($$);
1001 breakjumpsto($$, out);
1004 $$ = killvars($$);old_state();
1007 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
1008 $$ = state->initcode;state->initcode=0;
1010 code_t*myjmp = $$ = abc_jump($$, 0);
1011 code_t*loopstart = $$ = abc_label($$);
1012 $$ = code_append($$, $6);
1013 myjmp->branch = $$ = abc_label($$);
1014 $$ = code_append($$, $4.c);
1015 $$ = abc_iftrue($$, loopstart);
1016 code_t*out = $$ = abc_label($$);
1017 breakjumpsto($$, out);
1019 $$ = killvars($$);old_state();
1023 $$ = abc___break__(0);
1026 /* ------------ packages and imports ---------------- */
1028 X_IDENTIFIER: T_IDENTIFIER
1031 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,$2,$3);}
1032 PACKAGE: X_IDENTIFIER {$$=$1;}
1034 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1035 PACKAGE_DECLARATION : "package" '{' {startpackage(0)} MAYBECODE '}' {endpackage()}
1037 IMPORT : "import" QNAME {
1040 syntaxerror("Couldn't import class\n");
1041 state_has_imports();
1042 dict_put(state->imports, c->name, c);
1045 IMPORT : "import" PACKAGE '.' '*' {
1047 i->package = $2->text;
1048 state_has_imports();
1049 list_append(state->wildcard_imports, i);
1053 /* ------------ classes and interfaces (header) -------------- */
1055 MODIFIERS : {$$=empty_token();}
1056 MODIFIERS : MODIFIER_LIST {$$=$1}
1057 MODIFIER_LIST : MODIFIER MODIFIER_LIST {extend($2,$1);$$=$2;}
1058 MODIFIER_LIST : MODIFIER {$$=empty_token();extend($$,$1);}
1059 MODIFIER : KW_PUBLIC | KW_PRIVATE | KW_PROTECTED | KW_STATIC | KW_DYNAMIC | KW_FINAL | KW_OVERRIDE | KW_NATIVE | KW_INTERNAL
1061 EXTENDS : {$$=registry_getobjectclass();}
1062 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1064 EXTENDS_LIST : {$$=list_new();}
1065 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1067 IMPLEMENTS_LIST : {$$=list_new();}
1068 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1070 CLASS_DECLARATION : MODIFIERS "class" T_IDENTIFIER
1071 EXTENDS IMPLEMENTS_LIST
1072 '{' {startclass($1,$3,$4,$5, 0);}
1073 MAYBE_DECLARATION_LIST
1076 INTERFACE_DECLARATION : MODIFIERS "interface" T_IDENTIFIER
1078 '{' {startclass($1,$3,0,$4,1);}
1079 MAYBE_IDECLARATION_LIST
1082 /* ------------ classes and interfaces (body) -------------- */
1084 MAYBE_DECLARATION_LIST :
1085 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1086 DECLARATION_LIST : DECLARATION
1087 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1089 DECLARATION : SLOT_DECLARATION
1090 DECLARATION : FUNCTION_DECLARATION
1092 VARCONST: "var" | "const"
1093 SLOT_DECLARATION: MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1095 memberinfo_t* info = memberinfo_register(state->clsinfo, $3->text, MEMBER_SLOT);
1101 t=abc_class_slot(state->cls, $3->text, &m);
1103 t=abc_class_slot(state->cls, $3->text, 0);
1105 if($2->type==KW_CONST) {
1106 t->kind= TRAIT_CONST;
1108 info->slot = t->slot_id;
1109 if($5.c && !is_pushundefined($5.c)) {
1111 c = abc_getlocal_0(c);
1112 c = code_append(c, $5.c);
1113 c = converttype(c, $5.t, $4);
1114 c = abc_setslot(c, t->slot_id);
1115 //c = abc_setproperty(c, $3->text);
1116 state->cls_init = code_append(state->cls_init, c);
1120 FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1121 MAYBETYPE '{' {startfunction(0,$1,$3,$4,$6,$8)} MAYBECODE '}'
1123 if(!state->m) syntaxerror("internal error: undefined function");
1127 /* ------------- package + class ids --------------- */
1129 CLASS: T_IDENTIFIER {
1131 /* try current package */
1132 $$ = registry_findclass(state->package, $1->text);
1134 /* try explicit imports */
1135 dictentry_t* e = dict_get_slot(state->imports, $1->text);
1139 if(!strcmp(e->key, $1->text)) {
1140 $$ = (classinfo_t*)e->data;
1145 /* try package.* imports */
1146 import_list_t*l = state->wildcard_imports;
1150 //printf("does package %s contain a class %s?\n", l->import->package, $1->text);
1151 $$ = registry_findclass(l->import->package, $1->text);
1155 /* try global package */
1157 $$ = registry_findclass("", $1->text);
1160 if(!$$) syntaxerror("Could not find class %s\n", $1->text);
1163 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1164 $$ = registry_findclass($1->text, $3->text);
1165 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1->text, $3->text);
1168 QNAME: PACKAGEANDCLASS
1172 /* ----------function calls, constructor calls ------ */
1174 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1175 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1177 MAYBE_EXPRESSION_LIST : {$$=0;}
1178 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1179 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1180 typedcode_t*t = malloc(sizeof(typedcode_t));
1182 list_append($$, t);}
1183 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1184 typedcode_t*t = malloc(sizeof(typedcode_t));
1186 list_append($$, t);}
1188 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1192 /* TODO: why do we have to *find* our own classes? */
1193 $$.c = abc_findpropstrict2($$.c, &m);
1195 typedcode_list_t*l = $3;
1198 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1202 $$.c = abc_constructprop2($$.c, &m, len);
1206 /* TODO: use abc_call (for calling local variables),
1207 abc_callstatic (for calling own methods)
1210 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1211 typedcode_list_t*l = $3;
1213 code_t*paramcode = 0;
1215 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1221 if($$.c->opcode == OPCODE_GETPROPERTY) {
1222 multiname_t*name = multiname_clone($$.c->data[0]);
1223 $$.c = code_cutlast($$.c);
1224 $$.c = code_append($$.c, paramcode);
1225 $$.c = abc_callproperty2($$.c, name, len);
1226 } else if($$.c->opcode == OPCODE_GETSLOT) {
1227 int slot = (int)(ptroff_t)$$.c->data[0];
1228 trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME
1229 if(t->kind!=TRAIT_METHOD) {syntaxerror("not a function");}
1231 abc_method_t*m = t->method;
1232 $$.c = code_cutlast($$.c);
1233 $$.c = code_append($$.c, paramcode);
1235 //$$.c = abc_callmethod($$.c, m, len); //#1051 illegal early access binding
1236 $$.c = abc_callproperty2($$.c, t->name, len);
1238 int i = find_variable_safe("this", 0);
1239 $$.c = abc_getlocal($$.c, i);
1240 $$.c = code_append($$.c, paramcode);
1241 $$.c = abc_call($$.c, len);
1243 /* TODO: look up the functions's return value */
1247 RETURN: "return" %prec prec_none {
1248 $$ = abc_returnvoid(0);
1250 RETURN: "return" EXPRESSION {
1252 $$ = abc_returnvalue($$);
1254 // ----------------------- expression types -------------------------------------
1256 NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;}
1257 EXPRESSION : E %prec prec_belowminus {$$ = $1;}
1258 EXPRESSION : EXPRESSION ',' E %prec prec_belowminus {
1260 $$.c = abc_pop($$.c);
1261 $$.c = code_append($$.c,$3.c);
1264 VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=abc_pop($1.c);}
1266 // ----------------------- expression evaluation -------------------------------------
1269 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1271 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1275 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1276 $$.t = TYPE_BOOLEAN;
1278 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1279 $$.t = TYPE_BOOLEAN;
1281 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1282 $$.t = TYPE_BOOLEAN;
1284 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1285 $$.t = TYPE_BOOLEAN;
1287 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1288 $$.t = TYPE_BOOLEAN;
1290 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1291 $$.t = TYPE_BOOLEAN;
1293 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1294 $$.t = TYPE_BOOLEAN;
1297 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1299 $$.c = converttype($$.c, $1.t, $$.t);
1300 $$.c = abc_dup($$.c);
1301 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1302 $$.c = abc_pop($$.c);
1303 $$.c = code_append($$.c,$3.c);
1304 $$.c = converttype($$.c, $1.t, $$.t);
1305 code_t*label = $$.c = abc_label($$.c);
1306 jmp->branch = label;
1308 E : E "&&" E {$$.t = join_types($1.t, $3.t, 'A');
1310 $$.c = converttype($$.c, $1.t, $$.t);
1311 $$.c = abc_dup($$.c);
1312 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1313 $$.c = abc_pop($$.c);
1314 $$.c = code_append($$.c,$3.c);
1315 $$.c = converttype($$.c, $1.t, $$.t);
1316 code_t*label = $$.c = abc_label($$.c);
1317 jmp->branch = label;
1320 E : E '.' T_IDENTIFIER
1323 //namespace_t ns = {$$.t->access, (char*)$$.t->package};
1324 namespace_t ns = {$$.t->access, ""};
1325 multiname_t m = {QNAME, &ns, 0, $3->text};
1326 $$.c = abc_getproperty2($$.c, &m);
1327 /* FIXME: get type of ($1.t).$3 */
1328 $$.t = registry_getanytype();
1330 namespace_t ns = {ACCESS_PACKAGE, ""};
1331 multiname_t m = {QNAME, &ns, 0, $3->text};
1332 $$.c = abc_getproperty2($$.c, &m);
1333 $$.t = registry_getanytype();
1337 E : '!' E {$$.c=$2.c;
1338 $$.c = abc_not($$.c);
1339 $$.t = TYPE_BOOLEAN;
1344 E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c);
1345 $$.t = join_types($1.t, $3.t, '+');
1347 E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c);
1348 $$.t = join_types($1.t, $3.t, '%');
1350 E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c);
1351 $$.t = join_types($1.t, $3.t, '*');
1356 E : '(' E ')' {$$=$2;}
1361 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1366 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1368 $$.c = toreadwrite($1.c, c);
1371 E : E "-=" E { code_t*c = $3.c;
1372 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1373 c=abc_subtract_i(c);
1377 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
1379 $$.c = toreadwrite($1.c, c);
1382 E : E '=' E { code_t*c = 0;
1384 c = code_append(c, $3.c);
1385 c = converttype(c, $3.t, $1.t);
1386 $$.c = toreadwrite($1.c, c);
1390 // TODO: use inclocal where appropriate
1391 E : E "++" { code_t*c = 0;
1392 classinfo_t*type = $1.t;
1393 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1394 c=abc_increment_i(c);
1399 c=converttype(c, type, $1.t);
1400 $$.c = toreadwrite($1.c, c);
1403 E : E "--" { code_t*c = 0;
1404 classinfo_t*type = $1.t;
1405 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1406 c=abc_increment_i(c);
1411 c=converttype(c, type, $1.t);
1412 $$.c = toreadwrite($1.c, c);
1416 VAR_READ : T_IDENTIFIER {
1421 if((i = find_variable($1->text, &$$.t)) >= 0) {
1422 $$.c = abc_getlocal($$.c, i);
1423 } else if((m = registry_findmember(state->clsinfo, $1->text))) {
1426 $$.c = abc_getlocal_0($$.c);
1427 $$.c = abc_getslot($$.c, m->slot);
1429 $$.c = abc_getlocal_0($$.c);
1430 $$.c = abc_getproperty($$.c, $1->text);
1433 warning("Couldn't resolve %s, doing late binding", $1->text);
1434 state->late_binding = 1;
1437 $$.c = abc_findpropstrict($$.c, $1->text);
1438 $$.c = abc_getproperty($$.c, $1->text);
1443 // ------------------------------------------------------------------------------
1446 TYPE : QNAME {$$=$1;}
1447 | '*' {$$=registry_getanytype();}
1448 | "String" {$$=registry_getstringclass();}
1449 | "int" {$$=registry_getintclass();}
1450 | "uint" {$$=registry_getuintclass();}
1451 | "Boolean" {$$=registry_getbooleanclass();}
1452 | "Number" {$$=registry_getnumberclass();}
1454 MAYBETYPE: ':' TYPE {$$=$2;}
1457 //FUNCTION_HEADER: NAMESPACE MODIFIERS T_FUNCTION GETSET T_IDENTIFIER '(' PARAMS ')'
1458 FUNCTION_HEADER: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1461 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER
1462 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_IDENTIFIER
1463 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_STRING
1465 //NAMESPACE : {$$=empty_token();}
1466 //NAMESPACE : T_IDENTIFIER {$$=$1};
1468 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1469 //MULTINAME(m, registry_getintclass());
1470 //$$.c = abc_coerce2($$.c, &m); // FIXME
1473 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1476 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1479 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1482 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1485 CONSTANT : T_STRING {$$.c = abc_pushstring(0, $1);
1488 CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
1489 $$.t = TYPE_BOOLEAN;
1491 CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
1492 $$.t = TYPE_BOOLEAN;
1494 CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
1498 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
1501 //VARIABLE : T_IDENTIFIER
1502 //VARIABLE : VARIABLE '.' T_IDENTIFIER
1503 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
1504 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
1505 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
1506 //VARIABLE : VARIABLE '[' EXPRESSION ']' // unqualified expression
1508 GETSET : "get" {$$=$1;}
1510 | {$$=empty_token();}
1512 MAYBE_PARAM_LIST: {$$=list_new();}
1513 MAYBE_PARAM_LIST: PARAM_LIST {$$=$1;}
1514 PARAM_LIST: PARAM_LIST ',' PARAM {$$ =$1; list_append($$, $3);}
1515 PARAM_LIST: PARAM {$$ = list_new();list_append($$, $1);}
1516 PARAM: T_IDENTIFIER ':' TYPE {$$ = malloc(sizeof(param_t));
1517 $$->name=$1->text;$$->type = $3;}
1518 PARAM: T_IDENTIFIER {$$ = malloc(sizeof(param_t));
1519 $$->name=$1->text;$$->type = TYPE_ANY;}
1521 IDECLARATION : VARIABLE_DECLARATION
1522 IDECLARATION : FUNCTION_DECLARATION
1524 //IDENTIFIER_LIST : T_IDENTIFIER ',' IDENTIFIER_LIST {extend($3,$1);$$=$3;}
1525 //IDENTIFIER_LIST : T_IDENTIFIER {$$=empty_token();extend($$,$1);}
1527 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1528 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1531 MAYBE_IDECLARATION_LIST :
1532 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1533 IDECLARATION_LIST : IDECLARATION
1534 IDECLARATION_LIST : IDECLARATION_LIST FUNCTION_HEADER
1537 // 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
1538 // syntactic keywords: each get set namespace include dynamic final native override static