mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2025-05-10 05:42:08 +00:00
Resolve #137
This commit is contained in:
parent
d4bcc53178
commit
80b20a091f
15
README.md
15
README.md
@ -408,7 +408,12 @@ AST generation
|
|||||||
*cpp-peglib* is able to generate an AST (Abstract Syntax Tree) when parsing. `enable_ast` method on `peg::parser` class enables the feature.
|
*cpp-peglib* is able to generate an AST (Abstract Syntax Tree) when parsing. `enable_ast` method on `peg::parser` class enables the feature.
|
||||||
|
|
||||||
```
|
```
|
||||||
peg::parser parser("...");
|
peg::parser parser(R"(
|
||||||
|
...
|
||||||
|
defenition1 <- ... { no_ast_opt }
|
||||||
|
defenition2 <- ... { no_ast_opt }
|
||||||
|
...
|
||||||
|
)");
|
||||||
|
|
||||||
parser.enable_ast();
|
parser.enable_ast();
|
||||||
|
|
||||||
@ -416,14 +421,14 @@ shared_ptr<peg::Ast> ast;
|
|||||||
if (parser.parse("...", ast)) {
|
if (parser.parse("...", ast)) {
|
||||||
cout << peg::ast_to_s(ast);
|
cout << peg::ast_to_s(ast);
|
||||||
|
|
||||||
std::vector<std::string> exceptions = { "defenition1", "defenition2 };
|
ast = parser.optimize_ast(ast);
|
||||||
ast = peg::AstOptimizer(true, exceptions).optimize(ast);
|
|
||||||
|
|
||||||
cout << peg::ast_to_s(ast);
|
cout << peg::ast_to_s(ast);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`peg::AstOptimizer` removes redundant nodes to make a AST simpler. You can make your own AST optimizers to fit your needs.
|
`optimize_ast` removes redundant nodes to make a AST simpler. If you want to disable this behavior from particular rules, `no_ast_opt` instruction can be used.
|
||||||
|
|
||||||
|
It internally calls `peg::AstOptimizer` to do the job. You can make your own AST optimizers to fit your needs.
|
||||||
|
|
||||||
See actual usages in the [AST calculator example](https://github.com/yhirose/cpp-peglib/blob/master/example/calc3.cc) and [PL/0 language example](https://github.com/yhirose/cpp-peglib/blob/master/pl0/pl0.cc).
|
See actual usages in the [AST calculator example](https://github.com/yhirose/cpp-peglib/blob/master/example/calc3.cc) and [PL/0 language example](https://github.com/yhirose/cpp-peglib/blob/master/pl0/pl0.cc).
|
||||||
|
|
||||||
|
@ -23,8 +23,7 @@
|
|||||||
<div class="editor-sub-header">AST</div>
|
<div class="editor-sub-header">AST</div>
|
||||||
<pre id="code-ast" class="editor-area"></pre>
|
<pre id="code-ast" class="editor-area"></pre>
|
||||||
<div class="editor-sub-header">Optimized AST
|
<div class="editor-sub-header">Optimized AST
|
||||||
mode: <select id="opt_mode" type="checkbox"><option value="all">All</option><option value="only">Only</option></select>
|
mode: <select id="opt_mode" type="checkbox"><option value="all">All</option><option value="only">Only</option></select>
|
||||||
rules: <input id="opt_rules" type="text" size="40" placeholder="Enter definition rules separated by comma."></input>
|
|
||||||
</div>
|
</div>
|
||||||
<pre id="code-ast-optimized" class="editor-area"></pre>
|
<pre id="code-ast-optimized" class="editor-area"></pre>
|
||||||
<div id="code-info" class="editor-info"></div>
|
<div id="code-info" class="editor-info"></div>
|
||||||
|
@ -28,7 +28,6 @@ codeAstOptimized.setOptions({
|
|||||||
codeAstOptimized.renderer.$cursorLayer.element.style.opacity=0;
|
codeAstOptimized.renderer.$cursorLayer.element.style.opacity=0;
|
||||||
|
|
||||||
$('#opt_mode').val(localStorage.getItem('optimazationMode') || 'all');
|
$('#opt_mode').val(localStorage.getItem('optimazationMode') || 'all');
|
||||||
$('#opt_rules').val(localStorage.getItem('optimazationRules'));
|
|
||||||
|
|
||||||
function escapeHtml(unsafe) {
|
function escapeHtml(unsafe) {
|
||||||
return unsafe
|
return unsafe
|
||||||
@ -61,12 +60,10 @@ function parse() {
|
|||||||
const codeText = code.getValue();
|
const codeText = code.getValue();
|
||||||
|
|
||||||
const optimazationMode = $('#opt_mode').val();
|
const optimazationMode = $('#opt_mode').val();
|
||||||
const optimazationRules = $('#opt_rules').val();
|
|
||||||
|
|
||||||
localStorage.setItem('grammarText', grammarText);
|
localStorage.setItem('grammarText', grammarText);
|
||||||
localStorage.setItem('codeText', codeText);
|
localStorage.setItem('codeText', codeText);
|
||||||
localStorage.setItem('optimazationMode', optimazationMode);
|
localStorage.setItem('optimazationMode', optimazationMode);
|
||||||
localStorage.setItem('optimazationRules', optimazationRules);
|
|
||||||
|
|
||||||
$grammarInfo.html('');
|
$grammarInfo.html('');
|
||||||
$grammarValidation.hide();
|
$grammarValidation.hide();
|
||||||
@ -80,7 +77,7 @@ function parse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mode = optimazationMode == 'all';
|
const mode = optimazationMode == 'all';
|
||||||
const data = JSON.parse(Module.lint(grammarText, codeText, mode, optimazationRules));
|
const data = JSON.parse(Module.lint(grammarText, codeText, mode));
|
||||||
|
|
||||||
const isValid = data.grammar.length === 0;
|
const isValid = data.grammar.length === 0;
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
@ -126,7 +123,6 @@ $('#code-info').on('click', 'li', makeOnClickInInfo(code));
|
|||||||
|
|
||||||
// Event handing in the AST optimazation
|
// Event handing in the AST optimazation
|
||||||
$('#opt_mode').on('change', setupTimer);
|
$('#opt_mode').on('change', setupTimer);
|
||||||
$('#opt_rules').on('keyup', setupTimer);
|
|
||||||
|
|
||||||
// Show page
|
// Show page
|
||||||
$('#main').css({
|
$('#main').css({
|
||||||
|
@ -53,20 +53,7 @@ void parse_code(const std::string &text, peg::parser &peg, std::string &json,
|
|||||||
json += "]";
|
json += "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<std::string> splitRulesText(const std::string &s) {
|
std::string lint(const std::string &grammarText, const std::string &codeText, bool opt_mode) {
|
||||||
std::vector<std::string> elems;
|
|
||||||
std::stringstream ss(s);
|
|
||||||
std::string elem;
|
|
||||||
while (getline(ss, elem, ',')) {
|
|
||||||
if (!elem.empty()) {
|
|
||||||
elems.push_back(elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return elems;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string lint(const std::string &grammarText, const std::string &codeText,
|
|
||||||
bool opt_mode, const std::string &opt_rules_text) {
|
|
||||||
std::string grammarResult;
|
std::string grammarResult;
|
||||||
std::string codeResult;
|
std::string codeResult;
|
||||||
std::string astResult;
|
std::string astResult;
|
||||||
@ -80,9 +67,8 @@ std::string lint(const std::string &grammarText, const std::string &codeText,
|
|||||||
parse_code(codeText, peg, codeResult, ast);
|
parse_code(codeText, peg, codeResult, ast);
|
||||||
if (ast) {
|
if (ast) {
|
||||||
astResult = escape_json(peg::ast_to_s(ast));
|
astResult = escape_json(peg::ast_to_s(ast));
|
||||||
auto rules = splitRulesText(opt_rules_text);
|
|
||||||
astResultOptimized = escape_json(
|
astResultOptimized = escape_json(
|
||||||
peg::ast_to_s(peg::AstOptimizer(opt_mode, rules).optimize(ast)));
|
peg::ast_to_s(peg.optimize_ast(ast, opt_mode)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
BIN
docs/native.wasm
BIN
docs/native.wasm
Binary file not shown.
@ -54,7 +54,7 @@ int main(int argc, const char **argv) {
|
|||||||
auto expr = argv[1];
|
auto expr = argv[1];
|
||||||
std::shared_ptr<Ast> ast;
|
std::shared_ptr<Ast> ast;
|
||||||
if (parser.parse(expr, ast)) {
|
if (parser.parse(expr, ast)) {
|
||||||
ast = AstOptimizer(true).optimize(ast);
|
ast = parser.optimize_ast(ast);
|
||||||
std::cout << ast_to_s(ast);
|
std::cout << ast_to_s(ast);
|
||||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -54,7 +54,7 @@ int main(int argc, const char **argv) {
|
|||||||
auto expr = argv[1];
|
auto expr = argv[1];
|
||||||
std::shared_ptr<Ast> ast;
|
std::shared_ptr<Ast> ast;
|
||||||
if (parser.parse(expr, ast)) {
|
if (parser.parse(expr, ast)) {
|
||||||
ast = AstOptimizer(true).optimize(ast);
|
ast = parser.optimize_ast(ast);
|
||||||
std::cout << ast_to_s(ast);
|
std::cout << ast_to_s(ast);
|
||||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -35,7 +35,6 @@ inline vector<string> split(const string &s, char delim) {
|
|||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
auto opt_ast = false;
|
auto opt_ast = false;
|
||||||
auto opt_optimize = false;
|
auto opt_optimize = false;
|
||||||
auto opt_mode = true;
|
|
||||||
vector<string> opt_rules;
|
vector<string> opt_rules;
|
||||||
auto opt_help = false;
|
auto opt_help = false;
|
||||||
auto opt_source = false;
|
auto opt_source = false;
|
||||||
@ -50,17 +49,8 @@ int main(int argc, const char **argv) {
|
|||||||
opt_help = true;
|
opt_help = true;
|
||||||
} else if (string("--ast") == arg) {
|
} else if (string("--ast") == arg) {
|
||||||
opt_ast = true;
|
opt_ast = true;
|
||||||
} else if (string("--opt") == arg || string("--opt-all") == arg) {
|
} else if (string("--opt") == arg) {
|
||||||
opt_optimize = true;
|
opt_optimize = true;
|
||||||
opt_mode = true;
|
|
||||||
} else if (string("--opt-only") == arg) {
|
|
||||||
opt_optimize = true;
|
|
||||||
opt_mode = false;
|
|
||||||
} else if (string("--opt-rules") == arg) {
|
|
||||||
if (argi < argc) {
|
|
||||||
std::string s = argv[argi++];
|
|
||||||
opt_rules = split(s, ',');
|
|
||||||
}
|
|
||||||
} else if (string("--source") == arg) {
|
} else if (string("--source") == arg) {
|
||||||
opt_source = true;
|
opt_source = true;
|
||||||
if (argi < argc) {
|
if (argi < argc) {
|
||||||
@ -190,7 +180,7 @@ int main(int argc, const char **argv) {
|
|||||||
|
|
||||||
if (ast) {
|
if (ast) {
|
||||||
if (opt_optimize) {
|
if (opt_optimize) {
|
||||||
ast = peg::AstOptimizer(opt_mode, opt_rules).optimize(ast);
|
ast = parser.optimize_ast(ast);
|
||||||
}
|
}
|
||||||
std::cout << peg::ast_to_s(ast);
|
std::cout << peg::ast_to_s(ast);
|
||||||
}
|
}
|
||||||
|
54
peglib.h
54
peglib.h
@ -2287,6 +2287,7 @@ public:
|
|||||||
bool disable_action = false;
|
bool disable_action = false;
|
||||||
|
|
||||||
std::string error_message;
|
std::string error_message;
|
||||||
|
bool no_ast_opt = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Reference;
|
friend class Reference;
|
||||||
@ -3058,10 +3059,10 @@ private:
|
|||||||
~g["COMMA"] <= seq(chr(','), g["Spacing"]);
|
~g["COMMA"] <= seq(chr(','), g["Spacing"]);
|
||||||
|
|
||||||
// Instruction grammars
|
// Instruction grammars
|
||||||
g["Instruction"] <=
|
g["Instruction"] <= seq(g["BeginBlacket"],
|
||||||
seq(g["BeginBlacket"],
|
cho(cho(g["PrecedenceClimbing"]),
|
||||||
cho(cho(g["PrecedenceClimbing"]), cho(g["ErrorMessage"])),
|
cho(g["ErrorMessage"]), cho(g["NoAstOpt"])),
|
||||||
g["EndBlacket"]);
|
g["EndBlacket"]);
|
||||||
|
|
||||||
~g["SpacesZom"] <= zom(g["Space"]);
|
~g["SpacesZom"] <= zom(g["Space"]);
|
||||||
~g["SpacesOom"] <= oom(g["Space"]);
|
~g["SpacesOom"] <= oom(g["Space"]);
|
||||||
@ -3084,6 +3085,9 @@ private:
|
|||||||
g["ErrorMessage"] <=
|
g["ErrorMessage"] <=
|
||||||
seq(lit("message"), g["SpacesOom"], g["LiteralD"], g["SpacesZom"]);
|
seq(lit("message"), g["SpacesOom"], g["LiteralD"], g["SpacesZom"]);
|
||||||
|
|
||||||
|
// No Ast node optimazation instruction
|
||||||
|
g["NoAstOpt"] <= seq(lit("no_ast_opt"), g["SpacesZom"]);
|
||||||
|
|
||||||
// Set definition names
|
// Set definition names
|
||||||
for (auto &x : g) {
|
for (auto &x : g) {
|
||||||
x.second.name = x.first;
|
x.second.name = x.first;
|
||||||
@ -3403,6 +3407,12 @@ private:
|
|||||||
instruction.data = std::any_cast<std::string>(vs[0]);
|
instruction.data = std::any_cast<std::string>(vs[0]);
|
||||||
return instruction;
|
return instruction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
g["NoAstOpt"] = [](const SemanticValues & /*vs*/) {
|
||||||
|
Instruction instruction;
|
||||||
|
instruction.type = "no_ast_opt";
|
||||||
|
return instruction;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool apply_precedence_instruction(Definition &rule,
|
bool apply_precedence_instruction(Definition &rule,
|
||||||
@ -3618,6 +3628,8 @@ private:
|
|||||||
}
|
}
|
||||||
} else if (instruction.type == "message") {
|
} else if (instruction.type == "message") {
|
||||||
rule.error_message = std::any_cast<std::string>(instruction.data);
|
rule.error_message = std::any_cast<std::string>(instruction.data);
|
||||||
|
} else if (instruction.type == "no_ast_opt") {
|
||||||
|
rule.no_ast_opt = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4035,11 +4047,10 @@ public:
|
|||||||
|
|
||||||
const Definition &operator[](const char *s) const { return (*grammar_)[s]; }
|
const Definition &operator[](const char *s) const { return (*grammar_)[s]; }
|
||||||
|
|
||||||
std::vector<std::string> get_rule_names() {
|
std::vector<std::string> get_rule_names() const {
|
||||||
std::vector<std::string> rules;
|
std::vector<std::string> rules;
|
||||||
rules.reserve(grammar_->size());
|
for (auto &[name, _] : *grammar_) {
|
||||||
for (auto const &r : *grammar_) {
|
rules.push_back(name);
|
||||||
rules.emplace_back(r.first);
|
|
||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
@ -4051,14 +4062,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T = Ast> parser &enable_ast() {
|
|
||||||
for (auto &x : *grammar_) {
|
|
||||||
auto &rule = x.second;
|
|
||||||
if (!rule.action) { add_ast_action<T>(rule); }
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_trace(TracerEnter tracer_enter, TracerLeave tracer_leave) {
|
void enable_trace(TracerEnter tracer_enter, TracerLeave tracer_leave) {
|
||||||
if (grammar_ != nullptr) {
|
if (grammar_ != nullptr) {
|
||||||
auto &rule = (*grammar_)[start_];
|
auto &rule = (*grammar_)[start_];
|
||||||
@ -4067,6 +4070,17 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T = Ast> parser &enable_ast() {
|
||||||
|
for (auto &[_, rule] : *grammar_) {
|
||||||
|
if (!rule.action) { add_ast_action<T>(rule); }
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> std::shared_ptr<T> optimize_ast(std::shared_ptr<T> ast, bool opt_mode = true) const {
|
||||||
|
return AstOptimizer(opt_mode, get_no_ast_opt_rules()).optimize(ast);
|
||||||
|
}
|
||||||
|
|
||||||
Log log;
|
Log log;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -4077,6 +4091,14 @@ private:
|
|||||||
return ret && !r.recovered;
|
return ret && !r.recovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> get_no_ast_opt_rules() const {
|
||||||
|
std::vector<std::string> rules;
|
||||||
|
for (auto &[name, rule] : *grammar_) {
|
||||||
|
if (rule.no_ast_opt) { rules.push_back(name); }
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Grammar> grammar_;
|
std::shared_ptr<Grammar> grammar_;
|
||||||
std::string start_;
|
std::string start_;
|
||||||
};
|
};
|
||||||
|
@ -657,7 +657,7 @@ TEST_CASE("Calculator test with AST", "[general]") {
|
|||||||
|
|
||||||
std::shared_ptr<Ast> ast;
|
std::shared_ptr<Ast> ast;
|
||||||
auto ret = parser.parse("1+2*3*(4-5+6)/7-8", ast);
|
auto ret = parser.parse("1+2*3*(4-5+6)/7-8", ast);
|
||||||
ast = AstOptimizer(true).optimize(ast);
|
ast = parser.optimize_ast(ast);
|
||||||
auto val = eval(*ast);
|
auto val = eval(*ast);
|
||||||
|
|
||||||
REQUIRE(ret == true);
|
REQUIRE(ret == true);
|
||||||
|
@ -1124,7 +1124,7 @@ CATEGORY <- < [-_a-zA-Z0-9\u0080-\uFFFF ]+ > _
|
|||||||
ATTRIBUTES <- ATTRIBUTE (',' _ ATTRIBUTE)*
|
ATTRIBUTES <- ATTRIBUTE (',' _ ATTRIBUTE)*
|
||||||
ATTRIBUTE <- < [-_a-zA-Z0-9\u0080-\uFFFF]+ > _
|
ATTRIBUTE <- < [-_a-zA-Z0-9\u0080-\uFFFF]+ > _
|
||||||
|
|
||||||
ENTRIES <- (ENTRY (__ ENTRY)*)?
|
ENTRIES <- (ENTRY (__ ENTRY)*)? { no_ast_opt }
|
||||||
|
|
||||||
ENTRY <- ONE_WAY PHRASE ('|' _ PHRASE)* !'='
|
ENTRY <- ONE_WAY PHRASE ('|' _ PHRASE)* !'='
|
||||||
/ PHRASE ('|' _ PHRASE)+ !'='
|
/ PHRASE ('|' _ PHRASE)+ !'='
|
||||||
@ -1187,7 +1187,7 @@ rrr | sss
|
|||||||
|
|
||||||
)", ast));
|
)", ast));
|
||||||
|
|
||||||
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
ast = pg.optimize_ast(ast);
|
||||||
|
|
||||||
REQUIRE(ast_to_s(ast) ==
|
REQUIRE(ast_to_s(ast) ==
|
||||||
R"(+ START
|
R"(+ START
|
||||||
@ -1275,7 +1275,7 @@ TEST_CASE("Error recovery 2", "[error]") {
|
|||||||
REQUIRE_FALSE(pg.parse(R"([000]],[111],[222z,"aaa,"bbb",ccc"],[ddd",444,555,"eee],[
|
REQUIRE_FALSE(pg.parse(R"([000]],[111],[222z,"aaa,"bbb",ccc"],[ddd",444,555,"eee],[
|
||||||
)", ast));
|
)", ast));
|
||||||
|
|
||||||
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
ast = pg.optimize_ast(ast);
|
||||||
|
|
||||||
REQUIRE(ast_to_s(ast) ==
|
REQUIRE(ast_to_s(ast) ==
|
||||||
R"(+ START
|
R"(+ START
|
||||||
@ -1386,7 +1386,7 @@ SkipToRCUR ← (!RCUR (LCUR SkipToRCUR / .))* RCUR
|
|||||||
}
|
}
|
||||||
)", ast));
|
)", ast));
|
||||||
|
|
||||||
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
ast = pg.optimize_ast(ast);
|
||||||
|
|
||||||
REQUIRE(ast_to_s(ast) ==
|
REQUIRE(ast_to_s(ast) ==
|
||||||
R"(+ Prog
|
R"(+ Prog
|
||||||
|
Loading…
Reference in New Issue
Block a user