Improved AST support.

This commit is contained in:
yhirose 2015-06-04 11:41:14 -04:00
parent 36384e69a5
commit e56062715a
5 changed files with 115 additions and 144 deletions

View File

@ -12,71 +12,6 @@
using namespace peglib; using namespace peglib;
using namespace std; using namespace std;
template <typename T, typename U, typename F>
static U reduce(T i, T end, U val, F f) {
if (i == end) {
return val;
}
tie(val, i) = f(val, i);
return reduce(i, end, val, f);
};
struct ast_node
{
virtual ~ast_node() = default;
virtual long eval() = 0;
};
struct ast_ope : public ast_node
{
ast_ope(char ope, shared_ptr<ast_node> left, shared_ptr<ast_node> right)
: ope_(ope), left_(left), right_(right) {}
long eval() override {
switch (ope_) {
case '+': return left_->eval() + right_->eval();
case '-': return left_->eval() - right_->eval();
case '*': return left_->eval() * right_->eval();
case '/': return left_->eval() / right_->eval();
}
assert(false);
return 0;
};
static shared_ptr<ast_node> create(const SemanticValues& sv) {
assert(!sv.empty());
return reduce(
sv.begin() + 1,
sv.end(),
sv[0].get<shared_ptr<ast_node>>(),
[](shared_ptr<ast_node> r, SemanticValues::const_iterator i) {
auto ope = (i++)->val.get<char>();
auto nd = (i++)->val.get<shared_ptr<ast_node>>();
r = make_shared<ast_ope>(ope, r, nd);
return make_tuple(r, i);
});
}
private:
char ope_;
shared_ptr<ast_node> left_;
shared_ptr<ast_node> right_;
};
struct ast_num : public ast_node
{
ast_num(long num) : num_(num) {}
long eval() override { return num_; };
static shared_ptr<ast_node> create(const char* s, size_t n) {
return make_shared<ast_num>(atol(s));
}
private:
long num_;
};
int main(int argc, const char** argv) int main(int argc, const char** argv)
{ {
if (argc < 2 || string("--help") == argv[1]) { if (argc < 2 || string("--help") == argv[1]) {
@ -84,6 +19,26 @@ int main(int argc, const char** argv)
return 1; return 1;
} }
function<long (const Ast&)> eval = [&](const Ast& ast) {
if (ast.name == "NUMBER") {
return stol(ast.token);
} else {
const auto& nodes = ast.nodes;
auto result = eval(*nodes[0]);
for (auto i = 1u; i < nodes.size(); i += 2) {
auto num = eval(*nodes[i + 1]);
auto ope = nodes[i]->token[0];
switch (ope) {
case '+': result += num; break;
case '-': result -= num; break;
case '*': result *= num; break;
case '/': result /= num; break;
}
}
return result;
}
};
peg parser( peg parser(
" EXPRESSION <- _ TERM (TERM_OPERATOR TERM)* " " EXPRESSION <- _ TERM (TERM_OPERATOR TERM)* "
" TERM <- FACTOR (FACTOR_OPERATOR FACTOR)* " " TERM <- FACTOR (FACTOR_OPERATOR FACTOR)* "
@ -94,16 +49,13 @@ int main(int argc, const char** argv)
" ~_ <- [ \t\r\n]* " " ~_ <- [ \t\r\n]* "
); );
parser["EXPRESSION"] = ast_ope::create; parser.enable_ast();
parser["TERM"] = ast_ope::create;
parser["TERM_OPERATOR"] = [](const char* s, size_t n) { return *s; };
parser["FACTOR_OPERATOR"] = [](const char* s, size_t n) { return *s; };
parser["NUMBER"] = ast_num::create;
auto expr = argv[1]; auto expr = argv[1];
shared_ptr<ast_node> ast; shared_ptr<Ast> ast;
if (parser.parse(expr, ast)) { if (parser.parse(expr, ast)) {
cout << expr << " = " << ast->eval() << endl; ast->print();
cout << expr << " = " << eval(*ast) << endl;
return 0; return 0;
} }

View File

@ -72,36 +72,27 @@ peg& get_parser()
throw logic_error("invalid peg grammar"); throw logic_error("invalid peg grammar");
} }
parser.ast({ parser.enable_ast({
{ peg::AstNodeType::Regular, "STATEMENTS", Statements }, { "STATEMENTS", Statements },
{ peg::AstNodeType::Regular, "WHILE", While }, { "WHILE", While },
{ peg::AstNodeType::Regular, "ASSIGNMENT", Assignment }, { "ASSIGNMENT", Assignment },
{ peg::AstNodeType::Regular, "IF", If }, { "IF", If },
{ peg::AstNodeType::Regular, "FUNCTION", Function }, { "FUNCTION", Function },
{ peg::AstNodeType::Regular, "PARAMETERS", Undefined }, { "PARAMETERS", Undefined },
{ peg::AstNodeType::Regular, "FUNCTION_CALL", FunctionCall }, { "FUNCTION_CALL", FunctionCall },
{ peg::AstNodeType::Regular, "ARGUMENTS", Undefined }, { "ARGUMENTS", Undefined },
{ peg::AstNodeType::Optimizable, "PRIMARY", LogicalOr }, { "PRIMARY", LogicalOr, true },
{ peg::AstNodeType::Optimizable, "LOGICAL_OR", LogicalAnd }, { "LOGICAL_OR", LogicalAnd, true },
{ peg::AstNodeType::Optimizable, "LOGICAL_AND", Condition }, { "LOGICAL_AND", Condition, true },
{ peg::AstNodeType::Optimizable, "CONDITION", BinExpresion }, { "CONDITION", BinExpresion, true },
{ peg::AstNodeType::Optimizable, "TERM", UnaryPlus }, { "TERM", UnaryPlus, true },
{ peg::AstNodeType::Optimizable, "UNARY_PLUS", UnaryMinus }, { "UNARY_PLUS", UnaryMinus, true },
{ peg::AstNodeType::Optimizable, "UNARY_MINUS", UnaryNot }, { "UNARY_MINUS", UnaryNot, true },
{ peg::AstNodeType::Optimizable, "UNARY_NOT", BinExpresion }, { "UNARY_NOT", BinExpresion, true },
{ peg::AstNodeType::Token, "CONDITION_OPERATOR", Undefined }, { "NUMBER", Number },
{ peg::AstNodeType::Token, "TERM_OPERATOR", Undefined }, { "BOOLEAN", Boolean },
{ peg::AstNodeType::Token, "UNARY_PLUS_OPERATOR", Undefined }, { "IDENTIFIER", Identifier },
{ peg::AstNodeType::Token, "UNARY_MINUS_OPERATOR", Undefined }, { "INTERPOLATED_STRING", InterpolatedString },
{ peg::AstNodeType::Token, "UNARY_NOT_OPERATOR", Undefined },
{ peg::AstNodeType::Token, "FACTOR_OPERATOR", Undefined },
{ peg::AstNodeType::Token, "NUMBER", Number },
{ peg::AstNodeType::Token, "BOOLEAN", Boolean },
{ peg::AstNodeType::Token, "STRING", Undefined },
{ peg::AstNodeType::Token, "IDENTIFIER", Identifier },
{ peg::AstNodeType::Regular, "INTERPOLATED_STRING", InterpolatedString },
{ peg::AstNodeType::Token, "INTERPOLATED_CONTENT", Undefined },
{ peg::AstNodeType::Token, "MUTABLE", Undefined },
}, },
Undefined); Undefined);
} }

