sillydan1 / expr

Simple variable and environment manipulation language
MIT License
1 stars 0 forks source link

Feature/cpp parsers #17

Closed sillydan1 closed 1 year ago

sillydan1 commented 1 year ago

Complete rewrite number 3

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";