📄 expressions.cpp
字号:
}
}
// *fix 1.2.8 It's necessary to look at the 'no args' case specially.
PExpr function_cast_op(Type t, PExprList pel)
{
if (t.is_class() && !t.is_ref_or_ptr()) {
return construct_temporary_op(t,pel->size() > 0 ? function_op(NULL,pel) : NULL);
} else {
if (pel->size() != 1) return expr_error("typecast needs one argument");
return typecast_op(STATIC_CAST,t,pel->front());
}
}
PExpr must_be_lvalue(int op)
{ return expr_error(Operators::name_from_id(op) + ": must be lvalue"); }
PExpr inc_dec_op(int op, PExpr e, bool is_postfix)
{
Type t = e->type();
// C++ convention for indicating whether ++ is post or prefix: a dummy parm!
// (cd easily be constant, but what the hell...
PExpr extra = is_postfix ? constant_op(t_int,0) : NULL;
if (t.is_reference()) { // NOTE(3) hack!!
// *hack 1.2.9a 'const T*' is a pointer to const _data_ (working around UC const limitation)
if (t.is_const() && ! t.is_pointer()) return must_be_lvalue(op);
else if (t.is_pointer()) { //*a special case*
return make_op(op==INCR ? INCR_PTR : DECR_PTR,t,e,extra);
} else if (t.is_int()){
// plain ++/-- acting on variables
if (e->is_variable()) return make_op(op,t,e,extra);
// in the general case, we take the address of the argument & use it
else {
PExpr re = addr_op(e,false);
if (re == NULL) return must_be_lvalue(op);
return make_op(op,t,re,extra);
}
} else if (t.is_float()) return not_for_float(op); //*NOTE* this isn't true!
//else return expr_error("Que?");
}
//else
if (t.is_class()) return bin_op(op,e,extra);
else return must_be_lvalue(op);
}
PExpr equal_op(PExpr e1, PExpr e2)
{
return relational_op(EQUAL,e1,e2);
}
PExpr relational_op(int op, PExpr e1, PExpr e2)
{
PExpr e;
if (e2) e = arith_op(op,e1,e2);
else e = unary_op(op,e1);
// *fix 1.1.0 builtin relational ops return bool (but can be arbitrarily overloaded)
if (e->op() == op) e->set_type(t_bool);
return e;
}
PEntry ExprToEntry(PExpr e)
{
return e->entry();
}
PExpr convert_function_ptr(PFunction pf, PExpr e)
// pf is the function we're passing this fn ptr!
// *add 1.1.4: functions passed to builtin routines can be true function pointers!
{
if (pf->builtin()) { // we must pass a native routine to such beasts!
void *pfn;
if (e->op()==ADDR || e->op()==BCAST) e = e->arg1();
if (e->is_entry() && e->type().is_function()) {
PEntry pe = e->entry();
PFunction fun_ptr = PFunctionEntry(pe->data)->back();
if (fun_ptr->builtin()) { // extract the addr of wrapped native routine!
pfn = fun_ptr->fun_block()->native_addr();
} else { // build native code stub for this function!
pfn = Builtin::generate_native_stub(fun_ptr);
}
return constant_op(t_void_ptr,(long)pfn);
} else {
// a function ptr expression requires dynamic stub generation
return function_call("_native_stub",expr_list(e));
}
} else return e;
}
void generate_type_list(PExprList args, TypeList& tl)
{
ExprList::iterator eli;
for (eli = args->begin(); eli != args->end(); ++eli)
tl.push_back((*eli)->type());
}
PExpr convert_type_op(Type t, PExpr e, bool try_conversions_from)
{
// *fix 1.1.3 will also try to use ctors to construct a type!
Function *pf;
// first try user-defined conversions, if the expression is an object
// *fix 1.2.2 This was kicking in for pointers to objects as well!
Type et = e->type();
if (et.is_class() && ! et.is_pointer()) {
pf = et.as_class()->get_conversion_to(t);
if (pf != NULL) return method_call(pf,e,NULL);
}
// optionally try to convert the expression using a ctor
if (try_conversions_from && t.is_class()) {
pf = t.as_class()->get_conversion_from(et);
if (pf != NULL) return construct_temporary_op(t,e);
}
return NULL; // no conversions possible
}
int min(int a, int b) { return a > b ? b : a; }
bool was_plain(int match)
{
return match == EXACT_MATCH || match == TRIVIAL_MATCH || match == PROMOTE_MATCH || match == STD_MATCH;
}
PExpr this_ref()
{
PEntry pe = new Entry();
pe->data = THIS_OFFSET;
return entry_op(pe);
}
PExpr pass_by_value(Type t,PExpr e)
{
PExpr ec = construct_op(t.as_class(),expr_list(e));
return make_op(PASS_BY_VALUE,t,ec);
}
PExpr function_op(PExpr e, PExprList args, bool suppress_error)
{
PExpr er;
FunctionMatch fm;
PFunction fn;
// An empty function expression allows expr lists to be expressions
if (!e) return new Expr(FUNCTION,t_void,NULL,args);
if(e->op() == NIL) return e; // error in function expression
if (!args) args = new ExprList; /// empty arg list
//Extract the actual function expression (may be a method call!)
bool was_method = e->op() == DOT;
PExpr epf = was_method ? e->arg2() : e;
// If the expression isn't a function, then operator() may be overloaded...
if (!was_method && !e->type().is_signature()) {
Type t = e->type();
if (t.is_class() && !t.is_pointer()) {
PEntry fpe = e->type().as_class()->lookup("()");
if (fpe != NULL) return method_op(fpe,e,args);
}
return expr_error("Not a function");
}
// if the function expr is NOT an entry, it's assumed to evaluate to a function!
bool was_fn_ptr = epf->type().is_pointer() || !epf->is_entry();
PFunctionEntry pfe;
if (was_fn_ptr) {
// to match pointers to functions we make up a fake entry!
PEntry pef = new Entry;
pef->name = "";
pfe = new FunctionEntry(pef);
pfe->push_back(new Function(epf->type().as_signature(),pfe));
} else {
pfe = PFunctionEntry(epf->entry()->data);
}
// extract the signature from the list of actual arguments
Signature *sig = new Signature();
ExprList::iterator eli;
ImportScheme *import;
for (eli = args->begin(); eli != args->end(); ++eli)
sig->push_back((*eli)->type());
if (fm.function_match(*pfe,*sig)) {
PExprList pdef;
fn = fm.matched_function();
// *add 0.9.5 Enforce const method rule *HACK exclude operator[]
//* if (was_method && !fn->is_const() && e->arg1()->type().is_const()
//* && fn->name() != "[]")
//* return expr_error("cannot call a non-const method with a const object");
//*NOTE* Be careful - shd be fn->is_complete() or something - I will
// want to optimize this!!
if (fn == NULL)
return expr_error("fn==NULL");
// add any default arguments....
pdef = fn->complete_arg_list(args);
// *add 1.1.0 support for pre-constructing objects when passed by value
import = fn->import_scheme();
MatchList::iterator mli = fm.matches(pdef);
int i = 0, n_sig = min(fn->signature()->size(),args->size()); // i.e. not for default args..
for (eli = args->begin(); i < n_sig; ++eli,++mli,++i) {
int match = mli->match();
Type t = mli->type();
Type et = (*eli)->type();
if (import != NULL && t.is_object()) {
if(import->true_pass_by_value(t)) {
*eli = pass_by_value(t,*eli);
continue;
} else if(was_plain(match))
// *fix 1.2.0 Final 'true' here means that callee is responsible for disposing of object!
*eli = construct_temporary_op(t,*eli,true);
}
switch(match) {
case PROMOTE_MATCH:
case STD_MATCH:
*eli = bcast_op(t,*eli);
break;
case REFERENCE_MATCH:
case REF_STD_MATCH:
case REF_PROMOTE_MATCH:
if (et.is_variable()) et.strip_qualifiers(); // *hack 0.9.7 trouble w/ is_object()!
if (! et.is_object()) // objects are always passed by reference anyhow
*eli = force_addr_op(t,*eli); // if necesary, force a reference conversion
break;
case CONVERT_TO_MATCH:
*eli = convert_type_op(t,*eli);
break;
case CONVERT_FROM_MATCH:
t.strip_qualifiers();
*eli = construct_temporary_op(t,*eli);
break;
case FUN_PTR_MATCH:
*eli = convert_function_ptr(fn,*eli);
break;
}
}
}
else {
// *add 1.2.3 Suppressed errors (like when we are looking at operators) are saved for later...
set_function_error(pfe,fm.error());
if (suppress_error) return NULL;
else return function_error();
}
Type rt = fn->return_type();
bool returns_object = rt.is_object();
bool true_value_return = import != NULL && returns_object && import->true_return_by_value(rt);
if (returns_object && ! true_value_return) fn_return_object(rt,args);
// *fix 0.9.6 The DCALL function type _forces_ a function to be directly called, virtual or not
bool plain_fun = true;
if (gScopeContext != NULL) {
plain_fun = false;
gScopeContext = NULL;
}
if (was_method) args->push_back(e->arg1()); // append obj ptr to arg list
if (!was_fn_ptr) { // plain function or method
er = new Expr(was_method ? METHOD_CALL : (plain_fun ? FUNCTION : DCALL),rt,fn,args);
} else { // pointer to function or method
er = new Expr(was_method ? EXPR_METHOD : EXPR,rt,epf,args);
}
// Dereference any returned reference type! (can't use deref_op()!)
if (rt.is_reference()) er = make_op(DEREF,rt,er);
if(was_fn_ptr) { // was temporary!
delete pfe;
delete fn;
}
// finally, if it was a class template method then instantiate it (if not already)
if (!was_fn_ptr && pfe->get_template() && pfe->get_template()->is_method())
if (!pfe->get_template()->instantiate_method(fn)) return expr_error("instantiation failed");
if (returns_object && true_value_return) return fn_return_object_as_value(rt,er);
else return er;
}
PExpr expr_not_found_error(char *name, PClass pc)
{
using Parser::quotes;
return expr_error(quotes(name) + " is not a member of " + quotes(pc->name()));
}
PExpr selection_op(PExpr e, char *name,bool is_ptr,bool is_member_ptr)
{
Type t = e->type();
bool was_pointer = t.is_pointer();
if (!t.is_class()) return expr_error("Not a class object or pointer");
PClass pc = t.as_class();
PEntry pe;
bool not_found;
if (!is_member_ptr) {
pe = pc->lookup(name); // in the context of the first operand...
not_found = !pe || !pc->inherits_from((Class *)pe->context); // and not any enclosing scope...
} else {
pe = Parser::symbol_lookup(name);
not_found = !pe;
}
if (is_ptr && !was_pointer) { // -> may be overloaded...
PExpr efn = unary_op(ARROW,e);
if (efn->op() != ARROW) { // was overloaded!
Type rtype = efn->type();
if (!rtype.is_class()) return expr_error("operator-> should return a class");
return selection_op(efn,name,true);
}
}
// otherwise, it's just a plain selection member operator
if (not_found) return expr_not_found_error(name,pc);
if (!was_pointer && is_ptr) return expr_error("Must be a pointer"); else
if (was_pointer && !is_ptr) return expr_error("Cannot be an object pointer");
if (was_pointer) e = deref_op(e,false);
// crucial to use the _expression's_ type, since this will preserve
// reference information!
PExpr em = entry_op(pe);
return make_op(DOT,em->type(),e,em);
}
//----------------------------------------------------------
PExpr entry_op(PEntry pe)
{
// NOTE(3) identifiers are given an 'variable reference' type
// (Note: this replaces a scheme where it was actually a reference type! (Algol 68
// understood this kinda thing better). We now have an extra type flag to indicate
// that variable references are Different)
// NOTE(4) a reference x is represented by (* x)
// arrays are _not_variables!
Type t = pe->type;
if (!t.is_reference()) {
if (pe->name != "" && Parser::array_size(pe) == 1) t.make_variable();
return new Expr(IREF,t,pe);
} else
return make_op(DEREF,t,new Expr(IREF,t,pe));
}
PExpr constant_op(Type t, unsigned long val)
{
PEntry pe;
//using Parser::create_const_entry;
unsigned long *pi = (unsigned long *)Parser::create_const_entry(t,pe);
*pi = val;
return entry_op(pe);
}
// *add 1.2.3 Experimental implementation of __lambda
PExpr lambda_op(PEntry)
{
PEntry pe = Parser::symbol_lookup("_");
FunctionEntry* pfe = (FunctionEntry*)pe->data;
// *NOTE* shd dispose of '_'!!
Function* fn = pfe->back();
return entry_op(pe);
}
PExpr make_op(int op,Type type, PExpr e1, PExpr e2/* = NULL */)
{
return new Expr(op,type,e1,e2);
}
PExpr clone (PExpr e)
// make a deep copy of an expression!
{
if (!e) return NULL;
int op = e->op();
Type t = e->type();
PExpr er;
if (e->is_function() || e->is_expr()) {
PExprList pel = e->arg_list();
PExprList pelc = new ExprList(*pel);
er = new Expr(op,t,
e->is_function() ? (PExpr)e->function() : clone(e->arg1()),
pelc);
} else
if (e->is_entry()) {
er = new Expr(op,t,e->entry());
}
// for all other operators, can treat the arguments as expressions
else er = new Expr(op,t,clone(e->arg1()),clone(e->arg2()));
return er;
}
void dump(ostream& os, PExpr e)
{
if (!e) return;
if (e->is_function() || e->is_expr()) {
os << " (";
if (e->is_expr()) dump(os,e->arg1());
else os << '(' << e->type() << ')' << e->function()->name();
ExprList::iterator eli;
PExprList pel = e->arg_list();
if (pel)
for(eli = pel->begin(); eli != pel->end(); ++eli)
dump(os,*eli);
os << ')';
} else
if (e->is_nil()) os << "<nil>";
else
if (! e->is_entry()) { // i.e. a binary or unary op!
os << " (";
if (! e->is_bcast()) os << e->name();
else os << '(' << e->type() << ')';
dump(os,e->arg1());
dump(os,e->arg2());
os << ')';
} else os << ' ' << e->name();
}
void init()
{
int equiv[] = {MUL_A,STAR,DIV_A,DIVIDE,MOD_A,MODULO,ADD_A,PLUS,MINUS_A,MINUS,
SHL_A,LSHIFT,SHR_A,RSHIFT,BAND_A,BIN_AND,BOR_A,BIN_OR,XOR_A,BIN_XOR};
int takes_float[] = {PLUS,MINUS,UPLUS,UMINUS,STAR,DIVIDE,EQUAL,LESS_THAN,
NOT_EQUAL,GREATER,LEQ,GEQ,ASSIGN,COMMA,':'};
// *fix 0.9.3 arith. if refused floating-point arguments - added ':' above.
int i;
for(i = 0; i < sizeof(equiv)/(2*sizeof(int)); i++)
assign_equiv_op[equiv[2*i]] = equiv[2*i+1];
for(i = 0; i < sizeof(takes_float)/sizeof(int); i++)
float_op[takes_float[i]] = 1;
t_const_int = t_int;
t_const_int.make_const();
}
} // namespace Expressions
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -