mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 19:08:55 -04:00
1846 lines
48 KiB
C++
1846 lines
48 KiB
C++
// Filename: cppExpression.cxx
|
|
// Created by: drose (25Oct99)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "cppExpression.h"
|
|
#include "cppToken.h"
|
|
#include "cppIdentifier.h"
|
|
#include "cppType.h"
|
|
#include "cppSimpleType.h"
|
|
#include "cppPointerType.h"
|
|
#include "cppConstType.h"
|
|
#include "cppArrayType.h"
|
|
#include "cppPreprocessor.h"
|
|
#include "cppInstance.h"
|
|
#include "cppFunctionGroup.h"
|
|
#include "cppFunctionType.h"
|
|
#include "cppBison.h"
|
|
#include "pdtoa.h"
|
|
|
|
#include <assert.h>
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::Result::
|
|
Result() {
|
|
_type = RT_error;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::Result::
|
|
Result(int value) {
|
|
_type = RT_integer;
|
|
_u._integer = value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::Result::
|
|
Result(double value) {
|
|
_type = RT_real;
|
|
_u._real = value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::Result::
|
|
Result(void *value) {
|
|
_type = RT_pointer;
|
|
_u._pointer = value;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::as_integer
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
int CPPExpression::Result::
|
|
as_integer() const {
|
|
switch (_type) {
|
|
case RT_integer:
|
|
return _u._integer;
|
|
|
|
case RT_real:
|
|
return (int)_u._real;
|
|
|
|
case RT_pointer:
|
|
// We don't mind if this loses precision.
|
|
return (int)reinterpret_cast<long>(_u._pointer);
|
|
|
|
default:
|
|
cerr << "Invalid type\n";
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::as_real
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
double CPPExpression::Result::
|
|
as_real() const {
|
|
switch (_type) {
|
|
case RT_integer:
|
|
return (double)_u._integer;
|
|
|
|
case RT_real:
|
|
return _u._real;
|
|
|
|
case RT_pointer:
|
|
// We don't mind if this loses precision.
|
|
return (double)reinterpret_cast<long>(_u._pointer);
|
|
|
|
default:
|
|
cerr << "Invalid type\n";
|
|
assert(false);
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::as_pointer
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void *CPPExpression::Result::
|
|
as_pointer() const {
|
|
switch (_type) {
|
|
case RT_integer:
|
|
return reinterpret_cast<void*>((long)_u._integer);
|
|
|
|
case RT_real:
|
|
return reinterpret_cast<void*>((long)_u._real);
|
|
|
|
case RT_pointer:
|
|
return _u._pointer;
|
|
|
|
default:
|
|
cerr << "Invalid type\n";
|
|
assert(false);
|
|
return (void *)NULL;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::as_boolean
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
bool CPPExpression::Result::
|
|
as_boolean() const {
|
|
switch (_type) {
|
|
case RT_integer:
|
|
return (_u._integer != 0);
|
|
|
|
case RT_real:
|
|
return (_u._real != 0.0);
|
|
|
|
case RT_pointer:
|
|
return (_u._pointer != NULL);
|
|
|
|
default:
|
|
cerr << "Invalid type\n";
|
|
assert(false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Result::output
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void CPPExpression::Result::
|
|
output(ostream &out) const {
|
|
switch (_type) {
|
|
case RT_integer:
|
|
out << _u._integer;
|
|
break;
|
|
|
|
case RT_real:
|
|
out << _u._real;
|
|
break;
|
|
|
|
case RT_pointer:
|
|
out << _u._pointer;
|
|
break;
|
|
|
|
case RT_error:
|
|
out << "(error)";
|
|
break;
|
|
|
|
default:
|
|
out << "(**invalid type**)\n";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(unsigned long long value) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
_type = T_integer;
|
|
_u._integer = value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(int value) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
_type = T_integer;
|
|
_u._integer = value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(long double value) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
_type = T_real;
|
|
_u._real = value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(const string &value) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
_type = T_string;
|
|
_str = value;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(CPPIdentifier *ident, CPPScope *current_scope,
|
|
CPPScope *global_scope, CPPPreprocessor *error_sink) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
CPPDeclaration *decl =
|
|
ident->find_symbol(current_scope, global_scope);
|
|
|
|
if (decl != NULL) {
|
|
CPPInstance *inst = decl->as_instance();
|
|
if (inst != NULL) {
|
|
_type = T_variable;
|
|
_u._variable = inst;
|
|
return;
|
|
}
|
|
CPPFunctionGroup *fgroup = decl->as_function_group();
|
|
if (fgroup != NULL) {
|
|
_type = T_function;
|
|
_u._fgroup = fgroup;
|
|
return;
|
|
}
|
|
}
|
|
|
|
_type = T_unknown_ident;
|
|
_u._ident = ident;
|
|
_u._ident->_native_scope = current_scope;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(int unary_operator, CPPExpression *op1) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
_type = T_unary_operation;
|
|
_u._op._operator = unary_operator;
|
|
_u._op._op1 = op1;
|
|
_u._op._op2 = NULL;
|
|
_u._op._op3 = NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(int binary_operator, CPPExpression *op1, CPPExpression *op2) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
_type = T_binary_operation;
|
|
_u._op._operator = binary_operator;
|
|
_u._op._op1 = op1;
|
|
_u._op._op2 = op2;
|
|
_u._op._op3 = NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
CPPExpression(int trinary_operator, CPPExpression *op1, CPPExpression *op2,
|
|
CPPExpression *op3) :
|
|
CPPDeclaration(CPPFile())
|
|
{
|
|
_type = T_trinary_operation;
|
|
_u._op._operator = trinary_operator;
|
|
_u._op._op1 = op1;
|
|
_u._op._op2 = op2;
|
|
_u._op._op3 = op3;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named typecast_op constructor
|
|
// Access: Public, Static
|
|
// Description: Creates an expression that represents a typecast
|
|
// operation.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
typecast_op(CPPType *type, CPPExpression *op1) {
|
|
CPPExpression expr(0);
|
|
expr._type = T_typecast;
|
|
expr._u._typecast._to = type;
|
|
expr._u._typecast._op1 = op1;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named construct_op constructor
|
|
// Access: Public, Static
|
|
// Description: Creates an expression that represents a constructor
|
|
// call.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
construct_op(CPPType *type, CPPExpression *op1) {
|
|
CPPExpression expr(0);
|
|
if (op1 == NULL) {
|
|
// A default constructor call--no parameters.
|
|
expr._type = T_default_construct;
|
|
expr._u._typecast._to = type;
|
|
expr._u._typecast._op1 = NULL;
|
|
} else {
|
|
// A normal constructor call, with parameters.
|
|
expr._type = T_construct;
|
|
expr._u._typecast._to = type;
|
|
expr._u._typecast._op1 = op1;
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named new_op constructor
|
|
// Access: Public, Static
|
|
// Description: Creates an expression that represents a use of the
|
|
// new operator.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
new_op(CPPType *type, CPPExpression *op1) {
|
|
CPPExpression expr(0);
|
|
if (op1 == NULL) {
|
|
// A default new operation--no parameters.
|
|
expr._type = T_default_new;
|
|
expr._u._typecast._to = type;
|
|
expr._u._typecast._op1 = NULL;
|
|
} else {
|
|
// A normal new operation, with parameters.
|
|
expr._type = T_new;
|
|
expr._u._typecast._to = type;
|
|
expr._u._typecast._op1 = op1;
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named sizeof_func constructor
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
sizeof_func(CPPType *type) {
|
|
CPPExpression expr(0);
|
|
expr._type = T_sizeof;
|
|
expr._u._typecast._to = type;
|
|
expr._u._typecast._op1 = NULL;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named alignof_func constructor
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
alignof_func(CPPType *type) {
|
|
CPPExpression expr(0);
|
|
expr._type = T_alignof;
|
|
expr._u._typecast._to = type;
|
|
expr._u._typecast._op1 = NULL;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named literal constructor
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
literal(unsigned long long value, CPPInstance *lit_op) {
|
|
CPPExpression expr(0);
|
|
expr._type = T_literal;
|
|
expr._u._literal._value = new CPPExpression(value);
|
|
expr._u._literal._operator = lit_op;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named literal constructor
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
literal(long double value, CPPInstance *lit_op) {
|
|
CPPExpression expr(0);
|
|
expr._type = T_literal;
|
|
expr._u._literal._value = new CPPExpression(value);
|
|
expr._u._literal._operator = lit_op;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named literal constructor
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
literal(CPPExpression *value, CPPInstance *lit_op) {
|
|
CPPExpression expr(0);
|
|
expr._type = T_literal;
|
|
expr._u._literal._value = value;
|
|
expr._u._literal._operator = lit_op;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::named raw_literal constructor
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression CPPExpression::
|
|
raw_literal(const string &raw, CPPInstance *lit_op) {
|
|
CPPExpression expr(0);
|
|
expr._type = T_raw_literal;
|
|
expr._str = raw;
|
|
expr._u._literal._value = (CPPExpression *)NULL;
|
|
expr._u._literal._operator = lit_op;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::get_nullptr
|
|
// Access: Public, Static
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
const CPPExpression &CPPExpression::
|
|
get_nullptr() {
|
|
static CPPExpression expr(0);
|
|
expr._type = T_nullptr;
|
|
return expr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::Destructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::
|
|
~CPPExpression() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::evaluate
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression::Result CPPExpression::
|
|
evaluate() const {
|
|
Result r1, r2;
|
|
|
|
switch (_type) {
|
|
case T_nullptr:
|
|
return Result((void *)0);
|
|
|
|
case T_integer:
|
|
return Result((int)_u._integer);
|
|
|
|
case T_real:
|
|
return Result((double)_u._real);
|
|
|
|
case T_string:
|
|
case T_wstring:
|
|
case T_u8string:
|
|
case T_u16string:
|
|
case T_u32string:
|
|
return Result();
|
|
|
|
case T_variable:
|
|
if (_u._variable->_type != NULL &&
|
|
_u._variable->_initializer != NULL) {
|
|
// A const variable. Fetch its assigned value.
|
|
CPPConstType *const_type = _u._variable->_type->as_const_type();
|
|
if (const_type != NULL) {
|
|
return _u._variable->_initializer->evaluate();
|
|
}
|
|
}
|
|
return Result();
|
|
|
|
case T_function:
|
|
return Result();
|
|
|
|
case T_unknown_ident:
|
|
return Result();
|
|
|
|
case T_typecast:
|
|
assert(_u._typecast._op1 != NULL);
|
|
r1 = _u._typecast._op1->evaluate();
|
|
if (r1._type != RT_error) {
|
|
CPPSimpleType *stype = _u._typecast._to->as_simple_type();
|
|
if (stype != NULL) {
|
|
if (stype->_type == CPPSimpleType::T_int) {
|
|
return Result(r1.as_integer());
|
|
} else if (stype->_type == CPPSimpleType::T_float ||
|
|
stype->_type == CPPSimpleType::T_double) {
|
|
return Result(r1.as_real());
|
|
}
|
|
}
|
|
if (_u._typecast._to->as_pointer_type()) {
|
|
return Result(r1.as_pointer());
|
|
}
|
|
}
|
|
return Result();
|
|
|
|
case T_construct:
|
|
case T_default_construct:
|
|
case T_new:
|
|
case T_default_new:
|
|
case T_sizeof:
|
|
return Result();
|
|
|
|
case T_alignof:
|
|
if (_u._typecast._to != NULL) {
|
|
// Check if the type is defined with an alignas. TODO: this should
|
|
// probably be moved to a virtual getter on CPPType.
|
|
CPPExtensionType *etype = _u._typecast._to->as_extension_type();
|
|
if (etype != NULL && etype->_alignment != NULL) {
|
|
return etype->_alignment->evaluate();
|
|
}
|
|
}
|
|
return Result();
|
|
|
|
case T_binary_operation:
|
|
assert(_u._op._op2 != NULL);
|
|
r2 = _u._op._op2->evaluate();
|
|
|
|
// The operators && and || are special cases: these are
|
|
// shirt-circuiting operators. Thus, if we are using either of
|
|
// these it might be acceptable for the second operand to be
|
|
// invalid, since we might never evaluate it.
|
|
|
|
// In all other cases, both operands must be valid in order for
|
|
// the operation to be valid.
|
|
if (r2._type == RT_error &&
|
|
(_u._op._operator != OROR && _u._op._operator != ANDAND)) {
|
|
return r2;
|
|
}
|
|
// Fall through
|
|
|
|
|
|
case T_trinary_operation:
|
|
// The trinary operator is also a short-circuiting operator: we
|
|
// don't test the second or third operands until we need them.
|
|
// The only critical one is the first operand.
|
|
|
|
// Fall through
|
|
|
|
case T_unary_operation:
|
|
assert(_u._op._op1 != NULL);
|
|
r1 = _u._op._op1->evaluate();
|
|
if (r1._type == RT_error) {
|
|
// Here's one more special case: if the first operand is
|
|
// invalid, it really means we don't know how to evaluate it.
|
|
// However, if the operator is ||, then it might not matter as
|
|
// long as we can evaluate the second one *and* that comes out
|
|
// to be true.
|
|
if (_u._op._operator == OROR && r2._type == RT_integer &&
|
|
r2.as_integer() != 0) {
|
|
return r2;
|
|
}
|
|
|
|
// Ditto for the operator being && and the second one coming out
|
|
// false.
|
|
if (_u._op._operator == ANDAND && r2._type == RT_integer &&
|
|
r2.as_integer() == 0) {
|
|
return r2;
|
|
}
|
|
|
|
return r1;
|
|
}
|
|
|
|
switch (_u._op._operator) {
|
|
case UNARY_NOT:
|
|
return Result(!r1.as_integer());
|
|
|
|
case UNARY_NEGATE:
|
|
return Result(~r1.as_integer());
|
|
|
|
case UNARY_MINUS:
|
|
return (r1._type == RT_real) ? Result(-r1.as_real()) : Result(-r1.as_integer());
|
|
|
|
case UNARY_STAR:
|
|
case UNARY_REF:
|
|
return Result();
|
|
|
|
case '*':
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() * r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() * r2.as_integer());
|
|
}
|
|
|
|
case '/':
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() / r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() / r2.as_integer());
|
|
}
|
|
|
|
case '%':
|
|
return Result(r1.as_integer() % r2.as_integer());
|
|
|
|
case '+':
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() + r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() + r2.as_integer());
|
|
}
|
|
|
|
case '-':
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() - r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() - r2.as_integer());
|
|
}
|
|
|
|
case '|':
|
|
return Result(r1.as_integer() | r2.as_integer());
|
|
|
|
case '&':
|
|
return Result(r1.as_integer() & r2.as_integer());
|
|
|
|
case OROR:
|
|
if (r1.as_integer()) {
|
|
return r1;
|
|
} else {
|
|
return r2;
|
|
}
|
|
|
|
case ANDAND:
|
|
if (r1.as_integer()) {
|
|
return r2;
|
|
} else {
|
|
return r1;
|
|
}
|
|
|
|
case EQCOMPARE:
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() == r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() == r2.as_integer());
|
|
}
|
|
|
|
case NECOMPARE:
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() != r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() != r2.as_integer());
|
|
}
|
|
|
|
case LECOMPARE:
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() <= r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() <= r2.as_integer());
|
|
}
|
|
|
|
case GECOMPARE:
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() >= r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() >= r2.as_integer());
|
|
}
|
|
|
|
case '<':
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() < r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() < r2.as_integer());
|
|
}
|
|
|
|
case '>':
|
|
if (r1._type == RT_real || r2._type == RT_real) {
|
|
return Result(r1.as_real() > r2.as_real());
|
|
} else {
|
|
return Result(r1.as_integer() > r2.as_integer());
|
|
}
|
|
|
|
case LSHIFT:
|
|
return Result(r1.as_integer() << r2.as_integer());
|
|
|
|
case RSHIFT:
|
|
return Result(r1.as_integer() >> r2.as_integer());
|
|
|
|
case '?':
|
|
return r1.as_integer() ?
|
|
_u._op._op2->evaluate() : _u._op._op3->evaluate();
|
|
|
|
case '.':
|
|
case POINTSAT:
|
|
return Result();
|
|
|
|
case '[': // Array element reference
|
|
return Result();
|
|
|
|
case 'f': // Function evaluation
|
|
return Result();
|
|
|
|
case ',':
|
|
return r2;
|
|
|
|
default:
|
|
cerr << "**unexpected operator**\n";
|
|
abort();
|
|
}
|
|
|
|
case T_literal:
|
|
case T_raw_literal:
|
|
return Result();
|
|
|
|
default:
|
|
cerr << "**invalid operand**\n";
|
|
abort();
|
|
}
|
|
|
|
return Result(); // Compiler kludge; can't get here.
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::determine_type
|
|
// Access: Public
|
|
// Description: Returns the type of the expression, if it is known,
|
|
// or NULL if the type cannot be determined.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPType *CPPExpression::
|
|
determine_type() const {
|
|
CPPType *t1 = (CPPType *)NULL;
|
|
CPPType *t2 = (CPPType *)NULL;
|
|
|
|
static CPPType *nullptr_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_nullptr));
|
|
|
|
static CPPType *int_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int));
|
|
|
|
static CPPType *unsigned_long_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_int,
|
|
CPPSimpleType::F_unsigned |
|
|
CPPSimpleType::F_long));
|
|
|
|
static CPPType *bool_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_bool));
|
|
|
|
static CPPType *float_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_double));
|
|
|
|
static CPPType *char_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char));
|
|
|
|
static CPPType *wchar_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_wchar_t));
|
|
|
|
static CPPType *char16_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char16_t));
|
|
|
|
static CPPType *char32_type =
|
|
CPPType::new_type(new CPPSimpleType(CPPSimpleType::T_char32_t));
|
|
|
|
static CPPType *char_str_type = CPPType::new_type(
|
|
new CPPPointerType(CPPType::new_type(new CPPConstType(char_type))));
|
|
|
|
static CPPType *wchar_str_type = CPPType::new_type(
|
|
new CPPPointerType(CPPType::new_type(new CPPConstType(wchar_type))));
|
|
|
|
static CPPType *char16_str_type = CPPType::new_type(
|
|
new CPPPointerType(CPPType::new_type(new CPPConstType(char16_type))));
|
|
|
|
static CPPType *char32_str_type = CPPType::new_type(
|
|
new CPPPointerType(CPPType::new_type(new CPPConstType(char32_type))));
|
|
|
|
switch (_type) {
|
|
case T_nullptr:
|
|
return nullptr_type;
|
|
|
|
case T_integer:
|
|
return int_type;
|
|
|
|
case T_real:
|
|
return float_type;
|
|
|
|
case T_string:
|
|
return char_str_type;
|
|
|
|
case T_wstring:
|
|
return wchar_str_type;
|
|
|
|
case T_u8string:
|
|
return char_str_type;
|
|
|
|
case T_u16string:
|
|
return char16_str_type;
|
|
|
|
case T_u32string:
|
|
return char32_str_type;
|
|
|
|
case T_variable:
|
|
return _u._variable->_type;
|
|
|
|
case T_function:
|
|
if (_u._fgroup->get_return_type() == (CPPType *)NULL) {
|
|
// There are multiple functions by this name that have different
|
|
// return types. We could attempt to differentiate them based
|
|
// on the parameter list, but that's a lot of work. Let's just
|
|
// give up.
|
|
return (CPPType *)NULL;
|
|
}
|
|
return _u._fgroup->_instances.front()->_type;
|
|
|
|
case T_unknown_ident:
|
|
return (CPPType *)NULL;
|
|
|
|
case T_typecast:
|
|
case T_construct:
|
|
case T_default_construct:
|
|
return _u._typecast._to;
|
|
|
|
case T_new:
|
|
case T_default_new:
|
|
return CPPType::new_type(new CPPPointerType(_u._typecast._to));
|
|
|
|
case T_sizeof:
|
|
case T_alignof:
|
|
// Note: this should actually be size_t, but that is defined as a
|
|
// typedef in parser-inc. We could try to resolve it, but that's
|
|
// hacky. Eh, it's probably not worth the effort to get this right.
|
|
return unsigned_long_type;
|
|
|
|
case T_binary_operation:
|
|
case T_trinary_operation:
|
|
assert(_u._op._op2 != NULL);
|
|
t2 = _u._op._op2->determine_type();
|
|
// Fall through
|
|
|
|
case T_unary_operation:
|
|
assert(_u._op._op1 != NULL);
|
|
t1 = _u._op._op1->determine_type();
|
|
|
|
switch (_u._op._operator) {
|
|
case UNARY_NOT:
|
|
return bool_type;
|
|
|
|
case UNARY_NEGATE:
|
|
return int_type;
|
|
|
|
case UNARY_MINUS:
|
|
return t1;
|
|
|
|
case UNARY_STAR:
|
|
case '[': // Array element reference
|
|
if (t1 != NULL) {
|
|
if (t1->as_pointer_type()) {
|
|
return t1->as_pointer_type()->_pointing_at;
|
|
}
|
|
if (t1->as_array_type()) {
|
|
return t1->as_array_type()->_element_type;
|
|
}
|
|
}
|
|
return NULL;
|
|
|
|
case UNARY_REF:
|
|
return t1;
|
|
|
|
case '*':
|
|
case '/':
|
|
case '+':
|
|
case '-':
|
|
if (t1 == NULL) {
|
|
return t2;
|
|
} else if (t2 == NULL) {
|
|
return t1;
|
|
} else if (t1->as_pointer_type()) {
|
|
if (t2->as_pointer_type()) {
|
|
return int_type;
|
|
}
|
|
return t1;
|
|
}
|
|
return elevate_type(t1, t2);
|
|
|
|
case '%':
|
|
case '|':
|
|
case '&':
|
|
case LSHIFT:
|
|
case RSHIFT:
|
|
return int_type;
|
|
|
|
case OROR:
|
|
case ANDAND:
|
|
case EQCOMPARE:
|
|
case NECOMPARE:
|
|
case LECOMPARE:
|
|
case GECOMPARE:
|
|
case '<':
|
|
case '>':
|
|
return bool_type;
|
|
|
|
case '?':
|
|
return t2;
|
|
|
|
case '.':
|
|
case POINTSAT:
|
|
return NULL;
|
|
|
|
case 'f': // Function evaluation
|
|
if (t1 != NULL) {
|
|
CPPFunctionType *ftype = t1->as_function_type();
|
|
if (ftype != (CPPFunctionType *)NULL) {
|
|
return ftype->_return_type;
|
|
}
|
|
}
|
|
return NULL;
|
|
|
|
case ',':
|
|
return t2;
|
|
|
|
default:
|
|
cerr << "**unexpected operator**\n";
|
|
abort();
|
|
}
|
|
|
|
case T_literal:
|
|
case T_raw_literal:
|
|
if (_u._literal._operator != NULL) {
|
|
CPPType *type = _u._literal._operator->_type;
|
|
|
|
CPPFunctionType *ftype = type->as_function_type();
|
|
if (ftype != (CPPFunctionType *)NULL) {
|
|
return ftype->_return_type;
|
|
}
|
|
}
|
|
return NULL;
|
|
|
|
default:
|
|
cerr << "**invalid operand**\n";
|
|
abort();
|
|
}
|
|
|
|
return NULL; // Compiler kludge; can't get here.
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::is_fully_specified
|
|
// Access: Public, Virtual
|
|
// Description: Returns true if this declaration is an actual,
|
|
// factual declaration, or false if some part of the
|
|
// declaration depends on a template parameter which has
|
|
// not yet been instantiated.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool CPPExpression::
|
|
is_fully_specified() const {
|
|
if (!CPPDeclaration::is_fully_specified()) {
|
|
return false;
|
|
}
|
|
|
|
switch (_type) {
|
|
case T_nullptr:
|
|
case T_integer:
|
|
case T_real:
|
|
case T_string:
|
|
case T_wstring:
|
|
case T_u8string:
|
|
case T_u16string:
|
|
case T_u32string:
|
|
return false;
|
|
|
|
case T_variable:
|
|
return _u._variable->is_fully_specified();
|
|
|
|
case T_function:
|
|
return _u._fgroup->is_fully_specified();
|
|
|
|
case T_unknown_ident:
|
|
return _u._ident->is_fully_specified();
|
|
|
|
case T_typecast:
|
|
case T_construct:
|
|
case T_new:
|
|
return (_u._typecast._to->is_fully_specified() &&
|
|
_u._typecast._op1->is_fully_specified());
|
|
|
|
case T_default_construct:
|
|
case T_default_new:
|
|
case T_sizeof:
|
|
case T_alignof:
|
|
return _u._typecast._to->is_fully_specified();
|
|
|
|
case T_trinary_operation:
|
|
if (!_u._op._op3->is_fully_specified()) {
|
|
return false;
|
|
}
|
|
// Fall through
|
|
|
|
case T_binary_operation:
|
|
if (!_u._op._op2->is_fully_specified()) {
|
|
return false;
|
|
}
|
|
// Fall through
|
|
|
|
case T_unary_operation:
|
|
return _u._op._op1->is_fully_specified();
|
|
|
|
case T_literal:
|
|
return _u._literal._value->is_fully_specified() &&
|
|
_u._literal._operator->is_fully_specified();
|
|
|
|
case T_raw_literal:
|
|
return _u._literal._value->is_fully_specified();
|
|
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::substitute_decl
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPDeclaration *CPPExpression::
|
|
substitute_decl(CPPDeclaration::SubstDecl &subst,
|
|
CPPScope *current_scope, CPPScope *global_scope) {
|
|
CPPDeclaration *top =
|
|
CPPDeclaration::substitute_decl(subst, current_scope, global_scope);
|
|
if (top != this) {
|
|
return top;
|
|
}
|
|
|
|
CPPExpression *rep = new CPPExpression(*this);
|
|
bool any_changed = false;
|
|
CPPDeclaration *decl;
|
|
|
|
switch (_type) {
|
|
case T_variable:
|
|
decl = _u._variable->substitute_decl(subst, current_scope, global_scope);
|
|
if (decl != rep->_u._variable) {
|
|
if (decl->as_instance()) {
|
|
// Replacing the variable reference with another variable reference.
|
|
rep->_u._variable = decl->as_instance();
|
|
any_changed = true;
|
|
|
|
} else if (decl->as_expression()) {
|
|
// Replacing the variable reference with an expression.
|
|
delete rep;
|
|
rep = decl->as_expression();
|
|
any_changed = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case T_unknown_ident:
|
|
rep->_u._ident = _u._ident->substitute_decl(subst, current_scope, global_scope);
|
|
any_changed = any_changed || (rep->_u._ident != _u._ident);
|
|
|
|
// See if we can define it now.
|
|
decl = rep->_u._ident->find_symbol(current_scope, global_scope, subst);
|
|
if (decl != NULL) {
|
|
CPPInstance *inst = decl->as_instance();
|
|
if (inst != NULL) {
|
|
rep->_type = T_variable;
|
|
rep->_u._variable = inst;
|
|
any_changed = true;
|
|
|
|
decl = inst->substitute_decl(subst, current_scope, global_scope);
|
|
if (decl != inst) {
|
|
if (decl->as_instance()) {
|
|
// Replacing the variable reference with another variable reference.
|
|
rep->_u._variable = decl->as_instance();
|
|
|
|
} else if (decl->as_expression()) {
|
|
// Replacing the variable reference with an expression.
|
|
delete rep;
|
|
rep = decl->as_expression();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
CPPFunctionGroup *fgroup = decl->as_function_group();
|
|
if (fgroup != NULL) {
|
|
rep->_type = T_function;
|
|
rep->_u._fgroup = fgroup;
|
|
any_changed = true;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case T_typecast:
|
|
case T_construct:
|
|
case T_new:
|
|
rep->_u._typecast._op1 =
|
|
_u._typecast._op1->substitute_decl(subst, current_scope, global_scope)
|
|
->as_expression();
|
|
any_changed = any_changed || (rep->_u._typecast._op1 != _u._typecast._op1);
|
|
// fall through
|
|
|
|
case T_default_construct:
|
|
case T_default_new:
|
|
case T_sizeof:
|
|
case T_alignof:
|
|
rep->_u._typecast._to =
|
|
_u._typecast._to->substitute_decl(subst, current_scope, global_scope)
|
|
->as_type();
|
|
any_changed = any_changed || (rep->_u._typecast._to != _u._typecast._to);
|
|
break;
|
|
|
|
case T_trinary_operation:
|
|
rep->_u._op._op3 =
|
|
_u._op._op3->substitute_decl(subst, current_scope, global_scope)
|
|
->as_expression();
|
|
any_changed = any_changed || (rep->_u._op._op3 != _u._op._op3);
|
|
// fall through
|
|
|
|
case T_binary_operation:
|
|
rep->_u._op._op2 =
|
|
_u._op._op2->substitute_decl(subst, current_scope, global_scope)
|
|
->as_expression();
|
|
any_changed = any_changed || (rep->_u._op._op2 != _u._op._op2);
|
|
// fall through
|
|
|
|
case T_unary_operation:
|
|
rep->_u._op._op1 =
|
|
_u._op._op1->substitute_decl(subst, current_scope, global_scope)
|
|
->as_expression();
|
|
any_changed = any_changed || (rep->_u._op._op1 != _u._op._op1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!any_changed) {
|
|
delete rep;
|
|
rep = this;
|
|
}
|
|
subst.insert(SubstDecl::value_type(this, rep));
|
|
return rep;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::is_tbd
|
|
// Access: Public
|
|
// Description: Returns true if any type within the expression list is
|
|
// a CPPTBDType and thus isn't fully determined right
|
|
// now.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool CPPExpression::
|
|
is_tbd() const {
|
|
switch (_type) {
|
|
case T_variable:
|
|
if (_u._variable->_type != NULL &&
|
|
_u._variable->_initializer != NULL) {
|
|
CPPConstType *const_type = _u._variable->_type->as_const_type();
|
|
if (const_type != NULL) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
case T_unknown_ident:
|
|
return true;
|
|
|
|
case T_typecast:
|
|
case T_construct:
|
|
case T_new:
|
|
case T_default_construct:
|
|
case T_default_new:
|
|
case T_sizeof:
|
|
case T_alignof:
|
|
return _u._typecast._to->is_tbd();
|
|
|
|
case T_trinary_operation:
|
|
if (_u._op._op3->is_tbd()) {
|
|
return true;
|
|
}
|
|
// fall through
|
|
|
|
case T_binary_operation:
|
|
if (_u._op._op2->is_tbd()) {
|
|
return true;
|
|
}
|
|
// fall through
|
|
|
|
case T_unary_operation:
|
|
if (_u._op._op1->is_tbd()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::output
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void CPPExpression::
|
|
output(ostream &out, int indent_level, CPPScope *scope, bool) const {
|
|
switch (_type) {
|
|
case T_nullptr:
|
|
out << "nullptr";
|
|
break;
|
|
|
|
case T_integer:
|
|
out << _u._integer;
|
|
break;
|
|
|
|
case T_real:
|
|
{
|
|
// We use our own dtoa implementation here because it guarantees
|
|
// to never format the number as an integer.
|
|
char buffer[32];
|
|
pdtoa(_u._real, buffer);
|
|
out << buffer;
|
|
}
|
|
break;
|
|
|
|
case T_string:
|
|
case T_wstring:
|
|
case T_u8string:
|
|
case T_u16string:
|
|
case T_u32string:
|
|
{
|
|
switch (_type) {
|
|
case T_wstring:
|
|
out << 'L';
|
|
break;
|
|
case T_u8string:
|
|
out << "u8";
|
|
break;
|
|
case T_u16string:
|
|
out << "u";
|
|
break;
|
|
case T_u32string:
|
|
out << "U";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// We don't really care about preserving the encoding for now.
|
|
out << '"';
|
|
string::const_iterator si;
|
|
for (si = _str.begin(); si != _str.end(); ++si) {
|
|
switch (*si) {
|
|
case '\n':
|
|
out << "\\n";
|
|
break;
|
|
|
|
case '\t':
|
|
out << "\\t";
|
|
break;
|
|
|
|
case '\r':
|
|
out << "\\r";
|
|
break;
|
|
|
|
case '\a':
|
|
out << "\\a";
|
|
break;
|
|
|
|
case '"':
|
|
out << "\\\"";
|
|
break;
|
|
|
|
default:
|
|
if (isprint(*si)) {
|
|
out << *si;
|
|
} else {
|
|
out << '\\' << oct << setw(3) << setfill('0') << (int)(*si)
|
|
<< dec << setw(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
out << '"';
|
|
break;
|
|
|
|
case T_variable:
|
|
// We can just refer to the variable by name, except if it's a
|
|
// private constant, in which case we have to compute the value,
|
|
// since we may have to use it in generated code.
|
|
if (_u._variable->_type != NULL &&
|
|
_u._variable->_initializer != NULL &&
|
|
_u._variable->_vis > V_public) {
|
|
// A const variable. Fetch its assigned value.
|
|
CPPConstType *const_type = _u._variable->_type->as_const_type();
|
|
if (const_type != NULL) {
|
|
_u._variable->_initializer->output(out, indent_level, scope, false);
|
|
break;
|
|
}
|
|
}
|
|
_u._variable->_ident->output(out, scope);
|
|
break;
|
|
|
|
case T_function:
|
|
out << _u._fgroup->_name;
|
|
break;
|
|
|
|
case T_unknown_ident:
|
|
_u._ident->output(out, scope);
|
|
break;
|
|
|
|
case T_typecast:
|
|
out << "(";
|
|
_u._typecast._to->output(out, indent_level, scope, false);
|
|
out << ")(";
|
|
_u._typecast._op1->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case T_construct:
|
|
_u._typecast._to->output(out, indent_level, scope, false);
|
|
out << "(";
|
|
_u._typecast._op1->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case T_default_construct:
|
|
_u._typecast._to->output(out, indent_level, scope, false);
|
|
out << "()";
|
|
break;
|
|
|
|
case T_new:
|
|
out << "(new ";
|
|
_u._typecast._to->output(out, indent_level, scope, false);
|
|
out << "(";
|
|
_u._typecast._op1->output(out, indent_level, scope, false);
|
|
out << "))";
|
|
break;
|
|
|
|
case T_default_new:
|
|
out << "(new ";
|
|
_u._typecast._to->output(out, indent_level, scope, false);
|
|
out << "())";
|
|
break;
|
|
|
|
case T_sizeof:
|
|
out << "sizeof(";
|
|
_u._typecast._to->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case T_alignof:
|
|
out << "alignof(";
|
|
_u._typecast._to->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case T_unary_operation:
|
|
switch (_u._op._operator) {
|
|
case UNARY_NOT:
|
|
out << "(! ";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case UNARY_NEGATE:
|
|
out << "(~ ";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case UNARY_MINUS:
|
|
out << '-';
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
break;
|
|
|
|
case UNARY_STAR:
|
|
out << "(* ";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case UNARY_REF:
|
|
out << "(& ";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case 'f': // Function evaluation, no parameters.
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << "())";
|
|
break;
|
|
|
|
default:
|
|
out << "(" << (char)_u._op._operator << " ";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case T_binary_operation:
|
|
switch (_u._op._operator) {
|
|
case OROR:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " || ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case ANDAND:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " && ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case EQCOMPARE:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " == ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case NECOMPARE:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " != ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case LECOMPARE:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " <= ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case GECOMPARE:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " >= ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case LSHIFT:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " << ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case RSHIFT:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " >> ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case '.':
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << ".";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case POINTSAT:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << "->";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case '[': // Array element reference
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << "[";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << "])";
|
|
break;
|
|
|
|
case 'f': // Function evaluation
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << "(";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << "))";
|
|
break;
|
|
|
|
case ',': // Comma, no parens are used
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << ", ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
break;
|
|
|
|
default:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " " << (char)_u._op._operator << " ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
}
|
|
break;
|
|
|
|
case T_trinary_operation:
|
|
out << "(";
|
|
_u._op._op1->output(out, indent_level, scope, false);
|
|
out << " ? ";
|
|
_u._op._op2->output(out, indent_level, scope, false);
|
|
out << " : ";
|
|
_u._op._op3->output(out, indent_level, scope, false);
|
|
out << ")";
|
|
break;
|
|
|
|
case T_literal:
|
|
_u._literal._value->output(out, indent_level, scope, false);
|
|
if (_u._literal._operator != NULL) {
|
|
string name = _u._literal._operator->get_simple_name();
|
|
assert(name.substr(0, 12) == "operator \"\" ");
|
|
out << name.substr(12);
|
|
}
|
|
break;
|
|
|
|
case T_raw_literal:
|
|
out << _str;
|
|
if (_u._literal._operator != NULL) {
|
|
string name = _u._literal._operator->get_simple_name();
|
|
assert(name.substr(0, 12) == "operator \"\" ");
|
|
out << name.substr(12);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
out << "(** invalid operand type " << (int)_type << " **)";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::get_subtype
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPDeclaration::SubType CPPExpression::
|
|
get_subtype() const {
|
|
return ST_expression;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::as_expression
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPExpression *CPPExpression::
|
|
as_expression() {
|
|
return this;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::elevate_type
|
|
// Access: Public, Static
|
|
// Description: Returns the most general of the two given types.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPPType *CPPExpression::
|
|
elevate_type(CPPType *t1, CPPType *t2) {
|
|
CPPSimpleType *st1 = t1->as_simple_type();
|
|
CPPSimpleType *st2 = t2->as_simple_type();
|
|
|
|
if (st1 == NULL || st2 == NULL) {
|
|
// Nothing we can do about this. Who knows?
|
|
return NULL;
|
|
}
|
|
|
|
if (st1->_type == st2->_type) {
|
|
// They have the same type, so return the one with the largest
|
|
// flag bits.
|
|
if (st1->_flags & CPPSimpleType::F_longlong) {
|
|
return st1;
|
|
} else if (st2->_flags & CPPSimpleType::F_longlong) {
|
|
return st2;
|
|
} else if (st1->_flags & CPPSimpleType::F_long) {
|
|
return st1;
|
|
} else if (st2->_flags & CPPSimpleType::F_long) {
|
|
return st2;
|
|
} else if (st1->_flags & CPPSimpleType::F_short) {
|
|
return st2;
|
|
} else if (st2->_flags & CPPSimpleType::F_short) {
|
|
return st1;
|
|
}
|
|
return st1;
|
|
}
|
|
|
|
// They have different types.
|
|
if (st1->_type == CPPSimpleType::T_float ||
|
|
st1->_type == CPPSimpleType::T_double) {
|
|
return st1;
|
|
} else if (st2->_type == CPPSimpleType::T_float ||
|
|
st2->_type == CPPSimpleType::T_double) {
|
|
return st2;
|
|
} else if (st1->_type == CPPSimpleType::T_int) {
|
|
return st1;
|
|
} else if (st2->_type == CPPSimpleType::T_int) {
|
|
return st2;
|
|
} else if (st1->_type == CPPSimpleType::T_bool) {
|
|
return st1;
|
|
} else if (st2->_type == CPPSimpleType::T_bool) {
|
|
return st2;
|
|
}
|
|
return st1;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::is_equal
|
|
// Access: Protected, Virtual
|
|
// Description: Called by CPPDeclaration to determine whether this
|
|
// expr is equivalent to another expr.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool CPPExpression::
|
|
is_equal(const CPPDeclaration *other) const {
|
|
const CPPExpression *ot = ((CPPDeclaration *)other)->as_expression();
|
|
assert(ot != NULL);
|
|
|
|
if (_type != ot->_type) {
|
|
return false;
|
|
}
|
|
|
|
switch (_type) {
|
|
case T_nullptr:
|
|
return true;
|
|
|
|
case T_integer:
|
|
return _u._integer == ot->_u._integer;
|
|
|
|
case T_real:
|
|
return _u._real == ot->_u._real;
|
|
|
|
case T_string:
|
|
case T_wstring:
|
|
case T_u8string:
|
|
case T_u16string:
|
|
case T_u32string:
|
|
return _str == ot->_str;
|
|
|
|
case T_variable:
|
|
return _u._variable == ot->_u._variable;
|
|
|
|
case T_function:
|
|
return _u._fgroup == ot->_u._fgroup;
|
|
|
|
case T_unknown_ident:
|
|
return *_u._ident == *ot->_u._ident;
|
|
|
|
case T_typecast:
|
|
case T_construct:
|
|
case T_new:
|
|
return _u._typecast._to == ot->_u._typecast._to &&
|
|
*_u._typecast._op1 == *ot->_u._typecast._op1;
|
|
|
|
case T_default_construct:
|
|
case T_default_new:
|
|
case T_sizeof:
|
|
case T_alignof:
|
|
return _u._typecast._to == ot->_u._typecast._to;
|
|
|
|
case T_unary_operation:
|
|
return *_u._op._op1 == *ot->_u._op._op1;
|
|
|
|
case T_binary_operation:
|
|
return *_u._op._op1 == *ot->_u._op._op1 &&
|
|
*_u._op._op2 == *ot->_u._op._op2;
|
|
|
|
case T_trinary_operation:
|
|
return *_u._op._op1 == *ot->_u._op._op1 &&
|
|
*_u._op._op2 == *ot->_u._op._op2;
|
|
|
|
case T_literal:
|
|
return *_u._literal._value == *ot->_u._literal._value &&
|
|
_u._literal._operator == ot->_u._literal._operator;
|
|
|
|
case T_raw_literal:
|
|
return _str == ot->_str &&
|
|
_u._literal._operator == ot->_u._literal._operator;
|
|
|
|
default:
|
|
cerr << "(** invalid operand type " << (int)_type << " **)";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: CPPExpression::is_less
|
|
// Access: Protected, Virtual
|
|
// Description: Called by CPPDeclaration to determine whether this
|
|
// expr should be ordered before another expr of the
|
|
// same type, in an arbitrary but fixed ordering.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool CPPExpression::
|
|
is_less(const CPPDeclaration *other) const {
|
|
const CPPExpression *ot = ((CPPDeclaration *)other)->as_expression();
|
|
assert(ot != NULL);
|
|
|
|
if (_type != ot->_type) {
|
|
return (int)_type < (int)ot->_type;
|
|
}
|
|
|
|
switch (_type) {
|
|
case T_nullptr:
|
|
return false;
|
|
|
|
case T_integer:
|
|
return _u._integer < ot->_u._integer;
|
|
|
|
case T_real:
|
|
return _u._real < ot->_u._real;
|
|
|
|
case T_string:
|
|
case T_wstring:
|
|
case T_u8string:
|
|
case T_u16string:
|
|
case T_u32string:
|
|
return _str < ot->_str;
|
|
|
|
case T_variable:
|
|
return _u._variable < ot->_u._variable;
|
|
|
|
case T_function:
|
|
return *_u._fgroup < *ot->_u._fgroup;
|
|
|
|
case T_unknown_ident:
|
|
return *_u._ident < *ot->_u._ident;
|
|
|
|
case T_typecast:
|
|
case T_construct:
|
|
case T_new:
|
|
if (_u._typecast._to != ot->_u._typecast._to) {
|
|
return _u._typecast._to < ot->_u._typecast._to;
|
|
}
|
|
return *_u._typecast._op1 < *ot->_u._typecast._op1;
|
|
|
|
case T_default_construct:
|
|
case T_default_new:
|
|
case T_sizeof:
|
|
case T_alignof:
|
|
return _u._typecast._to < ot->_u._typecast._to;
|
|
|
|
case T_trinary_operation:
|
|
if (*_u._op._op3 != *ot->_u._op._op3) {
|
|
return *_u._op._op3 < *ot->_u._op._op3;
|
|
}
|
|
// Fall through
|
|
|
|
case T_binary_operation:
|
|
if (*_u._op._op2 != *ot->_u._op._op2) {
|
|
return *_u._op._op2 < *ot->_u._op._op2;
|
|
}
|
|
// Fall through
|
|
|
|
case T_unary_operation:
|
|
return *_u._op._op1 < *ot->_u._op._op1;
|
|
|
|
case T_literal:
|
|
if (_u._literal._operator != ot->_u._literal._operator) {
|
|
return _u._literal._operator < ot->_u._literal._operator;
|
|
}
|
|
return *_u._literal._value < *ot->_u._literal._value;
|
|
|
|
case T_raw_literal:
|
|
if (_u._literal._operator != ot->_u._literal._operator) {
|
|
return _u._literal._operator < ot->_u._literal._operator;
|
|
}
|
|
return _str < ot->_str;
|
|
|
|
default:
|
|
cerr << "(** invalid operand type " << (int)_type << " **)";
|
|
}
|
|
|
|
return false;
|
|
}
|