class Parsby
Constants
- VERSION
Attributes
Public Class Methods
Initialize parser with optional label argument, and parsing block. The parsing block is given an IO as argument, and its result is the result when parsing.
# File lib/parsby.rb, line 606 def initialize(label = nil, &b) self.label = label if label @parser = b end
Public Instance Methods
Set the label and return self.
# File lib/parsby.rb, line 709 def %(name) self.label = name self end
p * n, runs parser p n times, grouping results in an array.
# File lib/parsby.rb, line 684 def *(n) Parsby.new "(#{label} * #{n})" do |c| n.times.map { parse c } end end
x + y does + on the results of x and y. This is mostly meant to be used with arrays, but it would work with numbers and strings too.
# File lib/parsby.rb, line 692 def +(p) group(self, p) .fmap {|(x, y)| x + y } .tap {|r| r.label = "(#{label} + #{p.label})" } end
x < y runs parser x then y and returns x.
# File lib/parsby.rb, line 659 def <(p) ~splicer.start do |m| m.end(self).then {|r| m.end(p).then { pure r } } end % "(#{label} < #{p.label})" end
xs << x appends result of parser x to list result of parser xs.
# File lib/parsby.rb, line 699 def <<(p) Parsby.new "(#{label} << #{p.label})" do |c| x = parse c y = p.parse c # like x << y, but without modifying x. x + [y] end end
x > y runs parser x then y and returns y.
# File lib/parsby.rb, line 666 def >(p) self.then { p } % "(#{label} > #{p.label})" end
Like map for arrays, this lets you work with the value “inside” the parser, i.e. the result.
Example:
decimal.fmap {|x| x + 1}.parse("2") => 3
# File lib/parsby.rb, line 721 def fmap(&b) Parsby.new "#{label}.fmap" do |c| b.call parse c end end
The parser's label. It's an “unknown” token by default.
# File lib/parsby.rb, line 597 def label @label || "unknown" end
Parse a String or IO object.
# File lib/parsby.rb, line 612 def parse(src) ctx = src.is_a?(Context) ? src : Context.new(src) parsed_range = ParsedRange.new(ctx.bio.pos, ctx.bio.pos, label) ctx.parsed_ranges << parsed_range if ctx.parsed_ranges parent_parsed_range = ctx.parsed_ranges ctx.parsed_ranges = parsed_range begin r = @parser.call ctx rescue ExpectationFailed => e ctx.parsed_ranges.end = ctx.bio.pos ctx.parsed_ranges.failed = true ctx.bio.restore_to ctx.parsed_ranges.start raise else ctx.parsed_ranges.end = ctx.bio.pos r ensure # Keep the root one for use in ExceptionFailed#message if parent_parsed_range ctx.parsed_ranges = parent_parsed_range end end end
Parses without consuming input.
# File lib/parsby.rb, line 637 def peek(src) ctx = src.is_a?(Context) ? src : Context.new(src) starting_pos = ctx.bio.pos begin parse ctx ensure ctx.bio.restore_to starting_pos end end
x.that_fails(y)
will try y
, fail if y
succeeds, or parse with x
if y
fails.
Example:
decimal.that_fails(string("10")).parse "3" => 3 decimal.that_fails(string("10")).parse "10" Parsby::ExpectationFailed: line 1: 10 \/ expected: (not "10")
# File lib/parsby.rb, line 762 def that_fails(p) Parsby.new "#{label}.that_fails(#{p.label})" do |c| orig_pos = c.bio.pos begin r = p.parse c.bio rescue Error c.bio.restore_to orig_pos parse c.bio else raise ExpectationFailed.new c end end end
Pass result of self parser to block to construct the next parser.
For example, instead of writing:
Parsby.new do |c| x = foo.parse c bar(x).parse c end
you can write:
foo.then {|x| bar x }
This is analogous to Parsec's >>= operator in Haskell, where you could write:
foo >>= bar
# File lib/parsby.rb, line 744 def then(&b) Parsby.new "#{label}.then" do |c| b.call(parse(c)).parse(c) end end
x | y
tries y if x fails.
# File lib/parsby.rb, line 648 def |(p) Parsby.new "(#{self.label} | #{p.label})" do |c| begin parse c rescue Error p.parse c end end end
# File lib/parsby.rb, line 670 def ~ Parsby.new "(~ #{label})" do |c| begin parse c ensure c.parsed_ranges.children[0].splice_self! if c.parsed_ranges.parent c.parsed_ranges.splice_self! end end end end