mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 20:05:31 +00:00
Added AstOptimizer.
This commit is contained in:
parent
e778187df8
commit
65d1d99b2b
@ -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;
|
||||||
|
122
example/pl0.cc
122
example/pl0.cc
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
48
peglib.h
48
peglib.h
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user