packcr 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/packcr/buffer.rb +47 -0
- data/lib/packcr/code_block.rb +13 -0
- data/lib/packcr/context.rb +982 -0
- data/lib/packcr/generator.rb +35 -0
- data/lib/packcr/node/action_node.rb +52 -0
- data/lib/packcr/node/alternate_node.rb +71 -0
- data/lib/packcr/node/capture_node.rb +34 -0
- data/lib/packcr/node/charclass_node.rb +75 -0
- data/lib/packcr/node/error_node.rb +52 -0
- data/lib/packcr/node/expand_node.rb +36 -0
- data/lib/packcr/node/predicate_node.rb +51 -0
- data/lib/packcr/node/quantity_node.rb +55 -0
- data/lib/packcr/node/reference_node.rb +46 -0
- data/lib/packcr/node/rule_node.rb +30 -0
- data/lib/packcr/node/sequence_node.rb +61 -0
- data/lib/packcr/node/string_node.rb +43 -0
- data/lib/packcr/node.rb +17 -0
- data/lib/packcr/stream.rb +101 -0
- data/lib/packcr/util.rb +94 -0
- data/lib/packcr/version.rb +3 -0
- data/lib/packcr.rb +33 -0
- metadata +77 -0
@@ -0,0 +1,982 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "stringio"
|
3
|
+
|
4
|
+
class Packcr
|
5
|
+
class Context
|
6
|
+
def initialize(path, lines: false, debug: false, ascii: false, lang: nil)
|
7
|
+
if !path
|
8
|
+
raise ArgumentError, "bad path: #{path}";
|
9
|
+
end
|
10
|
+
|
11
|
+
@iname = path
|
12
|
+
@ifile = File.open(path, "rb")
|
13
|
+
dirname = File.dirname(path)
|
14
|
+
basename = File.basename(path, ".*")
|
15
|
+
if !lang
|
16
|
+
lang = File.extname(basename)[1..-1]&.to_sym
|
17
|
+
if lang
|
18
|
+
basename = File.basename(basename, ".*")
|
19
|
+
else
|
20
|
+
lang = :c
|
21
|
+
end
|
22
|
+
end
|
23
|
+
if dirname == "."
|
24
|
+
path = basename
|
25
|
+
else
|
26
|
+
path = File.join(dirname, basename)
|
27
|
+
end
|
28
|
+
|
29
|
+
@lang = lang.to_sym
|
30
|
+
case @lang
|
31
|
+
when :c
|
32
|
+
@sname = path + ".c"
|
33
|
+
@hname = path + ".h"
|
34
|
+
@hid = File.basename(@hname).upcase.gsub(/[^A-Z0-9]/, "_")
|
35
|
+
when :rb
|
36
|
+
@sname = path + ".rb"
|
37
|
+
@hname = nil
|
38
|
+
else
|
39
|
+
raise "unexpected lang: #{@lang}"
|
40
|
+
end
|
41
|
+
|
42
|
+
@lines = !!lines
|
43
|
+
@debug = !!debug
|
44
|
+
@ascii = !!ascii
|
45
|
+
|
46
|
+
@errnum = 0
|
47
|
+
@linenum = 0
|
48
|
+
@charnum = 0
|
49
|
+
@linepos = 0
|
50
|
+
@bufpos = 0
|
51
|
+
@bufcur = 0
|
52
|
+
|
53
|
+
@esource = []
|
54
|
+
@eheader = []
|
55
|
+
@source = []
|
56
|
+
@header = []
|
57
|
+
@lheader = []
|
58
|
+
@location = []
|
59
|
+
@rules = []
|
60
|
+
@rulehash = {}
|
61
|
+
@buffer = Packcr::Buffer.new
|
62
|
+
|
63
|
+
if block_given?
|
64
|
+
yield(self)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<#{self.class}:0x%016x>" % object_id
|
70
|
+
end
|
71
|
+
|
72
|
+
def error(line, col, message)
|
73
|
+
warn "#{@iname}:#{line}:#{col}: #{message}"
|
74
|
+
@errnum += 1
|
75
|
+
end
|
76
|
+
|
77
|
+
def value_type
|
78
|
+
@value_type || "int"
|
79
|
+
end
|
80
|
+
|
81
|
+
def auxil_type
|
82
|
+
@auxil_type || "void *"
|
83
|
+
end
|
84
|
+
|
85
|
+
def prefix
|
86
|
+
@prefix || "pcc"
|
87
|
+
end
|
88
|
+
|
89
|
+
def class_name
|
90
|
+
prefix.gsub(/(?:_|^)([a-z])/) { $1.upcase }
|
91
|
+
end
|
92
|
+
|
93
|
+
def auxil_def
|
94
|
+
type = auxil_type
|
95
|
+
"#{type}#{type =~ /\*$/ ? "" : " "}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def value_def
|
99
|
+
type = value_type
|
100
|
+
"#{type}#{type =~ /\*$/ ? "" : " "}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def eof?
|
104
|
+
refill_buffer(1) < 1
|
105
|
+
end
|
106
|
+
|
107
|
+
def eol?
|
108
|
+
return false if eof?
|
109
|
+
|
110
|
+
case @buffer[@bufcur]
|
111
|
+
when 0xa
|
112
|
+
@bufcur += 1
|
113
|
+
@linenum += 1
|
114
|
+
@charnum = 0
|
115
|
+
@linepos = @bufpos + @bufcur
|
116
|
+
true
|
117
|
+
when 0xd
|
118
|
+
@bufcur += 1
|
119
|
+
if !eof? && @buffer[@bufcur] == 0xd
|
120
|
+
@bufcur += 1
|
121
|
+
end
|
122
|
+
@linenum += 1
|
123
|
+
@charnum = 0
|
124
|
+
@linepos = @bufpos + @bufcur
|
125
|
+
true
|
126
|
+
else
|
127
|
+
false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def column_number
|
132
|
+
unless @bufpos + @bufcur >= @linepos
|
133
|
+
raise "invalid position: expect #{@bufpos + @bufcur} >= #{@linepos}"
|
134
|
+
end
|
135
|
+
offset = @linepos > @bufpos ? @linepos - @bufpos : 0
|
136
|
+
if @ascii
|
137
|
+
@charnum + @bufcur - offset
|
138
|
+
else
|
139
|
+
@charnum + @buffer.count_characters(offset, @bufcur)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def make_rulehash
|
144
|
+
@rules.each do |rule|
|
145
|
+
@rulehash[rule.name] = rule
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def refill_buffer(num = nil)
|
150
|
+
while !num || @buffer.len - @bufcur < num
|
151
|
+
c = @ifile.getc
|
152
|
+
break if c.nil?
|
153
|
+
@buffer.add(c.ord)
|
154
|
+
end
|
155
|
+
|
156
|
+
return @buffer.len - @bufcur
|
157
|
+
end
|
158
|
+
|
159
|
+
def commit_buffer
|
160
|
+
if @buffer.len < @bufcur
|
161
|
+
raise "unexpected buffer state: length(#{@buffer.len}), current(#{@bufcur})"
|
162
|
+
end
|
163
|
+
if @linepos < @bufpos + @bufcur
|
164
|
+
count = @ascii ? @bufcur : @buffer.count_characters(0, @bufcur)
|
165
|
+
@charnum += count
|
166
|
+
end
|
167
|
+
@buffer.add_pos(@bufcur)
|
168
|
+
@bufpos = @bufpos + @bufcur
|
169
|
+
@bufcur = 0
|
170
|
+
end
|
171
|
+
|
172
|
+
def write_buffer(stream)
|
173
|
+
n = @buffer.len
|
174
|
+
text = @buffer.to_s
|
175
|
+
if n > 0 && text[-1] == "\r"
|
176
|
+
text = text[0..-2]
|
177
|
+
end
|
178
|
+
stream.write_text(text)
|
179
|
+
@bufcur = n
|
180
|
+
end
|
181
|
+
|
182
|
+
def match_character(ch)
|
183
|
+
if refill_buffer(1) >= 1
|
184
|
+
if @buffer[@bufcur].ord == ch.ord
|
185
|
+
@bufcur += 1
|
186
|
+
return true
|
187
|
+
end
|
188
|
+
end
|
189
|
+
false
|
190
|
+
end
|
191
|
+
|
192
|
+
def match_character_range(min, max)
|
193
|
+
if refill_buffer(1) >= 1
|
194
|
+
c = @buffer[@bufcur].ord
|
195
|
+
if (min..max) === c
|
196
|
+
@bufcur += 1
|
197
|
+
return true
|
198
|
+
end
|
199
|
+
end
|
200
|
+
false
|
201
|
+
end
|
202
|
+
|
203
|
+
def match_character_set(chars)
|
204
|
+
if refill_buffer(1) >= 1
|
205
|
+
c = @buffer[@bufcur].ord
|
206
|
+
chars.each_byte do |ch|
|
207
|
+
if c == ch
|
208
|
+
@bufcur += 1
|
209
|
+
return true
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
false
|
214
|
+
end
|
215
|
+
|
216
|
+
def match_string(str)
|
217
|
+
n = str.length
|
218
|
+
if refill_buffer(n) >= n
|
219
|
+
if @buffer.to_s[@bufcur, n] == str
|
220
|
+
@bufcur += n
|
221
|
+
return true
|
222
|
+
end
|
223
|
+
end
|
224
|
+
false
|
225
|
+
end
|
226
|
+
|
227
|
+
def match_blank
|
228
|
+
match_character_set(" \t\v\f")
|
229
|
+
end
|
230
|
+
|
231
|
+
def match_character_any
|
232
|
+
if refill_buffer(1) >= 1
|
233
|
+
@bufcur += 1
|
234
|
+
return true
|
235
|
+
end
|
236
|
+
false
|
237
|
+
end
|
238
|
+
|
239
|
+
def match_section_line_(head)
|
240
|
+
if match_string(head)
|
241
|
+
while !eol? && !eof?
|
242
|
+
match_character_any
|
243
|
+
end
|
244
|
+
return true
|
245
|
+
end
|
246
|
+
false
|
247
|
+
end
|
248
|
+
|
249
|
+
def match_section_line_continuable_(head)
|
250
|
+
if match_string(head)
|
251
|
+
while !eof?
|
252
|
+
pos = @bufcur
|
253
|
+
if eol?
|
254
|
+
if @buffer[pos - 1] != "\\".ord
|
255
|
+
break
|
256
|
+
end
|
257
|
+
else
|
258
|
+
match_character_any
|
259
|
+
end
|
260
|
+
end
|
261
|
+
return true
|
262
|
+
end
|
263
|
+
false
|
264
|
+
end
|
265
|
+
|
266
|
+
def match_section_block_(left, right, name)
|
267
|
+
l = @linenum
|
268
|
+
m = column_number
|
269
|
+
if match_string(left)
|
270
|
+
while !match_string(right)
|
271
|
+
if eof?
|
272
|
+
error l + 1, m + 1, "Premature EOF in #{name}"
|
273
|
+
break
|
274
|
+
end
|
275
|
+
if !eol?
|
276
|
+
match_character_any
|
277
|
+
end
|
278
|
+
end
|
279
|
+
return true
|
280
|
+
end
|
281
|
+
false
|
282
|
+
end
|
283
|
+
|
284
|
+
def match_quotation_(left, right, name)
|
285
|
+
l = @linenum
|
286
|
+
m = column_number
|
287
|
+
if match_string(left)
|
288
|
+
while !match_string(right)
|
289
|
+
if eof?
|
290
|
+
error l + 1, m + 1, "Premature EOF in #{name}"
|
291
|
+
break
|
292
|
+
end
|
293
|
+
if match_character("\\".ord)
|
294
|
+
if !eol?
|
295
|
+
match_character_any
|
296
|
+
end
|
297
|
+
else
|
298
|
+
if eol?
|
299
|
+
error l + 1, m + 1, "Premature EOF in #{name}"
|
300
|
+
break
|
301
|
+
end
|
302
|
+
match_character_any
|
303
|
+
end
|
304
|
+
end
|
305
|
+
return true
|
306
|
+
end
|
307
|
+
false
|
308
|
+
end
|
309
|
+
|
310
|
+
def match_directive_c
|
311
|
+
match_section_line_continuable_("#")
|
312
|
+
end
|
313
|
+
|
314
|
+
def match_comment
|
315
|
+
match_section_line_("#")
|
316
|
+
end
|
317
|
+
|
318
|
+
def match_comment_c
|
319
|
+
match_section_block_("/*", "*/", "C comment")
|
320
|
+
end
|
321
|
+
|
322
|
+
def match_comment_cxx
|
323
|
+
match_section_line_("//")
|
324
|
+
end
|
325
|
+
|
326
|
+
def match_quotation_single
|
327
|
+
match_quotation_("\'", "\'", "single quotation")
|
328
|
+
end
|
329
|
+
|
330
|
+
def match_quotation_double
|
331
|
+
match_quotation_("\"", "\"", "double quotation")
|
332
|
+
end
|
333
|
+
|
334
|
+
def match_character_class
|
335
|
+
match_quotation_("[", "]", "character class")
|
336
|
+
end
|
337
|
+
|
338
|
+
def match_spaces
|
339
|
+
n = 0
|
340
|
+
while match_blank || eol? || match_comment
|
341
|
+
n += 1
|
342
|
+
end
|
343
|
+
n > 0
|
344
|
+
end
|
345
|
+
|
346
|
+
def match_number
|
347
|
+
if match_character_range("0".ord, "9".ord)
|
348
|
+
nil while match_character_range("0".ord, "9".ord)
|
349
|
+
return true
|
350
|
+
end
|
351
|
+
return false
|
352
|
+
end
|
353
|
+
|
354
|
+
def match_identifier
|
355
|
+
if match_character_range("a".ord, "z".ord) || match_character_range("A".ord, "Z".ord) || match_character("_".ord)
|
356
|
+
nil while match_character_range("a".ord, "z".ord) || match_character_range("A".ord, "Z".ord) || match_character_range("0".ord, "9".ord) || match_character("_".ord)
|
357
|
+
return true
|
358
|
+
end
|
359
|
+
false
|
360
|
+
end
|
361
|
+
|
362
|
+
def match_code_block
|
363
|
+
l = @linenum
|
364
|
+
m = column_number
|
365
|
+
if match_character("{".ord)
|
366
|
+
d = 1
|
367
|
+
while true
|
368
|
+
if eof?
|
369
|
+
error l + 1, m + 1, "Premature EOF in code block"
|
370
|
+
break
|
371
|
+
end
|
372
|
+
if match_directive_c || match_comment_c || match_comment_cxx || match_quotation_single || match_quotation_double
|
373
|
+
next
|
374
|
+
end
|
375
|
+
if match_character("{".ord)
|
376
|
+
d += 1
|
377
|
+
elsif match_character("}".ord)
|
378
|
+
d -= 1
|
379
|
+
if d == 0
|
380
|
+
break
|
381
|
+
end
|
382
|
+
else
|
383
|
+
if !eol?
|
384
|
+
if match_character("$".ord)
|
385
|
+
if @lang == :rb
|
386
|
+
@buffer[@bufcur - 1] = "__"
|
387
|
+
else
|
388
|
+
@buffer[@bufcur - 1] = "_"
|
389
|
+
end
|
390
|
+
else
|
391
|
+
match_character_any
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
return true
|
397
|
+
end
|
398
|
+
return false
|
399
|
+
end
|
400
|
+
|
401
|
+
def match_footer_start
|
402
|
+
match_string("%%")
|
403
|
+
end
|
404
|
+
|
405
|
+
def dump_options
|
406
|
+
$stdout.print <<~EOS
|
407
|
+
value_type: '#{value_type}'
|
408
|
+
auxil_type: '#{auxil_type}'
|
409
|
+
prefix: '#{prefix}'
|
410
|
+
EOS
|
411
|
+
end
|
412
|
+
|
413
|
+
def rule(name)
|
414
|
+
@rulehash[name]
|
415
|
+
end
|
416
|
+
|
417
|
+
def parse_directive_include(name, *outputs)
|
418
|
+
if !match_string(name)
|
419
|
+
return false
|
420
|
+
end
|
421
|
+
|
422
|
+
match_spaces
|
423
|
+
|
424
|
+
pos = @bufcur
|
425
|
+
l = @linenum
|
426
|
+
m = column_number
|
427
|
+
if match_code_block
|
428
|
+
q = @bufcur
|
429
|
+
match_spaces
|
430
|
+
outputs.each do |output|
|
431
|
+
code = Packcr::CodeBlock.new(@buffer.to_s[pos + 1, q - pos - 2], q - pos - 2, l, m)
|
432
|
+
output.push(code)
|
433
|
+
end
|
434
|
+
else
|
435
|
+
error l + 1, m + 1, "Illegal #{name} syntax"
|
436
|
+
end
|
437
|
+
true
|
438
|
+
end
|
439
|
+
|
440
|
+
def parse_directive_string(name, varname, must_not_be_empty: false, must_not_be_void: false, must_be_identifier: false)
|
441
|
+
l = @linenum
|
442
|
+
m = column_number
|
443
|
+
if !match_string(name)
|
444
|
+
return false
|
445
|
+
end
|
446
|
+
|
447
|
+
match_spaces
|
448
|
+
pos = @bufcur
|
449
|
+
lv = @linenum
|
450
|
+
mv = column_number
|
451
|
+
s = nil
|
452
|
+
if match_quotation_single || match_quotation_double
|
453
|
+
q = @bufcur
|
454
|
+
match_spaces
|
455
|
+
s = @buffer.to_s[pos + 1, q - pos - 2]
|
456
|
+
if !Packcr.unescape_string(s, false)
|
457
|
+
error lv + 1, mv + 1, "Illegal escape sequence"
|
458
|
+
end
|
459
|
+
else
|
460
|
+
error l + 1, m + 1, "Illegal #{name} syntax"
|
461
|
+
end
|
462
|
+
|
463
|
+
if s
|
464
|
+
valid = true
|
465
|
+
s.sub!(/\A\s+/, "")
|
466
|
+
s.sub!(/\s+\z/, "")
|
467
|
+
is_empty = must_not_be_empty && s !~ /[^\s]/
|
468
|
+
if is_empty
|
469
|
+
error lv + 1, mv + 1, "Empty string"
|
470
|
+
vaild = false
|
471
|
+
end
|
472
|
+
if must_not_be_void && s == "void"
|
473
|
+
error lv + 1, mv + 1, "'void' not allowed"
|
474
|
+
vaild = false
|
475
|
+
end
|
476
|
+
if !is_empty && must_be_identifier && !Packcr.is_identifier_string(s)
|
477
|
+
error lv + 1, mv + 1, "Invalid identifier"
|
478
|
+
valid = false
|
479
|
+
end
|
480
|
+
if instance_variable_get(varname) != nil
|
481
|
+
error l + 1, m + 1, "Multiple #{name} definition"
|
482
|
+
valid
|
483
|
+
end
|
484
|
+
if valid
|
485
|
+
instance_variable_set(varname, s)
|
486
|
+
end
|
487
|
+
end
|
488
|
+
return true
|
489
|
+
end
|
490
|
+
|
491
|
+
class StopParsing < StandardError
|
492
|
+
end
|
493
|
+
|
494
|
+
def parse_primary(rule)
|
495
|
+
pos = @bufcur
|
496
|
+
l = @linenum
|
497
|
+
m = column_number
|
498
|
+
n = @charnum
|
499
|
+
o = @linepos
|
500
|
+
if match_identifier
|
501
|
+
q = @bufcur
|
502
|
+
r = s = nil
|
503
|
+
match_spaces
|
504
|
+
if match_character(":".ord)
|
505
|
+
match_spaces
|
506
|
+
r = @bufcur
|
507
|
+
if !match_identifier
|
508
|
+
raise StopParsing
|
509
|
+
end
|
510
|
+
s = @bufcur
|
511
|
+
match_spaces
|
512
|
+
end
|
513
|
+
if match_string("<-")
|
514
|
+
raise StopParsing
|
515
|
+
end
|
516
|
+
|
517
|
+
n_p = Packcr::Node::ReferenceNode.new
|
518
|
+
if r == nil
|
519
|
+
name = @buffer.to_s
|
520
|
+
name = name[pos, q - pos]
|
521
|
+
unless q >= pos
|
522
|
+
raise "Internal error"
|
523
|
+
end
|
524
|
+
n_p.var = nil
|
525
|
+
n_p.index = nil
|
526
|
+
n_p.name = name
|
527
|
+
else
|
528
|
+
var = @buffer.to_s
|
529
|
+
var = var[pos, q - pos]
|
530
|
+
unless s != nil # s should have a valid value when r has a valid value
|
531
|
+
raise "Internal error"
|
532
|
+
end
|
533
|
+
unless q >= pos
|
534
|
+
raise "Internal error"
|
535
|
+
end
|
536
|
+
|
537
|
+
n_p.var = var
|
538
|
+
if var.ord == "_".ord
|
539
|
+
error l + 1, m + 1, "Leading underscore in variable name '#{var}'"
|
540
|
+
end
|
541
|
+
|
542
|
+
i = rule.vars.index do |ref|
|
543
|
+
unless ref.is_a?(Packcr::Node::ReferenceNode)
|
544
|
+
raise "Unexpected node type: #{ref.class}"
|
545
|
+
end
|
546
|
+
var == ref.var
|
547
|
+
end
|
548
|
+
if !i
|
549
|
+
i = rule.vars.length
|
550
|
+
rule.vars << n_p
|
551
|
+
end
|
552
|
+
n_p.index = i
|
553
|
+
unless s >= r
|
554
|
+
raise "Internal error"
|
555
|
+
end
|
556
|
+
|
557
|
+
name = @buffer.to_s
|
558
|
+
name = name[r, s - r]
|
559
|
+
n_p.name = name
|
560
|
+
end
|
561
|
+
n_p.line = l
|
562
|
+
n_p.col = m
|
563
|
+
elsif match_character("(")
|
564
|
+
match_spaces
|
565
|
+
n_p = parse_expression(rule)
|
566
|
+
if !n_p
|
567
|
+
raise StopParsing
|
568
|
+
end
|
569
|
+
if !match_character(")")
|
570
|
+
raise StopParsing
|
571
|
+
end
|
572
|
+
match_spaces
|
573
|
+
elsif match_character("<")
|
574
|
+
capts = rule.capts
|
575
|
+
match_spaces
|
576
|
+
n_p = Packcr::Node::CaptureNode.new
|
577
|
+
n_p.index = capts.length
|
578
|
+
rule.capts << n_p
|
579
|
+
expr = parse_expression(rule)
|
580
|
+
n_p.expr = expr
|
581
|
+
if !expr || !match_character(">")
|
582
|
+
rule.capts = rule.capts[0, n_p.index]
|
583
|
+
raise StopParsing
|
584
|
+
end
|
585
|
+
match_spaces
|
586
|
+
elsif match_character("$")
|
587
|
+
match_spaces
|
588
|
+
pos2 = @bufcur
|
589
|
+
if match_number
|
590
|
+
q = @bufcur
|
591
|
+
s = @buffer.to_s
|
592
|
+
s = s[pos2, q - pos2]
|
593
|
+
match_spaces
|
594
|
+
n_p = Packcr::Node::ExpandNode.new
|
595
|
+
unless q >= pos2
|
596
|
+
raise StopParsing
|
597
|
+
end
|
598
|
+
index = s.to_i
|
599
|
+
n_p.index = index
|
600
|
+
if index == nil
|
601
|
+
error l + 1, m + 1, "Invalid unsigned number '#{s}'"
|
602
|
+
elsif index == 0
|
603
|
+
error l + 1, m + 1, "0 not allowed"
|
604
|
+
elsif s.ord == "0".ord
|
605
|
+
error l + 1, m + 1, "0-prefixed number not allowed"
|
606
|
+
n_p.index = 0
|
607
|
+
end
|
608
|
+
if index > 0 && index != nil
|
609
|
+
n_p.index = index - 1
|
610
|
+
n_p.line = l
|
611
|
+
n_p.col = m
|
612
|
+
end
|
613
|
+
else
|
614
|
+
raise StopParsing
|
615
|
+
end
|
616
|
+
elsif match_character(".")
|
617
|
+
match_spaces
|
618
|
+
n_p = Packcr::Node::CharclassNode.new
|
619
|
+
n_p.value = nil
|
620
|
+
if !@ascii
|
621
|
+
@utf8 = true
|
622
|
+
end
|
623
|
+
elsif match_character_class
|
624
|
+
q = @bufcur
|
625
|
+
charclass = @buffer.to_s
|
626
|
+
charclass = charclass[pos + 1, q - pos - 2]
|
627
|
+
match_spaces
|
628
|
+
n_p = Packcr::Node::CharclassNode.new
|
629
|
+
Packcr.unescape_string(charclass, true)
|
630
|
+
if !@ascii
|
631
|
+
charclass.force_encoding(Encoding::UTF_8)
|
632
|
+
end
|
633
|
+
if !@ascii && !charclass.valid_encoding?
|
634
|
+
error l + 1, m + 1, "Invalid UTF-8 string"
|
635
|
+
end
|
636
|
+
if !@ascii && !charclass.empty?
|
637
|
+
@utf8 = true
|
638
|
+
end
|
639
|
+
n_p.value = charclass
|
640
|
+
elsif match_quotation_single || match_quotation_double
|
641
|
+
q = @bufcur
|
642
|
+
string = @buffer.to_s
|
643
|
+
string = string[pos + 1, q - pos - 2]
|
644
|
+
match_spaces
|
645
|
+
n_p = ::Packcr::Node::StringNode.new
|
646
|
+
Packcr.unescape_string(string, true)
|
647
|
+
if !@ascii
|
648
|
+
string.force_encoding(Encoding::UTF_8)
|
649
|
+
end
|
650
|
+
if !@ascii && !string.valid_encoding?
|
651
|
+
error l + 1, m + 1, "Invalid UTF-8 string"
|
652
|
+
end
|
653
|
+
n_p.value = string
|
654
|
+
elsif match_code_block
|
655
|
+
q = @bufcur
|
656
|
+
text = @buffer.to_s
|
657
|
+
text = text[pos + 1, q - pos - 2]
|
658
|
+
codes = rule.codes
|
659
|
+
match_spaces
|
660
|
+
n_p = Packcr::Node::ActionNode.new
|
661
|
+
n_p.code = Packcr::CodeBlock.new(text, Packcr.find_trailing_blanks(text), l, m)
|
662
|
+
n_p.index = codes.length
|
663
|
+
codes.push(n_p)
|
664
|
+
else
|
665
|
+
raise StopParsing
|
666
|
+
end
|
667
|
+
n_p
|
668
|
+
rescue StopParsing
|
669
|
+
@bufcur = pos
|
670
|
+
@linenum = l
|
671
|
+
@charnum = n
|
672
|
+
@linepos = o
|
673
|
+
return nil
|
674
|
+
end
|
675
|
+
|
676
|
+
def parse_term(rule)
|
677
|
+
pos = @bufcur
|
678
|
+
l = @linenum
|
679
|
+
n = @charnum
|
680
|
+
o = @linepos
|
681
|
+
if match_character("&")
|
682
|
+
t = "&".ord
|
683
|
+
elsif match_character("!")
|
684
|
+
t = "!".ord
|
685
|
+
else
|
686
|
+
t = 0
|
687
|
+
end
|
688
|
+
if t
|
689
|
+
match_spaces
|
690
|
+
end
|
691
|
+
|
692
|
+
n_p = parse_primary(rule)
|
693
|
+
if !n_p
|
694
|
+
raise StopParsing
|
695
|
+
end
|
696
|
+
if match_character("*")
|
697
|
+
match_spaces
|
698
|
+
n_q = Packcr::Node::QuantityNode.new
|
699
|
+
n_q.min = 0
|
700
|
+
n_q.max = -1
|
701
|
+
n_q.expr = n_p
|
702
|
+
elsif match_character("+")
|
703
|
+
match_spaces
|
704
|
+
n_q = Packcr::Node::QuantityNode.new
|
705
|
+
n_q.min = 1
|
706
|
+
n_q.max = -1
|
707
|
+
n_q.expr = n_p
|
708
|
+
elsif match_character("?")
|
709
|
+
match_spaces
|
710
|
+
n_q = Packcr::Node::QuantityNode.new
|
711
|
+
n_q.min = 0
|
712
|
+
n_q.max = 1
|
713
|
+
n_q.expr = n_p
|
714
|
+
else
|
715
|
+
n_q = n_p
|
716
|
+
end
|
717
|
+
|
718
|
+
case t
|
719
|
+
when "&".ord
|
720
|
+
n_r = Packcr::Node::PredicateNode.new
|
721
|
+
n_r.neg = false
|
722
|
+
n_r.expr = n_q
|
723
|
+
when "!".ord
|
724
|
+
n_r = Packcr::Node::PredicateNode.new
|
725
|
+
n_r.neg = true
|
726
|
+
n_r.expr = n_q
|
727
|
+
else
|
728
|
+
n_r = n_q
|
729
|
+
end
|
730
|
+
|
731
|
+
if match_character("~")
|
732
|
+
match_spaces
|
733
|
+
pos2 = @bufcur
|
734
|
+
l2 = @linenum
|
735
|
+
m = column_number
|
736
|
+
if match_code_block
|
737
|
+
q = @bufcur
|
738
|
+
text = @buffer.to_s
|
739
|
+
text = text[pos2 + 1, q - pos2 - 2]
|
740
|
+
match_spaces
|
741
|
+
n_t = Packcr::Node::ErrorNode.new
|
742
|
+
n_t.expr = n_r
|
743
|
+
n_t.code = Packcr::CodeBlock.new(text, Packcr.find_trailing_blanks(text), l2, m);
|
744
|
+
n_t.index = rule.codes.length
|
745
|
+
rule.codes.push(n_t)
|
746
|
+
else
|
747
|
+
raise StopParsing
|
748
|
+
end
|
749
|
+
else
|
750
|
+
n_t = n_r
|
751
|
+
end
|
752
|
+
n_t
|
753
|
+
rescue StopParsing
|
754
|
+
@bufcur = pos
|
755
|
+
@linenum = l
|
756
|
+
@charnum = n
|
757
|
+
@linepos = o
|
758
|
+
return nil
|
759
|
+
end
|
760
|
+
|
761
|
+
def parse_sequence(rule)
|
762
|
+
pos = @bufcur
|
763
|
+
l = @linenum
|
764
|
+
n = @charnum
|
765
|
+
o = @linepos
|
766
|
+
n_t = parse_term(rule)
|
767
|
+
if !n_t
|
768
|
+
raise StopParsing
|
769
|
+
end
|
770
|
+
n_u = parse_term(rule);
|
771
|
+
if n_u
|
772
|
+
n_s = Packcr::Node::SequenceNode.new
|
773
|
+
n_s.nodes << n_t
|
774
|
+
n_s.nodes << n_u
|
775
|
+
while (n_t = parse_term(rule))
|
776
|
+
n_s.nodes << n_t
|
777
|
+
end
|
778
|
+
else
|
779
|
+
n_s = n_t
|
780
|
+
end
|
781
|
+
n_s
|
782
|
+
rescue StopParsing
|
783
|
+
@bufcur = pos
|
784
|
+
@linenum = l
|
785
|
+
@charnum = n
|
786
|
+
@linepos = o
|
787
|
+
return nil
|
788
|
+
end
|
789
|
+
|
790
|
+
def parse_expression(rule)
|
791
|
+
pos = @bufcur
|
792
|
+
l = @linenum
|
793
|
+
n = @charnum
|
794
|
+
o = @linepos
|
795
|
+
n_s = parse_sequence(rule)
|
796
|
+
if !n_s
|
797
|
+
raise StopParsing
|
798
|
+
end
|
799
|
+
q = @bufcur
|
800
|
+
if (match_character("/".ord))
|
801
|
+
@bufcur = q
|
802
|
+
n_e = Packcr::Node::AlternateNode.new
|
803
|
+
n_e.nodes << n_s
|
804
|
+
while match_character("/".ord)
|
805
|
+
match_spaces
|
806
|
+
n_s = parse_sequence(rule)
|
807
|
+
if !n_s
|
808
|
+
raise StopParsing
|
809
|
+
end
|
810
|
+
n_e.nodes << n_s
|
811
|
+
end
|
812
|
+
else
|
813
|
+
n_e = n_s
|
814
|
+
end
|
815
|
+
return n_e
|
816
|
+
rescue StopParsing
|
817
|
+
@bufcur = pos
|
818
|
+
@linenum = l
|
819
|
+
@charnum = n
|
820
|
+
@linepos = o
|
821
|
+
return nil
|
822
|
+
end
|
823
|
+
|
824
|
+
def parse_rule
|
825
|
+
pos = @bufcur
|
826
|
+
l = @linenum
|
827
|
+
m = column_number
|
828
|
+
n = @charnum
|
829
|
+
o = @linepos
|
830
|
+
if !match_identifier
|
831
|
+
raise StopParsing
|
832
|
+
end
|
833
|
+
|
834
|
+
q = @bufcur
|
835
|
+
match_spaces
|
836
|
+
if !match_string("<-")
|
837
|
+
raise StopParsing
|
838
|
+
end
|
839
|
+
match_spaces
|
840
|
+
|
841
|
+
n_r = Packcr::Node::RuleNode.new
|
842
|
+
expr = parse_expression(n_r)
|
843
|
+
n_r.expr = expr
|
844
|
+
if !expr
|
845
|
+
raise StopParsing
|
846
|
+
end
|
847
|
+
unless q >= pos
|
848
|
+
raise "Internal error"
|
849
|
+
end
|
850
|
+
name = @buffer.to_s
|
851
|
+
name = name[pos, q - pos]
|
852
|
+
n_r.name = name
|
853
|
+
n_r.line = l
|
854
|
+
n_r.col = m
|
855
|
+
n_r
|
856
|
+
rescue StopParsing
|
857
|
+
@bufcur = pos
|
858
|
+
@linenum = l
|
859
|
+
@charnum = n
|
860
|
+
@linepos = o
|
861
|
+
return nil
|
862
|
+
end
|
863
|
+
|
864
|
+
def parse
|
865
|
+
match_spaces
|
866
|
+
|
867
|
+
b = true
|
868
|
+
while true
|
869
|
+
if eof? || match_footer_start
|
870
|
+
break
|
871
|
+
end
|
872
|
+
if (
|
873
|
+
parse_directive_include("%earlysource", @esource) ||
|
874
|
+
parse_directive_include("%earlycommon", @esource, @eheader) ||
|
875
|
+
parse_directive_include("%source", @source) ||
|
876
|
+
parse_directive_include("%lateheader", @lheader) ||
|
877
|
+
parse_directive_include("%header", @header) ||
|
878
|
+
parse_directive_include("%common", @source, @header) ||
|
879
|
+
parse_directive_include("%location", @location) ||
|
880
|
+
parse_directive_string("%value", "@value_type", must_not_be_empty: true, must_not_be_void: true) ||
|
881
|
+
parse_directive_string("%auxil", "@auxil_type", must_not_be_empty: true, must_not_be_void: true) ||
|
882
|
+
parse_directive_string("%prefix", "@prefix", must_not_be_empty: true, must_be_identifier: true)
|
883
|
+
)
|
884
|
+
b = true
|
885
|
+
elsif match_character("%")
|
886
|
+
l = @linenum
|
887
|
+
m = column_number
|
888
|
+
error l + 1, m + 1, "Invalid directive"
|
889
|
+
match_identifier
|
890
|
+
match_spaces
|
891
|
+
b = true
|
892
|
+
else
|
893
|
+
l = @linenum
|
894
|
+
m = column_number
|
895
|
+
n = @charnum
|
896
|
+
o = @linepos
|
897
|
+
node = parse_rule
|
898
|
+
if node == nil
|
899
|
+
if b
|
900
|
+
error l + 1, m + 1, "Illegal rule syntax"
|
901
|
+
b = false
|
902
|
+
end
|
903
|
+
@linenum = l
|
904
|
+
@charnum = n
|
905
|
+
@linepos = o
|
906
|
+
if !match_identifier && !match_spaces
|
907
|
+
match_character_any
|
908
|
+
end
|
909
|
+
else
|
910
|
+
@rules.push(node)
|
911
|
+
b = true
|
912
|
+
end
|
913
|
+
end
|
914
|
+
commit_buffer
|
915
|
+
end
|
916
|
+
|
917
|
+
if @location.empty?
|
918
|
+
@location = nil
|
919
|
+
end
|
920
|
+
commit_buffer
|
921
|
+
|
922
|
+
make_rulehash
|
923
|
+
@rules.each do |rule|
|
924
|
+
rule.expr.link_references(self)
|
925
|
+
end
|
926
|
+
@rules[1..-1]&.each do |rule|
|
927
|
+
if rule.ref == 0
|
928
|
+
error rule.line + 1, rule.col + 1, "Never used rule '#{rule.name}'"
|
929
|
+
elsif rule.ref < 0 # impossible?
|
930
|
+
error rule.line + 1, rule.col + 1, "Multiple definition of rule '#{rule.name}'"
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
@rules.each do |rule|
|
935
|
+
rule.verify(self)
|
936
|
+
end
|
937
|
+
|
938
|
+
if @debug
|
939
|
+
@rules.each(&:debug_dump)
|
940
|
+
dump_options
|
941
|
+
end
|
942
|
+
|
943
|
+
@errnum.zero?
|
944
|
+
end
|
945
|
+
|
946
|
+
def generate
|
947
|
+
if @hname
|
948
|
+
File.open(@hname, "wt") do |hio|
|
949
|
+
hstream = ::Packcr::Stream.new(hio, @hname, @lines ? 0 : nil)
|
950
|
+
|
951
|
+
hstream.write Packcr.template("context/header.#{@lang}.erb", binding), rewrite_line_directive: true
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
File.open(@sname, "wt") do |sio|
|
956
|
+
sstream = ::Packcr::Stream.new(sio, @sname, @lines ? 0 : nil)
|
957
|
+
|
958
|
+
sstream.write Packcr.template("context/source.#{@lang}.erb", binding), rewrite_line_directive: true
|
959
|
+
|
960
|
+
eol?
|
961
|
+
if !eof?
|
962
|
+
sstream.write("\n")
|
963
|
+
end
|
964
|
+
commit_buffer
|
965
|
+
if @lines && !eof?
|
966
|
+
sstream.write_line_directive(@iname, @linenum)
|
967
|
+
end
|
968
|
+
while refill_buffer > 0
|
969
|
+
write_buffer(sstream)
|
970
|
+
commit_buffer
|
971
|
+
end
|
972
|
+
end
|
973
|
+
|
974
|
+
if !@errnum.zero?
|
975
|
+
File.unlink(@hname) if @name
|
976
|
+
File.unlink(@sname)
|
977
|
+
return false
|
978
|
+
end
|
979
|
+
true
|
980
|
+
end
|
981
|
+
end
|
982
|
+
end
|