⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 expressions.cpp

📁 UC Library Extensions UnderC comes with a pocket implementation of the standard C++ libraries, wh
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/* 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 + -