Class: PackratParser::Parser
- Inherits:
-
Object
- Object
- PackratParser::Parser
- 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
Instance Method Summary collapse
-
#+(other) ⇒ Object
Sequence, keeping both results (Scala's
~). -
#<<(other) ⇒ Object
Sequence, keeping the left result (Scala's
<~). -
#>>(other) ⇒ Object
Sequence, keeping the right result (Scala's
~>). -
#call(input, pos) ⇒ Object
Run this parser against
inputstarting atpos. -
#filter ⇒ Object
Succeed only when the block returns a truthy value for the parsed result; otherwise fail at the position where this parser started.
-
#flat_map ⇒ Object
Sequencing / monadic bind.
-
#initialize(&fn) ⇒ Parser
constructor
A new instance of Parser.
-
#map ⇒ Object
Transform the successful value without consuming further input.
-
#|(other) ⇒ Object
Ordered choice (PEG
/).
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 |
#filter ⇒ Object
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_map ⇒ Object
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 |
#map ⇒ Object
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 |