#include "abc.h"
#include "pool.h"
#include "files.h"
+#include "common.h"
#include "tokenizer.h"
#include "registry.h"
#include "code.h"
#include "opcodes.h"
#include "compiler.h"
+#include "expr.h"
+#include "initcode.h"
extern int a3_lex();
regexp_t regexp;
modifiers_t flags;
namespace_decl_t* namespace_decl;
+ node_t*node;
struct {
abc_exception_list_t *l;
code_t*finally;
%token<token> T_EMPTY
%token<number_int> T_INT
%token<number_uint> T_UINT
-%token<number_uint> T_BYTE
-%token<number_uint> T_SHORT
%token<number_float> T_FLOAT
%token<id> T_FOR "for"
%token<token> KW_FUNCTION "function"
%token<token> KW_FINALLY "finally"
%token<token> KW_UNDEFINED "undefined"
+%token<token> KW_NAN "NaN"
%token<token> KW_CONTINUE "continue"
%token<token> KW_CLASS "class"
%token<token> KW_CONST "const"
%token<token> T_DIVBY "/="
%token<token> T_MODBY "%="
%token<token> T_MULBY "*="
+%token<token> T_ANDBY "&="
%token<token> T_PLUSBY "+="
%token<token> T_MINUSBY "-="
%token<token> T_XORBY "^="
%token<token> T_USHR ">>>"
%token<token> T_SHR ">>"
+%type <number_int> CONDITIONAL_COMPILATION
%type <for_start> FOR_START
%type <id> X_IDENTIFIER PACKAGE FOR_IN_INIT MAYBE_IDENTIFIER
%type <namespace_decl> NAMESPACE_ID
%type <code> INTERFACE_DECLARATION
%type <code> VOIDEXPRESSION
%type <value> EXPRESSION NONCOMMAEXPRESSION
-%type <value> MAYBEEXPRESSION
-%type <value> E DELETE
-%type <value> CONSTANT
+%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 <flags> MAYBE_MODIFIERS
%type <flags> MODIFIER_LIST
%type <flags> MODIFIER
-%type <constant> STATICCONSTANT MAYBESTATICCONSTANT
+%type <constant> CONSTANT MAYBECONSTANT
%type <classinfo_list> IMPLEMENTS_LIST
%type <classinfo> EXTENDS CLASS_SPEC
%type <classinfo_list> EXTENDS_LIST
%type <classinfo> TYPE
//%type <token> VARIABLE
-%type <value> VAR_READ
+%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
// needed for "return" precedence:
%nonassoc T_STRING T_REGEXP
-%nonassoc T_INT T_UINT T_BYTE T_SHORT T_FLOAT
+%nonassoc T_INT T_UINT T_FLOAT KW_NAN
%nonassoc "false" "true" "null" "undefined" "super" "function"
%left above_function
return 0; //make gcc happy
}
+static void parsererror(const char*file, int line, const char*f)
+{
+ syntaxerror("internal error in %s, %s:%d", f, file, line);
+}
+
+#define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
+
static char* concat2(const char* t1, const char* t2)
{
typedef struct _import {
char*package;
} import_t;
-
DECLARE_LIST(import);
DECLARE(methodstate);
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;\
static namespace_list_t nl1 = {&ns1,&nl2};
static namespace_set_t nopackage_namespace_set = {&nl1};
+static dict_t*definitions=0;
+void as3_set_define(const char*c)
+{
+ if(!definitions)
+ definitions = dict_new();
+ if(!dict_contains(definitions,c))
+ dict_put(definitions,c,0);
+}
+
static void new_state()
{
NEW(state_t, s);
if(oldstate)
state->active_namespace_urls = list_clone(oldstate->active_namespace_urls);
}
-static void state_has_imports()
-{
- state->wildcard_imports = list_clone(state->wildcard_imports);
- state->imports = dict_clone(state->imports);
- state->has_own_imports = 1;
-}
-static void import_toplevel(const char*package)
-{
- char* s = strdup(package);
- while(1) {
- dict_put(state->import_toplevel_packages, s, 0);
- char*x = strrchr(s, '.');
- if(!x)
- break;
- *x = 0;
- }
- free(s);
-}
static void state_destroy(state_t*state)
{
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
state->method->late_binding = 1; // init scripts use getglobalscope, so we need a getlocal0/pushscope
} else {
state->method = dict_lookup(global->token2info, (void*)(ptroff_t)as3_tokencount);
+ if(!state->method)
+ syntaxerror("internal error: skewed tokencount");
function_initvars(state->method, 0, 0, 1);
+ global->classinit = abc_initscript(global->file);
global->init = abc_initscript(global->file);
}
}
if(!state || state->level!=1) {
syntaxerror("unexpected end of file in pass %d", as3_pass);
}
-
+
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;
void* finish_parser()
{
dict_free_all(global->file2token2info, 1, (void*)dict_destroy);
-
global->token2info=0;
-
- return global->file;
-}
-
-
-static void xx_scopetest()
-{
- /* findpropstrict doesn't just return a scope object- it
- also makes it "active" somehow. Push local_0 on the
- scope stack and read it back with findpropstrict, it'll
- contain properties like "trace". Trying to find the same
- property on a "vanilla" local_0 yields only a "undefined" */
- //c = abc_findpropstrict(c, "[package]::trace");
- /*c = abc_getlocal_0(c);
- c = abc_findpropstrict(c, "[package]::trace");
- c = abc_coerce_a(c);
- c = abc_setlocal_1(c);
+ initcode_add_classlist(global->classinit, global->classes);
- c = abc_pushbyte(c, 0);
- c = abc_setlocal_2(c);
-
- code_t*xx = c = abc_label(c);
- c = abc_findpropstrict(c, "[package]::trace");
- c = abc_pushstring(c, "prop:");
- c = abc_hasnext2(c, 1, 2);
- c = abc_dup(c);
- c = abc_setlocal_3(c);
- c = abc_callpropvoid(c, "[package]::trace", 2);
- c = abc_getlocal_3(c);
- c = abc_kill(c, 3);
- c = abc_iftrue(c,xx);*/
+ return global->file;
}
typedef struct _variable {
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)
{
syntaxerror("undefined variable: %s", name);
return v;
}
+
static char variable_exists(char*name)
{
return dict_contains(state->vars, name);
}
-code_t*defaultvalue(code_t*c, classinfo_t*type);
+
+static code_t*defaultvalue(code_t*c, classinfo_t*type)
+{
+ if(TYPE_IS_INT(type)) {
+ c = abc_pushbyte(c, 0);
+ } else if(TYPE_IS_UINT(type)) {
+ c = abc_pushuint(c, 0);
+ } else if(TYPE_IS_FLOAT(type)) {
+ c = abc_pushnan(c);
+ } else if(TYPE_IS_BOOLEAN(type)) {
+ c = abc_pushfalse(c);
+ } else if(!type) {
+ //c = abc_pushundefined(c);
+ syntaxerror("internal error: can't generate default value for * type");
+ } else {
+ c = abc_pushnull(c);
+ MULTINAME(m, type);
+ c = abc_coerce2(c, &m);
+ }
+ return c;
+}
static int alloc_local()
{
v->type = type;
v->init = init;
- if(name)
+ if(name) {
dict_put(state->vars, name, v);
+ dict_put(state->allvars, name, v);
+ }
return v;
}
}
#define TEMPVARNAME "__as3_temp__"
-static int gettempvar()
+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;
}
-code_t* var_block(code_t*body)
+static code_t* var_block(code_t*body)
{
code_t*c = 0;
code_t*k = 0;
return c;
}
-void unknown_variable(char*name)
+static void unknown_variable(char*name)
{
if(!state->method->unresolved_variables)
state->method->unresolved_variables = dict_new();
dict_put(state->method->unresolved_variables, name, 0);
}
-#define parserassert(b) {if(!(b)) parsererror(__FILE__, __LINE__,__func__);}
-
-static void parsererror(const char*file, int line, const char*f)
-{
- syntaxerror("internal error in %s, %s:%d", f, file, line);
-}
-
-
static code_t* add_scope_code(code_t*c, methodstate_t*m, char init)
{
if(m->uses_slots || (m->late_binding && !m->inner)) { //???? especially inner functions need the pushscope
return c;
}
-
static void startpackage(char*name)
{
new_state();
- /*printf("entering package \"%s\"\n", name);*/
state->package = strdup(name);
}
static void endpackage()
{
- /*printf("leaving package \"%s\"\n", state->package);*/
-
//used e.g. in classinfo_register:
//free(state->package);state->package=0;
-
old_state();
}
return registry_findmember_nsset(cls, state->active_namespace_urls, name, recurse);
}
-void add_active_url(const char*url)
-{
- NEW(namespace_t,n);
- n->name = url;
- list_append(state->active_namespace_urls, n);
-}
-
static void function_initvars(methodstate_t*m, params_t*params, int flags, char var0)
{
if(var0) {
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);
}
v->is_parameter = 1;
}
}
- if(m->uses_slots) {
- dict_dump(m->slots, stdout, "");
- }
methodstate_list_t*l = m->innerfunctions;
while(l) {
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);
+ 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);
}
}
-static int slotstate_varconst = 0;
-static modifiers_t*slotstate_flags = 0;
-static void setslotstate(modifiers_t* flags, int varconst)
-{
- slotstate_varconst = varconst;
- slotstate_flags = flags;
- if(state->cls) {
- if(flags && flags->flags&FLAG_STATIC) {
- state->method = state->cls->static_init;
- } else {
- state->method = state->cls->init;
- }
- } else {
- parserassert(state->method);
- }
-}
-
static void endclass()
{
if(as3_pass == 2) {
char*name = string_cstr(c->data[0]);
syntaxerror("Unresolved \"continue %s\"", name);
}
+ if(c->opcode == OPCODE___RETHROW__) {
+ syntaxerror("Unresolved \"rethrow\"");
+ }
+ if(c->opcode == OPCODE___FALLTHROUGH__) {
+ syntaxerror("Unresolved \"fallthrough\"");
+ }
if(c->opcode == OPCODE___PUSHPACKAGE__) {
char*name = string_cstr(c->data[0]);
syntaxerror("Can't reference a package (%s) as such", name);
}
}
-
static void check_constant_against_type(classinfo_t*t, constant_t*c)
{
- return;
#define xassert(b) if(!(b)) syntaxerror("Invalid default value %s for type '%s'", constant_tostring(c), t->name)
if(TYPE_IS_NUMBER(t)) {
xassert(c->type == CONSTANT_FLOAT
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);
if(m) {
- printf("%s.%s | %s.%s\n",
- m->package, m->name,
- ns.name, name);
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 = return_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;
return 0;
}
-char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
-{
- return 1; // FIXME
-}
-
void breakjumpsto(code_t*c, char*name, code_t*jump)
{
while(c) {
}
}
-#define IS_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)))
#define IS_NUMBER_OR_INT(a) (TYPE_IS_INT((a)) || TYPE_IS_UINT((a)) || TYPE_IS_NUMBER((a)))
-#define BOTH_INT(a,b) (IS_INT(a) && IS_INT(b))
-classinfo_t*join_types(classinfo_t*type1, classinfo_t*type2, char op)
-{
- if(!type1 || !type2)
- return registry_getanytype();
- if(TYPE_IS_ANY(type1) || TYPE_IS_ANY(type2))
- return registry_getanytype();
-
- if(op=='+') {
- if(IS_NUMBER_OR_INT(type1) && IS_NUMBER_OR_INT(type2)) {
- return TYPE_NUMBER;
- } else {
- return TYPE_ANY;
- }
- }
-
- if(type1 == type2)
- return type1;
- return registry_getanytype();
-}
code_t*converttype(code_t*c, classinfo_t*from, classinfo_t*to)
{
if(from==to)
if((TYPE_IS_NUMBER(from) || TYPE_IS_UINT(from) || TYPE_IS_INT(from)) &&
(TYPE_IS_NUMBER(to) || TYPE_IS_UINT(to) || TYPE_IS_INT(to))) {
// allow conversion between number types
+ if(TYPE_IS_UINT(to))
+ return abc_convert_u(c);
+ else if(TYPE_IS_INT(to))
+ return abc_convert_i(c);
+ else if(TYPE_IS_NUMBER(to))
+ return abc_convert_d(c);
return abc_coerce2(c, &m);
}
- //printf("%s.%s\n", from.package, from.name);
- //printf("%s.%s\n", to.package, to.name);
+
+ if(TYPE_IS_BOOLEAN(to))
+ return abc_convert_b(c);
+ if(TYPE_IS_STRING(to))
+ return abc_convert_s(c);
+ if(TYPE_IS_OBJECT(to))
+ return abc_convert_o(c);
classinfo_t*supertype = from;
while(supertype) {
return c;
as3_error("can't convert type %s%s%s to %s%s%s",
- from->package, from->package?".":"", from->name,
- to->package, to->package?".":"", to->name);
- return c;
-}
+ from->package, from->package[0]?".":"", from->name,
+ to->package, to->package[0]?".":"", to->name);
-code_t*defaultvalue(code_t*c, classinfo_t*type)
-{
- if(TYPE_IS_INT(type)) {
- c = abc_pushbyte(c, 0);
- } else if(TYPE_IS_UINT(type)) {
- c = abc_pushuint(c, 0);
- } else if(TYPE_IS_FLOAT(type)) {
- c = abc_pushnan(c);
- } else if(TYPE_IS_BOOLEAN(type)) {
- c = abc_pushfalse(c);
- } else if(!type) {
- //c = abc_pushundefined(c);
- } else {
- c = abc_pushnull(c);
- MULTINAME(m, type);
- c = abc_coerce2(c, &m);
- }
return c;
}
+/* move to ast.c todo end */
char is_pushundefined(code_t*c)
{
return x;
}
-static char is_getlocal(code_t*c)
-{
- if(!c || c->prev || c->next)
- return 0;
- return(c->opcode == OPCODE_GETLOCAL
- || c->opcode == OPCODE_GETLOCAL_0
- || c->opcode == OPCODE_GETLOCAL_1
- || c->opcode == OPCODE_GETLOCAL_2
- || c->opcode == OPCODE_GETLOCAL_3);
-}
-static int getlocalnr(code_t*c)
-{
- if(c->opcode == OPCODE_GETLOCAL) {return (ptroff_t)c->data[0];}
- else if(c->opcode == OPCODE_GETLOCAL_0) {return 0;}
- else if(c->opcode == OPCODE_GETLOCAL_1) {return 1;}
- else if(c->opcode == OPCODE_GETLOCAL_2) {return 2;}
- else if(c->opcode == OPCODE_GETLOCAL_3) {return 3;}
- else syntaxerror("Internal error: opcode %02x is not a getlocal call", c->opcode);
- return 0;
-}
-
-static code_t* toreadwrite(code_t*in, code_t*middlepart, char justassign, char readbefore)
-{
- /* converts this:
-
- [prefix code] [read instruction]
-
- to this:
-
- [prefix code] ([dup]) [read instruction] [middlepart] [setvar] [write instruction] [getvar]
- */
- if(in && in->opcode == OPCODE_COERCE_A) {
- in = code_cutlast(in);
- }
- if(in->next)
- syntaxerror("internal error");
-
- /* chop off read instruction */
- code_t*prefix = in;
- code_t*r = in;
- if(r->prev) {
- prefix = r->prev;r->prev = 0;
- prefix->next=0;
- } else {
- prefix = 0;
- }
-
- char use_temp_var = readbefore;
-
- /* generate the write instruction, and maybe append a dup to the prefix code */
- code_t* write = abc_nop(0);
- if(r->opcode == OPCODE_GETPROPERTY) {
- write->opcode = OPCODE_SETPROPERTY;
- multiname_t*m = (multiname_t*)r->data[0];
- write->data[0] = multiname_clone(m);
- if(m->type == QNAME || m->type == MULTINAME) {
- if(!justassign) {
- prefix = abc_dup(prefix); // we need the object, too
- }
- use_temp_var = 1;
- } else if(m->type == MULTINAMEL) {
- if(!justassign) {
- /* dupping two values on the stack requires 5 operations and one register-
- couldn't adobe just have given us a dup2? */
- int temp = gettempvar();
- prefix = abc_setlocal(prefix, temp);
- prefix = abc_dup(prefix);
- prefix = abc_getlocal(prefix, temp);
- prefix = abc_swap(prefix);
- prefix = abc_getlocal(prefix, temp);
- if(!use_temp_var);
- prefix = abc_kill(prefix, temp);
- }
- use_temp_var = 1;
- } else {
- syntaxerror("illegal lvalue: can't assign a value to this expression (not a qname/multiname)");
- }
- } else if(r->opcode == OPCODE_GETSLOT) {
- write->opcode = OPCODE_SETSLOT;
- write->data[0] = r->data[0];
- if(!justassign) {
- prefix = abc_dup(prefix); // we need the object, too
- }
- use_temp_var = 1;
- } else if(r->opcode == OPCODE_GETLOCAL) {
- write->opcode = OPCODE_SETLOCAL;
- write->data[0] = r->data[0];
- } else if(r->opcode == OPCODE_GETLOCAL_0) {
- write->opcode = OPCODE_SETLOCAL_0;
- } else if(r->opcode == OPCODE_GETLOCAL_1) {
- write->opcode = OPCODE_SETLOCAL_1;
- } else if(r->opcode == OPCODE_GETLOCAL_2) {
- write->opcode = OPCODE_SETLOCAL_2;
- } else if(r->opcode == OPCODE_GETLOCAL_3) {
- write->opcode = OPCODE_SETLOCAL_3;
- } else if(r->opcode == OPCODE_GETSUPER) {
- write->opcode = OPCODE_SETSUPER;
- multiname_t*m = (multiname_t*)r->data[0];
- write->data[0] = multiname_clone(m);
- } else {
- code_dump(r);
- syntaxerror("illegal lvalue: can't assign a value to this expression");
- }
- code_t* c = 0;
-
- int temp = -1;
- if(!justassign) {
- if(use_temp_var) {
- /* with getproperty/getslot, we have to be extra careful not
- to execute the read code twice, as it might have side-effects
- (e.g. if the property is in fact a setter/getter combination)
-
- So read the value, modify it, and write it again,
- using prefix only once and making sure (by using a temporary
- register) that the return value is what we just wrote */
- temp = gettempvar();
- c = code_append(c, prefix);
- c = code_append(c, r);
- if(readbefore) {
- c = abc_dup(c);
- c = abc_setlocal(c, temp);
- }
- c = code_append(c, middlepart);
- if(!readbefore) {
- c = abc_dup(c);
- c = abc_setlocal(c, temp);
- }
- c = code_append(c, write);
- c = abc_getlocal(c, temp);
- c = abc_kill(c, temp);
- } else {
- /* if we're allowed to execute the read code twice *and*
- the middlepart doesn't modify the code, things are easier.
- */
- code_t* r2 = code_dup(r);
- //c = code_append(c, prefix);
- parserassert(!prefix);
- c = code_append(c, r);
- c = code_append(c, middlepart);
- c = code_append(c, write);
- c = code_append(c, r2);
- }
- } else {
- /* even smaller version: overwrite the value without reading
- it out first */
- if(!use_temp_var) {
- if(prefix) {
- c = code_append(c, prefix);
- c = abc_dup(c);
- }
- c = code_append(c, middlepart);
- c = code_append(c, write);
- c = code_append(c, r);
- } else {
- code_free(r);r=0;
- temp = gettempvar();
- if(prefix) {
- c = code_append(c, prefix);
- }
- c = code_append(c, middlepart);
- c = abc_dup(c);
- c = abc_setlocal(c, temp);
- c = code_append(c, write);
- c = abc_getlocal(c, temp);
- c = abc_kill(c, temp);
- }
- }
- return c;
-}
char is_break_or_jump(code_t*c)
{
#define PASS1 }} if(as3_pass == 1) {{
#define PASS1END }} if(as3_pass == 2) {{
#define PASS2 }} if(as3_pass == 2) {{
-#define PASS12 }} {{
+#define PASS12 }} if(as3_pass == 1 || as3_pass == 2) {{
#define PASS12END }} if(as3_pass == 2) {{
+#define PASS_ALWAYS }} {{
%}
| FUNCTION_DECLARATION
| SLOT_DECLARATION
| PACKAGE_INITCODE
- | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' // conditional compilation
+ | CONDITIONAL_COMPILATION '{' MAYBE_PROGRAM_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
| ';'
MAYBE_INPACKAGE_CODE_LIST: | INPACKAGE_CODE_LIST
| FUNCTION_DECLARATION
| SLOT_DECLARATION
| PACKAGE_INITCODE
- | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' // conditional compilation
+ | CONDITIONAL_COMPILATION '{' MAYBE_INPACKAGE_CODE_LIST '}' {PASS_ALWAYS as3_pass=$1;}
| ';'
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 '}' {$$=$3;}
+CODEPIECE: CONDITIONAL_COMPILATION '{' CODE '}' {
+ PASS_ALWAYS
+ if(as3_pass) {
+ $$ = $3;
+ } else {
+ $$ = 0;
+ }
+ as3_pass=$1;
+}
//CODEBLOCK : '{' CODE '}' {$$=$2;}
//CODEBLOCK : '{' '}' {$$=0;}
/* ------------ conditional compilation ------------- */
-CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER
+CONDITIONAL_COMPILATION: T_IDENTIFIER "::" T_IDENTIFIER {
+ PASS12
+ $$=as3_pass;
+ char*key = concat3($1,"::",$3);
+ if(!definitions || !dict_contains(definitions, key)) {
+ as3_pass=0;
+ }
+ free(key);
+}
/* ------------ variables --------------------------- */
-MAYBEEXPRESSION : '=' NONCOMMAEXPRESSION {$$=$2;}
- | {$$.c=abc_pushundefined(0);
- $$.t=TYPE_ANY;
- }
+%code {
+ char is_subtype_of(classinfo_t*type, classinfo_t*supertype)
+ {
+ return 1; // FIXME
+ }
+};
+
+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;
CASE: "case" E ':' MAYBECODE {
$$ = abc_getlocal(0, state->switch_var);
- $$ = code_append($$, $2.c);
+ $$ = code_append($$, node_read($2).c);
code_t*j = $$ = abc_ifne($$, 0);
$$ = code_append($$, $4);
if($$->opcode != OPCODE___BREAK__) {
$$ = $3;
}
SWITCH : T_SWITCH '(' {PASS12 new_state();state->switch_var=alloc_local();} E ')' '{' MAYBE_CASE_LIST '}' {
- $$=$4.c;
+ $$ = node_read($4).c;
$$ = abc_setlocal($$, state->switch_var);
$$ = code_append($$, $7);
PACKAGE_DECLARATION : "package" '{' {PASS12 startpackage("");}
MAYBE_INPACKAGE_CODE_LIST '}' {PASS12 endpackage();$$=0;}
+%code {
+ static void state_has_imports()
+ {
+ state->wildcard_imports = list_clone(state->wildcard_imports);
+ state->imports = dict_clone(state->imports);
+ state->has_own_imports = 1;
+ }
+ static void import_toplevel(const char*package)
+ {
+ char* s = strdup(package);
+ while(1) {
+ dict_put(state->import_toplevel_packages, s, 0);
+ char*x = strrchr(s, '.');
+ if(!x)
+ break;
+ *x = 0;
+ }
+ free(s);
+ }
+};
IMPORT : "import" PACKAGEANDCLASS {
PASS12
slotinfo_t*s = registry_find($2->package, $2->name);
CLASS_BODY : CLASS_BODY_ITEM
CLASS_BODY : CLASS_BODY CLASS_BODY_ITEM
CLASS_BODY_ITEM : ';'
-CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}'
+CLASS_BODY_ITEM : CONDITIONAL_COMPILATION '{' MAYBE_CLASS_BODY '}' {PASS_ALWAYS as3_pass=$1;}
CLASS_BODY_ITEM : SLOT_DECLARATION
CLASS_BODY_ITEM : FUNCTION_DECLARATION
/* ------------ classes and interfaces (body, slots ) ------- */
+%code {
+ static int slotstate_varconst = 0;
+ static modifiers_t*slotstate_flags = 0;
+ static void setslotstate(modifiers_t* flags, int varconst)
+ {
+ slotstate_varconst = varconst;
+ slotstate_flags = flags;
+ if(state->cls) {
+ if(flags && flags->flags&FLAG_STATIC) {
+ state->method = state->cls->static_init;
+ } else {
+ state->method = state->cls->init;
+ }
+ } else {
+ parserassert(state->method);
+ }
+ }
+};
+
VARCONST: "var" | "const"
-SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {setslotstate(&$1,$2);} SLOT_LIST {$$=$4;setslotstate(0, 0);}
+SLOT_DECLARATION: MAYBE_MODIFIERS VARCONST {PASS12 setslotstate(&$1,$2);} SLOT_LIST {PASS12 $$=$4;setslotstate(0, 0);}
-SLOT_LIST: ONE_SLOT {$$ = $1;}
-SLOT_LIST: SLOT_LIST ',' ONE_SLOT {$$ = code_append($1, $3);}
+SLOT_LIST: ONE_SLOT {PASS12 $$=0;}
+SLOT_LIST: SLOT_LIST ',' ONE_SLOT {PASS12 $$=0;}
ONE_SLOT: T_IDENTIFIER MAYBETYPE MAYBEEXPRESSION
{
+PASS12
int flags = slotstate_flags->flags;
namespace_t ns = modifiers2access(slotstate_flags);
- varinfo_t* info = 0;
- if(state->cls) {
- memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
- if(i) {
- check_override(i, flags);
- }
- info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
- } else {
- slotinfo_t*i = registry_find(state->package, $1);
- if(i) {
- syntaxerror("package %s already contains '%s'", state->package, $1);
- }
- if(ns.name && ns.name[0]) {
- syntaxerror("namespaces not allowed on package-level variables");
- }
- info = varinfo_register_global(ns.access, state->package, $1);
- }
+ if(as3_pass == 1) {
- info->type = $2;
- info->flags = flags;
+ varinfo_t* info = 0;
+ if(state->cls) {
+ memberinfo_t*i = registry_findmember(state->cls->info, ns.name, $1, 1);
+ if(i) {
+ check_override(i, flags);
+ }
+ info = varinfo_register_onclass(state->cls->info, ns.access, ns.name, $1);
+ } else {
+ slotinfo_t*i = registry_find(state->package, $1);
+ if(i) {
+ syntaxerror("package %s already contains '%s'", state->package, $1);
+ }
+ if(ns.name && ns.name[0]) {
+ syntaxerror("namespaces not allowed on package-level variables");
+ }
+ info = varinfo_register_global(ns.access, state->package, $1);
+ }
- /* slot name */
- multiname_t mname = {QNAME, &ns, 0, $1};
-
- trait_list_t**traits;
- 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;
- 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);
- }
- 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);
+ info->type = $2;
+ info->flags = flags;
+
+ dict_put(global->token2info, (void*)(ptroff_t)as3_tokencount, info);
}
- *code = code_append(*code, c);
+ 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;
+ 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;
+ 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);
+ }
+ info->slot = t->slot_id;
+
+ 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);
+ c = abc_setslot(c, t->slot_id);
+ }
+ *code = code_append(*code, c);
+ }
- if(slotstate_varconst==KW_CONST) {
- t->kind= TRAIT_CONST;
+ if(slotstate_varconst==KW_CONST) {
+ t->kind= TRAIT_CONST;
+ info->flags |= FLAG_CONST;
+ }
}
$$=0;
/* ------------ constants -------------------------------------- */
-MAYBESTATICCONSTANT: {$$=0;}
-MAYBESTATICCONSTANT: '=' STATICCONSTANT {$$=$2;}
-
-STATICCONSTANT : T_BYTE {$$ = constant_new_int($1);}
-STATICCONSTANT : T_INT {$$ = constant_new_int($1);}
-STATICCONSTANT : T_UINT {$$ = constant_new_uint($1);}
-STATICCONSTANT : T_FLOAT {$$ = constant_new_float($1);}
-STATICCONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
-//STATICCONSTANT : T_NAMESPACE {$$ = constant_new_namespace($1);}
-STATICCONSTANT : "true" {$$ = constant_new_true($1);}
-STATICCONSTANT : "false" {$$ = constant_new_false($1);}
-STATICCONSTANT : "null" {$$ = constant_new_null($1);}
-STATICCONSTANT : T_IDENTIFIER {
+MAYBECONSTANT: {$$=0;}
+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);}
+CONSTANT : T_UINT {
+ $$ = constant_new_uint($1);
+}
+CONSTANT : T_FLOAT {$$ = constant_new_float($1);}
+CONSTANT : T_STRING {$$ = constant_new_string2($1.str,$1.len);free((char*)$1.str);}
+CONSTANT : "true" {$$ = constant_new_true($1);}
+CONSTANT : "false" {$$ = constant_new_false($1);}
+CONSTANT : "null" {$$ = constant_new_null($1);}
+CONSTANT : "undefined" {$$ = constant_new_undefined($1);}
+CONSTANT : KW_NAN {$$ = constant_new_float(__builtin_nan(""));}
+
+/*
+CONSTANT : T_IDENTIFIER {
if(!strcmp($1, "NaN")) {
$$ = constant_new_float(__builtin_nan(""));
} else {
as3_warning("Couldn't evaluate constant value of %s", $1);
$$ = constant_new_null($1);
}
-}
+}*/
/* ------------ classes and interfaces (body, functions) ------- */
list_append($$.list, $1);
}
-PARAM: T_IDENTIFIER ':' TYPE MAYBESTATICCONSTANT {
+PARAM: T_IDENTIFIER ':' TYPE MAYBECONSTANT {
PASS12
$$ = rfx_calloc(sizeof(param_t));
$$->name=$1;
PASS2
$$->value = $4;
}
-PARAM: T_IDENTIFIER MAYBESTATICCONSTANT {
+PARAM: T_IDENTIFIER MAYBECONSTANT {
PASS12
$$ = rfx_calloc(sizeof(param_t));
$$->name=$1;
c->nsset = get_current_imports();
/* make the compiler look for this class in the current directory,
just in case: */
- //as3_schedule_class_noerror(state->package, $1);
+ as3_schedule_class_noerror(state->package, $1);
}
$$ = (classinfo_t*)c;
PASS2
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_ANY;}
/*
| "String" {$$=registry_getstringclass();}
| "int" {$$=registry_getintclass();}
XX : %prec new2
NEW : "new" E XX MAYBE_PARAM_VALUES {
- $$.c = $2.c;
+ typedcode_t v = node_read($2);
+ $$.c = v.c;
if($$.c->opcode == OPCODE_COERCE_A) $$.c = code_cutlast($$.c);
code_t*paramcode = $4.cc;
}
$$.t = TYPE_ANY;
- if(TYPE_IS_CLASS($2.t) && $2.t->data) {
- $$.t = $2.t->data;
+ if(TYPE_IS_CLASS(v.t) && v.t->data) {
+ $$.t = v.t->data;
} else {
$$.c = abc_coerce_a($$.c);
$$.t = TYPE_ANY;
call (for closures)
*/
FUNCTIONCALL : E '(' MAYBE_EXPRESSION_LIST ')' {
-
- $$.c = $1.c;
+
+ typedcode_t v = node_read($1);
+ $$.c = v.c;
if($$.c->opcode == OPCODE_COERCE_A) {
$$.c = code_cutlast($$.c);
}
$$.c = abc_call($$.c, $3.number);
}
- if(TYPE_IS_FUNCTION($1.t) && $1.t->data) {
- $$.t = ((methodinfo_t*)($1.t->data))->return_type;
+ 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;
}
DELETE: "delete" E {
- $$.c = $2.c;
+ typedcode_t v = node_read($2);
+ $$.c = v.c;
if($$.c->opcode == OPCODE_COERCE_A) {
$$.c = code_cutlast($$.c);
}
$$.c = abc_deleteproperty2($$.c, name);
} else {
$$.c = abc_getlocal_0($$.c);
- MULTINAME_LATE(m, $2.t?$2.t->access:ACCESS_PACKAGE, "");
+ MULTINAME_LATE(m, v.t?v.t->access:ACCESS_PACKAGE, "");
$$.c = abc_deleteproperty2($$.c, &m);
}
$$.t = TYPE_BOOLEAN;
// ----------------------- expression types -------------------------------------
-NONCOMMAEXPRESSION : E %prec below_minus {$$=$1;}
-EXPRESSION : E %prec below_minus {$$ = $1;}
-EXPRESSION : EXPRESSION ',' E %prec below_minus {
- $$.c = $1.c;
- $$.c = cut_last_push($$.c);
- $$.c = code_append($$.c,$3.c);
- $$.t = $3.t;
+NONCOMMAEXPRESSION : E %prec below_minus {
+ $$ = node_read($1);
}
-VOIDEXPRESSION : EXPRESSION %prec below_minus {
- $$=cut_last_push($1.c);
+EXPRESSION : COMMA_EXPRESSION {
+ $$ = node_read($1);
}
-
-// ----------------------- expression evaluation -------------------------------------
-
-E : INNERFUNCTION %prec prec_none {$$ = $1;}
-//V : CONSTANT {$$ = 0;}
-E : CONSTANT
-//V : VAR_READ %prec T_IDENTIFIER {$$ = 0;}
-E : VAR_READ %prec T_IDENTIFIER {$$ = $1;}
-//V : NEW {$$ = $1.c;}
-E : NEW {$$ = $1;}
-//V : DELETE {$$ = $1.c;}
-E : DELETE {$$ = $1;}
-
-E : FUNCTIONCALL
-
-E : T_REGEXP {
- $$.c = 0;
- namespace_t ns = {ACCESS_PACKAGE, ""};
- multiname_t m = {QNAME, &ns, 0, "RegExp"};
- if(!$1.options) {
- $$.c = abc_getlex2($$.c, &m);
- $$.c = abc_pushstring($$.c, $1.pattern);
- $$.c = abc_construct($$.c, 1);
- } else {
- $$.c = abc_getlex2($$.c, &m);
- $$.c = abc_pushstring($$.c, $1.pattern);
- $$.c = abc_pushstring($$.c, $1.options);
- $$.c = abc_construct($$.c, 2);
- }
- $$.t = TYPE_REGEXP;
-}
-
-CONSTANT : T_BYTE {$$.c = abc_pushbyte(0, $1);
- //MULTINAME(m, registry_getintclass());
- //$$.c = abc_coerce2($$.c, &m); // FIXME
- $$.t = TYPE_INT;
- }
-CONSTANT : T_SHORT {$$.c = abc_pushshort(0, $1);
- $$.t = TYPE_INT;
- }
-CONSTANT : T_INT {$$.c = abc_pushint(0, $1);
- $$.t = TYPE_INT;
- }
-CONSTANT : T_UINT {$$.c = abc_pushuint(0, $1);
- $$.t = TYPE_UINT;
- }
-CONSTANT : T_FLOAT {$$.c = abc_pushdouble(0, $1);
- $$.t = TYPE_FLOAT;
- }
-CONSTANT : T_STRING {$$.c = abc_pushstring2(0, &$1);free((char*)$1.str);
- $$.t = TYPE_STRING;
- }
-CONSTANT : "undefined" {$$.c = abc_pushundefined(0);
- $$.t = TYPE_ANY;
- }
-CONSTANT : "true" {$$.c = abc_pushtrue(0);
- $$.t = TYPE_BOOLEAN;
- }
-CONSTANT : "false" {$$.c = abc_pushfalse(0);
- $$.t = TYPE_BOOLEAN;
- }
-CONSTANT : "null" {$$.c = abc_pushnull(0);
- $$.t = TYPE_NULL;
- }
-
-E : E '<' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);$$.c=abc_not($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-E : E '>' E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-E : E "<=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterthan($$.c);$$.c=abc_not($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-E : E ">=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_greaterequals($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-E : E "==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-E : E "===" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-E : E "!==" E {$$.c = code_append($1.c,$3.c);$$.c = abc_strictequals($$.c);$$.c = abc_not($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-E : E "!=" E {$$.c = code_append($1.c,$3.c);$$.c = abc_equals($$.c);$$.c = abc_not($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-
-E : E "||" E {$$.t = join_types($1.t, $3.t, 'O');
- $$.c = $1.c;
- $$.c = converttype($$.c, $1.t, $$.t);
- $$.c = abc_dup($$.c);
- code_t*jmp = $$.c = abc_iftrue($$.c, 0);
- $$.c = cut_last_push($$.c);
- $$.c = code_append($$.c,$3.c);
- $$.c = converttype($$.c, $3.t, $$.t);
- code_t*label = $$.c = abc_label($$.c);
- jmp->branch = label;
- }
-E : E "&&" E {
- $$.t = join_types($1.t, $3.t, 'A');
- /*printf("%08x:\n",$1.t);
- code_dump($1.c, 0, 0, "", stdout);
- printf("%08x:\n",$3.t);
- code_dump($3.c, 0, 0, "", stdout);
- printf("joining %08x and %08x to %08x\n", $1.t, $3.t, $$.t);*/
- $$.c = $1.c;
- $$.c = converttype($$.c, $1.t, $$.t);
- $$.c = abc_dup($$.c);
- code_t*jmp = $$.c = abc_iffalse($$.c, 0);
- $$.c = cut_last_push($$.c);
- $$.c = code_append($$.c,$3.c);
- $$.c = converttype($$.c, $3.t, $$.t);
- code_t*label = $$.c = abc_label($$.c);
- jmp->branch = label;
- }
-
-E : '!' E {$$.c=$2.c;
- $$.c = abc_not($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-
-E : '~' E {$$.c=$2.c;
- $$.c = abc_bitnot($$.c);
- $$.t = TYPE_INT;
- }
-
-E : E '&' E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_bitand($$.c);
- $$.t = TYPE_INT;
- }
-
-E : E '^' E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_bitxor($$.c);
- $$.t = TYPE_INT;
- }
-
-E : E '|' E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_bitor($$.c);
- $$.t = TYPE_INT;
- }
-
-E : E ">>" E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_rshift($$.c);
- $$.t = TYPE_INT;
- }
-E : E ">>>" E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_urshift($$.c);
- $$.t = TYPE_INT;
- }
-E : E "<<" E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_lshift($$.c);
- $$.t = TYPE_INT;
- }
-
-E : E '/' E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_divide($$.c);
- $$.t = TYPE_NUMBER;
- }
-E : E '%' E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_modulo($$.c);
- $$.t = TYPE_NUMBER;
- }
-E : E '+' E {$$.c = code_append($1.c,$3.c);
- if(BOTH_INT($1.t, $3.t)) {
- $$.c = abc_add_i($$.c);
- $$.t = TYPE_INT;
- } else {
- $$.c = abc_add($$.c);
- $$.t = join_types($1.t,$3.t,'+');
- }
- }
-E : E '-' E {$$.c = code_append($1.c,$3.c);
- if(BOTH_INT($1.t,$3.t)) {
- $$.c = abc_subtract_i($$.c);
- $$.t = TYPE_INT;
- } else {
- $$.c = abc_subtract($$.c);
- $$.t = TYPE_NUMBER;
- }
- }
-E : E '*' E {$$.c = code_append($1.c,$3.c);
- if(BOTH_INT($1.t,$3.t)) {
- $$.c = abc_multiply_i($$.c);
- $$.t = TYPE_INT;
- } else {
- $$.c = abc_multiply($$.c);
- $$.t = TYPE_NUMBER;
- }
- }
-
-E : E "in" E {$$.c = code_append($1.c,$3.c);
- $$.c = abc_in($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-
-E : E "as" E {char use_astype=0; // flash player's astype works differently than astypelate
- if(use_astype && TYPE_IS_CLASS($3.t) && $3.t->data) {
- MULTINAME(m, (classinfo_t*)($3.t->data));
- $$.c = abc_astype2($1.c, &m);
- $$.t = $3.t->data;
- } else {
- $$.c = code_append($1.c, $3.c);
- $$.c = abc_astypelate($$.c);
- $$.t = TYPE_ANY;
- }
- }
-
-E : E "instanceof" E
- {$$.c = code_append($1.c, $3.c);
- $$.c = abc_instanceof($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-
-E : E "is" E {$$.c = code_append($1.c, $3.c);
- $$.c = abc_istypelate($$.c);
- $$.t = TYPE_BOOLEAN;
- }
-
-E : "typeof" '(' E ')' {
- $$.c = $3.c;
- $$.c = abc_typeof($$.c);
- $$.t = TYPE_STRING;
- }
-
-E : "void" E {
- $$.c = cut_last_push($2.c);
- $$.c = abc_pushundefined($$.c);
- $$.t = TYPE_ANY;
- }
-
-E : "void" { $$.c = abc_pushundefined(0);
- $$.t = TYPE_ANY;
- }
-
-E : '(' EXPRESSION ')' {$$=$2;} //allow commas in here, too
-
-E : '-' E {
- $$=$2;
- if(IS_INT($2.t)) {
- $$.c=abc_negate_i($$.c);
- $$.t = TYPE_INT;
- } else {
- $$.c=abc_negate($$.c);
- $$.t = TYPE_NUMBER;
- }
+COMMA_EXPRESSION : E %prec below_minus {
+ $$ = mkmultinode(&node_comma, $1);
}
-
-E : E '[' E ']' {
- $$.c = $1.c;
- $$.c = code_append($$.c, $3.c);
-
- MULTINAME_LATE(m, $1.t?$1.t->access:ACCESS_PACKAGE, "");
- $$.c = abc_getproperty2($$.c, &m);
- $$.t = 0; // array elements have unknown type
+COMMA_EXPRESSION : COMMA_EXPRESSION ',' E %prec below_minus {
+ $$ = multinode_extend($1, $3);
}
-
-E : '[' MAYBE_EXPRESSION_LIST ']' {
- $$.c = code_new();
- $$.c = code_append($$.c, $2.cc);
- $$.c = abc_newarray($$.c, $2.number);
- $$.t = registry_getarrayclass();
+VOIDEXPRESSION : E %prec below_minus {
+ $$ = node_exec($1);
}
+VOIDEXPRESSION : VOIDEXPRESSION ',' E %prec below_minus {
+ $$ = $1;
+ $$ = code_append($$, node_exec($3));
+}
+
+MAYBE_DICT_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
+MAYBE_DICT_EXPRPAIR_LIST : DICT_EXPRPAIR_LIST {$$=$1;}
-MAYBE_EXPRPAIR_LIST : {$$.cc=0;$$.number=0;}
-MAYBE_EXPRPAIR_LIST : EXPRPAIR_LIST {$$=$1;}
+DICTLH: T_IDENTIFIER {$$=abc_pushstring(0,$1);}
+DICTLH: T_STRING {$$=abc_pushstring2(0,&$1);}
-EXPRPAIR_LIST : NONCOMMAEXPRESSION ':' NONCOMMAEXPRESSION {
+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);
}
-//MAYBECOMMA: ','
-//MAYBECOMMA:
-E : "{ (dictionary)" MAYBE_EXPRPAIR_LIST '}' {
- $$.c = code_new();
- $$.c = code_append($$.c, $2.cc);
- $$.c = abc_newobject($$.c, $2.number/2);
- $$.t = registry_getobjectclass();
-}
-
-E : E "*=" E {
- code_t*c = $3.c;
- if(BOTH_INT($1.t,$3.t)) {
- c=abc_multiply_i(c);
- } else {
- c=abc_multiply(c);
- }
- c=converttype(c, join_types($1.t, $3.t, '*'), $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-
-E : E "%=" E {
- code_t*c = abc_modulo($3.c);
- c=converttype(c, join_types($1.t, $3.t, '%'), $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E "<<=" E {
- code_t*c = abc_lshift($3.c);
- c=converttype(c, join_types($1.t, $3.t, '<'), $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E ">>=" E {
- code_t*c = abc_rshift($3.c);
- c=converttype(c, join_types($1.t, $3.t, '>'), $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E ">>>=" E {
- code_t*c = abc_urshift($3.c);
- c=converttype(c, join_types($1.t, $3.t, 'U'), $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E "/=" E {
- code_t*c = abc_divide($3.c);
- c=converttype(c, join_types($1.t, $3.t, '/'), $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E "|=" E {
- code_t*c = abc_bitor($3.c);
- c=converttype(c, TYPE_INT, $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E "^=" E {
- code_t*c = abc_bitxor($3.c);
- c=converttype(c, TYPE_INT, $1.t);
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E "+=" E {
- code_t*c = $3.c;
-
- if(TYPE_IS_INT($1.t)) {
- c=abc_add_i(c);
- } else {
- c=abc_add(c);
- c=converttype(c, join_types($1.t, $3.t, '+'), $1.t);
- }
-
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E "-=" E { code_t*c = $3.c;
- if(TYPE_IS_INT($1.t)) {
- c=abc_subtract_i(c);
- } else {
- c=abc_subtract(c);
- c=converttype(c, join_types($1.t, $3.t, '-'), $1.t);
- }
-
- $$.c = toreadwrite($1.c, c, 0, 0);
- $$.t = $1.t;
- }
-E : E '=' E { code_t*c = 0;
- c = code_append(c, $3.c);
- c = converttype(c, $3.t, $1.t);
- $$.c = toreadwrite($1.c, c, 1, 0);
- $$.t = $1.t;
- }
+// ----------------------- expression evaluation -------------------------------------
-E : E '?' E ':' E %prec below_assignment {
- $$.t = join_types($3.t,$5.t,'?');
- $$.c = $1.c;
- code_t*j1 = $$.c = abc_iffalse($$.c, 0);
- $$.c = code_append($$.c, $3.c);
- $$.c = converttype($$.c, $3.t, $$.t);
- code_t*j2 = $$.c = abc_jump($$.c, 0);
- $$.c = j1->branch = abc_label($$.c);
- $$.c = code_append($$.c, $5.c);
- $$.c = converttype($$.c, $5.t, $$.t);
- $$.c = j2->branch = abc_label($$.c);
- }
+E : INNERFUNCTION %prec prec_none {$$ = 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 : E "++" { code_t*c = 0;
- classinfo_t*type = $1.t;
- if(is_getlocal($1.c) && (TYPE_IS_INT($1.t) || TYPE_IS_NUMBER($1.t))) {
- int nr = getlocalnr($1.c);
- code_free($1.c);$1.c=0;
- if(TYPE_IS_INT($1.t)) {
- $$.c = abc_getlocal(0, nr);
- $$.c = abc_inclocal_i($$.c, nr);
- } else if(TYPE_IS_NUMBER($1.t)) {
- $$.c = abc_getlocal(0, nr);
- $$.c = abc_inclocal($$.c, nr);
- } else syntaxerror("internal error");
- } else {
- if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
- c=abc_increment_i(c);
- type = TYPE_INT;
- } else {
- c=abc_increment(c);
- type = TYPE_NUMBER;
- }
- c=converttype(c, type, $1.t);
- $$.c = toreadwrite($1.c, c, 0, 1);
- $$.t = $1.t;
- }
- }
-
-// TODO: use inclocal, like with ++
-E : E "--" { code_t*c = 0;
- classinfo_t*type = $1.t;
- if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
- c=abc_decrement_i(c);
- type = TYPE_INT;
- } else {
- c=abc_decrement(c);
- type = TYPE_NUMBER;
- }
- c=converttype(c, type, $1.t);
- $$.c = toreadwrite($1.c, c, 0, 1);
- $$.t = $1.t;
- }
+E : CONSTANT {
+ $$ = mkconstnode($1);
+}
-E : "++" %prec plusplus_prefix E { code_t*c = 0;
- classinfo_t*type = $2.t;
- if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
- c=abc_increment_i(c);
- type = TYPE_INT;
- } else {
- c=abc_increment(c);
- type = TYPE_NUMBER;
- }
- c=converttype(c, type, $2.t);
- $$.c = toreadwrite($2.c, c, 0, 0);
- $$.t = $2.t;
- }
+/* regexp */
+E : T_REGEXP {
+ typedcode_t v;
+ v.c = 0;
+ namespace_t ns = {ACCESS_PACKAGE, ""};
+ multiname_t m = {QNAME, &ns, 0, "RegExp"};
+ if(!$1.options) {
+ v.c = abc_getlex2(v.c, &m);
+ v.c = abc_pushstring(v.c, $1.pattern);
+ v.c = abc_construct(v.c, 1);
+ } else {
+ v.c = abc_getlex2(v.c, &m);
+ v.c = abc_pushstring(v.c, $1.pattern);
+ v.c = abc_pushstring(v.c, $1.options);
+ v.c = abc_construct(v.c, 2);
+ }
+ v.t = TYPE_REGEXP;
+ $$ = mkcodenode(v);
+}
-E : "--" %prec minusminus_prefix E { code_t*c = 0;
- classinfo_t*type = $2.t;
- if(TYPE_IS_INT(type) || TYPE_IS_UINT(type)) {
- c=abc_decrement_i(c);
- type = TYPE_INT;
- } else {
- c=abc_decrement(c);
- type = TYPE_NUMBER;
- }
- c=converttype(c, type, $2.t);
- $$.c = toreadwrite($2.c, c, 0, 0);
- $$.t = $2.t;
- }
+/* array */
+E : '[' MAYBE_EXPRESSION_LIST ']' {
+ typedcode_t v;
+ v.c = code_new();
+ v.c = code_append(v.c, $2.cc);
+ v.c = abc_newarray(v.c, $2.number);
+ v.t = registry_getarrayclass();
+ $$ = mkcodenode(v);
+}
+
+/* dictionary */
+E : "{ (dictionary)" MAYBE_DICT_EXPRPAIR_LIST '}' {
+ typedcode_t v;
+ v.c = code_new();
+ v.c = code_append(v.c, $2.cc);
+ v.c = abc_newobject(v.c, $2.number/2);
+ v.t = registry_getobjectclass();
+ $$ = mkcodenode(v);
+}
+
+E : E '<' E {$$ = mknode2(&node_lt,$1,$3);}
+E : E '>' E {$$ = mknode2(&node_gt,$1,$3);}
+E : E "<=" E {$$ = mknode2(&node_le,$1,$3);}
+E : E ">=" E {$$ = mknode2(&node_ge,$1,$3);}
+E : E "==" E {$$ = mknode2(&node_eqeq,$1,$3);}
+E : E "===" E {$$ = mknode2(&node_eqeqeq,$1,$3);}
+E : E "!==" E {$$ = mknode2(&node_noteqeq,$1,$3);}
+E : E "!=" E {$$ = mknode2(&node_noteq,$1,$3);}
+E : E "||" E {$$ = mknode2(&node_oror,$1,$3);}
+E : E "&&" E {$$ = mknode2(&node_andand,$1,$3);}
+E : '!' E {$$ = mknode1(&node_not, $2);}
+E : '~' E {$$ = mknode1(&node_bitnot, $2);}
+E : E '&' E {$$ = mknode2(&node_bitand, $1, $3);}
+E : E '^' E {$$ = mknode2(&node_bitxor, $1, $3);}
+E : E '|' E {$$ = mknode2(&node_bitor, $1, $3);}
+E : E ">>" E {$$ = mknode2(&node_shr, $1, $3);}
+E : E ">>>" E {$$ = mknode2(&node_ushr, $1, $3);}
+E : E "<<" E {$$ = mknode2(&node_shl, $1, $3);}
+E : E '/' E {$$ = mknode2(&node_div, $1, $3);}
+E : E '%' E {$$ = mknode2(&node_mod, $1, $3);}
+E : E '+' E {$$ = mknode2(&node_plus, $1, $3);}
+E : E '-' E {$$ = mknode2(&node_minus, $1, $3);}
+E : E '*' E {$$ = mknode2(&node_multiply, $1, $3);}
+E : E "in" E {$$ = mknode2(&node_in, $1, $3);}
+E : E "as" E {$$ = mknode2(&node_as, $1, $3);}
+E : E "instanceof" E {$$ = mknode2(&node_instanceof, $1, $3);}
+E : E "is" E {$$ = mknode2(&node_is, $1, $3);}
+E : "typeof" '(' E ')' {$$ = mknode1(&node_typeof, $3);}
+E : "void" E {$$ = mknode1(&node_void, $2);}
+E : "void" { $$ = mkconstnode(constant_new_undefined());}
+E : '(' COMMA_EXPRESSION ')' { $$=$2;}
+E : '-' E {$$ = mknode1(&node_neg, $2);}
+E : E '[' E ']' {$$ = mknode2(&node_arraylookup, $1,$3);}
+E : E "*=" E {$$ = mknode2(&node_muleq, $1, $3);}
+E : E "%=" E {$$ = mknode2(&node_modeq, $1, $3);}
+E : E "<<=" E {$$ = mknode2(&node_shleq, $1, $3);}
+E : E ">>=" E {$$ = mknode2(&node_shreq, $1, $3);}
+E : E ">>>=" E {$$ = mknode2(&node_ushreq, $1, $3);}
+E : E "/=" E { $$ = mknode2(&node_diveq, $1, $3);}
+E : E "|=" E { $$ = mknode2(&node_bitoreq, $1, $3);}
+E : E "^=" E { $$ = mknode2(&node_bitxoreq, $1, $3);}
+E : E "&=" E { $$ = mknode2(&node_bitandeq, $1, $3);}
+E : E "+=" E { $$ = mknode2(&node_pluseq, $1, $3);}
+E : E "-=" E { $$ = mknode2(&node_minuseq, $1, $3);}
+E : E '=' E { $$ = mknode2(&node_assign, $1, $3);}
+E : E '?' E ':' E %prec below_assignment { $$ = mknode3(&node_tenary, $1, $3, $5);}
+
+E : E "++" { $$ = mknode1(&node_rplusplus, $1);}
+E : E "--" { $$ = mknode1(&node_rminusminus, $1);}
+E : "++" %prec plusplus_prefix E {$$ = mknode1(&node_lplusplus, $2); }
+E : "--" %prec minusminus_prefix E {$$ = mknode1(&node_lminusminus, $2); }
E : "super" '.' T_IDENTIFIER
{ if(!state->cls->info)
syntaxerror("super keyword not allowed outside a class");
classinfo_t*t = state->cls->info->superclass;
if(!t) t = TYPE_OBJECT;
-
memberinfo_t*f = findmember_nsset(t, $3, 1);
-
MEMBER_MULTINAME(m, f, $3);
- $$.c = 0;
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getsuper2($$.c, &m);
- $$.t = slotinfo_gettype((slotinfo_t*)f);
+ typedcode_t v;
+ v.c = 0;
+ v.c = abc_getlocal_0(v.c);
+ v.c = abc_getsuper2(v.c, &m);
+ v.t = slotinfo_gettype((slotinfo_t*)f);
+ $$ = mkcodenode(v);
}
E : '@' T_IDENTIFIER {
// attribute TODO
- $$.c = abc_pushundefined(0);
- $$.t = 0;
+ $$ = mkdummynode();
as3_warning("ignored @ operator");
}
E : E '.' '@' T_IDENTIFIER {
// child attribute TODO
- $$.c = abc_pushundefined(0);
- $$.t = 0;
+ $$ = mkdummynode();
as3_warning("ignored .@ operator");
}
E : E '.' T_IDENTIFIER "::" T_IDENTIFIER {
// namespace declaration TODO
- $$.c = abc_pushundefined(0);
- $$.t = 0;
+ $$ = mkdummynode();
as3_warning("ignored :: operator");
}
E : E ".." T_IDENTIFIER {
// descendants TODO
- $$.c = abc_pushundefined(0);
- $$.t = 0;
+ $$ = mkdummynode();
as3_warning("ignored .. operator");
}
E : E '.' '(' E ')' {
// filter TODO
- $$.c = abc_pushundefined(0);
- $$.t = 0;
+ $$ = mkdummynode();
as3_warning("ignored .() operator");
}
-//VARIABLE : VARIABLE "::" '[' EXPRESSION ']' // qualified expression
-
-
-
-E : E '.' T_IDENTIFIER {
- $$.c = $1.c;
- classinfo_t*t = $1.t;
+//E : E "::" '[' E ']' {
+// // qualified expression TODO
+// $$.c = abc_pushundefined(0);
+// $$.t = 0;
+// as3_warning("ignored ::[] operator");
+// }
+
+MEMBER : E '.' T_IDENTIFIER {
+ typedcode_t v1 = node_read($1);
+ $$.c = v1.c;
+ classinfo_t*t = v1.t;
char is_static = 0;
if(TYPE_IS_CLASS(t) && t->data) {
t = t->data;
if(f && f->slot && !noslot) {
$$.c = abc_getslot($$.c, f->slot);
} else {
+ if(!f) {
+ 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($1.c && $1.c->opcode == OPCODE___PUSHPACKAGE__) {
- string_t*package = $1.c->data[0];
+
+ } else if(v1.c && v1.c->opcode == OPCODE___PUSHPACKAGE__) {
+ string_t*package = v1.c->data[0];
char*package2 = concat3(package->str, ".", $3);
slotinfo_t*a = registry_find(package->str, $3);
$$ = push_class(a);
} else if(dict_contains(state->import_toplevel_packages, package2) ||
registry_ispackage(package2)) {
- $$.c = $1.c;
+ $$.c = v1.c;
$$.c->data[0] = string_new4(package2);
$$.t = 0;
} else {
/* when resolving a property on an unknown type, we do know the
name of the property (and don't seem to need the package), but
we need to make avm2 try out all access modes */
+ as3_warning("Resolving %s on unknown type", $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;
}
}
/* 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);
+ as3_schedule_class_noerror(state->package, $1);
PASS2
- $$.t = 0;
- $$.c = 0;
+ typedcode_t o;
+ o.t = 0;
+ o.c = 0;
+ $$ = 0;
+
slotinfo_t*a = 0;
memberinfo_t*f = 0;
/* look at variables */
if((v = find_variable(state, $1))) {
// $1 is a local variable
- $$.c = abc_getlocal($$.c, v->index);
- $$.t = v->type;
+ o.c = abc_getlocal(o.c, v->index);
+ o.t = v->type;
+ $$ = mkcodenode(o);
break;
}
if((v = find_slot(state, $1))) {
- $$.c = abc_getscopeobject($$.c, 1);
- $$.c = abc_getslot($$.c, v->index);
- $$.t = v->type;
+ o.c = abc_getscopeobject(o.c, 1);
+ o.c = abc_getslot(o.c, v->index);
+ o.t = v->type;
+ $$ = mkcodenode(o);
break;
}
/* 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)
+ (f = findmember_nsset(state->cls->info, $1, 1)))
{
- // $1 is a function in this class
+ // $1 is a member or attribute 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, ""};
- 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, ""};
- multiname_t m = {QNAME, &ns, 0, $1};
- $$.c = abc_getlocal_0($$.c);
- $$.c = abc_getproperty2($$.c, &m);
- break;
+ 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) {
+ $$ = mkconstnode(v->value);
+ break;
+ }
+ }
+
+ 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, $1};
+ o.c = abc_findpropstrict2(o.c, &m);
+ o.c = abc_getproperty2(o.c, &m);
+ $$ = mkcodenode(o);
+ break;
+ } else if(f->slot>0) {
+ o.c = abc_getlocal_0(o.c);
+ o.c = abc_getslot(o.c, f->slot);
+ $$ = mkcodenode(o);
+ break;
+ } else {
+ namespace_t ns = {f->access, f->package};
+ multiname_t m = {QNAME, &ns, 0, $1};
+ o.c = abc_getlocal_0(o.c);
+ o.c = abc_getproperty2(o.c, &m);
+ $$ = mkcodenode(o);
+ break;
+ }
}
}
/* look at actual classes, in the current package and imported */
if((a = find_class($1))) {
- $$ = push_class(a);
+ o = push_class(a);
+ $$ = mkcodenode(o);
break;
}
/* look through package prefixes */
if(dict_contains(state->import_toplevel_packages, $1) ||
registry_ispackage($1)) {
- $$.c = abc___pushpackage__($$.c, $1);
- $$.t = 0;
+ o.c = abc___pushpackage__(o.c, $1);
+ o.t = 0;
+ $$ = mkcodenode(o); //?
break;
}
multiname_t m = {MULTINAME, 0, &nopackage_namespace_set, $1};
- $$.t = 0;
- $$.c = abc_findpropstrict2($$.c, &m);
- $$.c = abc_getproperty2($$.c, &m);
+ o.t = 0;
+ o.c = abc_findpropstrict2(o.c, &m);
+ o.c = abc_getproperty2(o.c, &m);
+ $$ = mkcodenode(o);
+ break;
}
}
$$=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;
+
varinfo_t*s = (varinfo_t*)$3;
- if(!s || s->kind != INFOTYPE_SLOT)
+ if(s->kind == INFOTYPE_UNRESOLVED) {
+ s = (varinfo_t*)registry_resolve((slotinfo_t*)s);
+ if(!s)
+ syntaxerror("Couldn't resolve namespace %s", $3->name);
+ }
+
+ 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);