class PegLite

Constants

VERSION

Attributes

debug[RW]
got[RW]
input[RW]
wrap[RW]

Public Class Methods

new(attrs={}) { |self| ... } click to toggle source
# File lib/peglite.rb, line 53
def initialize attrs={}
  @got = nil
  @wrap = false
  @debug = false
  @input = nil

  attrs.each { |k,v| self.send "#{k}=", v }

  @pos = 0
  @far = 0
  yield self if block_given?
end
rule(args) click to toggle source
# File lib/peglite.rb, line 12
def self.rule args
  name, rule = args.first
  name = name.to_s
  $PegLiteTopRule ||= name
  if rule.kind_of? Regexp
    regex = Regexp.new(rule.to_s.sub(/:/, ':\\A'))
    rule = {
      'type' => 'rgx',
      'rule' => regex,
      'min' => 1,
      'max' => 1,
    }
  elsif rule.kind_of? String
    rule = PegLite::Compiler.new(rule).compile
  else
    fail "Don't know how to make rule '#{name}' from '#{rule}'"
  end
  self.send :define_method, "rule_#{name}" do
    match rule
  end
end

Public Instance Methods

failure() click to toggle source
# File lib/peglite.rb, line 173
def failure
  msg = "Parse failed for some reason"
  raise PegexParseError, format_error(msg)
end
format_error(msg) click to toggle source
# File lib/peglite.rb, line 179
  def format_error msg
    buffer = @input
    position = @far
    real_pos = @pos

    line = buffer[0, position].scan(/\n/).size + 1
    column = position - (buffer.rindex("\n", position) || -1)

    pretext = @input[
      position < 50 ? 0 : position - 50,
      position < 50 ? position : 50
    ]
    context = @input[position, 50]
    pretext.gsub! /.*\n/m, ''
    context.gsub! /\n/, "\\n"

    return <<"..."
Error parsing Pegex document:
  message:  #{msg}
  line:     #{line}
  column:   #{column}
  position: #{position}
  context:  #{pretext}#{context}
  #{' ' * (pretext.length + 10)}^
...
  end
match(rule=nil) click to toggle source
# File lib/peglite.rb, line 75
def match rule=nil
  if not rule.kind_of? Hash
    rule ||= caller.first.scan(/(\w+)/).last.first
    method_name = "rule_#{rule}"
    return send method_name if respond_to? method_name
    fail "Can't find rule for '#{rule}'"
  end

  pos, count, matched, type, child, min, max =
    @pos, 0, [], *(rule.values_at *%w(type rule min max))

  while (result = self.method("match_#{type}").call(child))
    pos = @pos
    count += 1
    if result.kind_of? Array
      matched.concat result
    else
      matched << result
    end
    break if max == 1
  end

  if count >= min and (max == 0 or count <= max)
    return matched
  else
    @pos = pos
    return
  end
end
match_all(all) click to toggle source
# File lib/peglite.rb, line 105
def match_all all
  pos, set, count = @pos, [], 0
  all.each do |elem|
    if (m = match elem)
      set.concat m
      count += 1
    else
      if (@pos = pos) > @far
        @far = pos
      end
      return
    end
  end
  set = [ set ] if count > 1
  return set
end
match_any(any) click to toggle source
# File lib/peglite.rb, line 122
def match_any any
  any.each do |elem|
    if (m = match elem)
      return m
    end
  end
  return
end
match_ref(ref) click to toggle source

TODO move trace/debug out of default match_ref method

# File lib/peglite.rb, line 132
def match_ref ref
  trace "Try #{ref}" if @debug
  method = [ ref, "rule_#{ref}" ].find{|e| respond_to? e}
  fail "No rule defined for '#{ref}'" unless method
  m = send method
  if m
    m = (@wrap and not m.empty?) ? [{ref => m}] : m
    trace "Got #{ref}" if @debug
  else
    trace "Not #{ref}" if @debug
  end
  return m
end
match_rgx(regex) click to toggle source
# File lib/peglite.rb, line 146
def match_rgx regex
  m = @input[@pos..-1].match(regex)
  return unless m
  @pos += m[0].length
  match = m.captures
  # XXX not sure about this:
  match = [ match ] if m.length > 2
  @far = @pos if @pos > @far
  return match
end
parse(input=@input, top=($PegLiteTopRule || 'top')) click to toggle source
# File lib/peglite.rb, line 66
def parse input=@input, top=($PegLiteTopRule || 'top')
  fail "PegLite parse() method requires an input string" \
    unless input.kind_of? String
  @input = input
  got = match_ref top
  failure if @pos < @input.length
  return @got || got[0]
end
trace(action) click to toggle source
# File lib/peglite.rb, line 160
def trace action
  indent = !!action.match(/^Try /)
  @indent ||= 0
  @indent -= 1 unless indent
  $stderr.print ' ' * @indent
  @indent += 1 if indent
  snippet = @input[@pos..-1]
  snippet = snippet[0..30] + '...' if snippet.length > 30;
  snippet.gsub! /\n/, "\\n"
  $stderr.printf "%-30s", action
  $stderr.print indent ? " >#{snippet}<\n" : "\n"
end