packcr 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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