This commit is contained in:
@@ -0,0 +1,976 @@
|
||||
#--
|
||||
#
|
||||
#
|
||||
#
|
||||
# Copyright (c) 1999-2006 Minero Aoki
|
||||
#
|
||||
# This program is free software.
|
||||
# You can distribute/modify this program under the same terms of ruby.
|
||||
# see the file "COPYING".
|
||||
#
|
||||
#++
|
||||
|
||||
require_relative 'iset'
|
||||
require_relative 'statetransitiontable'
|
||||
require_relative 'exception'
|
||||
require 'forwardable'
|
||||
|
||||
module Racc
|
||||
|
||||
# A table of LALR states.
|
||||
class States
|
||||
|
||||
include Enumerable
|
||||
|
||||
def initialize(grammar, debug_flags = DebugFlags.new)
|
||||
@grammar = grammar
|
||||
@symboltable = grammar.symboltable
|
||||
@d_state = debug_flags.state
|
||||
@d_la = debug_flags.la
|
||||
@d_prec = debug_flags.prec
|
||||
@states = []
|
||||
@statecache = {}
|
||||
@actions = ActionTable.new(@grammar, self)
|
||||
@nfa_computed = false
|
||||
@dfa_computed = false
|
||||
end
|
||||
|
||||
attr_reader :grammar
|
||||
attr_reader :actions
|
||||
|
||||
def size
|
||||
@states.size
|
||||
end
|
||||
|
||||
def inspect
|
||||
'#<state table>'
|
||||
end
|
||||
|
||||
alias to_s inspect
|
||||
|
||||
def [](i)
|
||||
@states[i]
|
||||
end
|
||||
|
||||
def each_state(&block)
|
||||
@states.each(&block)
|
||||
end
|
||||
|
||||
alias each each_state
|
||||
|
||||
def each_index(&block)
|
||||
@states.each_index(&block)
|
||||
end
|
||||
|
||||
extend Forwardable
|
||||
|
||||
def_delegator "@actions", :shift_n
|
||||
def_delegator "@actions", :reduce_n
|
||||
def_delegator "@actions", :nt_base
|
||||
|
||||
def should_report_srconflict?
|
||||
srconflict_exist? and
|
||||
(n_srconflicts() != @grammar.n_expected_srconflicts)
|
||||
end
|
||||
|
||||
def should_error_on_expect_mismatch?
|
||||
should_report_srconflict? && @grammar.error_on_expect_mismatch
|
||||
end
|
||||
|
||||
def srconflict_exist?
|
||||
n_srconflicts() != 0
|
||||
end
|
||||
|
||||
def n_srconflicts
|
||||
@n_srconflicts ||= inject(0) {|sum, st| sum + st.n_srconflicts }
|
||||
end
|
||||
|
||||
def rrconflict_exist?
|
||||
n_rrconflicts() != 0
|
||||
end
|
||||
|
||||
def n_rrconflicts
|
||||
@n_rrconflicts ||= inject(0) {|sum, st| sum + st.n_rrconflicts }
|
||||
end
|
||||
|
||||
def state_transition_table
|
||||
@state_transition_table ||= StateTransitionTable.generate(self.dfa)
|
||||
end
|
||||
|
||||
#
|
||||
# NFA (Non-deterministic Finite Automaton) Computation
|
||||
#
|
||||
|
||||
public
|
||||
|
||||
def nfa
|
||||
return self if @nfa_computed
|
||||
compute_nfa
|
||||
@nfa_computed = true
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compute_nfa
|
||||
@grammar.init
|
||||
# add state 0
|
||||
core_to_state [ @grammar[0].ptrs[0] ]
|
||||
# generate LALR states
|
||||
cur = 0
|
||||
@gotos = []
|
||||
while cur < @states.size
|
||||
generate_states @states[cur] # state is added here
|
||||
cur += 1
|
||||
end
|
||||
@actions.init
|
||||
end
|
||||
|
||||
def generate_states(state)
|
||||
puts "dstate: #{state}" if @d_state
|
||||
|
||||
table = {}
|
||||
state.closure.each do |ptr|
|
||||
if sym = ptr.dereference
|
||||
addsym table, sym, ptr.next
|
||||
end
|
||||
end
|
||||
table.each do |sym, core|
|
||||
puts "dstate: sym=#{sym} ncore=#{core}" if @d_state
|
||||
|
||||
dest = core_to_state(core.to_a)
|
||||
state.goto_table[sym] = dest
|
||||
id = sym.nonterminal?() ? @gotos.size : nil
|
||||
g = Goto.new(id, sym, state, dest)
|
||||
@gotos.push g if sym.nonterminal?
|
||||
state.gotos[sym] = g
|
||||
puts "dstate: #{state.ident} --#{sym}--> #{dest.ident}" if @d_state
|
||||
|
||||
# check infinite recursion
|
||||
if state.ident == dest.ident and state.closure.size == 1
|
||||
raise CompileError,
|
||||
sprintf("Infinite recursion: state %d, with rule %d",
|
||||
state.ident, state.ptrs[0].rule.ident)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def addsym(table, sym, ptr)
|
||||
unless s = table[sym]
|
||||
table[sym] = s = ISet.new
|
||||
end
|
||||
s.add ptr
|
||||
end
|
||||
|
||||
def core_to_state(core)
|
||||
#
|
||||
# convert CORE to a State object.
|
||||
# If matching state does not exist, create it and add to the table.
|
||||
#
|
||||
|
||||
k = fingerprint(core)
|
||||
unless dest = @statecache[k]
|
||||
# not registered yet
|
||||
dest = State.new(@states.size, core)
|
||||
@states.push dest
|
||||
|
||||
@statecache[k] = dest
|
||||
|
||||
puts "core_to_state: create state ID #{dest.ident}" if @d_state
|
||||
else
|
||||
if @d_state
|
||||
puts "core_to_state: dest is cached ID #{dest.ident}"
|
||||
puts "core_to_state: dest core #{dest.core.join(' ')}"
|
||||
end
|
||||
end
|
||||
|
||||
dest
|
||||
end
|
||||
|
||||
def fingerprint(arr)
|
||||
arr.map {|i| i.ident }.pack('L*')
|
||||
end
|
||||
|
||||
#
|
||||
# DFA (Deterministic Finite Automaton) Generation
|
||||
#
|
||||
|
||||
public
|
||||
|
||||
def dfa
|
||||
return self if @dfa_computed
|
||||
nfa
|
||||
compute_dfa
|
||||
@dfa_computed = true
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compute_dfa
|
||||
la = lookahead()
|
||||
@states.each do |state|
|
||||
state.la = la
|
||||
resolve state
|
||||
end
|
||||
set_accept
|
||||
@states.each do |state|
|
||||
pack state
|
||||
end
|
||||
check_useless
|
||||
end
|
||||
|
||||
def lookahead
|
||||
#
|
||||
# lookahead algorithm ver.3 -- from bison 1.26
|
||||
#
|
||||
|
||||
gotos = @gotos
|
||||
if @d_la
|
||||
puts "\n--- goto ---"
|
||||
gotos.each_with_index {|g, i| print i, ' '; p g }
|
||||
end
|
||||
|
||||
### initialize_LA()
|
||||
### set_goto_map()
|
||||
la_rules = []
|
||||
@states.each do |state|
|
||||
state.check_la la_rules
|
||||
end
|
||||
|
||||
### initialize_F()
|
||||
f = create_tmap(gotos.size)
|
||||
reads = []
|
||||
edge = []
|
||||
gotos.each do |goto|
|
||||
goto.to_state.goto_table.each do |t, st|
|
||||
if t.terminal?
|
||||
f[goto.ident] |= (1 << t.ident)
|
||||
elsif t.nullable?
|
||||
edge.push goto.to_state.gotos[t].ident
|
||||
end
|
||||
end
|
||||
if edge.empty?
|
||||
reads.push nil
|
||||
else
|
||||
reads.push edge
|
||||
edge = []
|
||||
end
|
||||
end
|
||||
digraph f, reads
|
||||
if @d_la
|
||||
puts "\n--- F1 (reads) ---"
|
||||
print_tab gotos, reads, f
|
||||
end
|
||||
|
||||
### build_relations()
|
||||
### compute_FOLLOWS
|
||||
path = nil
|
||||
edge = []
|
||||
lookback = Array.new(la_rules.size, nil)
|
||||
includes = []
|
||||
gotos.each do |goto|
|
||||
goto.symbol.heads.each do |ptr|
|
||||
path = record_path(goto.from_state, ptr.rule)
|
||||
lastgoto = path.last
|
||||
st = lastgoto ? lastgoto.to_state : goto.from_state
|
||||
if st.conflict?
|
||||
addrel lookback, st.rruleid(ptr.rule), goto
|
||||
end
|
||||
path.reverse_each do |g|
|
||||
break if g.symbol.terminal?
|
||||
edge.push g.ident
|
||||
break unless g.symbol.nullable?
|
||||
end
|
||||
end
|
||||
if edge.empty?
|
||||
includes.push nil
|
||||
else
|
||||
includes.push edge
|
||||
edge = []
|
||||
end
|
||||
end
|
||||
includes = transpose(includes)
|
||||
digraph f, includes
|
||||
if @d_la
|
||||
puts "\n--- F2 (includes) ---"
|
||||
print_tab gotos, includes, f
|
||||
end
|
||||
|
||||
### compute_lookaheads
|
||||
la = create_tmap(la_rules.size)
|
||||
lookback.each_with_index do |arr, i|
|
||||
if arr
|
||||
arr.each do |g|
|
||||
la[i] |= f[g.ident]
|
||||
end
|
||||
end
|
||||
end
|
||||
if @d_la
|
||||
puts "\n--- LA (lookback) ---"
|
||||
print_tab la_rules, lookback, la
|
||||
end
|
||||
|
||||
la
|
||||
end
|
||||
|
||||
def create_tmap(size)
|
||||
Array.new(size, 0) # use Integer as bitmap
|
||||
end
|
||||
|
||||
def addrel(tbl, i, item)
|
||||
if a = tbl[i]
|
||||
a.push item
|
||||
else
|
||||
tbl[i] = [item]
|
||||
end
|
||||
end
|
||||
|
||||
def record_path(begst, rule)
|
||||
st = begst
|
||||
path = []
|
||||
rule.symbols.each do |t|
|
||||
goto = st.gotos[t]
|
||||
path.push goto
|
||||
st = goto.to_state
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
def transpose(rel)
|
||||
new = Array.new(rel.size, nil)
|
||||
rel.each_with_index do |arr, idx|
|
||||
if arr
|
||||
arr.each do |i|
|
||||
addrel new, i, idx
|
||||
end
|
||||
end
|
||||
end
|
||||
new
|
||||
end
|
||||
|
||||
def digraph(map, relation)
|
||||
n = relation.size
|
||||
index = Array.new(n, nil)
|
||||
vertices = []
|
||||
@infinity = n + 2
|
||||
|
||||
index.each_index do |i|
|
||||
if not index[i] and relation[i]
|
||||
traverse i, index, vertices, map, relation
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def traverse(i, index, vertices, map, relation)
|
||||
vertices.push i
|
||||
index[i] = height = vertices.size
|
||||
|
||||
if rp = relation[i]
|
||||
rp.each do |proci|
|
||||
unless index[proci]
|
||||
traverse proci, index, vertices, map, relation
|
||||
end
|
||||
if index[i] > index[proci]
|
||||
# circulative recursion !!!
|
||||
index[i] = index[proci]
|
||||
end
|
||||
map[i] |= map[proci]
|
||||
end
|
||||
end
|
||||
|
||||
if index[i] == height
|
||||
while true
|
||||
proci = vertices.pop
|
||||
index[proci] = @infinity
|
||||
break if i == proci
|
||||
|
||||
map[proci] |= map[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# for debug
|
||||
def print_atab(idx, tab)
|
||||
tab.each_with_index do |i,ii|
|
||||
printf '%-20s', idx[ii].inspect
|
||||
p i
|
||||
end
|
||||
end
|
||||
|
||||
def print_tab(idx, rel, tab)
|
||||
tab.each_with_index do |bin,i|
|
||||
print i, ' ', idx[i].inspect, ' << '; p rel[i]
|
||||
print ' '
|
||||
each_t(@symboltable, bin) {|t| print ' ', t }
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
# for debug
|
||||
def print_tab_i(idx, rel, tab, i)
|
||||
bin = tab[i]
|
||||
print i, ' ', idx[i].inspect, ' << '; p rel[i]
|
||||
print ' '
|
||||
each_t(@symboltable, bin) {|t| print ' ', t }
|
||||
end
|
||||
|
||||
# for debug
|
||||
def printb(i)
|
||||
each_t(@symboltable, i) do |t|
|
||||
print t, ' '
|
||||
end
|
||||
puts
|
||||
end
|
||||
|
||||
def each_t(tbl, set)
|
||||
0.upto( set.size ) do |i|
|
||||
(0..7).each do |ii|
|
||||
if set[idx = i * 8 + ii] == 1
|
||||
yield tbl[idx]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# resolve
|
||||
#
|
||||
|
||||
def resolve(state)
|
||||
if state.conflict?
|
||||
resolve_rr state, state.ritems
|
||||
resolve_sr state, state.stokens
|
||||
else
|
||||
if state.rrules.empty?
|
||||
# shift
|
||||
state.stokens.each do |t|
|
||||
state.action[t] = @actions.shift(state.goto_table[t])
|
||||
end
|
||||
else
|
||||
# reduce
|
||||
state.defact = @actions.reduce(state.rrules[0])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_rr(state, r)
|
||||
r.each do |item|
|
||||
item.each_la(@symboltable) do |t|
|
||||
act = state.action[t]
|
||||
if act
|
||||
unless act.kind_of?(Reduce)
|
||||
raise "racc: fatal: #{act.class} in action table"
|
||||
end
|
||||
# Cannot resolve R/R conflict (on t).
|
||||
# Reduce with upper rule as default.
|
||||
state.rr_conflict act.rule, item.rule, t
|
||||
else
|
||||
# No conflict.
|
||||
state.action[t] = @actions.reduce(item.rule)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_sr(state, s)
|
||||
s.each do |stok|
|
||||
goto = state.goto_table[stok]
|
||||
act = state.action[stok]
|
||||
|
||||
unless act
|
||||
# no conflict
|
||||
state.action[stok] = @actions.shift(goto)
|
||||
else
|
||||
unless act.kind_of?(Reduce)
|
||||
puts 'DEBUG -------------------------------'
|
||||
p stok
|
||||
p act
|
||||
state.action.each do |k,v|
|
||||
print k.inspect, ' ', v.inspect, "\n"
|
||||
end
|
||||
raise "racc: fatal: #{act.class} in action table"
|
||||
end
|
||||
|
||||
# conflict on stok
|
||||
|
||||
rtok = act.rule.precedence
|
||||
case do_resolve_sr(stok, rtok)
|
||||
when :Reduce
|
||||
# action is already set
|
||||
|
||||
when :Shift
|
||||
# overwrite
|
||||
act.decref
|
||||
state.action[stok] = @actions.shift(goto)
|
||||
|
||||
when :Error
|
||||
act.decref
|
||||
state.action[stok] = @actions.error
|
||||
|
||||
when :CantResolve
|
||||
# shift as default
|
||||
act.decref
|
||||
state.action[stok] = @actions.shift(goto)
|
||||
state.sr_conflict stok, act.rule
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ASSOC = {
|
||||
:Left => :Reduce,
|
||||
:Right => :Shift,
|
||||
:Nonassoc => :Error
|
||||
}
|
||||
|
||||
def do_resolve_sr(stok, rtok)
|
||||
puts "resolve_sr: s/r conflict: rtok=#{rtok}, stok=#{stok}" if @d_prec
|
||||
|
||||
unless rtok and rtok.precedence
|
||||
puts "resolve_sr: no prec for #{rtok}(R)" if @d_prec
|
||||
return :CantResolve
|
||||
end
|
||||
rprec = rtok.precedence
|
||||
|
||||
unless stok and stok.precedence
|
||||
puts "resolve_sr: no prec for #{stok}(S)" if @d_prec
|
||||
return :CantResolve
|
||||
end
|
||||
sprec = stok.precedence
|
||||
|
||||
ret = if rprec == sprec
|
||||
ASSOC[rtok.assoc] or
|
||||
raise "racc: fatal: #{rtok}.assoc is not Left/Right/Nonassoc"
|
||||
else
|
||||
(rprec > sprec) ? (:Reduce) : (:Shift)
|
||||
end
|
||||
|
||||
puts "resolve_sr: resolved as #{ret.id2name}" if @d_prec
|
||||
ret
|
||||
end
|
||||
|
||||
#
|
||||
# complete
|
||||
#
|
||||
|
||||
def set_accept
|
||||
anch = @symboltable.anchor
|
||||
init_state = @states[0].goto_table[@grammar.start]
|
||||
targ_state = init_state.action[anch].goto_state
|
||||
acc_state = targ_state.action[anch].goto_state
|
||||
|
||||
acc_state.action.clear
|
||||
acc_state.goto_table.clear
|
||||
acc_state.defact = @actions.accept
|
||||
end
|
||||
|
||||
def pack(state)
|
||||
### find most frequently used reduce rule
|
||||
act = state.action
|
||||
arr = Array.new(@grammar.size, 0)
|
||||
act.each do |t, a|
|
||||
arr[a.ruleid] += 1 if a.kind_of?(Reduce)
|
||||
end
|
||||
i = arr.max
|
||||
s = (i > 0) ? arr.index(i) : nil
|
||||
|
||||
### set & delete default action
|
||||
if s
|
||||
r = @actions.reduce(s)
|
||||
if not state.defact or state.defact == r
|
||||
act.delete_if {|t, a| a == r }
|
||||
state.defact = r
|
||||
end
|
||||
else
|
||||
state.defact ||= @actions.error
|
||||
end
|
||||
end
|
||||
|
||||
def check_useless
|
||||
used = []
|
||||
@actions.each_reduce do |act|
|
||||
if not act or act.refn == 0
|
||||
act.rule.useless = true
|
||||
else
|
||||
t = act.rule.target
|
||||
used[t.ident] = t
|
||||
end
|
||||
end
|
||||
@symboltable.nt_base.upto(@symboltable.nt_max - 1) do |n|
|
||||
unless used[n]
|
||||
@symboltable[n].useless = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end # class StateTable
|
||||
|
||||
|
||||
# A LALR state.
|
||||
class State
|
||||
|
||||
def initialize(ident, core)
|
||||
@ident = ident
|
||||
@core = core
|
||||
@goto_table = {}
|
||||
@gotos = {}
|
||||
@stokens = nil
|
||||
@ritems = nil
|
||||
@action = {}
|
||||
@defact = nil
|
||||
@rrconf = nil
|
||||
@srconf = nil
|
||||
|
||||
@closure = make_closure(@core)
|
||||
end
|
||||
|
||||
attr_reader :ident
|
||||
alias stateid ident
|
||||
alias hash ident
|
||||
|
||||
attr_reader :core
|
||||
attr_reader :closure
|
||||
|
||||
attr_reader :goto_table
|
||||
attr_reader :gotos
|
||||
|
||||
attr_reader :stokens
|
||||
attr_reader :ritems
|
||||
attr_reader :rrules
|
||||
|
||||
attr_reader :action
|
||||
attr_accessor :defact # default action
|
||||
|
||||
attr_reader :rrconf
|
||||
attr_reader :srconf
|
||||
|
||||
def inspect
|
||||
"<state #{@ident}>"
|
||||
end
|
||||
|
||||
alias to_s inspect
|
||||
|
||||
def ==(oth)
|
||||
@ident == oth.ident
|
||||
end
|
||||
|
||||
alias eql? ==
|
||||
|
||||
def make_closure(core)
|
||||
set = ISet.new
|
||||
core.each do |ptr|
|
||||
set.add ptr
|
||||
if t = ptr.dereference and t.nonterminal?
|
||||
set.update_a t.expand
|
||||
end
|
||||
end
|
||||
set.to_a
|
||||
end
|
||||
|
||||
def check_la(la_rules)
|
||||
@conflict = false
|
||||
s = []
|
||||
r = []
|
||||
@closure.each do |ptr|
|
||||
if t = ptr.dereference
|
||||
if t.terminal?
|
||||
s[t.ident] = t
|
||||
if t.ident == 1 # $error
|
||||
@conflict = true
|
||||
end
|
||||
end
|
||||
else
|
||||
r.push ptr.rule
|
||||
end
|
||||
end
|
||||
unless r.empty?
|
||||
if not s.empty? or r.size > 1
|
||||
@conflict = true
|
||||
end
|
||||
end
|
||||
s.compact!
|
||||
@stokens = s
|
||||
@rrules = r
|
||||
|
||||
if @conflict
|
||||
@la_rules_i = la_rules.size
|
||||
@la_rules = r.map {|i| i.ident }
|
||||
la_rules.concat r
|
||||
else
|
||||
@la_rules_i = @la_rules = nil
|
||||
end
|
||||
end
|
||||
|
||||
def conflict?
|
||||
@conflict
|
||||
end
|
||||
|
||||
def rruleid(rule)
|
||||
if i = @la_rules.index(rule.ident)
|
||||
@la_rules_i + i
|
||||
else
|
||||
puts '/// rruleid'
|
||||
p self
|
||||
p rule
|
||||
p @rrules
|
||||
p @la_rules_i
|
||||
raise 'racc: fatal: cannot get reduce rule id'
|
||||
end
|
||||
end
|
||||
|
||||
def la=(la)
|
||||
return unless @conflict
|
||||
i = @la_rules_i
|
||||
@ritems = r = []
|
||||
@rrules.each do |rule|
|
||||
r.push Item.new(rule, la[i])
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
|
||||
def rr_conflict(high, low, ctok)
|
||||
c = RRconflict.new(@ident, high, low, ctok)
|
||||
|
||||
@rrconf ||= {}
|
||||
if a = @rrconf[ctok]
|
||||
a.push c
|
||||
else
|
||||
@rrconf[ctok] = [c]
|
||||
end
|
||||
end
|
||||
|
||||
def sr_conflict(shift, reduce)
|
||||
c = SRconflict.new(@ident, shift, reduce)
|
||||
|
||||
@srconf ||= {}
|
||||
if a = @srconf[shift]
|
||||
a.push c
|
||||
else
|
||||
@srconf[shift] = [c]
|
||||
end
|
||||
end
|
||||
|
||||
def n_srconflicts
|
||||
@srconf ? @srconf.size : 0
|
||||
end
|
||||
|
||||
def n_rrconflicts
|
||||
@rrconf ? @rrconf.size : 0
|
||||
end
|
||||
|
||||
end # class State
|
||||
|
||||
|
||||
#
|
||||
# Represents a transition on the grammar.
|
||||
# "Real goto" means a transition by nonterminal,
|
||||
# but this class treats also terminal's.
|
||||
# If one is a terminal transition, .ident returns nil.
|
||||
#
|
||||
class Goto
|
||||
def initialize(ident, sym, from, to)
|
||||
@ident = ident
|
||||
@symbol = sym
|
||||
@from_state = from
|
||||
@to_state = to
|
||||
end
|
||||
|
||||
attr_reader :ident
|
||||
attr_reader :symbol
|
||||
attr_reader :from_state
|
||||
attr_reader :to_state
|
||||
|
||||
def inspect
|
||||
"(#{@from_state.ident}-#{@symbol}->#{@to_state.ident})"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# LALR item. A set of rule and its lookahead tokens.
|
||||
class Item
|
||||
def initialize(rule, la)
|
||||
@rule = rule
|
||||
@la = la
|
||||
end
|
||||
|
||||
attr_reader :rule
|
||||
attr_reader :la
|
||||
|
||||
def each_la(tbl)
|
||||
la = @la
|
||||
0.upto(la.size - 1) do |i|
|
||||
(0..7).each do |ii|
|
||||
if la[idx = i * 8 + ii] == 1
|
||||
yield tbl[idx]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# The table of LALR actions. Actions are either of
|
||||
# Shift, Reduce, Accept and Error.
|
||||
class ActionTable
|
||||
|
||||
def initialize(rt, st)
|
||||
@grammar = rt
|
||||
@statetable = st
|
||||
|
||||
@reduce = []
|
||||
@shift = []
|
||||
@accept = nil
|
||||
@error = nil
|
||||
end
|
||||
|
||||
def init
|
||||
@grammar.each do |rule|
|
||||
@reduce.push Reduce.new(rule)
|
||||
end
|
||||
@statetable.each do |state|
|
||||
@shift.push Shift.new(state)
|
||||
end
|
||||
@accept = Accept.new
|
||||
@error = Error.new
|
||||
end
|
||||
|
||||
def reduce_n
|
||||
@reduce.size
|
||||
end
|
||||
|
||||
def reduce(i)
|
||||
case i
|
||||
when Rule then i = i.ident
|
||||
when Integer then ;
|
||||
else
|
||||
raise "racc: fatal: wrong class #{i.class} for reduce"
|
||||
end
|
||||
|
||||
r = @reduce[i] or raise "racc: fatal: reduce action #{i.inspect} not exist"
|
||||
r.incref
|
||||
r
|
||||
end
|
||||
|
||||
def each_reduce(&block)
|
||||
@reduce.each(&block)
|
||||
end
|
||||
|
||||
def shift_n
|
||||
@shift.size
|
||||
end
|
||||
|
||||
def shift(i)
|
||||
case i
|
||||
when State then i = i.ident
|
||||
when Integer then ;
|
||||
else
|
||||
raise "racc: fatal: wrong class #{i.class} for shift"
|
||||
end
|
||||
|
||||
@shift[i] or raise "racc: fatal: shift action #{i} does not exist"
|
||||
end
|
||||
|
||||
def each_shift(&block)
|
||||
@shift.each(&block)
|
||||
end
|
||||
|
||||
attr_reader :accept
|
||||
attr_reader :error
|
||||
|
||||
end
|
||||
|
||||
|
||||
class Shift
|
||||
def initialize(goto)
|
||||
@goto_state = goto
|
||||
end
|
||||
|
||||
attr_reader :goto_state
|
||||
|
||||
def goto_id
|
||||
@goto_state.ident
|
||||
end
|
||||
|
||||
def inspect
|
||||
"<shift #{@goto_state.ident}>"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Reduce
|
||||
def initialize(rule)
|
||||
@rule = rule
|
||||
@refn = 0
|
||||
end
|
||||
|
||||
attr_reader :rule
|
||||
attr_reader :refn
|
||||
|
||||
def ruleid
|
||||
@rule.ident
|
||||
end
|
||||
|
||||
def inspect
|
||||
"<reduce #{@rule.ident}>"
|
||||
end
|
||||
|
||||
def incref
|
||||
@refn += 1
|
||||
end
|
||||
|
||||
def decref
|
||||
@refn -= 1
|
||||
raise 'racc: fatal: act.refn < 0' if @refn < 0
|
||||
end
|
||||
end
|
||||
|
||||
class Accept
|
||||
def inspect
|
||||
"<accept>"
|
||||
end
|
||||
end
|
||||
|
||||
class Error
|
||||
def inspect
|
||||
"<error>"
|
||||
end
|
||||
end
|
||||
|
||||
class SRconflict
|
||||
def initialize(sid, shift, reduce)
|
||||
@stateid = sid
|
||||
@shift = shift
|
||||
@reduce = reduce
|
||||
end
|
||||
|
||||
attr_reader :stateid
|
||||
attr_reader :shift
|
||||
attr_reader :reduce
|
||||
|
||||
def to_s
|
||||
sprintf('state %d: S/R conflict rule %d reduce and shift %s',
|
||||
@stateid, @reduce.ruleid, @shift.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
class RRconflict
|
||||
def initialize(sid, high, low, tok)
|
||||
@stateid = sid
|
||||
@high_prec = high
|
||||
@low_prec = low
|
||||
@token = tok
|
||||
end
|
||||
|
||||
attr_reader :stateid
|
||||
attr_reader :high_prec
|
||||
attr_reader :low_prec
|
||||
attr_reader :token
|
||||
|
||||
def to_s
|
||||
sprintf('state %d: R/R conflict with rule %d and %d on %s',
|
||||
@stateid, @high_prec.ident, @low_prec.ident, @token.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user