minjs 0.1.2

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.
data/lib/minjs/lex.rb ADDED
@@ -0,0 +1,745 @@
1
+ require 'minjs/ctype'
2
+
3
+ module Minjs
4
+ class Lex
5
+ include Ctype
6
+
7
+ attr_reader :pos
8
+ attr_reader :error_pos
9
+ attr_reader :codes
10
+
11
+ def initialize(str = "", options = {})
12
+ str = str.gsub(/\r\n/, "\n")
13
+ @codes = str.codepoints
14
+ @pos = 0
15
+ @lit_cache = []
16
+ @lit_nextpos = []
17
+ if options[:debug]
18
+ @debug = true
19
+ end
20
+ end
21
+
22
+ def next_input_element(options = {})
23
+ if @lit_cache[@pos]
24
+ ret = @lit_cache[@pos]
25
+ @pos = @lit_nextpos[@pos]
26
+ @error_pos = @pos
27
+ return ret
28
+ end
29
+ pos0 = @pos
30
+ if ret = (white_space || line_terminator || comment || token)
31
+ if ret
32
+ @lit_cache[pos0] = ret
33
+ @lit_nextpos[pos0] = @pos
34
+ end
35
+ @error_pos = @pos
36
+ return ret
37
+ end
38
+ #
39
+ # ECMA262 say:
40
+ #
41
+ # There are no syntactic grammar contexts where both a leading division
42
+ # or division-assignment, and a leading RegularExpressionLiteral are permitted.
43
+ # This is not affected by semicolon insertion (see 7.9); in examples such as the following:
44
+ # To determine `/' is regular expression or not
45
+ #
46
+ #
47
+ if options[:hint] == :div
48
+ ret = div_punctuator
49
+ if ret
50
+ @lit_cache[pos0] = ret
51
+ @lit_nextpos[pos0] = @pos
52
+ end
53
+ @error_pos = @pos
54
+ return ret
55
+ elsif options[:hint] == :regexp
56
+ ret = regexp_literal
57
+ if ret
58
+ @lit_cache[pos0] = ret
59
+ @lit_nextpos[pos0] = @pos
60
+ end
61
+ @error_pos = @pos
62
+ return ret
63
+ else
64
+ # p pos0
65
+ # p @pos
66
+ #@error_pos = @pos
67
+ #debug_lit
68
+ #raise 'no hint'
69
+ #regexp_literal
70
+ #div_punctuator
71
+ #nil #unknown
72
+ ECMA262::LIT_DIV_OR_REGEXP_LITERAL
73
+ end
74
+ end
75
+
76
+ # 7.2
77
+ def white_space
78
+ code = @codes[@pos]
79
+ if white_space?(code)
80
+ while true
81
+ @pos += 1
82
+ code = @codes[@pos]
83
+ break unless white_space?(code)
84
+ end
85
+ return ECMA262::WhiteSpace.get
86
+ else
87
+ nil
88
+ end
89
+ end
90
+
91
+ #7.3
92
+ def line_terminator
93
+ code = @codes[@pos]
94
+ if line_terminator?(code)
95
+ while true
96
+ @pos += 1
97
+ code = @codes[@pos]
98
+ break unless line_terminator?(code)
99
+ end
100
+ return ECMA262::LineFeed.get
101
+ else
102
+ nil
103
+ end
104
+ end
105
+
106
+ #7.4
107
+ def comment
108
+ multi_line_comment || single_line_comment
109
+ end
110
+
111
+ def multi_line_comment
112
+ if @codes[@pos] == 0x2f and @codes[@pos + 1] == 0x2a
113
+ @pos = @pos + 2
114
+ pos0 = @pos
115
+ lf = false
116
+ while (@codes[@pos] != 0x2a or @codes[@pos + 1] != 0x2f)
117
+ if @codes[@pos].nil?
118
+ raise ParseError.new("no `*/' at end of comment")
119
+ end
120
+ if line_terminator?(@codes[@pos])
121
+ lf = true
122
+ end
123
+ @pos = @pos + 1
124
+ end
125
+ @pos = @pos + 2
126
+ return ECMA262::MultiLineComment.new(@codes[pos0...(@pos-2)].pack("U*"), lf)
127
+ else
128
+ nil
129
+ end
130
+ end
131
+
132
+ def single_line_comment
133
+ if @codes[@pos] == 0x2f and @codes[@pos + 1] == 0x2f
134
+ @pos = @pos + 2
135
+ pos0 = @pos
136
+ while !line_terminator?(@codes[@pos]) and @codes[@pos]
137
+ @pos += 1
138
+ end
139
+ if @codes[@pos].nil?
140
+ return ECMA262::SingleLineComment.new(@codes[pos0...@pos].pack("U*") + "\n")
141
+ else
142
+ return ECMA262::SingleLineComment.new(@codes[pos0...@pos].pack("U*"))
143
+ end
144
+ else
145
+ nil
146
+ end
147
+ end
148
+
149
+ #
150
+ # 7.5 tokens
151
+ #
152
+ def token
153
+ pos0 = @pos
154
+ ret = (identifier_name || numeric_literal || punctuator || string_literal)
155
+ if ret
156
+ @lit_cache[pos0] = ret
157
+ @lit_nextpos[pos0] = @pos
158
+ end
159
+ ret
160
+ end
161
+
162
+ def identifier_name
163
+ pos0 = @pos
164
+ code = @codes[@pos]
165
+ return nil if code.nil?
166
+ if identifier_start?(code)
167
+ while true
168
+ @pos += 1
169
+ code = @codes[@pos]
170
+ if code.nil?
171
+ break
172
+ elsif identifier_part?(code)
173
+ ;#
174
+ else
175
+ return ECMA262::IdentifierName.new(nil, @codes[pos0...@pos].pack("U*").to_sym)
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ def punctuator
182
+ code0 = @codes[@pos]
183
+ code1 = @codes[@pos+1]
184
+ code2 = @codes[@pos+2]
185
+ code3 = @codes[@pos+3]
186
+ if false
187
+ elsif (code0 == 0x3e and code1 == 0x3e and code2 == 0x3e and code3 == 0x3d)
188
+ @pos += 4
189
+ return ECMA262::Punctuator.get('>>>=')
190
+ elsif (code0 == 0x3d and code1 == 0x3d and code2 == 0x3d)
191
+ @pos += 3
192
+ return ECMA262::Punctuator.get('===')
193
+ elsif (code0 == 0x21 and code1 == 0x3d and code2 == 0x3d)
194
+ @pos += 3
195
+ return ECMA262::Punctuator.get('!==')
196
+ elsif (code0 == 0x3e and code1 == 0x3e and code2 == 0x3e)
197
+ @pos += 3
198
+ return ECMA262::Punctuator.get('>>>')
199
+ elsif (code0 == 0x3c and code1 == 0x3c and code2 == 0x3d)
200
+ @pos += 3
201
+ return ECMA262::Punctuator.get('<<=')
202
+ elsif (code0 == 0x3e and code1 == 0x3e and code2 == 0x3d)
203
+ @pos += 3
204
+ return ECMA262::Punctuator.get('>>=')
205
+ elsif (code0 == 0x3e and code1 == 0x3e)
206
+ @pos += 2
207
+ return ECMA262::Punctuator.get('>>')
208
+ elsif (code0 == 0x3c and code1 == 0x3d)
209
+ @pos += 2
210
+ return ECMA262::Punctuator.get('<=')
211
+ elsif (code0 == 0x3e and code1 == 0x3d)
212
+ @pos += 2
213
+ return ECMA262::Punctuator.get('>=')
214
+ elsif (code0 == 0x3d and code1 == 0x3d)
215
+ @pos += 2
216
+ return ECMA262::Punctuator.get('==')
217
+ elsif (code0 == 0x21 and code1 == 0x3d)
218
+ @pos += 2
219
+ return ECMA262::Punctuator.get('!=')
220
+ elsif (code0 == 0x2b and code1 == 0x2b)
221
+ @pos += 2
222
+ return ECMA262::Punctuator.get('++')
223
+ elsif (code0 == 0x2d and code1 == 0x2d)
224
+ @pos += 2
225
+ return ECMA262::Punctuator.get('--')
226
+ elsif (code0 == 0x3c and code1 == 0x3c)
227
+ @pos += 2
228
+ return ECMA262::Punctuator.get('<<')
229
+ elsif (code0 == 0x3e and code1 == 0x3e)
230
+ @pos += 2
231
+ return ECMA262::Punctuator.get('>>')
232
+ elsif (code0 == 0x26 and code1 == 0x26)
233
+ @pos += 2
234
+ return ECMA262::Punctuator.get('&&')
235
+ elsif (code0 == 0x7c and code1 == 0x7c)
236
+ @pos += 2
237
+ return ECMA262::Punctuator.get('||')
238
+ elsif (code0 == 0x2b and code1 == 0x3d)
239
+ @pos += 2
240
+ return ECMA262::Punctuator.get('+=')
241
+ elsif (code0 == 0x2d and code1 == 0x3d)
242
+ @pos += 2
243
+ return ECMA262::Punctuator.get('-=')
244
+ elsif (code0 == 0x2a and code1 == 0x3d)
245
+ @pos += 2
246
+ return ECMA262::Punctuator.get('*=')
247
+ elsif (code0 == 0x25 and code1 == 0x3d)
248
+ @pos += 2
249
+ return ECMA262::Punctuator.get('%=')
250
+ elsif (code0 == 0x26 and code1 == 0x3d)
251
+ @pos += 2
252
+ return ECMA262::Punctuator.get('&=')
253
+ elsif (code0 == 0x7c and code1 == 0x3d)
254
+ @pos += 2
255
+ return ECMA262::Punctuator.get('|=')
256
+ elsif (code0 == 0x5e and code1 == 0x3d)
257
+ @pos += 2
258
+ return ECMA262::Punctuator.get('^=')
259
+ elsif (code0 == 0x7b)
260
+ @pos += 1
261
+ return ECMA262::Punctuator.get('{')
262
+ elsif (code0 == 0x7d)
263
+ @pos += 1
264
+ return ECMA262::Punctuator.get('}')
265
+ elsif (code0 == 0x28)
266
+ @pos += 1
267
+ return ECMA262::Punctuator.get('(')
268
+ elsif (code0 == 0x29)
269
+ @pos += 1
270
+ return ECMA262::Punctuator.get(')')
271
+ elsif (code0 == 0x5b)
272
+ @pos += 1
273
+ return ECMA262::Punctuator.get('[')
274
+ elsif (code0 == 0x5d)
275
+ @pos += 1
276
+ return ECMA262::Punctuator.get(']')
277
+ elsif (code0 == 0x2e)
278
+ @pos += 1
279
+ return ECMA262::Punctuator.get('.')
280
+ elsif (code0 == 0x3b)
281
+ @pos += 1
282
+ return ECMA262::Punctuator.get(';')
283
+ elsif (code0 == 0x2c)
284
+ @pos += 1
285
+ return ECMA262::Punctuator.get(',')
286
+ elsif (code0 == 0x3c)
287
+ @pos += 1
288
+ return ECMA262::Punctuator.get('<')
289
+ elsif (code0 == 0x3e)
290
+ @pos += 1
291
+ return ECMA262::Punctuator.get('>')
292
+ elsif (code0 == 0x2b)
293
+ @pos += 1
294
+ return ECMA262::Punctuator.get('+')
295
+ elsif (code0 == 0x2d)
296
+ @pos += 1
297
+ return ECMA262::Punctuator.get('-')
298
+ elsif (code0 == 0x2a)
299
+ @pos += 1
300
+ return ECMA262::Punctuator.get('*')
301
+ elsif (code0 == 0x25)
302
+ @pos += 1
303
+ return ECMA262::Punctuator.get('%')
304
+ elsif (code0 == 0x26)
305
+ @pos += 1
306
+ return ECMA262::Punctuator.get('&')
307
+ elsif (code0 == 0x7c)
308
+ @pos += 1
309
+ return ECMA262::Punctuator.get('|')
310
+ elsif (code0 == 0x5e)
311
+ @pos += 1
312
+ return ECMA262::Punctuator.get('^')
313
+ elsif (code0 == 0x21)
314
+ @pos += 1
315
+ return ECMA262::Punctuator.get('!')
316
+ elsif (code0 == 0x7e)
317
+ @pos += 1
318
+ return ECMA262::Punctuator.get('~')
319
+ elsif (code0 == 0x3f)
320
+ @pos += 1
321
+ return ECMA262::Punctuator.get('?')
322
+ elsif (code0 == 0x3a)
323
+ @pos += 1
324
+ return ECMA262::Punctuator.get(':')
325
+ elsif (code0 == 0x3d)
326
+ @pos += 1
327
+ return ECMA262::Punctuator.get('=')
328
+ end
329
+ nil
330
+ end
331
+
332
+ def div_punctuator
333
+ if @codes[@pos] == 0x2f
334
+ if @codes[@pos+1] == 0x3d
335
+ @pos += 2
336
+ return ECMA262::PUNC_DIVLET
337
+ else
338
+ @pos += 1
339
+ return ECMA262::PUNC_DIV
340
+ end
341
+ end
342
+ nil
343
+ end
344
+
345
+ #
346
+ # 7.8.5
347
+ #
348
+ # RegularExpressionLiteral::
349
+ # / RegularExpressionBody / RegularExpressionFlags
350
+ #
351
+ def regexp_literal
352
+ pos0 = @pos
353
+ return nil unless @codes[@pos] == 0x2f
354
+
355
+ body = regexp_body
356
+ flags = regexp_flags
357
+ return ECMA262::ECMA262RegExp.new(body, flags)
358
+ end
359
+
360
+ def regexp_body
361
+ if @codes[@pos] == 0x2a
362
+ raise ParseError.new("first character of regular expression is `*'")
363
+ end
364
+ pos0 = @pos
365
+ @pos += 1
366
+ while !(@codes[@pos] == 0x2f)
367
+ if @codes[@pos].nil?
368
+ raise ParseError.new("no `/' end of regular expression")
369
+ end
370
+ if line_terminator?(@codes[@pos])
371
+ debug_lit
372
+ raise ParseError.new("regular expression has line terminator in body")
373
+ end
374
+ if @codes[@pos] == 0x5c # \
375
+ @pos += 1
376
+ if line_terminator?(@codes[@pos])
377
+ raise ParseError.new("regular expression has line terminator in body")
378
+ end
379
+ @pos += 1
380
+ elsif @codes[@pos] == 0x5b # [
381
+ regexp_class
382
+ else
383
+ @pos += 1
384
+ end
385
+ end
386
+ @pos += 1
387
+ return @codes[(pos0+1)...(@pos-1)].pack("U*")
388
+ end
389
+
390
+ def regexp_class
391
+ if @codes[@pos] != 0x5b
392
+ raise ParseError.new('bad regular expression')
393
+ end
394
+ @pos += 1
395
+ while !(@codes[@pos] == 0x5d)
396
+ if @codes[@pos].nil?
397
+ raise ParseError.new("no `]' end of regular expression class")
398
+ end
399
+ if line_terminator?(@codes[@pos])
400
+ raise ParseError.new("regular expression has line terminator in body")
401
+ end
402
+ if @codes[@pos] == 0x5c # \
403
+ @pos += 1
404
+ if line_terminator?(@codes[@pos])
405
+ raise ParseError.new("regular expression has line terminator in body")
406
+ end
407
+ @pos += 1
408
+ else
409
+ @pos += 1
410
+ end
411
+ end
412
+ @pos += 1
413
+ end
414
+
415
+ def regexp_flags
416
+ pos0 = @pos
417
+ while(identifier_part?(@codes[@pos]))
418
+ @pos += 1
419
+ end
420
+ return @codes[pos0...@pos].pack("U*")
421
+ end
422
+
423
+ #7.8.3
424
+ def numeric_literal
425
+ code = @codes[@pos]
426
+ return nil if code.nil?
427
+
428
+ hex_integer_literal || decimal_literal
429
+ end
430
+
431
+ def hex_integer_literal
432
+ pos0 = @pos
433
+ # 0x.... or 0X....
434
+ code = @codes[@pos]
435
+ if code == 0x30 and (@codes[@pos+1] == 0x78 || @codes[@pos+1] == 0x58) #hex integer
436
+ @pos += 2
437
+ while true
438
+ code = @codes[@pos]
439
+ if (code >= 0x30 and code <= 0x39) || (code >= 0x41 and code <= 0x4f) || (code >= 0x61 and code <= 0x6f)
440
+ else
441
+ raw = @codes[pos0...@pos].pack("U*")
442
+ return ECMA262::ECMA262Numeric.new(raw, @codes[(pos0+2)...@pos].pack("U*").to_i(16))
443
+ end
444
+ @pos += 1
445
+ end
446
+ else
447
+ nil
448
+ end
449
+ end
450
+
451
+ def decimal_literal
452
+ pos0 = @pos
453
+ code = @codes[@pos]
454
+ if code == 0x2e #.
455
+ @pos += 1
456
+ f = decimal_digits
457
+ if f.nil?
458
+ @pos = pos0
459
+ return nil
460
+ end
461
+ if @codes[@pos] == 0x65 || @codes[@pos] == 0x45
462
+ @pos += 1
463
+ e = exp_part
464
+ end
465
+ raw = @codes[pos0...@pos].pack("U*")
466
+ return ECMA262::ECMA262Numeric.new(raw, 0, f, e)
467
+ else
468
+ nil
469
+ end
470
+ if code >= 0x30 and code <= 0x39
471
+ i = decimal_digits
472
+ if @codes[@pos] == 0x2e
473
+ @pos += 1
474
+ f = decimal_digits
475
+ if @codes[@pos] == 0x65 || @codes[@pos] == 0x45
476
+ @pos += 1
477
+ e = exp_part
478
+ end
479
+ elsif @codes[@pos] == 0x65 || @codes[@pos] == 0x45
480
+ @pos += 1
481
+ e = exp_part
482
+ end
483
+ raw = @codes[pos0...@pos].pack("U*")
484
+ return ECMA262::ECMA262Numeric.new(raw, i, f, e)
485
+ end
486
+ end
487
+
488
+ def exp_part
489
+ if @codes[@pos] == 0x2b
490
+ @pos += 1
491
+ elsif @codes[@pos] == 0x2d
492
+ @pos += 1
493
+ neg = true
494
+ end
495
+ if neg
496
+ e = -decimal_digits
497
+ else
498
+ e = decimal_digits
499
+ end
500
+ e
501
+ end
502
+
503
+ def decimal_digits
504
+ pos0 = @pos
505
+ code = @codes[@pos]
506
+ if code >= 0x30 and code <= 0x39
507
+ @pos += 1
508
+ while true
509
+ code = @codes[@pos]
510
+ if code >= 0x30 and code <= 0x39
511
+ @pos += 1
512
+ else
513
+ return @codes[pos0...@pos].pack("U*").to_i
514
+ end
515
+ end
516
+ else
517
+ nil
518
+ end
519
+ end
520
+
521
+ #7.8.4
522
+ def string_literal
523
+ code = @codes[@pos]
524
+ return nil if code.nil?
525
+ pos0 = @pos
526
+ if code == 0x27 #'
527
+ term = 0x27
528
+ elsif code == 0x22 #"
529
+ term = 0x22
530
+ else
531
+ return nil
532
+ end
533
+
534
+ str = ''
535
+ while @codes[@pos]
536
+ @pos += 1
537
+ code = @codes[@pos]
538
+ if code.nil?
539
+ raise ParseError.new("no `#{term}' at end of string")
540
+ elsif line_terminator?(code)
541
+ raise ParseError.new("string has line terminator in body")
542
+ elsif code == 0x5c #\
543
+ @pos += 1
544
+ str << esc_string
545
+ elsif code == term
546
+ @pos += 1
547
+ return ECMA262::ECMA262String.new(str)
548
+ else
549
+ str << code
550
+ end
551
+ end
552
+ nil
553
+ end
554
+
555
+ # Annex B
556
+ def octal?(char)
557
+ char >= 0x30 and char <= 0x39
558
+ end
559
+
560
+ def esc_string
561
+ case @codes[@pos]
562
+ # when 0x30
563
+ # "\u{0}"
564
+ when 0x27
565
+ "\'"
566
+ when 0x22
567
+ "\""
568
+ when 0x5c
569
+ "\\"
570
+ when 0x62 #b
571
+ "\u{0008}"
572
+ when 0x74 #t
573
+ "\u{0009}"
574
+ when 0x6e #n
575
+ "\u{000a}"
576
+ when 0x76 #v
577
+ "\u{000b}"
578
+ when 0x66 #f
579
+ "\u{000c}"
580
+ when 0x72 #r
581
+ "\u{000d}"
582
+ when 0x78 #x
583
+ t = [[@codes[@pos+1], @codes[@pos+2]].pack("U*").to_i(16)].pack("U*")
584
+ @pos += 2
585
+ t
586
+ when 0x75 #u
587
+ t = [[@codes[@pos+1], @codes[@pos+2], @codes[@pos+3], @codes[@pos+4]].pack("U*").to_i(16)].pack("U*")
588
+ @pos += 4
589
+ t
590
+ else
591
+ #
592
+ # octal
593
+ # Annex B
594
+ if octal?(@codes[@pos])
595
+ oct = 0
596
+ while octal?(@codes[@pos])
597
+ oct *= 8
598
+ oct += (@codes[@pos] - 0x30)
599
+ @pos += 1
600
+ end
601
+ [oct].pack("U*")
602
+ else
603
+ [@codes[@pos]].pack("U*")
604
+ end
605
+ end
606
+ end
607
+
608
+ def eof?(pos = nil)
609
+ if pos.nil?
610
+ pos = @pos
611
+ end
612
+ @codes[pos].nil?
613
+ end
614
+
615
+ #
616
+ # check next literal is 'l' or not
617
+ # if next literal is not 'l', position is not forwarded
618
+ # if next literal is 'l', position is forwarded
619
+ #
620
+ def match_lit(l, options = {})
621
+ eval_lit {
622
+ t = fwd_lit(options)
623
+ STDERR.puts "match_lit #{t} <=> #{l} #{t==l}" if @debug
624
+ t == l ? t : nil
625
+ }
626
+ end
627
+
628
+ def next_lit(options = {})
629
+ lit = nil
630
+ pos0 = @pos
631
+ return nil if eof?
632
+ while lit = next_input_element(options)
633
+ if lit and (lit.ws? or lit.lt?)
634
+ ;
635
+ else
636
+ break
637
+ end
638
+ end
639
+ @pos = pos0
640
+ lit
641
+ end
642
+
643
+ def fwd_lit(options = {})
644
+ lit = nil
645
+ return nil if eof?
646
+ if options[:nolt]
647
+ while lit = next_input_element(options)
648
+ if lit and lit.ws?
649
+ ;
650
+ else
651
+ break
652
+ end
653
+ end
654
+ else
655
+ while lit = next_input_element(options)
656
+ if lit and (lit.ws? or lit.lt?)
657
+ ;
658
+ else
659
+ break
660
+ end
661
+ end
662
+ end
663
+ lit
664
+ end
665
+
666
+ def ws_lit(options = {})
667
+ ret = next_input_element(options)
668
+ if ret and (ret.ws? or ret.lt?)
669
+ ret
670
+ else
671
+ nil
672
+ end
673
+ end
674
+
675
+ def rewind_pos
676
+ if @pos > 0
677
+ @pos -= 1
678
+ end
679
+ end
680
+
681
+ def debug_code(from, to = nil)
682
+ if to.nil?
683
+ to = (@error_pos || @pos)
684
+ end
685
+ @codes[from,to].pack("U*")
686
+ end
687
+
688
+ def debug_str(pos = nil)
689
+ if pos.nil?
690
+ pos = @error_pos
691
+ if pos.nil?
692
+ pos = @pos
693
+ end
694
+ end
695
+ if pos > 20
696
+ pos -= 20
697
+ pos0 = 20
698
+ elsif pos >= 0
699
+ pos0 = pos
700
+ pos = 0
701
+ end
702
+ t = ''
703
+ t << @codes[pos..(pos+80)].collect{|u| u == 10 ? 0x20 : u}.pack("U*")
704
+ t << "\n"
705
+ t << (' ' * pos0) + "^"
706
+ t
707
+ end
708
+
709
+ def debug_lit(pos = nil)
710
+ if pos.nil?
711
+ pos = @error_pos
712
+ if pos.nil?
713
+ pos = @pos
714
+ end
715
+ end
716
+ if pos > 20
717
+ pos -= 20
718
+ pos0 = 20
719
+ elsif pos >= 0
720
+ pos0 = pos
721
+ pos = 0
722
+ end
723
+ #STDERR.puts pos0
724
+ STDERR.puts @codes[pos..(pos+80)].collect{|u| u == 10 ? 0x20 : u}.pack("U*")
725
+ STDERR.puts (' ' * pos0) + "^"
726
+ end
727
+ #
728
+ # break <val> => position is rewind, then break with <val>
729
+ # return <val> => position is rewind, then return <val>
730
+ # next <val> => position is not rewind, then break with <val>
731
+ #
732
+ def eval_lit(&block)
733
+ begin
734
+ saved_pos = @pos
735
+ ret = yield
736
+ ensure
737
+ if ret.nil?
738
+ #@error_pos = @pos
739
+ @pos = saved_pos
740
+ nil
741
+ end
742
+ end
743
+ end
744
+ end
745
+ end