Added AstOptimizer.

This commit is contained in:
yhirose 2015-07-31 13:06:31 -04:00
parent e778187df8
commit 65d1d99b2b
4 changed files with 139 additions and 67 deletions

View File

@ -49,11 +49,12 @@ int main(int argc, const char** argv)
" ~_ <- [ \t\r\n]* " " ~_ <- [ \t\r\n]* "
); );
parser.enable_ast(true); parser.enable_ast();
auto expr = argv[1]; auto expr = argv[1];
shared_ptr<Ast> ast; shared_ptr<Ast> ast;
if (parser.parse(expr, ast)) { if (parser.parse(expr, ast)) {
ast = AstOptimizer(true).optimize(ast);
ast->print(); ast->print();
cout << expr << " = " << eval(*ast) << endl; cout << expr << " = " << eval(*ast) << endl;
return 0; return 0;

View File

@ -8,6 +8,7 @@
#include <peglib.h> #include <peglib.h>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream>
using namespace peglib; using namespace peglib;
using namespace std; using namespace std;
@ -49,52 +50,71 @@ auto grammar = R"(
~_ <- [ \t\r\n]* ~_ <- [ \t\r\n]*
)"; )";
string format_error_message(const string& path, size_t ln, size_t col, const string& msg) {
stringstream ss;
ss << path << ":" << ln << ":" << col << ": " << msg << endl;
return ss.str();
}
struct Environment struct Environment
{ {
Environment(shared_ptr<Environment> outer = nullptr) : outer(outer) {} Environment(shared_ptr<Environment> outer = nullptr)
: outer_(outer) {}
bool has_value(const string& ident) const {
return has_constant(ident) || has_variable(ident);
}
bool has_variable(const string& ident) const {
if (variables.find(ident) != variables.end()) {
return true;
} else if (!outer_) {
return false;
}
return outer_->has_variable(ident);
}
int get_value(const string& ident) const { int get_value(const string& ident) const {
try { return has_constant(ident) ? get_constant(ident) : get_variable(ident);
return get_constant(ident);
} catch (...) {
return get_variable(ident);
}
} }
void set_variable(const string& ident, int val) { void set_variable(const string& ident, int val) {
if (variables.find(ident) != variables.end()) { if (variables.find(ident) != variables.end()) {
variables[ident] = val; variables[ident] = val;
} else if (outer) { return;
return outer->set_variable(ident, val);
} else {
throw runtime_error("undefined variable");
}
}
int get_constant(const string& ident) const {
if (constants.find(ident) != constants.end()) {
return constants.at(ident);
} else if (outer) {
return outer->get_constant(ident);
} else {
throw runtime_error("undefined constants");
}
}
int get_variable(const string& ident) const {
if (variables.find(ident) != variables.end()) {
return variables.at(ident);
} else if (outer) {
return outer->get_variable(ident);
} else {
throw runtime_error("undefined variable");
} }
outer_->set_variable(ident, val);
} }
map<string, int> constants; map<string, int> constants;
map<string, int> variables; map<string, int> variables;
map<string, shared_ptr<Ast>> procedures; map<string, shared_ptr<Ast>> procedures;
shared_ptr<Environment> outer;
private:
bool has_constant(const string& ident) const {
if (constants.find(ident) != constants.end()) {
return true;
} else if (!outer_) {
return false;
}
return outer_->has_constant(ident);
}
int get_constant(const string& ident) const {
if (constants.find(ident) != constants.end()) {
return constants.at(ident);
}
return outer_->get_constant(ident);
}
int get_variable(const string& ident) const {
if (variables.find(ident) != variables.end()) {
return variables.at(ident);
}
return outer_->get_variable(ident);
}
shared_ptr<Environment> outer_;
}; };
struct Interpreter struct Interpreter
@ -164,7 +184,13 @@ private:
void exec_assignment(const shared_ptr<Ast> ast, shared_ptr<Environment> env) { void exec_assignment(const shared_ptr<Ast> ast, shared_ptr<Environment> env) {
// assignment <- ident ':=' _ expression // assignment <- ident ':=' _ expression
const auto& ident = ast->nodes[0]->token; const auto& ident = ast->nodes[0]->token;
auto val = eval(ast->nodes[1], env); if (!env->has_variable(ident)) {
string msg = "undefined variable '" + ident + "'...";
string s = format_error_message(ast->path, ast->line, ast->column, msg);
throw runtime_error(s);
}
auto expr = ast->nodes[1];
auto val = eval(expr, env);
env->set_variable(ident, val); env->set_variable(ident, val);
} }
@ -185,8 +211,8 @@ private:
void exec_if(const shared_ptr<Ast> ast, shared_ptr<Environment> env) { void exec_if(const shared_ptr<Ast> ast, shared_ptr<Environment> env) {
// if <- 'IF' _ condition 'THEN' _ statement // if <- 'IF' _ condition 'THEN' _ statement
auto cond = eval_condition(ast->nodes[0], env); auto cond = eval_condition(ast->nodes[0], env);
auto stmt = ast->nodes[1];
if (cond) { if (cond) {
auto stmt = ast->nodes[1];
exec(stmt, env); exec(stmt, env);
} }
} }
@ -288,7 +314,9 @@ private:
break; break;
case '/': case '/':
if (rval == 0) { if (rval == 0) {
throw runtime_error("divide by 0 error"); string msg = "divide by 0 error";
string s = format_error_message(ast->path, ast->line, ast->column, msg);
throw runtime_error(s);
} }
val = val / rval; val = val / rval;
break; break;
@ -298,7 +326,13 @@ private:
} }
int eval_ident(const shared_ptr<Ast> ast, shared_ptr<Environment> env) { int eval_ident(const shared_ptr<Ast> ast, shared_ptr<Environment> env) {
return env->get_value(ast->token); const auto& ident = ast->token;
if (!env->has_value(ident)) {
string msg = "undefined variable '" + ident + "'...";
string s = format_error_message(ast->path, ast->line, ast->column, msg);
throw runtime_error(s);
}
return env->get_value(ident);
} }
int eval_number(const shared_ptr<Ast> ast, shared_ptr<Environment> env) { int eval_number(const shared_ptr<Ast> ast, shared_ptr<Environment> env) {
@ -326,6 +360,7 @@ int main(int argc, const char** argv)
return 1; return 1;
} }
// Read a source file into memory
auto path = argv[1]; auto path = argv[1];
vector<char> source; vector<char> source;
if (!read_file(path, source)) { if (!read_file(path, source)) {
@ -333,21 +368,34 @@ int main(int argc, const char** argv)
return -1; return -1;
} }
// Setup a PEG parser
peg parser(grammar); peg parser(grammar);
parser.enable_ast(false, { "program", "statement", "statements", "term", "factor" }); 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;
};
// Parse the source and make an AST
shared_ptr<Ast> ast; shared_ptr<Ast> ast;
if (parser.parse_n(source.data(), source.size(), ast, path)) { if (parser.parse_n(source.data(), source.size(), ast, path)) {
vector<string> filters = { "program", "statement", "statements", "term", "factor" };
ast = AstOptimizer(false, filters).optimize(ast);
if (argc > 2 && string("--ast") == argv[2]) { if (argc > 2 && string("--ast") == argv[2]) {
ast->print(); ast->print();
} }
// Run the AST
try {
Interpreter interp; Interpreter interp;
auto env = make_shared<Environment>(); auto env = make_shared<Environment>();
interp.exec(ast, env); interp.exec(ast, env);
} catch (const runtime_error& e) {
cerr << e.what() << endl;
}
return 0; return 0;
} }
cout << "syntax error..." << endl;
return -1; return -1;
} }

