This commit is contained in:
yhirose 2021-01-21 20:56:05 -05:00
parent d4bcc53178
commit 80b20a091f
12 changed files with 62 additions and 64 deletions

View File

@ -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).

View File

@ -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&nbsp;&nbsp;&nbsp;&nbsp;
mode:&nbsp;<select id="opt_mode" type="checkbox"><option value="all">All</option><option value="only">Only</option></select>&nbsp;&nbsp;
rules:&nbsp;<input id="opt_rules" type="text" size="40" placeholder="Enter definition rules separated by comma."></input>
mode:&nbsp;<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>

View File

@ -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({

View File

@ -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

Binary file not shown.

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -2287,6 +2287,7 @@ public:
bool disable_action = false;
std::string error_message;
bool no_ast_opt = false;
private:
friend class Reference;
@ -3058,9 +3059,9 @@ private:
~g["COMMA"] <= seq(chr(','), g["Spacing"]);
// Instruction grammars
g["Instruction"] <=
seq(g["BeginBlacket"],
cho(cho(g["PrecedenceClimbing"]), cho(g["ErrorMessage"])),
g["Instruction"] <= seq(g["BeginBlacket"],
cho(cho(g["PrecedenceClimbing"]),
cho(g["ErrorMessage"]), cho(g["NoAstOpt"])),
g["EndBlacket"]);
~g["SpacesZom"] <= zom(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_;
};

View File

@ -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);

View File

@ -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