mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 20:05:31 +00:00
Improved AST support.
This commit is contained in:
parent
36384e69a5
commit
e56062715a
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
66
peglib.h
66
peglib.h
@ -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;
|
||||||
|
43
test/test.cc
43
test/test.cc
@ -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]+");
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user