51 lines
1.3 KiB
Ruby
51 lines
1.3 KiB
Ruby
|
|
||
|
# Alternative during matching. Contains a list of parslets that is tried each
|
||
|
# one in turn. Only fails if all alternatives fail.
|
||
|
#
|
||
|
# Example:
|
||
|
#
|
||
|
# str('a') | str('b') # matches either 'a' or 'b'
|
||
|
#
|
||
|
class Parslet::Atoms::Alternative < Parslet::Atoms::Base
|
||
|
attr_reader :alternatives
|
||
|
|
||
|
# Constructs an Alternative instance using all given parslets in the order
|
||
|
# given. This is what happens if you call '|' on existing parslets, like
|
||
|
# this:
|
||
|
#
|
||
|
# str('a') | str('b')
|
||
|
#
|
||
|
def initialize(*alternatives)
|
||
|
super()
|
||
|
|
||
|
@alternatives = alternatives
|
||
|
@error_msg = "Expected one of #{alternatives.inspect}"
|
||
|
end
|
||
|
|
||
|
#---
|
||
|
# Don't construct a hanging tree of Alternative parslets, instead store them
|
||
|
# all here. This reduces the number of objects created.
|
||
|
#+++
|
||
|
def |(parslet)
|
||
|
self.class.new(*@alternatives + [parslet])
|
||
|
end
|
||
|
|
||
|
def try(source, context, consume_all)
|
||
|
errors = alternatives.map { |a|
|
||
|
success, value = result = a.apply(source, context, consume_all)
|
||
|
return result if success
|
||
|
|
||
|
# Aggregate all errors
|
||
|
value
|
||
|
}
|
||
|
|
||
|
# If we reach this point, all alternatives have failed.
|
||
|
context.err(self, source, @error_msg, errors)
|
||
|
end
|
||
|
|
||
|
precedence ALTERNATE
|
||
|
def to_s_inner(prec)
|
||
|
alternatives.map { |a| a.to_s(prec) }.join(' / ')
|
||
|
end
|
||
|
end
|