Class: Pegparse::ParserCore

Inherits:
Object
  • Object
show all
Included in:
BiopRuleChain
Defined in:
lib/pegparse/parser_core.rb

Overview

Parser base class (core mechanism for backtracking)

Direct Known Subclasses

ParserBase

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scanner_or_context) ⇒ ParserCore



10
11
12
# File 'lib/pegparse/parser_core.rb', line 10

def initialize(scanner_or_context)
  init_context(scanner_or_context) if scanner_or_context
end

Instance Attribute Details

#start_rule_symbolSymbol

start rule symbol used by parse()



7
8
9
# File 'lib/pegparse/parser_core.rb', line 7

def start_rule_symbol
  @start_rule_symbol
end

Class Method Details

.rule(method_sym) ⇒ Symbol

Wrap method as nonterminal symbol rule.



168
169
170
171
# File 'lib/pegparse/parser_core.rb', line 168

def self.rule(method_sym)
  self.wrap_with_trace_method(method_sym)
  method_sym
end

.wrap_with_trace_method(method_sym) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/pegparse/parser_core.rb', line 151

def self.wrap_with_trace_method(method_sym)
  original_method_sym = ('original_' + method_sym.to_s).to_sym
  unless self.method_defined?(original_method_sym)
    self.alias_method original_method_sym, method_sym
    self.define_method(method_sym) do |*args|
      @context.rule_stack.push [@context.scanner.pos, method_sym]
      ret = self.__send__(original_method_sym, *args)
      return ret
    ensure
      @context.rule_stack.pop
    end
  end
end

Instance Method Details

#backtrackObject



147
148
149
# File 'lib/pegparse/parser_core.rb', line 147

def backtrack()
  throw :backtrack
end

#backtrack_position_to(pos) ⇒ Object



50
51
52
53
# File 'lib/pegparse/parser_core.rb', line 50

def backtrack_position_to(pos)
  @context.scanner.pos = pos
  @context.borrowed_areas.backtracked(pos)
end

#best_errorsArray

parse error info



57
58
59
60
61
62
63
# File 'lib/pegparse/parser_core.rb', line 57

def best_errors
  @context.errors.best_errors.map{|error|
    error.map{|rule|
      [ @context.line_counter.position(rule.pos), rule.reason ]
    }
  }
end

#borrow_next_line(&block) ⇒ Object

Temporarily change scanner position to next line(use for here-document) area consumed by block becomes non-matchable().



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/pegparse/parser_core.rb', line 212

def borrow_next_line(&block)
  mark_pos = @context.scanner.pos
  if @context.borrowed_areas.borrowed_area_end_pos
    borrowed_start_pos = @context.borrowed_areas.borrowed_area_end_pos
  else
    read(/.*\n/)
    borrowed_start_pos = @context.scanner.pos
  end
  @context.scanner.pos = borrowed_start_pos
  ret = block.call
  borrowed_end_pos = @context.scanner.pos
  @context.scanner.pos = mark_pos
  @context.borrowed_areas.add_area(Pegparse::BorrowedArea.new(
    marker_pos: mark_pos,
    start_pos: borrowed_start_pos,
    end_pos: borrowed_end_pos,
  ))
  return ret
end

#borrowed_areaObject

match to borrowed area



233
234
235
236
237
238
239
240
241
242
# File 'lib/pegparse/parser_core.rb', line 233

def borrowed_area
  if area = @context.borrowed_areas.conflicted_area(@context.scanner.pos)
    if area.start_pos == @context.scanner.pos
      ret = @context.scanner.peek(area.end_pos - area.start_pos)
      @context.scanner.pos = area.end_pos
      return ret
    end
  end
  backtrack()
end

#choice(*alter_procs) ⇒ Object

Try to match some candidates in order. (PEG’s choice operator) Backtrack if all match failed.



178
179
180
181
182
183
184
# File 'lib/pegparse/parser_core.rb', line 178

def choice(*alter_procs)
  alter_procs.each do |alter_proc|
    ret = optional{ alter_proc.call() }
    return ret if ret
  end
  backtrack()
end

#eos?Boolean



42
43
44
# File 'lib/pegparse/parser_core.rb', line 42

def eos?
  @context.scanner.eos?
end

#init_context(scanner_or_context) ⇒ Object

initialize inner state



15
16
17
18
19
20
21
# File 'lib/pegparse/parser_core.rb', line 15

