mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2025-05-09 21:32: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.
|
||||
|
||||
```
|
||||
peg::parser parser("...");
|
||||
peg::parser parser(R"(
|
||||
...
|
||||
defenition1 <- ... { no_ast_opt }
|
||||
defenition2 <- ... { no_ast_opt }
|
||||
...
|
||||
)");
|
||||
|
||||
parser.enable_ast();
|
||||
|
||||
@ -416,14 +421,14 @@ shared_ptr<peg::Ast> ast;
|
||||
if (parser.parse("...", ast)) {
|
||||
cout << peg::ast_to_s(ast);
|
||||
|
||||
std::vector<std::string> exceptions = { "defenition1", "defenition2 };
|
||||
ast = peg::AstOptimizer(true, exceptions).optimize(ast);
|
||||
|
||||
ast = parser.optimize_ast(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).
|
||||
|
||||
|
@ -23,8 +23,7 @@
|
||||
<div class="editor-sub-header">AST</div>
|
||||
<pre id="code-ast" class="editor-area"></pre>
|
||||
<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>
|
||||
rules: <input id="opt_rules" type="text" size="40" placeholder="Enter definition rules separated by comma."></input>
|
||||
mode: <select id="opt_mode" type="checkbox"><option value="all">All</option><option value="only">Only</option></select>
|
||||
</div>
|
||||
<pre id="code-ast-optimized" class="editor-area"></pre>
|
||||
<div id="code-info" class="editor-info"></div>
|
||||
|
@ -28,7 +28,6 @@ codeAstOptimized.setOptions({
|
||||
codeAstOptimized.renderer.$cursorLayer.element.style.opacity=0;
|
||||
|
||||
$('#opt_mode').val(localStorage.getItem('optimazationMode') || 'all');
|
||||
$('#opt_rules').val(localStorage.getItem('optimazationRules'));
|
||||
|
||||
function escapeHtml(unsafe) {
|
||||
return unsafe
|
||||
@ -61,12 +60,10 @@ function parse() {
|
||||
const codeText = code.getValue();
|
||||
|
||||
const optimazationMode = $('#opt_mode').val();
|
||||
const optimazationRules = $('#opt_rules').val();
|
||||
|
||||
localStorage.setItem('grammarText', grammarText);
|
||||
localStorage.setItem('codeText', codeText);
|
||||
localStorage.setItem('optimazationMode', optimazationMode);
|
||||
localStorage.setItem('optimazationRules', optimazationRules);
|
||||
|
||||
$grammarInfo.html('');
|
||||
$grammarValidation.hide();
|
||||
@ -80,7 +77,7 @@ function parse() {
|
||||
}
|
||||
|
||||
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;
|
||||
if (isValid) {
|
||||
@ -126,7 +123,6 @@ $('#code-info').on('click', 'li', makeOnClickInInfo(code));
|
||||
|
||||
// Event handing in the AST optimazation
|
||||
$('#opt_mode').on('change', setupTimer);
|
||||
$('#opt_rules').on('keyup', setupTimer);
|
||||
|
||||
// Show page
|
||||
$('#main').css({
|
||||
|
@ -53,20 +53,7 @@ void parse_code(const std::string &text, peg::parser &peg, std::string &json,
|
||||
json += "]";
|
||||
}
|
||||
|
||||
inline std::vector<std::string> splitRulesText(const std::string &s) {
|
||||
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 lint(const std::string &grammarText, const std::string &codeText, bool opt_mode) {
|
||||
std::string grammarResult;
|
||||
std::string codeResult;
|
||||
std::string astResult;
|
||||
@ -80,9 +67,8 @@ std::string lint(const std::string &grammarText, const std::string &codeText,
|
||||
parse_code(codeText, peg, codeResult, ast);
|
||||
if (ast) {
|
||||
astResult = escape_json(peg::ast_to_s(ast));
|
||||
auto rules = splitRulesText(opt_rules_text);
|
||||
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];
|
||||
std::shared_ptr<Ast> ast;
|
||||
if (parser.parse(expr, ast)) {
|
||||
ast = AstOptimizer(true).optimize(ast);
|
||||
ast = parser.optimize_ast(ast);
|
||||
std::cout << ast_to_s(ast);
|
||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||
return 0;
|
||||
|
@ -54,7 +54,7 @@ int main(int argc, const char **argv) {
|
||||
auto expr = argv[1];
|
||||
std::shared_ptr<Ast> ast;
|
||||
if (parser.parse(expr, ast)) {
|
||||
ast = AstOptimizer(true).optimize(ast);
|
||||
ast = parser.optimize_ast(ast);
|
||||
std::cout << ast_to_s(ast);
|
||||
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||
return 0;
|
||||
|
@ -35,7 +35,6 @@ inline vector<string> split(const string &s, char delim) {
|
||||
int main(int argc, const char **argv) {
|
||||
auto opt_ast = false;
|
||||
auto opt_optimize = false;
|
||||
auto opt_mode = true;
|
||||
vector<string> opt_rules;
|
||||
auto opt_help = false;
|
||||
auto opt_source = false;
|
||||
@ -50,17 +49,8 @@ int main(int argc, const char **argv) {
|
||||
opt_help = true;
|
||||
} else if (string("--ast") == arg) {
|
||||
opt_ast = true;
|
||||
} else if (string("--opt") == arg || string("--opt-all") == arg) {
|
||||
} else if (string("--opt") == arg) {
|
||||
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) {
|
||||
opt_source = true;
|
||||
if (argi < argc) {
|
||||
@ -190,7 +180,7 @@ int main(int argc, const char **argv) {
|
||||
|
||||
if (ast) {
|
||||
if (opt_optimize) {
|
||||
ast = peg::AstOptimizer(opt_mode, opt_rules).optimize(ast);
|
||||
ast = parser.optimize_ast(ast);
|
||||
}
|
||||
std::cout << peg::ast_to_s(ast);
|
||||
}
|
||||
|
54
peglib.h
54
peglib.h
@ -2287,6 +2287,7 @@ public:
|
||||
bool disable_action = false;
|
||||
|
||||
std::string error_message;
|
||||
bool no_ast_opt = false;
|
||||
|
||||
private:
|
||||
friend class Reference;
|
||||
@ -3058,10 +3059,10 @@ private:
|
||||
~g["COMMA"] <= seq(chr(','), g["Spacing"]);
|
||||
|
||||
// Instruction grammars
|
||||
g["Instruction"] <=
|
||||
seq(g["BeginBlacket"],
|
||||
cho(cho(g["PrecedenceClimbing"]), cho(g["ErrorMessage"])),
|
||||
g["EndBlacket"]);
|
||||
g["Instruction"] <= seq(g["BeginBlacket"],
|
||||
cho(cho(g["PrecedenceClimbing"]),
|
||||
cho(g["ErrorMessage"]), cho(g["NoAstOpt"])),
|
||||
g["EndBlacket"]);
|
||||
|
||||
~g["SpacesZom"] <= zom(g["Space"]);
|
||||
~g["SpacesOom"] <= oom(g["Space"]);
|
||||
@ -3084,6 +3085,9 @@ private:
|
||||
g["ErrorMessage"] <=
|
||||
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
|
||||
for (auto &x : g) {
|
||||
x.second.name = x.first;
|
||||
@ -3403,6 +3407,12 @@ private:
|
||||
instruction.data = std::any_cast<std::string>(vs[0]);
|
||||
return instruction;
|
||||
};
|
||||
|
||||
g["NoAstOpt"] = [](const SemanticValues & /*vs*/) {
|
||||
Instruction instruction;
|
||||
instruction.type = "no_ast_opt";
|
||||
return instruction;
|
||||
};
|
||||
}
|
||||
|
||||
bool apply_precedence_instruction(Definition &rule,
|
||||
@ -3618,6 +3628,8 @@ private:
|
||||
}
|
||||
} else if (instruction.type == "message") {
|
||||
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]; }
|
||||
|
||||
std::vector<std::string> get_rule_names() {
|
||||
std::vector<std::string> get_rule_names() const {
|
||||
std::vector<std::string> rules;
|
||||
rules.reserve(grammar_->size());
|
||||
for (auto const &r : *grammar_) {
|
||||
rules.emplace_back(r.first);
|
||||
for (auto &[name, _] : *grammar_) {
|
||||
rules.push_back(name);
|
||||
}
|
||||
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) {
|
||||
if (grammar_ != nullptr) {
|
||||
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;
|
||||
|
||||
private:
|
||||
@ -4077,6 +4091,14 @@ private:
|
||||
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::string start_;
|
||||
};
|
||||
|
@ -657,7 +657,7 @@ TEST_CASE("Calculator test with AST", "[general]") {
|
||||
|
||||
std::shared_ptr<Ast> 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);
|
||||
|
||||
REQUIRE(ret == true);
|
||||
|
@ -1124,7 +1124,7 @@ CATEGORY <- < [-_a-zA-Z0-9\u0080-\uFFFF ]+ > _
|
||||
ATTRIBUTES <- ATTRIBUTE (',' _ ATTRIBUTE)*
|
||||
ATTRIBUTE <- < [-_a-zA-Z0-9\u0080-\uFFFF]+ > _
|
||||
|
||||
ENTRIES <- (ENTRY (__ ENTRY)*)?
|
||||
ENTRIES <- (ENTRY (__ ENTRY)*)? { no_ast_opt }
|
||||
|
||||
ENTRY <- ONE_WAY PHRASE ('|' _ PHRASE)* !'='
|
||||
/ PHRASE ('|' _ PHRASE)+ !'='
|
||||
@ -1187,7 +1187,7 @@ rrr | sss
|
||||
|
||||
)", ast));
|
||||
|
||||
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
||||
ast = pg.optimize_ast(ast);
|
||||
|
||||
REQUIRE(ast_to_s(ast) ==
|
||||
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],[
|
||||
)", ast));
|
||||
|
||||
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
||||
ast = pg.optimize_ast(ast);
|
||||
|
||||
REQUIRE(ast_to_s(ast) ==
|
||||
R"(+ START
|
||||
@ -1386,7 +1386,7 @@ SkipToRCUR ← (!RCUR (LCUR SkipToRCUR / .))* RCUR
|
||||
}
|
||||
)", ast));
|
||||
|
||||
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
||||
ast = pg.optimize_ast(ast);
|
||||
|
||||
REQUIRE(ast_to_s(ast) ==
|
||||
R"(+ Prog
|
||||
|
Loading…
Reference in New Issue
Block a user