69 lines
1.8 KiB
Ruby
69 lines
1.8 KiB
Ruby
class Propane
|
|
class Lexer
|
|
|
|
attr_reader :state_table
|
|
attr_reader :transition_table
|
|
attr_reader :mode_table
|
|
|
|
def initialize(grammar)
|
|
@grammar = grammar
|
|
build_tables!
|
|
end
|
|
|
|
# Get ID for a mode.
|
|
#
|
|
# @param mode_name [String]
|
|
# Mode name.
|
|
#
|
|
# @return [Integer, nil]
|
|
# Mode ID.
|
|
def mode_id(mode_name)
|
|
if mode_info = @modes[mode_name]
|
|
mode_info[:id]
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def build_tables!
|
|
@modes = @grammar.patterns.group_by do |pattern|
|
|
pattern.mode
|
|
end.transform_values do |patterns|
|
|
{dfa: DFA.new(patterns)}
|
|
end
|
|
@modes.each_with_index do |(mode_name, mode_info), index|
|
|
mode_info[:id] = index
|
|
end
|
|
@state_table = []
|
|
@transition_table = []
|
|
@mode_table = []
|
|
@modes.each do |mode_name, mode_info|
|
|
state_table_offset = @state_table.size
|
|
@mode_table << {
|
|
state_table_offset: state_table_offset,
|
|
}
|
|
states = mode_info[:dfa].enumerate
|
|
states.each do |state, id|
|
|
token = state.accepts && state.accepts.token && state.accepts.token.id
|
|
code_id = state.accepts && state.accepts.code_id && state.accepts.code_id
|
|
@state_table << {
|
|
transition_table_index: @transition_table.size,
|
|
n_transitions: state.transitions.size,
|
|
accepts: !!state.accepts,
|
|
token: token,
|
|
code_id: code_id,
|
|
}
|
|
state.transitions.each do |transition|
|
|
@transition_table << {
|
|
first: transition.code_point_range.first,
|
|
last: transition.code_point_range.last,
|
|
destination: states[transition.destination] + state_table_offset,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|