Initial version of AnimExpression class with minimal tokenizer

This commit is contained in:
Anthony J. Thibault 2015-11-01 15:16:00 -08:00
parent f4cc8c4c2e
commit 340096d457
4 changed files with 354 additions and 6 deletions

View file

@ -0,0 +1,228 @@
//
// AnimExpression.cpp
//
// Created by Anthony J. Thibault on 11/1/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <StreamUtils.h>
#include <QRegExp>
#include "AnimExpression.h"
#include "AnimationLogging.h"
#ifndef NDEBUG
void AnimExpression::Token::dump() {
switch (type) {
case End:
qCDebug(animation) << " End";
break;
case Identifier:
qCDebug(animation) << " Identifer =" << strVal;
break;
case LiteralInt:
qCDebug(animation) << " LiteralInt =" << intVal;
break;
case LiteralFloat:
qCDebug(animation) << " LiteralFloat" << floatVal;
break;
case LiteralVec3:
qCDebug(animation) << " LiteralVec3" << vec3Val;
break;
case LiteralVec4:
qCDebug(animation) << " LiteralVec4" << vec4Val;
break;
case LiteralQuat:
qCDebug(animation) << " LiteralQuat" << quatVal;
break;
case And:
qCDebug(animation) << " And";
break;
case Or:
qCDebug(animation) << " Or";
break;
case GreaterThan:
qCDebug(animation) << " GreaterThan";
break;
case GreaterThanEqual:
qCDebug(animation) << " GreaterThanEqual";
break;
case LessThan:
qCDebug(animation) << " LessThan";
break;
case LessThanEqual:
qCDebug(animation) << " LessThanEqual";
break;
case Equal:
qCDebug(animation) << " Equal";
break;
case NotEqual:
qCDebug(animation) << " NotEqual";
break;
case LeftParen:
qCDebug(animation) << " LeftParen";
break;
case RightParen:
qCDebug(animation) << " RightParen";
break;
case Not:
qCDebug(animation) << " Not";
break;
case Minus:
qCDebug(animation) << " Minus";
break;
case Plus:
qCDebug(animation) << " Plus";
break;
case Multiply:
qCDebug(animation) << " Multiply";
break;
case Modulus:
qCDebug(animation) << " Modulus";
break;
case Error:
qCDebug(animation) << " Error";
break;
}
}
#endif
AnimExpression::AnimExpression(const QString& str) :
_expression(str) {
parseExpression(_expression);
}
bool AnimExpression::parseExpression(const QString& str) {
Token token(Token::End);
auto iter = str.begin();
do {
token = consumeToken(str, iter);
switch(token.type) {
case Token::Error:
case Token::End:
return false;
}
} while(iter != str.end());
}
AnimExpression::Token AnimExpression::consumeToken(const QString& str, QString::const_iterator& iter) const {
while (iter != str.end()) {
if (iter->isSpace()) {
++iter;
} else if (iter->isLetter()) {
return consumeIdentifier(str, iter);
} else if (iter->isDigit()) {
return consumeNumber(str, iter);
} else {
switch (iter->unicode()) {
case '&': return consumeAnd(str, iter);
case '|': return consumeOr(str, iter);
case '>': return consumeGreaterThan(str, iter);
case '<': return consumeLessThan(str, iter);
case '(': ++iter; return Token(Token::LeftParen);
case ')': ++iter; return Token(Token::RightParen);
case '!': return consumeNot(str, iter);
case '-': ++iter; return Token(Token::Minus);
case '+': ++iter; return Token(Token::Plus);
case '*': ++iter; return Token(Token::Multiply);
case '%': ++iter; return Token(Token::Modulus);
default:
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
return Token(Token::Error);
}
}
}
return Token(Token::End);
}
AnimExpression::Token AnimExpression::consumeIdentifier(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->isLetter());
auto begin = iter;
while (iter->isLetter() && iter != str.end()) {
++iter;
}
int pos = (int)(begin - str.begin());
int len = (int)(iter - begin);
return Token(QStringRef(const_cast<const QString*>(&str), pos, len));
}
AnimExpression::Token AnimExpression::consumeNumber(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->isDigit());
auto begin = iter;
while (iter->isDigit() && iter != str.end()) {
++iter;
}
int pos = (int)(begin - str.begin());
int len = (int)(iter - begin);
QString sub = QStringRef(const_cast<const QString*>(&str), pos, len).toString();
return Token(sub.toInt());
}
AnimExpression::Token AnimExpression::consumeAnd(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '&');
iter++;
if (iter->unicode() == '&') {
iter++;
return Token(Token::And);
} else {
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
return Token(Token::Error);
}
}
AnimExpression::Token AnimExpression::consumeOr(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '|');
iter++;
if (iter->unicode() == '|') {
iter++;
return Token(Token::Or);
} else {
qCCritical(animation) << "AnimExpression: unexpected char" << *iter << "at index " << (int)(iter - str.begin());
return Token(Token::Error);
}
}
AnimExpression::Token AnimExpression::consumeGreaterThan(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '>');
iter++;
if (iter->unicode() == '=') {
iter++;
return Token(Token::GreaterThanEqual);
} else {
return Token(Token::GreaterThan);
}
}
AnimExpression::Token AnimExpression::consumeLessThan(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '<');
iter++;
if (iter->unicode() == '=') {
iter++;
return Token(Token::LessThanEqual);
} else {
return Token(Token::LessThan);
}
}
AnimExpression::Token AnimExpression::consumeNot(const QString& str, QString::const_iterator& iter) const {
assert(iter != str.end());
assert(iter->unicode() == '!');
iter++;
if (iter->unicode() == '=') {
iter++;
return Token(Token::NotEqual);
} else {
return Token(Token::Not);
}
}

