One Rule object stores all alternative patterns

This commit is contained in:
Josh Holtrop 2021-08-28 10:28:50 -04:00
parent e4f2fffe50
commit 9cc1890ddc
4 changed files with 50 additions and 36 deletions

View File

@ -36,7 +36,7 @@ class Imbecile
def initialize(input) def initialize(input)
@tokens = [] @tokens = []
@rules = [] @rules = {}
input = input.gsub("\r\n", "\n") input = input.gsub("\r\n", "\n")
while !input.empty? while !input.empty?
parse_grammar(input) parse_grammar(input)
@ -79,9 +79,10 @@ class Imbecile
pattern = $1 pattern = $1
@tokens << Token.new(nil, pattern, @tokens.size) @tokens << Token.new(nil, pattern, @tokens.size)
elsif input.slice!(/\A(\S+)\s*:\s*\[(.*?)\] <<\n(.*?)^>>\n/m) elsif input.slice!(/\A(\S+)\s*:\s*\[(.*?)\] <<\n(.*?)^>>\n/m)
rule_name, rule, code = $1, $2, $3 rule_name, components, code = $1, $2, $3
rule = rule.strip.split(/\s+/) components = components.strip.split(/\s+/)
@rules << Rule.new(rule_name, rule, code) @rules[rule_name] ||= Rule.new(rule_name)
@rules[rule_name].add_pattern(components, code)
else else
if input.size > 25 if input.size > 25
input = input.slice(0..20) + "..." input = input.slice(0..20) + "..."
@ -97,28 +98,28 @@ class Imbecile
end end
token_names[token.name] = token token_names[token.name] = token
end end
rule_names = @rules.each_with_object({}) do |rule, rule_names| @rules.each do |rule_name, rule|
if token_names.include?(rule.name) if token_names.include?(rule_name)
raise Error.new("Rule name collides with token name #{rule.name}") raise Error.new("Rule name collides with token name #{rule_name}")
end end
rule_names[rule.name] ||= []
rule_names[rule.name] << rule
end end
unless rule_names["Start"] unless @rules["Start"]
raise Error.new("Start rule not found") raise Error.new("Start rule not found")
end end
@rules.each do |rule| @rules.each do |rule_name, rule|
rule.components.map! do |component| rule.patterns.each do |pattern|
pattern.components.map! do |component|
if token_names[component] if token_names[component]
token_names[component] token_names[component]
elsif rule_names[component] elsif @rules[component]
rule_names[component] @rules[component]
else else
raise Error.new("Symbol #{component} not found") raise Error.new("Symbol #{component} not found")
end end
end end
end end
end end
end
class << self class << self

View File

@ -3,9 +3,8 @@ class Imbecile
class Parser class Parser
def initialize(tokens, rules) def initialize(tokens, rules)
start_rules = rules.select {|rule| rule.name == "Start"} start_items = rules["Start"].patterns.map do |pattern|
start_items = start_rules.map do |rule| Item.new(pattern, 0)
Item.new(rule, 0)
end end
start_item_set = ItemSet.new(start_items) start_item_set = ItemSet.new(start_items)
start_item_set.close! start_item_set.close!

View File

@ -3,24 +3,24 @@ class Imbecile
class Item class Item
attr_reader :rule attr_reader :pattern
attr_reader :position attr_reader :position
def initialize(rule, position) def initialize(pattern, position)
@rule = rule @pattern = pattern
@position = position @position = position
end end
def next_component def next_component
@rule.components[@position] @pattern.components[@position]
end end
def hash def hash
[@rule, @position].hash [@pattern, @position].hash
end end
def ==(other) def ==(other)
@rule == other.rule && @position == other.position @pattern == other.pattern && @position == other.position
end end
def eql?(other) def eql?(other)
@ -28,9 +28,9 @@ class Imbecile
end end
def closed_items def closed_items
if @rule.components[@position].is_a?(Array) if @pattern.components[@position].is_a?(Rule)
@rule.components[@position].map do |rule| @pattern.components[@position].patterns.map do |pattern|
Item.new(rule, 0) Item.new(pattern, 0)
end end
else else
[] []

View File

@ -2,18 +2,32 @@ class Imbecile
class Rule class Rule
attr_reader :name class Pattern
attr_reader :components attr_reader :components
attr_reader :code attr_reader :code
def initialize(name, rule_components, code) def initialize(components, code)
@name = name @components = components
@components = rule_components
@code = code @code = code
end end
end end
attr_reader :name
attr_reader :patterns
def initialize(name)
@name = name
@patterns = []
end
def add_pattern(components, code)
@patterns << Pattern.new(components, code)
end
end
end end