Lrama v0.5.4
This commit is contained in:
parent
d26b015e83
commit
8c447cffe4
Notes:
git
2023-08-17 10:29:55 +00:00
@ -1,6 +1,7 @@
|
|||||||
require "lrama/bitmap"
|
require "lrama/bitmap"
|
||||||
require "lrama/command"
|
require "lrama/command"
|
||||||
require "lrama/context"
|
require "lrama/context"
|
||||||
|
require "lrama/counterexamples"
|
||||||
require "lrama/digraph"
|
require "lrama/digraph"
|
||||||
require "lrama/grammar"
|
require "lrama/grammar"
|
||||||
require "lrama/lexer"
|
require "lrama/lexer"
|
||||||
@ -10,5 +11,6 @@ require "lrama/report"
|
|||||||
require "lrama/state"
|
require "lrama/state"
|
||||||
require "lrama/states"
|
require "lrama/states"
|
||||||
require "lrama/states_reporter"
|
require "lrama/states_reporter"
|
||||||
|
require "lrama/type"
|
||||||
require "lrama/version"
|
require "lrama/version"
|
||||||
require "lrama/warning"
|
require "lrama/warning"
|
||||||
|
@ -67,7 +67,7 @@ module Lrama
|
|||||||
bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
|
bison_list = %w[states itemsets lookaheads solved counterexamples cex all none]
|
||||||
others = %w[verbose]
|
others = %w[verbose]
|
||||||
list = bison_list + others
|
list = bison_list + others
|
||||||
not_supported = %w[counterexamples cex none]
|
not_supported = %w[cex none]
|
||||||
h = { grammar: true }
|
h = { grammar: true }
|
||||||
|
|
||||||
report.each do |r|
|
report.each do |r|
|
||||||
@ -121,13 +121,13 @@ module Lrama
|
|||||||
# Output Files:
|
# Output Files:
|
||||||
opt.on('-h', '--header=[FILE]') {|v| @header = true; @header_file = v }
|
opt.on('-h', '--header=[FILE]') {|v| @header = true; @header_file = v }
|
||||||
opt.on('-d') { @header = true }
|
opt.on('-d') { @header = true }
|
||||||
opt.on('-r', '--report=THINGS') {|v| @report = v.split(',') }
|
opt.on('-r', '--report=THINGS', Array) {|v| @report = v }
|
||||||
opt.on('--report-file=FILE') {|v| @report_file = v }
|
opt.on('--report-file=FILE') {|v| @report_file = v }
|
||||||
opt.on('-v') { } # Do nothing
|
opt.on('-v') { } # Do nothing
|
||||||
opt.on('-o', '--output=FILE') {|v| @outfile = v }
|
opt.on('-o', '--output=FILE') {|v| @outfile = v }
|
||||||
|
|
||||||
# Hidden
|
# Hidden
|
||||||
opt.on('--trace=THINGS') {|v| @trace = v.split(',') }
|
opt.on('--trace=THINGS', Array) {|v| @trace = v }
|
||||||
|
|
||||||
# Error Recovery
|
# Error Recovery
|
||||||
opt.on('-e') {|v| @error_recovery = true }
|
opt.on('-e') {|v| @error_recovery = true }
|
||||||
|
285
tool/lrama/lib/lrama/counterexamples.rb
Normal file
285
tool/lrama/lib/lrama/counterexamples.rb
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
require "set"
|
||||||
|
|
||||||
|
require "lrama/counterexamples/derivation"
|
||||||
|
require "lrama/counterexamples/example"
|
||||||
|
require "lrama/counterexamples/path"
|
||||||
|
require "lrama/counterexamples/state_item"
|
||||||
|
require "lrama/counterexamples/triple"
|
||||||
|
|
||||||
|
module Lrama
|
||||||
|
# See: https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf
|
||||||
|
# 4. Constructing Nonunifying Counterexamples
|
||||||
|
class Counterexamples
|
||||||
|
attr_reader :transitions, :productions
|
||||||
|
|
||||||
|
def initialize(states)
|
||||||
|
@states = states
|
||||||
|
setup_transitions
|
||||||
|
setup_productions
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#<Counterexamples>"
|
||||||
|
end
|
||||||
|
alias :inspect :to_s
|
||||||
|
|
||||||
|
def compute(conflict_state)
|
||||||
|
conflict_state.conflicts.flat_map do |conflict|
|
||||||
|
case conflict.type
|
||||||
|
when :shift_reduce
|
||||||
|
shift_reduce_example(conflict_state, conflict)
|
||||||
|
when :reduce_reduce
|
||||||
|
reduce_reduce_examples(conflict_state, conflict)
|
||||||
|
end
|
||||||
|
end.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def setup_transitions
|
||||||
|
# Hash [StateItem, Symbol] => StateItem
|
||||||
|
@transitions = {}
|
||||||
|
# Hash [StateItem, Symbol] => Set(StateItem)
|
||||||
|
@reverse_transitions = {}
|
||||||
|
|
||||||
|
@states.states.each do |src_state|
|
||||||
|
trans = {}
|
||||||
|
|
||||||
|
src_state.transitions.each do |shift, next_state|
|
||||||
|
trans[shift.next_sym] = next_state
|
||||||
|
end
|
||||||
|
|
||||||
|
src_state.items.each do |src_item|
|
||||||
|
next if src_item.end_of_rule?
|
||||||
|
sym = src_item.next_sym
|
||||||
|
dest_state = trans[sym]
|
||||||
|
|
||||||
|
dest_state.kernels.each do |dest_item|
|
||||||
|
next unless (src_item.rule == dest_item.rule) && (src_item.position + 1 == dest_item.position)
|
||||||
|
src_state_item = StateItem.new(src_state, src_item)
|
||||||
|
dest_state_item = StateItem.new(dest_state, dest_item)
|
||||||
|
|
||||||
|
@transitions[[src_state_item, sym]] = dest_state_item
|
||||||
|
|
||||||
|
key = [dest_state_item, sym]
|
||||||
|
@reverse_transitions[key] ||= Set.new
|
||||||
|
@reverse_transitions[key] << src_state_item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup_productions
|
||||||
|
# Hash [StateItem] => Set(Item)
|
||||||
|
@productions = {}
|
||||||
|
# Hash [State, Symbol] => Set(Item). Symbol is nterm
|
||||||
|
@reverse_productions = {}
|
||||||
|
|
||||||
|
@states.states.each do |state|
|
||||||
|
# LHS => Set(Item)
|
||||||
|
h = {}
|
||||||
|
|
||||||
|
state.closure.each do |item|
|
||||||
|
sym = item.lhs
|
||||||
|
|
||||||
|
h[sym] ||= Set.new
|
||||||
|
h[sym] << item
|
||||||
|
end
|
||||||
|
|
||||||
|
state.items.each do |item|
|
||||||
|
next if item.end_of_rule?
|
||||||
|
next if item.next_sym.term?
|
||||||
|
|
||||||
|
sym = item.next_sym
|
||||||
|
state_item = StateItem.new(state, item)
|
||||||
|
key = [state, sym]
|
||||||
|
|
||||||
|
@productions[state_item] = h[sym]
|
||||||
|
|
||||||
|
@reverse_productions[key] ||= Set.new
|
||||||
|
@reverse_productions[key] << item
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def shift_reduce_example(conflict_state, conflict)
|
||||||
|
conflict_symbol = conflict.symbols.first
|
||||||
|
shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
|
||||||
|
path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
|
||||||
|
path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
|
||||||
|
|
||||||
|
Example.new(path1, path2, conflict, conflict_symbol, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reduce_reduce_examples(conflict_state, conflict)
|
||||||
|
conflict_symbol = conflict.symbols.first
|
||||||
|
path1 = shortest_path(conflict_state, conflict.reduce1.item, conflict_symbol)
|
||||||
|
path2 = shortest_path(conflict_state, conflict.reduce2.item, conflict_symbol)
|
||||||
|
|
||||||
|
Example.new(path1, path2, conflict, conflict_symbol, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_shift_conflict_shortest_path(reduce_path, conflict_state, conflict_item)
|
||||||
|
state_items = find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
|
||||||
|
build_paths_from_state_items(state_items)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
|
||||||
|
target_state_item = StateItem.new(conflict_state, conflict_item)
|
||||||
|
result = [target_state_item]
|
||||||
|
reversed_reduce_path = reduce_path.to_a.reverse
|
||||||
|
# Index for state_item
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
while (path = reversed_reduce_path[i])
|
||||||
|
# Index for prev_state_item
|
||||||
|
j = i + 1
|
||||||
|
_j = j
|
||||||
|
|
||||||
|
while (prev_path = reversed_reduce_path[j])
|
||||||
|
if prev_path.production?
|
||||||
|
j += 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
state_item = path.to
|
||||||
|
prev_state_item = prev_path&.to
|
||||||
|
|
||||||
|
if target_state_item == state_item || target_state_item.item.start_item?
|
||||||
|
result.concat(reversed_reduce_path[_j..-1].map(&:to))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if target_state_item.item.beginning_of_rule?
|
||||||
|
queue = []
|
||||||
|
queue << [target_state_item]
|
||||||
|
|
||||||
|
# Find reverse production
|
||||||
|
while (sis = queue.shift)
|
||||||
|
si = sis.last
|
||||||
|
|
||||||
|
# Reach to start state
|
||||||
|
if si.item.start_item?
|
||||||
|
sis.shift
|
||||||
|
result.concat(sis)
|
||||||
|
target_state_item = si
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if !si.item.beginning_of_rule?
|
||||||
|
key = [si, si.item.previous_sym]
|
||||||
|
@reverse_transitions[key].each do |prev_target_state_item|
|
||||||
|
next if prev_target_state_item.state != prev_state_item.state
|
||||||
|
sis.shift
|
||||||
|
result.concat(sis)
|
||||||
|
result << prev_target_state_item
|
||||||
|
target_state_item = prev_target_state_item
|
||||||
|
i = j
|
||||||
|
queue.clear
|
||||||
|
break
|
||||||
|
end
|
||||||
|
else
|
||||||
|
key = [si.state, si.item.lhs]
|
||||||
|
@reverse_productions[key].each do |item|
|
||||||
|
state_item = StateItem.new(si.state, item)
|
||||||
|
queue << (sis + [state_item])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Find reverse transition
|
||||||
|
key = [target_state_item, target_state_item.item.previous_sym]
|
||||||
|
@reverse_transitions[key].each do |prev_target_state_item|
|
||||||
|
next if prev_target_state_item.state != prev_state_item.state
|
||||||
|
result << prev_target_state_item
|
||||||
|
target_state_item = prev_target_state_item
|
||||||
|
i = j
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result.reverse
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_paths_from_state_items(state_items)
|
||||||
|
paths = state_items.zip([nil] + state_items).map do |si, prev_si|
|
||||||
|
case
|
||||||
|
when prev_si.nil?
|
||||||
|
StartPath.new(si)
|
||||||
|
when si.item.beginning_of_rule?
|
||||||
|
ProductionPath.new(prev_si, si)
|
||||||
|
else
|
||||||
|
TransitionPath.new(prev_si, si)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
paths
|
||||||
|
end
|
||||||
|
|
||||||
|
def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
|
||||||
|
# queue: is an array of [Triple, [Path]]
|
||||||
|
queue = []
|
||||||
|
visited = {}
|
||||||
|
start_state = @states.states.first
|
||||||
|
raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
|
||||||
|
|
||||||
|
start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
|
||||||
|
|
||||||
|
queue << [start, [StartPath.new(start.state_item)]]
|
||||||
|
|
||||||
|
while true
|
||||||
|
triple, paths = queue.shift
|
||||||
|
|
||||||
|
next if visited[triple]
|
||||||
|
visited[triple] = true
|
||||||
|
|
||||||
|
# Found
|
||||||
|
if triple.state == conflict_state && triple.item == conflict_reduce_item && triple.l.include?(conflict_term)
|
||||||
|
return paths
|
||||||
|
end
|
||||||
|
|
||||||
|
# transition
|
||||||
|
triple.state.transitions.each do |shift, next_state|
|
||||||
|
next unless triple.item.next_sym && triple.item.next_sym == shift.next_sym
|
||||||
|
next_state.kernels.each do |kernel|
|
||||||
|
next if kernel.rule != triple.item.rule
|
||||||
|
t = Triple.new(next_state, kernel, triple.l)
|
||||||
|
queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# production step
|
||||||
|
triple.state.closure.each do |item|
|
||||||
|
next unless triple.item.next_sym && triple.item.next_sym == item.lhs
|
||||||
|
l = follow_l(triple.item, triple.l)
|
||||||
|
t = Triple.new(triple.state, item, l)
|
||||||
|
queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
|
||||||
|
end
|
||||||
|
|
||||||
|
break if queue.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow_l(item, current_l)
|
||||||
|
# 1. follow_L (A -> X1 ... Xn-1 • Xn) = L
|
||||||
|
# 2. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = {Xk+2} if Xk+2 is a terminal
|
||||||
|
# 3. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) if Xk+2 is a nonnullable nonterminal
|
||||||
|
# 4. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) + follow_L (A -> X1 ... Xk+1 • Xk+2 ... Xn) if Xk+2 is a nullable nonterminal
|
||||||
|
case
|
||||||
|
when item.number_of_rest_symbols == 1
|
||||||
|
current_l
|
||||||
|
when item.next_next_sym.term?
|
||||||
|
Set.new([item.next_next_sym])
|
||||||
|
when !item.next_next_sym.nullable
|
||||||
|
item.next_next_sym.first_set
|
||||||
|
else
|
||||||
|
item.next_next_sym.first_set + follow_l(item.new_by_next_position, current_l)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
63
tool/lrama/lib/lrama/counterexamples/derivation.rb
Normal file
63
tool/lrama/lib/lrama/counterexamples/derivation.rb
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
module Lrama
|
||||||
|
class Counterexamples
|
||||||
|
class Derivation
|
||||||
|
attr_reader :item, :left, :right
|
||||||
|
attr_writer :right
|
||||||
|
|
||||||
|
def initialize(item, left, right = nil)
|
||||||
|
@item = item
|
||||||
|
@left = left
|
||||||
|
@right = right
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#<Derivation(#{item.display_name})>"
|
||||||
|
end
|
||||||
|
alias :inspect :to_s
|
||||||
|
|
||||||
|
def render_strings_for_report
|
||||||
|
result = []
|
||||||
|
_render_for_report(self, 0, result, 0)
|
||||||
|
result.map(&:rstrip)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_for_report
|
||||||
|
render_strings_for_report.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def _render_for_report(derivation, offset, strings, index)
|
||||||
|
item = derivation.item
|
||||||
|
if strings[index]
|
||||||
|
strings[index] << " " * (offset - strings[index].length)
|
||||||
|
else
|
||||||
|
strings[index] = " " * offset
|
||||||
|
end
|
||||||
|
str = strings[index]
|
||||||
|
str << "#{item.rule_id}: #{item.symbols_before_dot.map(&:display_name).join(" ")} "
|
||||||
|
|
||||||
|
if derivation.left
|
||||||
|
len = str.length
|
||||||
|
str << "#{item.next_sym.display_name}"
|
||||||
|
length = _render_for_report(derivation.left, len, strings, index + 1)
|
||||||
|
# I want String#ljust!
|
||||||
|
str << " " * (length - str.length)
|
||||||
|
else
|
||||||
|
str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} "
|
||||||
|
return str.length
|
||||||
|
end
|
||||||
|
|
||||||
|
if derivation.right&.left
|
||||||
|
length = _render_for_report(derivation.right.left, str.length, strings, index + 1)
|
||||||
|
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
|
||||||
|
str << " " * (length - str.length) if length > str.length
|
||||||
|
elsif item.next_next_sym
|
||||||
|
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
|
||||||
|
end
|
||||||
|
|
||||||
|
return str.length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
124
tool/lrama/lib/lrama/counterexamples/example.rb
Normal file
124
tool/lrama/lib/lrama/counterexamples/example.rb
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
module Lrama
|
||||||
|
class Counterexamples
|
||||||
|
class Example
|
||||||
|
attr_reader :path1, :path2, :conflict, :conflict_symbol
|
||||||
|
|
||||||
|
# path1 is shift conflict when S/R conflict
|
||||||
|
# path2 is always reduce conflict
|
||||||
|
def initialize(path1, path2, conflict, conflict_symbol, counterexamples)
|
||||||
|
@path1 = path1
|
||||||
|
@path2 = path2
|
||||||
|
@conflict = conflict
|
||||||
|
@conflict_symbol = conflict_symbol
|
||||||
|
@counterexamples = counterexamples
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
@conflict.type
|
||||||
|
end
|
||||||
|
|
||||||
|
def path1_item
|
||||||
|
@path1.last.to.item
|
||||||
|
end
|
||||||
|
|
||||||
|
def path2_item
|
||||||
|
@path2.last.to.item
|
||||||
|
end
|
||||||
|
|
||||||
|
def derivations1
|
||||||
|
@derivations1 ||= _derivations(path1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def derivations2
|
||||||
|
@derivations2 ||= _derivations(path2)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def _derivations(paths)
|
||||||
|
derivation = nil
|
||||||
|
current = :production
|
||||||
|
lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil
|
||||||
|
|
||||||
|
paths.reverse.each do |path|
|
||||||
|
item = path.to.item
|
||||||
|
|
||||||
|
case current
|
||||||
|
when :production
|
||||||
|
case path
|
||||||
|
when StartPath
|
||||||
|
derivation = Derivation.new(item, derivation)
|
||||||
|
current = :start
|
||||||
|
when TransitionPath
|
||||||
|
derivation = Derivation.new(item, derivation)
|
||||||
|
current = :transition
|
||||||
|
when ProductionPath
|
||||||
|
derivation = Derivation.new(item, derivation)
|
||||||
|
current = :production
|
||||||
|
end
|
||||||
|
|
||||||
|
if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym)
|
||||||
|
state_item = @counterexamples.transitions[[path.to, item.next_sym]]
|
||||||
|
derivation2 = find_derivation_for_symbol(state_item, lookahead_sym)
|
||||||
|
derivation.right = derivation2
|
||||||
|
lookahead_sym = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
when :transition
|
||||||
|
case path
|
||||||
|
when StartPath
|
||||||
|
derivation = Derivation.new(item, derivation)
|
||||||
|
current = :start
|
||||||
|
when TransitionPath
|
||||||
|
# ignore
|
||||||
|
current = :transition
|
||||||
|
when ProductionPath
|
||||||
|
# ignore
|
||||||
|
current = :production
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "BUG: Unknown #{current}"
|
||||||
|
end
|
||||||
|
|
||||||
|
break if current == :start
|
||||||
|
end
|
||||||
|
|
||||||
|
derivation
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_derivation_for_symbol(state_item, sym)
|
||||||
|
queue = []
|
||||||
|
queue << [state_item]
|
||||||
|
|
||||||
|
while (sis = queue.shift)
|
||||||
|
si = sis.last
|
||||||
|
next_sym = si.item.next_sym
|
||||||
|
|
||||||
|
if next_sym == sym
|
||||||
|
derivation = nil
|
||||||
|
|
||||||
|
sis.reverse.each do |si|
|
||||||
|
derivation = Derivation.new(si.item, derivation)
|
||||||
|
end
|
||||||
|
|
||||||
|
return derivation
|
||||||
|
end
|
||||||
|
|
||||||
|
if next_sym.nterm? && next_sym.first_set.include?(sym)
|
||||||
|
@counterexamples.productions[si].each do |next_item|
|
||||||
|
next if next_item.empty_rule?
|
||||||
|
next_si = StateItem.new(si.state, next_item)
|
||||||
|
next if sis.include?(next_si)
|
||||||
|
queue << (sis + [next_si])
|
||||||
|
end
|
||||||
|
|
||||||
|
if next_sym.nullable
|
||||||
|
next_si = @counterexamples.transitions[[si, next_sym]]
|
||||||
|
queue << (sis + [next_si])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
69
tool/lrama/lib/lrama/counterexamples/path.rb
Normal file
69
tool/lrama/lib/lrama/counterexamples/path.rb
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
module Lrama
|
||||||
|
class Counterexamples
|
||||||
|
class Path
|
||||||
|
def initialize(from_state_item, to_state_item)
|
||||||
|
@from_state_item = from_state_item
|
||||||
|
@to_state_item = to_state_item
|
||||||
|
end
|
||||||
|
|
||||||
|
def from
|
||||||
|
@from_state_item
|
||||||
|
end
|
||||||
|
|
||||||
|
def to
|
||||||
|
@to_state_item
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#<Path(#{type})>"
|
||||||
|
end
|
||||||
|
alias :inspect :to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
class StartPath < Path
|
||||||
|
def initialize(to_state_item)
|
||||||
|
super nil, to_state_item
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
:start
|
||||||
|
end
|
||||||
|
|
||||||
|
def transition?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def production?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class TransitionPath < Path
|
||||||
|
def type
|
||||||
|
:transition
|
||||||
|
end
|
||||||
|
|
||||||
|
def transition?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def production?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ProductionPath < Path
|
||||||
|
def type
|
||||||
|
:production
|
||||||
|
end
|
||||||
|
|
||||||
|
def transition?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def production?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
6
tool/lrama/lib/lrama/counterexamples/state_item.rb
Normal file
6
tool/lrama/lib/lrama/counterexamples/state_item.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Lrama
|
||||||
|
class Counterexamples
|
||||||
|
class StateItem < Struct.new(:state, :item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
tool/lrama/lib/lrama/counterexamples/triple.rb
Normal file
21
tool/lrama/lib/lrama/counterexamples/triple.rb
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
module Lrama
|
||||||
|
class Counterexamples
|
||||||
|
# s: state
|
||||||
|
# itm: item within s
|
||||||
|
# l: precise lookahead set
|
||||||
|
class Triple < Struct.new(:s, :itm, :l)
|
||||||
|
alias :state :s
|
||||||
|
alias :item :itm
|
||||||
|
alias :precise_lookahead_set :l
|
||||||
|
|
||||||
|
def state_item
|
||||||
|
StateItem.new(state, item)
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
"#{state.inspect}. #{item.display_name}. #{l.map(&:id).map(&:s_value)}"
|
||||||
|
end
|
||||||
|
alias :to_s :inspect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -33,7 +33,7 @@ module Lrama
|
|||||||
@h[x] = d
|
@h[x] = d
|
||||||
@result[x] = @base_function[x] # F x = F' x
|
@result[x] = @base_function[x] # F x = F' x
|
||||||
|
|
||||||
@relation[x] && @relation[x].each do |y|
|
@relation[x]&.each do |y|
|
||||||
traverse(y) if @h[y] == 0
|
traverse(y) if @h[y] == 0
|
||||||
@h[x] = [@h[x], @h[y]].min
|
@h[x] = [@h[x], @h[y]].min
|
||||||
@result[x] |= @result[y] # F x = F x + F y
|
@result[x] |= @result[y] # F x = F x + F y
|
||||||
@ -43,9 +43,8 @@ module Lrama
|
|||||||
while true do
|
while true do
|
||||||
z = @stack.pop
|
z = @stack.pop
|
||||||
@h[z] = Float::INFINITY
|
@h[z] = Float::INFINITY
|
||||||
@result[z] = @result[x] # F (Top of S) = F x
|
|
||||||
|
|
||||||
break if z == x
|
break if z == x
|
||||||
|
@result[z] = @result[x] # F (Top of S) = F x
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
require "lrama/grammar/auxiliary"
|
||||||
require "lrama/grammar/code"
|
require "lrama/grammar/code"
|
||||||
require "lrama/grammar/error_token"
|
require "lrama/grammar/error_token"
|
||||||
require "lrama/grammar/precedence"
|
require "lrama/grammar/precedence"
|
||||||
@ -7,16 +8,13 @@ require "lrama/grammar/rule"
|
|||||||
require "lrama/grammar/symbol"
|
require "lrama/grammar/symbol"
|
||||||
require "lrama/grammar/union"
|
require "lrama/grammar/union"
|
||||||
require "lrama/lexer"
|
require "lrama/lexer"
|
||||||
|
require "lrama/type"
|
||||||
|
|
||||||
module Lrama
|
module Lrama
|
||||||
Type = Struct.new(:id, :tag, keyword_init: true)
|
|
||||||
Token = Lrama::Lexer::Token
|
Token = Lrama::Lexer::Token
|
||||||
|
|
||||||
# Grammar is the result of parsing an input grammar file
|
# Grammar is the result of parsing an input grammar file
|
||||||
class Grammar
|
class Grammar
|
||||||
# Grammar file information not used by States but by Output
|
|
||||||
Aux = Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true)
|
|
||||||
|
|
||||||
attr_reader :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux
|
attr_reader :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux
|
||||||
attr_accessor :union, :expect,
|
attr_accessor :union, :expect,
|
||||||
:printers, :error_tokens,
|
:printers, :error_tokens,
|
||||||
@ -38,7 +36,7 @@ module Lrama
|
|||||||
@error_symbol = nil
|
@error_symbol = nil
|
||||||
@undef_symbol = nil
|
@undef_symbol = nil
|
||||||
@accept_symbol = nil
|
@accept_symbol = nil
|
||||||
@aux = Aux.new
|
@aux = Auxiliary.new
|
||||||
|
|
||||||
append_special_symbols
|
append_special_symbols
|
||||||
end
|
end
|
||||||
@ -48,7 +46,7 @@ module Lrama
|
|||||||
end
|
end
|
||||||
|
|
||||||
def add_error_token(ident_or_tags:, code:, lineno:)
|
def add_error_token(ident_or_tags:, code:, lineno:)
|
||||||
@error_tokens << ErrorToken.new(ident_or_tags, code, lineno)
|
@error_tokens << ErrorToken.new(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_term(id:, alias_name: nil, tag: nil, token_id: nil, replace: false)
|
def add_term(id:, alias_name: nil, tag: nil, token_id: nil, replace: false)
|
||||||
@ -215,6 +213,41 @@ module Lrama
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def compute_first_set
|
||||||
|
terms.each do |term|
|
||||||
|
term.first_set = Set.new([term]).freeze
|
||||||
|
term.first_set_bitmap = Lrama::Bitmap.from_array([term.number])
|
||||||
|
end
|
||||||
|
|
||||||
|
nterms.each do |nterm|
|
||||||
|
nterm.first_set = Set.new([]).freeze
|
||||||
|
nterm.first_set_bitmap = Lrama::Bitmap.from_array([])
|
||||||
|
end
|
||||||
|
|
||||||
|
while true do
|
||||||
|
changed = false
|
||||||
|
|
||||||
|
@rules.each do |rule|
|
||||||
|
rule.rhs.each do |r|
|
||||||
|
if rule.lhs.first_set_bitmap | r.first_set_bitmap != rule.lhs.first_set_bitmap
|
||||||
|
changed = true
|
||||||
|
rule.lhs.first_set_bitmap = rule.lhs.first_set_bitmap | r.first_set_bitmap
|
||||||
|
end
|
||||||
|
|
||||||
|
break unless r.nullable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
break unless changed
|
||||||
|
end
|
||||||
|
|
||||||
|
nterms.each do |nterm|
|
||||||
|
nterm.first_set = Lrama::Bitmap.to_array(nterm.first_set_bitmap).map do |number|
|
||||||
|
find_symbol_by_number!(number)
|
||||||
|
end.to_set
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def find_symbol_by_s_value(s_value)
|
def find_symbol_by_s_value(s_value)
|
||||||
@symbols.find do |sym|
|
@symbols.find do |sym|
|
||||||
sym.id.s_value == s_value
|
sym.id.s_value == s_value
|
||||||
|
7
tool/lrama/lib/lrama/grammar/auxiliary.rb
Normal file
7
tool/lrama/lib/lrama/grammar/auxiliary.rb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module Lrama
|
||||||
|
class Grammar
|
||||||
|
# Grammar file information not used by States but by Output
|
||||||
|
class Auxiliary < Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -17,6 +17,12 @@ module Lrama
|
|||||||
"#{l}: #{r}"
|
"#{l}: #{r}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# opt_nl: ε <-- empty_rule
|
||||||
|
# | '\n' <-- not empty_rule
|
||||||
|
def empty_rule?
|
||||||
|
rhs.empty?
|
||||||
|
end
|
||||||
|
|
||||||
def precedence
|
def precedence
|
||||||
precedence_sym&.precedence
|
precedence_sym&.precedence
|
||||||
end
|
end
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
module Lrama
|
module Lrama
|
||||||
class Grammar
|
class Grammar
|
||||||
class Symbol < Struct.new(:id, :alias_name, :number, :tag, :term, :token_id, :nullable, :precedence, :printer, :error_token, keyword_init: true)
|
class Symbol < Struct.new(:id, :alias_name, :number, :tag, :term, :token_id, :nullable, :precedence, :printer, :error_token, keyword_init: true)
|
||||||
|
attr_accessor :first_set, :first_set_bitmap
|
||||||
attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
|
attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
|
||||||
|
|
||||||
def term?
|
def term?
|
||||||
@ -34,11 +35,7 @@ module Lrama
|
|||||||
end
|
end
|
||||||
|
|
||||||
def display_name
|
def display_name
|
||||||
if alias_name
|
alias_name || id.s_value
|
||||||
alias_name
|
|
||||||
else
|
|
||||||
id.s_value
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# name for yysymbol_kind_t
|
# name for yysymbol_kind_t
|
||||||
@ -51,11 +48,7 @@ module Lrama
|
|||||||
when eof_symbol?
|
when eof_symbol?
|
||||||
name = "YYEOF"
|
name = "YYEOF"
|
||||||
when term? && id.type == Token::Char
|
when term? && id.type == Token::Char
|
||||||
if alias_name
|
name = number.to_s + display_name
|
||||||
name = number.to_s + alias_name
|
|
||||||
else
|
|
||||||
name = number.to_s + id.s_value
|
|
||||||
end
|
|
||||||
when term? && id.type == Token::Ident
|
when term? && id.type == Token::Ident
|
||||||
name = id.s_value
|
name = id.s_value
|
||||||
when nterm? && (id.s_value.include?("$") || id.s_value.include?("@"))
|
when nterm? && (id.s_value.include?("$") || id.s_value.include?("@"))
|
||||||
@ -66,7 +59,7 @@ module Lrama
|
|||||||
raise "Unexpected #{self}"
|
raise "Unexpected #{self}"
|
||||||
end
|
end
|
||||||
|
|
||||||
"YYSYMBOL_" + name.gsub(/[^a-zA-Z_0-9]+/, "_")
|
"YYSYMBOL_" + name.gsub(/\W+/, "_")
|
||||||
end
|
end
|
||||||
|
|
||||||
# comment for yysymbol_kind_t
|
# comment for yysymbol_kind_t
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
require 'lrama/lexer/token/type'
|
||||||
|
|
||||||
module Lrama
|
module Lrama
|
||||||
class Lexer
|
class Lexer
|
||||||
class Token < Struct.new(:type, :s_value, :alias, keyword_init: true)
|
class Token
|
||||||
Type = Struct.new(:id, :name, keyword_init: true)
|
|
||||||
|
|
||||||
attr_accessor :line, :column, :referred
|
attr_accessor :line, :column, :referred
|
||||||
# For User_code
|
# For User_code
|
||||||
|
8
tool/lrama/lib/lrama/lexer/token/type.rb
Normal file
8
tool/lrama/lib/lrama/lexer/token/type.rb
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module Lrama
|
||||||
|
class Lexer
|
||||||
|
class Token < Struct.new(:type, :s_value, :alias, keyword_init: true)
|
||||||
|
class Type < Struct.new(:id, :name, keyword_init: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -252,7 +252,7 @@ module Lrama
|
|||||||
end
|
end
|
||||||
|
|
||||||
def extract_param_name(param)
|
def extract_param_name(param)
|
||||||
/\A(.)+([a-zA-Z0-9_]+)\z/.match(param)[2]
|
/\A(\W*)([a-zA-Z0-9_]+)\z/.match(param.split.last)[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_param_name
|
def parse_param_name
|
||||||
|
@ -22,6 +22,7 @@ module Lrama
|
|||||||
process_epilogue(grammar, lexer)
|
process_epilogue(grammar, lexer)
|
||||||
grammar.prepare
|
grammar.prepare
|
||||||
grammar.compute_nullable
|
grammar.compute_nullable
|
||||||
|
grammar.compute_first_set
|
||||||
grammar.validate!
|
grammar.validate!
|
||||||
|
|
||||||
grammar
|
grammar
|
||||||
|
@ -11,7 +11,7 @@ module Lrama
|
|||||||
end
|
end
|
||||||
|
|
||||||
def current_type
|
def current_type
|
||||||
current_token && current_token.type
|
current_token&.type
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_token
|
def previous_token
|
||||||
@ -26,9 +26,7 @@ module Lrama
|
|||||||
|
|
||||||
def consume(*token_types)
|
def consume(*token_types)
|
||||||
if token_types.include?(current_type)
|
if token_types.include?(current_type)
|
||||||
token = current_token
|
return self.next
|
||||||
self.next
|
|
||||||
return token
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -42,8 +40,7 @@ module Lrama
|
|||||||
a = []
|
a = []
|
||||||
|
|
||||||
while token_types.include?(current_type)
|
while token_types.include?(current_type)
|
||||||
a << current_token
|
a << self.next
|
||||||
self.next
|
|
||||||
end
|
end
|
||||||
|
|
||||||
raise "No token is consumed. #{token_types}" if a.empty?
|
raise "No token is consumed. #{token_types}" if a.empty?
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
require "lrama/state/reduce"
|
require "lrama/state/reduce"
|
||||||
require "lrama/state/shift"
|
require "lrama/state/reduce_reduce_conflict"
|
||||||
require "lrama/state/resolved_conflict"
|
require "lrama/state/resolved_conflict"
|
||||||
|
require "lrama/state/shift"
|
||||||
|
require "lrama/state/shift_reduce_conflict"
|
||||||
|
|
||||||
module Lrama
|
module Lrama
|
||||||
class State
|
class State
|
||||||
Conflict = Struct.new(:symbols, :reduce, :type, keyword_init: true)
|
|
||||||
|
|
||||||
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
||||||
:default_reduction_rule, :closure, :items
|
:default_reduction_rule, :closure, :items
|
||||||
attr_accessor :shifts, :reduces
|
attr_accessor :shifts, :reduces
|
||||||
@ -101,6 +101,10 @@ module Lrama
|
|||||||
@term_transitions
|
@term_transitions
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def transitions
|
||||||
|
term_transitions + nterm_transitions
|
||||||
|
end
|
||||||
|
|
||||||
def selected_term_transitions
|
def selected_term_transitions
|
||||||
term_transitions.select do |shift, next_state|
|
term_transitions.select do |shift, next_state|
|
||||||
!shift.not_selected
|
!shift.not_selected
|
||||||
@ -144,6 +148,10 @@ module Lrama
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def has_conflicts?
|
||||||
|
!@conflicts.empty?
|
||||||
|
end
|
||||||
|
|
||||||
def sr_conflicts
|
def sr_conflicts
|
||||||
@conflicts.select do |conflict|
|
@conflicts.select do |conflict|
|
||||||
conflict.type == :shift_reduce
|
conflict.type == :shift_reduce
|
||||||
|
9
tool/lrama/lib/lrama/state/reduce_reduce_conflict.rb
Normal file
9
tool/lrama/lib/lrama/state/reduce_reduce_conflict.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Lrama
|
||||||
|
class State
|
||||||
|
class ReduceReduceConflict < Struct.new(:symbols, :reduce1, :reduce2, keyword_init: true)
|
||||||
|
def type
|
||||||
|
:reduce_reduce
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
9
tool/lrama/lib/lrama/state/shift_reduce_conflict.rb
Normal file
9
tool/lrama/lib/lrama/state/shift_reduce_conflict.rb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Lrama
|
||||||
|
class State
|
||||||
|
class ShiftReduceConflict < Struct.new(:symbols, :shift, :reduce, keyword_init: true)
|
||||||
|
def type
|
||||||
|
:shift_reduce
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -102,43 +102,27 @@ module Lrama
|
|||||||
end
|
end
|
||||||
|
|
||||||
def direct_read_sets
|
def direct_read_sets
|
||||||
h = {}
|
@direct_read_sets.transform_values do |v|
|
||||||
|
bitmap_to_terms(v)
|
||||||
@direct_read_sets.each do |k, v|
|
|
||||||
h[k] = bitmap_to_terms(v)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return h
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_sets
|
def read_sets
|
||||||
h = {}
|
@read_sets.transform_values do |v|
|
||||||
|
bitmap_to_terms(v)
|
||||||
@read_sets.each do |k, v|
|
|
||||||
h[k] = bitmap_to_terms(v)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return h
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_sets
|
def follow_sets
|
||||||
h = {}
|
@follow_sets.transform_values do |v|
|
||||||
|
bitmap_to_terms(v)
|
||||||
@follow_sets.each do |k, v|
|
|
||||||
h[k] = bitmap_to_terms(v)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return h
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def la
|
def la
|
||||||
h = {}
|
@la.transform_values do |v|
|
||||||
|
bitmap_to_terms(v)
|
||||||
@la.each do |k, v|
|
|
||||||
h[k] = bitmap_to_terms(v)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return h
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -452,7 +436,7 @@ module Lrama
|
|||||||
|
|
||||||
# Can resolve only when both have prec
|
# Can resolve only when both have prec
|
||||||
unless shift_prec && reduce_prec
|
unless shift_prec && reduce_prec
|
||||||
state.conflicts << State::Conflict.new(symbols: [sym], reduce: reduce, type: :shift_reduce)
|
state.conflicts << State::ShiftReduceConflict.new(symbols: [sym], shift: shift, reduce: reduce)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -501,16 +485,21 @@ module Lrama
|
|||||||
|
|
||||||
def compute_reduce_reduce_conflicts
|
def compute_reduce_reduce_conflicts
|
||||||
states.each do |state|
|
states.each do |state|
|
||||||
a = []
|
count = state.reduces.count
|
||||||
|
|
||||||
state.reduces.each do |reduce|
|
for i in 0...count do
|
||||||
next if reduce.look_ahead.nil?
|
reduce1 = state.reduces[i]
|
||||||
|
next if reduce1.look_ahead.nil?
|
||||||
|
|
||||||
intersection = a & reduce.look_ahead
|
for j in (i+1)...count do
|
||||||
a += reduce.look_ahead
|
reduce2 = state.reduces[j]
|
||||||
|
next if reduce2.look_ahead.nil?
|
||||||
|
|
||||||
|
intersection = reduce1.look_ahead & reduce2.look_ahead
|
||||||
|
|
||||||
if !intersection.empty?
|
if !intersection.empty?
|
||||||
state.conflicts << State::Conflict.new(symbols: intersection.dup, reduce: reduce, type: :reduce_reduce)
|
state.conflicts << State::ReduceReduceConflict.new(symbols: intersection, reduce1: reduce1, reduce2: reduce2)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,20 +12,56 @@ module Lrama
|
|||||||
rule.id
|
rule.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def empty_rule?
|
||||||
|
rule.empty_rule?
|
||||||
|
end
|
||||||
|
|
||||||
|
def number_of_rest_symbols
|
||||||
|
rule.rhs.count - position
|
||||||
|
end
|
||||||
|
|
||||||
|
def lhs
|
||||||
|
rule.lhs
|
||||||
|
end
|
||||||
|
|
||||||
def next_sym
|
def next_sym
|
||||||
rule.rhs[position]
|
rule.rhs[position]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def next_next_sym
|
||||||
|
rule.rhs[position + 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def previous_sym
|
||||||
|
rule.rhs[position - 1]
|
||||||
|
end
|
||||||
|
|
||||||
def end_of_rule?
|
def end_of_rule?
|
||||||
rule.rhs.count == position
|
rule.rhs.count == position
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def beginning_of_rule?
|
||||||
|
position == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def start_item?
|
||||||
|
rule.id == 0 && position == 0
|
||||||
|
end
|
||||||
|
|
||||||
def new_by_next_position
|
def new_by_next_position
|
||||||
Item.new(rule: rule, position: position + 1)
|
Item.new(rule: rule, position: position + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
def previous_sym
|
def symbols_before_dot
|
||||||
rule.rhs[position - 1]
|
rule.rhs[0...position]
|
||||||
|
end
|
||||||
|
|
||||||
|
def symbols_after_dot
|
||||||
|
rule.rhs[position..-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{lhs.id.s_value}: #{display_name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def display_name
|
def display_name
|
||||||
|
@ -14,13 +14,13 @@ module Lrama
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, verbose: false)
|
def _report(io, grammar: false, states: false, itemsets: false, lookaheads: false, solved: false, counterexamples: false, verbose: false)
|
||||||
# TODO: Unused terms
|
# TODO: Unused terms
|
||||||
# TODO: Unused rules
|
# TODO: Unused rules
|
||||||
|
|
||||||
report_conflicts(io)
|
report_conflicts(io)
|
||||||
report_grammar(io) if grammar
|
report_grammar(io) if grammar
|
||||||
report_states(io, itemsets, lookaheads, solved, verbose)
|
report_states(io, itemsets, lookaheads, solved, counterexamples, verbose)
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_conflicts(io)
|
def report_conflicts(io)
|
||||||
@ -71,7 +71,11 @@ module Lrama
|
|||||||
io << "\n\n"
|
io << "\n\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
def report_states(io, itemsets, lookaheads, solved, verbose)
|
def report_states(io, itemsets, lookaheads, solved, counterexamples, verbose)
|
||||||
|
if counterexamples
|
||||||
|
cex = Counterexamples.new(@states)
|
||||||
|
end
|
||||||
|
|
||||||
@states.states.each do |state|
|
@states.states.each do |state|
|
||||||
# Report State
|
# Report State
|
||||||
io << "State #{state.id}\n\n"
|
io << "State #{state.id}\n\n"
|
||||||
@ -194,6 +198,27 @@ module Lrama
|
|||||||
io << "\n" if !state.resolved_conflicts.empty?
|
io << "\n" if !state.resolved_conflicts.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if counterexamples && state.has_conflicts?
|
||||||
|
# Report counterexamples
|
||||||
|
examples = cex.compute(state)
|
||||||
|
examples.each do |example|
|
||||||
|
label0 = example.type == :shift_reduce ? "shift/reduce" : "reduce/reduce"
|
||||||
|
label1 = example.type == :shift_reduce ? "Shift derivation" : "First Reduce derivation"
|
||||||
|
label2 = example.type == :shift_reduce ? "Reduce derivation" : "Second Reduce derivation"
|
||||||
|
|
||||||
|
io << " #{label0} conflict on token #{example.conflict_symbol.id.s_value}:\n"
|
||||||
|
io << " #{example.path1_item.to_s}\n"
|
||||||
|
io << " #{example.path2_item.to_s}\n"
|
||||||
|
io << " #{label1}\n"
|
||||||
|
example.derivations1.render_strings_for_report.each do |str|
|
||||||
|
io << " #{str}\n"
|
||||||
|
end
|
||||||
|
io << " #{label2}\n"
|
||||||
|
example.derivations2.render_strings_for_report.each do |str|
|
||||||
|
io << " #{str}\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if verbose
|
if verbose
|
||||||
# Report direct_read_sets
|
# Report direct_read_sets
|
||||||
|
4
tool/lrama/lib/lrama/type.rb
Normal file
4
tool/lrama/lib/lrama/type.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
module Lrama
|
||||||
|
class Type < Struct.new(:id, :tag, keyword_init: true)
|
||||||
|
end
|
||||||
|
end
|
@ -1,3 +1,3 @@
|
|||||||
module Lrama
|
module Lrama
|
||||||
VERSION = "0.5.3".freeze
|
VERSION = "0.5.4".freeze
|
||||||
end
|
end
|
||||||
|
@ -1220,26 +1220,30 @@ yydestruct (const char *yymsg,
|
|||||||
|
|
||||||
<%- if output.error_recovery -%>
|
<%- if output.error_recovery -%>
|
||||||
#ifndef YYMAXREPAIR
|
#ifndef YYMAXREPAIR
|
||||||
# define YYMAXREPAIR 3
|
# define YYMAXREPAIR(<%= output.parse_param_name %>) (3)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum repair_type {
|
#ifndef YYERROR_RECOVERY_ENABLED
|
||||||
|
# define YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>) (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum yy_repair_type {
|
||||||
insert,
|
insert,
|
||||||
delete,
|
delete,
|
||||||
shift,
|
shift,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct repair {
|
struct yy_repair {
|
||||||
enum repair_type type;
|
enum yy_repair_type type;
|
||||||
yysymbol_kind_t term;
|
yysymbol_kind_t term;
|
||||||
};
|
};
|
||||||
typedef struct repair repair;
|
typedef struct yy_repair yy_repair;
|
||||||
|
|
||||||
struct repairs {
|
struct yy_repairs {
|
||||||
/* For debug */
|
/* For debug */
|
||||||
int id;
|
int id;
|
||||||
/* For breadth-first traversing */
|
/* For breadth-first traversing */
|
||||||
struct repairs *next;
|
struct yy_repairs *next;
|
||||||
YYPTRDIFF_T stack_length;
|
YYPTRDIFF_T stack_length;
|
||||||
/* Bottom of states */
|
/* Bottom of states */
|
||||||
yy_state_t *states;
|
yy_state_t *states;
|
||||||
@ -1248,10 +1252,10 @@ struct repairs {
|
|||||||
/* repair length */
|
/* repair length */
|
||||||
int repair_length;
|
int repair_length;
|
||||||
/* */
|
/* */
|
||||||
struct repairs *prev_repair;
|
struct yy_repairs *prev_repair;
|
||||||
struct repair repair;
|
struct yy_repair repair;
|
||||||
};
|
};
|
||||||
typedef struct repairs repairs;
|
typedef struct yy_repairs yy_repairs;
|
||||||
|
|
||||||
struct yy_term {
|
struct yy_term {
|
||||||
yysymbol_kind_t kind;
|
yysymbol_kind_t kind;
|
||||||
@ -1260,12 +1264,12 @@ struct yy_term {
|
|||||||
};
|
};
|
||||||
typedef struct yy_term yy_term;
|
typedef struct yy_term yy_term;
|
||||||
|
|
||||||
struct repair_terms {
|
struct yy_repair_terms {
|
||||||
int id;
|
int id;
|
||||||
int length;
|
int length;
|
||||||
yy_term terms[];
|
yy_term terms[];
|
||||||
};
|
};
|
||||||
typedef struct repair_terms repair_terms;
|
typedef struct yy_repair_terms yy_repair_terms;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
yy_error_token_initialize (yysymbol_kind_t yykind, YYSTYPE * const yyvaluep, YYLTYPE * const yylocationp<%= output.user_formals %>)
|
yy_error_token_initialize (yysymbol_kind_t yykind, YYSTYPE * const yyvaluep, YYLTYPE * const yylocationp<%= output.user_formals %>)
|
||||||
@ -1280,11 +1284,11 @@ switch (yykind)
|
|||||||
YY_IGNORE_MAYBE_UNINITIALIZED_END
|
YY_IGNORE_MAYBE_UNINITIALIZED_END
|
||||||
}
|
}
|
||||||
|
|
||||||
static repair_terms *
|
static yy_repair_terms *
|
||||||
yy_create_repair_terms(repairs *reps)
|
yy_create_repair_terms(yy_repairs *reps<%= output.user_formals %>)
|
||||||
{
|
{
|
||||||
repairs *r = reps;
|
yy_repairs *r = reps;
|
||||||
repair_terms *rep_terms;
|
yy_repair_terms *rep_terms;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
while (r->prev_repair)
|
while (r->prev_repair)
|
||||||
@ -1293,7 +1297,7 @@ yy_create_repair_terms(repairs *reps)
|
|||||||
r = r->prev_repair;
|
r = r->prev_repair;
|
||||||
}
|
}
|
||||||
|
|
||||||
rep_terms = (repair_terms *) malloc (sizeof (repair_terms) + sizeof (yy_term) * count);
|
rep_terms = (yy_repair_terms *) YYMALLOC (sizeof (yy_repair_terms) + sizeof (yy_term) * count);
|
||||||
rep_terms->id = reps->id;
|
rep_terms->id = reps->id;
|
||||||
rep_terms->length = count;
|
rep_terms->length = count;
|
||||||
|
|
||||||
@ -1309,46 +1313,46 @@ yy_create_repair_terms(repairs *reps)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
yy_print_repairs(repairs *reps)
|
yy_print_repairs(yy_repairs *reps<%= output.user_formals %>)
|
||||||
{
|
{
|
||||||
repairs *r = reps;
|
yy_repairs *r = reps;
|
||||||
|
|
||||||
fprintf (stderr,
|
YYDPRINTF ((stderr,
|
||||||
"id: %d, repair_length: %d, repair_state: %d, prev_repair_id: %d\n",
|
"id: %d, repair_length: %d, repair_state: %d, prev_repair_id: %d\n",
|
||||||
reps->id, reps->repair_length, *reps->state, reps->prev_repair->id);
|
reps->id, reps->repair_length, *reps->state, reps->prev_repair->id));
|
||||||
|
|
||||||
while (r->prev_repair)
|
while (r->prev_repair)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "%s ", yysymbol_name (r->repair.term));
|
YYDPRINTF ((stderr, "%s ", yysymbol_name (r->repair.term)));
|
||||||
r = r->prev_repair;
|
r = r->prev_repair;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
YYDPRINTF ((stderr, "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
yy_print_repair_terms(repair_terms *rep_terms)
|
yy_print_repair_terms(yy_repair_terms *rep_terms<%= output.user_formals %>)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < rep_terms->length; i++)
|
for (int i = 0; i < rep_terms->length; i++)
|
||||||
fprintf (stderr, "%s ", yysymbol_name (rep_terms->terms[i].kind));
|
YYDPRINTF ((stderr, "%s ", yysymbol_name (rep_terms->terms[i].kind)));
|
||||||
|
|
||||||
fprintf (stderr, "\n");
|
YYDPRINTF ((stderr, "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
yy_free_repairs(repairs *reps)
|
yy_free_repairs(yy_repairs *reps<%= output.user_formals %>)
|
||||||
{
|
{
|
||||||
while (reps)
|
while (reps)
|
||||||
{
|
{
|
||||||
repairs *r = reps;
|
yy_repairs *r = reps;
|
||||||
reps = reps->next;
|
reps = reps->next;
|
||||||
free (r->states);
|
YYFREE (r->states);
|
||||||
free (r);
|
YYFREE (r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
yy_process_repairs(repairs *reps, yysymbol_kind_t token)
|
yy_process_repairs(yy_repairs *reps, yysymbol_kind_t token)
|
||||||
{
|
{
|
||||||
int yyn;
|
int yyn;
|
||||||
int yystate = *reps->state;
|
int yystate = *reps->state;
|
||||||
@ -1417,22 +1421,22 @@ yyrecover_errlab:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static repair_terms *
|
static yy_repair_terms *
|
||||||
yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar)
|
yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar<%= output.user_formals %>)
|
||||||
{
|
{
|
||||||
yysymbol_kind_t yytoken = YYTRANSLATE (yychar);
|
yysymbol_kind_t yytoken = YYTRANSLATE (yychar);
|
||||||
repair_terms *rep_terms = YY_NULLPTR;
|
yy_repair_terms *rep_terms = YY_NULLPTR;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
repairs *head = (repairs *) malloc (sizeof (repairs));
|
yy_repairs *head = (yy_repairs *) YYMALLOC (sizeof (yy_repairs));
|
||||||
repairs *current = head;
|
yy_repairs *current = head;
|
||||||
repairs *tail = head;
|
yy_repairs *tail = head;
|
||||||
YYPTRDIFF_T stack_length = yyssp - yyss + 1;
|
YYPTRDIFF_T stack_length = yyssp - yyss + 1;
|
||||||
|
|
||||||
head->id = count;
|
head->id = count;
|
||||||
head->next = 0;
|
head->next = 0;
|
||||||
head->stack_length = stack_length;
|
head->stack_length = stack_length;
|
||||||
head->states = (yy_state_t *) malloc (sizeof (yy_state_t) * (stack_length));
|
head->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length));
|
||||||
head->state = head->states + (yyssp - yyss);
|
head->state = head->states + (yyssp - yyss);
|
||||||
YYCOPY (head->states, yyss, stack_length);
|
YYCOPY (head->states, yyss, stack_length);
|
||||||
head->repair_length = 0;
|
head->repair_length = 0;
|
||||||
@ -1456,14 +1460,14 @@ yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar)
|
|||||||
{
|
{
|
||||||
if (yyx != YYSYMBOL_YYerror)
|
if (yyx != YYSYMBOL_YYerror)
|
||||||
{
|
{
|
||||||
if (current->repair_length + 1 > YYMAXREPAIR)
|
if (current->repair_length + 1 > YYMAXREPAIR(<%= output.parse_param_name %>))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
repairs *new = (repairs *) malloc (sizeof (repairs));
|
yy_repairs *new = (yy_repairs *) YYMALLOC (sizeof (yy_repairs));
|
||||||
new->id = count;
|
new->id = count;
|
||||||
new->next = 0;
|
new->next = 0;
|
||||||
new->stack_length = stack_length;
|
new->stack_length = stack_length;
|
||||||
new->states = (yy_state_t *) malloc (sizeof (yy_state_t) * (stack_length));
|
new->states = (yy_state_t *) YYMALLOC (sizeof (yy_state_t) * (stack_length));
|
||||||
new->state = new->states + (current->state - current->states);
|
new->state = new->states + (current->state - current->states);
|
||||||
YYCOPY (new->states, current->states, current->state - current->states + 1);
|
YYCOPY (new->states, current->states, current->state - current->states + 1);
|
||||||
new->repair_length = current->repair_length + 1;
|
new->repair_length = current->repair_length + 1;
|
||||||
@ -1474,7 +1478,7 @@ yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar)
|
|||||||
/* Process PDA assuming next token is yyx */
|
/* Process PDA assuming next token is yyx */
|
||||||
if (! yy_process_repairs (new, yyx))
|
if (! yy_process_repairs (new, yyx))
|
||||||
{
|
{
|
||||||
free (new);
|
YYFREE (new);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1484,18 +1488,18 @@ yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar)
|
|||||||
|
|
||||||
if (yyx == yytoken)
|
if (yyx == yytoken)
|
||||||
{
|
{
|
||||||
rep_terms = yy_create_repair_terms (current);
|
rep_terms = yy_create_repair_terms (current<%= output.user_args %>);
|
||||||
fprintf (stderr, "repair_terms found. id: %d, length: %d\n", rep_terms->id, rep_terms->length);
|
YYDPRINTF ((stderr, "repair_terms found. id: %d, length: %d\n", rep_terms->id, rep_terms->length));
|
||||||
yy_print_repairs (current);
|
yy_print_repairs (current<%= output.user_args %>);
|
||||||
yy_print_repair_terms (rep_terms);
|
yy_print_repair_terms (rep_terms<%= output.user_args %>);
|
||||||
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf (stderr,
|
YYDPRINTF ((stderr,
|
||||||
"New repairs is enqueued. count: %d, yystate: %d, yyx: %d\n",
|
"New repairs is enqueued. count: %d, yystate: %d, yyx: %d\n",
|
||||||
count, yystate, yyx);
|
count, yystate, yyx));
|
||||||
yy_print_repairs (new);
|
yy_print_repairs (new<%= output.user_args %>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1505,11 +1509,11 @@ yyrecover(yy_state_t *yyss, yy_state_t *yyssp, int yychar)
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
||||||
yy_free_repairs(head);
|
yy_free_repairs(head<%= output.user_args %>);
|
||||||
|
|
||||||
if (!rep_terms)
|
if (!rep_terms)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "repair_terms not found\n");
|
YYDPRINTF ((stderr, "repair_terms not found\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rep_terms;
|
return rep_terms;
|
||||||
@ -1586,7 +1590,7 @@ YYLTYPE yylloc = yyloc_default;
|
|||||||
/* The locations where the error started and ended. */
|
/* The locations where the error started and ended. */
|
||||||
YYLTYPE yyerror_range[3];
|
YYLTYPE yyerror_range[3];
|
||||||
<%- if output.error_recovery -%>
|
<%- if output.error_recovery -%>
|
||||||
repair_terms *rep_terms = 0;
|
yy_repair_terms *rep_terms = 0;
|
||||||
yy_term term_backup;
|
yy_term term_backup;
|
||||||
int rep_terms_index;
|
int rep_terms_index;
|
||||||
int yychar_backup;
|
int yychar_backup;
|
||||||
@ -1726,6 +1730,8 @@ yybackup:
|
|||||||
/* Not known => get a lookahead token if don't already have one. */
|
/* Not known => get a lookahead token if don't already have one. */
|
||||||
|
|
||||||
<%- if output.error_recovery -%>
|
<%- if output.error_recovery -%>
|
||||||
|
if (YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>))
|
||||||
|
{
|
||||||
if (yychar == YYEMPTY && rep_terms)
|
if (yychar == YYEMPTY && rep_terms)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -1749,11 +1755,12 @@ yybackup:
|
|||||||
yychar = yychar_backup;
|
yychar = yychar_backup;
|
||||||
YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc<%= output.user_args %>);
|
YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc<%= output.user_args %>);
|
||||||
|
|
||||||
free (rep_terms);
|
YYFREE (rep_terms);
|
||||||
rep_terms = 0;
|
rep_terms = 0;
|
||||||
yychar_backup = 0;
|
yychar_backup = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
/* YYCHAR is either empty, or end-of-input, or a valid lookahead. */
|
/* YYCHAR is either empty, or end-of-input, or a valid lookahead. */
|
||||||
if (yychar == YYEMPTY)
|
if (yychar == YYEMPTY)
|
||||||
@ -1980,8 +1987,9 @@ yyerrorlab:
|
|||||||
`-------------------------------------------------------------*/
|
`-------------------------------------------------------------*/
|
||||||
yyerrlab1:
|
yyerrlab1:
|
||||||
<%- if output.error_recovery -%>
|
<%- if output.error_recovery -%>
|
||||||
|
if (YYERROR_RECOVERY_ENABLED(<%= output.parse_param_name %>))
|
||||||
{
|
{
|
||||||
rep_terms = yyrecover (yyss, yyssp, yychar);
|
rep_terms = yyrecover (yyss, yyssp, yychar<%= output.user_args %>);
|
||||||
if (rep_terms)
|
if (rep_terms)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < rep_terms->length; i++)
|
for (int i = 0; i < rep_terms->length; i++)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user