rubyx/lib/parslet/cause.rb

94 lines
3.0 KiB
Ruby

module Parslet
# Represents a cause why a parse did fail. A lot of these objects are
# constructed - not all of the causes turn out to be failures for the whole
# parse.
#
class Cause
def initialize(message, source, pos, children)
@message, @source, @pos, @children =
message, source, pos, children
end
# @return [String, Array] A string or an array of message pieces that
# provide failure information. Use #to_s to get a formatted string.
attr_reader :message
# @return [Parslet::Source] Source that was parsed when this error
# happend. Mainly used for line number information.
attr_reader :source
# Location of the error.
#
# @return [Fixnum] Position where the error happened. (character offset)
attr_reader :pos
# When this cause is part of a tree of error causes: child nodes for this
# node. Very often carries the reasons for this cause.
#
# @return [Array<Parslet::Cause>] A list of reasons for this cause.
def children
@children ||= []
end
# Appends 'at line LINE char CHAR' to the string given. Use +pos+ to
# override the position of the +source+. This method returns an object
# that can be turned into a string using #to_s.
#
# @param source [Parslet::Source] source that was parsed when this error
# happened
# @param pos [Fixnum] position of error
# @param str [String, Array<String>] message parts
# @param children [Array<Parslet::Cause>] child nodes for this error tree
# @return [Parslet::Cause] a new instance of {Parslet::Cause}
#
def self.format(source, pos, str, children=[])
self.new(str, source, pos, children)
end
def to_s
line, column = source.line_and_column(pos)
# Allow message to be a list of objects. Join them here, since we now
# really need it.
Array(message).map { |o|
o.respond_to?(:to_slice) ?
o.str.inspect :
o.to_s }.join + " at line #{line} char #{column}."
end
# Signals to the outside that the parse has failed. Use this in
# conjunction with .format for nice error messages.
#
def raise(exception_klass=Parslet::ParseFailed)
exception = exception_klass.new(self.to_s, self)
Kernel.raise exception
end
# Returns an ascii tree representation of the causes of this node and its
# children.
#
def ascii_tree
StringIO.new.tap { |io|
recursive_ascii_tree(self, io, [true]) }.
string
end
private
def recursive_ascii_tree(node, stream, curved)
append_prefix(stream, curved)
stream.puts node.to_s
node.children.each do |child|
last_child = (node.children.last == child)
recursive_ascii_tree(child, stream, curved + [last_child])
end
end
def append_prefix(stream, curved)
return if curved.size < 2
curved[1..-2].each do |c|
stream.print c ? " " : "| "
end
stream.print curved.last ? "`- " : "|- "
end
end
end