From 138a15147f75e5a0afe88d3cd177b308f4da6489 Mon Sep 17 00:00:00 2001 From: ydah Date: Mon, 10 Jun 2024 01:40:50 +0900 Subject: [PATCH] Add support `--report=rules` option This PR, add an option to output the following report when there are unused rules. ``` 1 Unused Rules 0 unused ``` --- lib/lrama/option_parser.rb | 2 +- lib/lrama/states_reporter.rb | 21 ++++++++++++++++++--- spec/fixtures/common/basic.y | 3 +++ spec/lrama/lexer_spec.rb | 4 ++++ spec/lrama/option_parser_spec.rb | 2 +- spec/lrama/parser_spec.rb | 19 +++++++++++++++++++ spec/lrama/states_spec.rb | 9 ++++++++- 7 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/lrama/option_parser.rb b/lib/lrama/option_parser.rb index 07455ce1..fc245f76 100644 --- a/lib/lrama/option_parser.rb +++ b/lib/lrama/option_parser.rb @@ -92,7 +92,7 @@ def parse_by_option_parser(argv) end BISON_REPORTS = %w[states itemsets lookaheads solved counterexamples cex all none] - OTHER_REPORTS = %w[terms verbose] + OTHER_REPORTS = %w[rules terms verbose] NOT_SUPPORTED_REPORTS = %w[cex none] VALID_REPORTS = BISON_REPORTS + OTHER_REPORTS - NOT_SUPPORTED_REPORTS diff --git a/lib/lrama/states_reporter.rb b/lib/lrama/states_reporter.rb index d05c48c9..eb780711 100644 --- a/lib/lrama/states_reporter.rb +++ b/lib/lrama/states_reporter.rb @@ -16,9 +16,8 @@ def report(io, **options) private - def _report(io, grammar: false, terms: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false) - # TODO: Unused rules - + def _report(io, grammar: false, rules: false, terms: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false) + report_unused_rules(io) if rules report_unused_terms(io) if terms report_conflicts(io) report_grammar(io) if grammar @@ -63,6 +62,22 @@ def report_unused_terms(io) end end + def report_unused_rules(io) + used_rules = @states.rules.flat_map(&:rhs) + + unused_rules = @states.rules.map(&:lhs).select do |rule| + !used_rules.include?(rule) && rule.token_id != 0 + end + + unless unused_rules.empty? + io << "#{unused_rules.count} Unused Rules\n\n" + unused_rules.each_with_index do |rule, index| + io << sprintf("%5d %s\n", index, rule.display_name) + end + io << "\n\n" + end + end + def report_conflicts(io) has_conflict = false diff --git a/spec/fixtures/common/basic.y b/spec/fixtures/common/basic.y index 95abf58a..c38b7b10 100644 --- a/spec/fixtures/common/basic.y +++ b/spec/fixtures/common/basic.y @@ -81,6 +81,9 @@ string_2: string '+' string: tSTRING ; +unused: tNUMBER + ; + %% // Epilogue diff --git a/spec/lrama/lexer_spec.rb b/spec/lrama/lexer_spec.rb index 71ee0d7e..f0037567 100644 --- a/spec/lrama/lexer_spec.rb +++ b/spec/lrama/lexer_spec.rb @@ -257,6 +257,10 @@ expect(lexer.next_token).to eq([':', ':']) expect(lexer.next_token).to eq([:IDENTIFIER, token_class::Ident.new(s_value: 'tSTRING')]) expect(lexer.next_token).to eq([';', ';']) + expect(lexer.next_token).to eq([:IDENT_COLON, token_class::Ident.new(s_value: 'unused')]) + expect(lexer.next_token).to eq([':', ':']) + expect(lexer.next_token).to eq([:IDENTIFIER, token_class::Ident.new(s_value: 'tNUMBER')]) + expect(lexer.next_token).to eq([';', ';']) expect(lexer.next_token).to eq(['%%', '%%']) expect(lexer.next_token).to eq(nil) end diff --git a/spec/lrama/option_parser_spec.rb b/spec/lrama/option_parser_spec.rb index abc877e3..9fb294a7 100644 --- a/spec/lrama/option_parser_spec.rb +++ b/spec/lrama/option_parser_spec.rb @@ -68,7 +68,7 @@ -h, --help display this help and exit Valid Reports: - states itemsets lookaheads solved counterexamples all terms verbose + states itemsets lookaheads solved counterexamples all rules terms verbose Valid Traces: none locations scan parse automaton bitsets closure grammar rules actions resource sets muscles tools m4-early m4 skeleton time ielr cex all diff --git a/spec/lrama/parser_spec.rb b/spec/lrama/parser_spec.rb index 0495eb53..3e8ff6a6 100644 --- a/spec/lrama/parser_spec.rb +++ b/spec/lrama/parser_spec.rb @@ -107,6 +107,7 @@ Sym.new(id: T::Ident.new(s_value: "string_1"), alias_name: nil, number: 28, tag: nil, term: false, token_id: 9, nullable: false, precedence: nil, printer: nil), Sym.new(id: T::Ident.new(s_value: "string_2"), alias_name: nil, number: 29, tag: nil, term: false, token_id: 10, nullable: false, precedence: nil, printer: nil), Sym.new(id: T::Ident.new(s_value: "string"), alias_name: nil, number: 30, tag: nil, term: false, token_id: 11, nullable: false, precedence: nil, printer: nil), + Sym.new(id: T::Ident.new(s_value: "unused"), alias_name: nil, number: 31, tag: nil, term: false, token_id: 12, nullable: false, precedence: nil, printer: nil), ]) expect(grammar.types).to eq([Type.new(id: T::Ident.new(s_value: "class"), tag: T::Tag.new(s_value: ""))]) _rules = grammar.rule_builders.map {|b| [b.lhs, (b.rhs + [b.precedence_sym, b.user_code]).compact, b.line] } @@ -214,6 +215,13 @@ ], 81, ], + [ + T::Ident.new(s_value: "unused"), + [ + T::Ident.new(s_value: "tNUMBER") + ], + 84, + ], ]) expect(grammar.rules).to eq([ Rule.new( @@ -415,6 +423,17 @@ precedence_sym: grammar.find_symbol_by_s_value!("tSTRING"), lineno: 81, ), + Rule.new( + id: 17, + lhs: grammar.find_symbol_by_s_value!("unused"), + rhs: [ + grammar.find_symbol_by_s_value!("tNUMBER"), + ], + token_code: nil, + nullable: false, + precedence_sym: grammar.find_symbol_by_s_value!("tNUMBER"), + lineno: 84, + ), ]) end diff --git a/spec/lrama/states_spec.rb b/spec/lrama/states_spec.rb index 6b924e69..c43c7f03 100644 --- a/spec/lrama/states_spec.rb +++ b/spec/lrama/states_spec.rb @@ -15,9 +15,14 @@ states.compute io = StringIO.new - states.reporter.report(io, grammar: true, terms: true, states: true, itemsets: true, lookaheads: true) + states.reporter.report(io, grammar: true, rules: true, terms: true, states: true, itemsets: true, lookaheads: true) expect(io.string).to eq(<<~STR) + 1 Unused Rules + + 0 unused + + Unused Terms 0 YYerror @@ -69,6 +74,8 @@ 16 string: tSTRING + 17 unused: tNUMBER + State 0