module Paco::Combinators

Public Instance Methods

alt(*parsers) click to toggle source

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
failed(message) click to toggle source

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
index() click to toggle source

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
lazy(desc = "", &block) click to toggle source

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
lookahead(parser) click to toggle source

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
many(parser) click to toggle source

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
memoize(&block) click to toggle source

Helper used for memoization

# File lib/paco/combinators.rb, line 170
def memoize(&block)
  Memoizer.memoize(block.source_location, &block)
end
not_followed_by(parser) click to toggle source

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
optional(parser) click to toggle source

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
sep_by(parser, separator) click to toggle source

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
sep_by!(parser, separator) click to toggle source

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
Also aliased as: sep_by_1
sep_by_1(parser, separator)
Alias for: sep_by!
seq(*parsers) { |*results| ... } click to toggle source

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
succeed(result) click to toggle source

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
wrap(before, after, parser) click to toggle source

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