View File

@ -180,8 +180,10 @@ struct SemanticValues : protected std::vector<SemanticValue>
const char* s; const char* s;
size_t n; size_t n;
size_t choice; size_t choice;
bool has_anchor;
bool is_leaf;
SemanticValues() : s(nullptr), n(0), choice(0) {} SemanticValues() : s(nullptr), n(0), choice(0), has_anchor(false), is_leaf(true) {}
std::string str(size_t i = 0) const { std::string str(size_t i = 0) const {
if (i > 0) { if (i > 0) {
@ -190,6 +192,10 @@ struct SemanticValues : protected std::vector<SemanticValue>
return std::string(s, n); return std::string(s, n);
} }
bool is_token() const {
return has_anchor || is_leaf;
}
typedef SemanticValue T; typedef SemanticValue T;
using std::vector<T>::iterator; using std::vector<T>::iterator;
using std::vector<T>::const_iterator; using std::vector<T>::const_iterator;
@ -515,6 +521,8 @@ struct Context
} }
sv.s = nullptr; sv.s = nullptr;
sv.n = 0; sv.n = 0;
sv.has_anchor = false;
sv.is_leaf = true;
return sv; return sv;
} }
@ -618,6 +626,8 @@ public:
sv.s = chldsv.s; sv.s = chldsv.s;
sv.n = chldsv.n; sv.n = chldsv.n;
sv.choice = id; sv.choice = id;
sv.has_anchor = chldsv.has_anchor;
sv.is_leaf = chldsv.is_leaf;
c.pop(); c.pop();
return len; return len;
} }
@ -880,6 +890,7 @@ public:
if (success(len)) { if (success(len)) {
sv.s = s; sv.s = s;
sv.n = len; sv.n = len;
sv.has_anchor = true;
} }
return len; return len;
} }
@ -1286,6 +1297,7 @@ inline any Holder::reduce(const SemanticValues& sv, any& dt, const Action& actio
inline size_t DefinitionReference::parse( inline size_t DefinitionReference::parse(
const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const {
sv.is_leaf = false;
const auto& rule = *get_rule(); const auto& rule = *get_rule();
return rule.parse(s, n, sv, c, dt); return rule.parse(s, n, sv, c, dt);
} }
@ -2013,33 +2025,15 @@ public:
} }
} }
enum AstNodeType {
Regular,
Optimizable,
Token
};
struct AstNodeInfo { struct AstNodeInfo {
AstNodeType type;
const char* name; const char* name;
int tag; int tag;
bool optimize;
}; };
peg& ast(std::initializer_list<AstNodeInfo> list, int tag) { peg& enable_ast(std::initializer_list<AstNodeInfo> list = {}, int tag = -1) {
for (const auto& info: list) { for (const auto& info: list) {
switch (info.type) { ast_node(info);
case Regular:
ast_node(info.name, info.tag);
break;
case Optimizable:
ast_node_optimizable(info.name, info.tag);
break;
case Token:
ast_token(info.name, info.tag);
break;
default:
throw std::logic_error("Invalid Ast type was used...");
}
} }
ast_end(tag); ast_end(tag);
return *this; return *this;
@ -2061,25 +2055,16 @@ private:
} }
} }
void ast_node(const char* name, int tag) { void ast_node(const AstNodeInfo& info) {
(*this)[name] = [=](const SemanticValues& sv) { (*this)[info.name] = [info](const SemanticValues& sv) {
return std::make_shared<Ast>(name, tag, sv.map<std::shared_ptr<Ast>>()); if (sv.is_token()) {
}; return std::make_shared<Ast>(info.name, info.tag, std::string(sv.s, sv.n));
} }
if (info.optimize && sv.size() == 1) {
void ast_node_optimizable(const char* name, int tag) {
(*this)[name] = [=](const SemanticValues& sv) {
if (sv.size() == 1) {
std::shared_ptr<Ast> ast = sv[0].get<std::shared_ptr<Ast>>(); std::shared_ptr<Ast> ast = sv[0].get<std::shared_ptr<Ast>>();
return ast; return ast;
} }
return std::make_shared<Ast>(name, tag, sv.map<std::shared_ptr<Ast>>()); return std::make_shared<Ast>(info.name, info.tag, sv.map<std::shared_ptr<Ast>>());
};
}
void ast_token(const char* name, int tag) {
(*this)[name] = [=](const SemanticValues& sv) {
return std::make_shared<Ast>(name, tag, std::string(sv.s, sv.n));
}; };
} }
@ -2089,7 +2074,10 @@ private:
auto& def = x.second; auto& def = x.second;
auto& action = def.actions.front(); auto& action = def.actions.front();
if (!action) { if (!action) {
action = [&](const SemanticValues& sv) { action = [tag, name](const SemanticValues& sv) {
if (sv.is_token()) {
return std::make_shared<Ast>(name.c_str(), tag, std::string(sv.s, sv.n));
}
if (sv.size() == 1) { if (sv.size() == 1) {
std::shared_ptr<Ast> ast = sv[0].get<std::shared_ptr<Ast>>(); std::shared_ptr<Ast> ast = sv[0].get<std::shared_ptr<Ast>>();
return ast; return ast;

View File

@ -422,6 +422,49 @@ TEST_CASE("Calculator test3", "[general]")
REQUIRE(val == -3); REQUIRE(val == -3);
} }
TEST_CASE("Calculator test with AST", "[general]")
{
peg parser(
" EXPRESSION <- _ TERM (TERM_OPERATOR TERM)* "
" TERM <- FACTOR (FACTOR_OPERATOR FACTOR)* "
" FACTOR <- NUMBER / '(' _ EXPRESSION ')' _ "
" TERM_OPERATOR <- < [-+] > _ "
" FACTOR_OPERATOR <- < [/*] > _ "
" NUMBER <- < [0-9]+ > _ "
" ~_ <- [ \t\r\n]* "
);
const int kTagNumber = 0;
parser.enable_ast({ { "NUMBER", kTagNumber } });
function<long (const Ast&)> eval = [&](const Ast& ast) {
if (ast.tag == kTagNumber) {
return stol(ast.token);
} else {
const auto& nodes = ast.nodes;
auto result = eval(*nodes[0]);
for (auto i = 1u; i < nodes.size(); i += 2) {
auto num = eval(*nodes[i + 1]);
auto ope = nodes[i]->token[0];
switch (ope) {
case '+': result += num; break;
case '-': result -= num; break;
case '*': result *= num; break;
case '/': result /= num; break;
}
}
return result;
}
};
shared_ptr<Ast> ast;
auto ret = parser.parse("1+2*3*(4-5+6)/7-8", ast);
auto val = eval(*ast);
REQUIRE(ret == true);
REQUIRE(val == -3);
}
TEST_CASE("Predicate test", "[general]") TEST_CASE("Predicate test", "[general]")
{ {
peg parser("NUMBER <- [0-9]+"); peg parser("NUMBER <- [0-9]+");

View File

@ -25,7 +25,4 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal EndGlobal