#include "opcodes.h"
#include "compiler.h"
#include "expr.h"
+#include "initcode.h"
extern int a3_lex();
%type <number_int> CONDITIONAL_COMPILATION
%type <for_start> FOR_START
-%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
+%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER ID_OR_NS SUBNODE
%type <namespace_decl> NAMESPACE_ID
%type <token> VARCONST
%type <code> CODE
%type <code> INTERFACE_DECLARATION
%type <code> VOIDEXPRESSION
%type <value> EXPRESSION NONCOMMAEXPRESSION
-%type <value> MAYBEEXPRESSION
+%type <node> MAYBEEXPRESSION
%type <value> DELETE
%type <node> E COMMA_EXPRESSION
+%type <node> VAR_READ
%type <code> FOR FOR_IN IF WHILE DO_WHILE MAYBEELSE BREAK RETURN CONTINUE TRY
%type <value> INNERFUNCTION
%type <code> USE_NAMESPACE
%type <classinfo_list> IMPLEMENTS_LIST
%type <classinfo> EXTENDS CLASS_SPEC
%type <classinfo_list> EXTENDS_LIST
-
%type <classinfo> CLASS PACKAGEANDCLASS
%type <classinfo_list> CLASS_SPEC_LIST
-
+%type <id> XML XML2 XMLNODE XMLATTRIBUTE XMLATTRIBUTES MAYBE_XMLATTRIBUTES XMLTEXT
%type <classinfo> TYPE
//%type <token> VARIABLE
-%type <value> VAR_READ MEMBER
+%type <value> MEMBER
%type <value> NEW
//%type <token> T_IDENTIFIER
%type <value> FUNCTIONCALL
-%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES MAYBE_EXPRPAIR_LIST EXPRPAIR_LIST WITH_HEAD
+%type <value_list> MAYBE_EXPRESSION_LIST EXPRESSION_LIST EXPRESSION_LIST_AND_COMMA MAYBE_PARAM_VALUES
+%type <value_list> MAYBE_DICT_EXPRPAIR_LIST DICT_EXPRPAIR_LIST WITH_HEAD
+%type <code> DICTLH
// precedence: from low to high
%nonassoc '&'
%nonassoc "==" "!=" "===" "!=="
%nonassoc "is" "as" "in"
+%left below_lt
%nonassoc "<=" '<' ">=" '>' "instanceof" // TODO: support "a < b < c" syntax?
%left "<<" ">>" ">>>"
%left below_minus
int switch_var;
dict_t*vars;
+ dict_t*allvars; // also contains variables from sublevels
} state_t;
typedef struct _global {
abc_file_t*file;
- abc_script_t*init;
+
+ parsedclass_list_t*classes;
+ abc_script_t*classinit;
+
+ abc_script_t*init; //package-level code
+
dict_t*token2info;
dict_t*file2token2info;
} global_t;
DECLARE_LIST(state);
-#define MULTINAME(m,x) \
- multiname_t m;\
- namespace_t m##_ns;\
- (x)->package; \
- registry_fill_multiname(&m, &m##_ns, (slotinfo_t*)(x));
-
#define MEMBER_MULTINAME(m,f,n) \
multiname_t m;\
namespace_t m##_ns;\
dict_destroy(state->imports);state->imports=0;
}
if(state->vars) {
- int t;
- for(t=0;t<state->vars->hashsize;t++) {
- dictentry_t*e =state->vars->slots[t];
- while(e) {
- free(e->data);e->data=0;
- e = e->next;
- }
- }
dict_destroy(state->vars);state->vars=0;
}
+ if(state->new_vars && state->allvars) {
+ parserassert(!state->old || state->old->allvars != state->allvars);
+ DICT_ITERATE_DATA(state->allvars, void*, data) {
+ free(data);
+ }
+ dict_destroy(state->allvars);
+ }
list_free(state->active_namespace_urls)
state->active_namespace_urls = 0;
new_state();
state->package = internal_filename_package = strdup(filename);
+ state->allvars = dict_new();
global->token2info = dict_lookup(global->file2token2info,
current_filename // use long version
if(as3_pass==2) {
dict_del(global->file2token2info, current_filename);
-
code_t*header = method_header(state->method);
code_t*c = wrap_function(header, 0, global->init->method->body->code);
- global->init->method->body->code = c;
+ global->init->method->body->code = abc_returnvoid(c);
free(state->method);state->method=0;
}
global->file->flags &= ~ABCFILE_LAZY;
global->file2token2info = dict_new();
global->token2info = 0;
+ global->classinit = abc_initscript(global->file);
}
void* finish_parser()
{
dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
global->token2info=0;
+
+ initcode_add_classlist(global->classinit, global->classes);
+
return global->file;
}
static variable_t* find_variable(state_t*s, char*name)
{
+ state_t*top = s;
while(s) {
variable_t*v = 0;
v = dict_lookup(s->vars, name);
if(s->new_vars) break;
s = s->old;
}
- return 0;
+ return dict_lookup(top->allvars, name);
}
static variable_t* find_slot(state_t*s, const char*name)
{
v->type = type;
v->init = init;
- if(name)
+ if(name) {
dict_put(state->vars, name, v);
+ dict_put(state->allvars, name, v);
+ }
return v;
}
int gettempvar()
{
variable_t*v = find_variable(state, TEMPVARNAME);
+ int i;
if(v)
- return v->index;
- return new_variable(TEMPVARNAME, 0, 0, 0);
+ i = v->index;
+ else
+ i = new_variable(TEMPVARNAME, 0, 0, 0);
+ parserassert(i);
+ return i;
}
static code_t* var_block(code_t*body)
index = new_variable((flags&FLAG_STATIC)?"class":"this", state->cls?state->cls->info:0, 0, 0);
else
index = new_variable("globalscope", 0, 0, 0);
- if(index)
- *(int*)0=0;
parserassert(!index);
}
state->cls = rfx_calloc(sizeof(classstate_t));
state->cls->init = rfx_calloc(sizeof(methodstate_t));
state->cls->static_init = rfx_calloc(sizeof(methodstate_t));
+ state->cls->static_init->variable_count=1;
/* notice: we make no effort to initialize the top variable (local0) here,
even though it has special meaning. We just rely on the facat
that pass 1 won't do anything with variables */
int num_interfaces = (list_length(implements));
state->cls->info = classinfo_register(access, package, classname, num_interfaces);
state->cls->info->flags |= mod->flags & (FLAG_DYNAMIC|FLAG_INTERFACE|FLAG_FINAL);
+ state->cls->info->superclass = extends;
int pos = 0;
classinfo_list_t*l = implements;
pos++;
}
- /* fill out interfaces and extends (we couldn't resolve those during the first pass) */
- state->cls->info->superclass = extends;
-
/* generate the abc code for this class */
MULTINAME(classname2,state->cls->info);
multiname_t*extends2 = sig2mname(extends);
- state->cls->abc = abc_class_new(global->file, &classname2, extends2);
+ /* don't add the class to the class index just yet- that will be done later
+ by initscript */
+ state->cls->abc = abc_class_new(0, &classname2, extends2);
+ state->cls->abc->file = global->file;
+
+ multiname_destroy(extends2);
if(state->cls->info->flags&FLAG_FINAL) abc_class_final(state->cls->abc);
if(!(state->cls->info->flags&FLAG_DYNAMIC)) abc_class_sealed(state->cls->abc);
if(state->cls->info->flags&FLAG_INTERFACE) {
abc_class_add_interface(state->cls->abc, &m);
}
- /* write the construction code for this class to the global init
- function */
- int slotindex = abc_initscript_addClassTrait(global->init, &classname2, state->cls->abc);
-
- abc_method_body_t*m = global->init->method->body;
- __ getglobalscope(m);
- classinfo_t*s = extends;
-
- int count=0;
-
- while(s) {
- //TODO: take a look at the current scope stack, maybe
- // we can re-use something
- s = s->superclass;
- if(!s)
- break;
-
- multiname_t*s2 = sig2mname(s);
- __ getlex2(m, s2);
- multiname_destroy(s2);
-
- __ pushscope(m); count++;
- m->code = m->code->prev->prev; // invert
- }
- /* continue appending after last op end */
- while(m->code && m->code->next) m->code = m->code->next;
-
- /* TODO: if this is one of *our* classes, we can also
- do a getglobalscope/getslot <nr> (which references
- the init function's slots) */
- if(extends2) {
- __ getlex2(m, extends2);
- __ dup(m);
- /* notice: we get a Verify Error #1107 if the top elemnt on the scope
- stack is not the superclass */
- __ pushscope(m);count++;
- } else {
- __ pushnull(m);
- /* notice: we get a verify error #1107 if the top element on the scope
- stack is not the global object */
- __ getlocal_0(m);
- __ pushscope(m);count++;
- }
- __ newclass(m,state->cls->abc);
- while(count--) {
- __ popscope(m);
- }
- __ setslot(m, slotindex);
- multiname_destroy(extends2);
+ NEW(parsedclass_t,p);
+ p->cls = state->cls->info;
+ p->abc = state->cls->abc;
+ list_append(global->classes, p);
/* flash.display.MovieClip handling */
-
if(!as3_globalclass && (mod->flags&FLAG_PUBLIC) && slotinfo_equals((slotinfo_t*)registry_getMovieClip(),(slotinfo_t*)extends)) {
if(state->package && state->package[0]) {
as3_globalclass = concat3(state->package, ".", classname);
if(!state->cls) {
//package method
minfo = methodinfo_register_global(ns.access, state->package, name);
- minfo->return_type = 0; // save this for pass 2
+ minfo->return_type = return_type;
} else if(getset != KW_GET && getset != KW_SET) {
//class method
memberinfo_t* m = registry_findmember(state->cls->info, ns.name, name, 0);
syntaxerror("class already contains a %s '%s'", infotypename((slotinfo_t*)m), m->name);
}
minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
- minfo->return_type = 0; // save this for pass 2
+ minfo->return_type = return_type;
// getslot on a member slot only returns "undefined", so no need
// to actually store these
//state->minfo->slot = state->method->abc->method->trait->slot_id;
// not sure wether to look into superclasses here, too
minfo = (methodinfo_t*)registry_findmember(state->cls->info, ns.name, name, 1);
if(minfo) {
- if(minfo->kind!=INFOTYPE_SLOT)
+ if(minfo->kind!=INFOTYPE_VAR)
syntaxerror("class already contains a method called '%s'", name);
if(!(minfo->subtype & (SUBTYPE_GETSET)))
syntaxerror("class already contains a field called '%s'", name);
}*/
} else {
minfo = methodinfo_register_onclass(state->cls->info, ns.access, ns.name, name);
- minfo->kind = INFOTYPE_SLOT; //hack
+ minfo->kind = INFOTYPE_VAR; //hack
minfo->subtype = gs;
- minfo->return_type = 0;
+ minfo->return_type = type;
}
+
/* can't assign a slot as getter and setter might have different slots */
//minfo->slot = slot;
}
new_state();
state->new_vars = 1;
+ state->allvars = dict_new();
if(as3_pass == 1) {
state->method = rfx_calloc(sizeof(methodstate_t));
}
new_state();
state->new_vars = 1;
-
+ state->allvars = dict_new();
+
if(as3_pass == 1) {
state->method = rfx_calloc(sizeof(methodstate_t));
state->method->has_super = 0;
state->cls->has_constructor |= state->method->is_constructor;
}
- state->method->info->return_type = return_type;
function_initvars(state->method, params, mod->flags, 1);
}
}
}
state->method->uses_slots = i;
dict_destroy(state->vars);state->vars = 0;
+ parserassert(state->new_vars);
+ dict_destroy(state->allvars);state->allvars = 0;
}
old_state();
return 0;
} else if(state->method->is_constructor) {
f = abc_class_getconstructor(state->cls->abc, type2);
} else if(!state->method->is_global) {
- namespace_t mname_ns = modifiers2access(mod);
- multiname_t mname = {QNAME, &mname_ns, 0, name};
+ namespace_t ns = modifiers2access(mod);
+
+ /* deal with protected */
+ if(ns.access == ACCESS_PROTECTED && state->cls)
+ ns.name = state->cls->info->name;
+
+ multiname_t mname = {QNAME, &ns, 0, name};
if(mod->flags&FLAG_STATIC)
f = abc_class_staticmethod(state->cls->abc, type2, &mname);
MAYBECODE: CODE {$$=$1;}
MAYBECODE: {$$=code_new();}
-CODE: CODE CODEPIECE {$$=code_append($1,$2);}
+CODE: CODE CODEPIECE {
+ $$=code_append($1,$2);
+}
CODE: CODEPIECE {$$=$1;}
// code which may appear outside of methods
CODEPIECE: CONTINUE
CODEPIECE: RETURN
CODEPIECE: THROW
-CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {PASS_ALWAYS as3_pass=$1;}
+CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {
+ PASS_ALWAYS
+ if(as3_pass) {
+ $$ = $3;
+ } else {
+ $$ = 0;
+ }
+ as3_pass=$1;
+}
//CODEBLOCK : '{' CODE '}' {$$=$2;}
//CODEBLOCK : '{' '}' {$$=0;}
}
};
-MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
- | {$$.c=abc_pushundefined(0);
- $$.t=TYPE_ANY;
- }
+MAYBEEXPRESSION : '=' E {$$=$2;}
+ | {$$=mkdummynode();}
VARIABLE_DECLARATION : "var" VARIABLE_LIST {$$=$2;}
VARIABLE_DECLARATION : "const" VARIABLE_LIST {$$=$2;}
new_variable($1, 0, 1, 0);
PASS2
- if(!is_subtype_of($3.t, $2)) {
- syntaxerror("Can't convert %s to %s", $3.t->name,
- $2->name);
- }
-
char slot = 0;
int index = 0;
if(state->method->uses_slots) {
$$ = slot?abc_getscopeobject(0, 1):0;
+ typedcode_t v = node_read($3);
+ if(!is_subtype_of(v.t, $2)) {
+ syntaxerror("Can't convert %s to %s", v.t->name, $2->name);
+ }
if($2) {
- if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
- $$ = code_append($$, $3.c);
- $$ = converttype($$, $3.t, $2);
+ if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = code_append($$, v.c);
+ $$ = converttype($$, v.t, $2);
} else {
- code_free($3.c);
+ code_free(v.c);
$$ = defaultvalue($$, $2);
}
} else {
- if($3.c->prev || $3.c->opcode != OPCODE_PUSHUNDEFINED) {
- $$ = code_append($$, $3.c);
+ if(v.c->prev || v.c->opcode != OPCODE_PUSHUNDEFINED) {
+ $$ = code_append($$, v.c);
$$ = abc_coerce_a($$);
} else {
// don't do anything
- code_free($3.c);
+ code_free(v.c);
code_free($$);
$$ = 0;
break;
parserassert(state->method);
}
}
+ static trait_t* add_abc_slot(modifiers_t* modifiers, const char*name, multiname_t*m, code_t***c)
+ {
+ int flags = modifiers->flags;
+ namespace_t ns = modifiers2access(modifiers);
+ /* deal with protected */
+ if(ns.access == ACCESS_PROTECTED && state->cls)
+ ns.name = state->cls->info->name;
+
+ /* slot name */
+ multiname_t mname = {QNAME, &ns, 0, name};
+
+ trait_list_t**traits;
+ code_t**code=0;
+ if(!state->cls) {
+ // global variable
+ ns.name = state->package;
+ traits = &global->init->traits;
+ code = &global->init->method->body->code;
+ } else if(flags&FLAG_STATIC) {
+ // static variable
+ traits = &state->cls->abc->static_traits;
+ code = &state->cls->static_init->header;
+ } else {
+ // instance variable
+ traits = &state->cls->abc->traits;
+ code = &state->cls->init->header;
+ }
+ if(c)
+ *c = code;
+ if(m)
+ memcpy(m, &mname, sizeof(multiname_t));
+
+ return trait_new_member(traits, 0, multiname_clone(&mname), 0);
+ }
};
VARCONST: "var" | "const"
if(as3_pass == 2) {
varinfo_t*info = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
- /* slot name */
- multiname_t mname = {QNAME, &ns, 0, $1};
-
- trait_list_t**traits;
+ multiname_t mname;
code_t**code;
- if(!state->cls) {
- // global variable
- ns.name = state->package;
- traits = &global->init->traits;
- code = &global->init->method->body->code;
- } else if(flags&FLAG_STATIC) {
- // static variable
- traits = &state->cls->abc->static_traits;
- code = &state->cls->static_init->header;
- } else {
- // instance variable
- traits = &state->cls->abc->traits;
- code = &state->cls->init->header;
- }
-
- trait_t*t=0;
+ trait_t*t = add_abc_slot(slotstate_flags, $1, &mname, &code);
+
if($2) {
MULTINAME(m, $2);
- t = trait_new_member(traits, multiname_clone(&m), multiname_clone(&mname), 0);
- } else {
- t = trait_new_member(traits, 0, multiname_clone(&mname), 0);
+ t->type_name = multiname_clone(&m);
}
info->slot = t->slot_id;
- /* initalization code (if needed) */
- code_t*c = 0;
- if($3.c && !is_pushundefined($3.c)) {
- c = abc_getlocal_0(c);
- c = code_append(c, $3.c);
- c = converttype(c, $3.t, $2);
- c = abc_setslot(c, t->slot_id);
+ /* workaround for "VerifyError: Error #1053: Illegal override of ::test2 in C1"
+ FIXME: is there a way to use slots and still don't have conflicting overrides?
+ */
+ info->slot = t->slot_id = 0;
+
+ constant_t cval = $3->type->eval($3);
+ if(cval.type!=CONSTANT_UNKNOWN) {
+ /* compile time constant */
+ t->value = malloc(sizeof(constant_t));
+ memcpy(t->value, &cval, sizeof(constant_t));
+ info->value = constant_clone(t->value);
+ } else {
+ typedcode_t v = node_read($3);
+ /* initalization code (if needed) */
+ code_t*c = 0;
+ if(v.c && !is_pushundefined(v.c)) {
+ c = abc_getlocal_0(c);
+ c = code_append(c, v.c);
+ c = converttype(c, v.t, $2);
+ if(!t->slot_id) {
+ c = abc_setproperty2(c, &mname);
+ } else {
+ c = abc_setslot(c, t->slot_id);
+ }
+ }
+ *code = code_append(*code, c);
}
- *code = code_append(*code, c);
-
if(slotstate_varconst==KW_CONST) {
t->kind= TRAIT_CONST;
+ info->flags |= FLAG_CONST;
}
}
/* ------------ constants -------------------------------------- */
MAYBECONSTANT: {$$=0;}
-MAYBECONSTANT: '=' CONSTANT {$$=$2;}
+MAYBECONSTANT: '=' E {
+ $$ = malloc(sizeof(constant_t));
+ *$$ = node_eval($2);
+ if($$->type == CONSTANT_UNKNOWN) {
+ syntaxerror("can't evaluate default parameter value (needs to be a compile-time constant)");
+ }
+}
//CONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
CONSTANT : T_INT {$$ = constant_new_int($1);}
}
}*/
+/* ---------------------------xml ------------------------------ */
+
+%code {
+ static int xml_level = 0;
+};
+
+XML: XMLNODE
+
+OPEN : '<' {PASS_ALWAYS tokenizer_begin_xml();xml_level++;}
+CLOSE : '>' {PASS_ALWAYS tokenizer_begin_xmltext();}
+CLOSE2 : {PASS_ALWAYS if(!--xml_level) tokenizer_end_xml(); else tokenizer_begin_xmltext();}
+
+XMLTEXT : {$$="";}
+XMLTEXT : XMLTEXT T_STRING {$$=concat2($1, string_cstr(&$2));}
+XMLTEXT : XMLTEXT '>' {$$=concat2($1, ">");}
+
+XML2 : XMLNODE XMLTEXT {$$=concat2($1,$2);}
+XML2 : XML2 XMLNODE XMLTEXT {$$=concat3($1,$2,$3);free($1);free($2);free($3);}
+
+XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES CLOSE XMLTEXT '<' '/' T_IDENTIFIER CLOSE2 '>' {
+ $$ = allocprintf("<%s%s>%s</%s>", $2, $3, $5, $8);
+ free($2);free($3);free($5);free($8);
+}
+XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES '/' CLOSE2 '>' {
+ $$ = allocprintf("<%s%s/>", $2, $3);
+}
+XMLNODE : OPEN T_IDENTIFIER MAYBE_XMLATTRIBUTES CLOSE XMLTEXT XML2 '<' '/' T_IDENTIFIER CLOSE2 '>' {
+ $$ = allocprintf("<%s%s>%s%s</%s>", $2, $3, $5, $6, $9);
+ free($2);free($3);free($5);free($6);free($6);free($9);
+}
+
+MAYBE_XMLATTRIBUTES: {$$=strdup("");}
+MAYBE_XMLATTRIBUTES: XMLATTRIBUTES {$$=concat2(" ",$1);}
+XMLATTRIBUTES: XMLATTRIBUTE {$$=$1;}
+XMLATTRIBUTES: XMLATTRIBUTES XMLATTRIBUTE {$$=concat3($1," ",$2);free($1);free($2);}
+XMLATTRIBUTE: T_IDENTIFIER '=' T_STRING {
+ char* str = string_cstr(&$3);
+ $$=allocprintf("%s=\"%s\"", $1,str);
+ free(str);
+ free($1);free((char*)$3.str);
+}
+
/* ------------ classes and interfaces (body, functions) ------- */
// non-vararg version
CLASS_SPEC_LIST : CLASS_SPEC_LIST ',' CLASS_SPEC {PASS12 $$=$1;list_append($$,$3);}
TYPE : CLASS_SPEC {PASS12 $$=$1;}
- | '*' {PASS12 $$=registry_getanytype();}
- | "void" {PASS12 $$=registry_getanytype();}
+ | '*' {PASS12 $$=TYPE_ANY;}
+ | "void" {PASS12 $$=TYPE_VOID;}
/*
| "String" {$$=registry_getstringclass();}
| "int" {$$=registry_getintclass();}
if(TYPE_IS_FUNCTION(v.t) && v.t->data) {
$$.t = ((methodinfo_t*)(v.t->data))->return_type;
+ } else if(TYPE_IS_CLASS(v.t) && v.t->data) {
+ // calling a class is like a typecast
+ $$.t = (classinfo_t*)v.t->data;
} else {
$$.c = abc_coerce_a($$.c);
$$.t = TYPE_ANY;
// ----------------------- expression types -------------------------------------
-NONCOMMAEXPRESSION : E %prec below_minus {
+NONCOMMAEXPRESSION : E %prec below_lt {
$$ = node_read($1);
}
EXPRESSION : COMMA_EXPRESSION {
$$ = node_read($1);
}
-COMMA_EXPRESSION : E %prec below_minus {
+COMMA_EXPRESSION : E %prec below_lt {
$$ = mkmultinode(&node_comma, $1);
}
-COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_minus {
+COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_lt {
$$ = multinode_extend($1, $3);
}
VOIDEXPRESSION : E %prec below_minus {
$$ = node_exec($1);
}
-VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_minus {
+VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_lt {
$$ = $1;
$$ = code_append($$, node_exec($3));
}
-MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
-MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
+MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
+MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;}
-EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);}
+DICTLH: T_STRING {$$=abc_pushstring2(0,&$1);}
+
+DICT_EXPRPAIR_LIST : DICTLH ':' NONCOMMAEXPRESSION {
$$.cc = 0;
- $$.cc = code_append($$.cc, $1.c);
+ $$.cc = code_append($$.cc, $1);
$$.cc = code_append($$.cc, $3.c);
$$.number = 2;
}
-EXPRPAIR_LIST : EXPRPAIR_LIST ',' NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST ',' DICTLH ':' NONCOMMAEXPRESSION {
$$.cc = $1.cc;
$$.number = $1.number+2;
- $$.cc = code_append($$.cc, $3.c);
+ $$.cc = code_append($$.cc, $3);
$$.cc = code_append($$.cc, $5.c);
}
// ----------------------- expression evaluation -------------------------------------
E : INNERFUNCTION %prec prec_none {$$ = mkcodenode($1);}
-E : VAR_READ %prec T_IDENTIFIER {$$ = mkcodenode($1);}
E : MEMBER %prec '.' {$$ = mkcodenode($1);}
E : NEW {$$ = mkcodenode($1);}
E : DELETE {$$ = mkcodenode($1);}
E : FUNCTIONCALL {$$ = mkcodenode($1);}
+E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
E : CONSTANT {
$$ = mkconstnode($1);
}
+E : XML {
+ typedcode_t v;
+ v.c = 0;
+ namespace_t ns = {ACCESS_PACKAGE, ""};
+ multiname_t m = {QNAME, &ns, 0, "XML"};
+ v.c = abc_getlex2(v.c, &m);
+ v.c = abc_pushstring(v.c, $1);
+ v.c = abc_construct(v.c, 1);
+ v.t = TYPE_XML;
+ $$ = mkcodenode(v);
+}
+
/* regexp */
E : T_REGEXP {
typedcode_t v;
}
/* dictionary */
-E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
+E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
typedcode_t v;
v.c = code_new();
v.c = code_append(v.c, $2.cc);
}
E : '@' T_IDENTIFIER {
- // attribute TODO
- $$ = mkdummynode();
- as3_warning("ignored @ operator");
- }
-
-E : E '.' '@' T_IDENTIFIER {
- // child attribute TODO
- $$ = mkdummynode();
- as3_warning("ignored .@ operator");
- }
+ // attribute occuring in .() loops
+ // TODO
+ $$ = mkdummynode();
+ as3_warning("ignored @ operator");
+}
-E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
- // namespace declaration TODO
- $$ = mkdummynode();
- as3_warning("ignored :: operator");
- }
+E : E '.' '(' E ')' {
+ // filter
+ // TODO: this needs to be implemented using a loop
+ $$ = mkdummynode();
+ as3_warning("ignored .() operator");
+}
-E : E ".." T_IDENTIFIER {
- // descendants TODO
- $$ = mkdummynode();
- as3_warning("ignored .. operator");
- }
+ID_OR_NS : T_IDENTIFIER {$$=$1;}
+ID_OR_NS : T_NAMESPACE {$$=(char*)$1;}
+SUBNODE: T_IDENTIFIER
+ | '*' {$$="*";}
-E : E '.' '(' E ')' {
- // filter TODO
- $$ = mkdummynode();
- as3_warning("ignored .() operator");
- }
+E : E '.' ID_OR_NS "::" SUBNODE {
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read(resolve_identifier($3));
+ v.c = code_append(v.c, w.c);
+ if(!TYPE_IS_NAMESPACE(w.t)) {
+ as3_softwarning("%s might not be a namespace", $3);
+ }
+ v.c = converttype(v.c, w.t, TYPE_NAMESPACE);
+ multiname_t m = {RTQNAME, 0, 0, $5};
+ v.c = abc_getproperty2(v.c, &m);
+ if(TYPE_IS_XML(v.t)) {
+ v.t = TYPE_XMLLIST;
+ } else {
+ v.c = abc_coerce_a(v.c);
+ v.t = TYPE_ANY;
+ }
+ $$ = mkcodenode(v);
+}
+E : E ".." SUBNODE {
+ typedcode_t v = node_read($1);
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+ v.c = abc_getdescendants2(v.c, &m);
+ v.t = TYPE_XMLLIST;
+ $$ = mkcodenode(v);
+}
+E : E '.' '[' E ']' {
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($4);
+ multiname_t m = {MULTINAMEL, 0, &nopackage_namespace_set, 0};
+ v.c = code_append(v.c, w.c);
+ v.c = converttype(w.c, w.t, TYPE_STRING);
+ v.c = abc_getproperty2(v.c, &m);
+ v.t = TYPE_XMLLIST;
+ $$ = mkcodenode(v);
+}
-//E : E "::" '[' E ']' {
-// // qualified expression TODO
-// $$.c = abc_pushundefined(0);
-// $$.t = 0;
-// as3_warning("ignored ::[] operator");
-// }
+E : E '.' '@' SUBNODE {
+ typedcode_t v = node_read($1);
+ multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
+ v.c = abc_getproperty2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
+E : E ".." '@' SUBNODE {
+ typedcode_t v = node_read($1);
+ multiname_t m = {MULTINAMEA, 0, &nopackage_namespace_set, $4};
+ v.c = abc_getdescendants2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
+E : E '.' '@' '[' E ']' {
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($5);
+ multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
+ v.c = code_append(v.c, w.c);
+ v.c = converttype(w.c, w.t, TYPE_STRING);
+ v.c = abc_getproperty2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
+E : E ".." '@' '[' E ']' {
+ typedcode_t v = node_read($1);
+ typedcode_t w = node_read($5);
+ multiname_t m = {MULTINAMELA, 0, &nopackage_namespace_set, 0};
+ v.c = code_append(v.c, w.c);
+ v.c = converttype(w.c, w.t, TYPE_STRING);
+ v.c = abc_getdescendants2(v.c, &m);
+ v.t = TYPE_STRING;
+ $$ = mkcodenode(v);
+}
-MEMBER : E '.' T_IDENTIFIER {
+MEMBER : E '.' SUBNODE {
typedcode_t v1 = node_read($1);
$$.c = v1.c;
classinfo_t*t = v1.t;
t = t->data;
is_static = 1;
}
- if(t) {
+ if(TYPE_IS_XML(t)) {
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
+ $$.c = abc_getproperty2($$.c, &m);
+ $$.c = abc_coerce_a($$.c);
+ $$.t = TYPE_XMLLIST;
+ } else if(t) {
if(t->subtype==INFOTYPE_UNRESOLVED) {
syntaxerror("syntaxerror: trying to resolve property '%s' on incomplete object '%s'", $3, t->name);
}
$$.c = abc_getslot($$.c, f->slot);
} else {
if(!f) {
- as3_warning("Access of undefined property '%s' in %s", $3, t->name);
+ as3_softwarning("Access of undefined property '%s' in %s", $3, t->name);
}
-
MEMBER_MULTINAME(m, f, $3);
$$.c = abc_getproperty2($$.c, &m);
}
$$.t = slotinfo_gettype((slotinfo_t*)f);
if(!$$.t)
$$.c = abc_coerce_a($$.c);
+
} else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) {
string_t*package = v1.c->data[0];
char*package2 = concat3(package->str, ".", $3);
multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $3};
$$.c = abc_getproperty2($$.c, &m);
$$.c = abc_coerce_a($$.c);
- $$.t = registry_getanytype();
+ $$.t = TYPE_ANY;
}
}
+%code {
+ node_t* resolve_identifier(char*name)
+ {
+ typedcode_t o;
+ o.t = 0;
+ o.c = 0;
+
+ slotinfo_t*a = 0;
+ memberinfo_t*f = 0;
+
+ variable_t*v;
+ /* look at variables */
+ if((v = find_variable(state, name))) {
+ // name is a local variable
+ o.c = abc_getlocal(o.c, v->index);
+ o.t = v->type;
+ return mkcodenode(o);
+ }
+ if((v = find_slot(state, name))) {
+ o.c = abc_getscopeobject(o.c, 1);
+ o.c = abc_getslot(o.c, v->index);
+ o.t = v->type;
+ return mkcodenode(o);
+ }
+
+ int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
+
+ /* look at current class' members */
+ if(!state->method->inner &&
+ state->cls &&
+ (f = findmember_nsset(state->cls->info, name, 1)))
+ {
+ // name is a member or attribute in this class
+ int var_is_static = (f->flags&FLAG_STATIC);
+
+ if(f->kind == INFOTYPE_VAR && (f->flags&FLAG_CONST)) {
+ /* if the variable is a constant (and we know what is evaluates to), we
+ can just use the value itself */
+ varinfo_t*v = (varinfo_t*)f;
+ if(v->value) {
+ return mkconstnode(v->value);
+ }
+ }
+
+ if(var_is_static >= i_am_static) {
+ if(f->kind == INFOTYPE_METHOD) {
+ o.t = TYPE_FUNCTION(f);
+ } else {
+ o.t = f->type;
+ }
+
+ if(var_is_static && !i_am_static) {
+ /* access to a static member from a non-static location.
+ do this via findpropstrict:
+ there doesn't seem to be any non-lookup way to access
+ static properties of a class */
+ state->method->late_binding = 1;
+ o.t = f->type;
+ namespace_t ns = {f->access, f->package};
+ multiname_t m = {QNAME, &ns, 0, name};
+ o.c = abc_findpropstrict2(o.c, &m);
+ o.c = abc_getproperty2(o.c, &m);
+ return mkcodenode(o);
+ } else if(f->slot>0) {
+ o.c = abc_getlocal_0(o.c);
+ o.c = abc_getslot(o.c, f->slot);
+ return mkcodenode(o);
+ } else {
+ namespace_t ns = {f->access, f->package};
+ multiname_t m = {QNAME, &ns, 0, name};
+ o.c = abc_getlocal_0(o.c);
+ o.c = abc_getproperty2(o.c, &m);
+ return mkcodenode(o);
+ }
+ }
+ }
+
+ /* look at actual classes, in the current package and imported */
+ if((a = find_class(name))) {
+ o = push_class(a);
+ return mkcodenode(o);
+ }
+
+ /* look through package prefixes */
+ if(dict_contains(state->import_toplevel_packages, name) ||
+ registry_ispackage(name)) {
+ o.c = abc___pushpackage__(o.c, name);
+ o.t = 0;
+ return mkcodenode(o); //?
+ }
+
+ /* unknown object, let the avm2 resolve it */
+ if(1) {
+ //as3_softwarning("Couldn't resolve '%s', doing late binding", name);
+ as3_warning("Couldn't resolve '%s', doing late binding", name);
+ state->method->late_binding = 1;
+
+ multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, name};
+
+ o.t = 0;
+ o.c = abc_findpropstrict2(o.c, &m);
+ o.c = abc_getproperty2(o.c, &m);
+ return mkcodenode(o);
+ }
+ }
+};
+
VAR_READ : T_IDENTIFIER {
PASS1
/* Queue unresolved identifiers for checking against the parent
etc. which is *correct* because local variables of the parent function
would shadow those.
*/
- if(state->method->inner && !find_variable(state, $1)) {
- unknown_variable($1);
+ if(!find_variable(state, $1)) {
+ if(state->method->inner) {
+ unknown_variable($1);
+ }
+ /* let the compiler know that it might want to check the current directory/package
+ for this identifier- maybe there's a file $1.as defining $1. */
+ as3_schedule_class_noerror(state->package, $1);
}
- /* let the compiler know that it might want to check the current directory/package
- for this identifier- maybe there's a file $1.as defining $1. */
- as3_schedule_class_noerror(state->package, $1);
+ $$ = 0;
PASS2
- $$.t = 0;
- $$.c = 0;
- slotinfo_t*a = 0;
- memberinfo_t*f = 0;
-
- variable_t*v;
- /* look at variables */
- if((v = find_variable(state, $1))) {
- // $1 is a local variable
- $$.c = abc_getlocal($$.c, v->index);
- $$.t = v->type;
- break;
- }
- if((v = find_slot(state, $1))) {
- $$.c = abc_getscopeobject($$.c, 1);
- $$.c = abc_getslot($$.c, v->index);
- $$.t = v->type;
- break;
- }
-
- int i_am_static = (state->method && state->method->info)?(state->method->info->flags&FLAG_STATIC):FLAG_STATIC;
-
- /* look at current class' members */
- if(!state->method->inner &&
- state->cls &&
- (f = findmember_nsset(state->cls->info, $1, 1)) &&
- (f->flags&FLAG_STATIC) >= i_am_static)
- {
- // $1 is a function in this class
- int var_is_static = (f->flags&FLAG_STATIC);
-
- if(f->kind == INFOTYPE_METHOD) {
- $$.t = TYPE_FUNCTION(f);
- } else {
- $$.t = f->type;
- }
- if(var_is_static && !i_am_static) {
- /* access to a static member from a non-static location.
- do this via findpropstrict:
- there doesn't seem to be any non-lookup way to access
- static properties of a class */
- state->method->late_binding = 1;
- $$.t = f->type;
- namespace_t ns = {f->access, f->package};
- multiname_t m = {QNAME, &ns, 0, $1};
- $$.c = abc_findpropstrict2($$.c, &m);
- $$.c = abc_getproperty2($$.c, &m);
- break;
- } else if(f->slot>0) {
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getslot($$.c, f->slot);
- break;
- } else {
- namespace_t ns = {f->access, f->package};
- multiname_t m = {QNAME, &ns, 0, $1};
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getproperty2($$.c, &m);
- break;
- }
- }
-
- /* look at actual classes, in the current package and imported */
- if((a = find_class($1))) {
- $$ = push_class(a);
- break;
- }
-
- /* look through package prefixes */
- if(dict_contains(state->import_toplevel_packages, $1) ||
- registry_ispackage($1)) {
- $$.c = abc___pushpackage__($$.c, $1);
- $$.t = 0;
- break;
- }
-
- /* unknown object, let the avm2 resolve it */
- if(1) {
- //as3_softwarning("Couldn't resolve '%s', doing late binding", $1);
- as3_warning("Couldn't resolve '%s', doing late binding", $1);
- state->method->late_binding = 1;
-
- multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
-
- $$.t = 0;
- $$.c = abc_findpropstrict2($$.c, &m);
- $$.c = abc_getproperty2($$.c, &m);
- }
+ $$ = resolve_identifier($1);
}
// ----------------- namespaces -------------------------------------------------
+%code {
+ void add_active_url(const char*url)
+ {
+ NEW(namespace_t,n);
+ n->name = url;
+ list_append(state->active_namespace_urls, n);
+ }
+};
+
NAMESPACE_ID : "namespace" T_IDENTIFIER {
PASS12
NEW(namespace_decl_t,n);
ns.access = ACCESS_NAMESPACE;
ns.name = $2->url;
var->value = constant_new_namespace(&ns);
+
+ if(as3_pass==2) {
+ MULTINAME(m, TYPE_NAMESPACE);
+ trait_t*t = add_abc_slot(&$1, $2->name, 0, 0);
+ t->value = var->value;
+ t->type_name = multiname_clone(&m);
+ }
$$=0;
}
-%code {
- void add_active_url(const char*url)
- {
- NEW(namespace_t,n);
- n->name = url;
- list_append(state->active_namespace_urls, n);
- }
-};
-
USE_NAMESPACE : "use" "namespace" CLASS_SPEC {
PASS12
const char*url = $3->name;
syntaxerror("Couldn't resolve namespace %s", $3->name);
}
- if(!s || s->kind != INFOTYPE_SLOT)
+ if(!s || s->kind != INFOTYPE_VAR)
syntaxerror("%s.%s is not a public namespace (%d)", $3->package, $3->name, s?s->kind:-1);
if(!s->value || !NS_TYPE(s->value->type))
syntaxerror("%s.%s is not a namespace", $3->package, $3->name);