diff --git a/lib/lrama/grammar/rule.rb b/lib/lrama/grammar/rule.rb index 1f55bf8b..445752ae 100644 --- a/lib/lrama/grammar/rule.rb +++ b/lib/lrama/grammar/rule.rb @@ -21,6 +21,14 @@ def ==(other) def display_name l = lhs.id.s_value r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(" ") + "#{l} -> #{r}" + end + + def display_name_without_action + l = lhs.id.s_value + r = empty_rule? ? "ε" : rhs.map do |r| + r.id.s_value if r.first_set.any? + end.compact.join(" ") "#{l} -> #{r}" end diff --git a/lib/lrama/option_parser.rb b/lib/lrama/option_parser.rb index a4b5d580..60da58d7 100644 --- a/lib/lrama/option_parser.rb +++ b/lib/lrama/option_parser.rb @@ -85,6 +85,7 @@ def parse_by_option_parser(argv) o.on_tail ' automaton display states' o.on_tail ' closure display states' o.on_tail ' rules display grammar rules' + o.on_tail ' only-explicit-rules display only explicit grammar rules' o.on_tail ' actions display grammar rules with actions' o.on_tail ' time display generation time' o.on_tail ' all include all the above traces' @@ -135,26 +136,27 @@ def aliased_report_option(opt) VALID_TRACES = %w[ locations scan parse automaton bitsets closure - grammar rules actions resource sets muscles - tools m4-early m4 skeleton time ielr cex + grammar rules only-explicit-rules actions resource + sets muscles tools m4-early m4 skeleton time ielr cex ].freeze NOT_SUPPORTED_TRACES = %w[ locations scan parse bitsets grammar resource sets muscles tools m4-early m4 skeleton ielr cex ].freeze + SUPPORTED_TRACES = VALID_TRACES - NOT_SUPPORTED_TRACES def validate_trace(trace) h = {} return h if trace.empty? || trace == ['none'] - supported = VALID_TRACES - NOT_SUPPORTED_TRACES + all_traces = SUPPORTED_TRACES - %w[only-explicit-rules] if trace == ['all'] - supported.each { |t| h[t.to_sym] = true } + all_traces.each { |t| h[t.gsub(/-/, '_').to_sym] = true } return h end trace.each do |t| - if supported.include?(t) - h[t.to_sym] = true + if SUPPORTED_TRACES.include?(t) + h[t.gsub(/-/, '_').to_sym] = true else raise "Invalid trace option \"#{t}\"." end diff --git a/lib/lrama/trace_reporter.rb b/lib/lrama/trace_reporter.rb index 7a6fd326..bcf1ef1e 100644 --- a/lib/lrama/trace_reporter.rb +++ b/lib/lrama/trace_reporter.rb @@ -15,9 +15,10 @@ def report(**options) private - # @rbs rules: (bool rules, bool actions, **untyped _) -> void - def _report(rules: false, actions: false, **_) - report_rules if rules + # @rbs rules: (bool rules, bool actions, bool only_explicit_rules, **untyped _) -> void + def _report(rules: false, actions: false, only_explicit_rules: false, **_) + report_rules if rules && !only_explicit_rules + report_only_explicit_rules if only_explicit_rules report_actions if actions end @@ -27,6 +28,14 @@ def report_rules @grammar.rules.each { |rule| puts rule.display_name } end + # @rbs () -> void + def report_only_explicit_rules + puts "Grammar rules:" + @grammar.rules.each do |rule| + puts rule.display_name_without_action if rule.lhs.first_set.any? + end + end + # @rbs () -> void def report_actions puts "Grammar rules with actions:" diff --git a/sig/generated/lrama/trace_reporter.rbs b/sig/generated/lrama/trace_reporter.rbs index 2d637df9..94e81527 100644 --- a/sig/generated/lrama/trace_reporter.rbs +++ b/sig/generated/lrama/trace_reporter.rbs @@ -10,12 +10,15 @@ module Lrama private - # @rbs rules: (bool rules, bool actions, **untyped _) -> void - def _report: (?rules: untyped, ?actions: untyped, **untyped _) -> untyped + # @rbs rules: (bool rules, bool actions, bool only_explicit_rules, **untyped _) -> void + def _report: (?rules: untyped, ?actions: untyped, ?only_explicit_rules: untyped, **untyped _) -> untyped # @rbs () -> void def report_rules: () -> void + # @rbs () -> void + def report_only_explicit_rules: () -> void + # @rbs () -> void def report_actions: () -> void end diff --git a/spec/lrama/option_parser_spec.rb b/spec/lrama/option_parser_spec.rb index cb2b6e09..22560dd7 100644 --- a/spec/lrama/option_parser_spec.rb +++ b/spec/lrama/option_parser_spec.rb @@ -85,6 +85,7 @@ automaton display states closure display states rules display grammar rules + only-explicit-rules display only explicit grammar rules actions display grammar rules with actions time display generation time all include all the above traces @@ -169,9 +170,15 @@ end context "when valid options are passed" do + let(:valid_traces) do + %w[automaton closure rules only-explicit-rules actions time] + end + it "returns option hash" do - opts = option_parser.send(:validate_trace, ["automaton", "closure"]) - expect(opts).to eq({automaton: true, closure: true}) + opts = option_parser.send(:validate_trace, valid_traces) + expect(opts).to eq({ + only_explicit_rules: true, actions: true, automaton: true, closure: true, rules: true, time: true + }) end context "when all is passed" do diff --git a/spec/lrama/trace_reporter_spec.rb b/spec/lrama/trace_reporter_spec.rb new file mode 100644 index 00000000..8b3f58ee --- /dev/null +++ b/spec/lrama/trace_reporter_spec.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +RSpec.describe Lrama::TraceReporter do + describe "#report" do + let(:path) { "common/basic.y" } + let(:y) { File.read(fixture_path(path)) } + let(:grammar) do + grammar = Lrama::Parser.new(y, path).parse + grammar.prepare + grammar.validate! + grammar + end + + context "when rules: true and only_explicit_rules: false" do + it "prints the all rules" do + expect do + described_class.new(grammar).report(rules: true, only_explicit_rules: false) + end.to output(<<~RULES).to_stdout + Grammar rules: + $accept -> program EOI + program -> class + program -> '+' strings_1 + program -> '-' strings_2 + class -> keyword_class tSTRING keyword_end + $@1 -> ε + $@2 -> ε + class -> keyword_class $@1 tSTRING '!' keyword_end $@2 + $@3 -> ε + $@4 -> ε + class -> keyword_class $@3 tSTRING '?' keyword_end $@4 + strings_1 -> string_1 + strings_2 -> string_1 + strings_2 -> string_2 + string_1 -> string + string_2 -> string '+' + string -> tSTRING + unused -> tNUMBER + RULES + end + end + + context "when rules: true and only_explicit_rules: true" do + it "prints the only explicit rules" do + expect do + described_class.new(grammar).report(rules: true, only_explicit_rules: true) + end.to output(<<~RULES).to_stdout + Grammar rules: + $accept -> program EOI + program -> class + program -> '+' strings_1 + program -> '-' strings_2 + class -> keyword_class tSTRING keyword_end + class -> keyword_class tSTRING '!' keyword_end + class -> keyword_class tSTRING '?' keyword_end + strings_1 -> string_1 + strings_2 -> string_1 + strings_2 -> string_2 + string_1 -> string + string_2 -> string '+' + string -> tSTRING + unused -> tNUMBER + RULES + end + end + + context "when actions: true" do + it "prints the actions" do + expect do + described_class.new(grammar).report(actions: true) + end.to output(<<~RULES).to_stdout + Grammar rules with actions: + $accept -> program EOI {} + program -> class {} + program -> '+' strings_1 {} + program -> '-' strings_2 {} + class -> keyword_class tSTRING keyword_end { code 1 } + $@1 -> ε { code 2 } + $@2 -> ε { code 3 } + class -> keyword_class $@1 tSTRING '!' keyword_end $@2 {} + $@3 -> ε { code 4 } + $@4 -> ε { code 5 } + class -> keyword_class $@3 tSTRING '?' keyword_end $@4 {} + strings_1 -> string_1 {} + strings_2 -> string_1 {} + strings_2 -> string_2 {} + string_1 -> string {} + string_2 -> string '+' {} + string -> tSTRING {} + unused -> tNUMBER {} + RULES + end + end + + context 'when empty options' do + it 'does not print anything' do + expect do + described_class.new(grammar).report + end.to_not output.to_stdout + end + end + end +end