Add NFA::Transition, start on DFA construction

This commit is contained in:
Josh Holtrop 2021-05-23 21:41:50 -04:00
parent 8473df421a
commit 214ece7d90
2 changed files with 71 additions and 15 deletions

View File

@ -3,12 +3,53 @@ module Imbecile
class DFA class DFA
class State
end
def initialize(nfas) def initialize(nfas)
start_nfa = NFA.new start_nfa = NFA.new
nfas.each do |nfa| nfas.each do |nfa|
start_nfa.start_state.add_transition(nil, nfa.start_state) start_nfa.start_state.add_transition(nil, nfa.start_state)
end end
@states = {}
@to_process = Set.new
nil_transition_states = start_nfa.start_state.nil_transition_states nil_transition_states = start_nfa.start_state.nil_transition_states
@states[nil_transition_states] = 0
process_nfa_state_set(nil_transition_states)
end
private
def process_nfa_state_set(nfa_state_set)
transitions = transitions_for(nfa_state_set)
while transitions.size > 0
subrange = CodePointRange.first_subrange(transitions.map(&:code_point_range))
dest_nfa_states = transitions.reduce(Set.new) do |result, transition|
if transition.code_point_range.include?(subrange)
result << transition.last
end
result
end
unless @states.include?(dest_nfa_states)
@to_process << dest_nfa_states
end
transitions.delete_if do |transition|
transition.code_point_range.last <= subrange.last
end
transitions.map! do |transition|
if transition.code_point_range.first <= subrange.last
Transition.new(CodePointRange.new(subrange.last + 1, transition.code_point_range.last), transition.destination)
else
transition
end
end
end
end
def transitions_for(states)
states.reduce([]) do |result, state|
result + state.cp_transitions
end
end end
end end

View File

@ -5,6 +5,21 @@ module Imbecile
class State class State
class Transition
attr_reader :code_point_range
attr_reader :destination
def initialize(code_point_range, destination)
@code_point_range = code_point_range
end
def nil?
@code_point_range.nil?
end
end
attr_accessor :accepts attr_accessor :accepts
attr_reader :transitions attr_reader :transitions
@ -12,8 +27,8 @@ module Imbecile
@transitions = [] @transitions = []
end end
def add_transition(code_point, destination_state) def add_transition(code_point_range, destination)
@transitions << [code_point, destination_state] @transitions << Transition.new(code_point_range, destination)
end end
# Determine the set of states that can be reached by nil transitions. # Determine the set of states that can be reached by nil transitions.
@ -24,10 +39,10 @@ module Imbecile
def nil_transition_states def nil_transition_states
states = Set[self] states = Set[self]
analyze_state = lambda do |state| analyze_state = lambda do |state|
state.nil_transitions.each do |range, dest_state| state.nil_transitions.each do |transition|
unless states.include?(dest_state) unless states.include?(transition.destination)
states << dest_state states << transition.destination
analyze_state[dest_state] analyze_state[transition.destination]
end end
end end
end end
@ -36,14 +51,14 @@ module Imbecile
end end
def nil_transitions def nil_transitions
@transitions.select do |code_point, dest_state| @transitions.select do |transition|
code_point.nil? transition.nil?
end end
end end
def cp_transitions def cp_transitions
@transitions.select do |code_point, dest_state| @transitions.reject do |transition|
code_point transition.nil?
end end
end end
@ -79,13 +94,13 @@ module Imbecile
visit = lambda do |state| visit = lambda do |state|
accepts_s = state.accepts ? " *" : "" accepts_s = state.accepts ? " *" : ""
rv += "#{state_id[state]}#{accepts_s}:\n" rv += "#{state_id[state]}#{accepts_s}:\n"
state.transitions.each do |code_point_range, dest_state| state.transitions.each do |transition|
if code_point_range.nil? if transition.nil?
range_s = "nil" range_s = "nil"
else else
range_s = chr[code_point_range.first] range_s = chr[transition.code_point_range.first]
if code_point_range.size > 1 if transition.code_point_range.size > 1
range_s += "-" + chr[code_point_range.last] range_s += "-" + chr[transition.code_point_range.last]
end end
end end
accepts_s = dest_state.accepts ? " *" : "" accepts_s = dest_state.accepts ? " *" : ""