Move FA class out of Regex class

This commit is contained in:
Josh Holtrop 2021-08-18 17:05:03 -04:00
parent 37d6917b49
commit 28591907c1
8 changed files with 169 additions and 175 deletions

View File

@ -2,17 +2,17 @@ require "erb"
require "set" require "set"
require_relative "imbecile/cli" require_relative "imbecile/cli"
require_relative "imbecile/code_point_range" require_relative "imbecile/code_point_range"
require_relative "imbecile/fa"
require_relative "imbecile/fa/state"
require_relative "imbecile/fa/state/transition"
require_relative "imbecile/generator" require_relative "imbecile/generator"
require_relative "imbecile/grammar" require_relative "imbecile/grammar"
require_relative "imbecile/grammar/rule" require_relative "imbecile/grammar/rule"
require_relative "imbecile/grammar/token" require_relative "imbecile/grammar/token"
require_relative "imbecile/lexer_dfa"
require_relative "imbecile/regex" 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/nfa" require_relative "imbecile/regex/nfa"
require_relative "imbecile/regex/unit" require_relative "imbecile/regex/unit"
require_relative "imbecile/lexer_dfa"
require_relative "imbecile/version" require_relative "imbecile/version"
module Imbecile module Imbecile

90
lib/imbecile/fa.rb Normal file
View File

@ -0,0 +1,90 @@
module Imbecile
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 = enumerate
states.each do |state, id|
accepts_s = state.accepts ? " #{state.accepts}" : ""
rv += "#{id}#{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 ? " #{transition.destination.accepts}" : ""
rv += " #{range_s} => #{states[transition.destination]}#{accepts_s}\n"
end
end
rv
end
def enumerate
@_enumerated ||=
begin
id = 0
states = {}
visit = lambda do |state|
unless states.include?(state)
states[state] = id
id += 1
state.transitions.each do |transition|
visit[transition.destination]
end
end
end
visit[@start_state]
states
end
end
def build_tables
transition_table = []
state_table = []
states = enumerate
states.each do |state, id|
accepts =
if state.accepts.nil?
TOKEN_NONE
elsif state.accepts.name
state.accepts.id
else
TOKEN_DROP
end
state_table << {
transition_table_index: transition_table.size,
n_transitions: state.transitions.size,
accepts: accepts,
}
state.transitions.each do |transition|
transition_table << {
first: transition.code_point_range.first,
last: transition.code_point_range.last,
destination: states[transition.destination],
}
end
end
[transition_table, state_table]
end
end
end

51
lib/imbecile/fa/state.rb Normal file
View File

@ -0,0 +1,51 @@
module Imbecile
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

View File

@ -0,0 +1,23 @@
module Imbecile
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

View File

@ -1,6 +1,6 @@
module Imbecile module Imbecile
class LexerDFA < Regex::FA class LexerDFA < FA
def initialize(tokens) def initialize(tokens)
super() super()

View File

@ -1,92 +0,0 @@
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 = enumerate
states.each do |state, id|
accepts_s = state.accepts ? " #{state.accepts}" : ""
rv += "#{id}#{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 ? " #{transition.destination.accepts}" : ""
rv += " #{range_s} => #{states[transition.destination]}#{accepts_s}\n"
end
end
rv
end
def enumerate
@_enumerated ||=
begin
id = 0
states = {}
visit = lambda do |state|
unless states.include?(state)
states[state] = id
id += 1
state.transitions.each do |transition|
visit[transition.destination]
end
end
end
visit[@start_state]
states
end
end
def build_tables
transition_table = []
state_table = []
states = enumerate
states.each do |state, id|
accepts =
if state.accepts.nil?
TOKEN_NONE
elsif state.accepts.name
state.accepts.id
else
TOKEN_DROP
end
state_table << {
transition_table_index: transition_table.size,
n_transitions: state.transitions.size,
accepts: accepts,
}
state.transitions.each do |transition|
transition_table << {
first: transition.code_point_range.first,
last: transition.code_point_range.last,
destination: states[transition.destination],
}
end
end
[transition_table, state_table]
end
end
end
end

View File

@ -1,53 +0,0 @@
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

View File

@ -1,25 +0,0 @@
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