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 abc_method_body_t*m = state->init->method->body;
371 __ findpropstrict(m, "[package]::trace");
372 __ pushstring(m, "[entering global init function]");
373 __ callpropvoid(m, "[package]::trace", 1);
375 void* finalize_state()
377 if(state->level!=1) {
378 syntaxerror("unexpected end of file");
380 abc_method_body_t*m = state->init->method->body;
383 __ findpropstrict(m, "[package]::trace");
384 __ pushstring(m, "[leaving global init function]");
385 __ callpropvoid(m, "[package]::trace", 1);
391 static void startpackage(token_t*t)
394 syntaxerror("Packages can not be nested.");
397 char*name = t?t->text:"";
398 /*printf("entering package \"%s\"\n", name);*/
399 state->package = name;
401 static void endpackage()
403 /*printf("leaving package \"%s\"\n", state->package);*/
408 static void startclass(token_t*modifiers, token_t*name, classinfo_t*extends, classinfo_list_t*implements, char interface)
411 syntaxerror("inner classes now allowed");
414 char*classname = name->text;
417 classinfo_list_t*mlist=0;
418 /*printf("entering class %s\n", name->text);
419 printf(" modifiers: ");for(t=modifiers->tokens;t;t=t->next) printf("%s ", t->token->text);printf("\n");
421 printf(" extends: %s.%s\n", extends->package, extends->name);
422 printf(" implements (%d): ", list_length(implements));
423 for(mlist=implements;mlist;mlist=mlist->next) {
424 printf("%s ", mlist->classinfo?mlist->classinfo->name:0);
429 char public=0,internal=0,final=0,sealed=1;
430 for(t=modifiers->tokens;t;t=t->next) {
431 if(t->token->type == KW_INTERNAL) {
432 /* the programmer is being explicit-
433 being internal is the default anyway */
435 } else if(t->token->type == KW_PUBLIC) {
437 } else if(t->token->type == KW_FINAL) {
440 syntaxerror("modifier \"%s\" not supported in class declaration", t->token->text);
444 syntaxerror("public and internal not supported at the same time.");
446 /* create the class name, together with the proper attributes */
450 if(!public && !state->package) {
451 access = ACCESS_PRIVATE; package = current_filename;
452 } else if(!public && state->package) {
453 access = ACCESS_PACKAGEINTERNAL; package = state->package;
454 } else if(state->package) {
455 access = ACCESS_PACKAGE; package = state->package;
457 syntaxerror("public classes only allowed inside a package");
460 if(registry_findclass(package, classname)) {
461 syntaxerror("Package \"%s\" already contains a class called \"%s\"", package, classname);
464 state->clsinfo = classinfo_register(access, package, classname);
466 MULTINAME(classname2,state->clsinfo);
468 multiname_t*extends2 = sig2mname(extends);
471 state->cls_init = abc_getlocal_0(state->cls_init);
472 state->cls_init = abc_constructsuper(state->cls_init, 0);
475 state->cls = abc_class_new(state->file, &classname2, extends2);
476 if(final) abc_class_final(state->cls);
477 if(sealed) abc_class_sealed(state->cls);
478 if(interface) abc_class_interface(state->cls);
480 for(mlist=implements;mlist;mlist=mlist->next) {
481 MULTINAME(m, mlist->classinfo);
482 abc_class_add_interface(state->cls, &m);
485 /* now write the construction code for this class */
486 int slotindex = abc_initscript_addClassTrait(state->init, &classname2, state->cls);
488 abc_method_body_t*m = state->init->method->body;
489 __ getglobalscope(m);
490 classinfo_t*s = extends;
495 //TODO: take a look at the current scope stack, maybe
496 // we can re-use something
501 multiname_t*s2 = sig2mname(s);
503 multiname_destroy(s2);
505 __ pushscope(m); count++;
506 m->code = m->code->prev->prev; // invert
508 /* continue appending after last op end */
509 while(m->code && m->code->next) m->code = m->code->next;
511 /* TODO: if this is one of *our* classes, we can also
512 do a getglobalscope/getslot <nr> (which references
513 the init function's slots) */
515 __ getlex2(m, extends2);
517 /* notice: we get a Verify Error #1107 if the top elemnt on the scope
518 stack is not the superclass */
519 __ pushscope(m);count++;
522 /* notice: we get a verify error #1107 if the top element on the scope
523 stack is not the global object */
525 __ pushscope(m);count++;
527 __ newclass(m,state->cls);
531 __ setslot(m, slotindex);
533 /* flash.display.MovieClip handling */
534 if(!globalclass && public && classinfo_equals(registry_getMovieClip(),extends)) {
535 if(state->package && state->package[0]) {
536 globalclass = concat3str(state->package, ".", classname);
538 globalclass = strdup(classname);
541 multiname_destroy(extends2);
544 static void endclass()
546 if(state->cls_init) {
547 if(!state->cls->constructor) {
548 abc_method_body_t*m = abc_class_constructor(state->cls, 0, 0);
549 m->code = code_append(m->code, state->cls_init);
550 m->code = abc_returnvoid(m->code);
552 code_t*c = state->cls->constructor->body->code;
553 c = code_append(state->cls_init, c);
554 state->cls->constructor->body->code = c;
558 if(state->cls_static_init) {
559 if(!state->cls->static_constructor) {
560 abc_method_body_t*m = abc_class_staticconstructor(state->cls, 0, 0);
561 m->code = state->cls_static_init;
563 state->cls->static_constructor->body->code =
564 code_append(state->cls_static_init, state->cls->static_constructor->body->code);
570 static void startfunction(token_t*ns, token_t*mod, token_t*getset, token_t*name,
571 param_list_t*params, classinfo_t*type)
575 state->function = name->text;
578 syntaxerror("not able to start another method scope");
581 multiname_t*type2 = sig2mname(type);
582 if(!strcmp(state->clsinfo->name,name->text)) {
583 state->m = abc_class_constructor(state->cls, type2, 0);
585 state->minfo = memberinfo_register(state->clsinfo, name->text, MEMBER_METHOD);
586 state->m = abc_class_method(state->cls, type2, name->text, 0);
587 state->minfo->slot = state->m->method->trait->slot_id;
589 if(getset->type == KW_GET) {
590 state->m->method->trait->kind = TRAIT_GETTER;
592 if(getset->type == KW_SET) {
593 state->m->method->trait->kind = TRAIT_SETTER;
597 for(p=params;p;p=p->next) {
598 multiname_t*m = sig2mname(p->param->type);
599 list_append(state->m->method->parameters, m);
602 /* state->vars is initialized by state_new */
603 array_append(state->vars, "this", 0);
604 for(p=params;p;p=p->next) {
605 array_append(state->vars, p->param->name, 0);
608 static void endfunction(code_t*body)
611 if(state->late_binding) {
612 c = abc_getlocal_0(c);
613 c = abc_pushscope(c);
615 c = code_append(c, state->initcode);
616 c = code_append(c, body);
617 c = abc_returnvoid(c);
619 if(state->m->code) syntaxerror("internal error");
625 static token_t* empty_token()
633 void extend(token_t*list, token_t*add) {
634 list_append(list->tokens,add);
636 list->text = add->text;
638 void extend_s(token_t*list, char*seperator, token_t*add) {
639 list_append(list->tokens,add);
640 char*t1 = list->text;
646 list->text = malloc(l1+l2+l3+1);
647 strcpy(list->text, t1);
648 strcpy(list->text+l1, t2);
649 strcpy(list->text+l1+l2, t3);
650 list->text[l1+l2+l3]=0;
653 static int find_variable(char*name, classinfo_t**m)
655 state_list_t* s = state_stack;
657 int i = array_find(s->state->vars, name);
660 *m = array_getvalue(s->state->vars, i);
662 return i + s->state->local_var_base;
668 static int find_variable_safe(char*name, classinfo_t**m)
670 int i = find_variable(name, m);
672 syntaxerror("undefined variable: %s", name);
675 static char variable_exists(char*name)
677 return array_contains(state->vars, name);
679 static int new_variable(char*name, classinfo_t*type)
681 return array_append(state->vars, name, type) + state->local_var_base;
683 #define TEMPVARNAME "__as3_temp__"
684 static int gettempvar()
686 int i = find_variable(TEMPVARNAME, 0);
688 return new_variable(TEMPVARNAME, 0);
694 code_t* killvars(code_t*c)
697 for(t=0;t<state->vars->num;t++) {
698 classinfo_t*type = array_getvalue(state->vars, t);
699 //do this always, otherwise register types don't match
700 //in the verifier when doing nested loops
701 //if(!TYPE_IS_BUILTIN_SIMPLE(type)) {
702 c = abc_kill(c, t+state->local_var_base);
708 char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
713 void breakjumpsto(code_t*c, code_t*jump)
718 if(c->opcode == OPCODE___BREAK__) {
719 c->opcode = OPCODE_JUMP;
726 classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
728 return registry_getanytype(); // FIXME
730 code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
735 /*TODO: can omit this if from is zero? */
736 return abc_coerce_a(c);
738 if(TYPE_IS_NUMBER(from) && TYPE_IS_UINT(to)) {
739 MULTINAME(m, TYPE_UINT);
740 return abc_coerce2(c, &m);
742 if(TYPE_IS_NUMBER(from) && TYPE_IS_INT(to)) {
743 MULTINAME(m, TYPE_INT);
744 return abc_coerce2(c, &m);
749 code_t*defaultvalue(code_t*c, classinfo_t*type)
751 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type) || TYPE_IS_FLOAT(type)) {
752 c = abc_pushbyte(c, 0);
753 } else if(TYPE_IS_BOOLEAN(type)) {
754 c = abc_pushfalse(c);
761 char is_pushundefined(code_t*c)
763 return (c && !c->prev && !c->next && c->opcode == OPCODE_PUSHUNDEFINED);
766 static code_t* toreadwrite(code_t*in, code_t*middlepart)
770 [prefix code] [read instruction]
774 [prefix code] ([dup]) [read instruction] [setvar] [middlepart] [write instruction] [getvar]
778 syntaxerror("internal error");
780 int temp = gettempvar();
782 /* chop off read instruction */
786 prefix = r->prev;r->prev = 0;
792 /* generate the write instruction, and maybe append a dup to the prefix code */
793 code_t* write = abc_nop(middlepart);
794 if(r->opcode == OPCODE_GETPROPERTY) {
795 write->opcode = OPCODE_SETPROPERTY;
796 multiname_t*m = (multiname_t*)r->data[0];
797 write->data[0] = multiname_clone(m);
799 syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname)");
800 prefix = abc_dup(prefix); // we need the object, too
801 } else if(r->opcode == OPCODE_GETSLOT) {
802 write->opcode = OPCODE_SETSLOT;
803 write->data[0] = r->data[0];
804 prefix = abc_dup(prefix); // we need the object, too
805 } else if(r->opcode == OPCODE_GETLOCAL) {
806 write->opcode = OPCODE_SETLOCAL;
807 write->data[0] = r->data[0];
808 } else if(r->opcode == OPCODE_GETLOCAL_0) {
809 write->opcode = OPCODE_SETLOCAL_0;
810 } else if(r->opcode == OPCODE_GETLOCAL_1) {
811 write->opcode = OPCODE_SETLOCAL_1;
812 } else if(r->opcode == OPCODE_GETLOCAL_2) {
813 write->opcode = OPCODE_SETLOCAL_2;
814 } else if(r->opcode == OPCODE_GETLOCAL_3) {
815 write->opcode = OPCODE_SETLOCAL_3;
817 code_dump(r, 0, 0, "", stdout);
818 syntaxerror("illegal lvalue: can't assign a value to this expression");
821 c = code_append(c, r);
824 c = abc_setlocal(c, temp);
825 c = code_append(c, middlepart);
826 c = abc_getlocal(c, temp);
827 c = abc_kill(c, temp);
838 /* ------------ code blocks / statements ---------------- */
842 MAYBECODE: CODE {$$=$1;}
843 MAYBECODE: {$$=code_new();}
845 CODE: CODE CODEPIECE {$$=code_append($1,$2);}
846 CODE: CODEPIECE {$$=$1;}
848 CODEPIECE: PACKAGE_DECLARATION {$$=code_new();/*enters a scope*/}
849 CODEPIECE: CLASS_DECLARATION {$$=code_new();/*enters a scope*/}
850 CODEPIECE: INTERFACE_DECLARATION {/*TODO*/$$=code_new();}
851 CODEPIECE: IMPORT {$$=code_new();/*adds imports to current scope*/}
852 CODEPIECE: ';' {$$=code_new();}
853 CODEPIECE: VARIABLE_DECLARATION {$$=$1}
854 CODEPIECE: VOIDEXPRESSION {$$=$1}
855 CODEPIECE: FOR {$$=$1}
856 CODEPIECE: WHILE {$$=$1}
857 CODEPIECE: BREAK {$$=$1}
858 CODEPIECE: RETURN {$$=$1}
859 CODEPIECE: IF {$$=$1}
860 CODEPIECE: NAMESPACE_DECLARATION {/*TODO*/$$=code_new();}
861 CODEPIECE: FUNCTION_DECLARATION {/*TODO*/$$=code_new();}
862 CODEPIECE: USE_NAMESPACE {/*TODO*/$$=code_new();}
864 CODEBLOCK : '{' MAYBECODE '}' {$$=$2;}
865 CODEBLOCK : CODEPIECE ';' {$$=$1;}
866 CODEBLOCK : CODEPIECE %prec below_semicolon {$$=$1;}
868 /* ------------ variables --------------------------- */
870 MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
871 | {$$.c=abc_pushundefined(0);
875 VAR : "const" | "var"
876 VARIABLE_DECLARATION : VAR VARIABLE_LIST {$$=$2;}
878 VARIABLE_LIST: ONE_VARIABLE {$$ = $1;}
879 VARIABLE_LIST: VARIABLE_LIST ',' ONE_VARIABLE {$$ = code_append($1, $3);}
881 ONE_VARIABLE: {} T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
883 if(variable_exists($2->text))
884 syntaxerror("Variable %s already defined", $2->text);
886 if(!is_subtype_of($4.t, $3)) {
887 syntaxerror("Can't convert %s to %s", $4.t->name,
891 int index = new_variable($2->text, $3);
894 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
896 $$ = converttype($$, $4.t, $3);
897 $$ = abc_setlocal($$, index);
899 $$ = defaultvalue(0, $3);
900 $$ = abc_setlocal($$, index);
903 /* push default value for type on stack */
904 state->initcode = defaultvalue(state->initcode, $3);
905 state->initcode = abc_setlocal(state->initcode, index);
907 /* only bother to actually set this variable if its syntax is either
912 if($4.c->prev || $4.c->opcode != OPCODE_PUSHUNDEFINED) {
914 $$ = abc_coerce_a($$);
915 $$ = abc_setlocal($$, index);
921 /* that's the default for a local register, anyway
923 state->initcode = abc_pushundefined(state->initcode);
924 state->initcode = abc_setlocal(state->initcode, index);
926 printf("variable %s -> %d (%s)\n", $2->text, index, $4.t?$4.t->name:"");
929 /* ------------ control flow ------------------------- */
931 MAYBEELSE: %prec prec_none {$$ = code_new();}
932 MAYBEELSE: "else" CODEBLOCK {$$=$2;}
933 //MAYBEELSE: ';' "else" CODEBLOCK {$$=$3;}
935 IF : "if" '(' {new_state();} EXPRESSION ')' CODEBLOCK MAYBEELSE {
936 $$ = state->initcode;state->initcode=0;
938 $$ = code_append($$, $4.c);
939 code_t*myjmp,*myif = $$ = abc_iffalse($$, 0);
941 $$ = code_append($$, $6);
943 myjmp = $$ = abc_jump($$, 0);
945 myif->branch = $$ = abc_label($$);
947 $$ = code_append($$, $7);
948 myjmp->branch = $$ = abc_label($$);
951 $$ = killvars($$);old_state();
954 FOR_INIT : {$$=code_new();}
955 FOR_INIT : VARIABLE_DECLARATION
956 FOR_INIT : VOIDEXPRESSION
958 FOR : "for" '(' {new_state();} FOR_INIT ';' EXPRESSION ';' VOIDEXPRESSION ')' CODEBLOCK {
959 $$ = state->initcode;state->initcode=0;
961 $$ = code_append($$, $4);
962 code_t*loopstart = $$ = abc_label($$);
963 $$ = code_append($$, $6.c);
964 code_t*myif = $$ = abc_iffalse($$, 0);
965 $$ = code_append($$, $10);
966 $$ = code_append($$, $8);
967 $$ = abc_jump($$, loopstart);
968 code_t*out = $$ = abc_label($$);
969 breakjumpsto($$, out);
972 $$ = killvars($$);old_state();
975 WHILE : "while" '(' {new_state();} EXPRESSION ')' CODEBLOCK {
976 $$ = state->initcode;state->initcode=0;
978 code_t*myjmp = $$ = abc_jump($$, 0);
979 code_t*loopstart = $$ = abc_label($$);
980 $$ = code_append($$, $6);
981 myjmp->branch = $$ = abc_label($$);
982 $$ = code_append($$, $4.c);
983 $$ = abc_iftrue($$, loopstart);
984 code_t*out = $$ = abc_label($$);
985 breakjumpsto($$, out);
987 $$ = killvars($$);old_state();
991 $$ = abc___break__(0);
994 /* ------------ packages and imports ---------------- */
996 X_IDENTIFIER: T_IDENTIFIER
999 PACKAGE: PACKAGE '.' X_IDENTIFIER {$$ = concat3($1,$2,$3);}
1000 PACKAGE: X_IDENTIFIER {$$=$1;}
1002 PACKAGE_DECLARATION : "package" PACKAGE '{' {startpackage($2)} MAYBECODE '}' {endpackage()}
1003 PACKAGE_DECLARATION : "package" '{' {startpackage(0)} MAYBECODE '}' {endpackage()}
1005 IMPORT : "import" QNAME {
1008 syntaxerror("Couldn't import class\n");
1009 state_has_imports();
1010 dict_put(state->imports, c->name, c);
1013 IMPORT : "import" PACKAGE '.' '*' {
1015 i->package = $2->text;
1016 state_has_imports();
1017 list_append(state->wildcard_imports, i);
1021 /* ------------ classes and interfaces (header) -------------- */
1023 MODIFIERS : {$$=empty_token();}
1024 MODIFIERS : MODIFIER_LIST {$$=$1}
1025 MODIFIER_LIST : MODIFIER MODIFIER_LIST {extend($2,$1);$$=$2;}
1026 MODIFIER_LIST : MODIFIER {$$=empty_token();extend($$,$1);}
1027 MODIFIER : KW_PUBLIC | KW_PRIVATE | KW_PROTECTED | KW_STATIC | KW_DYNAMIC | KW_FINAL | KW_OVERRIDE | KW_NATIVE | KW_INTERNAL
1029 EXTENDS : {$$=registry_getobjectclass();}
1030 EXTENDS : KW_EXTENDS QNAME {$$=$2;}
1032 EXTENDS_LIST : {$$=list_new();}
1033 EXTENDS_LIST : KW_EXTENDS QNAME_LIST {$$=$2;}
1035 IMPLEMENTS_LIST : {$$=list_new();}
1036 IMPLEMENTS_LIST : KW_IMPLEMENTS QNAME_LIST {$$=$2;}
1038 CLASS_DECLARATION : MODIFIERS "class" T_IDENTIFIER
1039 EXTENDS IMPLEMENTS_LIST
1040 '{' {startclass($1,$3,$4,$5, 0);}
1041 MAYBE_DECLARATION_LIST
1044 INTERFACE_DECLARATION : MODIFIERS "interface" T_IDENTIFIER
1046 '{' {startclass($1,$3,0,$4,1);}
1047 MAYBE_IDECLARATION_LIST
1050 /* ------------ classes and interfaces (body) -------------- */
1052 MAYBE_DECLARATION_LIST :
1053 MAYBE_DECLARATION_LIST : DECLARATION_LIST
1054 DECLARATION_LIST : DECLARATION
1055 DECLARATION_LIST : DECLARATION_LIST DECLARATION
1057 DECLARATION : SLOT_DECLARATION
1058 DECLARATION : FUNCTION_DECLARATION
1060 VARCONST: "var" | "const"
1061 SLOT_DECLARATION: MODIFIERS VARCONST T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION {
1063 memberinfo_t* info = memberinfo_register(state->clsinfo, $3->text, MEMBER_SLOT);
1069 t=abc_class_slot(state->cls, $3->text, &m);
1071 t=abc_class_slot(state->cls, $3->text, 0);
1073 if($2->type==KW_CONST) {
1074 t->kind= TRAIT_CONST;
1076 info->slot = t->slot_id;
1077 if($5.c && !is_pushundefined($5.c)) {
1079 c = abc_getlocal_0(c);
1080 c = code_append(c, $5.c);
1081 c = converttype(c, $5.t, $4);
1082 c = abc_setslot(c, t->slot_id);
1083 //c = abc_setproperty(c, $3->text);
1084 state->cls_init = code_append(state->cls_init, c);
1088 FUNCTION_DECLARATION: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1089 MAYBETYPE '{' {startfunction(0,$1,$3,$4,$6,$8)} MAYBECODE '}'
1091 if(!state->m) syntaxerror("internal error: undefined function");
1095 /* ------------- package + class ids --------------- */
1097 CLASS: T_IDENTIFIER {
1099 /* try current package */
1100 $$ = registry_findclass(state->package, $1->text);
1102 /* try explicit imports */
1103 dictentry_t* e = dict_get_slot(state->imports, $1->text);
1107 if(!strcmp(e->key, $1->text)) {
1108 $$ = (classinfo_t*)e->data;
1113 /* try package.* imports */
1114 import_list_t*l = state->wildcard_imports;
1118 //printf("does package %s contain a class %s?\n", l->import->package, $1->text);
1119 $$ = registry_findclass(l->import->package, $1->text);
1123 /* try global package */
1125 $$ = registry_findclass("", $1->text);
1128 if(!$$) syntaxerror("Could not find class %s\n", $1->text);
1131 PACKAGEANDCLASS : PACKAGE '.' T_IDENTIFIER {
1132 $$ = registry_findclass($1->text, $3->text);
1133 if(!$$) syntaxerror("Couldn't find class %s.%s\n", $1->text, $3->text);
1136 QNAME: PACKAGEANDCLASS
1140 /* ----------function calls, constructor calls ------ */
1142 MAYBE_PARAM_VALUES : %prec prec_none {$$=0;}
1143 MAYBE_PARAM_VALUES : '(' MAYBE_EXPRESSION_LIST ')' {$$=$2}
1145 MAYBE_EXPRESSION_LIST : {$$=0;}
1146 MAYBE_EXPRESSION_LIST : EXPRESSION_LIST
1147 EXPRESSION_LIST : NONCOMMAEXPRESSION {$$=list_new();
1148 typedcode_t*t = malloc(sizeof(typedcode_t));
1150 list_append($$, t);}
1151 EXPRESSION_LIST : EXPRESSION_LIST ',' NONCOMMAEXPRESSION {$$=$1;
1152 typedcode_t*t = malloc(sizeof(typedcode_t));
1154 list_append($$, t);}
1156 NEW : "new" CLASS MAYBE_PARAM_VALUES {
1160 /* TODO: why do we have to *find* our own classes? */
1161 $$.c = abc_findpropstrict2($$.c, &m);
1163 typedcode_list_t*l = $3;
1166 $$.c = code_append($$.c, l->typedcode->c); // push parameters on stack
1170 $$.c = abc_constructprop2($$.c, &m, len);
1174 /* TODO: use abc_call (for calling local variables),
1175 abc_callstatic (for calling own methods)
1178 FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
1179 typedcode_list_t*l = $3;
1181 code_t*paramcode = 0;
1183 paramcode = code_append(paramcode, l->typedcode->c); // push parameters on stack
1189 if($$.c->opcode == OPCODE_GETPROPERTY) {
1190 multiname_t*name = multiname_clone($$.c->data[0]);
1191 $$.c = code_cutlast($$.c);
1192 $$.c = code_append($$.c, paramcode);
1193 $$.c = abc_callproperty2($$.c, name, len);
1194 } else if($$.c->opcode == OPCODE_GETSLOT) {
1195 int slot = (int)(ptroff_t)$$.c->data[0];
1196 trait_t*t = abc_class_find_slotid(state->cls,slot);//FIXME
1197 if(t->kind!=TRAIT_METHOD) {syntaxerror("not a function");}
1199 abc_method_t*m = t->method;
1200 $$.c = code_cutlast($$.c);
1201 $$.c = code_append($$.c, paramcode);
1203 //$$.c = abc_callmethod($$.c, m, len); //#1051 illegal early access binding
1204 $$.c = abc_callproperty2($$.c, t->name, len);
1206 int i = find_variable_safe("this", 0);
1207 $$.c = abc_getlocal($$.c, i);
1208 $$.c = code_append($$.c, paramcode);
1209 $$.c = abc_call($$.c, len);
1211 /* TODO: look up the functions's return value */
1215 RETURN: "return" %prec prec_none {
1216 $$ = abc_returnvoid(0);
1218 RETURN: "return" EXPRESSION {
1220 $$ = abc_returnvalue($$);
1222 // ----------------------- expression types -------------------------------------
1224 NONCOMMAEXPRESSION : E %prec prec_belowminus {$$=$1;}
1225 EXPRESSION : E %prec prec_belowminus {$$ = $1;}
1226 EXPRESSION : EXPRESSION ',' E %prec prec_belowminus {
1228 $$.c = abc_pop($$.c);
1229 $$.c = code_append($$.c,$3.c);
1232 VOIDEXPRESSION : EXPRESSION %prec prec_belowminus {$$=abc_pop($1.c);}
1234 // ----------------------- expression evaluation -------------------------------------
1237 E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
1239 E : T_REGEXP {$$.c = abc_pushundefined(0); /* FIXME */
1243 E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
1244 $$.t = TYPE_BOOLEAN;
1246 E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
1247 $$.t = TYPE_BOOLEAN;
1249 E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
1250 $$.t = TYPE_BOOLEAN;
1252 E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
1253 $$.t = TYPE_BOOLEAN;
1255 E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
1256 $$.t = TYPE_BOOLEAN;
1258 E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
1259 $$.t = TYPE_BOOLEAN;
1261 E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
1262 $$.t = TYPE_BOOLEAN;
1265 E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
1267 $$.c = converttype($$.c, $1.t, $$.t);
1268 $$.c = abc_dup($$.c);
1269 code_t*jmp = $$.c = abc_iftrue($$.c, 0);
1270 $$.c = abc_pop($$.c);
1271 $$.c = code_append($$.c,$3.c);
1272 $$.c = converttype($$.c, $1.t, $$.t);
1273 code_t*label = $$.c = abc_label($$.c);
1274 jmp->branch = label;
1276 E : E "&&" E {$$.t = join_types($1.t, $3.t, 'A');
1278 $$.c = converttype($$.c, $1.t, $$.t);
1279 $$.c = abc_dup($$.c);
1280 code_t*jmp = $$.c = abc_iffalse($$.c, 0);
1281 $$.c = abc_pop($$.c);
1282 $$.c = code_append($$.c,$3.c);
1283 $$.c = converttype($$.c, $1.t, $$.t);
1284 code_t*label = $$.c = abc_label($$.c);
1285 jmp->branch = label;
1288 E : E '.' T_IDENTIFIER
1291 //namespace_t ns = {$$.t->access, (char*)$$.t->package};
1292 namespace_t ns = {$$.t->access, ""};
1293 multiname_t m = {QNAME, &ns, 0, $3->text};
1294 $$.c = abc_getproperty2($$.c, &m);
1295 /* FIXME: get type of ($1.t).$3 */
1296 $$.t = registry_getanytype();
1298 namespace_t ns = {ACCESS_PACKAGE, ""};
1299 multiname_t m = {QNAME, &ns, 0, $3->text};
1300 $$.c = abc_getproperty2($$.c, &m);
1301 $$.t = registry_getanytype();
1305 E : '!' E {$$.c=$2.c;
1306 $$.c = abc_not($$.c);
1307 $$.t = TYPE_BOOLEAN;
1312 E : E '+' E {$$.c = code_append($1.c,$3.c);$$.c = abc_add($$.c);$$.c=abc_coerce_a($$.c);
1313 $$.t = join_types($1.t, $3.t, '+');
1315 E : E '%' E {$$.c = code_append($1.c,$3.c);$$.c = abc_modulo($$.c);$$.c=abc_coerce_a($$.c);
1316 $$.t = join_types($1.t, $3.t, '%');
1318 E : E '*' E {$$.c = code_append($1.c,$3.c);$$.c = abc_multiply($$.c);$$.c=abc_coerce_a($$.c);
1319 $$.t = join_types($1.t, $3.t, '*');
1324 E : '(' E ')' {$$=$2;}
1329 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1334 c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
1336 $$.c = toreadwrite($1.c, c);
1339 E : E "-=" E { code_t*c = $3.c;
1340 if(TYPE_IS_INT($3.t) || TYPE_IS_UINT($3.t)) {
1341 c=abc_subtract_i(c);
1345 c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
1347 $$.c = toreadwrite($1.c, c);
1350 E : E '=' E { code_t*c = 0;
1352 c = code_append(c, $3.c);
1353 c = converttype(c, $3.t, $1.t);
1354 $$.c = toreadwrite($1.c, c);
1358 // TODO: use inclocal where appropriate
1359 E : E "++" { code_t*c = 0;
1360 classinfo_t*type = $1.t;
1361 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1362 c=abc_increment_i(c);
1367 c=converttype(c, type, $1.t);
1368 $$.c = toreadwrite($1.c, c);
1371 E : E "--" { code_t*c = 0;
1372 classinfo_t*type = $1.t;
1373 if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
1374 c=abc_increment_i(c);
1379 c=converttype(c, type, $1.t);
1380 $$.c = toreadwrite($1.c, c);
1384 VAR_READ : T_IDENTIFIER {
1389 if((i = find_variable($1->text, &$$.t)) >= 0) {
1390 $$.c = abc_getlocal($$.c, i);
1391 } else if((m = registry_findmember(state->clsinfo, $1->text))) {
1394 $$.c = abc_getlocal_0($$.c);
1395 $$.c = abc_getslot($$.c, m->slot);
1397 $$.c = abc_getlocal_0($$.c);
1398 $$.c = abc_getproperty($$.c, $1->text);
1401 warning("Couldn't resolve %s, doing late binding", $1->text);
1402 state->late_binding = 1;
1405 $$.c = abc_findpropstrict($$.c, $1->text);
1406 $$.c = abc_getproperty($$.c, $1->text);
1411 // ------------------------------------------------------------------------------
1414 TYPE : QNAME {$$=$1;}
1415 | '*' {$$=registry_getanytype();}
1416 | "String" {$$=registry_getstringclass();}
1417 | "int" {$$=registry_getintclass();}
1418 | "uint" {$$=registry_getuintclass();}
1419 | "Boolean" {$$=registry_getbooleanclass();}
1420 | "Number" {$$=registry_getnumberclass();}
1422 MAYBETYPE: ':' TYPE {$$=$2;}
1425 //FUNCTION_HEADER: NAMESPACE MODIFIERS T_FUNCTION GETSET T_IDENTIFIER '(' PARAMS ')'
1426 FUNCTION_HEADER: MODIFIERS "function" GETSET T_IDENTIFIER '(' MAYBE_PARAM_LIST ')'
1429 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER
1430 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_IDENTIFIER
1431 NAMESPACE_DECLARATION : MODIFIERS KW_NAMESPACE T_IDENTIFIER '=' T_STRING
1433 //NAMESPACE : {$$=empty_token();}
1434 //NAMESPACE : T_IDENTIFIER {$$=$1};
1436 CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
1437 //MULTINAME(m, registry_getintclass());
1438 //$$.c = abc_coerce2($$.c, &m); // FIXME
1441 CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
1444 CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
1447 CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
1450 CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
1453 CONSTANT : T_STRING {$$.c = abc_pushstring(0, $1);
1456 CONSTANT : KW_TRUE {$$.c = abc_pushtrue(0);
1457 $$.t = TYPE_BOOLEAN;
1459 CONSTANT : KW_FALSE {$$.c = abc_pushfalse(0);
1460 $$.t = TYPE_BOOLEAN;
1462 CONSTANT : KW_NULL {$$.c = abc_pushnull(0);
1466 USE_NAMESPACE : "use" "namespace" T_IDENTIFIER
1469 //VARIABLE : T_IDENTIFIER
1470 //VARIABLE : VARIABLE '.' T_IDENTIFIER
1471 //VARIABLE : VARIABLE ".." T_IDENTIFIER // descendants
1472 //VARIABLE : VARIABLE "::" VARIABLE // namespace declaration
1473 //VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
1474 //VARIABLE : VARIABLE '[' EXPRESSION ']' // unqualified expression
1476 GETSET : "get" {$$=$1;}
1478 | {$$=empty_token();}
1480 MAYBE_PARAM_LIST: {$$=list_new();}
1481 MAYBE_PARAM_LIST: PARAM_LIST {$$=$1;}
1482 PARAM_LIST: PARAM_LIST ',' PARAM {$$ =$1; list_append($$, $3);}
1483 PARAM_LIST: PARAM {$$ = list_new();list_append($$, $1);}
1484 PARAM: T_IDENTIFIER ':' TYPE {$$ = malloc(sizeof(param_t));
1485 $$->name=$1->text;$$->type = $3;}
1486 PARAM: T_IDENTIFIER {$$ = malloc(sizeof(param_t));
1487 $$->name=$1->text;$$->type = TYPE_ANY;}
1489 IDECLARATION : VARIABLE_DECLARATION
1490 IDECLARATION : FUNCTION_DECLARATION
1492 //IDENTIFIER_LIST : T_IDENTIFIER ',' IDENTIFIER_LIST {extend($3,$1);$$=$3;}
1493 //IDENTIFIER_LIST : T_IDENTIFIER {$$=empty_token();extend($$,$1);}
1495 QNAME_LIST : QNAME {$$=list_new();list_append($$, $1);}
1496 QNAME_LIST : QNAME_LIST ',' QNAME {$$=$1;list_append($$,$3);}
1499 MAYBE_IDECLARATION_LIST :
1500 MAYBE_IDECLARATION_LIST : IDECLARATION_LIST
1501 IDECLARATION_LIST : IDECLARATION
1502 IDECLARATION_LIST : IDECLARATION_LIST FUNCTION_HEADER
1505 // 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
1506 // syntactic keywords: each get set namespace include dynamic final native override static