diff --git a/example/calc3.cc b/example/calc3.cc index e4d2304..38d87c7 100644 --- a/example/calc3.cc +++ b/example/calc3.cc @@ -55,7 +55,7 @@ int main(int argc, const char** argv) shared_ptr ast; if (parser.parse(expr, ast)) { ast = AstOptimizer(true).optimize(ast); - ast->print(); + AstPrint::print(ast); cout << expr << " = " << eval(*ast) << endl; return 0; } diff --git a/example/pl0.cc b/example/pl0.cc index 56be19f..ce4f447 100644 --- a/example/pl0.cc +++ b/example/pl0.cc @@ -78,11 +78,11 @@ bool read_file(const char* path, vector& buff) /* * Ast */ -struct Scope; +struct SymbolScope; struct Annotation { - shared_ptr scope; + shared_ptr scope; }; typedef AstBase AstPL0; @@ -90,142 +90,124 @@ typedef AstBase AstPL0; /* * Symbol Table */ -struct Symbol { - int value; - bool is_constant; -}; - -struct Scope +struct SymbolScope { - Scope(shared_ptr outer = nullptr) : outer(outer) {} + SymbolScope(shared_ptr outer) : outer(outer) {} bool has_symbol(const string& ident) const { - auto it = symbols.find(ident); - if (it != symbols.end()) { - return true; - } - return outer ? outer->has_symbol(ident) : false; + auto ret = constants.find(ident) != constants.end() || variables.find(ident) != variables.end(); + return ret ? true : (outer ? outer->has_symbol(ident) : false); } bool has_constant(const string& ident) const { - auto it = symbols.find(ident); - if (it != symbols.end() && it->second.is_constant) { - return true; - } - return outer ? outer->has_constant(ident) : false; + auto ret = constants.find(ident) != constants.end(); + return ret ? true : (outer ? outer->has_constant(ident) : false); } bool has_variable(const string& ident) const { - auto it = symbols.find(ident); - if (it != symbols.end() && !it->second.is_constant) { - return true; - } - return outer ? outer->has_variable(ident) : false; + auto ret = variables.find(ident) != variables.end(); + return ret ? true : (outer ? outer->has_variable(ident) : false); } bool has_procedure(const string& ident) const { - auto it = procedures.find(ident); - if (it != procedures.end()) { - return true; - } - return outer ? outer->has_procedure(ident) : false; + auto ret = procedures.find(ident) != procedures.end(); + return ret ? true : (outer ? outer->has_procedure(ident) : false); } - shared_ptr get_procedure(const string& ident) const { - auto it = procedures.find(ident); - if (it != procedures.end()) { - return it->second; - } - return outer->get_procedure(ident); - } - - map symbols; + map constants; + set variables; map> procedures; private: - shared_ptr outer; + shared_ptr outer; }; +void throw_runtime_error(const shared_ptr node, const string& msg) { + throw runtime_error(format_error_message(node->path, node->line, node->column, msg)); +} + struct SymbolTable { - static void build(const shared_ptr ast, shared_ptr scope = nullptr) { + static void build_on_ast(const shared_ptr ast, shared_ptr scope = nullptr) { switch (ast->tag) { - case "block"_: visit_block(ast, scope); break; - case "assignment"_: visit_assignment(ast, scope); break; - case "call"_: visit_call(ast, scope); break; - case "ident"_: visit_ident(ast, scope); break; - default: for (auto node: ast->nodes) { build(node, scope); } break; + case "block"_: block(ast, scope); break; + case "assignment"_: assignment(ast, scope); break; + case "call"_: call(ast, scope); break; + case "ident"_: ident(ast, scope); break; + default: for (auto node: ast->nodes) { build_on_ast(node, scope); } break; } } private: - static void visit_block(const shared_ptr ast, shared_ptr outer) { + static void block(const shared_ptr ast, shared_ptr outer) { // block <- const var procedure statement - auto scope = make_shared(outer); + auto scope = make_shared(outer); const auto& nodes = ast->nodes; - visit_constants(nodes[0], scope); - visit_variables(nodes[1], scope); - visit_procedures(nodes[2], scope); - build(nodes[3], scope); + constants(nodes[0], scope); + variables(nodes[1], scope); + procedures(nodes[2], scope); + build_on_ast(nodes[3], scope); ast->scope = scope; } - static void visit_constants(const shared_ptr ast, shared_ptr scope) { + static void constants(const shared_ptr ast, shared_ptr scope) { // const <- ('CONST' _ ident '=' _ number(',' _ ident '=' _ number)* ';' _) ? const auto& nodes = ast->nodes; for (auto i = 0u; i < nodes.size(); i += 2) { const auto& ident = nodes[i + 0]->token; + if (scope->has_symbol(ident)) { + throw_runtime_error(nodes[i], "'" + ident + "' is already defined..."); + } auto number = stoi(nodes[i + 1]->token); - scope->symbols.emplace(ident, Symbol{ number, true }); + scope->constants.emplace(ident, number); } } - static void visit_variables(const shared_ptr ast, shared_ptr scope) { + static void variables(const shared_ptr ast, shared_ptr scope) { // var <- ('VAR' _ ident(',' _ ident)* ';' _) ? const auto& nodes = ast->nodes; for (auto i = 0u; i < nodes.size(); i += 1) { const auto& ident = nodes[i]->token; - scope->symbols.emplace(ident, Symbol{ 0, false }); + if (scope->has_symbol(ident)) { + throw_runtime_error(nodes[i], "'" + ident + "' is already defined..."); + } + scope->variables.emplace(ident); } } - static void visit_procedures(const shared_ptr ast, shared_ptr scope) { + static void procedures(const shared_ptr ast, shared_ptr scope) { // procedure <- ('PROCEDURE' _ ident ';' _ block ';' _)* const auto& nodes = ast->nodes; for (auto i = 0u; i < nodes.size(); i += 2) { const auto& ident = nodes[i + 0]->token; auto block = nodes[i + 1]; scope->procedures[ident] = block; - build(block, scope); + build_on_ast(block, scope); } } - static void visit_assignment(const shared_ptr ast, shared_ptr scope) { + static void assignment(const shared_ptr ast, shared_ptr scope) { // assignment <- ident ':=' _ expression const auto& ident = ast->nodes[0]->token; - if (!scope->has_variable(ident)) { - string msg = "undefined variable '" + ident + "'..."; - string s = format_error_message(ast->path, ast->line, ast->column, msg); - throw runtime_error(s); + if (scope->has_constant(ident)) { + throw_runtime_error(ast->nodes[0], "cannot modify constant value '" + ident + "'..."); + } else if (!scope->has_variable(ident)) { + throw_runtime_error(ast->nodes[0], "undefined variable '" + ident + "'..."); } } - static void visit_call(const shared_ptr ast, shared_ptr scope) { + static void call(const shared_ptr ast, shared_ptr scope) { // call <- 'CALL' _ ident const auto& ident = ast->nodes[0]->token; if (!scope->has_procedure(ident)) { - string msg = "undefined procedure '" + ident + "'..."; - string s = format_error_message(ast->path, ast->line, ast->column, msg); - throw runtime_error(s); + throw_runtime_error(ast->nodes[0], "undefined procedure '" + ident + "'..."); } } - static void visit_ident(const shared_ptr ast, shared_ptr scope) { + static void ident(const shared_ptr ast, shared_ptr scope) { const auto& ident = ast->token; if (!scope->has_symbol(ident)) { - string msg = "undefined variable '" + ident + "'..."; - string s = format_error_message(ast->path, ast->line, ast->column, msg); - throw runtime_error(s); + throw_runtime_error(ast, "undefined variable '" + ident + "'..."); } } }; @@ -235,33 +217,36 @@ private: */ struct Environment { - Environment(shared_ptr scope = nullptr, shared_ptr outer = nullptr) : scope(scope), outer(outer) { - if (scope) { - symbols = scope->symbols; - } - } + Environment(shared_ptr scope, shared_ptr outer) + : scope(scope), outer(outer) {} int get_value(const string& ident) const { - auto it = symbols.find(ident); - if (it != symbols.end()) { - return it->second.value; + auto it = scope->constants.find(ident); + if (it != scope->constants.end()) { + return it->second; + } else if (scope->variables.find(ident) != scope->variables.end()) { + return variables.at(ident); } return outer->get_value(ident); } void set_variable(const string& ident, int val) { - auto it = symbols.find(ident); - if (it != symbols.end() && !it->second.is_constant) { - symbols[ident].value = val; + if (scope->variables.find(ident) != scope->variables.end()) { + variables[ident] = val; } else { outer->set_variable(ident, val); } } - shared_ptr scope; + shared_ptr get_procedure(const string& ident) const { + auto it = scope->procedures.find(ident); + return it != scope->procedures.end() ? it->second : outer->get_procedure(ident); + } + + shared_ptr scope; private: - map symbols; + map variables; shared_ptr outer; }; @@ -270,7 +255,7 @@ private: */ struct Interpreter { - static void exec(const shared_ptr ast, shared_ptr env) { + static void exec(const shared_ptr ast, shared_ptr env = nullptr) { switch (ast->tag) { case "block"_: exec_block(ast, env); break; case "statement"_: exec_statement(ast, env); break; @@ -288,12 +273,11 @@ struct Interpreter private: static void exec_block(const shared_ptr ast, shared_ptr outer) { // block <- const var procedure statement - auto env = make_shared(ast->scope, outer); - exec(ast->nodes[3], env); + exec(ast->nodes[3], make_shared(ast->scope, outer)); } static void exec_statement(const shared_ptr ast, shared_ptr env) { - // statement <-(assignment / call / statements / if / while / out / in) ? + // statement <- (assignment / call / statements / if / while / out / in)? if (!ast->nodes.empty()) { exec(ast->nodes[0], env); } @@ -301,17 +285,12 @@ private: static void exec_assignment(const shared_ptr ast, shared_ptr env) { // assignment <- ident ':=' _ expression - const auto& ident = ast->nodes[0]->token; - auto expr = ast->nodes[1]; - auto val = eval(expr, env); - env->set_variable(ident, val); + env->set_variable(ast->nodes[0]->token, eval(ast->nodes[1], env)); } static void exec_call(const shared_ptr ast, shared_ptr env) { // call <- 'CALL' _ ident - const auto& ident = ast->nodes[0]->token; - auto proc = env->scope->get_procedure(ident); - exec_block(proc, env); + exec_block(env->get_procedure(ast->nodes[0]->token), env); } static void exec_statements(const shared_ptr ast, shared_ptr env) { @@ -323,10 +302,8 @@ private: static void exec_if(const shared_ptr ast, shared_ptr env) { // if <- 'IF' _ condition 'THEN' _ statement - auto cond = eval_condition(ast->nodes[0], env); - if (cond) { - auto stmt = ast->nodes[1]; - exec(stmt, env); + if (eval_condition(ast->nodes[0], env)) { + exec(ast->nodes[1], env); } } @@ -334,25 +311,21 @@ private: // while <- 'WHILE' _ condition 'DO' _ statement auto cond = ast->nodes[0]; auto stmt = ast->nodes[1]; - auto ret = eval_condition(cond, env); - while (ret) { + while (eval_condition(cond, env)) { exec(stmt, env); - ret = eval_condition(cond, env); } } static void exec_out(const shared_ptr ast, shared_ptr env) { // out <- '!' _ expression - auto val = eval(ast->nodes[0], env); - cout << val << endl; + cout << eval(ast->nodes[0], env) << endl; } static void exec_in(const shared_ptr ast, shared_ptr env) { // in <- '?' _ ident int val; cin >> val; - const auto& ident = ast->nodes[0]->token; - env->set_variable(ident, val); + env->set_variable(ast->nodes[0]->token, val); } static bool eval_condition(const shared_ptr ast, shared_ptr env) { @@ -367,8 +340,7 @@ private: static bool eval_odd(const shared_ptr ast, shared_ptr env) { // odd <- 'ODD' _ expression - auto val = eval_expression(ast->nodes[0], env); - return val != 0; + return eval_expression(ast->nodes[0], env) != 0; } static bool eval_compare(const shared_ptr ast, shared_ptr env) { @@ -427,8 +399,7 @@ private: break; case '/': if (rval == 0) { - string msg = "divide by 0 error"; - throw runtime_error(format_error_message(ast->path, ast->line, ast->column, msg)); + throw_runtime_error(ast, "divide by 0 error"); } val = val / rval; break; @@ -438,8 +409,7 @@ private: } static int eval_ident(const shared_ptr ast, shared_ptr env) { - const auto& ident = ast->token; - return env->get_value(ident); + return env->get_value(ast->token); } static int eval_number(const shared_ptr ast, shared_ptr env) { @@ -468,20 +438,19 @@ int main(int argc, const char** argv) // Setup a PEG parser peg parser(grammar); parser.enable_ast(); - parser.log = [&](size_t ln, size_t col, const string& err_msg) { - cerr << format_error_message(path, ln, col, err_msg) << endl; + parser.log = [&](size_t ln, size_t col, const string& msg) { + cerr << format_error_message(path, ln, col, msg) << endl; }; // Parse the source and make an AST shared_ptr ast; if (parser.parse_n(source.data(), source.size(), ast, path)) { if (argc > 2 && string("--ast") == argv[2]) { - ast->print(); + AstPrint::print(ast); } - try { - SymbolTable::build(ast); - Interpreter::exec(ast, make_shared()); + SymbolTable::build_on_ast(ast); + Interpreter::exec(ast); } catch (const runtime_error& e) { cerr << e.what() << endl; } diff --git a/lint/peglint.cc b/lint/peglint.cc index 6b7095b..1125e64 100644 --- a/lint/peglint.cc +++ b/lint/peglint.cc @@ -97,7 +97,7 @@ int main(int argc, const char** argv) } ast = peglib::AstOptimizer(opt_optimize_ast_nodes).optimize(ast); - peglib::AstPrint().print(*ast); + peglib::AstPrint::print(ast); } else { if (!peg.parse_n(source.data(), source.size())) { return -1; diff --git a/peglib.h b/peglib.h index 8d07fba..64760da 100644 --- a/peglib.h +++ b/peglib.h @@ -1955,8 +1955,8 @@ inline constexpr unsigned int operator "" _(const char* s, size_t) { } #endif -template -struct AstBase : public MixedIn +template +struct AstBase : public Annotation { AstBase(const char* path, size_t line, size_t column, const char* name, const std::vector>& nodes) : path(path ? path : "") @@ -2001,8 +2001,6 @@ struct AstBase : public MixedIn , nodes(ast.nodes) {} - void print() const; - const std::string path; const size_t line; const size_t column; @@ -2017,62 +2015,53 @@ struct AstBase : public MixedIn const bool is_token; const std::string token; - std::vector>> nodes; - std::shared_ptr> parent_node; + std::vector>> nodes; + std::shared_ptr> parent_node; }; -template -struct AstPrintBase +struct AstPrint { - AstPrintBase() : level_(-1) {} - - void print(const AstBase& ast) { - level_ += 1; - for (auto i = 0; i < level_; i++) { std::cout << " "; } + template + static void print(const std::shared_ptr& ptr, int level = 0) { + const auto& ast = *ptr; + for (auto i = 0; i < level; i++) { std::cout << " "; } if (ast.is_token) { std::cout << "- " << name(ast) << ": '" << ast.token << "'" << std::endl; } else { std::cout << "+ " << name(ast) << std::endl; } - for (auto node : ast.nodes) { print(*node); } - level_ -= 1; + for (auto node : ast.nodes) { print(node, level + 1); } } private: - std::string name(const AstBase& ast) { + template + static std::string name(const T& ast) { if (ast.name == ast.original_name) { return ast.name; } else { return ast.original_name + " (" + ast.name + ")"; } } - - int level_; }; -template -inline void AstBase::print() const { - AstPrintBase().print(*this); -} - -template -struct AstOptimizerBase +struct AstOptimizer { - AstOptimizerBase(bool optimize_nodes, const std::vector& filters = {}) + AstOptimizer(bool optimize_nodes, const std::vector& filters = {}) : optimize_nodes_(optimize_nodes) , filters_(filters) {} - std::shared_ptr> optimize(std::shared_ptr> original, std::shared_ptr> parent = nullptr) { + template + std::shared_ptr optimize(std::shared_ptr original, std::shared_ptr parent = nullptr) { auto found = std::find(filters_.begin(), filters_.end(), original->name) != filters_.end(); bool opt = optimize_nodes_ ? !found : found; if (opt && original->nodes.size() == 1) { auto child = optimize(original->nodes[0], parent); - return std::make_shared>(*child, original->name.c_str()); + return std::make_shared(*child, original->name.c_str()); } - auto ast = std::make_shared>(*original); + auto ast = std::make_shared(*original); ast->parent_node = parent; ast->nodes.clear(); for (auto node : original->nodes) { @@ -2089,8 +2078,6 @@ private: struct EmptyType {}; typedef AstBase Ast; -typedef AstPrintBase AstPrint; -typedef AstOptimizerBase AstOptimizer; /*----------------------------------------------------------------------------- * peg