Create common FA/State/Transition classes across NFA/DFA
This commit is contained in:
parent
1228a76c55
commit
ed3f599e25
@ -4,6 +4,9 @@ require_relative "imbecile/cli"
|
||||
require_relative "imbecile/code_point_range"
|
||||
require_relative "imbecile/grammar"
|
||||
require_relative "imbecile/regex"
|
||||
require_relative "imbecile/regex/fa"
|
||||
require_relative "imbecile/regex/fa/state"
|
||||
require_relative "imbecile/regex/fa/state/transition"
|
||||
require_relative "imbecile/regex/dfa"
|
||||
require_relative "imbecile/regex/nfa"
|
||||
require_relative "imbecile/regex/unit"
|
||||
|
@ -1,39 +1,10 @@
|
||||
module Imbecile
|
||||
class Regex
|
||||
|
||||
class DFA
|
||||
|
||||
class State
|
||||
|
||||
attr_reader :id
|
||||
|
||||
class Transition
|
||||
|
||||
attr_reader :code_point_range
|
||||
attr_reader :destination
|
||||
|
||||
def initialize(code_point_range, destination)
|
||||
@code_point_range = code_point_range
|
||||
@destination = destination
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
attr_accessor :accepts
|
||||
attr_reader :transitions
|
||||
|
||||
def initialize(id)
|
||||
@id = id
|
||||
@transitions = []
|
||||
end
|
||||
|
||||
def add_transition(code_point_range, destination)
|
||||
@transitions << Transition.new(code_point_range, destination)
|
||||
end
|
||||
|
||||
end
|
||||
class DFA < FA
|
||||
|
||||
def initialize(nfas)
|
||||
super()
|
||||
start_nfa = NFA.new
|
||||
nfas.each do |nfa|
|
||||
start_nfa.start_state.add_transition(nil, nfa.start_state)
|
||||
@ -48,30 +19,7 @@ module Imbecile
|
||||
@to_process.delete(state_set)
|
||||
process_nfa_state_set(state_set)
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
chr = lambda do |value|
|
||||
if value < 32 || value > 127
|
||||
"{#{value}}"
|
||||
else
|
||||
value.chr
|
||||
end
|
||||
end
|
||||
rv = ""
|
||||
@states.each_with_index do |state, state_id|
|
||||
accepts_s = state.accepts ? " (#{state.accepts})" : ""
|
||||
rv += "#{state_id}#{accepts_s}:\n"
|
||||
state.transitions.each do |transition|
|
||||
range_s = chr[transition.code_point_range.first]
|
||||
if transition.code_point_range.size > 1
|
||||
range_s += "-" + chr[transition.code_point_range.last]
|
||||
end
|
||||
accepts_s = transition.destination.accepts ? " (#{transition.destination.accepts})" : ""
|
||||
rv += " #{range_s} => #{transition.destination.id}#{accepts_s}\n"
|
||||
end
|
||||
end
|
||||
rv
|
||||
@start_state = @states[0]
|
||||
end
|
||||
|
||||
private
|
||||
@ -80,7 +28,7 @@ module Imbecile
|
||||
unless @nfa_state_sets.include?(nfa_state_set)
|
||||
state_id = @states.size
|
||||
@nfa_state_sets[nfa_state_set] = state_id
|
||||
@states << State.new(state_id)
|
||||
@states << State.new
|
||||
@to_process << nfa_state_set
|
||||
end
|
||||
end
|
||||
|
56
lib/imbecile/regex/fa.rb
Normal file
56
lib/imbecile/regex/fa.rb
Normal file
@ -0,0 +1,56 @@
|
||||
module Imbecile
|
||||
class Regex
|
||||
|
||||
class FA
|
||||
|
||||
attr_reader :start_state
|
||||
|
||||
def initialize
|
||||
@start_state = State.new
|
||||
end
|
||||
|
||||
def to_s
|
||||
chr = lambda do |value|
|
||||
if value < 32 || value > 127
|
||||
"{#{value}}"
|
||||
else
|
||||
value.chr
|
||||
end
|
||||
end
|
||||
rv = ""
|
||||
states = {start_state => 0}
|
||||
to_visit = [start_state]
|
||||
state_id = lambda do |state|
|
||||
unless states.include?(state)
|
||||
states[state] = states.values.max + 1
|
||||
to_visit << state
|
||||
end
|
||||
states[state]
|
||||
end
|
||||
visit = lambda do |state|
|
||||
accepts_s = state.accepts ? " *" : ""
|
||||
rv += "#{state_id[state]}#{accepts_s}:\n"
|
||||
state.transitions.each do |transition|
|
||||
if transition.nil?
|
||||
range_s = "nil"
|
||||
else
|
||||
range_s = chr[transition.code_point_range.first]
|
||||
if transition.code_point_range.size > 1
|
||||
range_s += "-" + chr[transition.code_point_range.last]
|
||||
end
|
||||
end
|
||||
accepts_s = transition.destination.accepts ? " *" : ""
|
||||
rv += " #{range_s} => #{state_id[transition.destination]}#{accepts_s}\n"
|
||||
end
|
||||
end
|
||||
while to_visit.size > 0
|
||||
visit[to_visit[0]]
|
||||
to_visit.slice!(0)
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
53
lib/imbecile/regex/fa/state.rb
Normal file
53
lib/imbecile/regex/fa/state.rb
Normal file
@ -0,0 +1,53 @@
|
||||
module Imbecile
|
||||
class Regex
|
||||
class FA
|
||||
|
||||
class State
|
||||
|
||||
attr_accessor :accepts
|
||||
attr_reader :transitions
|
||||
|
||||
def initialize
|
||||
@transitions = []
|
||||
end
|
||||
|
||||
def add_transition(code_point_range, destination)
|
||||
@transitions << Transition.new(code_point_range, destination)
|
||||
end
|
||||
|
||||
# Determine the set of states that can be reached by nil transitions.
|
||||
# from this state.
|
||||
#
|
||||
# @return [Set<NFA::State>]
|
||||
# Set of states.
|
||||
def nil_transition_states
|
||||
states = Set[self]
|
||||
analyze_state = lambda do |state|
|
||||
state.nil_transitions.each do |transition|
|
||||
unless states.include?(transition.destination)
|
||||
states << transition.destination
|
||||
analyze_state[transition.destination]
|
||||
end
|
||||
end
|
||||
end
|
||||
analyze_state[self]
|
||||
states
|
||||
end
|
||||
|
||||
def nil_transitions
|
||||
@transitions.select do |transition|
|
||||
transition.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def cp_transitions
|
||||
@transitions.reject do |transition|
|
||||
transition.nil?
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
25
lib/imbecile/regex/fa/state/transition.rb
Normal file
25
lib/imbecile/regex/fa/state/transition.rb
Normal file
@ -0,0 +1,25 @@
|
||||
module Imbecile
|
||||
class Regex
|
||||
class FA
|
||||
class State
|
||||
|
||||
class Transition
|
||||
|
||||
attr_reader :code_point_range
|
||||
attr_reader :destination
|
||||
|
||||
def initialize(code_point_range, destination)
|
||||
@code_point_range = code_point_range
|
||||
@destination = destination
|
||||
end
|
||||
|
||||
def nil?
|
||||
@code_point_range.nil?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,120 +1,15 @@
|
||||
module Imbecile
|
||||
class Regex
|
||||
|
||||
class NFA
|
||||
class NFA < FA
|
||||
|
||||
class State
|
||||
|
||||
class Transition
|
||||
|
||||
attr_reader :code_point_range
|
||||
attr_reader :destination
|
||||
|
||||
def initialize(code_point_range, destination)
|
||||
@code_point_range = code_point_range
|
||||
@destination = destination
|
||||
end
|
||||
|
||||
def nil?
|
||||
@code_point_range.nil?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
attr_accessor :accepts
|
||||
attr_reader :transitions
|
||||
attr_reader :end_state
|
||||
|
||||
def initialize
|
||||
@transitions = []
|
||||
end
|
||||
|
||||
def add_transition(code_point_range, destination)
|
||||
@transitions << Transition.new(code_point_range, destination)
|
||||
end
|
||||
|
||||
# Determine the set of states that can be reached by nil transitions.
|
||||
# from this state.
|
||||
#
|
||||
# @return [Set<NFA::State>]
|
||||
# Set of states.
|
||||
def nil_transition_states
|
||||
states = Set[self]
|
||||
analyze_state = lambda do |state|
|
||||
state.nil_transitions.each do |transition|
|
||||
unless states.include?(transition.destination)
|
||||
states << transition.destination
|
||||
analyze_state[transition.destination]
|
||||
end
|
||||
end
|
||||
end
|
||||
analyze_state[self]
|
||||
states
|
||||
end
|
||||
|
||||
def nil_transitions
|
||||
@transitions.select do |transition|
|
||||
transition.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def cp_transitions
|
||||
@transitions.reject do |transition|
|
||||
transition.nil?
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
attr_accessor :start_state
|
||||
|
||||
attr_accessor :end_state
|
||||
|
||||
def initialize
|
||||
@start_state = State.new
|
||||
super()
|
||||
@end_state = State.new
|
||||
end
|
||||
|
||||
def to_s
|
||||
chr = lambda do |value|
|
||||
if value < 32 || value > 127
|
||||
"{#{value}}"
|
||||
else
|
||||
value.chr
|
||||
end
|
||||
end
|
||||
rv = ""
|
||||
states = {start_state => 0}
|
||||
to_visit = [start_state]
|
||||
state_id = lambda do |state|
|
||||
unless states.include?(state)
|
||||
states[state] = states.values.max + 1
|
||||
to_visit << state
|
||||
end
|
||||
states[state]
|
||||
end
|
||||
visit = lambda do |state|
|
||||
accepts_s = state.accepts ? " *" : ""
|
||||
rv += "#{state_id[state]}#{accepts_s}:\n"
|
||||
state.transitions.each do |transition|
|
||||
if transition.nil?
|
||||
range_s = "nil"
|
||||
else
|
||||
range_s = chr[transition.code_point_range.first]
|
||||
if transition.code_point_range.size > 1
|
||||
range_s += "-" + chr[transition.code_point_range.last]
|
||||
end
|
||||
end
|
||||
accepts_s = transition.destination.accepts ? " *" : ""
|
||||
rv += " #{range_s} => #{state_id[transition.destination]}#{accepts_s}\n"
|
||||
end
|
||||
end
|
||||
while to_visit.size > 0
|
||||
visit[to_visit[0]]
|
||||
to_visit.slice!(0)
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
class << self
|
||||
|
||||
def empty
|
||||
|
Loading…
x
Reference in New Issue
Block a user