diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..70cf3a3 --- /dev/null +++ b/.clang-format @@ -0,0 +1,7 @@ +UseTab: Never +IndentWidth: 4 +BreakBeforeBraces: Allman +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +ColumnLimit: 80 +AccessModifierOffset: -4 diff --git a/CMakeLists.txt b/CMakeLists.txt index ee2c5a9..f3b5221 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ endif() # test_bytebeat target if(TEST) set(test_cpp_files - test/test_expression.cpp + test/test_ast.cpp test/test_lex.cpp test/test_parse.cpp ) diff --git a/README.md b/README.md index 5a1ce44..9f47f60 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,12 @@ $ cmake --build build/ --target install - One expression only - Only one pre-defined variable: `t` +- Integers (+/-), strings - Mathematic operators: ​`(), +, -, *, /, %` -- Bitwise operators: ​`&, |, ^, <<, >>` -- Relational operators: `<, >, <=, >=, ==, !=` +- Bitwise operators: ​`&, |, ^, <<, >>, ~` +- Relational operators: `<, >, <=, >=, ==, !=, !` +- Array subscript operator: `[]` +- Ternary if operator: `?, :` ## SuperCollider Usage @@ -75,13 +78,13 @@ benchmark name samples iterations estimated mean low mean high mean std dev low std dev high std dev ------------------------------------------------------------------------------- -parse crowd 100 5 1.7205 ms - 4.06055 us 3.81938 us 4.37245 us - 1.38736 us 1.14322 us 1.68179 us +parse crowd 100 7 1.8942 ms + 2.82587 us 2.79468 us 2.97907 us + 305.722 ns 7.57781 ns 729.356 ns -eval crowd 100 615 1.6605 ms - 27.3658 ns 27.3508 ns 27.382 ns - 0.0791974 ns 0.0684235 ns 0.0937675 ns +eval crowd 100 456 1.6872 ms + 36.8993 ns 36.8727 ns 36.9713 ns + 0.208576 ns 0.0980819 ns 0.445346 ns ``` Host: Raspberry Pi 3 Model B+ diff --git a/include/ast.hpp b/include/ast.hpp new file mode 100644 index 0000000..e6e948a --- /dev/null +++ b/include/ast.hpp @@ -0,0 +1,702 @@ +#pragma once + +#include +#include + +using namespace std; + +namespace bb +{ + +enum class ValueType +{ + Undefined, + Integer, + String, +}; + +class Value +{ +public: + Value() : type(ValueType::Undefined) {} + Value(const Value &value) : type(value.type) + { + if (type == ValueType::Integer) + { + i = value.i; + } + else if (type == ValueType::String) + { + s = value.s; + } + } + Value(const Value &&value) : type(move(value.type)) + { + if (type == ValueType::Integer) + { + i = value.i; + } + else if (type == ValueType::String) + { + s = move(value.s); + } + } + Value(int i) : type(ValueType::Integer), i(i) {} + Value(const string &s) : type(ValueType::String), s(s) {} + ~Value() {} + + bool is_undefined() const { return type == ValueType::Undefined; } + bool is_int() const { return type == ValueType::Integer; } + bool is_str() const { return type == ValueType::String; } + + int to_int() const { return i; } + const string &to_str() const { return s; } + +private: + ValueType type; + const union + { + int i; + string s; + }; +}; + +class Ast +{ +public: + virtual ~Ast(){}; + virtual Value eval(int t) const = 0; + virtual operator string() const = 0; +}; + +using AstPtr = unique_ptr; + +class Undefined : public Ast +{ +public: + Value eval(int t) const { return Value(); } + operator string() const { return "UNDEFINED"; } +}; + +class Identifier : public Ast +{ +public: + Value eval(int t) const { return t; } + operator string() const { return "t"; } +}; + +class Integer : public Ast +{ +public: + Integer(int value) : value(value) {} + Value eval(int t) const { return value; } + operator string() const { return to_string(value); } + +private: + const int value; +}; + +class String : public Ast +{ +public: + String(const string &value) : value(value) {} + Value eval(int t) const { return value; } + operator string() const { return "\"" + value + "\""; } + +private: + const string value; +}; + +class UnaryOperator : public Ast +{ +public: + UnaryOperator(AstPtr &&inner) : inner(move(inner)) {} + + operator string() const + { + return "(" + operand() + inner->operator string() + ")"; + } + +protected: + virtual string operand() const = 0; + + AstPtr inner; +}; + +class Negate : public UnaryOperator +{ +public: + using UnaryOperator::UnaryOperator; + + Value eval(int t) const + { + Value val = inner->eval(t); + if (val.is_int()) + { + return -val.to_int(); + } + return Value(); + } + +protected: + string operand() const { return "-"; } +}; + +class BitwiseComplement : public UnaryOperator +{ +public: + using UnaryOperator::UnaryOperator; + + Value eval(int t) const + { + Value val = inner->eval(t); + if (val.is_int()) + { + return ~val.to_int(); + } + return Value(); + } + +protected: + string operand() const { return "-"; } +}; + +class Not : public UnaryOperator +{ +public: + using UnaryOperator::UnaryOperator; + + Value eval(int t) const + { + Value val = inner->eval(t); + if (!val.is_int()) + { + return Value(); + } + return !val.to_int(); + } + +protected: + string operand() const { return "-"; } +}; + +class BinaryOperator : public Ast +{ +public: + explicit BinaryOperator(AstPtr left, AstPtr right) + : left(move(left)), right(move(right)) + { + } + + operator string() const + { + return "(" + left->operator string() + operand() + + right->operator string() + ")"; + } + +protected: + virtual string operand() const = 0; + + AstPtr left; + AstPtr right; +}; + +class Subscript : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value s_val = left->eval(t); + if (!s_val.is_str()) + { + return Value(); + } + + Value i_val = right->eval(t); + if (!i_val.is_int()) + { + return Value(); + } + + string s = s_val.to_str(); + int i = i_val.to_int(); + if (i < 0 || i >= s.length()) + { + return Value(); + } + + return s[i]; + } + + operator string() const + { + return "(" + left->operator string() + "[" + right->operator string() + + "])"; + } + +protected: + string operand() const { return "["; } +}; + +class Add : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() + b.to_int(); + } + +protected: + string operand() const { return "+"; } +}; + +class Subtract : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() - b.to_int(); + } + +protected: + string operand() const { return "-"; } +}; + +class Multiply : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() * b.to_int(); + } + +protected: + string operand() const { return "*"; } +}; + +class Divide : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + int b_val = b.to_int(); + if (b_val == 0) + { + return Value(); + } + + return a.to_int() / b.to_int(); + } + +protected: + string operand() const { return "/"; } +}; + +class Modulo : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + int b_val = b.to_int(); + if (b_val == 0) + { + return Value(); + } + + return a.to_int() % b.to_int(); + } + +protected: + string operand() const { return "%"; } +}; + +class BitwiseAnd : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() & b.to_int(); + } + +protected: + string operand() const { return "&"; } +}; + +class BitwiseOr : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() | b.to_int(); + } + +protected: + string operand() const { return "|"; } +}; + +class BitwiseXor : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() ^ b.to_int(); + } + +protected: + string operand() const { return "^"; } +}; + +class BitwiseShiftLeft : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() << b.to_int(); + } + +protected: + string operand() const { return "<<"; } +}; + +class BitwiseShiftRight : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() >> b.to_int(); + } + +protected: + string operand() const { return ">>"; } +}; + +class LessThan : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() < b.to_int(); + } + +protected: + string operand() const { return "<"; } +}; + +class LessThanEqual : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() <= b.to_int(); + } + +protected: + string operand() const { return "<="; } +}; + +class GreaterThan : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() > b.to_int(); + } + +protected: + string operand() const { return ">"; } +}; + +class GreaterThanEqual : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() >= b.to_int(); + } + +protected: + string operand() const { return ">="; } +}; + +class Equal : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() == b.to_int(); + } + +protected: + string operand() const { return "=="; } +}; + +class NotEqual : public BinaryOperator +{ +public: + using BinaryOperator::BinaryOperator; + + Value eval(int t) const + { + Value a = left->eval(t); + if (!a.is_int()) + { + return Value(); + } + + Value b = right->eval(t); + if (!b.is_int()) + { + return Value(); + } + + return a.to_int() != b.to_int(); + } + +protected: + string operand() const { return "!="; } +}; + +class TernaryIf : public Ast +{ +public: + TernaryIf(AstPtr pred, AstPtr pass, AstPtr fail) + : pred(move(pred)), pass(move(pass)), fail(move(fail)) + { + } + + Value eval(int t) const + { + Value p = pred->eval(t); + if (!p.is_int()) + { + return Value(); + } + + return p.to_int() ? pass->eval(t) : fail->eval(t); + } + + operator string() const + { + return "(" + pred->operator string() + "?" + pass->operator string() + + ":" + fail->operator string() + ")"; + } + +private: + AstPtr pred; + AstPtr pass; + AstPtr fail; +}; + +} // namespace bb diff --git a/include/expression.hpp b/include/expression.hpp deleted file mode 100644 index fb6dcd5..0000000 --- a/include/expression.hpp +++ /dev/null @@ -1,354 +0,0 @@ -#pragma once - -#include -#include - -using namespace std; - -namespace bb -{ - class Expression - { - public: - virtual ~Expression(){}; - virtual int evaluate(int t) const = 0; - virtual string to_string() const = 0; - }; - - using ExpressionPtr = unique_ptr; - - class Variable : public Expression - { - public: - int evaluate(int t) const - { - return t; - }; - - string to_string() const - { - return "t"; - } - }; - - class Constant : public Expression - { - public: - Constant(int c) : c(c){}; - - int evaluate(int t) const - { - return c; - }; - - string to_string() const - { - return std::to_string(c); - } - - private: - int c; - }; - - class BinaryOperator : public Expression - { - public: - explicit BinaryOperator(ExpressionPtr left, ExpressionPtr right) : left(move(left)), right(move(right)){}; - - string to_string() const - { - return "(" + left->to_string() + operand() + right->to_string() + ")"; - } - - protected: - virtual string operand() const = 0; - - ExpressionPtr left; - ExpressionPtr right; - }; - - class Add : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) + right->evaluate(t); - }; - - protected: - string operand() const - { - return "+"; - } - }; - - class Subtract : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) - right->evaluate(t); - }; - - protected: - string operand() const - { - return "-"; - } - }; - - class Multiply : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) * right->evaluate(t); - }; - - protected: - string operand() const - { - return "*"; - } - }; - - class Divide : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - int divisor = right->evaluate(t); - // Dividing by zero will crash the SuperCollider server - if (divisor == 0) - { - return 0; - } - return left->evaluate(t) / divisor; - }; - - protected: - string operand() const - { - return "/"; - } - }; - - class Modulo : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - - int divisor = right->evaluate(t); - // Dividing by zero will crash the SuperCollider server - if (divisor == 0) - { - return 0; - } - return left->evaluate(t) % divisor; - }; - - protected: - string operand() const - { - return "%"; - } - }; - - class BitwiseAnd : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) & right->evaluate(t); - }; - - protected: - string operand() const - { - return "&"; - } - }; - - class BitwiseOr : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) | right->evaluate(t); - }; - - protected: - string operand() const - { - return "|"; - } - }; - - class BitwiseXor : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) ^ right->evaluate(t); - }; - - protected: - string operand() const - { - return "^"; - } - }; - - class BitwiseShiftLeft : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) << right->evaluate(t); - }; - - protected: - string operand() const - { - return "<<"; - } - }; - - class BitwiseShiftRight : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) >> right->evaluate(t); - }; - - protected: - string operand() const - { - return ">>"; - } - }; - - class LessThan : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) < right->evaluate(t); - }; - - protected: - string operand() const - { - return "<"; - } - }; - - class GreaterThan : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) > right->evaluate(t); - }; - - protected: - string operand() const - { - return ">"; - } - }; - - class LessThanOrEqual : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) <= right->evaluate(t); - }; - - protected: - string operand() const - { - return "<="; - } - }; - - class GreaterThanOrEqual : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) >= right->evaluate(t); - }; - - protected: - string operand() const - { - return ">="; - } - }; - - class Equal : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) == right->evaluate(t); - }; - - protected: - string operand() const - { - return "=="; - } - }; - - class NotEqual : public BinaryOperator - { - public: - using BinaryOperator::BinaryOperator; - - int evaluate(int t) const - { - return left->evaluate(t) != right->evaluate(t); - }; - - protected: - string operand() const - { - return "!="; - } - }; -} diff --git a/include/lex.hpp b/include/lex.hpp index 244181b..1610ba5 100644 --- a/include/lex.hpp +++ b/include/lex.hpp @@ -7,5 +7,191 @@ using namespace std; namespace bb { - vector lex(string &input); + +/** Set of token types that can be parsed */ +enum class TokenType +{ + Unknown, + Identifier, + Integer, + String, + LeftParen, + RightParen, + LeftBracket, + RightBracket, + Plus, + Minus, + Multiply, + Divide, + Modulo, + BitwiseAnd, + BitwiseOr, + BitwiseXor, + BitwiseShiftLeft, + BitwiseShiftRight, + BitwiseComplement, + LessThan, + GreaterThan, + LessThanEqual, + GreaterThanEqual, + Equal, + NotEqual, + Not, + And, + Or, + TernaryIf, + TernaryElse, +}; + +inline ostream &operator<<(std::ostream &os, const TokenType &type) +{ + + if (type == TokenType::Identifier) + { + os << string{"Identifier"}; + } + else if (type == TokenType::Integer) + { + os << string{"Integer"}; + } + else if (type == TokenType::String) + { + os << string{"String"}; + } + else if (type == TokenType::LeftParen) + { + os << string{"LeftParen"}; + } + else if (type == TokenType::RightParen) + { + os << string{"RightParen"}; + } + else if (type == TokenType::LeftBracket) + { + os << string{"LeftBracket"}; + } + else if (type == TokenType::RightBracket) + { + os << string{"RightBracket"}; + } + else if (type == TokenType::Plus) + { + os << string{"Plus"}; + } + else if (type == TokenType::Minus) + { + os << string{"Minus"}; + } + else if (type == TokenType::Multiply) + { + os << string{"Multiply"}; + } + else if (type == TokenType::Divide) + { + os << string{"Divide"}; + } + else if (type == TokenType::Modulo) + { + os << string{"Modulo"}; + } + else if (type == TokenType::BitwiseAnd) + { + os << string{"BitwiseAnd"}; + } + else if (type == TokenType::BitwiseOr) + { + os << string{"BitwiseOr"}; + } + else if (type == TokenType::BitwiseXor) + { + os << string{"BitwiseXor"}; + } + else if (type == TokenType::BitwiseShiftLeft) + { + os << string{"BitwiseShiftLeft"}; + } + else if (type == TokenType::BitwiseShiftRight) + { + os << string{"BitwiseShiftRight"}; + } + else if (type == TokenType::BitwiseComplement) + { + os << string{"BitwiseComplement"}; + } + else if (type == TokenType::LessThan) + { + os << string{"LessThan"}; + } + else if (type == TokenType::GreaterThan) + { + os << string{"GreaterThan"}; + } + else if (type == TokenType::LessThanEqual) + { + os << string{"LessThanEqual"}; + } + else if (type == TokenType::GreaterThanEqual) + { + os << string{"GreaterThanEqual"}; + } + else if (type == TokenType::Equal) + { + os << string{"Equal"}; + } + else if (type == TokenType::NotEqual) + { + os << string{"NotEqual"}; + } + else if (type == TokenType::Not) + { + os << string{"Not"}; + } + else if (type == TokenType::And) + { + os << string{"And"}; + } + else if (type == TokenType::Or) + { + os << string{"Or"}; + } + else if (type == TokenType::TernaryIf) + { + os << string{"TernaryIf"}; + } + else if (type == TokenType::TernaryElse) + { + os << string{"TernaryElse"}; + } + else + { + os << string{"Unknown"}; + } + return os; } + +/** A token and the category that it belongs to */ +struct Token +{ + TokenType type; + string value; +}; + +inline bool operator==(const Token &t1, const Token &t2) +{ + return t1.type == t2.type && t1.value == t2.value; +} + +inline ostream &operator<<(std::ostream &os, const Token &token) +{ + os << string{"Token{"}; + os << token.type; + os << string{", "}; + os << token.value; + os << string{"}"}; + return os; +} + +/** Split a string into a list of component tokens */ +vector lex(const string &input); + +} // namespace bb diff --git a/include/parse.hpp b/include/parse.hpp index 22313f7..c45f664 100644 --- a/include/parse.hpp +++ b/include/parse.hpp @@ -1,12 +1,14 @@ #pragma once -#include +#include "ast.hpp" -#include "expression.hpp" +#include using namespace std; namespace bb { - ExpressionPtr parse(string &input); -} + +AstPtr parse(const string &tokens); + +} // namespace bb diff --git a/plugins/ByteBeat/ByteBeat.cpp b/plugins/ByteBeat/ByteBeat.cpp index 1bc62ec..87c687c 100644 --- a/plugins/ByteBeat/ByteBeat.cpp +++ b/plugins/ByteBeat/ByteBeat.cpp @@ -1,7 +1,7 @@ #include -#include #include +#include #include "ByteBeat.hpp" #include "parse.hpp" @@ -12,74 +12,77 @@ static InterfaceTable *ft; namespace ByteBeat { - ByteBeat::ByteBeat() +ByteBeat::ByteBeat() +{ + mCalcFunc = make_calc_function(); + + // Initialize with no audio to avoid popping/unitialized buffer. The + // Undefined expression will be evaluated as 0.0 when sampled. + mAst = bb::AstPtr(new bb::Undefined()); +} + +void ByteBeat::parse(const char *input) +{ + string s = input; + + try { - mCalcFunc = make_calc_function(); - - // Initialize with no audio to avoid popping/unitialized buffer. - // Expressions are evaluated as 32-bit signed integers, then cast to - // 8-bit unsigned integers, then transformed to floats with a range of - // +/-1.0. An expression that emits a constant value of 128 will - // correspond roughly to 0.0 at the output. - mExpression = bb::ExpressionPtr(new bb::Constant(128)); + mAst = bb::parse(s); } - - void ByteBeat::parse(const char *input) + catch (invalid_argument &ex) { - string s = input; - - try - { - mExpression = bb::parse(s); - } - catch (invalid_argument &ex) - { - Print("%s", ex.what()); - // TODO: Send back to client somehow. May not be possible without a - // more general sendResponse interface. - // See: https://scsynth.org/t/scsynth-plugincmd-and-sending-responses/2638 - } + Print("%s", ex.what()); + // TODO: Send back to client somehow. May not be possible without a + // more general sendResponse interface. + // See: + // https://scsynth.org/t/scsynth-plugincmd-and-sending-responses/2638 } +} + +void ByteBeat::next(int nSamples) +{ + const float *tBuf = in(0); + float *outBuf = out(0); - void ByteBeat::next(int nSamples) + for (int i = 0; i < nSamples; ++i) { - const float *tBuf = in(0); - float *outBuf = out(0); + int t = tBuf[i]; + float sample; - for (int i = 0; i < nSamples; ++i) + // In most cases, the t input will change slower than the sample + // rate. Save some cycles by only evaluating the expression when + // t changes. + if (t == mPrevT) { - int t = tBuf[i]; - float sample; - - // In most cases, the t input will change slower than the sample - // rate. Save some cycles by only evaluating the expression when - // t changes. - if (t == mPrevT) + sample = mPrevSample; + } + else + { + bb::Value val = mAst->eval(t); + if (val.is_int()) { - sample = mPrevSample; + uint8_t byte = val.to_int(); + sample = 2 * (float)byte / 255 - 1; } else { - uint8_t byte = mExpression->evaluate(t); - sample = 2 * (float)byte / 255 - 1; - mPrevT = t; - mPrevSample = sample; + sample = 0; } - - outBuf[i] = sample; + mPrevT = t; + mPrevSample = sample; } - } - /** - * Unit command callback for the /eval command. Expects args to contain - * a single string argument representing the new bytebeat expression. - */ - void evalCmd(ByteBeat *unit, sc_msg_iter *args) - { - unit->parse(args->gets()); + outBuf[i] = sample; } } +/** + * Unit command callback for the /eval command. Expects args to contain + * a single string argument representing the new bytebeat expression. + */ +void evalCmd(ByteBeat *unit, sc_msg_iter *args) { unit->parse(args->gets()); } +} // namespace ByteBeat + PluginLoad(ByteBeat) { ft = inTable; diff --git a/plugins/ByteBeat/ByteBeat.hpp b/plugins/ByteBeat/ByteBeat.hpp index e7ddfc8..44e267a 100644 --- a/plugins/ByteBeat/ByteBeat.hpp +++ b/plugins/ByteBeat/ByteBeat.hpp @@ -2,45 +2,45 @@ #include -#include "expression.hpp" +#include "ast.hpp" namespace ByteBeat { +/** + * ByteBeat is able to parse simple mathematical expressions and evaluate + * them to produce audio samples. + * + * ByteBeat will not produce any audio output initially. The /eval unit + * command must be used to send expressions to the synth to be parsed. Once + * an expression has been parsed, it will become the active expression and + * begin producing audio samples. + * + * ByteBeat expects a single audio-rate input, "t", that is passed to the + * expression. + */ +class ByteBeat : public SCUnit +{ +public: + ByteBeat(); + /** - * ByteBeat is able to parse simple mathematical expressions and evaluate - * them to produce audio samples. - * - * ByteBeat will not produce any audio output initially. The /eval unit - * command must be used to send expressions to the synth to be parsed. Once - * an expression has been parsed, it will become the active expression and - * begin producing audio samples. - * - * ByteBeat expects a single audio-rate input, "t", that is passed to the - * expression. + * Parse the incoming expression and replace the existing expression. + * Does not replace the existing expression if the incoming expression + * cannot be parsed. */ - class ByteBeat : public SCUnit - { - public: - ByteBeat(); - - /** - * Parse the incoming expression and replace the existing expression. - * Does not replace the existing expression if the incoming expression - * cannot be parsed. - */ - void parse(const char *input); + void parse(const char *input); - private: - /** - * Evaluate the current bytebeat expression for the given number of - * samples. - */ - void next(int nSamples); +private: + /** + * Evaluate the current bytebeat expression for the given number of + * samples. + */ + void next(int nSamples); - float mPrevSample = 0; - int mPrevT = 0; + float mPrevSample = 0; + int mPrevT = 0; - /** bytebeat expression used to generate audio samples */ - bb::ExpressionPtr mExpression; - }; -} + /** bytebeat expression used to generate audio samples */ + bb::AstPtr mAst; +}; +} // namespace ByteBeat diff --git a/src/lex.cpp b/src/lex.cpp index 7cb6072..525e75c 100644 --- a/src/lex.cpp +++ b/src/lex.cpp @@ -1,102 +1,276 @@ +#include "lex.hpp" + +#include #include #include -#include -#include "lex.hpp" +namespace bb +{ -using namespace std; +TokenType get_terminal_type(char c); -namespace bb +vector lex(const string &input) { - bool is_whitespace(char c); - bool is_terminal(char c); + vector tokens; - vector lex(string &input) + for (int i = 0; i < input.length(); ++i) { - vector tokens; + char c = input[i]; - for (auto it = input.begin(); it != input.end(); ++it) + // Whitespace + if (c == ' ') { - char c = *it; - if (is_whitespace(c)) + continue; + } + + // Integer constant + if (isdigit(c)) + { + string buffer{c}; + bool is_hex = false; + while (i < input.length() - 1) { - continue; + c = input[i + 1]; + if (!is_hex && !isdigit(c) || is_hex && !isxdigit(c)) + { + if (tolower(c) == 'x' && buffer.length() == 1 && + buffer[0] == '0') + { + is_hex = true; + } + else + { + break; + } + } + buffer.push_back(c); + ++i; } - if (is_terminal(c)) + if (buffer.length() == 2 && buffer[1] == 'x') { - tokens.push_back(string{c}); - continue; + throw invalid_argument("Invalid hexadecimal integer: 0x"); } - if (c == '<' || c == '>') + tokens.push_back({ + TokenType::Integer, + buffer, + }); + continue; + } + + // Terminal tokens + auto terminal_type = get_terminal_type(c); + if (terminal_type != TokenType::Unknown) + { + tokens.push_back(Token{ + terminal_type, + string{c}, + }); + continue; + } + + // Less than, less than or equal, bitwise shift left + if (c == '<') + { + TokenType type = TokenType::LessThan; + string buffer{c}; + if (i < input.length()) { - string buffer{c}; - char n = *next(it); - if (n == c || n == '=') + c = input[i + 1]; + if (c == '<') + { + type = TokenType::BitwiseShiftLeft; + buffer.push_back(c); + ++i; + } + else if (c == '=') { - buffer.push_back(n); - ++it; + type = TokenType::LessThanEqual; + buffer.push_back(c); + ++i; } - tokens.push_back(buffer); - continue; } + tokens.push_back({type, buffer}); + continue; + } - if (c == '=' || c == '!') + // Greater than, greater than or equal, bitwise shift right + if (c == '>') + { + TokenType type = TokenType::GreaterThan; + string buffer{c}; + if (i < input.length()) { - char n = *next(it); - if (n != '=') + c = input[i + 1]; + if (c == '>') + { + type = TokenType::BitwiseShiftRight; + buffer.push_back(c); + ++i; + } + else if (c == '=') { - auto msg = "invalid character sequence: " + string{n, c}; - throw invalid_argument(msg); + type = TokenType::GreaterThanEqual; + buffer.push_back(c); + ++i; } - tokens.push_back(string{c, n}); - ++it; - continue; } + tokens.push_back({type, buffer}); + continue; + } - if (!isdigit(c)) + // Not, not equal + if (c == '!') + { + TokenType type = TokenType::Not; + string buffer{c}; + if (i < input.length()) { - auto msg = "invalid character: " + string{c}; - throw invalid_argument(msg); + c = input[i + 1]; + if (c == '=') + { + type = TokenType::NotEqual; + buffer.push_back(c); + ++i; + } } + tokens.push_back({type, buffer}); + continue; + } + // Equal + if (c == '=') + { + if (i == input.length() - 1) + { + throw invalid_argument("Invalid token: = (EOF)"); + } + + string buffer{c}; + c = input[i + 1]; + if (c != '=') + { + throw invalid_argument("Invalid token: ="); + } + ++i; + buffer.push_back(c); + tokens.push_back({TokenType::Equal, buffer}); + continue; + } + + // Bitwise and, and + if (c == '&') + { + TokenType type = TokenType::BitwiseAnd; + string buffer{c}; + if (i < input.length()) + { + c = input[i + 1]; + if (c == '&') + { + type = TokenType::And; + buffer.push_back(c); + ++i; + } + } + tokens.push_back({type, buffer}); + continue; + } + + // Bitwise or, or + if (c == '|') + { + TokenType type = TokenType::BitwiseOr; string buffer{c}; - auto peek = next(it); - while (peek != input.end()) + if (i < input.length()) + { + c = input[i + 1]; + if (c == '|') + { + type = TokenType::Or; + buffer.push_back(c); + ++i; + } + } + tokens.push_back({type, buffer}); + continue; + } + + // String literal + if (c == '"') + { + if (i == input.length() - 1) { - c = *peek; - if (!isdigit(c)) + throw invalid_argument("Invalid string: \" (EOF)"); + } + + string buffer; + ++i; + while (i < input.length()) + { + c = input[i]; + if (c == '"') { break; } - buffer.push_back(c); - ++it; - peek = next(it); + buffer.push_back(input[i]); + ++i; } - tokens.push_back(buffer); + + if (i == input.length()) + { + throw invalid_argument("Invalid string: \"" + buffer); + } + + tokens.push_back({TokenType::String, buffer}); + continue; } - return tokens; + // Invalid token + string msg = "Invalid token: "; + msg.push_back(c); + throw invalid_argument(msg); } - bool is_whitespace(char c) - { - return c == ' '; - } + return tokens; +} - bool is_terminal(char c) +TokenType get_terminal_type(char c) +{ + switch (c) { - return c == 't' || - c == '(' || - c == ')' || - c == '+' || - c == '-' || - c == '*' || - c == '/' || - c == '%' || - c == '&' || - c == '|' || - c == '^'; + case 't': + return TokenType::Identifier; + case '+': + return TokenType::Plus; + case '-': + return TokenType::Minus; + case '*': + return TokenType::Multiply; + case '/': + return TokenType::Divide; + case '%': + return TokenType::Modulo; + case '^': + return TokenType::BitwiseXor; + case '~': + return TokenType::BitwiseComplement; + case '?': + return TokenType::TernaryIf; + case ':': + return TokenType::TernaryElse; + case '(': + return TokenType::LeftParen; + case ')': + return TokenType::RightParen; + case '[': + return TokenType::LeftBracket; + case ']': + return TokenType::RightBracket; + default: + return TokenType::Unknown; } } + +} // namespace bb diff --git a/src/main.cpp b/src/main.cpp index e8aa87c..74e0fad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,19 +11,22 @@ int main(int argc, char *argv[]) { cout << endl; cout << " usage:" << endl; - cout << " ./bytebeat [EXPRESSION] | head -c [BYTES] > [OUT].raw" << endl; + cout << " ./bytebeat [EXPRESSION] | head -c [BYTES] > [OUT].raw" + << endl; cout << endl; cout << " expression tokens:" << endl; cout << " t" << endl; + cout << " 1, -1, 0xf, \"foo\"" << endl; cout << " ( ) + - * / %" << endl; - cout << " & | ^ << >>" << endl; - cout << " < > <= >= == !=" << endl; + cout << " & | ^ << >> ~" << endl; + cout << " < > <= >= == != ! ? :" << endl; + cout << " [ ]" << endl; cout << endl; return 1; } string input{argv[1]}; - ExpressionPtr expr; + AstPtr expr; try { expr = parse(input); @@ -37,6 +40,6 @@ int main(int argc, char *argv[]) int t = 0; while (true) { - putchar(expr->evaluate(t++)); + putchar(expr->eval(t++).to_int()); } } diff --git a/src/parse.cpp b/src/parse.cpp index 5404901..2a43e3f 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -1,253 +1,357 @@ #include "parse.hpp" #include "lex.hpp" -#include - namespace bb { - using TokenIter = vector::iterator; - ExpressionPtr parse_expression(TokenIter &it, TokenIter &end); - ExpressionPtr parse_expression_inner(TokenIter &it, - TokenIter &end, - ExpressionPtr lhs, - int min_precedence); - ExpressionPtr parse_primary(TokenIter &it, TokenIter &end); +int get_precedence(TokenType type); +AstPtr make_unary_op(TokenType type, AstPtr inner); +AstPtr make_binary_op(TokenType type, AstPtr left, AstPtr right); + +using TokenIter = vector::iterator; - int get_precedence(string &token); - ExpressionPtr make_binary_op(string &op, ExpressionPtr lhs, ExpressionPtr rhs); +AstPtr parse_expression(TokenIter &it, TokenIter &end); +AstPtr parse_expression_inner(TokenIter &it, TokenIter &end, AstPtr lhs, + int min_precedence); +AstPtr parse_primary(TokenIter &it, TokenIter &end); - ExpressionPtr parse(string &input) +AstPtr parse(const string &input) +{ + auto tokens = lex(input); + if (tokens.empty()) { - vector tokens = lex(input); - if (tokens.empty()) - { - throw invalid_argument("no tokens to parse"); - } + throw invalid_argument("No tokens to parse"); + } - auto it = tokens.begin(); - auto end = tokens.end(); - ExpressionPtr expr = parse_expression(it, end); - if (it != tokens.end()) - { - throw invalid_argument("not all tokens consumed"); - } + TokenIter it = tokens.begin(); + TokenIter end = tokens.end(); + AstPtr expr = parse_expression(it, end); - return expr; + if (it != tokens.end()) + { + throw invalid_argument("Not all tokens consumed"); } - ExpressionPtr parse_expression(TokenIter &it, TokenIter &end) + return expr; +} + +/** + * An operator-precedence parser + * + * See: + * - https://en.wikipedia.org/wiki/Operator-precedence_parser + * - https://en.cppreference.com/w/c/language/operator_precedence + * - https://www.lysator.liu.se/c/ANSI-C-grammar-y.html + */ +AstPtr parse_expression(TokenIter &it, TokenIter &end) +{ + AstPtr lhs = parse_primary(it, end); + return parse_expression_inner(it, end, move(lhs), 0); +} + +AstPtr parse_expression_inner(TokenIter &it, TokenIter &end, AstPtr lhs, + int min_precedence) +{ + if (it == end) { - ExpressionPtr lhs = parse_primary(it, end); - return parse_expression_inner(it, end, move(lhs), 0); + return lhs; } - ExpressionPtr parse_expression_inner(TokenIter &it, - TokenIter &end, - ExpressionPtr lhs, - int min_precedence) + TokenType lookahead = it->type; + int precedence = get_precedence(lookahead); + + while (precedence >= min_precedence) { + TokenType op = lookahead; + ++it; + + AstPtr rhs; + if (op == TokenType::LeftBracket) + { + rhs = parse_expression(it, end); + if (it->type != TokenType::RightBracket) + { + throw invalid_argument("Unbalanced brackets"); + } + ++it; + } + else if (op == TokenType::TernaryIf) + { + auto pass = parse_expression(it, end); + if (it->type != TokenType::TernaryElse) + { + throw invalid_argument("Missing ternary else"); + } + ++it; + auto fail = parse_expression(it, end); + return AstPtr(new TernaryIf(move(lhs), move(pass), move(fail))); + } + else + { + rhs = parse_primary(it, end); + } + if (it == end) { - return lhs; + return make_binary_op(op, move(lhs), move(rhs)); } - string lookahead = *it; - int precedence = get_precedence(lookahead); + lookahead = it->type; + int next_precedence = get_precedence(lookahead); - while (precedence >= min_precedence) + while (next_precedence > precedence) { - string op = lookahead; - ++it; - - ExpressionPtr rhs = parse_primary(it, end); + rhs = parse_expression_inner(it, end, move(rhs), next_precedence); if (it == end) { - return make_binary_op(op, move(lhs), move(rhs)); + next_precedence = -1; + break; } - lookahead = *it; - int next_precedence = get_precedence(lookahead); + lookahead = it->type; + next_precedence = get_precedence(lookahead); + } - while (next_precedence > precedence) - { - rhs = parse_expression_inner(it, end, move(rhs), next_precedence); - if (it == end) - { - next_precedence = -1; - break; - } - - lookahead = *it; - next_precedence = get_precedence(lookahead); - } + precedence = next_precedence; + lhs = make_binary_op(op, move(lhs), move(rhs)); + } - precedence = next_precedence; - lhs = make_binary_op(op, move(lhs), move(rhs)); - } + return lhs; +} - return lhs; +AstPtr parse_primary(TokenIter &it, TokenIter &end) +{ + if (it == end) + { + throw invalid_argument("Expected primary token but got end of input"); } - ExpressionPtr parse_primary(TokenIter &it, TokenIter &end) + TokenType type = it->type; + + if (type == TokenType::Identifier) { - if (it == end) - { - throw invalid_argument("expected token but got end of input"); - } + ++it; + return AstPtr(new Identifier()); + } - string token = *it; + if (type == TokenType::Integer) + { + int n = stoi(it->value, 0, 0); // automatically detect base + ++it; + return AstPtr(new Integer(n)); + } - if (token == "t") - { - ++it; - return ExpressionPtr(new Variable()); - } + if (type == TokenType::String) + { + string s = it->value; + ++it; + return AstPtr(new String(s)); + } - if (token == "(") + if (type == TokenType::LeftParen) + { + ++it; + AstPtr inner = parse_expression(it, end); + if (it == end || it->type != TokenType::RightParen) { - ++it; - auto expr = parse_expression(it, end); - if (it == end || *it != ")") - { - throw invalid_argument("unbalanced parenthesis: "); - } - ++it; - return expr; + throw invalid_argument("Unbalanced parentheses"); } + ++it; + return inner; + } - try - { - int n = stoi(token); - ++it; - return ExpressionPtr(new Constant(n)); - } - catch (invalid_argument &ex) + if (type == TokenType::Minus || type == TokenType::BitwiseComplement || + type == TokenType::Not) + { + ++it; + if (it == end) { - throw invalid_argument("unexpected primary token: " + token); + throw invalid_argument("Expected primary token for unary prefix " + "operator but got end-of-input"); } + AstPtr inner = parse_primary(it, end); + return make_unary_op(type, move(inner)); } - int get_precedence(string &token) + throw invalid_argument("Unexpected primary token"); +} + +int get_precedence(TokenType type) +{ + if (type == TokenType::LeftBracket) { - if (token == "*" || token == "/" || token == "%") - { - return 7; - } - else if (token == "+" || token == "-") - { - return 6; - } - else if (token == "<<" || token == ">>") - { - return 5; - } - else if (token == "<" || token == "<=" || token == ">" || token == ">=") - { - return 4; - } - else if (token == "==" || token == "!=") - { - return 3; - } - else if (token == "&") - { - return 2; - } - else if (token == "^") - { - return 1; - } - else if (token == "|") - { - return 0; - } + return 11; + } - return -1; + if (type == TokenType::Multiply || type == TokenType::Divide || + type == TokenType::Modulo) + { + return 10; } - ExpressionPtr make_binary_op(std::string &op, ExpressionPtr lhs, ExpressionPtr rhs) + if (type == TokenType::Plus || type == TokenType::Minus) { - if (op == "+") - { - return ExpressionPtr(new Add(move(lhs), move(rhs))); - } + return 9; + } - if (op == "-") - { - return ExpressionPtr(new Subtract(move(lhs), move(rhs))); - } + if (type == TokenType::BitwiseShiftLeft || + type == TokenType::BitwiseShiftRight) + { + return 8; + } - if (op == "*") - { - return ExpressionPtr(new Multiply(move(lhs), move(rhs))); - } + if (type == TokenType::LessThan || type == TokenType::LessThanEqual || + type == TokenType::GreaterThan || type == TokenType::GreaterThanEqual) + { + return 7; + } - if (op == "/") - { - return ExpressionPtr(new Divide(move(lhs), move(rhs))); - } + if (type == TokenType::Equal || type == TokenType::NotEqual) + { + return 6; + } - if (op == "%") - { - return ExpressionPtr(new Modulo(move(lhs), move(rhs))); - } + if (type == TokenType::BitwiseAnd) + { + return 5; + } - if (op == "&") - { - return ExpressionPtr(new BitwiseAnd(move(lhs), move(rhs))); - } + if (type == TokenType::BitwiseXor) + { + return 4; + } - if (op == "|") - { - return ExpressionPtr(new BitwiseOr(move(lhs), move(rhs))); - } + if (type == TokenType::BitwiseOr) + { + return 3; + } - if (op == "^") - { - return ExpressionPtr(new BitwiseXor(move(lhs), move(rhs))); - } + if (type == TokenType::And) + { + return 2; + } - if (op == "<<") - { - return ExpressionPtr(new BitwiseShiftLeft(move(lhs), move(rhs))); - } + if (type == TokenType::Or) + { + return 1; + } - if (op == ">>") - { - return ExpressionPtr(new BitwiseShiftRight(move(lhs), move(rhs))); - } + if (type == TokenType::TernaryIf) + { + return 0; + } - if (op == "<") - { - return ExpressionPtr(new LessThan(move(lhs), move(rhs))); - } + return -1; +} - if (op == ">") - { - return ExpressionPtr(new GreaterThan(move(lhs), move(rhs))); - } +AstPtr make_unary_op(TokenType type, AstPtr inner) +{ + if (type == TokenType::Minus) + { + return AstPtr(new Negate(move(inner))); + } - if (op == "<=") - { - return ExpressionPtr(new LessThanOrEqual(move(lhs), move(rhs))); - } + if (type == TokenType::BitwiseComplement) + { + return AstPtr(new BitwiseComplement(move(inner))); + } - if (op == ">=") - { - return ExpressionPtr(new GreaterThanOrEqual(move(lhs), move(rhs))); - } + if (type == TokenType::Not) + { + return AstPtr(new Not(move(inner))); + } - if (op == "==") - { - return ExpressionPtr(new Equal(move(lhs), move(rhs))); - } + throw invalid_argument("Unrecognized unary operator"); +} - if (op == "!=") - { - return ExpressionPtr(new NotEqual(move(lhs), move(rhs))); - } +AstPtr make_binary_op(TokenType type, AstPtr left, AstPtr right) +{ + if (type == TokenType::LeftBracket) + { + return AstPtr(new Subscript(move(left), move(right))); + } - throw invalid_argument("unrecognized operator"); + if (type == TokenType::Plus) + { + return AstPtr(new Add(move(left), move(right))); } + + if (type == TokenType::Minus) + { + return AstPtr(new Subtract(move(left), move(right))); + } + + if (type == TokenType::Multiply) + { + return AstPtr(new Multiply(move(left), move(right))); + } + + if (type == TokenType::Divide) + { + return AstPtr(new Divide(move(left), move(right))); + } + + if (type == TokenType::Modulo) + { + return AstPtr(new Modulo(move(left), move(right))); + } + + if (type == TokenType::BitwiseAnd) + { + return AstPtr(new BitwiseAnd(move(left), move(right))); + } + + if (type == TokenType::BitwiseOr) + { + return AstPtr(new BitwiseOr(move(left), move(right))); + } + + if (type == TokenType::BitwiseXor) + { + return AstPtr(new BitwiseXor(move(left), move(right))); + } + + if (type == TokenType::BitwiseShiftLeft) + { + return AstPtr(new BitwiseShiftLeft(move(left), move(right))); + } + + if (type == TokenType::BitwiseShiftRight) + { + return AstPtr(new BitwiseShiftRight(move(left), move(right))); + } + + if (type == TokenType::LessThan) + { + return AstPtr(new LessThan(move(left), move(right))); + } + + if (type == TokenType::GreaterThan) + { + return AstPtr(new GreaterThan(move(left), move(right))); + } + + if (type == TokenType::LessThanEqual) + { + return AstPtr(new LessThanEqual(move(left), move(right))); + } + + if (type == TokenType::GreaterThanEqual) + { + return AstPtr(new GreaterThanEqual(move(left), move(right))); + } + + if (type == TokenType::Equal) + { + return AstPtr(new Equal(move(left), move(right))); + } + + if (type == TokenType::NotEqual) + { + return AstPtr(new NotEqual(move(left), move(right))); + } + + throw invalid_argument("Unrecognized operator"); } + +} // namespace bb diff --git a/test/test_ast.cpp b/test/test_ast.cpp new file mode 100644 index 0000000..502942d --- /dev/null +++ b/test/test_ast.cpp @@ -0,0 +1,156 @@ +#define CATCH_CONFIG_ENABLE_BENCHMARKING +#include + +#include "ast.hpp" + +using namespace std; +using namespace bb; + +TEST_CASE("ast", "[ast]") +{ + SECTION("array subscript") + { + AstPtr l = AstPtr(new String("foo")); + AstPtr r = AstPtr(new Identifier()); + AstPtr ast = AstPtr(new Subscript(move(l), move(r))); + Value val = ast->eval(0); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 102); + } + + SECTION("array subscript above bounds") + { + AstPtr l = AstPtr(new String("foo")); + AstPtr r = AstPtr(new Identifier()); + AstPtr ast = AstPtr(new Subscript(move(l), move(r))); + Value val = ast->eval(3); + REQUIRE(val.is_undefined()); + } + + SECTION("array subscript below bounds") + { + AstPtr l = AstPtr(new String("foo")); + AstPtr r = AstPtr(new Identifier()); + AstPtr ast = AstPtr(new Subscript(move(l), move(r))); + Value val = ast->eval(-1); + REQUIRE(val.is_undefined()); + } + + SECTION("add") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(1)); + AstPtr ast = AstPtr(new Add(move(l), move(r))); + Value val = ast->eval(2); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 3); + } + + SECTION("subtract") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(1)); + AstPtr ast = AstPtr(new Subtract(move(l), move(r))); + Value val = ast->eval(2); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 1); + } + + SECTION("multiply") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(2)); + AstPtr ast = AstPtr(new Multiply(move(l), move(r))); + Value val = ast->eval(3); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 6); + } + + SECTION("divide") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(2)); + AstPtr ast = AstPtr(new Divide(move(l), move(r))); + Value val = ast->eval(4); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 2); + } + + SECTION("divide by zero") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(0)); + AstPtr ast = AstPtr(new Divide(move(l), move(r))); + Value val = ast->eval(1); + REQUIRE(val.is_undefined()); + } + + SECTION("modulo") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(2)); + AstPtr ast = AstPtr(new Modulo(move(l), move(r))); + Value val = ast->eval(3); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 1); + } + + SECTION("modulo by zero") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(0)); + AstPtr ast = AstPtr(new Modulo(move(l), move(r))); + Value val = ast->eval(1); + REQUIRE(val.is_undefined()); + } + + SECTION("bitwise and") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(3)); + AstPtr ast = AstPtr(new BitwiseAnd(move(l), move(r))); + Value val = ast->eval(5); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 1); + } + + SECTION("bitwise or") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(2)); + AstPtr ast = AstPtr(new BitwiseOr(move(l), move(r))); + Value val = ast->eval(1); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 3); + } + + SECTION("bitwise xor") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(3)); + AstPtr ast = AstPtr(new BitwiseXor(move(l), move(r))); + Value val = ast->eval(1); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 2); + } + + SECTION("bitwise shift left") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(1)); + AstPtr ast = AstPtr(new BitwiseShiftLeft(move(l), move(r))); + Value val = ast->eval(2); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 4); + } + + SECTION("bitwise shift right") + { + AstPtr l = AstPtr(new Identifier()); + AstPtr r = AstPtr(new Integer(1)); + AstPtr ast = AstPtr(new BitwiseShiftRight(move(l), move(r))); + Value val = ast->eval(4); + REQUIRE(val.is_int()); + REQUIRE(val.to_int() == 2); + } +} diff --git a/test/test_expression.cpp b/test/test_expression.cpp deleted file mode 100644 index cd88d26..0000000 --- a/test/test_expression.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include "expression.hpp" - -using namespace std; -using namespace bb; - -TEST_CASE("expression", "[expression]") -{ - SECTION("constant") - { - REQUIRE(Constant(99).evaluate(42) == 99); - } - - SECTION("variable") - { - REQUIRE(Variable().evaluate(42) == 42); - } - - SECTION("add") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(99)); - REQUIRE(Add(move(left), move(right)).evaluate(1) == 100); - } - - SECTION("subtract") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(99)); - REQUIRE(Subtract(move(left), move(right)).evaluate(100) == 1); - } - - SECTION("multiply") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(2)); - REQUIRE(Multiply(move(left), move(right)).evaluate(2) == 4); - } - - SECTION("divide") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(2)); - REQUIRE(Divide(move(left), move(right)).evaluate(3) == 1); - } - - SECTION("modulo") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(2)); - REQUIRE(Modulo(move(left), move(right)).evaluate(3) == 1); - } - - SECTION("bitwise and") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(2)); - REQUIRE(BitwiseAnd(move(left), move(right)).evaluate(1) == 0); - } - - SECTION("bitwise or") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(2)); - REQUIRE(BitwiseOr(move(left), move(right)).evaluate(1) == 3); - } - - SECTION("bitwise xor") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - REQUIRE(BitwiseXor(move(left), move(right)).evaluate(1) == 0); - } - - SECTION("bitwise shift left") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - REQUIRE(BitwiseShiftLeft(move(left), move(right)).evaluate(1) == 2); - } - - SECTION("bitwise shift right") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - REQUIRE(BitwiseShiftRight(move(left), move(right)).evaluate(2) == 1); - } - - SECTION("less than") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(2)); - LessThan lt(move(left), move(right)); - REQUIRE(lt.evaluate(1) == 1); - REQUIRE(lt.evaluate(2) == 0); - REQUIRE(lt.evaluate(3) == 0); - } - - SECTION("greater than") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - GreaterThan gt(move(left), move(right)); - REQUIRE(gt.evaluate(2) == 1); - REQUIRE(gt.evaluate(1) == 0); - REQUIRE(gt.evaluate(0) == 0); - } - - SECTION("less than or equal") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - LessThanOrEqual lte(move(left), move(right)); - REQUIRE(lte.evaluate(0) == 1); - REQUIRE(lte.evaluate(1) == 1); - REQUIRE(lte.evaluate(2) == 0); - } - - SECTION("greater than or equal") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - GreaterThanOrEqual gte(move(left), move(right)); - REQUIRE(gte.evaluate(2) == 1); - REQUIRE(gte.evaluate(1) == 1); - REQUIRE(gte.evaluate(0) == 0); - } - - SECTION("equal") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - Equal eq(move(left), move(right)); - REQUIRE(eq.evaluate(1) == 1); - REQUIRE(eq.evaluate(2) == 0); - } - - SECTION("not equal") - { - ExpressionPtr left = ExpressionPtr(new Variable()); - ExpressionPtr right = ExpressionPtr(new Constant(1)); - NotEqual neq(move(left), move(right)); - REQUIRE(neq.evaluate(1) == 0); - REQUIRE(neq.evaluate(2) == 1); - } -} diff --git a/test/test_lex.cpp b/test/test_lex.cpp index c33e2dc..adf6846 100644 --- a/test/test_lex.cpp +++ b/test/test_lex.cpp @@ -1,101 +1,223 @@ +#define CATCH_CONFIG_ENABLE_BENCHMARKING #include + #include "lex.hpp" using namespace std; using namespace bb; -TEST_CASE("lex empty input", "[lex]") +TEST_CASE("lex", "[lex]") { - string input = ""; - REQUIRE(lex(input).empty()); -} + SECTION("skip whitespace") + { + string in = "t + 1"; + vector out = { + Token{TokenType::Identifier, "t"}, + Token{TokenType::Plus, "+"}, + Token{TokenType::Integer, "1"}, + }; + REQUIRE(lex(in) == out); + } -TEST_CASE("single-char tokens", "[lex]") -{ - string input = "t()+-*/%&|^"; - vector expected = {"t", "(", ")", "+", "-", "*", "/", "%", "&", "|", "^"}; - REQUIRE(lex(input) == expected); -} + SECTION("terminal tokens") + { + string in = "t+-*/%^~?:()[]"; + vector out = { + Token{TokenType::Identifier, "t"}, + Token{TokenType::Plus, "+"}, + Token{TokenType::Minus, "-"}, + Token{TokenType::Multiply, "*"}, + Token{TokenType::Divide, "/"}, + Token{TokenType::Modulo, "%"}, + Token{TokenType::BitwiseXor, "^"}, + Token{TokenType::BitwiseComplement, "~"}, + Token{TokenType::TernaryIf, "?"}, + Token{TokenType::TernaryElse, ":"}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::LeftBracket, "["}, + Token{TokenType::RightBracket, "]"}, + }; + REQUIRE(lex(in) == out); + } -TEST_CASE("whitespace", "[lex]") -{ - string input = " ( t ) "; - vector expected = {"(", "t", ")"}; - REQUIRE(lex(input) == expected); -} + SECTION("multi-digit numbers") + { + string in = "1234"; + vector out = {Token{TokenType::Integer, "1234"}}; + REQUIRE(lex(in) == out); + } -TEST_CASE("integers", "[lex]") -{ - string input = "(t+1)"; - vector expected = {"(", "t", "+", "1", ")"}; - REQUIRE(lex(input) == expected); -} + SECTION("hex numbers") + { + string in = "0xfff"; + vector out = {Token{TokenType::Integer, "0xfff"}}; + REQUIRE(lex(in) == out); + } -TEST_CASE("multi-digit integers", "[lex]") -{ - string input = "t&63"; - vector expected = {"t", "&", "63"}; - REQUIRE(lex(input) == expected); -} + SECTION("invalid hex numbers") + { + string in = "0x"; + REQUIRE_THROWS(lex(in)); -TEST_CASE("multi-char tokens", "[lex]") -{ - string input = "t>>1"; - vector expected = {"t", ">>", "1"}; - REQUIRE(lex(input) == expected); -} + in = "0x0x1"; + REQUIRE_THROWS(lex(in)); -TEST_CASE("lex crowd", "[lex]") -{ - std::string input = "((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7"; - std::vector expected = { - "(", - "(", - "t", - "<<", - "1", - ")", - "^", - "(", - "(", - "t", - "<<", - "1", - ")", - "+", - "(", - "t", - ">>", - "7", - ")", - "&", - "t", - ">>", - "12", - ")", - ")", - "|", - "t", - ">>", - "(", - "4", - "-", - "(", - "1", - "^", - "7", - "&", - "(", - "t", - ">>", - "19", - ")", - ")", - ")", - "|", - "t", - ">>", - "7", - }; - REQUIRE(bb::lex(input) == expected); + in = "0xx1"; + REQUIRE_THROWS(lex(in)); + + in = "1x1"; + REQUIRE_THROWS(lex(in)); + + in = "1ff"; + REQUIRE_THROWS(lex(in)); + } + + SECTION("left arrow tokens") + { + string in = "< << <= <"; + vector out = { + Token{TokenType::LessThan, "<"}, + Token{TokenType::BitwiseShiftLeft, "<<"}, + Token{TokenType::LessThanEqual, "<="}, + Token{TokenType::LessThan, "<"}, + }; + REQUIRE(lex(in) == out); + } + + SECTION("right arrow tokens") + { + string in = "> >> >= >"; + vector out = { + Token{TokenType::GreaterThan, ">"}, + Token{TokenType::BitwiseShiftRight, ">>"}, + Token{TokenType::GreaterThanEqual, ">="}, + Token{TokenType::GreaterThan, ">"}, + }; + REQUIRE(lex(in) == out); + } + + SECTION("not tokens") + { + string in = "! != !"; + vector out = { + Token{TokenType::Not, "!"}, + Token{TokenType::NotEqual, "!="}, + Token{TokenType::Not, "!"}, + }; + REQUIRE(lex(in) == out); + } + + SECTION("equal") + { + string in = "=="; + vector out = {Token{TokenType::Equal, "=="}}; + REQUIRE(lex(in) == out); + } + + SECTION("invalid equal") + { + string in = "="; + REQUIRE_THROWS(lex(in)); + + in = "=+"; + REQUIRE_THROWS(lex(in)); + } + + SECTION("and tokens") + { + string in = "& && &"; + vector out = { + Token{TokenType::BitwiseAnd, "&"}, + Token{TokenType::And, "&&"}, + Token{TokenType::BitwiseAnd, "&"}, + }; + REQUIRE(lex(in) == out); + } + + SECTION("or tokens") + { + string in = "| || |"; + vector out = { + Token{TokenType::BitwiseOr, "|"}, + Token{TokenType::Or, "||"}, + Token{TokenType::BitwiseOr, "|"}, + }; + REQUIRE(lex(in) == out); + } + + SECTION("strings") + { + string in = "\"foo\""; + vector out = { + Token{TokenType::String, "foo"}, + }; + REQUIRE(lex(in) == out); + } + + SECTION("invalid strings") + { + string in = "\""; + REQUIRE_THROWS(lex(in)); + + in = "\"foo"; + REQUIRE_THROWS(lex(in)); + } + + SECTION("crowd") + { + string in = "((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7"; + vector out = { + Token{TokenType::LeftParen, "("}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::Identifier, "t"}, + Token{TokenType::BitwiseShiftLeft, "<<"}, + Token{TokenType::Integer, "1"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::BitwiseXor, "^"}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::Identifier, "t"}, + Token{TokenType::BitwiseShiftLeft, "<<"}, + Token{TokenType::Integer, "1"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::Plus, "+"}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::Identifier, "t"}, + Token{TokenType::BitwiseShiftRight, ">>"}, + Token{TokenType::Integer, "7"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::BitwiseAnd, "&"}, + Token{TokenType::Identifier, "t"}, + Token{TokenType::BitwiseShiftRight, ">>"}, + Token{TokenType::Integer, "12"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::BitwiseOr, "|"}, + Token{TokenType::Identifier, "t"}, + Token{TokenType::BitwiseShiftRight, ">>"}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::Integer, "4"}, + Token{TokenType::Minus, "-"}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::Integer, "1"}, + Token{TokenType::BitwiseXor, "^"}, + Token{TokenType::Integer, "7"}, + Token{TokenType::BitwiseAnd, "&"}, + Token{TokenType::LeftParen, "("}, + Token{TokenType::Identifier, "t"}, + Token{TokenType::BitwiseShiftRight, ">>"}, + Token{TokenType::Integer, "19"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::RightParen, ")"}, + Token{TokenType::BitwiseOr, "|"}, + Token{TokenType::Identifier, "t"}, + Token{TokenType::BitwiseShiftRight, ">>"}, + Token{TokenType::Integer, "7"}, + }; + REQUIRE(lex(in) == out); + + BENCHMARK("lex crowd") { return lex(in); }; + } } diff --git a/test/test_parse.cpp b/test/test_parse.cpp index 11d409c..87af00f 100644 --- a/test/test_parse.cpp +++ b/test/test_parse.cpp @@ -1,5 +1,7 @@ #define CATCH_CONFIG_ENABLE_BENCHMARKING #include + +#include "lex.hpp" #include "parse.hpp" using namespace std; @@ -7,110 +9,140 @@ using namespace bb; TEST_CASE("parse", "[parse]") { - SECTION("simple expression") { - string input = "t+1"; - auto expr = parse(input); - REQUIRE(expr->to_string() == "(t+1)"); - REQUIRE(expr->evaluate(2) == 3); + string in = "t+1"; + auto ast = parse(in); + REQUIRE((string)*ast == "(t+1)"); + REQUIRE(ast->eval(2).to_int() == 3); } SECTION("simple expression w/o parentheses") { - string input = "t+2*t"; - auto expr = parse(input); - REQUIRE(expr->to_string() == "(t+(2*t))"); - REQUIRE(expr->evaluate(2) == 6); + string in = "t+2*t"; + auto ast = parse(in); + REQUIRE((string)*ast == "(t+(2*t))"); + REQUIRE(ast->eval(2).to_int() == 6); } SECTION("simple expression w/ parentheses") { - string input = "(t+1)*t"; - auto expr = parse(input); - REQUIRE(expr->to_string() == "((t+1)*t)"); - REQUIRE(expr->evaluate(2) == 6); + string in = "(t+1)*t"; + auto ast = parse(in); + REQUIRE((string)*ast == "((t+1)*t)"); + REQUIRE(ast->eval(2).to_int() == 6); } SECTION("unbalanced parentheses") { - string input = "(t+1"; - REQUIRE_THROWS_AS(parse(input), invalid_argument); + string in = "(t+1"; + REQUIRE_THROWS_AS(parse(in), invalid_argument); } SECTION("incomplete expression") { - string input = "t+"; - REQUIRE_THROWS_AS(parse(input), invalid_argument); + string in = "t+"; + REQUIRE_THROWS_AS(parse(in), invalid_argument); } SECTION("extra parentheses") { - string input = "t+1)))"; - REQUIRE_THROWS_AS(parse(input), invalid_argument); + string in = "t+1)))"; + REQUIRE_THROWS_AS(parse(in), invalid_argument); } - SECTION("parse empty input") + SECTION("parse empty in") { - string input = ""; - REQUIRE_THROWS_AS(parse(input), invalid_argument); + string in = ""; + REQUIRE_THROWS_AS(parse(in), invalid_argument); } SECTION("atomic expression") { - string input = "t"; - auto expr = parse(input); - REQUIRE(expr->to_string() == "t"); - REQUIRE(expr->evaluate(42) == 42); + string in = "t"; + auto ast = parse(in); + REQUIRE((string)*ast == "t"); + REQUIRE(ast->eval(42).to_int() == 42); } SECTION("multiple expressions") { - string input = "(t+3)|(t*2)"; - auto expr = parse(input); - REQUIRE(expr->to_string() == "((t+3)|(t*2))"); - REQUIRE(expr->evaluate(1) == 6); + string in = "(t+3)|(t*2)"; + auto ast = parse(in); + REQUIRE((string)*ast == "((t+3)|(t*2))"); + REQUIRE(ast->eval(1).to_int() == 6); } SECTION("precedence") { - string input = "t*2+t*3"; - auto expr = parse(input); - REQUIRE(expr->to_string() == "((t*2)+(t*3))"); - REQUIRE(expr->evaluate(1) == 5); + string in = "t*2+t*3"; + auto ast = parse(in); + REQUIRE((string)*ast == "((t*2)+(t*3))"); + REQUIRE(ast->eval(1).to_int() == 5); } SECTION("deep nesting") { - string input = "(((t*t)))"; - auto expr = parse(input); - REQUIRE(expr->to_string() == "(t*t)"); - REQUIRE(expr->evaluate(2) == 4); + string in = "(((t*t)))"; + auto ast = parse(in); + REQUIRE((string)*ast == "(t*t)"); + REQUIRE(ast->eval(2).to_int() == 4); + } + + SECTION("unary") + { + string in = "1+-t"; + auto ast = parse(in); + REQUIRE((string)*ast == "(1+(-t))"); + REQUIRE(ast->eval(1).to_int() == 0); + } + + SECTION("brackets") + { + string in = "\"foo\"[t+1]+1"; + auto ast = parse(in); + REQUIRE((string)*ast == "((\"foo\"[(t+1)])+1)"); + REQUIRE((char)ast->eval(1).to_int() == 'p'); + } + + SECTION("ternary if") + { + string in = "t%2==0?(t*10):(t*100)+1"; + auto ast = parse(in); + REQUIRE((string)*ast == "(((t%2)==0)?(t*10):((t*100)+1))"); + REQUIRE(ast->eval(2).to_int() == 20); + REQUIRE(ast->eval(3).to_int() == 301); + } + + SECTION("nested if") + { + string in = "t > 10 ? t > 20 ? 1 : 0 : -1"; + auto ast = parse(in); + REQUIRE((string)*ast == "((t>10)?((t>20)?1:0):(-1))"); + REQUIRE(ast->eval(10).to_int() == -1); + REQUIRE(ast->eval(20).to_int() == 0); + REQUIRE(ast->eval(30).to_int() == 1); } - SECTION("parse crowd") + SECTION("if string subscript") { - string input = "((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7"; - auto expr = parse(input); - REQUIRE(expr); + string in = "(t==1?\"foo\":\"bar\")[t]"; + auto ast = parse(in); + REQUIRE((string)*ast == "(((t==1)?\"foo\":\"bar\")[t])"); + REQUIRE((char)ast->eval(0).to_int() == 'b'); + REQUIRE((char)ast->eval(1).to_int() == 'o'); + REQUIRE((char)ast->eval(2).to_int() == 'r'); } SECTION("benchmarks") { - string input = "((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7"; + string in = "((t<<1)^((t<<1)+(t>>7)&t>>12))|t>>(4-(1^7&(t>>19)))|t>>7"; - BENCHMARK("parse crowd") - { - return parse(input); - }; + BENCHMARK("parse crowd") { return parse(in); }; - auto crowd = parse(input); - CHECK(crowd); + auto crowd = parse(in); int t = 0; - BENCHMARK("eval crowd") - { - return crowd->evaluate(t++); - }; + BENCHMARK("eval crowd") { return crowd->eval(t++); }; } }