1# Copyright (C) 2006-2010, Parrot Foundation.
2
3=head1 NAME
4
5PCT::HLLCompiler - base class for compiler objects
6
7=head1 DESCRIPTION
8
9This file implements a C<HLLCompiler> class of objects used for
10creating HLL compilers.  It provides the standard methods required
11for all compilers, as well as some standard scaffolding for
12running compilers from a command line.
13
14=cut
15
16.sub 'onload' :anon :load :init
17    load_bytecode 'P6object.pbc'
18    load_bytecode 'Parrot/Exception.pbc'
19    $P0 = new 'P6metaclass'
20    $S0 = '@stages $parsegrammar $parseactions $astgrammar $commandline_banner $commandline_prompt @cmdoptions $usage $version $compiler_progname'
21    $P0.'new_class'('PCT::HLLCompiler', 'attr'=>$S0)
22.end
23
24.namespace [ 'PCT';'HLLCompiler' ]
25
26.include 'cclass.pasm'
27.include 'iglobals.pasm'
28
29.sub 'init' :vtable :method
30    $P0 = split ' ', 'parse past post pir evalpmc'
31    setattribute self, '@stages', $P0
32
33    $P0 = split ' ', 'e=s help|h target=s dumper=s trace|t=s encoding=s output|o=s combine version|v stagestats ll-backtrace'
34    setattribute self, '@cmdoptions', $P0
35
36    $P1 = box <<'    USAGE'
37  This compiler is based on PCT::HLLCompiler.
38
39  Options:
40    USAGE
41
42    .local pmc it
43    it = iter $P0
44  options_loop:
45    unless it goto options_end
46    $P3  = shift it
47    $P1 .= "    "
48    $P1 .= $P3
49    $P1 .= "\n"
50    goto options_loop
51  options_end:
52    setattribute self, '$usage', $P1
53
54    $S0 = 'This compiler is built with the Parrot Compiler Toolkit, parrot '
55    $P0 = getinterp
56    $P0 = $P0[.IGLOBALS_CONFIG_HASH]
57    $S1 = $P0['VERSION']
58    $S0 .= $S1
59    $S1 = $P0['git_describe']
60    unless $S1 goto version_done
61    $S0 .= ' revision '
62    $S0 .= $S1
63  version_done:
64
65    $P2 = box $S0
66    setattribute self, '$version', $P2
67.end
68
69
70=head2 Methods
71
72=over 4
73
74=item attr(string attrname, pmc value, int has_value)
75
76Helper method for accessors -- gets/sets an attribute given
77by C<attrname> based on C<has_value>.
78
79=cut
80
81.sub 'attr' :method
82    .param string attrname
83    .param pmc value
84    .param int has_value
85    if has_value goto set_value
86    value = getattribute self, attrname
87    unless null value goto end
88    value = new 'Undef'
89    goto end
90  set_value:
91    setattribute self, attrname, value
92  end:
93    .return (value)
94.end
95
96
97=item panic(message :slurpy)
98
99Helper method to throw an exception (with a message).
100
101=cut
102
103.sub 'panic' :method
104    .param pmc args            :slurpy
105    $S0 = join '', args
106    die $S0
107.end
108
109
110=item language(string name)
111
112Register this object as the compiler for C<name> using the
113C<compreg> opcode.
114
115=cut
116
117.sub 'language' :method
118    .param string name
119    compreg name, self
120    .return ()
121.end
122
123=item stages([stages])
124
125Accessor for the C<stages> attribute.
126
127=item parsegrammar([string grammar])
128
129Accessor for the C<parsegrammar> attribute.
130
131=item parseactions([actions])
132
133Accessor for the C<parseactions> attribute.
134
135=item astgrammar([grammar])
136
137Accessor for the C<astgrammar> attribute.
138
139=item commandline_banner([string value])
140
141Set the command-line banner for this compiler to C<value>.
142The banner is displayed at the beginning of interactive mode.
143
144=item commandline_prompt([string value])
145
146Set the command-line prompt for this compiler to C<value>.
147The prompt is displayed in interactive mode at each point where
148the compiler is ready for code to be compiled and executed.
149
150=item compiler_progname([string name])
151
152Accessor for the C<compiler_progname>, which is often the filename of
153the compiler's program entry point, like C<perl6.pbc>.
154
155=cut
156
157.sub 'stages' :method
158    .param pmc value           :optional
159    .param int has_value       :opt_flag
160    .tailcall self.'attr'('@stages', value, has_value)
161.end
162
163.sub 'parsegrammar' :method
164    .param pmc value        :optional
165    .param int has_value       :opt_flag
166    .tailcall self.'attr'('$parsegrammar', value, has_value)
167.end
168
169.sub 'parseactions' :method
170    .param pmc value           :optional
171    .param int has_value       :opt_flag
172    .tailcall self.'attr'('$parseactions', value, has_value)
173.end
174
175.sub 'astgrammar' :method
176    .param pmc value        :optional
177    .param int has_value       :opt_flag
178    .tailcall self.'attr'('$astgrammar', value, has_value)
179.end
180
181.sub 'commandline_banner' :method
182    .param string value        :optional
183    .param int has_value       :opt_flag
184    .tailcall self.'attr'('$commandline_banner', value, has_value)
185.end
186
187.sub 'commandline_prompt' :method
188    .param string value        :optional
189    .param int has_value       :opt_flag
190    .tailcall self.'attr'('$commandline_prompt', value, has_value)
191.end
192
193.sub 'compiler_progname' :method
194    .param pmc value        :optional
195    .param int has_value       :opt_flag
196    .tailcall self.'attr'('$compiler_progname', value, has_value)
197.end
198
199=item removestage(string stagename)
200
201Delete a stage from the compilation process queue.
202
203=cut
204
205.sub 'removestage' :method
206    .param string stagename
207
208    .local pmc stages, it, newstages
209    stages = getattribute self, '@stages'
210    newstages = new 'ResizableStringArray'
211
212    it = iter stages
213  iter_loop:
214    unless it goto iter_end
215    .local pmc current
216    current = shift it
217    if current == stagename goto iter_loop
218      push newstages, current
219    goto iter_loop
220  iter_end:
221    setattribute self, '@stages', newstages
222.end
223
224=item addstage(string stagename [, "option" => value, ... ])
225
226Add a stage to the compilation process queue. Takes either a "before" or
227"after" named argument, which gives the relative ordering of the stage
228to be added. If "before" and "after" aren't specified, the new stage is
229inserted at the end of the queue.
230
231It's possible to add multiple stages of the same name: for example, you
232might repeat a stage like "optimize_tree" or "display_benchmarks" after
233each transformation. If you have multiple stages of the same name, and
234add a new stage before or after that repeated stage, the new stage will
235be added at every instance of the repeated stage.
236
237=cut
238
239.sub 'addstage' :method
240    .param string stagename
241    .param pmc adverbs         :slurpy :named
242
243    .local string position, target
244    .local pmc stages
245    stages = getattribute self, '@stages'
246
247    $I0 = exists adverbs['before']
248    unless $I0 goto next_test
249      position = 'before'
250      target = adverbs['before']
251    goto positional_insert
252
253  next_test:
254    $I0 = exists adverbs['after']
255    unless $I0 goto simple_insert
256      position = 'after'
257      target = adverbs['after']
258
259  positional_insert:
260    .local pmc it, newstages
261    newstages = new 'ResizableStringArray'
262
263    it = iter stages
264  iter_loop:
265    unless it goto iter_end
266    .local pmc current
267    current = shift it
268    unless current == target goto no_insert_before
269      unless position == 'before' goto no_insert_before
270        push newstages, stagename
271    no_insert_before:
272
273    push newstages, current
274
275    unless current == target goto no_insert_after
276      unless position == 'after' goto no_insert_after
277        push newstages, stagename
278    no_insert_after:
279
280    goto iter_loop
281  iter_end:
282    setattribute self, '@stages', newstages
283    goto done
284
285  simple_insert:
286    push stages, stagename
287  done:
288
289.end
290
291=item compile(pmc code [, "option" => value, ... ])
292
293Compile C<source> (possibly modified by any provided options)
294by iterating through any stages identified for this compiler.
295If a C<target> option is provided, then halt the iteration
296when the stage corresponding to target has been reached.
297
298=cut
299
300.sub 'compile' :method
301    .param pmc source
302    .param pmc adverbs         :slurpy :named
303
304    .local pmc compiling, options
305    compiling = new ['Hash']
306    .lex '%*COMPILING', compiling
307    compiling['%?OPTIONS'] = adverbs
308
309    .local string target
310    target = adverbs['target']
311    target = downcase target
312
313    .local int stagestats
314    stagestats = adverbs['stagestats']
315
316    .local pmc stages, result, it
317    result = source
318    stages = getattribute self, '@stages'
319    it = iter stages
320    if stagestats goto stagestats_loop
321
322  iter_loop:
323    unless it goto have_result
324    .local string stagename
325    stagename = shift it
326    result = self.stagename(result, adverbs :flat :named)
327    if target == stagename goto have_result
328    goto iter_loop
329
330  stagestats_loop:
331    unless it goto have_result
332    stagename = shift it
333    $N0 = time
334    result = self.stagename(result, adverbs :flat :named)
335    $N1 = time
336    $N2 = $N1 - $N0
337    $P0 = getinterp
338    $P1 = $P0.'stderr_handle'()
339    $P1.'print'("Stage '")
340    $P1.'print'(stagename)
341    $P1.'print'("': ")
342    $P2 = new ['ResizablePMCArray']
343    push $P2, $N2
344    $S0 = sprintf "%.3f", $P2
345    $P1.'print'($S0)
346    $P1.'print'(" sec\n")
347    if target == stagename goto have_result
348    goto stagestats_loop
349
350  have_result:
351    .return (result)
352.end
353
354
355=item parse(source [, "option" => value, ...])
356
357Parse C<source> using the compiler's C<parsegrammar> according
358to any options and return the resulting parse tree.
359
360=cut
361
362.sub 'parse' :method
363    .param pmc source
364    .param pmc adverbs         :slurpy :named
365    .local pmc parsegrammar, top
366
367    .local string tcode
368    tcode = adverbs['transcode']
369    unless tcode goto transcode_done
370    .local pmc tcode_it
371    $P0 = split ' ', tcode
372    tcode_it = iter $P0
373  tcode_loop:
374    unless tcode_it goto transcode_done
375    tcode = shift tcode_it
376    push_eh tcode_fail
377    $I0 = find_encoding tcode
378    $S0 = source
379    $S0 = trans_encoding $S0, $I0
380    assign source, $S0
381    pop_eh
382    goto transcode_done
383  tcode_fail:
384    pop_eh
385    goto tcode_loop
386  transcode_done:
387
388    .local string target
389    target = adverbs['target']
390    target = downcase target
391
392    parsegrammar = self.'parsegrammar'()
393    $I0 = can parsegrammar, 'TOP'
394    unless $I0 goto parsegrammar_string
395    top = find_method parsegrammar, 'TOP'
396    goto have_top
397  parsegrammar_string:
398    $S0 = typeof parsegrammar
399    eq $S0, 'NameSpace', parsegrammar_ns
400    $P0 = self.'parse_name'(parsegrammar)
401    $S0 = pop $P0
402    $P1 = get_hll_global $P0, $S0
403    $I0 = can $P1, 'TOP'
404    unless $I0 goto parsegrammar_ns_string
405    top = find_method $P1, 'TOP'
406    goto have_top
407  parsegrammar_ns_string:
408    $P0 = self.'parse_name'(parsegrammar)
409    top = get_hll_global $P0, 'TOP'
410    unless null top goto have_top
411    goto err_notop
412  parsegrammar_ns:
413    top = parsegrammar['TOP']
414    unless null top goto have_top
415  err_notop:
416    self.'panic'('Cannot find TOP regex in ', parsegrammar)
417  have_top:
418    .local pmc parseactions, action
419    null action
420    if target == 'parse' goto have_action
421    parseactions = self.'parseactions'()
422    $I0 = isa parseactions, ['Undef']
423    if $I0 goto have_action
424    ##  if parseactions is a protoobject, use it directly
425    $I0 = isa parseactions, 'P6protoobject'
426    if $I0 goto action_exact
427    ##  if parseactions is a Class or array, make action directly from that
428    $I0 = isa parseactions, 'Class'
429    if $I0 goto action_make
430    $I0 = isa parseactions, 'NameSpace'
431    if $I0 goto action_namespace
432    $I0 = does parseactions, 'array'
433    if $I0 goto action_make
434    ##  if parseactions is not a String, use it directly.
435    $I0 = isa parseactions, 'String'
436    if $I0 goto action_string
437  action_exact:
438    action = parseactions
439    goto have_action
440  action_namespace:
441    $P0 = get_class parseactions
442    action = new $P0
443    goto have_action
444  action_string:
445    ##  Try the string itself, if that fails try splitting on '::'
446    $P0 = get_class parseactions
447    unless null $P0 goto action_make
448    $S0 = parseactions
449    parseactions = split '::', $S0
450    push_eh err_bad_parseactions
451    $P0 = get_class parseactions
452    if null $P0 goto err_bad_parseactions
453    pop_eh
454  action_make:
455    action = new parseactions
456  have_action:
457    .local pmc match
458    match = top(source, 'grammar' => parsegrammar, 'action' => action)
459    unless match goto err_failedparse
460    .return (match)
461
462  err_no_parsegrammar:
463    self.'panic'('Missing parsegrammar in compiler')
464    .return ()
465  err_failedparse:
466    self.'panic'('Failed to parse source')
467    .return ()
468  err_bad_parseactions:
469    pop_eh
470    $P0 = self.'parseactions'()
471    self.'panic'('Unable to find action grammar ', $P0)
472    .return ()
473.end
474
475
476=item past(source [, "option" => value, ...])
477
478Transform C<source> into PAST using the compiler's
479C<astgrammar> according to any options, and return the
480resulting ast.
481
482=cut
483
484.sub 'past' :method
485    .param pmc source
486    .param pmc adverbs         :slurpy :named
487
488  compile_astgrammar:
489    .local pmc astgrammar_name
490    astgrammar_name = self.'astgrammar'()
491    $S0 = typeof astgrammar_name
492    eq $S0, 'NameSpace', astgrammar_ns
493    unless astgrammar_name goto compile_match
494
495    .local pmc astgrammar_namelist
496    .local pmc astgrammar, astbuilder
497    astgrammar_namelist = self.'parse_name'(astgrammar_name)
498    unless astgrammar_namelist goto err_past
499    astgrammar = new astgrammar_namelist
500    astbuilder = astgrammar.'apply'(source)
501    .tailcall astbuilder.'get'('past')
502  astgrammar_ns:
503    $P0 = get_class astgrammar_name
504    astgrammar = new $P0
505    astbuilder = astgrammar.'apply'(source)
506    .tailcall astbuilder.'get'('past')
507
508  compile_match:
509    #push_eh err_past
510    .local pmc ast
511    ast = source.'ast'()
512    #pop_eh
513    $I0 = isa ast, ['PAST';'Node']
514    unless $I0 goto err_past
515    .return (ast)
516
517  err_past:
518    #pop_eh
519    $S0 = typeof source
520    .tailcall self.'panic'('Unable to obtain PAST from ', $S0)
521.end
522
523
524=item post(source [, adverbs :slurpy :named])
525
526Transform PAST C<source> into POST.
527
528=cut
529
530.sub 'post' :method
531    .param pmc source
532    .param pmc adverbs         :slurpy :named
533    $P0 = compreg 'PAST'
534    .tailcall $P0.'to_post'(source, adverbs :flat :named)
535.end
536
537
538.sub 'pir' :method
539    .param pmc source
540    .param pmc adverbs         :slurpy :named
541
542    $P0 = compreg 'POST'
543    .tailcall $P0.'to_pir'(source, adverbs :flat :named)
544.end
545
546
547.sub 'evalpmc' :method
548    .param pmc source
549    .param pmc adverbs         :slurpy :named
550
551    $P0 = compreg 'PIR'
552    $P1 = $P0(source)
553    $P2 = $P1.'subs_by_tag'('init')
554    $P3 = iter $P2
555  loop_top:
556    unless $P3 goto loop_bottom
557    $P4 = shift $P3
558    $P4()
559    goto loop_top
560  loop_bottom:
561    $P1 = $P1.'first_sub_in_const_table'()
562    .return($P1)
563.end
564
565
566
567=item eval(code [, "option" => value, ...])
568
569Compile and execute the given C<code> taking into account any
570options provided.
571
572=cut
573
574.sub 'eval' :method
575    .param pmc code
576    .param pmc args            :slurpy
577    .param pmc adverbs         :slurpy :named
578
579    unless null args goto have_args
580    args = new 'ResizablePMCArray'
581  have_args:
582    unless null adverbs goto have_adverbs
583    adverbs = new 'Hash'
584  have_adverbs:
585
586    $P0 = self.'compile'(code, adverbs :flat :named)
587    $I0 = isa $P0, 'String'
588    if $I0 goto end
589    .local string target
590    target = adverbs['target']
591    if target != '' goto end
592    .local pmc outer_ctx, outer
593    outer_ctx = adverbs['outer_ctx']
594    if null outer_ctx goto outer_done
595    outer = outer_ctx['current_sub']
596    $P1 = $P0[0]
597    $P1.'set_outer'(outer)
598  outer_done:
599    $I0 = adverbs['trace']
600    trace $I0
601    $P0 = $P0(args :flat)
602    trace 0
603  end:
604    .return ($P0)
605.end
606
607
608=item interactive(["encoding" => encoding] [, "option" => value, ...])
609
610Runs an interactive compilation session -- reads lines of input
611from the standard input and evaluates each.  The C<encoding> option
612specifies the encoding to use for the input (e.g., "utf8").
613
614=cut
615
616.sub 'interactive' :method
617    .param pmc adverbs         :slurpy :named
618    .local string target, encoding
619    target = adverbs['target']
620    target = downcase target
621
622    # on startup show the welcome message
623    $P0 = self.'commandline_banner'()
624    $P1 = getinterp
625    $P2 = $P1.'stderr_handle'()
626    $P2.'print'($P0)
627
628    .local pmc stdin
629    .local int has_readline
630    $P0 = getinterp
631    stdin = $P0.'stdin_handle'()
632    encoding = adverbs['encoding']
633    if encoding == 'fixed_8' goto interactive_loop
634    unless encoding goto interactive_loop
635    stdin.'encoding'(encoding)
636  interactive_loop:
637    .local pmc code
638    unless stdin goto interactive_end
639
640    .local string prompt
641    prompt = '> '
642    $P0 = self.'commandline_prompt'()
643    $I0 = defined $P0
644    unless $I0 goto have_prompt
645    prompt = $P0
646  have_prompt:
647
648    ##  display a prompt ourselves if readline isn't present
649  interactive_read:
650    code = stdin.'readline_interactive'(prompt)
651    if null code goto interactive_end
652    unless code goto interactive_loop
653    code = concat code, "\n"
654    push_eh interactive_trap
655    $P0 = self.'eval'(code, adverbs :flat :named)
656    pop_eh
657    if null $P0 goto interactive_loop
658    unless target goto interactive_loop
659    if target == 'pir' goto target_pir
660    self.'dumper'($P0, target, adverbs :flat :named)
661    goto interactive_loop
662  target_pir:
663    say $P0
664    goto interactive_loop
665  interactive_trap:
666    get_results '0', $P0
667    pop_eh
668    $S0 = $P0
669    if $S0 == '' goto have_newline
670    $S1 = substr $S0, -1, 1
671    $I0 = is_cclass .CCLASS_NEWLINE, $S1, 0
672    if $I0 goto have_newline
673    $S0 = concat $S0, "\n"
674  have_newline:
675    print $S0
676    goto interactive_loop
677  interactive_end:
678    .return ()
679.end
680
681
682=item EXPORTALL(source, destination)
683
684Export all namespace entries from the default export namespace for source
685(source::EXPORT::ALL) to the destination namespace.
686
687=cut
688
689.sub 'EXPORTALL' :method
690    .param pmc source
691    .param pmc dest
692    .local pmc ns_iter, item, export_list
693
694    source = source['EXPORT']
695    unless source, no_namespace_error
696    source = source['ALL']
697    unless source, no_namespace_error
698
699    ns_iter = iter source
700    export_list = new 'ResizablePMCArray'
701  export_loop:
702    unless ns_iter, export_loop_end
703    item = shift ns_iter
704    push export_list, item
705    goto export_loop
706  export_loop_end:
707
708    source.'export_to'(dest,export_list)
709    .return ()
710
711  no_namespace_error:
712    $P0 = new 'Exception'
713    $P0 = 'Missing EXPORT::ALL NameSpace'
714    throw $P0
715    .return ()
716.end
717
718=item evalfiles(files [, args] [, "encoding" => encoding] [, "option" => value, ...])
719
720Compile and evaluate a file or files.  The C<files> argument may
721be either a single filename or an array of files to be processed
722as a single compilation unit.  The C<encoding> option specifies
723the encoding to use when reading the files, and any remaining
724options are passed to the evaluator.
725
726=cut
727
728.sub 'evalfiles' :method
729    .param pmc files
730    .param pmc args            :slurpy
731    .param pmc adverbs         :slurpy :named
732
733    unless null adverbs goto have_adverbs
734    adverbs = new 'Hash'
735  have_adverbs:
736    .local string target
737    target = adverbs['target']
738    target = downcase target
739    .local string encoding
740    encoding = adverbs['encoding']
741    $I0 = does files, 'array'
742    if $I0 goto have_files_array
743    $P0 = new 'ResizablePMCArray'
744    push $P0, files
745    files = $P0
746  have_files_array:
747    .local string code
748    code = ''
749    .local pmc it
750    it = iter files
751  iter_loop:
752    unless it goto iter_end
753    .local string iname
754    .local pmc ifh
755    iname = shift it
756    ifh = new 'FileHandle'
757    unless encoding == 'utf8' goto iter_loop_1
758    ifh.'encoding'(encoding)
759  iter_loop_1:
760    $S0 = ifh.'readall'(iname)
761    code = concat code, $S0
762    ifh.'close'()
763    goto iter_loop
764  iter_end:
765    $S0 = join ' ', files
766    $P1 = box $S0
767    .lex '$?FILES', $P1
768    $P0 = self.'eval'(code, args :flat, adverbs :flat :named)
769    if target == '' goto end
770    if target == 'pir' goto end
771    self.'dumper'($P0, target, adverbs :flat :named)
772  end:
773    .return ($P0)
774
775  err_infile:
776    .tailcall self.'panic'('Error: file cannot be read: ', iname)
777.end
778
779
780=item process_args(PMC args)
781
782Performs option processing of command-line args
783
784=cut
785
786.sub 'process_args' :method
787    .param pmc args
788
789    load_bytecode 'Getopt/Obj.pbc'
790
791    .local string arg0
792    arg0 = shift args
793    self.'compiler_progname'(arg0)
794    .local pmc getopts
795    getopts = new ['Getopt';'Obj']
796    getopts.'notOptStop'(1)
797    $P0 = getattribute self, '@cmdoptions'
798    .local pmc it
799    it = iter $P0
800  getopts_loop:
801    unless it goto getopts_end
802    $S0 = shift it
803    push getopts, $S0
804    goto getopts_loop
805  getopts_end:
806    .tailcall getopts.'get_options'(args)
807.end
808
809
810=item command_line(PMC args)
811
812Generic method for compilers invoked from a shell command line.
813
814=cut
815
816.include 'except_severity.pasm'
817.sub 'command_line' :method
818    .param pmc args
819    .param pmc adverbs         :slurpy :named
820
821    ## this bizarre piece of code causes the compiler to
822    ## immediately abort if it looks like it's being run
823    ## from Perl's Test::Harness.  (Test::Harness versions 2.64
824    ## and earlier have a hardwired commandline option that is
825    ## always passed to an initial run of the interpreter binary,
826    ## whether you want it or not.)  We expect to remove this
827    ## check eventually (or make it a lot smarter than it is here).
828    $S0 = args[2]
829    $I0 = index $S0, '@INC'
830    if $I0 < 0 goto not_harness
831    exit 0
832  not_harness:
833
834    load_bytecode 'dumper.pbc'
835    load_bytecode 'PGE/Dumper.pbc'
836
837    ##  get the name of the program
838    .local string arg0
839    arg0 = args[0]
840
841    ##   perform option processing of command-line args
842    .local pmc opts
843    opts = self.'process_args'(args)
844
845    ##   merge command-line args with defaults passed in from caller
846    .local pmc it
847    it = iter opts
848  mergeopts_loop:
849    unless it goto mergeopts_end
850    $S0 = shift it
851    $P0 = opts[$S0]
852    adverbs[$S0] = $P0
853    goto mergeopts_loop
854  mergeopts_end:
855
856    $I0 = adverbs['help']
857    if $I0 goto usage
858
859    $I0 = adverbs['version']
860    if $I0 goto version
861
862    .local string target
863    target = adverbs['target']
864    target = downcase target
865
866    .local int can_backtrace, ll_backtrace
867    can_backtrace = can self, 'backtrace'
868    unless can_backtrace goto no_push_eh
869    ll_backtrace = adverbs['ll-backtrace']
870    if ll_backtrace goto no_push_eh
871    push_eh uncaught_exception
872  no_push_eh:
873
874    $S0 = adverbs['e']
875    $I0 = exists adverbs['e']
876    if $I0 goto eval_line
877    .local pmc result
878    result = box ''
879    unless args goto interactive
880    $I0 = adverbs['combine']
881    if $I0 goto combine
882    $S0 = args[0]
883    result = self.'evalfiles'($S0, args :flat, adverbs :flat :named)
884    goto save_output
885  combine:
886    result = self.'evalfiles'(args, adverbs :flat :named)
887    goto save_output
888  interactive:
889    self.'interactive'(args :flat, adverbs :flat :named)
890    goto save_output
891  eval_line:
892    result = self.'eval'($S0, '-e', args :flat, adverbs :flat :named)
893    if target == '' goto save_output
894    if target == 'pir' goto save_output
895    '_dumper'(result, target)
896
897  save_output:
898    unless can_backtrace goto no_pop_eh
899    pop_eh
900  no_pop_eh:
901    if null result goto end
902    $I0 = defined result
903    unless $I0 goto end
904    if target != 'pir' goto end
905    .local string output
906    .local pmc ofh
907    $P0 = getinterp
908    ofh = $P0.'stdout_handle'()
909    output = adverbs['output']
910    if output == '' goto save_output_1
911    if output == '-' goto save_output_1
912    ofh = new ['FileHandle']
913    ofh.'open'(output, 'w')
914    unless ofh goto err_output
915  save_output_1:
916    print ofh, result
917    ofh.'close'()
918  end:
919    .return ()
920
921  err_output:
922    .tailcall self.'panic'('Error: file cannot be written: ', output)
923  usage:
924    self.'usage'(arg0)
925    goto end
926  version:
927    self.'version'()
928    goto end
929
930    # If we get an uncaught exception in the program and the HLL provides
931    # a backtrace method, we end up here. We pass it the exception object
932    # so it can render a backtrace, unless the severity is exit or warning
933    # in which case it needs special handling.
934  uncaught_exception:
935    .get_results ($P0)
936    pop_eh
937    $P1 = getinterp
938    $P1 = $P1.'stderr_handle'()
939    $I0 = $P0['severity']
940    if $I0 == .EXCEPT_EXIT goto do_exit
941    $S0 = self.'backtrace'($P0)
942    print $P1, $S0
943    if $I0 <= .EXCEPT_WARNING goto do_warning
944    exit 1
945  do_exit:
946    $I0 = $P0['exit_code']
947    exit $I0
948  do_warning:
949    $P0 = $P0["resume"]
950    push_eh uncaught_exception # Otherwise we get errors about no handler to delete
951    $P0()
952.end
953
954
955=item parse_name(string name)
956
957Split C<name> into its component namespace parts, as
958required by pdd21.  The default is simply to split the name
959based on double-colon separators.
960
961=cut
962
963.sub 'parse_name' :method
964    .param string name
965    $P0 = split '::', name
966    .return ($P0)
967.end
968
969=item lineof(target, pos [, cache :named('cache')])
970
971Return the line number of offset C<pos> within C<target>.  The return
972value uses zero for the first line.  If C<cache> is true, then
973memoize the line offsets as a C<!lineof> property on C<target>.
974
975=cut
976
977.sub 'lineof' :method
978    .param pmc target
979    .param int pos
980    .param int cache           :optional :named('cache')
981    .local pmc linepos
982
983    # If we've previously cached C<linepos> for target, we use it.
984    unless cache goto linepos_build
985    linepos = getprop target, '!linepos'
986    unless null linepos goto linepos_done
987
988    # calculate a new linepos array.
989  linepos_build:
990    linepos = new ['ResizableIntegerArray']
991    unless cache goto linepos_build_1
992    setprop target, '!linepos', linepos
993  linepos_build_1:
994    .local string s
995    .local int jpos, eos
996    s = target
997    eos = length s
998    jpos = 0
999    # Search for all of the newline markers in C<target>.  When we
1000    # find one, mark the ending offset of the line in C<linepos>.
1001  linepos_loop:
1002    jpos = find_cclass .CCLASS_NEWLINE, s, jpos, eos
1003    unless jpos < eos goto linepos_done_1
1004    $I0 = ord s, jpos
1005    inc jpos
1006    push linepos, jpos
1007    # Treat \r\n as a single logical newline.
1008    if $I0 != 13 goto linepos_loop
1009    $I0 = ord s, jpos
1010    if $I0 != 10 goto linepos_loop
1011    inc jpos
1012    goto linepos_loop
1013  linepos_done_1:
1014  linepos_done:
1015
1016    # We have C<linepos>, so now we (binary) search the array
1017    # for the largest element that is not greater than C<pos>.
1018    .local int lo, hi, line
1019    lo = 0
1020    hi = elements linepos
1021  binary_loop:
1022    if lo >= hi goto binary_done
1023    line = lo + hi
1024    line = line / 2
1025    $I0 = linepos[line]
1026    if $I0 > pos goto binary_hi
1027    lo = line + 1
1028    goto binary_loop
1029  binary_hi:
1030    hi = line
1031    goto binary_loop
1032  binary_done:
1033    .return (lo)
1034.end
1035
1036
1037=item dumper(obj, name, options)
1038
1039Dump C<obj> with C<name> according to C<options>.
1040
1041=cut
1042
1043.sub 'dumper' :method
1044    .param pmc obj
1045    .param string name
1046    .param pmc options         :slurpy :named
1047
1048    $S0 = options['dumper']
1049    if $S0 goto load_dumper
1050    .tailcall '_dumper'(obj, name)
1051
1052  load_dumper:
1053    load_bytecode 'PCT/Dumper.pbc'
1054    $S0 = downcase $S0
1055    $P0 = get_hll_global ['PCT';'Dumper'], $S0
1056    .tailcall $P0(obj, name)
1057.end
1058
1059
1060=item usage()
1061
1062A usage method.
1063
1064=cut
1065
1066.sub 'usage' :method
1067    .param string name     :optional
1068    .param int    has_name :opt_flag
1069
1070    unless has_name goto no_name
1071    say name
1072  no_name:
1073    $P0 = getattribute self, '$usage'
1074    say $P0
1075    exit 0
1076.end
1077
1078
1079=item version()
1080
1081Display compiler version information.
1082
1083=cut
1084
1085.sub 'version' :method
1086    $P0 = getattribute self, '$version'
1087    say $P0
1088    exit 0
1089.end
1090
1091
1092=back
1093
1094=head1 AUTHOR
1095
1096Patrick R. Michaud <pmichaud@pobox.com>
1097
1098=cut
1099
1100
1101# Local Variables:
1102#   mode: pir
1103#   fill-column: 100
1104# End:
1105# vim: expandtab shiftwidth=4 ft=pir:
1106