tagged release 0.7.1
[parrot.git] / t / compilers / pge / p5regex / p5rx.t
blob1793548d244211a857f038b1cbbe25104c7b2084
1 #!./parrot
2 # Copyright (C) 2001-2008, The Perl Foundation.
3 # $Id$
4 # vi: ft=pir
6 =head1 NAME
8 t/compilers/pge/p5regex/p5rx.t - Perl 5 Regular Expression tests
10 =head1 DESCRIPTION
12 These tests are ripped from the Perl 5.9.2 distribution. The test harness
13 has been modified to feed them to PGE's P5Regex compiler. The tests are
14 in a separate file in the same directory, named 're_tests'.
16 This test harness honors a special environment variable called C<TEST_P5RX>.
17 If set to a number, that test will be run alone and unconditionally--even
18 if it's designated as SKIP or TODO by the harness. This is quite helpful
19 in debugging tests that cause parrot to spiral out of control. {{ XXX }}
21 B<NOTE:> Don't add new tests to C<re_tests>. That file is strictly for
22 Perl 5's tests.
24 The Perl 5 equivalent file provides the following description of the test
25 format. There are 5-6 columns, separated by tabs.
27 Column 1 contains the pattern, optionally enclosed in C<''>.
28 Modifiers can be put after the closing C<'>.
30 Column 2 contains the string to be matched.
32 Column 3 contains the expected result:
33     y    expect a match
34     n    expect no match
35     c    expect an error
36     B    test exposes a known bug in Perl, should be skipped
37     b    test exposes a known bug in Perl, should be skipped if noamp
39 Columns 4 and 5 are used only if column 3 contains C<y> or C<c>.
41 Column 4 contains a string, usually C<$&>.
43 Column 5 contains the expected result of double-quote
44 interpolating that string after the match, or start of error message.
46 Column 6, if present, contains a description of what is being tested.
48 \n in the tests are interpolated, as are variables of the form ${\w+}.
50 =head1 SYNOPSIS
52     % prove t/compilers/pge/p5regex/p5rx.t
54 =cut
56 .const int TESTS = 960
58 .sub main :main
59     load_bytecode 'Test/Builder.pir'
60     load_bytecode 'PGE.pbc'
61     load_bytecode 'PGE/Dumper.pbc'
62     .include 'iglobals.pasm'
64     # Variable declarations, initializations
65     .local pmc test       # the test harness object.
66                test = new [ 'Test'; 'Builder' ]
68     .local pmc todo_tests # keys indicate test file; values test number.
69                todo_tests = new 'Hash'
71     .local pmc skip_tests # keys indicate tests ID; values reasons.
72                skip_tests = new 'Hash'
74     .local string test_dir # the directory containing tests
75                   test_dir = 't/compilers/pge/p5regex/'
77     .local pmc test_files # values are test file names to run.
78                test_files = new 'ResizablePMCArray'
80     # populate the list of test files
81     push test_files, 're_tests'
83     .local pmc file_iterator # iterate over list of files..
84                file_iterator = new 'Iterator', test_files
86     .local int test_number   # the number of the test we're running
87                test_number = 0
89     # these vars are in the loops below
90     .local pmc file_handle   # currently open file.
91     .local string test_file  # name of the current test file
92     .local string test_line  # one line of one test file, a single test
93     .local int ok            # is this a passing test?
95     # for any given test:
96     .local pmc regex          # the regex
97     .local pmc match          # the match
98     .local string pattern     # the regex
99     .local string target      # this string to match against the regex
100     .local string result      # expected result of this test. (y/n/...)
101     .local string testvar     # the value to test against expected results
102     .local string expected    # the expected result of the match, or the error
103     .local string description # user-facing description of the test.
105     todo_tests = 'set_todo_info'()
106     skip_tests = 'set_skip_info'()
108     # how many tests to run?
109     # XXX: this should be summed automatically from test_files data
110     #      until then, we use the constant above
111     test.'plan'(TESTS)
113   outer_loop:
114     unless file_iterator goto end_outer_loop
115     .local string test_name
116                   test_name = shift file_iterator
117     # local test number in test file
118     .local int local_test_number
119                local_test_number = 0
121     # append the test directory and filename
122     test_file = test_dir . test_name
124     # Open the test file
125     file_handle = open test_file, '<'
126     $S0 = typeof file_handle
127     if $S0 == 'Undef' goto bad_file
129     # loop over the file, one at a time.
131   loop:
132     # read in the file one line at a time...
133     $I0 = file_handle.'eof'()
134     if $I0 goto end_loop
136     test_line = readline file_handle
138     # skip lines without tabs
139     $I0 = index test_line, "\t"
140     if $I0 == -1 goto loop
141     inc test_number
142     inc local_test_number
144   parse_data:
145     push_eh eh_bad_line
146      ( pattern, target, result, testvar, expected, description ) = 'parse_data'( test_line )
147     pop_eh
149     # build the test description
150     #   start with the pattern
151     $S0 = concat '/', pattern
152     $S0 .= '/ '
153     #  add the test description, if it exists
154     $I0 = length description
155     unless $I0 goto no_desc
156     description = concat '-- ', description
157   no_desc:
158     description = concat $S0, description
159     # prepend test filename and line number to description
160     description = 'build_test_desc'( description, test_name, local_test_number )
162     if target != "''" goto got_target
163     target = ''
165   got_target:
166     target = 'backslash_escape'( target )
167     result = 'backslash_escape'( result )
169     # Should this test be skipped?
170     $I0 = exists skip_tests[test_name]
171     unless $I0 goto not_skip
172     $P0 = skip_tests[test_name]
173     $I0 = exists $P0[local_test_number]
174     unless $I0 goto not_skip
175     # extract reason from skip data
176     $S0 = $P0[local_test_number]
177     if $S0 == '1' goto set_skip
178     description = 'build_test_desc'( $S0, test_name, local_test_number )
179   set_skip:
180     test.'skip'(1, description)
181     goto loop
183   not_skip:
184     push_eh thrown
185     match = 'match_p5regex'( pattern, target )
186     pop_eh
188     if match goto matched
190     if result == 'n' goto is_ok
191     if result == 'y' goto is_nok
192     goto check_dump
194   matched:
195     if result == 'y' goto is_ok
196     if result == 'n' goto is_nok
198   check_dump:
199     $S1 = match.'dump_str'('mob', ' ', '')
201     $I0 = index $S1, result
202     if $I0 == -1 goto is_nok
204   is_ok:
205     ok = 1
206     goto emit_test
207   is_nok:
208     ok = 0
210   emit_test:
211     $I0 = exists todo_tests[test_name]
212     unless $I0 goto not_todo
213     $P0 = todo_tests[test_name]
214     $I0 = exists $P0[local_test_number]
215     unless $I0 goto not_todo
216     # extract reason from todo data
217     $S0 = $P0[local_test_number]
218     if $S0 == '1' goto set_todo
219     description = 'build_test_desc'( $S0, test_name, local_test_number )
220   set_todo:
221     test.'todo'(ok,description)
222     goto loop
223   not_todo:
224     test.'ok'(ok,description)
225     if ok goto loop
226     $S0 = concat 'pattern: /', pattern
227     $S1 = concat '/, target: "', target
228     $S0 .= $S1
229     $S1 = concat '", result: "', result
230     $S0 .= $S1
231     $S1 = concat '", testvar: "', testvar
232     $S0 .= $S1
233     $S1 = concat '", expected: "', expected
234     $S0 .= $S1
235     $S2 = match
236     $S1 = concat '", got: "', $S2
237     $S0 .= $S1
238     $S0 .= '"'
239     test.'diag'($S0)
241     goto loop
242   end_loop:
243     close file_handle
244     goto outer_loop
245   end_outer_loop:
247     test.'finish'()
248     end
250   bad_file:
251     print "Unable to open '"
252     print test_file
253     print "'\n"
255   thrown:
256     .local pmc exception
257     .local string message
258     get_results '0,0', exception, message
259     # remove /'s
260     # $S0 = substr result, 0, 1
261     # if $S0 != '/' goto bad_error
262     # substr result, 0, 1, ''
263     # substr result, -1, 1, ''
264     $I0 = index message, expected
265     if $I0 == -1 goto bad_error
266     ok = 1
267     goto emit_test
268   bad_error:
269     ok = 0
270     goto emit_test
271   bad_line:
272     $S0 = 'Test not formatted properly!'
273     test.'ok'(0, $S0)
274     goto loop
275   eh_bad_line:
276     $S0 = 'Test not formatted properly!'
277     test.'ok'(0, $S0)
278     goto loop
279 .end
282 # set todo information
283 .sub 'set_todo_info'
284     .local pmc todo_tests # keys indicate test file; values are just defined
285                todo_tests = new 'Hash'
287     .local pmc todo_info
288                todo_info = new 'Hash'
290     .local string test_file
292     test_file = 're_tests'
293     bsr reset_todo_info
295     $S0 = 'character class in enumeration'
296     todo_info[116] = $S0
297     todo_info[119] = $S0
298     todo_info[120] = $S0
299     todo_info[123] = $S0
300     todo_info[124] = $S0
301     todo_info[127] = $S0
303     $S0 = 'unknown reason'
304 #    todo_info[172] = $S0
305 #    todo_info[184] = $S0
306 #    todo_info[223] = $S0
307 #    todo_info[232] = $S0
308 #    todo_info[233] = $S0
309     todo_info[234] = $S0
310     todo_info[235] = $S0
311     todo_info[236] = $S0
312 #    todo_info[241] = $S0
313 #    todo_info[243] = $S0
314 #    todo_info[244] = $S0
315     todo_info[246] = $S0
316     todo_info[247] = $S0
317 #    todo_info[253] = $S0
318     todo_info[254] = $S0
319     todo_info[256] = $S0
320     todo_info[257] = $S0
321 #    todo_info[260] = $S0
322 #    todo_info[261] = $S0
323     todo_info[381] = $S0
324     todo_info[382] = $S0
325     todo_info[396] = $S0
326     todo_info[397] = $S0
327     todo_info[398] = $S0
328     todo_info[419] = $S0
329     todo_info[422] = $S0
330 #    todo_info[428] = $S0
331     todo_info[429] = $S0
332     todo_info[432] = $S0
333     todo_info[434] = $S0
334     todo_info[435] = $S0
335     todo_info[439] = $S0
336 #    todo_info[440] = $S0
337 #    todo_info[444] = $S0
338 #    todo_info[445] = $S0
339     todo_info[446] = $S0
340     todo_info[447] = $S0
341     todo_info[448] = $S0
342     todo_info[449] = $S0
343     todo_info[452] = $S0
344     todo_info[453] = $S0
345     todo_info[454] = $S0
346     todo_info[455] = $S0
347     todo_info[495] = $S0
348     todo_info[498] = $S0
349     todo_info[500] = $S0
350     todo_info[501] = $S0
351     todo_info[503] = $S0
352     todo_info[504] = $S0
353     todo_info[505] = $S0
354     todo_info[506] = $S0
355     todo_info[507] = $S0
356     todo_info[508] = $S0
357     todo_info[509] = $S0
358     todo_info[510] = $S0
359     todo_info[511] = $S0
360     todo_info[512] = $S0
361     todo_info[515] = $S0
362     todo_info[521] = $S0
363     todo_info[522] = $S0
364     todo_info[523] = $S0
365     todo_info[524] = $S0
366     todo_info[527] = $S0
367     todo_info[528] = $S0
368     todo_info[535] = $S0
369     todo_info[536] = $S0
370     todo_info[539] = $S0
371     todo_info[540] = $S0
372     todo_info[541] = $S0
373     todo_info[544] = $S0
374     todo_info[545] = $S0
375     todo_info[559] = $S0
376     todo_info[595] = $S0
377     todo_info[596] = $S0
378     todo_info[600] = $S0
379     todo_info[601] = $S0
380 #    todo_info[602] = $S0
381     todo_info[603] = $S0
382     todo_info[604] = $S0
383 #    todo_info[605] = $S0
384     todo_info[606] = $S0
385     todo_info[607] = $S0
386     todo_info[621] = $S0
387     todo_info[623] = $S0
388     todo_info[624] = $S0
389     todo_info[625] = $S0
390     todo_info[639] = $S0
391     todo_info[641] = $S0
392     todo_info[642] = $S0
393     todo_info[643] = $S0
394     todo_info[693] = $S0
395     todo_info[695] = $S0
396     todo_info[696] = $S0
397     todo_info[697] = $S0
398     todo_info[747] = $S0
399     todo_info[749] = $S0
400     todo_info[750] = $S0
401     todo_info[751] = $S0
402     todo_info[801] = $S0
403 #    todo_info[840] = $S0
404     todo_info[858] = $S0
405     todo_info[859] = $S0
406 #    todo_info[860] = $S0
407 #    todo_info[861] = $S0
408     todo_info[862] = $S0
409 #    todo_info[863] = $S0
410     todo_info[865] = $S0
411     todo_info[866] = $S0
412 #    todo_info[874] = $S0
413 #    todo_info[875] = $S0
414 #    todo_info[876] = $S0
415     todo_info[881] = $S0
416 #    todo_info[882] = $S0
417     todo_info[887] = $S0
418     todo_info[888] = $S0
419     todo_info[890] = $S0
420     todo_info[891] = $S0
421     todo_info[893] = $S0
422 #    todo_info[894] = $S0
423 #    todo_info[895] = $S0
424     todo_info[896] = $S0
425     todo_info[897] = $S0
426     todo_info[898] = $S0
427     todo_info[899] = $S0
428 #    todo_info[900] = $S0
430     $S0 = 'reuse captured group'
431     todo_info[928] = $S0
432     todo_info[929] = $S0
433     todo_info[930] = $S0
434     todo_info[931] = $S0
435     todo_info[932] = $S0
436     todo_info[933] = $S0
437     todo_info[934] = $S0
438     todo_info[935] = $S0
439     todo_info[936] = $S0
440     todo_info[937] = $S0
441     todo_info[938] = $S0
442     todo_info[939] = $S0
443     todo_info[940] = $S0
444     todo_info[941] = $S0
445     todo_info[942] = $S0
447     $S0 = 'non-greedy/lookbehind'
448     todo_info[915] = $S0
449     todo_info[916] = $S0
450     todo_info[918] = $S0
451 #    todo_info[919] = $S0
452 #    todo_info[920] = $S0
453     todo_info[921] = $S0
454     todo_info[922] = $S0
456     $S0 = 'greediness/lookbehind'
457     todo_info[901] = $S0
458 #    todo_info[902] = $S0
459 #    todo_info[903] = $S0
460     todo_info[904] = $S0
461     todo_info[905] = $S0
463     $S0 = 'non-greedy/zero-width assertion'
464     todo_info[907] = $S0
465     todo_info[908] = $S0
466 #    todo_info[909] = $S0
467     todo_info[910] = $S0
468 #    todo_info[912] = $S0
469     todo_info[913] = $S0
470     todo_info[914] = $S0
471     todo_info[960] = $S0
473     $S0 = '\d in character class'
474 #    todo_info[825] = $S0
475 #    todo_info[826] = $S0
476     todo_info[827] = $S0
478     $S0 = '[ID 20010803.016]'
479 #    todo_info[884] = $S0
481     $S0 = '[perl #34195]'
482     todo_info[959] = $S0
484     $S0 = 'undef [perl #16773]'
485 #    todo_info[925] = $S0
487     $S0 = 'unmatched bracket'
488     todo_info[923] = $S0
490     $S0 = '16 tests for [perl #23171]'
491     todo_info[927] = $S0
493     todo_tests[test_file] = todo_info
495     .return (todo_tests)
497   reset_todo_info:
498     todo_info = new 'Hash'
499     ret
501   set_todo_loop: # for developer testing. not used normally
502     if $I0 > $I1 goto end_loop
503     todo_info[$I0] = 1
504     $I0 += 1
505     goto set_todo_loop
506   end_loop:
507     ret
508 .end
511 # set skip information
512 .sub 'set_skip_info'
513     .local pmc skip_tests # keys indicate test file; values are just defined
514                skip_tests = new 'Hash'
516     .local pmc skip_info
517                skip_info = new 'Hash'
519     .local string test_file
521     test_file = 're_tests'
522     bsr reset_skip_info
524     $S0 = 'trailing modifiers'
525     $I0 = 264
526     $I1 = 395
527     bsr set_range
528     $I0 = 458
529     $I1 = 480
530     bsr set_range
531     skip_info[483] = $S0
532     skip_info[484] = $S0
533     skip_info[496] = $S0
534     $I0 = 609
535     $I1 = 617
536     bsr set_range
537     $I0 = 627
538     $I1 = 635
539     bsr set_range
540     $I0 = 645
541     $I1 = 653
542     bsr set_range
543     $I0 = 663
544     $I1 = 671
545     bsr set_range
546     $I0 = 681
547     $I1 = 689
548     bsr set_range
549     $I0 = 699
550     $I1 = 707
551     bsr set_range
552     $I0 = 717
553     $I1 = 725
554     bsr set_range
555     $I0 = 735
556     $I1 = 743
557     bsr set_range
558     $I0 = 753
559     $I1 = 761
560     bsr set_range
561     $I0 = 771
562     $I1 = 779
563     bsr set_range
564     $I0 = 789
565     $I1 = 797
566     bsr set_range
567     skip_info[802] = $S0
568     skip_info[803] = $S0
569     skip_info[805] = $S0
570     skip_info[834] = $S0
571     skip_info[835] = $S0
572     skip_info[836] = $S0
573     skip_info[838] = $S0
574     skip_info[859] = $S0
575     skip_info[862] = $S0
576     skip_info[877] = $S0
577     skip_info[886] = $S0
579     $S0 = 'bug or error'
580     skip_info[143] = $S0
581     skip_info[144] = $S0
582     skip_info[148] = $S0
583     skip_info[149] = $S0
584     skip_info[155] = $S0
585     skip_info[167] = $S0
586     skip_info[248] = $S0
587     skip_info[249] = $S0
588     skip_info[252] = $S0
589     skip_info[308] = $S0
590     skip_info[309] = $S0
591     skip_info[310] = $S0
592     skip_info[322] = $S0
593     skip_info[323] = $S0
594     skip_info[325] = $S0
595     skip_info[330] = $S0
596     skip_info[331] = $S0
597     skip_info[336] = $S0
598     skip_info[347] = $S0
599     skip_info[408] = $S0
600     skip_info[436] = $S0
601     skip_info[487] = $S0
602     skip_info[488] = $S0
603     skip_info[489] = $S0
604     skip_info[490] = $S0
605     skip_info[492] = $S0
606     skip_info[531] = $S0
607     skip_info[532] = $S0
608     skip_info[563] = $S0
609     skip_info[564] = $S0
610     skip_info[566] = $S0
611     skip_info[593] = $S0
612     skip_info[594] = $S0
613     skip_info[598] = $S0
614     skip_info[599] = $S0
615     skip_info[944] = $S0
616     skip_info[945] = $S0
618     $S0 = 'broken col 4?'
619     skip_info[139] = $S0
621     $S0 = 'kills a parrot'
622     skip_info[491] = $S0
623     skip_info[493] = $S0
624     skip_info[556] = $S0
625     skip_info[557] = $S0
626     $I0 = 568
627     $I1 = 592
628     bsr set_range
629     skip_info[800] = $S0
630     skip_info[828] = $S0
631     skip_info[829] = $S0
632     skip_info[830] = $S0
633     skip_info[957] = $S0
634     skip_info[958] = $S0
636     $S0 = 'hangs a parrot'
637     $I0 = 806
638     $I1 = 823
639     bsr set_range
640     skip_info[924] = $S0
642     $S0 = 'unknown reason'
643     skip_info[502] = $S0
644     skip_info[597] = $S0
645     skip_info[944] = $S0
646     skip_info[945] = $S0
648     $S0 = '[ID 20010811.006]'
649     skip_info[879] = $S0
651     $S0 = '[perl #18019]'
652     skip_info[926] = $S0
654     $S0 = 'parser bug'
655     skip_info[138] = $S0
657     skip_tests[test_file] = skip_info
659     .return (skip_tests)
661   reset_skip_info:
662     skip_info = new 'Hash'
663     ret
665   set_range:                         # for setting a range of tests
666     if $I0 > $I1 goto end_loop       # put range min in $I0, max in $I1
667     if $S0 != '' goto set_skip_info  # put skip reason in $S0
668     $S0 = 'unknown reason'
669   set_skip_info:
670     skip_info[$I0] = $S0
671     $I0 += 1
672     goto set_range
673   end_loop:
674     $S0 = ''
675     ret
676 .end
679 .sub 'parse_data'
680     .param string test_line   # the data record
682     .local pmc regex          # the regex matching object
683     .local pmc match          # the match
684     .local string pattern     # the regex
685     .local string target      # this string to match against the regex
686     .local string result      # expected result of this test. (y/n/...)
687     .local string testvar     # the value to test against expected results
688     .local string expected    # the expected result of the match, or the error
689     .local string description # user-facing description of the test.
691     # NOTE: there can be multiple tabs between entries, so skip until
692     # we have something.
693     # remove the trailing newline from record
694     chopn test_line, 1
696     $P1 = split "\t", test_line
698   get_pattern:
699     unless $P1 goto bad_line
700     pattern = shift $P1
701     if pattern == '' goto get_pattern
702   get_target:
703     unless $P1 goto bad_line
704     target = shift $P1
705   get_result:
706     unless $P1 goto bad_line
707     result = shift $P1
708     if result == '' goto get_result
709   get_testvar:
710     unless $P1 goto bad_line
711     testvar = shift $P1
712     if testvar == '' goto get_testvar
713   get_expected:
714     unless $P1 goto bad_line
715     expected = shift $P1
717   description = ''
719   return:
720     .return ( pattern, target, result, testvar, expected, description )
722   bad_line:
723      $P1 = new 'Exception'
724      $P1[0] = 'invalid data format'
725      throw $P1
726 .end
729 .sub 'build_test_desc'
730     .param string desc
731     .param string test_name
732     .param string local_test_number
734     $S0  = '['
735     $S0 .= test_name
736     $S0 .= ':'
737     $S0 .= local_test_number
738     $S0 .= '] '
739     $S0 .= desc
741     .return ($S0)
742 .end
745 .sub 'match_p5regex'
746     .param string pattern
747     .param string target
749     .local pmc match
751     .local pmc p5regex     # the perl5 regex compiler
752                p5regex = compreg 'PGE::P5Regex'
754     .local pmc regex
755                regex = p5regex(pattern)
757     unless_null regex, match_it
758     $P1 = new 'Exception'
759     $P1[0] = 'regex error'
760     throw $P1
761   match_it:
762     match = regex(target)
764     .return (match)
765 .end
768 # given a 2 digit string, convert to appropriate chr() value.
769 .sub hex_chr
770     .param string hex
772     $S0 = substr hex, 0, 1
773     $S1 = substr hex, 1, 1
775     $I0 = hex_val($S0)
776     $I1 = hex_val($S1)
778     $I0 *=16
779     $I0 += $I1
781     $S2 = chr $I0
783     .return ($S2)
784 .end
787 # given a single digit hex value, return it's int value.
788 .sub hex_val
789     .param string digit
791     $I0 = ord digit
792     if $I0 < 48 goto bad_digit
793     if $I0 > 57 goto non_numeric
794     $I0 -=48
795     .return ($I0)
796   non_numeric:
797     if $I0 < 65 goto bad_digit
798     if $I0 > 70 goto not_capital
799     $I0 -= 55 # A is ascii 65, so reset to zero, add 10 for hex..
800     .return ($I0)
801   not_capital:
802     if $I0 < 97 goto  bad_digit
803     if $I0 > 102 goto bad_digit
804     $I0 -= 87 # a is ascii 97, so reset to zero, add 10 for hex..
805     .return ($I0)
807   bad_digit:
808     $P1 = new 'Exception'
809     $P1[0] = 'invalid hex digit'
810     throw $P1
811 .end
814 .sub backslash_escape
815     .param string target
817     .local int x_pos         # position in string of last \x escape..
818                x_pos = 0
820   target1:
821     $I0 = index target, '\n'
822     if $I0 == -1 goto target2
823     substr target, $I0, 2, "\n"
824     goto target1
825   target2:
826     $I0 = index target, '\r'
827     if $I0 == -1 goto target3
828     substr target, $I0, 2, "\r"
829     goto target2
830   target3:
831     $I0 = index target, '\e'
832     if $I0 == -1 goto target4
833     substr target, $I0, 2, "\e"
834     goto target3
835   target4:
836     $I0 = index target, '\t'
837     if $I0 == -1 goto target5
838     substr target, $I0, 2, "\t"
839     goto target4
840   target5:
841     $I0 = index target, '\f'
842     if $I0 == -1 goto target6
843     substr target, $I0, 2, "\f"
844     goto target5
845   target6:
846     # handle \xHH, hex escape.
848     $I0 = index target, '\x', x_pos
849     if $I0 == -1 goto target7
851     $I1 = length target
852     $I2 = $I0 + 2
854     if $I2 > $I1 goto target7
855     $S0 = substr target, $I2, 2
856     $S1 = hex_chr($S0)
857     substr target, $I0, 4, $S1
859     inc x_pos
860     goto target6
861   target7:
862     .return (target)
863 .end
865 =head1 BUGS AND LIMITATIONS
867 Note that while our job would be easier if we could use regular expressions
868 in here, but we want to avoid any dependency on the thing we're testing.
870 Need to add in test IDs, to avoid the precarious line numbering.
872 =cut
874 # Local Variables:
875 #   mode: pir
876 #   fill-column: 100
877 # End:
878 # vim: expandtab shiftwidth=4 ft=pir: