diff --git a/assets/parser.d.erb b/assets/parser.d.erb index b95c8c3..c44fd22 100644 --- a/assets/parser.d.erb +++ b/assets/parser.d.erb @@ -386,7 +386,7 @@ class <%= @classname %> { Lexer.LexedToken lexed_token; uint token = _TOKEN_COUNT; - StateResult[] states = new StateResult[](1); + StateResult[] stateresults = new StateResult[](1); uint reduced_rule_set = 0xFFFFFFFFu; <%= @grammar.result_type %> reduced_parse_result; for (;;) @@ -399,28 +399,31 @@ class <%= @classname %> uint shift_state = 0xFFFFFFFFu; if (reduced_rule_set != 0xFFFFFFFFu) { - shift_state = check_shift(states[$-1].state, reduced_rule_set); + shift_state = check_shift(stateresults[$-1].state, reduced_rule_set); } if (shift_state == 0xFFFFFFFFu) { - shift_state = check_shift(states[$-1].state, token); + shift_state = check_shift(stateresults[$-1].state, token); + if ((shift_state != 0xFFFFFFFFu) && (token == TOKEN_0EOF)) + { + /* Successful parse. */ + parse_result = stateresults[$-1].result; + return true; + } } if (shift_state != 0xFFFFFFFFu) { - if (token == TOKEN_0EOF) - { - /* Successful parse. */ - parse_result = states[$-1].result; - return true; - } - states ~= StateResult(shift_state); + /* We have something to shift. */ + stateresults ~= StateResult(shift_state); if (reduced_rule_set == 0xFFFFFFFFu) { + /* We shifted a token, mark it consumed. */ token = _TOKEN_COUNT; } else { - states[$-1].result = reduced_parse_result; + /* We shifted a RuleSet. */ + stateresults[$-1].result = reduced_parse_result; <%= @grammar.result_type %> new_parse_result; reduced_parse_result = new_parse_result; reduced_rule_set = 0xFFFFFFFFu; @@ -428,11 +431,13 @@ class <%= @classname %> continue; } - uint reduce_index = check_reduce(states[$-1].state, token); + uint reduce_index = check_reduce(stateresults[$-1].state, token); if (reduce_index != 0xFFFFFFFFu) { + /* We have something to reduce. */ + reduced_parse_result = user_code(reduces[reduce_index].rule, stateresults, reduces[reduce_index].n_states); reduced_rule_set = reduces[reduce_index].rule_set; - states.length -= reduces[reduce_index].n_states; + stateresults.length -= reduces[reduce_index].n_states; continue; } @@ -500,5 +505,31 @@ class <%= @classname %> } return 0xFFFFFFFFu; } + + /** + * Execute user code associated with a parser rule. + * + * @param rule The ID of the rule. + * + * @return Parse result. + */ + private <%= @grammar.result_type %> user_code(uint rule, StateResult[] stateresults, uint n_states) + { + <%= @grammar.result_type %> _result; + + switch (rule) + { +<% @grammar.rules.each do |rule| %> +<% if rule.code %> + case <%= rule.id %>u: { +<%= expand_code(rule.code) %> + } break; +<% end %> +<% end %> + default: break; + } + + return _result; + } } } diff --git a/lib/propane/generator.rb b/lib/propane/generator.rb index 5645999..106aeb0 100644 --- a/lib/propane/generator.rb +++ b/lib/propane/generator.rb @@ -170,6 +170,11 @@ class Propane raise Error.new("Lexer mode '#{mode_name}' not found") end "m_mode = #{mode_id}u" + end.gsub(/\$\$/) do |match| + "_result" + end.gsub(/\$(\d+)/) do |match| + index = $1.to_i + "stateresults[$-1-n_states+#{index}].result" end end diff --git a/spec/propane_spec.rb b/spec/propane_spec.rb index 2e75d96..7a7077a 100644 --- a/spec/propane_spec.rb +++ b/spec/propane_spec.rb @@ -19,6 +19,8 @@ describe Propane do Results = Struct.new(:stdout, :stderr, :status) def run stdout, stderr, status = Open3.capture3("spec/run/testparser") + File.binwrite("spec/run/.stderr", stderr) + File.binwrite("spec/run/.stdout", stdout) Results.new(stdout, stderr, status) end @@ -225,4 +227,29 @@ EOF "pass2", ]) end + + it "executes user code associated with a parser rule" do + write_grammar < A B << + writeln("Start!"); +>> +A -> a << + writeln("A!"); +>> +B -> b << + writeln("B!"); +>> +EOF + build_parser + compile("spec/test_parser_rule_user_code.d") + results = run + expect(results.status).to eq 0 + verify_lines(results.stdout, [ + "A!", + "B!", + "Start!", + ]) + end end diff --git a/spec/test_parser_rule_user_code.d b/spec/test_parser_rule_user_code.d new file mode 100644 index 0000000..5829f74 --- /dev/null +++ b/spec/test_parser_rule_user_code.d @@ -0,0 +1,14 @@ +import testparser; +import std.stdio; + +int main() +{ + return 0; +} + +unittest +{ + string input = "ab"; + auto parser = new Testparser.Parser(cast(const(ubyte) *)input.ptr, input.length); + assert(parser.parse() == true); +}