148 lines
3.7 KiB
Ruby
148 lines
3.7 KiB
Ruby
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
|
|
@_hash = [@rule, @position].hash
|
|
end
|
|
|
|
# Hash function.
|
|
#
|
|
# @return [Integer]
|
|
# Hash code.
|
|
def hash
|
|
@_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 next 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|
|
|
Item.new(rule, 0)
|
|
end
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
# Return whether the item is "complete", meaning that the parse position
|
|
# marker is at the end of the rule.
|
|
#
|
|
# @return [Boolean]
|
|
# Whether the item is "complete".
|
|
def complete?
|
|
@position == @rule.components.size
|
|
end
|
|
|
|
# Get the next symbol for the Item.
|
|
#
|
|
# That is, the symbol which is after the parse position marker in the
|
|
# current Item.
|
|
#
|
|
# @param offset [Integer]
|
|
# Offset from current parse position to examine.
|
|
#
|
|
# @return [Token, RuleSet, nil]
|
|
# Next symbol for the Item.
|
|
def next_symbol(offset = 0)
|
|
@rule.components[@position + offset]
|
|
end
|
|
|
|
# Get the previous symbol for the Item.
|
|
#
|
|
# That is, the symbol which precedes the parse position marker in the
|
|
# current Item.
|
|
#
|
|
# @return [Token, RuleSet, nil]
|
|
# Previous symbol for this Item.
|
|
def previous_symbol
|
|
if @position > 0
|
|
@rule.components[@position - 1]
|
|
end
|
|
end
|
|
|
|
# Get whether this Item's next symbol is the given symbol.
|
|
#
|
|
# @param symbol [Token, RuleSet]
|
|
# Symbol to query.
|
|
#
|
|
# @return [Boolean]
|
|
# Whether this Item's next symbol is the given symbol.
|
|
def next_symbol?(symbol)
|
|
next_symbol == symbol
|
|
end
|
|
|
|
# Get the next 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 next item for this Item.
|
|
def next_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.map(&:name)
|
|
parts[@position, 0] = "."
|
|
"#{@rule.name} -> #{parts.join(" ")}"
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
end
|