From 9cc1890ddc378b4241b5f99e7391ea7b42210324 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sat, 28 Aug 2021 10:28:50 -0400 Subject: [PATCH] One Rule object stores all alternative patterns --- lib/imbecile.rb | 37 +++++++++++++++++++------------------ lib/imbecile/parser.rb | 5 ++--- lib/imbecile/parser/item.rb | 18 +++++++++--------- lib/imbecile/rule.rb | 26 ++++++++++++++++++++------ 4 files changed, 50 insertions(+), 36 deletions(-) diff --git a/lib/imbecile.rb b/lib/imbecile.rb index 85fb185..fb810f6 100644 --- a/lib/imbecile.rb +++ b/lib/imbecile.rb @@ -36,7 +36,7 @@ class Imbecile def initialize(input) @tokens = [] - @rules = [] + @rules = {} input = input.gsub("\r\n", "\n") while !input.empty? parse_grammar(input) @@ -79,9 +79,10 @@ class Imbecile pattern = $1 @tokens << Token.new(nil, pattern, @tokens.size) elsif input.slice!(/\A(\S+)\s*:\s*\[(.*?)\] <<\n(.*?)^>>\n/m) - rule_name, rule, code = $1, $2, $3 - rule = rule.strip.split(/\s+/) - @rules << Rule.new(rule_name, rule, code) + rule_name, components, code = $1, $2, $3 + components = components.strip.split(/\s+/) + @rules[rule_name] ||= Rule.new(rule_name) + @rules[rule_name].add_pattern(components, code) else if input.size > 25 input = input.slice(0..20) + "..." @@ -97,24 +98,24 @@ class Imbecile end token_names[token.name] = token end - rule_names = @rules.each_with_object({}) do |rule, rule_names| - if token_names.include?(rule.name) - raise Error.new("Rule name collides with token name #{rule.name}") + @rules.each do |rule_name, rule| + if token_names.include?(rule_name) + raise Error.new("Rule name collides with token name #{rule_name}") end - rule_names[rule.name] ||= [] - rule_names[rule.name] << rule end - unless rule_names["Start"] + unless @rules["Start"] raise Error.new("Start rule not found") end - @rules.each do |rule| - rule.components.map! do |component| - if token_names[component] - token_names[component] - elsif rule_names[component] - rule_names[component] - else - raise Error.new("Symbol #{component} not found") + @rules.each do |rule_name, rule| + rule.patterns.each do |pattern| + pattern.components.map! do |component| + if token_names[component] + token_names[component] + elsif @rules[component] + @rules[component] + else + raise Error.new("Symbol #{component} not found") + end end end end diff --git a/lib/imbecile/parser.rb b/lib/imbecile/parser.rb index 8a1c923..97789a2 100644 --- a/lib/imbecile/parser.rb +++ b/lib/imbecile/parser.rb @@ -3,9 +3,8 @@ class Imbecile class Parser def initialize(tokens, rules) - start_rules = rules.select {|rule| rule.name == "Start"} - start_items = start_rules.map do |rule| - Item.new(rule, 0) + start_items = rules["Start"].patterns.map do |pattern| + Item.new(pattern, 0) end start_item_set = ItemSet.new(start_items) start_item_set.close! diff --git a/lib/imbecile/parser/item.rb b/lib/imbecile/parser/item.rb index 0ab3c6f..cf10336 100644 --- a/lib/imbecile/parser/item.rb +++ b/lib/imbecile/parser/item.rb @@ -3,24 +3,24 @@ class Imbecile class Item - attr_reader :rule + attr_reader :pattern attr_reader :position - def initialize(rule, position) - @rule = rule + def initialize(pattern, position) + @pattern = pattern @position = position end def next_component - @rule.components[@position] + @pattern.components[@position] end def hash - [@rule, @position].hash + [@pattern, @position].hash end def ==(other) - @rule == other.rule && @position == other.position + @pattern == other.pattern && @position == other.position end def eql?(other) @@ -28,9 +28,9 @@ class Imbecile end def closed_items - if @rule.components[@position].is_a?(Array) - @rule.components[@position].map do |rule| - Item.new(rule, 0) + if @pattern.components[@position].is_a?(Rule) + @pattern.components[@position].patterns.map do |pattern| + Item.new(pattern, 0) end else [] diff --git a/lib/imbecile/rule.rb b/lib/imbecile/rule.rb index c89543e..4398248 100644 --- a/lib/imbecile/rule.rb +++ b/lib/imbecile/rule.rb @@ -2,16 +2,30 @@ class Imbecile class Rule + class Pattern + + attr_reader :components + + attr_reader :code + + def initialize(components, code) + @components = components + @code = code + end + + end + attr_reader :name - attr_reader :components + attr_reader :patterns - attr_reader :code - - def initialize(name, rule_components, code) + def initialize(name) @name = name - @components = rule_components - @code = code + @patterns = [] + end + + def add_pattern(components, code) + @patterns << Pattern.new(components, code) end end