module Paco::Combinators
Public Instance Methods
Accepts any number of parsers, and returns a parser that returns the value of the first parser that succeeds, backtracking in between. @param [Array<Paco::Parser>] parsers @return [Paco::Parser]
# File lib/paco/combinators.rb, line 61 def alt(*parsers) raise ArgumentError, "no parsers specified" if parsers.empty? Parser.new("alt(#{parsers.map(&:desc).join(", ")})") do |ctx| result = nil last_error = nil start_pos = ctx.pos parsers.each do |pars| break result = {value: pars._parse(ctx)} rescue ParseError => e last_error = e ctx.pos = start_pos next end raise last_error unless result result[:value] end end
Returns a parser that doesn’t consume any input and always fails with passed ‘message`. @param [String] message @return [Paco::Parser]
# File lib/paco/combinators.rb, line 41 def failed(message) Parser.new(message) { |ctx, parser| parser.failure(ctx) } end
Returns parser that returns ‘Paco::Index` representing the current offset into the parse without consuming the input. @return [Paco::Parser]
# File lib/paco/combinators.rb, line 165 def index Parser.new { |ctx| ctx.index } end
Accepts a block that returns a parser, which is evaluated the first time the parser is used. This is useful for referencing parsers that haven’t yet been defined, and for implementing recursive parsers. @return [Paco::Parser]
# File lib/paco/combinators.rb, line 106 def lazy(desc = "", &block) Parser.new(desc) { |ctx| block.call._parse(ctx) } end
Returns a parser that runs the passed ‘parser` without consuming the input, and returns empty string. @param [Paco::Parser] parser @return [Paco::Parser]
# File lib/paco/combinators.rb, line 49 def lookahead(parser) Parser.new("lookahead(#{parser.desc})") do |ctx| start_pos = ctx.pos parser._parse(ctx) ctx.pos = start_pos "" end end
Expects ‘parser` zero or more times, and returns an array of the results. @param [Paco::Parser] parser @return [Paco::Parser]
# File lib/paco/combinators.rb, line 143 def many(parser) Parser.new("many(#{parser.desc})") do |ctx| results = [] loop do results << parser._parse(ctx) rescue ParseError break end results end end
Helper used for memoization
# File lib/paco/combinators.rb, line 170 def memoize(&block) Memoizer.memoize(block.source_location, &block) end
Returns a parser that runs the passed ‘parser` without consuming the input, and returns `null` if the passed `parser` _does not match_ the input. Fails otherwise. @param [Paco::Parser] parser @return [Paco::Parser]
# File lib/paco/combinators.rb, line 17 def not_followed_by(parser) Parser.new("not #{parser.desc}") do |ctx, pars| start_pos = ctx.pos begin parser._parse(ctx) rescue ParseError nil else pars.failure(ctx) ensure ctx.pos = start_pos end end end
Returns parser that returns result of the passed ‘parser` or nil if `parser` fails. @param [Paco::Parser] parser @return [Paco::Parser]
# File lib/paco/combinators.rb, line 158 def optional(parser) alt(parser, succeed(nil)) end
Returns a parser that expects zero or more matches for ‘parser`, separated by the parser `separator`. Returns an array of `parser` results. @param [Paco::Parser] parser @param [Paco::Parser] separator @return [Paco::Parser]
# File lib/paco/combinators.rb, line 115 def sep_by(parser, separator) alt(sep_by!(parser, separator), succeed([])) .with_desc("sep_by(#{parser.desc}, #{separator.desc})") end
Returns a parser that expects one or more matches for ‘parser`, separated by the parser `separator`. Returns an array of `parser` results. @param [Paco::Parser] parser @param [Paco::Parser] separator @return [Paco::Parser]
# File lib/paco/combinators.rb, line 125 def sep_by!(parser, separator) seq(parser, many(separator.next(parser))) { |first, arr| [first] + arr } .with_desc("sep_by!(#{parser.desc}, #{separator.desc})") end
Accepts one or more parsers, and returns a parser that expects them to match in order, returns an array of all their results. If ‘block` specified, passes results of the `parses` as an arguments to a `block`, and at the end returns its result. @param [Array<Paco::Parser>] parsers @return [Paco::Parser]
# File lib/paco/combinators.rb, line 86 def seq(*parsers) raise ArgumentError, "no parsers specified" if parsers.empty? result = Parser.new("seq(#{parsers.map(&:desc).join(", ")})") do |ctx| start_pos = ctx.pos begin parsers.map { |parser| parser._parse(ctx) } rescue ParseError => e ctx.pos = start_pos raise e end end return result unless block_given? result.fmap { |results| yield(*results) } end
Returns a parser that doesn’t consume any input and always returns ‘result`. @return [Paco::Parser]
# File lib/paco/combinators.rb, line 34 def succeed(result) Parser.new("succeed(#{result})") { result } end
Expects the parser ‘before` before `parser` and `after` after `parser. Returns the result of the parser. @param [Paco::Parser] before @param [Paco::Parser] after @param [Paco::Parser] parser @return [Paco::Parser]
# File lib/paco/combinators.rb, line 136 def wrap(before, after, parser) before.next(parser).skip(after) end