Skip to content

Commit

Permalink
Merge pull request #496 from ydah/improve-trace-rules
Browse files Browse the repository at this point in the history
Add 'only-explicit-rules' option to display explicit grammar rules
  • Loading branch information
ydah authored Jan 12, 2025
2 parents 55f0b20 + 004e4d2 commit 79387f4
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 13 deletions.
8 changes: 8 additions & 0 deletions lib/lrama/grammar/rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 8 additions & 6 deletions lib/lrama/option_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down
15 changes: 12 additions & 3 deletions lib/lrama/trace_reporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:"
Expand Down
7 changes: 5 additions & 2 deletions sig/generated/lrama/trace_reporter.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 9 additions & 2 deletions spec/lrama/option_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
102 changes: 102 additions & 0 deletions spec/lrama/trace_reporter_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 79387f4

Please sign in to comment.