Class: FuPeg::Parser
- Inherits:
-
Object
show all
- Defined in:
- lib/fupeg/parser.rb
Defined Under Namespace
Classes: CutPoint, Fail, Position
Instance Attribute Summary collapse
Instance Method Summary
collapse
Constructor Details
#initialize(str, pos = 0) ⇒ Parser
Returns a new instance of Parser.
12
13
14
|
# File 'lib/fupeg/parser.rb', line 12
def initialize(str, pos = 0)
reset!(str, pos)
end
|
Instance Attribute Details
#debug ⇒ Object
Returns the value of attribute debug.
7
8
9
|
# File 'lib/fupeg/parser.rb', line 7
def debug
@debug
end
|
#failed ⇒ Object
Returns the value of attribute failed.
9
10
11
|
# File 'lib/fupeg/parser.rb', line 9
def failed
@failed
end
|
#file ⇒ Object
Returns the value of attribute file.
8
9
10
|
# File 'lib/fupeg/parser.rb', line 8
def file
@file
end
|
#str ⇒ Object
Returns the value of attribute str.
10
11
12
|
# File 'lib/fupeg/parser.rb', line 10
def str
@str
end
|
Instance Method Details
#backtrack ⇒ Object
204
205
206
207
208
209
210
211
212
213
214
215
216
217
|
# File 'lib/fupeg/parser.rb', line 204
def backtrack
pos = @scan.pos
res = yield
if res
@failed = nil if @failed && @failed.bytepos <= @scan.pos
res
else
@scan.pos = pos
nil
end
rescue
@scan.pos = pos
raise
end
|
#bounds(lit = nil, &block) ⇒ Object
173
174
175
176
|
# File 'lib/fupeg/parser.rb', line 173
def bounds(lit = nil, &block)
pos = @scan.pos
match(lit, &block) && pos...@scan.pos
end
|
#bytepos ⇒ Object
31
32
33
|
# File 'lib/fupeg/parser.rb', line 31
def bytepos
@scan.pos
end
|
#charpos(pos = @scan.pos) ⇒ Object
35
36
37
|
# File 'lib/fupeg/parser.rb', line 35
def charpos(pos = @scan.pos)
@str_size - @str.byteslice(pos..).size
end
|
#current_cutpoint ⇒ Object
113
114
115
|
# File 'lib/fupeg/parser.rb', line 113
def current_cutpoint
@cut
end
|
#dot ⇒ Object
196
197
198
|
# File 'lib/fupeg/parser.rb', line 196
def dot
match(/./m)
end
|
#eof? ⇒ Boolean
200
201
202
|
# File 'lib/fupeg/parser.rb', line 200
def eof?
@scan.eos?
end
|
#fail!(pat: nil, skip: 2) ⇒ Object
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
# File 'lib/fupeg/parser.rb', line 41
def fail!(*, pat: nil, skip: 2)
if debug || !@failed || bytepos > @failed.bytepos
stack = caller_locations(skip)
stack.delete_if do |loc|
path = loc.path
if path == __FILE__
true
elsif path.start_with?(__dir__)
loc.label =~ /\b(backtrack|each|block)\b/
end
end
@failed = Fail.new(stack, bytepos, pat)
report_failed($stderr) if debug
end
nil
end
|
#failed_position ⇒ Object
58
59
60
|
# File 'lib/fupeg/parser.rb', line 58
def failed_position
position(bytepos: @failed.bytepos)
end
|
#init_line_ends ⇒ Object
121
122
123
124
125
126
127
128
|
# File 'lib/fupeg/parser.rb', line 121
def init_line_ends
@line_ends = [-1]
scan = StringScanner.new(@str)
while scan.skip_until(/\n|\r\n?/)
@line_ends << scan.pos - 1
end
@line_ends << @str.bytesize
end
|
#look_ahead(positive, lit = nil, &block) ⇒ Object
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
# File 'lib/fupeg/parser.rb', line 219
def look_ahead(positive, lit = nil, &block)
if block
p, f = @scan.pos, @failed
r = yield
@scan.pos = p
if positive ? r : !r
@failed = f
true
else
fail!
end
else
m = @scan.match?(lit)
if positive ? m : !m
true
else
fail!(pat: lit)
end
end
end
|
#position(bytepos: @scan.pos) ⇒ Object
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
# File 'lib/fupeg/parser.rb', line 130
def position(bytepos: @scan.pos)
lineno = @line_ends.bsearch_index { |x| x >= bytepos }
case lineno
when nil
raise "Position #{bytepos} is larger than string byte size #{@str.bytesize}"
else
prev_end = @line_ends[lineno - 1]
line_start = prev_end + 1
column = @str.byteslice(line_start, bytepos - prev_end).size
end
if bytepos == @str.bytesize
if @str[-1] == "\n"
lineno, column = lineno + 1, 1
else
column += 1
end
end
line = @str.byteslice(line_start..@line_ends[lineno])
Position.new(lineno, column, line, charpos(bytepos))
end
|
#repetition(range = 0, lit = nil, &block) ⇒ Object
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
# File 'lib/fupeg/parser.rb', line 178
def repetition(range = 0.., lit = nil, &block)
range = range..range if Integer === range
range = 0..range.max if range.begin.nil?
unless Integer === range.min && (range.end.nil? || Integer === range.max)
raise "Range malformed #{range}"
end
backtrack do
max = range.end && range.max
ar = []
(1..max).each do |i|
res = backtrack { yield i == 1 }
break unless res
ar << res
end
(ar.size >= range.min) ? ar : fail!
end
end
|
#report_failed(out) ⇒ Object
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
# File 'lib/fupeg/parser.rb', line 62
def report_failed(out)
pos = position(bytepos: @failed.bytepos)
out << if @failed.pattern
"Failed #{failed.pattern.inspect} at #{pos.lineno}:#{pos.colno}"
else
"Failed at #{pos.lineno}:#{pos.colno}"
end
if @file
out << " of #{@file}"
end
out << ":\n"
out << pos.line.chomp + "\n"
curpos = pos.line[...pos.colno].gsub("\t", " " * 8).size
curpos = 1 if curpos == 0 && @failed.bytepos == @str.bytesize
out << (" " * (curpos - 1) + "^\n")
out << "Call stack:\n"
@failed.stack.each do |loc|
out << "#{loc.path}:#{loc.lineno} in #{loc.label}\n"
end
out
end
|
#reset!(str = nil, pos = nil) ⇒ Object
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
# File 'lib/fupeg/parser.rb', line 16
def reset!(str = nil, pos = nil)
if str
@str = str.dup
@str_size = str.size
init_line_ends
@scan = StringScanner.new(str)
end
if pos
@scan.pos = pos
end
@failed = nil
@debug = false
@cut = CutPoint.new
end
|
#text(lit = nil, &block) ⇒ Object
168
169
170
171
|
# File 'lib/fupeg/parser.rb', line 168
def text(lit = nil, &block)
pos = @scan.pos
match(lit, &block) && @str.byteslice(pos, @scan.pos - pos)
end
|
#with_cut_point ⇒ Object
for use with cut! and cont?
103
104
105
106
107
108
109
110
111
|
# File 'lib/fupeg/parser.rb', line 103
def with_cut_point
prev_cut = @cut
@cut = CutPoint.new
prev_cut.next = @cut
yield @cut
ensure
prev_cut.next = nil
@cut = prev_cut
end
|