diff --git a/lib/imbecile.rb b/lib/imbecile.rb index 9524a48..2099602 100644 --- a/lib/imbecile.rb +++ b/lib/imbecile.rb @@ -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" diff --git a/lib/imbecile/regex/dfa.rb b/lib/imbecile/regex/dfa.rb index 65a93cf..00db7f7 100644 --- a/lib/imbecile/regex/dfa.rb +++ b/lib/imbecile/regex/dfa.rb @@ -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 diff --git a/lib/imbecile/regex/fa.rb b/lib/imbecile/regex/fa.rb new file mode 100644 index 0000000..d242bd5 --- /dev/null +++ b/lib/imbecile/regex/fa.rb @@ -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 diff --git a/lib/imbecile/regex/fa/state.rb b/lib/imbecile/regex/fa/state.rb new file mode 100644 index 0000000..ee4fab9 --- /dev/null +++ b/lib/imbecile/regex/fa/state.rb @@ -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] + # 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 diff --git a/lib/imbecile/regex/fa/state/transition.rb b/lib/imbecile/regex/fa/state/transition.rb new file mode 100644 index 0000000..5bd0efb --- /dev/null +++ b/lib/imbecile/regex/fa/state/transition.rb @@ -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 diff --git a/lib/imbecile/regex/nfa.rb b/lib/imbecile/regex/nfa.rb index f5d97ae..824ed04 100644 --- a/lib/imbecile/regex/nfa.rb +++ b/lib/imbecile/regex/nfa.rb @@ -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 - - 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] - # 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 + attr_reader :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