Skip to content

Commit

Permalink
refactor!: Identifiers are now stored as a string instead of a iterator
Browse files Browse the repository at this point in the history
This trades memory safety for lookup times
  • Loading branch information
sillydan1 committed Sep 11, 2022
1 parent ead430a commit c6dae37
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 70 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.21)
# to generate a changelog, use: (python3 -m pip install git-changelog)
# $ git-changelog -s conventional . -o CHANGELOG.MD
project(expr VERSION 1.7.0)
include(cmake/CPM.cmake)
configure_file(src/config.h.in config.h)
Expand Down
5 changes: 1 addition & 4 deletions include/drivers/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,13 @@ namespace expr {
#else
using compiled_expr_collection_t = std::unordered_map<std::string, compiled_expr_t>;
#endif
explicit compiler(const symbol_table_t &env) : driver{}, trees{}, environment{env} {}
compiler(std::initializer_list<symbol_table_ref_t> environments) : driver{environments}, trees{} {}
int parse(const std::string &f) override;
auto get_symbol(const std::string &identifier) -> syntax_tree_t override;
void add_tree(const syntax_tree_t& tree) override;
void add_tree(const std::string& identifier, const syntax_tree_t& tree) override;
auto get_environment() const -> const symbol_table_t& { return environment; }

compiled_expr_collection_t trees;
protected:
const symbol_table_t& environment{};
};
}

Expand Down
7 changes: 1 addition & 6 deletions include/drivers/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

