📄 expressions.cpp
字号:
if (is_arr) ed = vector_context_op(ed);
else ed = dynamic_context_op(ed);
if (overloaded && overalloc) ed = add_const_op(t,-sizeof(void *),ed);
ed = append_op(e1,ed);
} else ed = e1;
er = function_call(delete_fn,expr_list(ed,ez));
} else
er = function_call(delete_fn,expr_list(e1,ez));
return er;
}
PExpr construct_op(PClass pc, PExprList pel, bool check_abstract)
{
if (pc->is_abstract() && check_abstract) return expr_error("class contains abstract methods");
// call the constructor, passing the arguments
PEntry pec = pc->get_constructor();
if (pec==NULL) return expr_error("cannot create constructor");
return function_op(entry_op(pec),pel);
}
PExpr init_ref_op(PExpr el, PExpr er)
{
// NOTE(4) we are fed a _reference entry expression_, which is (* e)
return new Expr(INIT_REF,t_void,el->arg1(),er);
}
static PExpr array_init_op(PEntry pe, PExprList pel)
{
ExprList::iterator eli = pel->begin();
PExpr arr_expr, assign_expr;
int mem_unit = (pe->is_stack_relative()) ? sizeof(int) : 1; // stack-relative counts in words...
for(int i = 0; eli != pel->end(); ++eli,++i) {
PEntry aie = new Entry; *aie = *pe; // construct an Array Item Entry
aie->type.decr_pointer(); // using base type of array
aie->size = 1; // (which is not an array!)
aie->data += (i*aie->type.size())/mem_unit;
assign_expr = initialize_op(aie,*eli,NULL,0);
if (i > 0) arr_expr = append_op(arr_expr,assign_expr,true); // i.e normal COMMA op!
else arr_expr = assign_expr;
}
return arr_expr;
}
static PExpr dot_op(PExpr obj, PEntry pe)
{
PExpr ef = entry_op(pe);
return make_op(DOT,ef->type(),obj,ef);
}
static PExpr struct_init_op(PExpr obj, PClass pc, PExprList pel)
{
// *add 1.1.1 Explicit initialization of structs.
// *fix 1.1.2 Handling of array fields & simplification.
EntryList fields;
EntryList::iterator ei;
ExprList::iterator eli;
PExpr init_expr, assign_expr;
pc->list_entries(fields,FIELDS | NON_STATIC);
if (fields.size() != pel->size()) return expr_error("init list wrong size for this struct");
for(ei = fields.begin(), eli = pel->begin(); ei != fields.end(); ++ei, ++eli) {
assign_expr = init_op(entry_op(*ei),Parser::array_size(*ei),*eli,NULL,0);
if (assign_expr->is_nil()) return assign_expr; // couldn't assign to this entry!
if (ei != fields.begin()) init_expr = append_op(init_expr,assign_expr,true);
else init_expr = assign_expr;
}
return make_op(DOT,t_void,obj,init_expr);
}
PExpr init_op(PExpr el, int arr_sz, PExpr er, PExprList pel, int ctype)
{
// initializing an auto object, if necessary calling the constructor
// Only one of er and pel is non-NULL!
Type t = el->type();
bool brace_init = false;
if (er && er->is_expr_list()) {
pel = er->expr_list();
brace_init = er->is_brace_list();
er = NULL;
}
bool is_ref = t.is_plain_reference();
bool is_ref_or_ptr = is_ref && t.is_pointer();
bool is_array = arr_sz > 1;
bool is_non_init_array = is_array && pel == NULL;
// *fix 0.9.4 Now does both initialized and non-initialized arrays of objects.
if (t.is_class() && !is_ref && (is_non_init_array || !t.is_pointer())) {
PClass pc = t.as_class();
if (brace_init) return struct_init_op(el,pc,pel); // initialized struct!
if (t.as_class()->has_constructors()) { // an object or an uninitialized array of objects...
if (er) pel = expr_list(er); // object initialized w/ assign syntax
if (!pel) pel = expr_list(); // i.e, call default constructor!
PExpr ec = construct_op(pc,pel);
if (ec->is_nil()) {
if (pel->size()==0) return expr_error("no default constructor");
return expr_error("cannot find constructor");
}
// either object (initialized or not), or uninitialized array.
if (! is_non_init_array) return construct_context_op(ec, el, ctype);
else return vector_context_op(ec, el);
} else
// *fix 1.2.6 plain simple structs are just initialized with a copy operation
if (er != NULL) return make_op(COPY_BLOCK,t,el,er);
else return NULL; // no operation..
} else { // reference or simple initialization
if (pel) er = pel->front(); // scalar init. w/ object syntax
if (!er) return expr_error("bad init");
if (is_ref) return init_ref_op(el,er); // init. reference
else if (is_array) {
// *add 1.1.2 special case 'char p[] = "hello"'
// *fix 1.1.4 pointer test because 'char *a[] = {"one","two"}' was broken
// *add 1.2.0L special case implemented
if (t.is_char() && t.pointer_depth()==1 && er) {
return function_call("strcpy",expr_list(el,er));
} else
return array_init_op(el->entry(),pel); // initialized array of scalars, or objects.
}
else return assign_op(el,er,false); // simple initialization
}
}
// *change 1.2.9 When compiling in proper 'program mode', then integer
// constant expressions are directly evaluated. If we can't fold the constant,
// then the usual defered evaluation is carried out. We do this to
// support arrays being sized by constants.
PExpr initialize_op(PEntry pe, PExpr er, PExprList pel, int ctype)
{
Type t = pe->type;
if (Parser::debug.compile_program && pe->is_direct()
&& ! t.is_pointer() && t.is_int() && t.is_const()) {
int val;
bool succeeded = true;
try {
val = Parser::const_int_expr(er);
} catch(string msg) {
// it wasn't a simple constant expression; pass through
// force it to be zero, however, so it will cause a clean error if
// if used to declare an array.
val = 0;
succeeded = false;
}
*(int*)(pe->global_ptr()) = val;
if (succeeded) return NULL;
}
return init_op(entry_op(pe), Parser::array_size(pe),er,pel,ctype);
}
PExpr expr_list_op(PExprList pel, bool is_list)
{
PExpr ex = function_op(NULL,pel);
if (! is_list) ex->set_type(t_int);
return ex;
}
void add_to_arg_list(PExpr ec, PExpr arg)
{
PExprList pel = (PExprList) ec->arg2();
pel->push_front(arg);
}
Type base_type(Type t)
{
t.decr_pointer();
return t;
}
PExpr new_op(Type t, PExpr e, PExprList pel)
{
// e represents the (opt.)_number_ of objects, pel the (opt.) _arguments_ to the constructor
// *change 1.1.0 we switch to the overallocating version of new when there's a VMT
// unless the operator was overloaded.
// *fix 1.2.4 Make sure that the base type of t is an object, not itself a pointer.
PClass pc = t.is_class() ? t.as_class() : NULL;
bool overalloc = Parser::debug.do_overalloc || (pc != NULL ? pc->has_true_VMT() : false);
bool is_scalar = e == NULL;
// *NOTE* Do we do new[] yet?
bool overload = Function::lookup(is_scalar ? "new" : "new[]") != NULL;
const char *new_fn;
if (is_scalar) new_fn = overload ? "new" : (overalloc ? "_new_ex" : "_new");
else new_fn = overload ? "new[]" : (overalloc ? "_new_vect_ex" : "_new_vect");
PExpr er, ez = constant_op(t_int,t.size());
t.incr_pointer();
// allocating the memory
if (is_scalar) er = function_call(new_fn,expr_list(ez));
else er = function_call(new_fn,expr_list(e,ez));
// a result of T* needs construction if T::T() exists; but T** etc doesn't!
if (pc && t.pointer_depth() == 1 && pc->has_constructors()) {
if (!pel) pel = expr_list();
PExpr ector = construct_op(pc,pel);
if (ector->is_nil()) return ector;
PExpr ec;
// wrap construction in its object context
if (is_scalar) ec = dynamic_context_op(ector);
else ec = vector_context_op(ector);
// NB to skip first word if we're putting a VMT there!
if (overalloc && overload) er = add_const_op(t,sizeof(void *),er);
add_to_arg_list(ector,er);
er = ec;
//er = append_op(er,ec);
} else if (pel && pel->size() == 1){
// *fix 1.1.0 a plain scalar initialization - make sure it's typecast properly!
er = append_op(er,assign_op(NULL,typecast_op(STATIC_CAST,base_type(t),pel->front())));
}
er->set_type(t); // i.e. make it T *
return er;
}
bool is_object(Type t)
{ return t.is_class() && !t.is_pointer(); } //*NOTE* Type::is_object
PExpr assign_op(PExpr e1, PExpr e2, bool constness)
{
if (!e1) return make_op(ASSIGN,e2->type(),NULL,e2); // see above...only called from there?
Type t = type_of(e1);
// *hack 1.2.8 things like 'const char*' fox UC because it can't understand the
// difference between a pointer which is const, and a pointer to const data.
if (constness && t.is_const() && ! t.is_pointer())
return expr_error("Can't assign to a const type");
// *fix 0.9.6 We didn't check to see if the _RHS_ was an object..
if (is_object(t) || is_object(e2->type())) {
if (is_object(t) && t.as_class()->simple_struct()) return make_op(COPY_BLOCK,t,e1,e2);
else return bin_op(ASSIGN,e1,e2);
} else {
e2 = typecast_op(STATIC_CAST,t,e2);
return make_op(ASSIGN,t,e1,e2);
}
}
PExpr convert_type_op(Type t, PExpr e, bool try_conversions_from = false); // forward
PExpr cast_to_bool_op(PExpr e1)
{
Type t = e1->type();
// it's necessary to strip the reference before is_object() works;
// I'm sure this is going to cause grief elsewhere!
t.strip_reference();
PExpr ce; // *fix 0.9.5 We fell over when an object didn't have a conversion....
if (t.is_object() && (ce = convert_type_op(t_int,e1)) != NULL) return ce;
else return e1;
}
PExpr compound_assign_op(int op, PExpr e1, PExpr e2)
{
if (is_object(e1->type()) || is_object(e2->type()) )
return bin_op(op,e1,e2);
else return assign_op(e1,arith_op(assign_equiv_op[op],e1,e2));
}
PEntry add_temporary(Type t, PExpr e, bool always_construct, bool dont_call_dtor)
{
using namespace Parser;
PEntry pe;
state.check_context(0);
// *fix 0.9.6 Nested temporary contexts require special attention.
Table *context = &state.context();
if (context == temp_context()) context = temp_context()->parent_context();
// *fix 0.9.4 If a temporary is the first object in a nested fn context,
// then we need to force this context to be available.
//temp_context()->reserved_space(t.size());
temp_context()->set_parent(context);
state.push_context(temp_context());
temp_context()->set_no_auto_dtor(dont_call_dtor);
pe = state.add_variable(t,"*",e,always_construct ? TEMP : DEFER_TEMP);
temp_context()->set_no_auto_dtor(false);
state.pop_context();
return pe;
}
// *fix 1.2.8 must guarantee that the temporary is initialized as close
// to the point of use; this expression ensures that ctors for temp objects
// are constructed next to the argument push value.
PExpr construct_temporary_op(Type t, PExpr e, bool dont_call_dtor=false)
{
//* return entry_op(add_temporary(t,e,true,dont_call_dtor));
// create a temporary entry, but don't initialize it.
PExpr ee = entry_op(add_temporary(t,NULL,false,dont_call_dtor));
PExpr ec = init_op(ee,1,e,NULL,1);
// this is a comma expression which evaluates the initialization code first;
// but explicitly use the temporary's type!
PExpr ea;
if (ec) ea = append_op(ec,ee,true);
else ea = ee;
ea->set_type(ee->type());
return ea;
}
PExpr force_addr_op(Type tt, PExpr e); // forward...
// *note* in general this is the _initialization logic_
PExpr return_op(Function *fn, Type rt, PExpr e)
{
if(rt.is_reference()) return force_addr_op(rt,e); // force a reference conversion
else if (rt.is_object()) {
// basically, force a copy construction!
PExpr re = entry_op(fn->return_object());
if (rt.as_class()->has_constructors()) {
PExpr ctor = construct_op(rt.as_class(),expr_list(e));
if (ctor->is_nil()) return ctor;
else return construct_context_op(ctor,re);
} else return assign_op(re,e,false);
} else
return typecast_op(STATIC_CAST,rt,e);
}
// These guys are very similar by the array/pointer equivalence;
// arrays are implemented as a dereference of a pointer addition.
// note again that the result is an implicit reference!
PExpr deref_op(PExpr e1, bool do_overload)
{
Type t = e1->type();
if (t.is_pointer()) {
// try to reduce (* (& x)) to x
if (e1->op()==ADDR) return e1->arg1();
t.decr_pointer();
t.strip_array(); // *fix 0.9.7 damn array flag caused trouble
t.make_reference();
return make_op(DEREF,t,e1);
} else
if (do_overload) return unary_op(DEREF,e1);
else return expr_error("Was not a pointer");
}
PExpr array_op(PExpr e1, PExpr e2)
{
if (e1->type().is_pointer()) {
return deref_op(make_op(ARRAY,e1->type(),e1,e2));
} else
return bin_op(ARRAY,e1,e2);
}
// *fix 0.9.5 Non-reference values can be passed as references via a _reference stub_
// (this copies the value into a temporary static var, and pushes the addr of that)
// *fix 0.9.7 The target type can be of a different size, so we need it explicitly!
PExpr reference_stub_op(Type tt, PExpr e)
{
tt.strip_qualifiers();
PExpr temp = entry_op(Parser::state.add_variable(tt,"*",NULL,Static));
return make_op(REF_STUB,tt,e,temp);
}
PExpr addr_op(PExpr e1, bool do_overload, bool force_reference /* = false */)
{
// tricky one this - not clear how to unambiguously distinguish the ordinary
// old case!
Type t = e1->type();
if (t.is_reference() || e1->is_entry()) { // works also for _literal constants_
if (e1->op()==DEREF) { // we reduce (& (* x)) to x
PExpr e = e1->arg1();
// NOTE(4) a reference x is represented by (* x)
// *fix 1.1.2 This typecast is necessary even if we're not an entry!
if (t.is_reference() /*&& e->is_entry()*/) {
t.incr_pointer(); // so ensure that we do get a pointer
e = bcast_op(t,e); // force a new entry w/ this type
}
return e;
}
// *fix 1.2.1 Ensure that function pointer signatures are unique
if (t.is_function()) t = Type(unique_signature(t.as_signature()));
t.incr_pointer();
return make_op(ADDR,t,e1);
} else
if (do_overload) return unary_op(ADDR,e1);
else return NULL; /// expr_error("Not an lvalue"); *now caller is responsible*
}
PExpr force_addr_op(Type tt, PExpr e)
{
// This is ONLY called for reference conversions - so tt is always a reference....
PExpr ex;
if (e->op()==ADDR || e->type().size() != tt.size()) return reference_stub_op(tt,e);
ex = addr_op(e,false);
if (ex==NULL) // i.e we got a 'Not an lvalue' error message!
ex = reference_stub_op(tt,e);
return ex;
}
Type t_const_int; // initialized in init();
PExpr unary_op(int op, PExpr e1)
{
// A special case: -<number>
// *NOTE* 1st Oct 00: let's think about this, because it would be
// a good easier to get numbers in negative right from the start;
// this JUST does integers
if(op==UMINUS && e1->is_entry() && e1->type()==t_const_int) {
int *data = (int *)Parser::ExprToPtr(e1);
*data = - *data;
return e1;
} else
return bin_op(op,e1,NULL);
}
PExpr arith_if_op(PExpr cond, PExpr e1, PExpr e2)
{
PExpr rest = arith_op(':',e1,e2);
return make_op(ARITH_IF,rest->type(),cond,rest);
}
PExpr sizeof_op(int type_size)
// *Fix 0.9.2 No longer takes a type argument; currently
// there's no array size information in Type, so I had
// to ask the entry...
{
return constant_op(t_int,type_size);
}
string t2s(Type t)
{
string ts;
t.as_string(ts);
return "'" + ts + "'";
}
bool type_has_VMT(Type t)
{
return t.is_class() ? t.as_class()->has_VMT() : false;
}
PExpr typecast_op(int which, Type t, PExpr e)
{
Type te = type_of(e);
if (which == DYNAMIC_CAST) { // *add 0.9.5 dynamic cast
if (! t.is_class() && t != t_void_ptr) return expr_error("dynamic_cast: must be class or void *");
if (!type_has_VMT(t) || !type_has_VMT(e->type())) return expr_error("dynamic_cast: class must have virtual methods");
return make_op(DYNACAST,t,e,NULL);
} else {
TypeDistance td = match(t,te);
if (td == NO_MATCH && (which==STATIC_CAST || which==REINTERPRET_CAST)) { //*SJD* 23/09/00 TEMP
// *fix 0.9.8 try user-defined conversions in typecast
// I'm not sure about the REINTERPRET_CAST; trying to distinguish from STATIC_CAST!
PExpr et = convert_type_op(t,e,which == REINTERPRET_CAST);
if (et != NULL) return et;
}
return bcast_op(t,e);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -