--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Expression.cc Mon Feb 03 20:12:44 2014 +0200 @@ -0,0 +1,277 @@ +#include "Expression.h" +#include "DataBuffer.h" +#include "Lexer.h" +#include "Variables.h" + +struct OperatorInfo +{ + EToken token; + int numoperands; + int priority; + EDataHeader header; +}; + +static const OperatorInfo gOperators[] = +{ + { tkExclamationMark, 0, 1, dhNegateLogical, }, + { tkMinus, 0, 1, dhUnaryMinus, }, + { tkMultiply, 10, 2, dhMultiply, }, + { tkDivide, 10, 2, dhDivide, }, + { tkModulus, 10, 2, dhModulus, }, + { tkPlus, 20, 2, dhAdd, }, + { tkMinus, 20, 2, dhSubtract, }, + { tkLeftShift, 30, 2, dhLeftShift, }, + { tkRightShift, 30, 2, dhRightShift, }, + { tkLesser, 40, 2, dhLessThan, }, + { tkGreater, 40, 2, dhGreaterThan, }, + { tkAtLeast, 40, 2, dhAtLeast, }, + { tkAtMost, 40, 2, dhAtMost, }, + { tkEquals, 50, 2, dhEquals }, + { tkNotEquals, 50, 2, dhNotEquals }, + { tkAmperstand, 60, 2, dhAndBitwise }, + { tkCaret, 70, 2, dhEorBitwise }, + { tkBar, 80, 2, dhOrBitwise }, + { tkDoubleAmperstand, 90, 2, dhAndLogical }, + { tkDoubleBar, 100, 2, dhOrLogical }, + { tkQuestionMark, 110, 3, (EDataHeader) 0 }, +}; + +/* + // There isn't a dataheader for ternary operator. Instead, we use dhIfNotGoto + // to create an "if-block" inside an expression. + // Behold, big block of writing madness! :P + ByteMark* mark1 = retbuf->AddMark (""); // start of "else" case + ByteMark* mark2 = retbuf->AddMark (""); // end of expression + retbuf->WriteDWord (dhIfNotGoto); // if the first operand (condition) + retbuf->AddReference (mark1); // didn't eval true, jump into mark1 + retbuf->MergeAndDestroy (rb); // otherwise, perform second operand (true case) + retbuf->WriteDWord (dhGoto); // afterwards, jump to the end, which is + retbuf->AddReference (mark2); // marked by mark2. + retbuf->AdjustMark (mark1); // move mark1 at the end of the true case + retbuf->MergeAndDestroy (tb); // perform third operand (false case) + retbuf->AdjustMark (mark2); // move the ending mark2 here +*/ + +// ============================================================================= +// +Expression::Expression (BotscriptParser* parser, EType reqtype, Lexer* lx) : + mParser (parser), + mLexer (lx), + mType (reqtype), + mResult (null) +{ + ExpressionSymbol* sym; + + while ((sym = ParseSymbol()) != null) + mSymbols << sym; + + if (mSymbols.IsEmpty()) + Error ("Expected expression"); + + Verify(); + mResult = Evaluate(); +} + +// ============================================================================= +// +Expression::~Expression() +{ + for (ExpressionSymbol* sym : mSymbols) + delete sym; + + delete mResult; +} + +// ============================================================================= +// +// Try to parse an expression symbol (i.e. an operator or operand or a colon) +// from the lexer. +// +ExpressionSymbol* Expression::ParseSymbol() +{ + int pos = mLexer->GetPosition(); + ExpressionValue* op = null; + enum ELocalException { failed }; + + try + { + ScriptVariable* globalvar; + mLexer->MustGetNext(); + + if (mLexer->GetTokenType() == tkColon) + return new ExpressionColon; + + // Check for operator + for (const OperatorInfo* op : gOperators) + if (mLexer->GetTokenType() == op->token) + return new ExpressionOperator (op - gOperators); + + // Check sub-expression + if (mLexer->GetTokenType() == tkParenStart) + { + mLexer->MustGetNext(); + Expression expr (mParser, mLexer, mType); + mLexer->MustGetNext (tkParenEnd); + return expr.GetResult(); + } + + op = new ExpressionValue; + + // Check function + if (CommandInfo* comm = FindCommandByName (GetTokenString())) + { + if (mType != EUnknownType && comm->returnvalue != mType) + Error ("%1 returns an incompatible data type", comm->name); + + op->SetBuffer (mParser->ParseCommand (comm)); + return op; + } + + // Check constant + if (ConstantInfo* constant = mParser->FindConstant (GetTokenString())) + { + if (mType != constant->type) + Error ("constant `%1` is %2, expression requires %3\n", + constant->name, GetTypeName (constant->type), + GetTypeName (mType)); + + switch (constant->type) + { + case EBoolType: + case EIntType: + op->SetValue (constant->val.ToLong()); + break; + + case EStringType: + op->SetValue (GetStringTableIndex (constant->val)); + break; + + case EVoidType: + case EUnknownType: + break; + } + + return op; + } + + // Check global variable + if ((globalvar = FindGlobalVariable (GetTokenString()))) + { + DataBuffer* buf = new DataBuffer (8); + buf->WriteDWord (dhPushGlobalVar); + buf->WriteDWord (globalvar->index); + op->SetBuffer (buf); + return op; + } + + EToken tt; + + // Check for literal + switch (mType) + { + case EVoidType: + case EUnknownType: + Error ("unknown identifier `%1` (expected keyword, function or variable)", GetTokenString()); + break; + + case EBoolType: + if ((tt = mLexer->GetTokenType()) == tkTrue || tt == tkFalse) + { + op->SetValue (tt == tkTrue ? 1 : 0); + return op; + } + case EIntType: + if (!mLexer->GetTokenType() != tkNumber) + throw failed; + + op->SetValue (GetTokenString().ToLong()); + return op; + + case EStringType: + if (!mLexer->GetTokenType() != tkString) + throw failed; + + op->SetValue (GetStringTableIndex (GetTokenString())); + return op; + } + + assert (false); + throw failed; + } + catch (ELocalException&) + { + // We use a local enum here since catch(...) would catch Error() calls. + mLexer->SetPosition (pos); + delete op; + return false; + } + + assert (false); + return false; +} + +// ============================================================================= +// +ExpressionValue* Expression::Evaluate() +{ + +} + +// ============================================================================= +// +ExpressionValue* Expression::GetResult() +{ + return mResult; +} + +// ============================================================================= +// +String Expression::GetTokenString() +{ + return mLexer->GetToken()->text; +} + +// ============================================================================= +// +ExpressionOperator::ExpressionOperator (int id) : + mID (id), + mType (eOperator) {} + +// ============================================================================= +// +ExpressionValue::ExpressionValue(EType valuetype) : + mBuffer (null), + mType (eOperand), + mValueType (valuetype) {} + +// ============================================================================= +// +void ExpressionValue::ConvertToBuffer() +{ + if (IsConstexpr() == false) + return; + + SetBuffer (new DataBuffer); + + switch (mValueType) + { + case EBoolType: + case EIntType: + GetBuffer()->WriteDWord (dhPushNumber); + GetBuffer()->WriteDWord (abs (mValue)); + + if (mValue < 0) + GetBuffer()->WriteDWord (dhUnaryMinus); + break; + + case EStringType: + GetBuffer()->WriteDWord (dhPushStringIndex); + GetBuffer()->WriteDWord (mValue); + break; + + case EVoidType: + case EUnknownType: + assert (false); + break; + } +}