📄 expressions.cpp
字号:
/* Expressions.cpp
* Code to manipulate expressions - the type PExpr
* UnderC C++ interpreter
* Steve Donovan, 2001
* This is GPL'd software, and the usual disclaimers apply.
* See LICENCE
*
* Expr is a fairly basic type, consisting of an operation type (e.g. FUNCTION)
* and up to two operands. C++ expressions are represented by binary trees built out of
* Expr pointers.
*/
#include "expressions.h"
#include "common.h"
#include "function_match.h"
#include "tparser.h"
#include "operators.h"
#include "directcall.h"
#include <assert.h>
#include <map>
typedef std::map<int,int> IntMap;
extern Table* gScopeContext; // from common.cpp (yes, a hack)
// found in type.cpp (of all places!)
Signature *unique_signature(Signature *sig);
// found in common.cpp
string as_str(Type t);
namespace {
IntMap assign_equiv_op, float_op;
};
bool
Expr::is_function()
{
return m_op == FUNCTION || m_op == METHOD_CALL || m_op == DCALL;
}
string
Expr::name()
{
if (is_entry()) {
if (!entry()->is_constant()) return entry()->name;
if (!entry()->type.is_int()) return "?";
return itos(Parser::const_int_expr(entry()));
} else
if (is_function()) return function()->name(); else
if (is_expr()) return "(...)";
else { // we assume it's a specific operator!
return Operators::name_from_id(op());
}
}
bool
Expr::is_variable()
{
return is_entry() && type().is_reference() && ! entry()->type.is_reference();
}
bool Expr::is_expr_list()
{
return is_function() && function()==NULL;
}
bool Expr::is_brace_list()
{
return is_expr_list() && m_type == t_int;
}
namespace Expressions {
// *add 1.2.3 A mechanism for defering function errors when looking at operators
FunctionEntry* mErrPfe;
string mErrMatch;
PExpr expr_error(string msg); // forward
void set_function_error(FunctionEntry* pfe, string str)
{
mErrPfe = pfe;
mErrMatch = str;
}
PExpr function_error()
{
if (mErrPfe) {
cerr << "Candidates were\n";
mErrPfe->dump(cerr);
}
PExpr e = expr_error(mErrMatch);
mErrPfe = NULL;
mErrMatch = "";
return e;
}
PExpr function_call(const char *name, PExprList pel)
{
Function *fn = Function::lookup(name);
return new Expr(FUNCTION,fn->return_type(),fn,pel);
}
PExpr bcast_op(Type t, PExpr e)
{
if (t == e->type()) return e;
else {
if (e->type().is_plain_reference()) t.make_reference(); // *fix 0.9.7 made var casts to be references!
return new Expr(BCAST,t,e);
}
}
bool is_lvalue(Type t)
{
return t.is_reference() && !t.is_const();
}
Type type_of(PExpr e)
{
// this peculiar logic is the inverse of that in entry_op() below!
if (e->is_entry()) return e->entry()->type;
else return e->type();
}
// *add 0.9.3 The (non-standard) typeof operator is still experimental.
// So far, I have to strip reference types to make __typeof(*p)
// work properly.
Type typeof_op(PExpr e)
{
Type t = type_of(e);
t.strip_reference();
return t;
}
bool is_true_reference(PExpr e)
{
return type_of(e).is_reference();
}
PExpr nil()
{
return new Expr(NIL,t_void);
}
PExpr expr_error(string msg)
{
error(msg);
return nil();
}
string quoted_op_name(int op) { return Parser::quotes(Operators::name_from_id(op)); }
PExpr not_for_float(int op)
{
return expr_error(quoted_op_name(op)+" does not take floating point arguments");
}
PExprList expr_list(PExpr e1, PExpr e2)
{
PExprList pel = new ExprList;
if (e1) pel->push_back(e1);
if (e2) pel->push_back(e2);
return pel;
}
const int NTYPES = 9;
PExpr pointer_addition_op(int op, PExpr e1, PExpr e2)
{
if (op == MINUS) e2 = make_op(UMINUS,e2->type(),e2);
return make_op(ARRAY,e1->type(),e1,e2);
}
// here are the functions which the parser uses to generate expressions
PExpr arith_op(int op, PExpr e1, PExpr e2)
{
static Type types[NTYPES]
= {t_double,t_ulong,t_long, t_uint,t_int,t_ushort,t_short,t_uchar,t_char};
Type t1 = e1->type(), t2 = e2->type();
t1.strip_qualifiers();
t2.strip_qualifiers();
if (t1.is_number() && t2.is_number()) {
if (t1 == t_float) { e1 = bcast_op(t_double,e1); t1 = t_double; }
if (t2 == t_float) { e2 = bcast_op(t_double,e2); t2 = t_double; }
for(int i = 0; i < NTYPES; i++) {
Type tt = types[i];
if (t1 == tt || t2 == tt) {
if (tt == t_double && float_op[op] == 0) return not_for_float(op);
if (t1 != tt) e1 = bcast_op(tt,e1);
if (t2 != tt) e2 = bcast_op(tt,e2);
return make_op(op,tt,e1,e2);
}
}
} else
if (op == PLUS || op == MINUS) {
// *add 0.9.3 Subtracting two pointers should give the number of elements...
// *fix 1.2.1 Wrong! p1-p2 should be equiv. to (int)p1 - (int)p2; also, it was wrong for int types!
// *fix 1.2.4 Sorry, wrong again! I was right for 0.9.3.
if (t1.is_pointer() && t2.is_pointer()) {
if (op == MINUS) {
t1.decr_pointer();
PExpr ae = make_op(MINUS,t_int,e1,e2);
return make_op(DIVIDE,t_int,ae,sizeof_op(t1.size()));
}
else return expr_error("Cannot add pointers");
}
if (t1.is_pointer() && t2.is_int()) return pointer_addition_op(op,e1,e2); else
if (t2.is_pointer() && t1.is_int()) return pointer_addition_op(op,e2,e1);
}
// otherwise, try for user-defined operator!
return bin_op(op,e1,e2);
}
PExpr dot_function_op(void *pfn, PExpr obj)
{
return make_op(DOT,obj->type(),obj,entry_op((PEntry)pfn));
}
PExpr method_op(PEntry pef, PExpr obj, PExprList pel)
{
// *NOTE* are we having returning-object problems w/ this one?
// used for bin_op() methods, also operator().
return function_op(dot_function_op(pef,obj),pel);
}
PExpr construct_context_op(PExpr e1, PExpr e2=NULL, int ctype=0); // forward
PEntry add_temporary(Type t, PExpr e, bool always_construct, bool dont_do_dtor = false); // forward
void set_first_object(PExpr e1); // forward;
PExpr init_op(PExpr el, int arr_sz, PExpr er, PExprList pel, int ctype); // forward;
void fn_return_object(Type rt, PExprList args)
{
// Functions returning objects are passed a temporary reference;
// if it's an object requiring destruction, we also push the ODS!
PExpr et = entry_op(add_temporary(rt,NULL,false));
if (rt.as_class()->destructor()) et = make_op(FCONTEXT,rt,et,0);
args->push_front(et);
}
// *add 1.2.6 true return-by-value requires a different coding;
PExpr fn_return_object_as_value(Type rt, PExpr er)
{
PExpr et = entry_op(add_temporary(rt,NULL,false));
PExpr ret_obj = Parser::get_object_return_expr();
Type init_rt = rt;
// init_rt.make_reference();
// init_rt.make_const();
ret_obj->entry()->type = init_rt;
ret_obj->set_type(init_rt);
return append_op(er, // the function expresssion
append_op(init_op(et,1,ret_obj,NULL,1), // initializing the temporary
et,true) // pushing the temporary
);
}
PExpr method_call(PFunction fn, PExpr obj, PExprList pel)
// note: will mess w/ pel!!
{
Type rt = fn->return_type();
if (!pel) pel = new ExprList; /// empty arg list
if (rt.is_object()) fn_return_object(rt,pel);
pel->push_back(obj);
return make_op(METHOD_CALL,rt,(PExpr)fn,(PExpr)pel);
}
typedef bool (TypeCheck)(Type t);
bool to_number(Type t) { return t.is_number() ; }
bool to_pointer(Type t) { return t.is_pointer(); }
static Type mType;
bool to_specified_type(Type t)
{
return match(mType,t) != NO_MATCH;
}
PExpr try_convert(PExpr e, TypeCheck type_check)
{
if (!e) return NULL;
Type t = e->type();
if (type_check(t)) return e;
if (t.is_class()) {
TypeList tl;
PClass pc = t.as_class();
if (pc->get_to_type_list(tl)) {
TypeList::iterator tli;
for (tli = tl.begin(); tli != tl.end(); ++tli)
if (type_check(*tli)) {
PFunction pf = pc->get_conversion_to(*tli);
return method_call(pf,e,NULL);
}
}
}
return NULL;
}
PExpr try_convert_to(PExpr e, Type t)
{
mType = t;
return try_convert(e,to_specified_type);
}
bool is_assign_op(int op) // note: dependent on these values being continguous.
{
return op >= ASSIGN && op <= XOR_A;
}
bool is_object(PExpr e)
// note: Type::is_object() fails for class _references_.
// Not sure if that is sensible, but here goes...
{
if (e==NULL) return false;
Type t = e->type();
return t.is_class() && !t.is_pointer();
}
string type_as_str(PExpr e)
{
return as_str(e->type());
}
PExpr cannot_convert_error(PExpr e1, PExpr e2)
{
return expr_error("cannot convert " + type_as_str(e2) + " to " + type_as_str(e1));
}
PExpr bin_op(int op, PExpr e1, PExpr e2)
{
string name = Operators::name_from_id(op);
PEntry pe = NULL;
bool class_args = is_object(e1) || is_object(e2);
if (class_args) {
// first see if it's a method of an object
if (is_object(e1)) pe = e1->type().as_class()->lookup(name);
// then if it's global (or at least injected into global)
if(!pe) pe = Parser::global().lookup(name);
if (pe && Parser::is_class(pe->context)) { // was a method operator
PExpr er = method_op(pe,e1,expr_list(e2));
return er;
}
}
if (pe) { // plain old operator - treat as function!
// *fix 1.1.0 If we can't match the bin op, pass thru and try conversions...
PExpr efn = function_op(entry_op(pe),expr_list(e1,e2),true);
if (efn) return efn;
}
// no entry was found, or no match possible;
if (is_assign_op(op)) { // *fix 0.9.6 try for assignment conversions
Type t = e1->type();
PExpr ce = try_convert_to(e2,t);
if (ce) {
if (op == ASSIGN) return make_op(ASSIGN,t,e1,ce);
else return compound_assign_op(op,e1,ce);
} else return cannot_convert_error(e1,e2);
} else
if (op != COMMA) { // try for numerical/pointer conversions
PExpr ce1,ce2;
ce1 = try_convert(e1,to_number);
ce2 = try_convert(e2,to_number);
if (!e2 && ce1) return make_op(op,ce1->type(),ce1,NULL); // unary case!
else if (ce1 && ce2) return arith_op(op,ce1,ce2);
if (op == PLUS || op == MINUS) { // pointer arithmetric
if (ce2) ce1 = try_convert(e1,to_pointer);
if (ce1) ce2 = try_convert(e2,to_pointer);
if (ce1 && ce2) return arith_op(op,ce1,ce2);
}
}
if (class_args) {
// *hack 0.9.7 list<> needs op== at the mo, whether or not it's used.
// This seems arbitrary but is nicer than shoving a spurious op== into every class!
// *add 1.2.3 Now give a more informative error message
if (op == EQUAL) return constant_op(t_int,0);
else return function_error();
}
else return make_op(op,e1->type(),e1,e2);
}
PExpr append_op(PExpr e1, PExpr e2, bool drop_values)
{
return make_op(drop_values ? COMMA : APPEND, e1->type(),e1,e2);
}
void set_first_object(PExpr e1)
{
if (e1->is_entry() && e1->entry()->is_stack_relative()) {
// *fix 1.0.0 The first object on an auto var frame is the first one put on the ODS...
e1->entry()->context->first_obj(e1->entry());
}
}
PExpr construct_context_op(PExpr e1, PExpr e2, int ctype)
{
Type t;
// default is a normal auto var, ctype==1 means a temporary
if (ctype == 0) t = t_void; else t = t_int;
return make_op(CCONTEXT,t,e2,e1); //*NOTE* order has switched for DOT compat!
}
PExpr dynamic_context_op(PExpr efn)
{
return make_op(DCONTEXT,t_void,efn,NULL);
}
PExpr vector_context_op(PExpr efn, PExpr e = NULL)
{
if (e) set_first_object(e);
return make_op(VCONTEXT,t_void,e,efn);
}
PExpr add_const_op(Type t, int val, PExpr e)
{
PExpr ec = constant_op(t_int,val);
return make_op(PLUS,t,e,ec);
}
PExpr delete_op(PExpr e1, bool is_arr)
{
// destructing the object, if necessary, and freeing the memory
// the pointer includes the VMT entry, if any, which means subtracting
// a word to get the actual alloc pointer.
// *change 1.1.0 This is handled by the builtin delete_ex, unless delete is overloaded.
Type t = e1->type();
t.decr_pointer();
PClass pc = t.is_class() ? t.as_class() : NULL;
bool overalloc = Parser::debug.do_overalloc || (pc != NULL ? pc->has_true_VMT() : false);
bool overloaded = Function::lookup("delete") != NULL;
const char *delete_fn = overloaded ? "delete" : (overalloc ? "_delete_ex" : "_delete");
PExpr er,ed=NULL,ez = constant_op(t_int,t.size());
if (pc != NULL) {
if (pc->destructor()) {
PEntry pe = pc->lookup(pc->destructor_name());
ed = function_op(entry_op(pe),expr_list());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -