From 9d850294a921a473ad210036a93e239f2b106a3e Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 5 Jun 2022 18:12:14 -0400 Subject: [PATCH] Clean up and comment Item and ItemSet --- lib/propane/parser.rb | 32 ++++++------- lib/propane/parser/item.rb | 86 +++++++++++++++++++++++++++------- lib/propane/parser/item_set.rb | 70 ++++++++++++++++++++++++--- 3 files changed, 148 insertions(+), 40 deletions(-) diff --git a/lib/propane/parser.rb b/lib/propane/parser.rb index 172d936..6149d37 100644 --- a/lib/propane/parser.rb +++ b/lib/propane/parser.rb @@ -21,10 +21,10 @@ class Propane item_set.id = @item_sets.size @item_sets << item_set @item_sets_set[item_set] = item_set - item_set.follow_symbols.each do |follow_symbol| - unless follow_symbol == @token_eof - follow_set = item_set.build_follow_set(follow_symbol) - eval_item_sets << follow_set + item_set.following_symbols.each do |following_symbol| + unless following_symbol == @token_eof + following_set = item_set.build_following_item_set(following_symbol) + eval_item_sets << following_set end end end @@ -39,8 +39,8 @@ class Propane puts " (in from #{ids.join(", ")})" end puts item_set - item_set.follow_item_set.each do |follow_symbol, follow_item_set| - puts " #{follow_symbol.name} => #{follow_item_set.id}" + item_set.following_item_set.each do |following_symbol, following_item_set| + puts " #{following_symbol.name} => #{following_item_set.id}" end puts end @@ -50,12 +50,12 @@ class Propane shift_table = [] state_table = [] @item_sets.each do |item_set| - shift_entries = item_set.follow_symbols.select do |follow_symbol| - follow_symbol.is_a?(Token) - end.map do |follow_symbol| + shift_entries = item_set.following_symbols.select do |following_symbol| + following_symbol.is_a?(Token) + end.map do |following_symbol| { - token_id: follow_symbol.id, - state_id: item_set.follow_item_set[follow_symbol].id, + token_id: following_symbol.id, + state_id: item_set.following_item_set[following_symbol].id, } end state_table << { @@ -70,11 +70,11 @@ class Propane private def process_item_set(item_set) - item_set.follow_symbols.each do |follow_symbol| - unless follow_symbol == @token_eof - follow_set = @item_sets_set[item_set.build_follow_set(follow_symbol)] - item_set.follow_item_set[follow_symbol] = follow_set - follow_set.in_sets << item_set + item_set.following_symbols.each do |following_symbol| + unless following_symbol == @token_eof + following_set = @item_sets_set[item_set.build_following_item_set(following_symbol)] + item_set.following_item_set[following_symbol] = following_set + following_set.in_sets << item_set end end end diff --git a/lib/propane/parser/item.rb b/lib/propane/parser/item.rb index 8f46cbb..6ed9cf5 100644 --- a/lib/propane/parser/item.rb +++ b/lib/propane/parser/item.rb @@ -1,32 +1,67 @@ class Propane class Parser + # Represent a parser "item", which is a position in a Rule that the parser + # could potentially be at. class Item + # @return [Rule] + # The Rule corresponding to this Item. attr_reader :rule + + # @return [Integer] + # The parse position in this item. attr_reader :position + # Construct an Item. + # + # @param rule [Rule] + # The Rule corresponding to this Item. + # @param position [Integer] + # The parse position in this Item. def initialize(rule, position) @rule = rule @position = position end - def next_component - @rule.components[@position] - end - + # Hash function. + # + # @return [Integer] + # Hash code. def hash [@rule, @position].hash end + # Compare Item objects. + # + # @param other [Item] + # Item to compare to. + # + # @return [Boolean] + # Whether the Items are equal. def ==(other) @rule == other.rule && @position == other.position end + # Compare Item objects. + # + # @param other [Item] + # Item to compare to. + # + # @return [Boolean] + # Whether the Items are equal. def eql?(other) self == other end + # Return the set of Items obtained by "closing" the current item. + # + # If the following symbol for the current item is another Rule name, then + # this method will return all Items for that Rule with a position of 0. + # Otherwise, an empty Array is returned. + # + # @return [Array] + # Items obtained by "closing" the current item. def closed_items if @rule.components[@position].is_a?(RuleSet) @rule.components[@position].rules.map do |rule| @@ -37,29 +72,46 @@ class Propane end end - def follow_symbol + # Get the following symbol for the Item. + # + # That is, the symbol which follows the parse position marker in the + # current Item. + # + # @return [Token, RuleSet, nil] + # Following symbol for the Item. + def following_symbol @rule.components[@position] end + # Get whether this Item is followed by the provided symbol. + # + # @param symbol [Token, RuleSet] + # Symbol to query. + # + # @return [Boolean] + # Whether this Item is followed by the provided symbol. def followed_by?(symbol) - follow_symbol == symbol + following_symbol == symbol end - def next_position + # Get the following item for this Item. + # + # That is, the Item formed by moving the parse position marker one place + # forward from its position in this Item. + # + # @return [Item] + # The following item for this Item. + def following_item Item.new(@rule, @position + 1) end + # Represent the Item as a String. + # + # @return [String] + # The Item represented as a String. def to_s - parts = [] - @rule.components.each_with_index do |symbol, index| - if @position == index - parts << "." - end - parts << symbol.name - end - if @position == @rule.components.size - parts << "." - end + parts = @rule.components.map(&:name) + parts[@position, 0] = "." "#{@rule.name} -> #{parts.join(" ")}" end diff --git a/lib/propane/parser/item_set.rb b/lib/propane/parser/item_set.rb index d34116f..3aacee9 100644 --- a/lib/propane/parser/item_set.rb +++ b/lib/propane/parser/item_set.rb @@ -1,53 +1,102 @@ class Propane class Parser + # Represent a parser "item set", which is a set of possible items that the + # parser could currently be parsing. class ItemSet + # @return [Set] + # Items in this ItemSet. attr_reader :items + # @return [Integer] + # ID of this ItemSet. attr_accessor :id # @return [Hash] - # Maps a follow symbol to its item set. - attr_reader :follow_item_set + # Maps a following symbol to its ItemSet. + attr_reader :following_item_set # @return [Set] # Item sets leading to this item set. attr_reader :in_sets + # Build an ItemSet. + # + # @param items [Array] + # Items in this ItemSet. def initialize(items) @items = Set.new(items) - @follow_item_set = {} + @following_item_set = {} @in_sets = Set.new close! end - def follow_symbols - Set.new(@items.map(&:follow_symbol).compact) + # Get the set of following symbols for all Items in this ItemSet. + # + # @return [Set] + # Set of following symbols for all Items in this ItemSet. + def following_symbols + Set.new(@items.map(&:following_symbol).compact) end - def build_follow_set(symbol) - ItemSet.new(items_followed_by(symbol).map(&:next_position)) + # Build a following ItemSet for the given following symbol. + # + # @param symbol [Token, RuleSet] + # Following symbol to build the following ItemSet for. + # + # @return [ItemSet] + # Following ItemSet for the given following symbol. + def build_following_item_set(symbol) + ItemSet.new(items_followed_by(symbol).map(&:following_item)) end + # Hash function. + # + # @return [Integer] + # Hash code. def hash @items.hash end + # Compare ItemSet objects. + # + # @param other [ItemSet] + # ItemSet to compare to. + # + # @return [Boolean] + # Whether the ItemSets are equal. def ==(other) @items.eql?(other.items) end + # Compare ItemSet objects. + # + # @param other [ItemSet] + # ItemSet to compare to. + # + # @return [Boolean] + # Whether the ItemSets are equal. def eql?(other) self == other end + # Represent the ItemSet as a String. + # + # @return [String] + # The ItemSet represented as a String. def to_s @items.map(&:to_s).join("\n") end private + # Close the ItemSet. + # + # This is done by recursively adding the closed Items for each Item in + # the ItemSet. + # + # @return [void] def close! eval_items = @items while eval_items.size > 0 @@ -64,6 +113,13 @@ class Propane end end + # Get the Items followed by the given following symbol. + # + # @param symbol [Token, RuleSet] + # Following symbol. + # + # @return [Array] + # Items followed by the given following symbol. def items_followed_by(symbol) @items.select do |item| item.followed_by?(symbol)