Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- //#define BOOST_SPIRIT_DEBUG
- //static inline std::ostream dbg_out {std::clog.rdbuf()};
- static inline std::ostream dbg_out{ nullptr };
- // header ast.h
- #include <boost/multiprecision/cpp_dec_float.hpp>
- #include <boost/variant.hpp>
- #include <iomanip>
- #include <iostream>
- #include <string>
- #include <cassert>
- namespace Ast {
- using Identifier = std::string;
- struct String : std::string {
- String(std::string const& s) : std::string(s) {}
- using std::string::string;
- using std::string::operator=;
- private:
- std::string const& str() const { return *this; }
- std::string & str() { return *this; }
- friend std::ostream& operator<<(std::ostream& os, String const& s) {
- return os << std::quoted(s.str());
- }
- };
- enum class Boolean { False, True };
- using Number = boost::multiprecision::cpp_dec_float_100;
- enum class Operator {
- Plus, Minus, Mult, Div, Mod,
- Less, LessEq, Greater, GreaterEq, Equal, NotEq,
- NOT, AND, OR, XOR,
- };
- struct Member;
- struct Unary;
- struct Binary;
- struct Ternary;
- struct Call;
- struct Subscript;
- using Expression = boost::make_recursive_variant<
- Boolean,
- Number,
- String,
- Identifier,
- boost::recursive_wrapper<Member>,
- boost::recursive_wrapper<Unary>,
- boost::recursive_wrapper<Binary>,
- boost::recursive_wrapper<Ternary>,
- boost::recursive_wrapper<Call>,
- boost::recursive_wrapper<Subscript>,
- boost::recursive_variant_
- >::type;
- using Expressions = std::vector<Expression>;
- struct Member { Expression obj, member; };
- struct Unary { Operator op; Expression rhs; };
- struct Binary { Expression lhs, rhs; Operator op; };
- struct Ternary { Expression true_, cond, false_; };
- struct Call { Expression fun; Expressions params; };
- struct Subscript { Expression obj; Expressions indices; };
- struct Assignment { Expression lhs, value; };
- }
- namespace Ast {
- std::ostream& operator<<(std::ostream& os, Boolean const&b) {
- switch (b) {
- case Boolean::False: return os << "False";
- case Boolean::True: return os << "True";
- }
- return os << "?bool?";
- }
- std::ostream& operator<<(std::ostream& os, Operator const&op) {
- switch (op) {
- case Operator::Plus: return os << "+";
- case Operator::Minus: return os << "-";
- case Operator::Mult: return os << "*";
- case Operator::Div: return os << "/";
- case Operator::Mod: return os << "%";
- // logical
- case Operator::AND: return os << "and";
- case Operator::NOT: return os << "not";
- case Operator::OR: return os << "or";
- case Operator::XOR: return os << "xor";
- // relational
- case Operator::Equal: return os << "=";
- case Operator::NotEq: return os << "!=";
- case Operator::Less: return os << "<";
- case Operator::LessEq: return os << "<=";
- case Operator::Greater: return os << ">";
- case Operator::GreaterEq: return os << ">=";
- }
- return os << "?op?";
- }
- std::ostream& operator<<(std::ostream& os, Expressions const&ee) {
- auto first = true;
- for (auto& e : ee) {
- if (!std::exchange(first, false))
- os << ", ";
- os << e;
- }
- return os;
- }
- std::ostream& operator<<(std::ostream&os, Member const&o) {
- return os << o.obj << "." << o.member;
- }
- std::ostream& operator<<(std::ostream&os, Unary const&o) {
- return os << o.op << " " << o.rhs;
- }
- std::ostream& operator<<(std::ostream&os, Binary const&o) {
- return os << o.lhs << " " << o.op << " " << o.rhs;
- }
- std::ostream& operator<<(std::ostream&os, Ternary const&o) {
- return os << o.true_ << " if " << o.cond << " else " << o.false_;
- }
- std::ostream& operator<<(std::ostream&os, Call const&o) {
- return os << o.fun << "(" << o.params << ")";
- }
- std::ostream& operator<<(std::ostream&os, Subscript const&o) {
- return os << o.obj << "[" << o.indices << "]";
- }
- std::ostream& operator<<(std::ostream&os, Assignment const&o) {
- return os << o.lhs << ":=" << o.value;
- }
- }
- // header parser.h
- //#define BOOST_SPIRIT_DEBUG
- //#include "ast.h"
- #include <boost/spirit/include/qi.hpp>
- #include <boost/core/ignore_unused.hpp>
- #include <boost/fusion/adapted.hpp>
- #include <boost/spirit/include/phoenix.hpp>
- namespace qi = boost::spirit::qi;
- namespace px = boost::phoenix;
- BOOST_FUSION_ADAPT_STRUCT(Ast::Assignment, lhs, value)
- namespace Parser {
- template <typename It>
- struct Expression : qi::grammar<It, Ast::Expression()> {
- Expression() : Expression::base_type(start) {
- identifier_ = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z0-9_");
- quoted_string = '"' >> *(R"("")" >> qi::attr('"') | ~qi::char_('"')) >> '"';
- using namespace boost::spirit::labels;
- term_ =
- factor_[_val = _1] >>
- *(
- (mult_div_mod_binary_op_ >> factor_)[_val = make_binary(_val, _2, _1)]
- )
- ;
- factor_
- = '(' >> expression_ >> ')'
- | quoted_string
- | unary_
- | qi::no_case[bool_]
- | identifier_
- | number_
- ;
- expression_
- = term_[_val = _1] >>
- *(
- (minus_plus_binary_op_ >> expression_)[_val = make_binary(_val, _2, _1)]
- | ('.' >> expression_)[_val = make_member(_val, _1)]
- | (qi::no_case[binary_op_] >> expression_)[_val = make_binary(_val, _2, _1)]
- | ("if" >> expression_ >> "else" >> expression_)[_val = make_ternary(_val, _1, _2)]
- | ('(' >> list_ >> ')')[_val = make_call(_val, _1)]
- | ('[' >> list_ >> ']')[_val = make_subscript(_val, _1)]
- );
- list_ = -(expression_ % ',');
- start = qi::skip(qi::blank)[expression_];
- unary_ = (qi::no_case[unary_op_] >> expression_)[_val = make_unary(_1, _2)];
- BOOST_SPIRIT_DEBUG_NODES(
- (start)(expression_)(factor_)
- (quoted_string)(identifier_)(unary_)
- )
- }
- private:
- template <typename T> struct make_ {
- struct arg_print {
- template <typename V>
- int operator()(V const& v) const { dbg_out << " (" << v << ")"; return 42; }
- };
- template <typename... Args>
- auto operator()(Args const&... args) const {
- dbg_out << "Make " << boost::core::demangle(typeid(T).name());
- int ignored[]{ arg_print{}(args)... };
- boost::ignore_unused(ignored);
- dbg_out << std::endl;
- return T{ args... };
- }
- };
- px::function<make_<Ast::Binary> > make_binary{};
- px::function<make_<Ast::Unary> > make_unary{};
- px::function<make_<Ast::Ternary> > make_ternary{};
- px::function<make_<Ast::Member> > make_member{};
- px::function<make_<Ast::Call> > make_call{};
- px::function<make_<Ast::Subscript> > make_subscript{};
- struct binary_op_sym : qi::symbols<char, Ast::Operator> {
- binary_op_sym() {
- this->add
- //("-", Ast::Operator::Minus)
- // ("+", Ast::Operator::Plus)
- // ("*", Ast::Operator::Mult)
- // ("/", Ast::Operator::Div)
- // ("%", Ast::Operator::Mod)
- // logical
- ("and", Ast::Operator::AND)
- ("or", Ast::Operator::OR)
- ("xor", Ast::Operator::XOR) // aka '<>' or '!='
- // relational
- ("=", Ast::Operator::Equal)
- ("<>", Ast::Operator::NotEq)
- ("<", Ast::Operator::Less)
- ("<=", Ast::Operator::LessEq)
- (">", Ast::Operator::Greater)
- (">=", Ast::Operator::GreaterEq)
- ;
- }
- } binary_op_;
- struct minus_plus_binary_op_sym : qi::symbols<char, Ast::Operator> {
- minus_plus_binary_op_sym() {
- this->add
- ("-", Ast::Operator::Minus)
- ("+", Ast::Operator::Plus)
- ;
- }
- } minus_plus_binary_op_;
- struct mult_div_mod_binary_op_sym : qi::symbols<char, Ast::Operator> {
- mult_div_mod_binary_op_sym() {
- this->add
- ("*", Ast::Operator::Mult)
- ("/", Ast::Operator::Div)
- ("%", Ast::Operator::Mod)
- ;
- }
- } mult_div_mod_binary_op_;
- struct bool_sym : qi::symbols<char, Ast::Boolean> {
- bool_sym() {
- this->add
- ("false", Ast::Boolean::False)
- ("true", Ast::Boolean::True)
- ;
- }
- } bool_;
- struct unary_op_sym : qi::symbols<char, Ast::Operator> {
- unary_op_sym() {
- this->add
- ("-", Ast::Operator::Minus)
- ("+", Ast::Operator::Plus)
- ("not", Ast::Operator::NOT)
- ;
- }
- } unary_op_;
- qi::rule<It, Ast::Expression()> start;
- qi::rule<It, Ast::Expression(), qi::blank_type> factor_, expression_;
- qi::rule<It, Ast::Expression(), qi::blank_type> term_;
- qi::rule<It, Ast::Expressions(), qi::blank_type> list_;
- qi::rule<It, Ast::Unary(), qi::blank_type> unary_;
- // implicit lexemes
- qi::real_parser<Ast::Number> number_;
- qi::rule<It, Ast::Identifier()> identifier_;
- qi::rule<It, Ast::String()> quoted_string;
- };
- template <typename It>
- struct Assignment : qi::grammar<It, Ast::Assignment()> {
- Assignment() : Assignment::base_type(start) {
- start = qi::skip(qi::blank)[expression_ >> ":=" >> expression_];
- BOOST_SPIRIT_DEBUG_NODES((start))
- }
- private:
- Expression<It> expression_;
- qi::rule<It, Ast::Assignment()> start;
- };
- }
- // header null.h
- #include <iosfwd>
- namespace Eval {
- // We define an inert type Null
- struct Null {
- friend std::ostream& operator<<(std::ostream& os, Null) { return os << "null"; }
- };
- template <typename T> Null operator+ (Null, T const&) { return {}; }
- template <typename T> Null operator- (Null, T const&) { return {}; }
- template <typename T> Null operator* (Null, T const&) { return {}; }
- template <typename T> Null operator/ (Null, T const&) { return {}; }
- template <typename T> Null operator% (Null, T const&) { return {}; }
- template <typename T> Null operator&&(Null, T const&) { return {}; }
- template <typename T> Null operator||(Null, T const&) { return {}; }
- template <typename T> Null operator^ (Null, T const&) { return {}; }
- template <typename T> Null operator+ (T const&, Null) { return {}; }
- template <typename T> Null operator- (T const&, Null) { return {}; }
- template <typename T> Null operator* (T const&, Null) { return {}; }
- template <typename T> Null operator/ (T const&, Null) { return {}; }
- template <typename T> Null operator% (T const&, Null) { return {}; }
- template <typename T> Null operator&&(T const&, Null) { return {}; }
- template <typename T> Null operator||(T const&, Null) { return {}; }
- template <typename T> Null operator^ (T const&, Null) { return {}; }
- template <typename T> Null operator< (Null, T const&) { return {}; }
- template <typename T> Null operator<=(Null, T const&) { return {}; }
- template <typename T> Null operator> (Null, T const&) { return {}; }
- template <typename T> Null operator>=(Null, T const&) { return {}; }
- template <typename T> Null operator==(Null, T const&) { return {}; }
- template <typename T> Null operator!=(Null, T const&) { return {}; }
- template <typename T> Null operator< (T const&, Null) { return {}; }
- template <typename T> Null operator<=(T const&, Null) { return {}; }
- template <typename T> Null operator> (T const&, Null) { return {}; }
- template <typename T> Null operator>=(T const&, Null) { return {}; }
- template <typename T> Null operator==(T const&, Null) { return {}; }
- template <typename T> Null operator!=(T const&, Null) { return {}; }
- Null operator- (Null) { return {}; }
- Null operator! (Null) { return {}; }
- }
- // header eval.h
- //#include "ast.h"
- //#include "null.h"
- #include <boost/variant.hpp>
- #include <vector>
- #include <map>
- namespace Eval {
- using boost::core::demangle;
- struct Value;
- using Values = std::vector<Value>;
- using Function = std::function<Value(Values const&)>;
- using ValueV = boost::variant<
- Null,
- Ast::Number,
- std::string,
- Ast::Boolean,
- Function
- >;
- struct Value : ValueV {
- using ValueV::ValueV;
- using ValueV::operator=;
- };
- static inline std::ostream& operator<<(std::ostream& os, Function const&) {
- return os << "function(...){}";
- }
- // Reflects a dynamic object instance in the Evaluation engine. Losely
- // based on the idea of ExpandoObject in System.Dynamic from the CLR
- struct Expando {
- using Sub = boost::recursive_wrapper<Expando>;
- using Key = std::string;
- using Array = std::vector<Sub>;
- using Object = std::map<Key, Sub>;
- Expando() = default;
- Expando(Value v) : _value(std::move(v)) {}
- Expando(Object v) : _object(std::move(v)) {}
- Expando(Array v) : _array(std::move(v)) {}
- Value _value;
- Object _object;
- Array _array;
- Expando& operator[](Values indices) {
- if (indices.empty())
- return *this;
- auto f = indices.front();
- if (auto index = boost::get<Ast::Number>(&f)) {
- auto i = index->convert_to<std::size_t>();
- if (i >= _array.size()) {
- _array.insert(_array.end(), 1 + i - _array.size(), Expando{});
- }
- indices.erase(indices.begin());
- return _array.at(i).get()[indices];
- }
- if (auto key = boost::get<std::string>(&f)) {
- auto it = _object.find(*key);
- if (it == _object.end()) {
- auto insertion = _object.emplace(*key, Expando{});
- it = insertion.first;
- }
- indices.erase(indices.begin());
- return it->second.get()[indices];
- }
- throw std::runtime_error("Type Mismatch: " + type_name(f));
- }
- Expando& operator[](Value const& key) { return operator[](Values{ key }); }
- private:
- static std::string type_name(Value const& v) {
- return demangle(v.type().name());
- }
- void dump(std::ostream& os, std::string indent = "\n") const {
- os << _value << " ";
- if (!_object.empty()) {
- os << "{";
- for (auto&[k, v] : _object) {
- os << indent << std::quoted(k) << ": ";
- v.get().dump(os, indent + " ");
- }
- if (_object.empty())
- os << " }";
- else
- os << indent << "}";
- }
- if (!_array.empty()) {
- os << "[";
- size_t i = 0;
- for (auto& v : _array) {
- v.get().dump(os << indent << "#" << i++ << ": ", indent + " ");
- }
- if (i)
- os << indent << "]";
- else
- os << " ]";
- }
- }
- friend std::ostream& operator<<(std::ostream& os, Expando const& obj) {
- obj.dump(os);
- return os;
- }
- };
- // In our "typesystem" we will have Dynamic variables, where each variable
- // can be either an LValue or RValue
- struct WrapValue { Value _value; }; // to unconfuse boost::variant convert_construct
- using RValue = Value;
- using LValue = Expando & ;
- struct Dynamic : boost::variant<WrapValue, Expando*> {
- using Variant = boost::variant<WrapValue, Expando*>;
- Dynamic() = default;
- Dynamic(Value const& v) : Variant(WrapValue{ v }) {}
- Dynamic(LValue r) : Variant(&r) {}
- using Variant::operator=;
- LValue lvalue() const {
- if (auto* p = boost::strict_get<Expando*>(&base()))
- return **p;
- throw std::runtime_error("LValue required (" + boost::lexical_cast<std::string>(*this) + ")");
- }
- Value value() const {
- return which() == 0
- ? boost::strict_get<WrapValue>(base())._value
- : boost::strict_get<Expando*>(base())->_value;
- }
- operator LValue() const { return lvalue(); }
- operator Value() const { return value(); }
- friend inline bool is_rvalue(Dynamic const& v) { return 0 == v.which(); }
- friend inline bool is_lvalue(Dynamic const& v) { return 1 == v.which(); }
- private:
- Variant& base() { return *this; }
- Variant const& base() const { return *this; }
- friend std::ostream& operator<<(std::ostream& os, Dynamic const& obj) {
- return is_lvalue(obj)
- ? os << obj.lvalue()
- : os << obj.value();
- }
- };
- namespace detail {
- struct Truthyness {
- template <typename... T>
- bool operator()(boost::variant<T...> const& v) const { return boost::apply_visitor(*this, v); }
- bool operator()(Dynamic const& o) const { return operator()(o.value()); }
- bool operator()(Value const& o) const { return boost::apply_visitor(*this, o); }
- bool operator()(Null) const { return false; }
- bool operator()(Ast::Boolean const& o) const { return o == Ast::Boolean::True; }
- bool operator()(Ast::Number const& o) const { return o != 0; }
- bool operator()(Ast::String const& o) const { return !o.empty(); }
- template <typename T>
- bool operator()(T const& v) const {
- std::ostringstream oss;
- oss << "No conversion of " << v << " (" << demangle(typeid(T).name()) << " to boolean";
- throw std::runtime_error(oss.str());
- }
- };
- static inline const Truthyness truthy = {};
- struct OperationBase {
- static Null boolcast(Null) {
- return {};
- }
- static Ast::Boolean boolcast(bool b) {
- return b ? Ast::Boolean::True : Ast::Boolean::False;
- }
- };
- template <typename Op>
- struct Arithmetic : OperationBase {
- using result_type = RValue;
- Ast::Number operator()(Ast::Number const& a) const { return Op{}(a); }
- template <typename T, typename U>
- RValue operator()(T const& a, U const& b, decltype(Op{}(T{}, U{}))* = nullptr) const {
- return Op{}(a, b);
- }
- template <typename... T>
- RValue operator()(T const&...) const {
- //dbg_out << "L" << __LINE__ << ": " << __PRETTY_FUNCTION__ << "\n";
- throw std::runtime_error("Incompatible operands to " + demangle(typeid(Op).name()));
- }
- };
- template <typename Op>
- struct Logical : OperationBase {
- using result_type = RValue;
- template <typename T>
- RValue operator()(T const& a) const { return boolcast(Op{}(truthy(a))); }
- template <typename T, typename U>
- RValue operator()(T const& a, U const& b) const { return boolcast(Op{}(truthy(a), truthy(b))); }
- };
- template <typename Op>
- struct Relational : OperationBase {
- using result_type = RValue;
- template <typename T, typename U>
- RValue operator()(T const& a, U const& b, decltype(Op{}(T{}, U{}))* = nullptr) const
- {
- return boolcast(Op{}(a, b));
- }
- template <typename... T>
- RValue operator()(T const&...) const {
- //dbg_out << "L" << __LINE__ << ": " << __PRETTY_FUNCTION__ << "\n";
- throw std::runtime_error("Incompatible operands to " + demangle(typeid(Op).name()));
- }
- };
- struct Modulus : std::modulus<> {
- auto operator()(Ast::Number const& a, Ast::Number const& b) const {
- return fmod(a, b);
- }
- using std::modulus<>::operator();
- };
- }
- struct Evaluator {
- using result_type = Dynamic;
- LValue _root_context;
- Evaluator(LValue root) : _root_context(root) {}
- Evaluator(Evaluator const&) = default;
- template <typename... T> Dynamic operator()(T&&...args) {
- return call(_root_context, std::forward<T>(args)...);
- }
- private:
- template <typename... T> Dynamic call(LValue ctx, boost::variant<T...> const& v) {
- Evaluator nest(ctx);
- return boost::apply_visitor(nest, v);
- }
- Dynamic call(LValue ctx, Ast::Assignment const& o) {
- LValue dest = call(ctx, o.lhs);
- // protect against aliasing source/destination
- RValue tmp = call(ctx, o.value);
- dest = tmp;
- return dest;
- }
- // Expression
- RValue call(LValue, Ast::Boolean const& o) { return o; }
- RValue call(LValue, Ast::Number const& o) { return o; }
- RValue call(LValue, Ast::String const& o) { return o; }
- LValue call(LValue ctx, Ast::Identifier const& o) { return ctx[o]; }
- Dynamic call(LValue ctx, Ast::Member const& o) {
- LValue obj = call(ctx, o.obj);
- return call(obj, o.member);
- }
- RValue call(LValue ctx, Ast::Unary const& o) {
- RValue const rhs = call(ctx, o.rhs);
- switch (o.op) {
- case Ast::Operator::Plus:
- return rhs;
- case Ast::Operator::Minus:
- return boost::apply_visitor(detail::Arithmetic<std::negate<>>{}, rhs);
- case Ast::Operator::NOT:
- return boost::apply_visitor(detail::Logical<std::logical_not<>>{}, rhs);
- default:
- throw std::runtime_error("Not implemented");
- }
- }
- RValue call(LValue ctx, Ast::Binary const& o) {
- ValueV lhs = call(ctx, o.lhs);
- ValueV rhs = call(ctx, o.rhs);
- auto arith = [&](auto op) {
- return boost::apply_visitor(detail::Arithmetic<decltype(op)>{}, lhs, rhs);
- };
- auto logical = [&](auto op) {
- return boost::apply_visitor(detail::Logical<decltype(op)>{}, lhs, rhs);
- };
- auto relational = [&](auto op) {
- return boost::apply_visitor(detail::Relational<decltype(op)>{}, lhs, rhs);
- };
- switch (o.op) {
- case Ast::Operator::Minus: return arith(std::minus<>{});
- case Ast::Operator::Plus: return arith(std::plus<>{});
- case Ast::Operator::Mult: return arith(std::multiplies<>{});
- case Ast::Operator::Div: return arith(std::divides<>{});
- case Ast::Operator::Mod: return arith(detail::Modulus{});
- // logical
- case Ast::Operator::AND: return logical(std::logical_and<>{});
- case Ast::Operator::OR: return logical(std::logical_or<>{});
- case Ast::Operator::XOR: return logical(std::not_equal_to<>{});
- // relational
- case Ast::Operator::Equal: return relational(std::equal_to<>{});
- case Ast::Operator::NotEq: return relational(std::not_equal_to<>{});
- case Ast::Operator::Less: return relational(std::less<>{});
- case Ast::Operator::LessEq: return relational(std::less_equal<>{});
- case Ast::Operator::Greater: return relational(std::greater<>{});
- case Ast::Operator::GreaterEq: return relational(std::greater_equal<>{});
- default: throw std::runtime_error("Invalid operator");
- }
- }
- Dynamic call(LValue ctx, Ast::Ternary const& o) {
- return detail::truthy(call(ctx, o.cond))
- ? call(ctx, o.true_)
- : call(ctx, o.false_);
- }
- RValue call(LValue ctx, Ast::Call const& o) {
- Values params;
- for (auto& p : o.params) {
- params.push_back(call(ctx, p));
- }
- RValue fun = call(ctx, o.fun);
- if (auto* f = boost::get<Function>(&fun)) {
- return (*f)(params);
- }
- throw std::runtime_error("Invalid callable");
- }
- LValue call(LValue ctx, Ast::Subscript const& o) {
- Values indices;
- for (auto& p : o.indices) {
- indices.push_back(call(ctx, p));
- }
- LValue obj = call(ctx, o.obj);
- return obj[indices];
- }
- };
- }
- //#include "ast.h"
- //#include "parser.h"
- //#include "eval.h"
- template <typename T, typename G>
- bool require_result(Eval::Expando context, G const& g, std::string const& str, const Eval::Value& compare_value_) {
- auto iter = str.begin(), end = str.end();
- std::cout << std::quoted(str) << " ";
- T parsed;
- if (qi::parse(iter, end, g, parsed)) {
- std::cout << "OK: " << parsed << "\n";
- try {
- Eval::Value outcome = Eval::Evaluator{ context }(parsed);
- std::cout << " -- outcome: " << outcome << "\n"; // , context: " << context << std::endl;
- return true; // TODO: how to compare outcome and compare_value_?
- }
- catch (std::exception const& e) {
- std::cout << " -- evaluation failed: " << e.what() << "\n";
- return false;
- }
- }
- else {
- std::cout << "Failed\n";
- }
- if (iter != end) {
- std::cout << "Remaining unparsed: " << std::quoted(std::string(iter, end)) << "\n";
- }
- return false;
- }
- template <typename T, typename G>
- void run_tests(Eval::Expando context, G const& g, std::vector<std::string> const& inputs) {
- for (std::string const str : inputs) {
- auto iter = str.begin(), end = str.end();
- std::cout << std::quoted(str) << " ";
- T parsed;
- if (qi::parse(iter, end, g, parsed)) {
- std::cout << "OK: " << parsed << "\n";
- try {
- Eval::Value outcome = Eval::Evaluator{ context }(parsed);
- std::cout << " -- outcome: " << outcome << "\n"; // , context: " << context << std::endl;
- }
- catch (std::exception const& e) {
- std::cout << " -- evaluation failed: " << e.what() << "\n";
- }
- }
- else {
- std::cout << "Failed\n";
- }
- if (iter != end) {
- std::cout << "Remaining unparsed: " << std::quoted(std::string(iter, end)) << "\n";
- }
- }
- }
- static inline Eval::Value demofunction(Eval::Values const& params) {
- #if 0
- std::ostringstream oss;
- oss << "demofunction returned: c(";
- bool first = true;
- for (auto& param : params)
- oss << (std::exchange(first, false) ? "" : ", ") << param;
- oss << ")";
- return oss.str();
- #else
- return Ast::Number(23.45);
- #endif
- }
- static inline auto make_context() {
- using namespace Eval;
- Expando ctx;
- Value f = demofunction;
- Value identity = [](Values v) { return v.front(); };
- ctx["a"] = Value(43);
- ctx["b"] = Value(1);
- ctx["c"] = Value(7);
- ctx["a"]["d"][2]["c"] = f;
- ctx["a"]["d"][3]["c"] = f;
- ctx["a"]["b"]["c"] = identity;
- return ctx;
- }
- int main() {
- std::cout << std::unitbuf
- << "========== Context: " << make_context() << "\n"
- << "==========\n";
- auto expressions = [](std::vector<std::string> const& cases) {
- Parser::Expression<std::string::const_iterator> g;
- run_tests<Ast::Expression>(make_context(), g, cases);
- };
- auto assignments = [](std::vector<std::string> const& cases) {
- Parser::Assignment<std::string::const_iterator> g;
- run_tests<Ast::Assignment>(make_context(), g, cases);
- };
- //expressions({
- // R"(a > 20)",
- // R"(a+10 > 30)",
- // R"(a = True)",
- // R"(a = b.a.c)",
- // R"(not a)",
- // R"(a and (b and c))",
- // R"(a*34+a.b.c(23))",
- // R"(10 if a > b else 30)", // python style unary
- // });
- //expressions({
- // R"("hello")",
- // R"(10)",
- // R"(a)",
- // R"(a.b)",
- // R"(a.b[2])",
- // R"(a.d)",
- // R"(a.d[2].c(a.e*3,20,"hello"))",
- // R"(a.d[3])",
- // R"(a.d[3].c(34))",
- // R"(a.d[3].c)",
- // R"(a.e)",
- // R"(b.c.d*20)",
- // R"(-20)"
- // });
- //// Mutability makes the universe wonky:
- //assignments({
- // R"(a.b.c := 10)",
- // // Invoking a function doesn't yield an LValue
- // R"(a.d[3].c(34) := a.b[2])",
- // R"(a.d[2].c(a.e*3,20,"hello") := b.c.d*20)",
- // // NOTE: replaces a.d entirely!
- // R"(a := 10)",
- // // Now, this will not invoke a.d[2].c anymore:
- // R"(a.d[2].c(a.e*3,20,"hello") := b.c.d*20)",
- // R"(a.d := a.b)",
- // R"(a.d[3] := 10)",
- // R"(a.d[3] := a.b[2])",
- // R"(a.e := "hello")",
- // });
- //expressions({
- // R"(-a)",
- // R"(b)",
- // R"(a>-b)",
- // R"(a<-b)",
- // R"(a<a)",
- // R"(a<=a)",
- // R"(True = (a=a))",
- // R"(True = not (False))",
- // R"(True xor (a=a))",
- // R"(True or (a=a))",
- // R"(True and (a=a))",
- // R"(False xor (a=a))",
- // R"(False or (a=a))",
- // R"(False and (a=a))",
- // R"(2*8 + 7)",
- // R"(2*8 + False)",
- // R"(a.d[3].c(34))",
- // });
- //expressions({
- // R"(-a)",
- // R"(b)",
- // R"(a-b)",
- // R"(b-a)",
- // R"(b/a)",
- // R"(b*a)",
- // R"(b%3)",
- // R"(b/0)",
- // });
- ////bool x = true || true && false; // true ==> and then or, false ==> or then and
- ////// in C/C++ true
- ////bool y1 = false == (1 > 20);
- ////bool y2 = (false == 1) > 20;
- //#define RequiresTrue(EXPRESSION) assert( EXPRESSION );
- //#define RequiresFalse(EXPRESSION) assert( !(EXPRESSION) );
- //RequiresFalse(false == 1 < 20); // '<' bindet staerker als '=='
- //RequiresFalse(false == (1 < 20)); // beweis
- //RequiresTrue((false == 1) < 20); // passt nicht
- //RequiresFalse(false && true == false); // '==' bindet staerker als '&&'
- //RequiresFalse(false && (true == false)); // beweis
- //RequiresTrue((false && true) == false); // passt nicht
- //assert( (0 == 1 && 0) == 0); // 0: '==' bindet staerker als '&&'
- //assert( ((0 == 1) && 0) == 0);
- //assert( (0 == (1 && 0)) == 1);
- //expressions({
- // R"(2*8 + 7)",
- // R"((1*2+3*4+5)*6)",
- // R"((1+2*3+4*5)+6)",
- // R"(1+2+3+4)",
- // R"(-20)",
- // R"(1+3*4)",
- // R"(1+4/2)",
- // R"(1+4%3)",
- // R"((1+3)*4)",
- // R"((1+4)/2)",
- // R"((1+4)%3)",
- // R"(True or True and False)",
- // R"(False = (1 < 20))", // should be False - OK
- // R"(False = 1 < 20)", // should be also False - OK
- // R"(False = (1 > 20))", // should be True - OK
- // R"(False = 1 > 20)", // should be also True - OK
- // R"(10 < 20 = 10 > 20)", // should be False - OK
- // R"((false and true) = (false and true))", // should be True - OK
- // R"(false and (true = false) and true)", // should be False - OK
- // R"(false and true = false and true)", // should be False - OK
- // R"(a.b.c(123))",
- // R"(a.d[3].c(321))",
- // R"((8*2+7)=((8*2)+7))", // True: * stringer binding then +
- // R"((8*2-7)=((8*2)-7))", // True: * stringer binding then -
- // R"((8/2+7)=((8/2)+7))", // True: / stringer binding then +
- // R"((8/2-7)=((8/2)-7))", // True: / stringer binding then -
- // R"((8/2*7)=(8/2*7)", // True: * / equal binding
- // R"((8/2*7)=(7*8/2)", // True: * / equal binding
- // R"((8-2+7)=(8-2+7)", // True: * / equal binding
- // R"((8-2+7)=(7+8-2)", // True: * / equal binding
- // R"((False = 1 < 20)=(False = (1 < 20)))", // True: '<' stronger binding then '=='
- // R"((False = 1 > 20)=(False = (1 > 20)))", // True: '>' stronger binding then '=='
- // R"((False = 1 <= 20)=(False = (1 <= 20)))", // True: '<' stronger binding then '=='
- // R"((False = 1 => 20)=(False = (1 => 20)))", // True: '>' stronger binding then '=='
- // R"((False and True = False)=(False and (True = False)))", // True: '==' stronger binding then '&&'
- // R"((True or False and False)=(True or (False and False)))", // True: '&&' stronger binding then '||'
- // });
- //expressions({
- // R"((8*2+7)=((8*2)+7))", // True: * stronger binding then +
- // R"((8*2-7)=((8*2)-7))", // True: * stronger binding then -
- // R"((8/2+7)=((8/2)+7))", // True: / stronger binding then +
- // R"((8/2-7)=((8/2)-7))", // True: / stronger binding then -
- // R"((8/2*7)=(8/2*7))", // True: * / equal binding, etc. test
- // R"((8/2*7)=(7*8/2))", // True: * / equal binding, etc. test
- // R"((8-2+7)=(8-2+7))", // True: + - equal binding, etc. test
- // R"((8-2+7)=(7+8-2))", // True: + - equal binding, etc. test // FAILS
- // R"(8-2+7)", // 13 // OK
- // R"(7+8-2)", // -1 // ??
- // R"((False = 1 < 20)=(False = (1 < 20)))", // True: '<' stronger binding then '=='
- // R"((False = 1 > 20)=(False = (1 > 20)))", // True: '>' stronger binding then '=='
- // R"((False = 1 <= 20)=(False = (1 <= 20)))", // True: '<' stronger binding then '=='
- // R"((False = 1 => 20)=(False = (1 => 20)))", // True: '>' stronger binding then '=='
- // R"((False and True = False)=(False and (True = False)))", // True: '==' stronger binding then '&&'
- // R"((True or False and False)=(True or (False and False)))", // True: '&&' stronger binding then '||'
- // });
- expressions({
- R"(8-2*7)",
- R"(7*8-2)",
- R"(7+8-2)",
- R"(8-2+7)",
- });
- Parser::Expression<std::string::const_iterator> g;
- auto c = make_context();
- assert(require_result<Ast::Expression>(c, g, "8-2*7", -6));
- assert(require_result<Ast::Expression>(c, g, "8-2*7", 54));
- assert(require_result<Ast::Expression>(c, g, "7+8-2", 13));
- assert(require_result<Ast::Expression>(c, g, "8-2+7", 13)); // Fails -1
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement