Class: KPeg::Parser

Inherits:
StringScanner
  • Object
show all
Includes:
Position
Defined in:
lib/kpeg/parser.rb

Defined Under Namespace

Classes: LeftRecursive, MemoEntry

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Position

#current_character, #current_column, #current_line, #current_pos_info, #get_line, #lines, #position_line_offsets

Constructor Details

#initialize(str, grammar, log = false) ⇒ Parser

Returns a new instance of Parser.



5
6
7
8
9
10
11
12
13
14
15
# File 'lib/kpeg/parser.rb', line 5

def initialize(str, grammar, log=false)
  super str

  @grammar = grammar
  # A 2 level hash.
  @memoizations = Hash.new { |h,k| h[k] = {} }

  @failing_offset = nil
  @failing_op = nil
  @log = log
end

Instance Attribute Details

#failing_offsetObject (readonly)

Returns the value of attribute failing_offset.



17
18
19
# File 'lib/kpeg/parser.rb', line 17

def failing_offset
  @failing_offset
end

#failing_opObject

Returns the value of attribute failing_op.



18
19
20
# File 'lib/kpeg/parser.rb', line 18

def failing_op
  @failing_op
end

#grammarObject (readonly)

Returns the value of attribute grammar.



17
18
19
# File 'lib/kpeg/parser.rb', line 17

def grammar
  @grammar
end

#memoizationsObject (readonly)

Returns the value of attribute memoizations.



17
18
19
# File 'lib/kpeg/parser.rb', line 17

def memoizations
  @memoizations
end

Instance Method Details

#apply(rule) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/kpeg/parser.rb', line 81

def apply(rule)
  ans = nil
  if m = @memoizations[rule][pos]
    m.inc!

    self.pos = m.pos
    if m.ans.kind_of? LeftRecursive
      m.ans.detected = true
      if @log
        puts "LR #{rule.name} @ #{self.inspect}"
      end
      return nil
    end

    ans = m.ans
  else
    lr = LeftRecursive.new(false)
    m = MemoEntry.new(lr, pos)
    @memoizations[rule][pos] = m
    start_pos = pos

    if @log
      puts "START #{rule.name} @ #{self.inspect}"
    end

    ans = rule.op.match(self)

    m.move! ans, pos

    # Don't bother trying to grow the left recursion
    # if it's failing straight away (thus there is no seed)
    if ans and lr.detected
      ans = grow_lr(rule, start_pos, m)
    end
  end

  if @log
    if ans
      puts "   OK #{rule.name} @ #{self.inspect}"
    else
      puts " FAIL #{rule.name} @ #{self.inspect}"
    end
  end
  return ans
end

#expectationObject



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/kpeg/parser.rb', line 165

def expectation
  error_pos = @failing_offset
  line_no = current_line(error_pos)
  col_no = current_column(error_pos)

  expected = expected_string()

  prefix = nil

  case expected
  when String
    prefix = expected.inspect
  when Range
    prefix = "to be between #{expected.begin} and #{expected.end}"
  when Array
    prefix = "to be one of #{expected.inspect}"
  when nil
    prefix = "anything (no more input)"
  else
    prefix = "unknown"
  end

  return "Expected #{prefix} at line #{line_no}, column #{col_no} (offset #{error_pos})"
end

#expected_stringObject



38
39
40
41
42
43
44
45
46
47
# File 'lib/kpeg/parser.rb', line 38

def expected_string
  case @failing_op
  when Choice
    return Range.new(@failing_op.start, @failing_op.fin)
  when Dot
    return nil
  else
    @failing_op.string
  end
end

#fail(op) ⇒ Object



32
33
34
35
36
# File 'lib/kpeg/parser.rb', line 32

def fail(op)
  @failing_offset = pos
  @failing_op = op
  return nil
end

#failed?Boolean

Returns:

  • (Boolean)


142
143
144
# File 'lib/kpeg/parser.rb', line 142

def failed?
  !!@failing_op
end

#grow_lr(rule, start_pos, m) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/kpeg/parser.rb', line 127

def grow_lr(rule, start_pos, m)
  while true
    self.pos = start_pos
    ans = rule.op.match(self)
    return nil unless ans

    break if pos <= m.pos

    m.move! ans, pos
  end

  self.pos = m.pos
  return m.ans
end

#invoke(rule) ⇒ Object

Call a rule without memoization



77
78
79
# File 'lib/kpeg/parser.rb', line 77

def invoke(rule)
  rule.op.match(self)
end

#parse(name = nil) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/kpeg/parser.rb', line 146

def parse(name=nil)
  if name
    rule = @grammar.find(name)
    unless rule
      raise "Unknown rule - #{name}"
    end
  else
    rule = @grammar.root
  end

  match = apply rule

  if pos == string.size
    @failing_op = nil
  end

  return match
end

#switch_grammar(gram) ⇒ Object



22
23
24
25
26
27
28
29
30
# File 'lib/kpeg/parser.rb', line 22

def switch_grammar(gram)
  begin
    old = @grammar
    @grammar = gram
    yield
  ensure
    @grammar = old
  end
end