namespace expr {
struct interpreter : public driver, arithmetic_operator, boolean_operator, compare_operator {
explicit interpreter(const symbol_table_t &env);
interpreter(std::initializer_list<symbol_table_ref_t> environments);
~interpreter() override = default;

auto parse(const std::string &f) -> int override;
Expand Down Expand Up @@ -63,11 +63,6 @@ namespace expr {

auto evaluate(const syntax_tree_t& tree) -> symbol_value_t;
auto evaluate(const compiler::compiled_expr_collection_t& tree) -> symbol_table_t;
static auto evaluate(const syntax_tree_t& tree, const interpreter& op, const symbol_table_t& symbols) -> symbol_value_t;
static auto evaluate(const compiler::compiled_expr_collection_t& symbol_tree_map, const interpreter& op, const symbol_table_t& symbols) -> symbol_table_t;

protected:
const symbol_table_t &environment{};
};
}

Expand Down
14 changes: 5 additions & 9 deletions include/symbol_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,21 +95,18 @@ namespace expr {
explicit operator_t(operator_type_t t) : operator_type{t} {}
};

using symbol_reference_t = symbol_table_t::iterator;
using c_symbol_reference_t = symbol_table_t::const_iterator;
struct identifier_t {
std::string ident;
};
struct root_t {};

using underlying_syntax_node_t = std::variant<symbol_reference_t, c_symbol_reference_t, operator_t, root_t, symbol_value_t>;
using underlying_syntax_node_t = std::variant<identifier_t, operator_t, root_t, symbol_value_t>;
struct syntax_node_t : public underlying_syntax_node_t {
syntax_node_t() : underlying_syntax_node_t{root_t{}} {}

template<typename T>
syntax_node_t(const T &t) : underlying_syntax_node_t{t} {}

syntax_node_t(symbol_reference_t r) : underlying_syntax_node_t{r} {}

syntax_node_t(c_symbol_reference_t r) : underlying_syntax_node_t{r} {}

template<typename T>
auto &operator=(const T &t) {
this->underlying_syntax_node_t::operator=(t);
Expand All @@ -121,8 +118,7 @@ namespace expr {

auto operator<<(std::ostream &o, const operator_t &p) -> std::ostream &;
auto operator<<(std::ostream &o, const root_t &r) -> std::ostream &;
auto operator<<(std::ostream &o, const symbol_reference_t &r) -> std::ostream &;
auto operator<<(std::ostream &o, const c_symbol_reference_t &r) -> std::ostream &;
auto operator<<(std::ostream &o, const identifier_t &r) -> std::ostream &;
auto operator<<(std::ostream &o, const underlying_syntax_node_t &n) -> std::ostream &;
auto operator<<(std::ostream &o, const syntax_tree_t &t) -> std::ostream &;
}
Expand Down
6 changes: 2 additions & 4 deletions src/drivers/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,9 @@ namespace expr {
}
}
auto compiler::get_symbol(const std::string& identifier) -> syntax_tree_t {
#ifndef NDEBUG
if (!environment.contains(identifier))
if (!contains(identifier))
throw std::out_of_range(identifier + " not found");
#endif
return syntax_tree_t{environment.find(identifier)};
return syntax_tree_t{identifier_t{identifier}};
}
void compiler::add_tree(const syntax_tree_t& tree) {
trees["expression_result"] = (tree);
Expand Down
19 changes: 18 additions & 1 deletion src/drivers/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,28 @@
YY_DECL;

namespace expr {
using symbol_table_ref_t = std::reference_wrapper<const expr::symbol_table_t>;
using symbol_table_ref_collection_t = std::vector<std::reference_wrapper<const expr::symbol_table_t>>;
struct driver {
explicit driver() : trace_parsing(false), trace_scanning(false) {}
driver(std::initializer_list<symbol_table_ref_t> environments)
: trace_parsing(false), trace_scanning(false), environments{environments} {}
virtual ~driver() = default;

virtual int parse(const std::string &f) = 0;
virtual auto get_symbol(const std::string &identifier) -> syntax_tree_t = 0;
virtual void add_tree(const syntax_tree_t& tree) = 0;
virtual void add_tree(const std::string& identifier, const syntax_tree_t& tree) = 0;
auto contains(const std::string& identifier) const -> bool {
return find(identifier) != end;
}
auto find(const std::string& identifier) const -> expr::symbol_table_t::const_iterator {
for(auto& env : environments) {
auto env_it = env.get().find(identifier);
if(env_it != env.get().end())
return env_it;
}
return end;
}

void scan_begin();
void scan_end();
Expand All @@ -46,6 +60,9 @@ namespace expr {
bool trace_parsing;
bool trace_scanning;
yy::location location;
protected:
expr::symbol_table_t::const_iterator end{};
symbol_table_ref_collection_t environments;
};
}

Expand Down
73 changes: 33 additions & 40 deletions src/drivers/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
#include "parser.hpp"

namespace expr {
interpreter::interpreter(const symbol_table_t& map) : environment{map}, driver{} {
}
interpreter::interpreter(std::initializer_list<symbol_table_ref_t> environments) : driver{environments} {}

int interpreter::parse(const std::string &f) {
if (f.empty()) {
Expand Down Expand Up @@ -67,11 +66,9 @@ namespace expr {
}

auto interpreter::get_symbol(const std::string &identifier) -> syntax_tree_t {
#ifndef NDEBUG
if (!environment.contains(identifier))
if (!contains(identifier))
throw std::out_of_range(identifier + " not found");
#endif
return syntax_tree_t{environment.find(identifier)};
return syntax_tree_t{identifier_t{identifier}};
}

void interpreter::add_tree(const syntax_tree_t& tree) {
Expand All @@ -83,54 +80,50 @@ namespace expr {
}

auto interpreter::evaluate(const syntax_tree_t& tree) -> symbol_value_t {
return evaluate(tree, *this, environment);
}

auto interpreter::evaluate(const compiler::compiled_expr_collection_t& trees) -> symbol_table_t {
return evaluate(trees, *this, environment);
}

auto interpreter::evaluate(const syntax_tree_t& tree, const interpreter& op, const symbol_table_t& symbols) -> symbol_value_t {
symbol_value_t v{};
std::visit(ya::overload(
[&](const symbol_reference_t& r){ v = symbols.at(r->first); }, // TODO: Should we look up every time? If so, what is the point of storing an iterator in the ast?
[&](const c_symbol_reference_t& r){ v = symbols.at(r->first); }, // TODO: Should we look up every time? If so, what is the point of storing an iterator in the ast?
[&](const identifier_t& r){
auto s = find(r.ident);
if(s == end)
throw std::out_of_range("not found: " + r.ident);
v = s->second;
},
[&](const operator_t& o) {
switch (o.operator_type) {
case operator_type_t::minus: v = op.sub(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::plus: v = op.add(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::star: v = op.mul(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::slash: v = op.div(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::percent: v = op.mod(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::hat: v = op.pow(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::_and: v = op._and(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::_or: v = op._or(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::_xor: v = op._xor(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::_not: v = op._not(evaluate(tree.children[0], op, symbols)); break;
case operator_type_t::_implies: v = op._implies(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::gt: v = op.gt(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::ge: v = op.ge(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::ne: v = op.ne(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::ee: v = op.ee(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::le: v = op.le(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::lt: v = op.lt(evaluate(tree.children[0], op, symbols), evaluate(tree.children[1], op, symbols)); break;
case operator_type_t::parentheses: v = evaluate(tree.children[0], op, symbols); break;
case operator_type_t::minus: v = sub(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::plus: v = add(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::star: v = mul(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::slash: v = div(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::percent: v = mod(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::hat: v = pow(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::_and: v = _and(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::_or: v = _or(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::_xor: v = _xor(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::_not: v = _not(evaluate(tree.children[0])); break;
case operator_type_t::_implies: v = _implies(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::gt: v = gt(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::ge: v = ge(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::ne: v = ne(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::ee: v = ee(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::le: v = le(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::lt: v = lt(evaluate(tree.children[0]), evaluate(tree.children[1])); break;
case operator_type_t::parentheses: v = evaluate(tree.children[0]); break;
}
},
[&v](const symbol_value_t& o){ v = o; },
[&](const root_t& r){
if(!tree.children.empty())
v = evaluate(tree.children[0], op, symbols);
v = evaluate(tree.children[0]);
},
[](auto&&){ throw std::logic_error("operator type not recognized"); }
), static_cast<const underlying_syntax_node_t&>(tree.node));
return v;
}

auto interpreter::evaluate(const compiler::compiled_expr_collection_t& symbol_tree_map, const interpreter& op, const symbol_table_t& symbols) -> symbol_table_t {
symbol_table_t result{};
for(auto& tree : symbol_tree_map)
result[tree.first] = evaluate(tree.second, op, symbols);
return result;
auto interpreter::evaluate(const compiler::compiled_expr_collection_t& trees) -> symbol_table_t {
symbol_table_t res{};
for(auto& tree : trees)
res[tree.first] = evaluate(tree.second);
return res;
}
}
10 changes: 4 additions & 6 deletions src/symbol_table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,12 @@ namespace expr {
}
}

auto operator<<(std::ostream &o, const root_t &r) -> std::ostream & { return o << "ROOT"; }

auto operator<<(std::ostream &o, const symbol_reference_t &r) -> std::ostream & {
return o << r->first << " :-> " << r->second;
auto operator<<(std::ostream &o, const root_t &r) -> std::ostream & {
return o << "ROOT";
}

auto operator<<(std::ostream &o, const c_symbol_reference_t &r) -> std::ostream & {
return o << r->first << " :-> " << r->second;
auto operator<<(std::ostream& o, const identifier_t& r) -> std::ostream& {
return o << r.ident;
}

auto operator<<(std::ostream &o, const underlying_syntax_node_t &n) -> std::ostream & {
Expand Down

0 comments on commit c6dae37

Please sign in to comment.