mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-08 21:53:02 +02:00
AnimExpression: support for parsing simple expressions
supports parens, binary +, -, / and *. / and * have higher precedence then + and -
This commit is contained in:
parent
710ce7e639
commit
99223d0a3c
4 changed files with 417 additions and 131 deletions
|
@ -17,9 +17,16 @@
|
|||
AnimExpression::AnimExpression(const QString& str) :
|
||||
_expression(str) {
|
||||
auto iter = str.begin();
|
||||
parseExpression(_expression, iter);
|
||||
parseExpr(_expression, iter);
|
||||
while(!_tokenStack.empty()) {
|
||||
_tokenStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Tokenizer
|
||||
//
|
||||
|
||||
void AnimExpression::unconsumeToken(const Token& token) {
|
||||
_tokenStack.push(token);
|
||||
}
|
||||
|
@ -49,6 +56,7 @@ AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::
|
|||
case '-': ++iter; return Token(Token::Minus);
|
||||
case '+': ++iter; return Token(Token::Plus);
|
||||
case '*': ++iter; return Token(Token::Multiply);
|
||||
case '/': ++iter; return Token(Token::Divide);
|
||||
case '%': ++iter; return Token(Token::Modulus);
|
||||
case ',': ++iter; return Token(Token::Comma);
|
||||
default:
|
||||
|
@ -184,73 +192,137 @@ AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::co
|
|||
}
|
||||
}
|
||||
|
||||
bool AnimExpression::parseExpression(const QString& str, QString::const_iterator& iter) {
|
||||
//
|
||||
// Parser
|
||||
//
|
||||
|
||||
/*
|
||||
Expr → Term Expr'
|
||||
Expr' → + Term Expr'
|
||||
| – Term Expr'
|
||||
| ε
|
||||
Term → Factor Term'
|
||||
Term' → * Term'
|
||||
| / Term'
|
||||
| ε
|
||||
Factor → INT
|
||||
| FLOAT
|
||||
| IDENTIFIER
|
||||
| (Expr)
|
||||
*/
|
||||
|
||||
bool AnimExpression::parseExpr(const QString& str, QString::const_iterator& iter) {
|
||||
if (!parseTerm(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseExprPrime(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimExpression::parseExprPrime(const QString& str, QString::const_iterator& iter) {
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::Identifier) {
|
||||
if (token.strVal == "true") {
|
||||
_opCodes.push_back(OpCode {true});
|
||||
} else if (token.strVal == "false") {
|
||||
_opCodes.push_back(OpCode {false});
|
||||
} else {
|
||||
_opCodes.push_back(OpCode {token.strVal});
|
||||
if (token.type == Token::Plus) {
|
||||
if (!parseTerm(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
if (!parseExprPrime(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
_opCodes.push_back(OpCode {OpCode::Add});
|
||||
return true;
|
||||
} else if (token.type == Token::Int) {
|
||||
} else if (token.type == Token::Minus) {
|
||||
if (!parseTerm(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
if (!parseExprPrime(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
_opCodes.push_back(OpCode {OpCode::Subtract});
|
||||
return true;
|
||||
} else {
|
||||
unconsumeToken(token);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimExpression::parseTerm(const QString& str, QString::const_iterator& iter) {
|
||||
if (!parseFactor(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseTermPrime(str, iter)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimExpression::parseTermPrime(const QString& str, QString::const_iterator& iter) {
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::Multiply) {
|
||||
if (!parseTerm(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
if (!parseTermPrime(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
_opCodes.push_back(OpCode {OpCode::Multiply});
|
||||
return true;
|
||||
} else if (token.type == Token::Divide) {
|
||||
if (!parseTerm(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
if (!parseTermPrime(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
_opCodes.push_back(OpCode {OpCode::Divide});
|
||||
return true;
|
||||
} else {
|
||||
unconsumeToken(token);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimExpression::parseFactor(const QString& str, QString::const_iterator& iter) {
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::Int) {
|
||||
_opCodes.push_back(OpCode {token.intVal});
|
||||
return true;
|
||||
} else if (token.type == Token::Float) {
|
||||
_opCodes.push_back(OpCode {token.floatVal});
|
||||
return true;
|
||||
} else if (token.type == Token::Identifier) {
|
||||
_opCodes.push_back(OpCode {token.strVal});
|
||||
return true;
|
||||
} else if (token.type == Token::LeftParen) {
|
||||
if (parseUnaryExpression(str, iter)) {
|
||||
token = consumeToken(str, iter);
|
||||
if (token.type != Token::RightParen) {
|
||||
qCCritical(animation) << "Error parsing expression, expected ')'";
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!parseExpr(str, iter)) {
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
auto nextToken = consumeToken(str, iter);
|
||||
if (nextToken.type != Token::RightParen) {
|
||||
unconsumeToken(nextToken);
|
||||
unconsumeToken(token);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
unconsumeToken(token);
|
||||
if (parseUnaryExpression(str, iter)) {
|
||||
return true;
|
||||
} else {
|
||||
qCCritical(animation) << "Error parsing expression";
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimExpression::parseUnaryExpression(const QString& str, QString::const_iterator& iter) {
|
||||
auto token = consumeToken(str, iter);
|
||||
if (token.type == Token::Plus) { // unary plus is a no op.
|
||||
if (parseExpression(str, iter)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (token.type == Token::Minus) {
|
||||
if (parseExpression(str, iter)) {
|
||||
_opCodes.push_back(OpCode {OpCode::Minus});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (token.type == Token::Not) {
|
||||
if (parseExpression(str, iter)) {
|
||||
_opCodes.push_back(OpCode {OpCode::Not});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
unconsumeToken(token);
|
||||
return parseExpression(str, iter);
|
||||
}
|
||||
}
|
||||
//
|
||||
// Evaluator
|
||||
//
|
||||
|
||||
AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const {
|
||||
std::stack<OpCode> stack;
|
||||
|
@ -274,8 +346,9 @@ AnimExpression::OpCode AnimExpression::evaluate(const AnimVariantMap& map) const
|
|||
case OpCode::Subtract: evalSubtract(map, stack); break;
|
||||
case OpCode::Add: evalAdd(map, stack); break;
|
||||
case OpCode::Multiply: evalMultiply(map, stack); break;
|
||||
case OpCode::Divide: evalDivide(map, stack); break;
|
||||
case OpCode::Modulus: evalModulus(map, stack); break;
|
||||
case OpCode::Minus: evalMinus(map, stack); break;
|
||||
case OpCode::UnaryMinus: evalUnaryMinus(map, stack); break;
|
||||
}
|
||||
}
|
||||
return stack.top();
|
||||
|
@ -362,15 +435,107 @@ void AnimExpression::evalSubtract(const AnimVariantMap& map, std::stack<OpCode>&
|
|||
PUSH(0.0f);
|
||||
}
|
||||
|
||||
void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
void AnimExpression::add(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs + rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH((float)lhs + rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
PUSH(0.0f);
|
||||
void AnimExpression::add(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs + (float)rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH(lhs + rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::evalAdd(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
OpCode rhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
|
||||
switch (lhs.type) {
|
||||
case OpCode::Bool:
|
||||
add(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Int:
|
||||
add(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
add(lhs.floatVal, rhs, stack);
|
||||
break;
|
||||
default:
|
||||
add(0, rhs, stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::evalMultiply(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
OpCode rhs = coerseToValue(map, stack.top());
|
||||
stack.pop();
|
||||
|
||||
switch(lhs.type) {
|
||||
case OpCode::Bool:
|
||||
mul(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Int:
|
||||
mul(lhs.intVal, rhs, stack);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
mul(lhs.floatVal, rhs, stack);
|
||||
break;
|
||||
default:
|
||||
mul(0, rhs, stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::mul(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs * rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH((float)lhs * rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::mul(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const {
|
||||
switch (rhs.type) {
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
PUSH(lhs * (float)rhs.intVal);
|
||||
break;
|
||||
case OpCode::Float:
|
||||
PUSH(lhs * rhs.floatVal);
|
||||
break;
|
||||
default:
|
||||
PUSH(lhs);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::evalDivide(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode lhs = stack.top(); stack.pop();
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
|
@ -386,7 +551,7 @@ void AnimExpression::evalModulus(const AnimVariantMap& map, std::stack<OpCode>&
|
|||
PUSH((int)0);
|
||||
}
|
||||
|
||||
void AnimExpression::evalMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
void AnimExpression::evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const {
|
||||
OpCode rhs = stack.top(); stack.pop();
|
||||
|
||||
switch (rhs.type) {
|
||||
|
@ -429,3 +594,69 @@ void AnimExpression::evalMinus(const AnimVariantMap& map, std::stack<OpCode>& st
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AnimExpression::OpCode AnimExpression::coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const {
|
||||
switch (opCode.type) {
|
||||
case OpCode::Identifier:
|
||||
{
|
||||
const AnimVariant& var = map.get(opCode.strVal);
|
||||
switch (var.getType()) {
|
||||
case AnimVariant::Type::Bool:
|
||||
qCWarning(animation) << "AnimExpression: type missmatch, expected a number not a bool";
|
||||
return OpCode(0);
|
||||
break;
|
||||
case AnimVariant::Type::Int:
|
||||
return OpCode(var.getInt());
|
||||
break;
|
||||
case AnimVariant::Type::Float:
|
||||
return OpCode(var.getFloat());
|
||||
break;
|
||||
default:
|
||||
// TODO: Vec3, Quat are unsupported
|
||||
assert(false);
|
||||
return OpCode(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OpCode::Bool:
|
||||
case OpCode::Int:
|
||||
case OpCode::Float:
|
||||
return opCode;
|
||||
default:
|
||||
qCCritical(animation) << "AnimExpression: ERROR expected a number, type = " << opCode.type;
|
||||
assert(false);
|
||||
return OpCode(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimExpression::dumpOpCodes() const {
|
||||
QString tmp;
|
||||
for (auto& op : _opCodes) {
|
||||
switch (op.type) {
|
||||
case OpCode::Identifier: tmp += QString(" %1").arg(op.strVal); break;
|
||||
case OpCode::Bool: tmp += QString(" %1").arg(op.intVal ? "true" : "false"); break;
|
||||
case OpCode::Int: tmp += QString(" %1").arg(op.intVal); break;
|
||||
case OpCode::Float: tmp += QString(" %1").arg(op.floatVal); break;
|
||||
case OpCode::And: tmp += " &&"; break;
|
||||
case OpCode::Or: tmp += " ||"; break;
|
||||
case OpCode::GreaterThan: tmp += " >"; break;
|
||||
case OpCode::GreaterThanEqual: tmp += " >="; break;
|
||||
case OpCode::LessThan: tmp += " <"; break;
|
||||
case OpCode::LessThanEqual: tmp += " <="; break;
|
||||
case OpCode::Equal: tmp += " =="; break;
|
||||
case OpCode::NotEqual: tmp += " !="; break;
|
||||
case OpCode::Not: tmp += " !"; break;
|
||||
case OpCode::Subtract: tmp += " -"; break;
|
||||
case OpCode::Add: tmp += " +"; break;
|
||||
case OpCode::Multiply: tmp += " *"; break;
|
||||
case OpCode::Divide: tmp += " /"; break;
|
||||
case OpCode::Modulus: tmp += " %"; break;
|
||||
case OpCode::UnaryMinus: tmp += " unary-"; break;
|
||||
default: tmp += " ???"; break;
|
||||
}
|
||||
}
|
||||
qCDebug(animation).nospace().noquote() << "opCodes =" << tmp;
|
||||
qCDebug(animation).resetFormat();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ protected:
|
|||
Minus,
|
||||
Plus,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulus,
|
||||
Comma,
|
||||
Error
|
||||
|
@ -75,8 +76,9 @@ protected:
|
|||
Subtract,
|
||||
Add,
|
||||
Multiply,
|
||||
Divide,
|
||||
Modulus,
|
||||
Minus
|
||||
UnaryMinus
|
||||
};
|
||||
OpCode(Type type) : type {type} {}
|
||||
OpCode(const QStringRef& strRef) : type {Type::Identifier}, strVal {strRef.toString()} {}
|
||||
|
@ -111,8 +113,11 @@ protected:
|
|||
Token consumeLessThan(const QString& str, QString::const_iterator& iter) const;
|
||||
Token consumeNot(const QString& str, QString::const_iterator& iter) const;
|
||||
|
||||
bool parseExpression(const QString& str, QString::const_iterator& iter);
|
||||
bool parseUnaryExpression(const QString& str, QString::const_iterator& iter);
|
||||
bool parseExpr(const QString& str, QString::const_iterator& iter);
|
||||
bool parseExprPrime(const QString& str, QString::const_iterator& iter);
|
||||
bool parseTerm(const QString& str, QString::const_iterator& iter);
|
||||
bool parseTermPrime(const QString& str, QString::const_iterator& iter);
|
||||
bool parseFactor(const QString& str, QString::const_iterator& iter);
|
||||
|
||||
OpCode evaluate(const AnimVariantMap& map) const;
|
||||
void evalAnd(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
|
@ -126,13 +131,24 @@ protected:
|
|||
void evalNot(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalSubtract(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalAdd(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void add(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void add(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void evalMultiply(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void mul(int lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void mul(float lhs, const OpCode& rhs, std::stack<OpCode>& stack) const;
|
||||
void evalDivide(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalModulus(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
void evalUnaryMinus(const AnimVariantMap& map, std::stack<OpCode>& stack) const;
|
||||
|
||||
OpCode coerseToValue(const AnimVariantMap& map, const OpCode& opCode) const;
|
||||
|
||||
QString _expression;
|
||||
mutable std::stack<Token> _tokenStack; // TODO: remove, only needed during parsing
|
||||
std::vector<OpCode> _opCodes;
|
||||
|
||||
#ifndef NDEBUG
|
||||
void dumpOpCodes() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -366,82 +366,120 @@ void AnimTests::testExpressionParser() {
|
|||
vars.set("five", (float)5.0f);
|
||||
vars.set("forty", (float)40.0f);
|
||||
|
||||
AnimExpression e("(!f)");
|
||||
QVERIFY(e._opCodes.size() == 2);
|
||||
if (e._opCodes.size() == 2) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier);
|
||||
QVERIFY(e._opCodes[0].strVal == "f");
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not);
|
||||
|
||||
auto opCode = e.evaluate(vars);
|
||||
QVERIFY(opCode.type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY((opCode.intVal != 0) == true);
|
||||
}
|
||||
|
||||
e = AnimExpression("!!f");
|
||||
QVERIFY(e._opCodes.size() == 3);
|
||||
if (e._opCodes.size() == 3) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier);
|
||||
QVERIFY(e._opCodes[0].strVal == "f");
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not);
|
||||
|
||||
auto opCode = e.evaluate(vars);
|
||||
QVERIFY(opCode.type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY((opCode.intVal != 0) == false);
|
||||
}
|
||||
|
||||
e = AnimExpression("!!(!f)");
|
||||
QVERIFY(e._opCodes.size() == 4);
|
||||
if (e._opCodes.size() == 4) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier);
|
||||
QVERIFY(e._opCodes[0].strVal == "f");
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Not);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not);
|
||||
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Not);
|
||||
|
||||
auto opCode = e.evaluate(vars);
|
||||
QVERIFY(opCode.type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY((opCode.intVal != 0) == true);
|
||||
}
|
||||
/*
|
||||
e = AnimExpression("f || !f");
|
||||
QVERIFY(e._opCodes.size() == 4);
|
||||
if (e._opCodes.size() == 4) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier);
|
||||
QVERIFY(e._opCodes[0].strVal == "f");
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Identifier);
|
||||
QVERIFY(e._opCodes[1].strVal == "f");
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Not);
|
||||
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Or);
|
||||
|
||||
auto opCode = e.evaluate(vars);
|
||||
QVERIFY(opCode.type == AnimExpression::OpCode::Bool);
|
||||
QVERIFY((opCode.intVal != 0) == true);
|
||||
}
|
||||
*/
|
||||
e = AnimExpression("-10");
|
||||
QVERIFY(e._opCodes.size() == 2);
|
||||
AnimExpression e("10");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 10);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Minus);
|
||||
|
||||
auto opCode = e.evaluate(vars);
|
||||
QVERIFY(opCode.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(opCode.intVal == 10);
|
||||
}
|
||||
|
||||
e = AnimExpression("-ten");
|
||||
QVERIFY(e._opCodes.size() == 2);
|
||||
e = AnimExpression("(10)");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 10);
|
||||
}
|
||||
|
||||
e = AnimExpression("((10))");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 10);
|
||||
}
|
||||
|
||||
e = AnimExpression("12.5");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Float);
|
||||
QVERIFY(e._opCodes[0].floatVal == 12.5f);
|
||||
}
|
||||
|
||||
e = AnimExpression("twenty");
|
||||
QVERIFY(e._opCodes.size() == 1);
|
||||
if (e._opCodes.size() == 1) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Identifier);
|
||||
QVERIFY(e._opCodes[0].strVal == "ten");
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Minus);
|
||||
QVERIFY(e._opCodes[0].strVal == "twenty");
|
||||
}
|
||||
|
||||
auto opCode = e.evaluate(vars);
|
||||
QVERIFY(opCode.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(opCode.intVal == 10);
|
||||
e = AnimExpression("2 + 3");
|
||||
QVERIFY(e._opCodes.size() == 3);
|
||||
if (e._opCodes.size() == 3) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 2);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[1].intVal == 3);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Add);
|
||||
}
|
||||
|
||||
e = AnimExpression("2 + 3 * 10");
|
||||
QVERIFY(e._opCodes.size() == 5);
|
||||
if (e._opCodes.size() == 5) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 2);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[1].intVal == 3);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[2].intVal == 10);
|
||||
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Multiply);
|
||||
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Add);
|
||||
}
|
||||
|
||||
e = AnimExpression("(2 + 3) * 10");
|
||||
QVERIFY(e._opCodes.size() == 5);
|
||||
if (e._opCodes.size() == 5) {
|
||||
QVERIFY(e._opCodes[0].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[0].intVal == 2);
|
||||
QVERIFY(e._opCodes[1].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[1].intVal == 3);
|
||||
QVERIFY(e._opCodes[2].type == AnimExpression::OpCode::Add);
|
||||
QVERIFY(e._opCodes[3].type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(e._opCodes[3].intVal == 10);
|
||||
QVERIFY(e._opCodes[4].type == AnimExpression::OpCode::Multiply);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimTests::testExpressionEvaluator() {
|
||||
auto vars = AnimVariantMap();
|
||||
vars.set("f", false);
|
||||
vars.set("t", true);
|
||||
vars.set("ten", (int)10);
|
||||
vars.set("twenty", (int)20);
|
||||
vars.set("five", (float)5.0f);
|
||||
vars.set("forty", (float)40.0f);
|
||||
|
||||
AnimExpression::OpCode result = AnimExpression("10").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == 10);
|
||||
|
||||
result = AnimExpression("(10)").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == 10);
|
||||
|
||||
result = AnimExpression("2 + 3").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == 2 + 3);
|
||||
|
||||
result = AnimExpression("2 + 3 * 5").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == 2 + 3 * 5);
|
||||
|
||||
result = AnimExpression("(2 + 3) * 5").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == (2 + 3) * 5);
|
||||
|
||||
result = AnimExpression("(2 + 3) * (5 + 3)").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == (2 + 3) * (5 + 3));
|
||||
|
||||
result = AnimExpression("(ten + twenty) * 5").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Int);
|
||||
QVERIFY(result.intVal == (10 + 20) * 5);
|
||||
|
||||
result = AnimExpression("(ten + twenty) * 5.0").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Float);
|
||||
QVERIFY(result.floatVal == (10 + 20) * 5.0f);
|
||||
|
||||
result = AnimExpression("five * forty").evaluate(vars);
|
||||
QVERIFY(result.type == AnimExpression::OpCode::Float);
|
||||
QVERIFY(result.floatVal == 5.0f * 40.0f);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ private slots:
|
|||
void testAccumulateTime();
|
||||
void testExpressionTokenizer();
|
||||
void testExpressionParser();
|
||||
void testExpressionEvaluator();
|
||||
};
|
||||
|
||||
#endif // hifi_AnimTests_h
|
||||
|
|
Loading…
Reference in a new issue