Much better flex/bison usage, where we use the c++ functionality, making the system easier to both extend and use!
$ ./expr_demo -m -
provide an environment. End with <<EOF>> (ctrl+d):
<<
a := 32
>>
provide an expression. End with <<EOF>> (ctrl+d):
<<
b := a / 2
>>
ast:
> b :-> ROOT[/[a 2 i ]]
evaluated:
> b :-> 16 i
z3 sat check: (apply these changes to satisfy the expression)
> not a raw rhs, cannot check for sat
You can also use the project directly in code like so:
expr::symbol_table_t env{}; // provide an environment for identifier lookup(s)
expr::generic_driver d{}; // instantiate default driver (make sure to link with libexpr_generic_driver.so for this
d.parse_expressions("a := 32 + 2"); // parse your expression
auto result = d.get_symbols(env); // extract the symbol-table
If you want more direct control, or want to inject some custom code (e.g. override the add/+ operator), you can use the direct API:
using namespace expr; // All elements are in the expr:: namespace
std::string expression = "a := 32+2"; // Some expression string
std::stringstream stream{expression}; // inputs must be wrapped in a c++ stream
ast_factory factory{}; // Initialize an overridable ast factory
declaration_tree_builder builder{}; // Initialize an overridable tree builder
scanner sc{stream, std::cerr, &factory}; // Initialize scanner with input stream - write errors to std::cerr
parser_args args{expression, &sc, &factory, &builder}; // Wrap parser arguments
if(parser{args}.parse() != 0) // Actually parse the expression(s)
throw std::logic_error(""); // something bad happened either throw or handle the error
auto result = builder.build(); // build the resulting AST(s)
// Now, we can evaluate our built AST(s)
symbol_table_t env{}; // Initialize an environment for identifier lookup
symbol_operator op{}; // Initialize an overridable operator collection
evaluator e{{env}, op}; // Initialize an evaluator tree-visitor
symbol_table_t result_env{};
for(auto& r : result.declarations) // Extract all the declarations
result_env[r.first] = e.evaluate(r.second.tree);
for(auto& s : result_env) // Print the resulting declarations
std::cout << s.first << " :-> " << s.second << "\n";
if(result.raw_expression) // If the expression is just a RHS expression, print it
std::cout << e.evaluate(result.raw_expression.value()) << "\n";
Complete rewrite number 3
Much better flex/bison usage, where we use the c++ functionality, making the system easier to both extend and use!
You can also use the project directly in code like so:
If you want more direct control, or want to inject some custom code (e.g. override the
add/+
operator), you can use the direct API: