Debugger: Add basic support for types in expressions.
SourceLanguage/CLanguageFamily/ExpressionEvaluationJob: - Add TeamTypeInformation parameter to EvaluateExpression() hook. Adjust implementing subclasses and callers accordingly. CLanguageExpressionEvaluator: - Add class InternalVariableID for representing intermediate variables generated while parsing an expression. - When parsing an identifier, if we were passed in a type information object, then first attempt to resolve the name as a type. If not matched, then fall through to attempting to match it to a value node as before. - When parsing an atom, check if it resulted in a type. If it did, and there still remains more of the expression to parse, then parse the result to see what value/variable to try to apply the typecast to. If the result is a primitive, generate an appropriate internal variable + value node child for it, otherwise use the target variable's child. Then, attempt to typecast it as requested. - Simplify _EatToken(). - If the final result of an expression is a type, configure the result object accordingly. As a result of all the above, an expression can now resolve to a type, allowing the evaluator to take over the duties of parsing the desired type for a typecast request in the variables view, and in addition, expressions themselves can now contain typecasts, which opens up quite a few new possibilities.
This commit is contained in:
parent
9d6cd8b210
commit
af76b51633
@ -91,7 +91,7 @@ ExpressionEvaluationJob::Do()
|
||||
|
||||
ValueNode* neededNode = NULL;
|
||||
result = fLanguage->EvaluateExpression(fExpressionInfo->Expression(),
|
||||
fManager, fResultValue, neededNode);
|
||||
fManager, fTeam->GetTeamTypeInformation(), fResultValue, neededNode);
|
||||
if (neededNode != NULL) {
|
||||
result = ResolveNodeValue(neededNode);
|
||||
if (State() == JOB_STATE_WAITING)
|
||||
|
@ -30,8 +30,8 @@ SourceLanguage::ParseTypeExpression(const BString& expression,
|
||||
|
||||
status_t
|
||||
SourceLanguage::EvaluateExpression(const BString& expression,
|
||||
ValueNodeManager* manager, ExpressionResult*& _resultValue,
|
||||
ValueNode*& _neededNode)
|
||||
ValueNodeManager* manager, TeamTypeInformation* info,
|
||||
ExpressionResult*& _resultValue, ValueNode*& _neededNode)
|
||||
{
|
||||
return B_NOT_SUPPORTED;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
|
||||
virtual status_t EvaluateExpression(const BString& expression,
|
||||
ValueNodeManager* manager,
|
||||
TeamTypeInformation* info,
|
||||
ExpressionResult*& _output,
|
||||
ValueNode*& _neededNode);
|
||||
};
|
||||
|
@ -18,11 +18,18 @@
|
||||
#include "CLanguageTokenizer.h"
|
||||
#include "ExpressionInfo.h"
|
||||
#include "FloatValue.h"
|
||||
#include "IntegerFormatter.h"
|
||||
#include "IntegerValue.h"
|
||||
#include "ObjectID.h"
|
||||
#include "StackFrame.h"
|
||||
#include "SyntheticPrimitiveType.h"
|
||||
#include "TeamTypeInformation.h"
|
||||
#include "Thread.h"
|
||||
#include "Type.h"
|
||||
#include "TypeHandlerRoster.h"
|
||||
#include "TypeLookupConstraints.h"
|
||||
#include "Value.h"
|
||||
#include "ValueLocation.h"
|
||||
#include "ValueNode.h"
|
||||
#include "ValueNodeManager.h"
|
||||
#include "Variable.h"
|
||||
@ -142,6 +149,42 @@ static BString TokenTypeToString(int32 type)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - CLanguageExpressionEvaluator::InternalVariableID
|
||||
|
||||
|
||||
class CLanguageExpressionEvaluator::InternalVariableID : public ObjectID {
|
||||
public:
|
||||
InternalVariableID(const BVariant& value)
|
||||
:
|
||||
fValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~InternalVariableID()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool operator==(const ObjectID& other) const
|
||||
{
|
||||
const InternalVariableID* otherID
|
||||
= dynamic_cast<const InternalVariableID*>(&other);
|
||||
if (otherID == NULL)
|
||||
return false;
|
||||
|
||||
return fValue == otherID->fValue;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual uint32 ComputeHashValue() const
|
||||
{
|
||||
return *(uint32*)(&fValue);
|
||||
}
|
||||
|
||||
private:
|
||||
BVariant fValue;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - CLanguageExpressionEvaluator::Operand
|
||||
|
||||
|
||||
@ -1394,6 +1437,7 @@ private:
|
||||
CLanguageExpressionEvaluator::CLanguageExpressionEvaluator()
|
||||
:
|
||||
fTokenizer(new Tokenizer()),
|
||||
fTypeInfo(NULL),
|
||||
fNodeManager(NULL)
|
||||
{
|
||||
}
|
||||
@ -1407,9 +1451,10 @@ CLanguageExpressionEvaluator::~CLanguageExpressionEvaluator()
|
||||
|
||||
ExpressionResult*
|
||||
CLanguageExpressionEvaluator::Evaluate(const char* expressionString,
|
||||
ValueNodeManager* manager)
|
||||
ValueNodeManager* manager, TeamTypeInformation* info)
|
||||
{
|
||||
fNodeManager = manager;
|
||||
fTypeInfo = info;
|
||||
fTokenizer->SetTo(expressionString);
|
||||
|
||||
Operand value = _ParseSum();
|
||||
@ -1420,26 +1465,26 @@ CLanguageExpressionEvaluator::Evaluate(const char* expressionString,
|
||||
ExpressionResult* result = new(std::nothrow)ExpressionResult;
|
||||
if (result != NULL) {
|
||||
BReference<ExpressionResult> resultReference(result, true);
|
||||
Value* outputValue = NULL;
|
||||
BVariant primitive = value.PrimitiveValue();
|
||||
if (primitive.IsInteger())
|
||||
outputValue = new(std::nothrow) IntegerValue(primitive);
|
||||
else if (primitive.IsFloat()) {
|
||||
outputValue = new(std::nothrow) FloatValue(
|
||||
primitive.ToDouble());
|
||||
}
|
||||
|
||||
BReference<Value> valueReference;
|
||||
if (outputValue != NULL)
|
||||
valueReference.SetTo(outputValue, true);
|
||||
|
||||
if (value.Kind() == OPERAND_KIND_PRIMITIVE) {
|
||||
if (outputValue == NULL)
|
||||
return NULL;
|
||||
Value* outputValue = NULL;
|
||||
BVariant primitive = value.PrimitiveValue();
|
||||
if (primitive.IsInteger())
|
||||
outputValue = new(std::nothrow) IntegerValue(primitive);
|
||||
else if (primitive.IsFloat()) {
|
||||
outputValue = new(std::nothrow) FloatValue(
|
||||
primitive.ToDouble());
|
||||
}
|
||||
|
||||
result->SetToPrimitive(outputValue);
|
||||
BReference<Value> valueReference;
|
||||
if (outputValue != NULL) {
|
||||
valueReference.SetTo(outputValue, true);
|
||||
result->SetToPrimitive(outputValue);
|
||||
} else
|
||||
return NULL;
|
||||
} else if (value.Kind() == OPERAND_KIND_VALUE_NODE)
|
||||
result->SetToValueNode(value.GetValueNode()->NodeChild());
|
||||
else if (value.Kind() == OPERAND_KIND_TYPE)
|
||||
result->SetToType(value.GetType());
|
||||
|
||||
resultReference.Detach();
|
||||
}
|
||||
@ -1638,14 +1683,31 @@ CLanguageExpressionEvaluator::Operand
|
||||
CLanguageExpressionEvaluator::_ParseIdentifier(ValueNode* parentNode)
|
||||
{
|
||||
Token token = fTokenizer->NextToken();
|
||||
const BString& identifierName = token.string;
|
||||
|
||||
if (fTypeInfo != NULL) {
|
||||
Type* resultType = NULL;
|
||||
status_t error = fTypeInfo->LookupTypeByName(identifierName,
|
||||
TypeLookupConstraints(), resultType);
|
||||
if (error == B_OK) {
|
||||
BReference<Type> typeReference(resultType, true);
|
||||
return _ParseType(resultType);
|
||||
} else if (error != B_ENTRY_NOT_FOUND) {
|
||||
BString errorMessage;
|
||||
errorMessage.SetToFormat("Failed to look up type name '%s': %"
|
||||
B_PRId32 ".", identifierName.String(), error);
|
||||
throw ParseException(errorMessage.String(), token.position);
|
||||
}
|
||||
|
||||
// we didn't recognize the identifier as a type name, fall through
|
||||
// and see if it's possibly a value
|
||||
}
|
||||
|
||||
if (fNodeManager == NULL) {
|
||||
throw ParseException("Identifiers not resolvable without manager.",
|
||||
token.position);
|
||||
}
|
||||
|
||||
const BString& identifierName = token.string;
|
||||
|
||||
ValueNodeContainer* container = fNodeManager->GetContainer();
|
||||
AutoLocker<ValueNodeContainer> containerLocker(container);
|
||||
|
||||
@ -1725,7 +1787,7 @@ CLanguageExpressionEvaluator::_ParseAtom()
|
||||
{
|
||||
Token token = fTokenizer->NextToken();
|
||||
if (token.type == TOKEN_END_OF_LINE)
|
||||
throw ParseException("unexpected end of expression", token.position);
|
||||
throw ParseException("Unexpected end of expression", token.position);
|
||||
|
||||
Operand value;
|
||||
|
||||
@ -1741,6 +1803,41 @@ CLanguageExpressionEvaluator::_ParseAtom()
|
||||
_EatToken(TOKEN_CLOSING_PAREN);
|
||||
}
|
||||
|
||||
if (value.Kind() == OPERAND_KIND_TYPE) {
|
||||
token = fTokenizer->NextToken();
|
||||
if (token.type == TOKEN_END_OF_LINE)
|
||||
return value;
|
||||
|
||||
Type* castType = value.GetType();
|
||||
// if our evaluated result was a type, and there still remain
|
||||
// further tokens to evaluate, then this is a typecast for
|
||||
// a subsequent expression. Attempt to evaluate it, and then
|
||||
// apply the cast to the result.
|
||||
fTokenizer->RewindToken();
|
||||
value = _ParseSum();
|
||||
ValueNodeChild* child = NULL;
|
||||
if (value.Kind() != OPERAND_KIND_PRIMITIVE
|
||||
&& value.Kind() != OPERAND_KIND_VALUE_NODE) {
|
||||
throw ParseException("Expected value or variable expression after"
|
||||
" typecast.", token.position);
|
||||
}
|
||||
|
||||
if (value.Kind() == OPERAND_KIND_VALUE_NODE)
|
||||
child = value.GetValueNode()->NodeChild();
|
||||
else if (value.Kind() == OPERAND_KIND_PRIMITIVE)
|
||||
_GetNodeChildForPrimitive(token, value.PrimitiveValue(), child);
|
||||
|
||||
ValueNode* newNode = NULL;
|
||||
status_t error = TypeHandlerRoster::Default()->CreateValueNode(child,
|
||||
castType, newNode);
|
||||
if (error != B_OK) {
|
||||
throw ParseException("Unable to create value node for typecast"
|
||||
" operation.", token.position);
|
||||
}
|
||||
child->SetNode(newNode);
|
||||
value.SetTo(newNode);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -1760,28 +1857,6 @@ CLanguageExpressionEvaluator::_EatToken(int32 type)
|
||||
expected = "a constant";
|
||||
break;
|
||||
|
||||
case TOKEN_PLUS:
|
||||
case TOKEN_MINUS:
|
||||
case TOKEN_STAR:
|
||||
case TOKEN_MODULO:
|
||||
case TOKEN_POWER:
|
||||
case TOKEN_OPENING_PAREN:
|
||||
case TOKEN_CLOSING_PAREN:
|
||||
case TOKEN_LOGICAL_AND:
|
||||
case TOKEN_BITWISE_AND:
|
||||
case TOKEN_LOGICAL_OR:
|
||||
case TOKEN_BITWISE_OR:
|
||||
case TOKEN_LOGICAL_NOT:
|
||||
case TOKEN_BITWISE_NOT:
|
||||
case TOKEN_EQ:
|
||||
case TOKEN_NE:
|
||||
case TOKEN_GT:
|
||||
case TOKEN_GE:
|
||||
case TOKEN_LT:
|
||||
case TOKEN_LE:
|
||||
expected << "'" << TokenTypeToString(type) << "'";
|
||||
break;
|
||||
|
||||
case TOKEN_SLASH:
|
||||
expected = "'/', '\\', or ':'";
|
||||
break;
|
||||
@ -1789,7 +1864,12 @@ CLanguageExpressionEvaluator::_EatToken(int32 type)
|
||||
case TOKEN_END_OF_LINE:
|
||||
expected = "'\\n'";
|
||||
break;
|
||||
|
||||
default:
|
||||
expected << "'" << TokenTypeToString(type) << "'";
|
||||
break;
|
||||
}
|
||||
|
||||
BString temp;
|
||||
temp << "Expected " << expected.String() << " got '" << token.string
|
||||
<< "'";
|
||||
@ -1798,6 +1878,76 @@ CLanguageExpressionEvaluator::_EatToken(int32 type)
|
||||
}
|
||||
|
||||
|
||||
CLanguageExpressionEvaluator::Operand
|
||||
CLanguageExpressionEvaluator::_ParseType(Type* baseType)
|
||||
{
|
||||
BReference<Type> typeReference;
|
||||
Type* finalType = baseType;
|
||||
|
||||
bool arraySpecifierEncountered = false;
|
||||
status_t error;
|
||||
for (;;) {
|
||||
Token token = fTokenizer->NextToken();
|
||||
if (token.type == TOKEN_STAR || token.type == TOKEN_BITWISE_AND) {
|
||||
if (arraySpecifierEncountered)
|
||||
break;
|
||||
|
||||
address_type_kind addressKind = (token.type == TOKEN_STAR)
|
||||
? DERIVED_TYPE_POINTER : DERIVED_TYPE_REFERENCE;
|
||||
AddressType* derivedType = NULL;
|
||||
error = finalType->CreateDerivedAddressType(addressKind,
|
||||
derivedType);
|
||||
if (error != B_OK) {
|
||||
BString errorMessage;
|
||||
errorMessage.SetToFormat("Failed to create derived address"
|
||||
" type %d for base type %s: %s (%" B_PRId32 ")",
|
||||
addressKind, finalType->Name().String(), strerror(error),
|
||||
error);
|
||||
throw ParseException(errorMessage, token.position);
|
||||
}
|
||||
|
||||
finalType = derivedType;
|
||||
typeReference.SetTo(finalType, true);
|
||||
} else if (token.type == TOKEN_OPENING_SQUARE_BRACKET) {
|
||||
Operand indexSize = _ParseSum();
|
||||
if (indexSize.Kind() == OPERAND_KIND_TYPE) {
|
||||
throw ParseException("Cannot specify type name as array"
|
||||
" subscript.", token.position);
|
||||
}
|
||||
|
||||
_EatToken(TOKEN_CLOSING_SQUARE_BRACKET);
|
||||
|
||||
uint32 resolvedSize = indexSize.PrimitiveValue().ToUInt32();
|
||||
if (resolvedSize == 0) {
|
||||
throw ParseException("Non-zero array size required in type"
|
||||
" specifier.", token.position);
|
||||
}
|
||||
|
||||
ArrayType* derivedType = NULL;
|
||||
error = finalType->CreateDerivedArrayType(0, resolvedSize, true,
|
||||
derivedType);
|
||||
if (error != B_OK) {
|
||||
BString errorMessage;
|
||||
errorMessage.SetToFormat("Failed to create derived array type"
|
||||
" of size %" B_PRIu32 " for base type %s: %s (%"
|
||||
B_PRId32 ")", resolvedSize, finalType->Name().String(),
|
||||
strerror(error), error);
|
||||
throw ParseException(errorMessage, token.position);
|
||||
}
|
||||
|
||||
arraySpecifierEncountered = true;
|
||||
finalType = derivedType;
|
||||
typeReference.SetTo(finalType, true);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
typeReference.Detach();
|
||||
fTokenizer->RewindToken();
|
||||
return Operand(finalType);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CLanguageExpressionEvaluator::_RequestValueIfNeeded(
|
||||
const Token& token, ValueNodeChild* child)
|
||||
@ -1832,3 +1982,59 @@ CLanguageExpressionEvaluator::_RequestValueIfNeeded(
|
||||
throw ParseException(errorMessage, token.position);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CLanguageExpressionEvaluator::_GetNodeChildForPrimitive(const Token& token,
|
||||
const BVariant& value, ValueNodeChild*& _output) const
|
||||
{
|
||||
Type* type = new(std::nothrow) SyntheticPrimitiveType(value.Type());
|
||||
if (type == NULL) {
|
||||
throw ParseException("Out of memory while creating type object.",
|
||||
token.position);
|
||||
}
|
||||
|
||||
BReference<Type> typeReference(type, true);
|
||||
ValueLocation* location = new(std::nothrow) ValueLocation();
|
||||
if (location == NULL) {
|
||||
throw ParseException("Out of memory while creating location object.",
|
||||
token.position);
|
||||
}
|
||||
|
||||
BReference<ValueLocation> locationReference(location, true);
|
||||
ValuePieceLocation piece;
|
||||
if (!piece.SetToValue(value.Bytes(), value.Size())
|
||||
|| !location->AddPiece(piece)) {
|
||||
throw ParseException("Out of memory populating location"
|
||||
" object.", token.position);
|
||||
}
|
||||
|
||||
char variableName[32];
|
||||
if (!IntegerFormatter::FormatValue(value, INTEGER_FORMAT_HEX_DEFAULT,
|
||||
variableName, sizeof(variableName))) {
|
||||
throw ParseException("Failed to generate internal variable name.",
|
||||
token.position);
|
||||
}
|
||||
|
||||
InternalVariableID* id = new(std::nothrow) InternalVariableID(value);
|
||||
if (id == NULL) {
|
||||
throw ParseException("Out of memory while creating ID object.",
|
||||
token.position);
|
||||
}
|
||||
|
||||
BReference<ObjectID> idReference(id, true);
|
||||
Variable* variable = new(std::nothrow) Variable(id, variableName, type,
|
||||
location);
|
||||
if (variable == NULL) {
|
||||
throw ParseException("Out of memory while creating variable object.",
|
||||
token.position);
|
||||
}
|
||||
|
||||
BReference<Variable> variableReference(variable, true);
|
||||
_output = new(std::nothrow) VariableValueNodeChild(
|
||||
variable);
|
||||
if (_output == NULL) {
|
||||
throw ParseException("Out of memory while creating node child object.",
|
||||
token.position);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ namespace CLanguage {
|
||||
class Tokenizer;
|
||||
}
|
||||
|
||||
|
||||
class BVariant;
|
||||
class TeamTypeInformation;
|
||||
class Type;
|
||||
class ValueNode;
|
||||
class ValueNodeChild;
|
||||
class ValueNodeManager;
|
||||
@ -45,17 +47,19 @@ class Number;
|
||||
|
||||
class CLanguageExpressionEvaluator {
|
||||
|
||||
public:
|
||||
public:
|
||||
CLanguageExpressionEvaluator();
|
||||
~CLanguageExpressionEvaluator();
|
||||
|
||||
ExpressionResult* Evaluate(const char* expressionString,
|
||||
ValueNodeManager* manager);
|
||||
ValueNodeManager* manager,
|
||||
TeamTypeInformation* info);
|
||||
|
||||
private:
|
||||
private:
|
||||
class InternalVariableID;
|
||||
class Operand;
|
||||
|
||||
private:
|
||||
private:
|
||||
Operand _ParseSum();
|
||||
Operand _ParseProduct();
|
||||
Operand _ParsePower();
|
||||
@ -65,11 +69,27 @@ class CLanguageExpressionEvaluator {
|
||||
|
||||
void _EatToken(int32 type);
|
||||
|
||||
Operand _ParseType(Type* baseType);
|
||||
// the passed in Type object
|
||||
// is expected to be the initial
|
||||
// base type that was recognized by
|
||||
// e.g. ParseIdentifier. This function then
|
||||
// takes care of handling any modifiers
|
||||
// that go with it, and returns a
|
||||
// corresponding final type.
|
||||
|
||||
void _RequestValueIfNeeded(
|
||||
const CLanguage::Token& token,
|
||||
ValueNodeChild* child);
|
||||
|
||||
void _GetNodeChildForPrimitive(
|
||||
const CLanguage::Token& token,
|
||||
const BVariant& value,
|
||||
ValueNodeChild*& _output) const;
|
||||
|
||||
private:
|
||||
CLanguage::Tokenizer* fTokenizer;
|
||||
TeamTypeInformation* fTypeInfo;
|
||||
ValueNodeManager* fNodeManager;
|
||||
};
|
||||
|
||||
|
@ -169,14 +169,14 @@ CLanguageFamily::ParseTypeExpression(const BString& expression,
|
||||
|
||||
status_t
|
||||
CLanguageFamily::EvaluateExpression(const BString& expression,
|
||||
ValueNodeManager* manager, ExpressionResult*& _output,
|
||||
ValueNode*& _neededNode)
|
||||
ValueNodeManager* manager, TeamTypeInformation* info,
|
||||
ExpressionResult*& _output, ValueNode*& _neededNode)
|
||||
{
|
||||
_output = NULL;
|
||||
_neededNode = NULL;
|
||||
CLanguageExpressionEvaluator evaluator;
|
||||
try {
|
||||
_output = evaluator.Evaluate(expression, manager);
|
||||
_output = evaluator.Evaluate(expression, manager, info);
|
||||
return B_OK;
|
||||
} catch (ParseException ex) {
|
||||
BString error;
|
||||
|
@ -23,6 +23,7 @@ public:
|
||||
|
||||
virtual status_t EvaluateExpression(const BString& expression,
|
||||
ValueNodeManager* manager,
|
||||
TeamTypeInformation* info,
|
||||
ExpressionResult*& _output,
|
||||
ValueNode*& _neededNode);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user