module Parsby::Combinators::ModuleMethods

Public Instance Methods

define_combinator(name, wrap: true, &b) click to toggle source

The only reason to use this over regular def syntax is to get automatic labels. For combinators defined with this, you'll get labels that resemble the corresponding ruby expression.

# File lib/parsby/combinators.rb, line 9
def define_combinator(name, wrap: true, &b)
  # Convert block to method. This is necessary not only to convert
  # the proc to something that'll verify arity, but also to get
  # super() in b to work.
  define_method(name, &b)
  m = if defined? instance_method
    instance_method name
  else
    # self is probably main
    method(name).unbind
  end

  # Lambda used to access private module method from instance method.
  inspectable_labels_lambda = lambda {|x| inspectable_labels(x) }

  define_method name do |*args, &b2|
    inspected_args = inspectable_labels_lambda.call(args).map(&:inspect)
    label = name.to_s
    label += "(#{inspected_args.join(", ")})" unless inspected_args.empty?
    # Wrap in new parser so we don't overwrite another automatic
    # label.
    p = m.bind(self).call(*args, &b2)
    if wrap
      Parsby.new(label) {|c| p.parse c }
    else
      p % label
    end
  end
end

Private Instance Methods

included(base) click to toggle source
# File lib/parsby/combinators.rb, line 70
def included(base)
  base.extend ModuleMethods
end
inspectable_as(s) click to toggle source

Returns an object whose inspect representation is exactly as given in the argument string.

# File lib/parsby/combinators.rb, line 43
def inspectable_as(s)
  Object.new.tap do |obj|
    obj.define_singleton_method :inspect do
      s
    end
  end
end
inspectable_labels(arg) click to toggle source

Deeply traverses arrays and hashes changing each Parsby object to another object that returns their label on inspect. The point of this is to be able to inspect the result and get something resembling the original combinator expression. Instead of writing this method, I could also just have redefined inspect on Parsby to return the label, but I like ruby's default inspect in general.

# File lib/parsby/combinators.rb, line 57
def inspectable_labels(arg)
  case arg
  when Parsby
    inspectable_as arg.label
  when Array # for methods like group() that accept arguments spliced or not
    arg.map(&method(:inspectable_labels))
  when Hash # for key arguments
    arg.map {|k, v| [k, inspectable_labels(v)] }.to_h
  else
    arg
  end
end