Clean up and comment Item and ItemSet
This commit is contained in:
parent
df8088c3c6
commit
9d850294a9
@ -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
|
||||
|
@ -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<Item>]
|
||||
# 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
|
||||
|
||||
|
@ -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<Item>]
|
||||
# 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<Item>]
|
||||
# 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<Token, RuleSet>]
|
||||
# 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<Item>]
|
||||
# Items followed by the given following symbol.
|
||||
def items_followed_by(symbol)
|
||||
@items.select do |item|
|
||||
item.followed_by?(symbol)
|
||||
|
Loading…
x
Reference in New Issue
Block a user