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_set.id = @item_sets.size
|
||||||
@item_sets << item_set
|
@item_sets << item_set
|
||||||
@item_sets_set[item_set] = item_set
|
@item_sets_set[item_set] = item_set
|
||||||
item_set.follow_symbols.each do |follow_symbol|
|
item_set.following_symbols.each do |following_symbol|
|
||||||
unless follow_symbol == @token_eof
|
unless following_symbol == @token_eof
|
||||||
follow_set = item_set.build_follow_set(follow_symbol)
|
following_set = item_set.build_following_item_set(following_symbol)
|
||||||
eval_item_sets << follow_set
|
eval_item_sets << following_set
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -39,8 +39,8 @@ class Propane
|
|||||||
puts " (in from #{ids.join(", ")})"
|
puts " (in from #{ids.join(", ")})"
|
||||||
end
|
end
|
||||||
puts item_set
|
puts item_set
|
||||||
item_set.follow_item_set.each do |follow_symbol, follow_item_set|
|
item_set.following_item_set.each do |following_symbol, following_item_set|
|
||||||
puts " #{follow_symbol.name} => #{follow_item_set.id}"
|
puts " #{following_symbol.name} => #{following_item_set.id}"
|
||||||
end
|
end
|
||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
@ -50,12 +50,12 @@ class Propane
|
|||||||
shift_table = []
|
shift_table = []
|
||||||
state_table = []
|
state_table = []
|
||||||
@item_sets.each do |item_set|
|
@item_sets.each do |item_set|
|
||||||
shift_entries = item_set.follow_symbols.select do |follow_symbol|
|
shift_entries = item_set.following_symbols.select do |following_symbol|
|
||||||
follow_symbol.is_a?(Token)
|
following_symbol.is_a?(Token)
|
||||||
end.map do |follow_symbol|
|
end.map do |following_symbol|
|
||||||
{
|
{
|
||||||
token_id: follow_symbol.id,
|
token_id: following_symbol.id,
|
||||||
state_id: item_set.follow_item_set[follow_symbol].id,
|
state_id: item_set.following_item_set[following_symbol].id,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
state_table << {
|
state_table << {
|
||||||
@ -70,11 +70,11 @@ class Propane
|
|||||||
private
|
private
|
||||||
|
|
||||||
def process_item_set(item_set)
|
def process_item_set(item_set)
|
||||||
item_set.follow_symbols.each do |follow_symbol|
|
item_set.following_symbols.each do |following_symbol|
|
||||||
unless follow_symbol == @token_eof
|
unless following_symbol == @token_eof
|
||||||
follow_set = @item_sets_set[item_set.build_follow_set(follow_symbol)]
|
following_set = @item_sets_set[item_set.build_following_item_set(following_symbol)]
|
||||||
item_set.follow_item_set[follow_symbol] = follow_set
|
item_set.following_item_set[following_symbol] = following_set
|
||||||
follow_set.in_sets << item_set
|
following_set.in_sets << item_set
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,32 +1,67 @@
|
|||||||
class Propane
|
class Propane
|
||||||
class Parser
|
class Parser
|
||||||
|
|
||||||
|
# Represent a parser "item", which is a position in a Rule that the parser
|
||||||
|
# could potentially be at.
|
||||||
class Item
|
class Item
|
||||||
|
|
||||||
|
# @return [Rule]
|
||||||
|
# The Rule corresponding to this Item.
|
||||||
attr_reader :rule
|
attr_reader :rule
|
||||||
|
|
||||||
|
# @return [Integer]
|
||||||
|
# The parse position in this item.
|
||||||
attr_reader :position
|
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)
|
def initialize(rule, position)
|
||||||
@rule = rule
|
@rule = rule
|
||||||
@position = position
|
@position = position
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_component
|
# Hash function.
|
||||||
@rule.components[@position]
|
#
|
||||||
end
|
# @return [Integer]
|
||||||
|
# Hash code.
|
||||||
def hash
|
def hash
|
||||||
[@rule, @position].hash
|
[@rule, @position].hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compare Item objects.
|
||||||
|
#
|
||||||
|
# @param other [Item]
|
||||||
|
# Item to compare to.
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
# Whether the Items are equal.
|
||||||
def ==(other)
|
def ==(other)
|
||||||
@rule == other.rule && @position == other.position
|
@rule == other.rule && @position == other.position
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compare Item objects.
|
||||||
|
#
|
||||||
|
# @param other [Item]
|
||||||
|
# Item to compare to.
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
# Whether the Items are equal.
|
||||||
def eql?(other)
|
def eql?(other)
|
||||||
self == other
|
self == other
|
||||||
end
|
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
|
def closed_items
|
||||||
if @rule.components[@position].is_a?(RuleSet)
|
if @rule.components[@position].is_a?(RuleSet)
|
||||||
@rule.components[@position].rules.map do |rule|
|
@rule.components[@position].rules.map do |rule|
|
||||||
@ -37,29 +72,46 @@ class Propane
|
|||||||
end
|
end
|
||||||
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]
|
@rule.components[@position]
|
||||||
end
|
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)
|
def followed_by?(symbol)
|
||||||
follow_symbol == symbol
|
following_symbol == symbol
|
||||||
end
|
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)
|
Item.new(@rule, @position + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Represent the Item as a String.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# The Item represented as a String.
|
||||||
def to_s
|
def to_s
|
||||||
parts = []
|
parts = @rule.components.map(&:name)
|
||||||
@rule.components.each_with_index do |symbol, index|
|
parts[@position, 0] = "."
|
||||||
if @position == index
|
|
||||||
parts << "."
|
|
||||||
end
|
|
||||||
parts << symbol.name
|
|
||||||
end
|
|
||||||
if @position == @rule.components.size
|
|
||||||
parts << "."
|
|
||||||
end
|
|
||||||
"#{@rule.name} -> #{parts.join(" ")}"
|
"#{@rule.name} -> #{parts.join(" ")}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,53 +1,102 @@
|
|||||||
class Propane
|
class Propane
|
||||||
class Parser
|
class Parser
|
||||||
|
|
||||||
|
# Represent a parser "item set", which is a set of possible items that the
|
||||||
|
# parser could currently be parsing.
|
||||||
class ItemSet
|
class ItemSet
|
||||||
|
|
||||||
|
# @return [Set<Item>]
|
||||||
|
# Items in this ItemSet.
|
||||||
attr_reader :items
|
attr_reader :items
|
||||||
|
|
||||||
|
# @return [Integer]
|
||||||
|
# ID of this ItemSet.
|
||||||
attr_accessor :id
|
attr_accessor :id
|
||||||
|
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
# Maps a follow symbol to its item set.
|
# Maps a following symbol to its ItemSet.
|
||||||
attr_reader :follow_item_set
|
attr_reader :following_item_set
|
||||||
|
|
||||||
# @return [Set]
|
# @return [Set]
|
||||||
# Item sets leading to this item set.
|
# Item sets leading to this item set.
|
||||||
attr_reader :in_sets
|
attr_reader :in_sets
|
||||||
|
|
||||||
|
# Build an ItemSet.
|
||||||
|
#
|
||||||
|
# @param items [Array<Item>]
|
||||||
|
# Items in this ItemSet.
|
||||||
def initialize(items)
|
def initialize(items)
|
||||||
@items = Set.new(items)
|
@items = Set.new(items)
|
||||||
@follow_item_set = {}
|
@following_item_set = {}
|
||||||
@in_sets = Set.new
|
@in_sets = Set.new
|
||||||
close!
|
close!
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_symbols
|
# Get the set of following symbols for all Items in this ItemSet.
|
||||||
Set.new(@items.map(&:follow_symbol).compact)
|
#
|
||||||
|
# @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
|
end
|
||||||
|
|
||||||
def build_follow_set(symbol)
|
# Build a following ItemSet for the given following symbol.
|
||||||
ItemSet.new(items_followed_by(symbol).map(&:next_position))
|
#
|
||||||
|
# @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
|
end
|
||||||
|
|
||||||
|
# Hash function.
|
||||||
|
#
|
||||||
|
# @return [Integer]
|
||||||
|
# Hash code.
|
||||||
def hash
|
def hash
|
||||||
@items.hash
|
@items.hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compare ItemSet objects.
|
||||||
|
#
|
||||||
|
# @param other [ItemSet]
|
||||||
|
# ItemSet to compare to.
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
# Whether the ItemSets are equal.
|
||||||
def ==(other)
|
def ==(other)
|
||||||
@items.eql?(other.items)
|
@items.eql?(other.items)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Compare ItemSet objects.
|
||||||
|
#
|
||||||
|
# @param other [ItemSet]
|
||||||
|
# ItemSet to compare to.
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
# Whether the ItemSets are equal.
|
||||||
def eql?(other)
|
def eql?(other)
|
||||||
self == other
|
self == other
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Represent the ItemSet as a String.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# The ItemSet represented as a String.
|
||||||
def to_s
|
def to_s
|
||||||
@items.map(&:to_s).join("\n")
|
@items.map(&:to_s).join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Close the ItemSet.
|
||||||
|
#
|
||||||
|
# This is done by recursively adding the closed Items for each Item in
|
||||||
|
# the ItemSet.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
def close!
|
def close!
|
||||||
eval_items = @items
|
eval_items = @items
|
||||||
while eval_items.size > 0
|
while eval_items.size > 0
|
||||||
@ -64,6 +113,13 @@ class Propane
|
|||||||
end
|
end
|
||||||
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)
|
def items_followed_by(symbol)
|
||||||
@items.select do |item|
|
@items.select do |item|
|
||||||
item.followed_by?(symbol)
|
item.followed_by?(symbol)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user