def init_context(scanner_or_context)
  if scanner_or_context.is_a? Pegparse::ParserContext
    @context = scanner_or_context
  else
    @context = Pegparse::ParserContext.new(scanner_or_context)
  end
end

#one_or_more(&block) ⇒ Array<Object>

Try to match in loop. Backtrack if no loop succeeded.



200
201
202
203
204
205
206
207
208
# File 'lib/pegparse/parser_core.rb', line 200

def one_or_more(&block)
  ret = [block.call()]
  while true
    val = optional { block.call() }
    break unless val
    ret << val
  end
  return ret
end

#optional(str_or_regexp = nil, &block) ⇒ String, Object

Match with pattern or block. Returns nil if match failed.

Raises:

  • (ArgumentError)


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/pegparse/parser_core.rb', line 124

def optional(str_or_regexp = nil, &block)
  raise ArgumentError if str_or_regexp && block
  raise ArgumentError if !str_or_regexp && !block

  if block
    bk_pos = @context.scanner.pos
    ret = nil
    catch(:backtrack) do
      @context.rule_stack.push [@context.scanner.pos, :optional]
      ret = block.call()
      return ret
    ensure
      @context.rule_stack.pop
    end
    backtrack_position_to(bk_pos)
    return nil
  end

  ret = peek(str_or_regexp)
  @context.scanner.pos += ret.bytesize if ret
  return ret
end

#parse(scanner_or_context = nil, rule: nil) ⇒ Object

Start parse

Raises:

  • (ArgumentError)


27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/pegparse/parser_core.rb', line 27

def parse(scanner_or_context = nil, rule: nil)
  raise ArgumentError if !scanner_or_context && !@context
  raise ArgumentError if !rule && !@start_rule_symbol

  init_context(scanner_or_context) if scanner_or_context
  current_start_rule_symbol = rule || @start_rule_symbol

  ret = nil
  catch(:backtrack) do
    ret = __send__(current_start_rule_symbol)
  end
  @context.errors.clear_errors if eos?
  return ret
end

#peek(str_or_regexp = nil, &block) ⇒ String, Object

Check whether matching will success or not.

Raises:

  • (ArgumentError)


68
69
70
71
72
73
74
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
104
105
# File 'lib/pegparse/parser_core.rb', line 68

def peek(str_or_regexp = nil, &block)
  raise ArgumentError if str_or_regexp && block
  raise ArgumentError if !str_or_regexp && !block

  if block
    bk_pos = @context.scanner.pos
    ret = nil
    catch(:backtrack) do
      ret = block.call()
    end
    backtrack_position_to(bk_pos)
    return ret
  end

  if str_or_regexp.is_a?(String)
    if @context.scanner.match?(str_or_regexp)
      @context.line_counter.memo(@context.scanner.pos, str_or_regexp)
      if @context.borrowed_areas.conflicted_area(@context.scanner.pos + str_or_regexp.bytesize - 1)
        return nil
      end
      return str_or_regexp
    else
      return nil
    end
  end
  if str_or_regexp.is_a?(Regexp)
    if (size = @context.scanner.match?(str_or_regexp))
      str = @context.scanner.peek(size)
      @context.line_counter.memo(@context.scanner.pos, str)
      if @context.borrowed_areas.conflicted_area(@context.scanner.pos + size - 1)
        return nil
      end
      return str
    end
    return nil
  end
  raise ArgumentError
end

#read(str_or_regexp) ⇒ String

Match with pattern. Backtrack if match failed.

Raises:

  • (ArgumentError)


110
111
112
113
114
115
116
117
118
119
# File 'lib/pegparse/parser_core.rb', line 110

def read(str_or_regexp)
  raise ArgumentError unless str_or_regexp
  ret = peek(str_or_regexp)
  if ret
    @context.scanner.pos += ret.bytesize
    return ret
  end
  save_error(str_or_regexp)
  backtrack()
end

#save_error(reason) ⇒ Object



46
47
48
# File 'lib/pegparse/parser_core.rb', line 46

def save_error(reason)
  @context.errors.save_error(@context.scanner.pos, @context.rule_stack, reason)
end

#zero_or_more(&block) ⇒ Array<Object>

Try to match in loop. Returns [] even no loop succeeded.



188
189
190
191
192
193
194
195
196
# File 'lib/pegparse/parser_core.rb', line 188

def zero_or_more(&block)
  ret = []
  while true
    val = optional { block.call() }
    break unless val
    ret << val
  end
  return ret
end