View File

@ -90,7 +90,7 @@ inline peglib::peg& get_parser()
throw std::logic_error("invalid peg grammar"); throw std::logic_error("invalid peg grammar");
} }
parser.enable_ast(true, { "PARAMETERS", "ARGUMENTS", "OBJECT", "ARRAY" }); parser.enable_ast();
} }
return parser; return parser;
@ -924,6 +924,7 @@ inline bool run(
}; };
if (parser.parse_n(expr, len, ast, path.c_str())) { if (parser.parse_n(expr, len, ast, path.c_str())) {
ast = peglib::AstOptimizer(true, { "PARAMETERS", "ARGUMENTS", "OBJECT", "ARRAY" }).optimize(ast);
val = Eval(debugger).eval(*ast, env); val = Eval(debugger).eval(*ast, env);
return true; return true;
} }

View File

@ -1990,7 +1990,7 @@ struct Ast
const std::string original_name; const std::string original_name;
const bool is_token; const bool is_token;
const std::string token; const std::string token;
const std::vector<std::shared_ptr<Ast>> nodes; std::vector<std::shared_ptr<Ast>> nodes;
std::shared_ptr<Ast> parent_node; std::shared_ptr<Ast> parent_node;
#ifndef PEGLIB_NO_CONSTEXPR_SUPPORT #ifndef PEGLIB_NO_CONSTEXPR_SUPPORT
const unsigned int tag; const unsigned int tag;
@ -2028,11 +2028,9 @@ private:
inline const Ast& Ast::get_smallest_ancestor() const { inline const Ast& Ast::get_smallest_ancestor() const {
assert(nodes.size() <= 1); assert(nodes.size() <= 1);
if (nodes.empty()) { if (nodes.empty()) {
return *this; return *this;
} }
return nodes[0]->get_smallest_ancestor(); return nodes[0]->get_smallest_ancestor();
} }
@ -2040,6 +2038,37 @@ inline void Ast::print() const {
AstPrint().print(*this); AstPrint().print(*this);
} }
struct AstOptimizer
{
AstOptimizer(bool optimize_nodes, const std::vector<std::string>& filters = {})
: optimize_nodes_(optimize_nodes)
, filters_(filters) {}
std::shared_ptr<Ast> optimize(std::shared_ptr<Ast> original, std::shared_ptr<Ast> 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<Ast>(*child, original->name.c_str());
}
auto ast = std::make_shared<Ast>(*original);
ast->parent_node = parent;
ast->nodes.clear();
for (auto node : original->nodes) {
auto child = optimize(node, ast);
ast->nodes.push_back(child);
}
return ast;
}
private:
const bool optimize_nodes_;
const std::vector<std::string> filters_;
};
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* peg * peg
*---------------------------------------------------------------------------*/ *---------------------------------------------------------------------------*/
@ -2193,14 +2222,11 @@ public:
} }
} }
peg& enable_ast(bool optimize_nodes, const std::initializer_list<std::string>& filters = {}) { peg& enable_ast() {
for (auto& x: *grammar_) { for (auto& x: *grammar_) {
const auto& name = x.first; const auto& name = x.first;
auto& rule = x.second; auto& rule = x.second;
auto found = std::find(filters.begin(), filters.end(), name) != filters.end();
bool opt = optimize_nodes ? !found : found;
if (!rule.action) { if (!rule.action) {
auto is_token = rule.is_token; auto is_token = rule.is_token;
rule.action = [=](const SemanticValues& sv) { rule.action = [=](const SemanticValues& sv) {
@ -2209,13 +2235,9 @@ public:
return std::make_shared<Ast>(sv.path, line.first, line.second, name.c_str(), std::string(sv.s, sv.n)); return std::make_shared<Ast>(sv.path, line.first, line.second, name.c_str(), std::string(sv.s, sv.n));
} }
std::shared_ptr<Ast> ast;
if (opt && sv.size() == 1) {
ast = std::make_shared<Ast>(*sv[0].get<std::shared_ptr<Ast>>(), name.c_str());
} else {
auto line = line_info(sv.ss, sv.s); auto line = line_info(sv.ss, sv.s);
ast = std::make_shared<Ast>(sv.path, line.first, line.second, name.c_str(), sv.transform<std::shared_ptr<Ast>>()); auto ast = std::make_shared<Ast>(sv.path, line.first, line.second, name.c_str(), sv.transform<std::shared_ptr<Ast>>());
}
for (auto node: ast->nodes) { for (auto node: ast->nodes) {
node->parent_node = ast; node->parent_node = ast;
} }