%description: Tests the expression evaluation class %includes: #include %global: using namespace omnetpp::common; class Variable : public Expression::Functor { private: std::string varname; public: Variable(const char *name) {varname = name;} virtual ~Variable() { } virtual Expression::Functor *dup() const override {return new Variable(varname.c_str());} virtual const char *getName() const override {return varname.c_str();} virtual const char *getArgTypes() const override {return "";} virtual char getReturnType() const override {return Expression::Value::DBL;} virtual Expression::Value evaluate(Expression::Value args[], int numargs) override {return (double)(varname[1]-'0');} virtual std::string str(std::string args[], int numargs) override {return getName();} }; class Resolver : public Expression::Resolver { public: virtual Expression::Functor *resolveVariable(const char *varname) override; virtual Expression::Functor *resolveFunction(const char *funcname, int argcount) override; }; Expression::Functor *Resolver::resolveVariable(const char *varname) { return new Variable(varname); } Expression::Functor *Resolver::resolveFunction(const char *funcname, int argcount) { if (MathFunction::supports(funcname)) return new MathFunction(funcname); else throw opp_runtime_error("Unrecognized function: %s()", funcname); } static void eval(const char *txt) { try { EV << txt << " -> "; Expression expr; Resolver resolver; expr.parse(txt, &resolver); //EV << expr.str() << " --> "; // echo parsed form EV << expr.evaluate().str() << "\n"; } catch (std::exception& e) { EV << "exception: " << e.what() << "\n"; } } %activity: // precedence eval("3+2"); eval("3+2*5"); eval("(3+2)*5"); // variables, functions eval("a1"); eval("a2"); eval("a3"); eval("a1+a2+a3"); eval("fabs(-3.14)"); eval("pow(2,8)"); // boolean eval("true"); eval("false"); eval("false || true"); eval("false || false"); eval("false && true"); eval("true && true"); eval("1 < 3"); eval("1 < 3 ? \"lt\" : \"gt\""); // string eval("\"foo\""); eval("\"foo\" + \"bar\""); // quantities: OK eval("2m"); eval("-2m"); eval("1q+2q"); eval("1s+2ms"); eval("-1s+2ms"); eval("1q-2q"); eval("1s-2ms"); eval("-1s-2ms"); eval("3*5s"); eval("-3*5s"); eval("5s*3"); eval("-5s*3"); eval("1s/2"); eval("1s/2s"); eval("1s/2ms"); // quantities: errors eval("1+2m"); eval("1m+2"); eval("1m+2s"); eval("1-2m"); eval("1m-2"); eval("1m-2s"); eval("1s*2s"); eval("1s/2m"); eval("1s^2"); eval("1s^2s"); eval("1^2s"); //TODO many more tests needed, for all operators, precedence rules, etc EV << ".\n"; %exitcode: 0 %contains: stdout 3+2 -> 5 3+2*5 -> 13 (3+2)*5 -> 25 a1 -> 1 a2 -> 2 a3 -> 3 a1+a2+a3 -> 6 fabs(-3.14) -> 3.14 pow(2,8) -> 256 true -> true false -> false false || true -> true false || false -> false false && true -> false true && true -> true 1 < 3 -> true 1 < 3 ? "lt" : "gt" -> "lt" "foo" -> "foo" "foo" + "bar" -> "foobar" 2m -> 2m -2m -> -2m 1q+2q -> 3q 1s+2ms -> 1.002s -1s+2ms -> -0.998s 1q-2q -> -1q 1s-2ms -> 0.998s -1s-2ms -> -1.002s 3*5s -> 15s -3*5s -> -15s 5s*3 -> 15s -5s*3 -> -15s 1s/2 -> 0.5s 1s/2s -> 0.5 1s/2ms -> 500 1+2m -> exception: Cannot convert unit 'm' (meter) to none 1m+2 -> exception: Cannot convert unit none to 'm' (meter) 1m+2s -> exception: Cannot convert unit 's' (second) to 'm' (meter) 1-2m -> exception: Cannot convert unit 'm' (meter) to none 1m-2 -> exception: Cannot convert unit none to 'm' (meter) 1m-2s -> exception: Cannot convert unit 's' (second) to 'm' (meter) 1s*2s -> exception: Multiplying two quantities with units is not supported 1s/2m -> exception: Cannot convert unit 'm' (meter) to 's' (second) 1s^2 -> exception: Error in expression: '^': argument(s) must be dimensionless 1s^2s -> exception: Error in expression: '^': argument(s) must be dimensionless 1^2s -> exception: Error in expression: '^': argument(s) must be dimensionless .