View file

@ -0,0 +1,78 @@
//
// AnimExpression.h
//
// Created by Anthony J. Thibault on 11/1/15.
// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_AnimExpression
#define hifi_AnimExpression
#include <QString>
#include <QVector>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
class AnimExpression {
public:
friend class AnimTests;
AnimExpression(const QString& str);
protected:
struct Token {
enum Type {
End = 0,
Identifier,
LiteralInt,
LiteralFloat,
LiteralVec3,
LiteralVec4,
LiteralQuat,
And,
Or,
GreaterThan,
GreaterThanEqual,
LessThan,
LessThanEqual,
Equal,
NotEqual,
LeftParen,
RightParen,
Not,
Minus,
Plus,
Multiply,
Modulus,
Error
};
Token(Type type) : type(type) {}
Token(const QStringRef& strRef) : type(Type::Identifier), strVal(strRef.toString()) {}
Token(int val) : type(Type::LiteralInt), intVal(val) {}
Type type = End;
QString strVal;
int intVal;
float floatVal;
glm::vec3 vec3Val;
glm::vec4 vec4Val;
glm::quat quatVal;
#ifndef NDEBUG
void dump();
#endif
};
bool parseExpression(const QString& str);
Token consumeToken(const QString& str, QString::const_iterator& iter) const;
Token consumeIdentifier(const QString& str, QString::const_iterator& iter) const;
Token consumeNumber(const QString& str, QString::const_iterator& iter) const;
Token consumeAnd(const QString& str, QString::const_iterator& iter) const;
Token consumeOr(const QString& str, QString::const_iterator& iter) const;
Token consumeGreaterThan(const QString& str, QString::const_iterator& iter) const;
Token consumeLessThan(const QString& str, QString::const_iterator& iter) const;
Token consumeNot(const QString& str, QString::const_iterator& iter) const;
QString _expression;
};
#endif

View file

@ -8,12 +8,13 @@
//
#include "AnimTests.h"
#include "AnimNodeLoader.h"
#include "AnimClip.h"
#include "AnimBlendLinear.h"
#include "AnimationLogging.h"
#include "AnimVariant.h"
#include "AnimUtil.h"
#include <AnimNodeLoader.h>
#include <AnimClip.h>
#include <AnimBlendLinear.h>
#include <AnimationLogging.h>
#include <AnimVariant.h>
#include <AnimExpression.h>
#include <AnimUtil.h>
#include <../QTestExtensions.h>
@ -322,4 +323,44 @@ void AnimTests::testAccumulateTimeWithParameters(float startFrame, float endFram
QVERIFY(resultFrame == startFrame + 0.5f);
QVERIFY(!triggers.empty() && triggers[0] == "testNodeOnLoop");
triggers.clear();
void AnimTests::testTokenizer() {
QString str = "(10 + x) >= 20 && (y != !z)";
AnimExpression e("");
auto iter = str.cbegin();
AnimExpression::Token token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::LeftParen);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::LiteralInt);
QVERIFY(token.intVal == 10);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Plus);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Identifier);
QVERIFY(token.strVal == "x");
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::RightParen);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::GreaterThanEqual);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::LiteralInt);
QVERIFY(token.intVal == 20);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::And);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::LeftParen);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Identifier);
QVERIFY(token.strVal == "y");
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::NotEqual);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Not);
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::Identifier);
QVERIFY(token.strVal == "z");
token = e.consumeToken(str, iter);
QVERIFY(token.type == AnimExpression::Token::RightParen);
token = e.consumeToken(str, iter);
}

View file

@ -26,6 +26,7 @@ private slots:
void testLoader();
void testVariant();
void testAccumulateTime();
void testTokenizer();
};
#endif // hifi_AnimTests_h