Class: PegLite

Inherits:
Object
  • Object
show all
Defined in:
lib/peglite.rb,
lib/peglite/compiler.rb

Overview


Defined Under Namespace

Classes: Compiler, PegexParseError

Constant Summary collapse

VERSION =
'0.0.3'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs = {}) {|_self| ... } ⇒ PegLite

Returns a new instance of PegLite.

Yields:

  • (_self)

Yield Parameters:

  • _self (PegLite)

    the object that the method was called on



53
54
55
56
57
58
59
60
61
62
63
64
# 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

Instance Attribute Details

#debugObject

Returns the value of attribute debug.



51
52
53
# File 'lib/peglite.rb', line 51

def debug
  @debug
end

#gotObject

Returns the value of attribute got.



49
50
51
# File 'lib/peglite.rb', line 49

def got
  @got
end

#inputObject

Returns the value of attribute input.



52
53
54
# File 'lib/peglite.rb', line 52

def input
  @input
end

#wrapObject

Returns the value of attribute wrap.



50
51
52
# File 'lib/peglite.rb', line 50

def wrap
  @wrap
end

Class Method Details

.rule(args) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 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

Instance Method Details

#failureObject

Raises:



173
174
175
176
# File 'lib/peglite.rb', line 173

def failure
  msg = "Parse failed for some reason"
  raise PegexParseError, format_error(msg)
end

#format_error(msg) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# 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) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 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) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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) ⇒ Object



122
123
124
125
126
127
128
129
# 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) ⇒ Object

TODO move trace/debug out of default match_ref method



132
133
134
135
136
137
138
139
140
141
142
143
144
# 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) ⇒ Object



146
147
148
149
150
151
152
153
154
155
# 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')) ⇒ Object



66
67
68
69
70
71
72
73
# 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) ⇒ Object


Debugging and error reporting support methods




160
161
162
163
164
165
166
167
168
169
170
171
# 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