Module: Paco::Combinators

Extended by:
Char
Includes:
Char
Defined in:
lib/paco/combinators.rb,
lib/paco/combinators/char.rb

Defined Under Namespace

Modules: Char

Class Method Summary collapse

Methods included from Char

any_char, cr, crlf, digit, digits, end_of_line, eof, letter, letters, lf, newline, none_of, one_of, opt_digits, opt_letters, opt_ws, regexp, regexp_char, remainder, satisfy, spaced, string, take_while, ws

Class Method Details

.alt(*parsers) ⇒ Paco::Parser

Accepts any number of parsers, and returns a parser that returns the value of the first parser that succeeds, backtracking in between.

Parameters:

Returns:

Raises:

  • (ArgumentError)


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 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) ⇒ Paco::Parser

Returns a parser that doesn’t consume any input and always fails with passed ‘message`.

Parameters:

  • message (String)

Returns:



41
42
43
# File 'lib/paco/combinators.rb', line 41

def failed(message)
  Parser.new(message) { |ctx, parser| parser.failure(ctx) }
end

.indexPaco::Parser

Returns parser that returns ‘Paco::Index` representing the current offset into the parse without consuming the input.

Returns:



165
166
167
# File 'lib/paco/combinators.rb', line 165

def index
  Parser.new { |ctx| ctx.index }
end

.lazy(desc = "", &block) ⇒ Paco::Parser

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.

Returns:



106
107
108
# File 'lib/paco/combinators.rb', line 106

def lazy(desc = "", &block)
  Parser.new(desc) { |ctx| block.call._parse(ctx) }
end

.lookahead(parser) ⇒ Paco::Parser

Returns a parser that runs the passed ‘parser` without consuming the input, and returns empty string.

Parameters:

Returns:



49
50
51
52
53
54
55
56
# 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) ⇒ Paco::Parser

Expects ‘parser` zero or more times, and returns an array of the results.

Parameters:

Returns:



143
144
145
146
147
148
149
150
151
152
153
# 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) ⇒ Object

Helper used for memoization



170
171
172
# File 'lib/paco/combinators.rb', line 170

def memoize(&block)
  Memoizer.memoize(block.source_location, &block)
end

.not_followed_by(parser) ⇒ Paco::Parser

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.

Parameters:

Returns:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 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) ⇒ Paco::Parser

Returns parser that returns result of the passed ‘parser` or nil if `parser` fails.

Parameters:

Returns:



158
159
160
# File 'lib/paco/combinators.rb', line 158

def optional(parser)
  alt(parser, succeed(nil))
end

.sep_by(parser, separator) ⇒ Paco::Parser

Returns a parser that expects zero or more matches for ‘parser`, separated by the parser `separator`. Returns an array of `parser` results.

Parameters:

Returns:



115
116
117
118
# 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) ⇒ Paco::Parser Also known as: sep_by_1

Returns a parser that expects one or more matches for ‘parser`, separated by the parser `separator`. Returns an array of `parser` results.

Parameters:

Returns:



125
126
127
128
# 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

.seq(*parsers) ⇒ Paco::Parser

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.

Parameters:

Returns:

Raises:

  • (ArgumentError)


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 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) ⇒ Paco::Parser

Returns a parser that doesn’t consume any input and always returns ‘result`.

Returns:



34
35
36
# File 'lib/paco/combinators.rb', line 34

def succeed(result)
  Parser.new("succeed(#{result})") { result }
end

.wrap(before, after, parser) ⇒ Paco::Parser

Expects the parser ‘before` before `parser` and `after` after `parser. Returns the result of the parser.

Parameters:

Returns:



136
137
138
# File 'lib/paco/combinators.rb', line 136

def wrap(before, after, parser)
  before.next(parser).skip(after)
end