Class: PackratParser::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/packrat_parser/parser.rb

Overview

A parser combinator. Wraps a function (input, pos) -> Success | Failure.

The four monadic operations (+flat_map+, map, filter, pure) are what the for ... then comprehension in the Ruby fork desugars to, so grammar rules can be written with comprehension syntax:

for x in multitive, _ in term("+"), y in additive then x + y end
# => multitive.flat_map { |x| term("+").flat_map { |_| additive.map { |y| x + y } } }

Direct Known Subclasses

Rule

Instance Method Summary collapse

Constructor Details

#initialize(&fn) ⇒ Parser

Returns a new instance of Parser.



11
12
13
# File 'lib/packrat_parser/parser.rb', line 11

def initialize(&fn)
  @fn = fn
end

Instance Method Details

#+(other) ⇒ Object

Sequence, keeping both results (Scala's ~). Run this parser, then other, and on success return the pair [left, right]. Like Scala's ~ this is left-associative and nests, so p + q + r yields [[a, b], c]; Ruby's block-parameter destructuring takes them apart the way Scala's case a ~ b ~ c does:

(p + q + r).map { |(a, b), c| ... }


97
98
99
# File 'lib/packrat_parser/parser.rb', line 97

def +(other)
  flat_map { |x| other.map { |y| [x, y] } }
end

#<<(other) ⇒ Object

Sequence, keeping the left result (Scala's <~). Run this parser, then other, and on success return this parser's value, discarding +other+'s.

number << term(";")   # parse a number followed by ";", yield the number


78
79
80
# File 'lib/packrat_parser/parser.rb', line 78

def <<(other)
  flat_map { |x| other.map { |_| x } }
end

#>>(other) ⇒ Object

Sequence, keeping the right result (Scala's ~>). Run this parser, then other, and on success return +other+'s value, discarding this one's.

term("(") >> additive   # skip "(", yield whatever additive produces


86
87
88
# File 'lib/packrat_parser/parser.rb', line 86

def >>(other)
  flat_map { |_| other }
end

#call(input, pos) ⇒ Object

Run this parser against input starting at pos.



16
17
18
# File 'lib/packrat_parser/parser.rb', line 16

def call(input, pos)
  @fn.call(input, pos)
end

#filterObject

Succeed only when the block returns a truthy value for the parsed result; otherwise fail at the position where this parser started. This is what a when guard in a comprehension desugars to.



45
46
47
48
49
50
51
52
53
54
# File 'lib/packrat_parser/parser.rb', line 45

def filter
  Parser.new do |input, pos|
    result = call(input, pos)
    if result.success? && yield(result.value)
      result
    else
      Failure.new(pos, "guard failed")
    end
  end
end

#flat_mapObject

Sequencing / monadic bind. On success, yield the value to obtain the next parser and run it where this one stopped. Failures short-circuit.



22
23
24
25
26
27
28
29
30
31
32
# File 'lib/packrat_parser/parser.rb', line 22

def flat_map
  Parser.new do |input, pos|
    result = call(input, pos)
    if result.success?
      next_parser = yield(result.value)
      next_parser.call(input, result.pos)
    else
      result
    end
  end
end

#mapObject

Transform the successful value without consuming further input.



35
36
37
38
39
40
# File 'lib/packrat_parser/parser.rb', line 35

def map
  Parser.new do |input, pos|
    result = call(input, pos)
    result.success? ? Success.new(yield(result.value), result.pos) : result
  end
end

#|(other) ⇒ Object

Ordered choice (PEG /). Try this parser; if it fails, try other at the same position. Reports whichever failure reached furthest into the input.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/packrat_parser/parser.rb', line 58

def |(other)
  Parser.new do |input, pos|
    result = call(input, pos)
    if result.success?
      result
    else
      alt = other.call(input, pos)
      if alt.success?
        alt
      else
        alt.pos >= result.pos ? alt : result
      end
    end
  end
end