1 # Copyright (C) 2006-2010, Parrot Foundation.
6 PCT::HLLCompiler - base class for compiler objects
10 This file implements a C<HLLCompiler> class of objects used for
11 creating HLL compilers. It provides the standard methods required
12 for all compilers, as well as some standard scaffolding for
13 running compilers from a command line.
17 .sub 'onload' :anon :load :init
18 load_bytecode 'P6object.pbc'
19 load_bytecode 'Parrot/Exception.pbc'
20 $P0 = new 'P6metaclass'
21 $S0 = '@stages $parsegrammar $parseactions $astgrammar $commandline_banner $commandline_prompt @cmdoptions $usage $version'
22 $P0.'new_class'('PCT::HLLCompiler', 'attr'=>$S0)
25 .namespace [ 'PCT';'HLLCompiler' ]
27 .include 'cclass.pasm'
29 .include 'iglobals.pasm'
31 .sub 'init' :vtable :method
32 $P0 = split ' ', 'parse past post pir evalpmc'
33 setattribute self, '@stages', $P0
35 $P0 = split ' ', 'e=s help|h target=s dumper=s trace|t=s encoding=s output|o=s combine version|v stagestats'
36 setattribute self, '@cmdoptions', $P0
39 This compiler is based on PCT::HLLCompiler.
47 unless it goto options_end
54 setattribute self, '$usage', $P1
59 $P0 = $P0[.IGLOBALS_CONFIG_HASH]
60 $S0 = $P0['revision'] # also $I0 = P0['installed'] could be used
63 $P2 = box 'This compiler is built with the Parrot Compiler Toolkit, parrot '
64 if $S0 goto _revision_lab
73 setattribute self, '$version', $P2
81 =item attr(string attrname, pmc value, int has_value)
83 Helper method for accessors -- gets/sets an attribute given
84 by C<attrname> based on C<has_value>.
89 .param string attrname
92 if has_value goto set_value
93 value = getattribute self, attrname
94 unless null value goto end
98 setattribute self, attrname, value
104 =item panic(message :slurpy)
106 Helper method to throw an exception (with a message).
111 .param pmc args :slurpy
117 =item language(string name)
119 Register this object as the compiler for C<name> using the
124 .sub 'language' :method
130 =item stages([stages])
132 Accessor for the C<stages> attribute.
134 =item parsegrammar([string grammar])
136 Accessor for the C<parsegrammar> attribute.
138 =item parseactions([actions])
140 Accessor for the C<parseactions> attribute.
142 =item astgrammar([grammar])
144 Accessor for the C<astgrammar> attribute.
146 =item commandline_banner([string value])
148 Set the command-line banner for this compiler to C<value>.
149 The banner is displayed at the beginning of interactive mode.
151 =item commandline_prompt([string value])
153 Set the command-line prompt for this compiler to C<value>.
154 The prompt is displayed in interactive mode at each point where
155 the compiler is ready for code to be compiled and executed.
159 .sub 'stages' :method
160 .param pmc value :optional
161 .param int has_value :opt_flag
162 .tailcall self.'attr'('@stages', value, has_value)
165 .sub 'parsegrammar' :method
166 .param pmc value :optional
167 .param int has_value :opt_flag
168 .tailcall self.'attr'('$parsegrammar', value, has_value)
171 .sub 'parseactions' :method
172 .param pmc value :optional
173 .param int has_value :opt_flag
174 .tailcall self.'attr'('$parseactions', value, has_value)
177 .sub 'astgrammar' :method
178 .param pmc value :optional
179 .param int has_value :opt_flag
180 .tailcall self.'attr'('$astgrammar', value, has_value)
183 .sub 'commandline_banner' :method
184 .param string value :optional
185 .param int has_value :opt_flag
186 .tailcall self.'attr'('$commandline_banner', value, has_value)
189 .sub 'commandline_prompt' :method
190 .param string value :optional
191 .param int has_value :opt_flag
192 .tailcall self.'attr'('$commandline_prompt', value, has_value)
195 =item removestage(string stagename)
197 Delete a stage from the compilation process queue.
201 .sub 'removestage' :method
202 .param string stagename
204 .local pmc stages, it, newstages
205 stages = getattribute self, '@stages'
206 newstages = new 'ResizableStringArray'
210 unless it goto iter_end
213 if current == stagename goto iter_loop
214 push newstages, current
217 setattribute self, '@stages', newstages
220 =item addstage(string stagename [, "option" => value, ... ])
222 Add a stage to the compilation process queue. Takes either a "before" or
223 "after" named argument, which gives the relative ordering of the stage
224 to be added. If "before" and "after" aren't specified, the new stage is
225 inserted at the end of the queue.
227 It's possible to add multiple stages of the same name: for example, you
228 might repeat a stage like "optimize_tree" or "display_benchmarks" after
229 each transformation. If you have multiple stages of the same name, and
230 add a new stage before or after that repeated stage, the new stage will
231 be added at every instance of the repeated stage.
235 .sub 'addstage' :method
236 .param string stagename
237 .param pmc adverbs :slurpy :named
239 .local string position, target
241 stages = getattribute self, '@stages'
243 $I0 = exists adverbs['before']
244 unless $I0 goto next_test
246 target = adverbs['before']
247 goto positional_insert
250 $I0 = exists adverbs['after']
251 unless $I0 goto simple_insert
253 target = adverbs['after']
256 .local pmc it, newstages
257 newstages = new 'ResizableStringArray'
261 unless it goto iter_end
264 unless current == target goto no_insert_before
265 unless position == 'before' goto no_insert_before
266 push newstages, stagename
269 push newstages, current
271 unless current == target goto no_insert_after
272 unless position == 'after' goto no_insert_after
273 push newstages, stagename
278 setattribute self, '@stages', newstages
282 push stages, stagename
287 =item compile(pmc code [, "option" => value, ... ])
289 Compile C<source> (possibly modified by any provided options)
290 by iterating through any stages identified for this compiler.
291 If a C<target> option is provided, then halt the iteration
292 when the stage corresponding to target has been reached.
296 .sub 'compile' :method
298 .param pmc adverbs :slurpy :named
300 .local pmc compiling, options
301 compiling = new ['Hash']
302 .lex '%*COMPILING', compiling
303 compiling['%?OPTIONS'] = adverbs
306 target = adverbs['target']
307 target = downcase target
309 .local int stagestats
310 stagestats = adverbs['stagestats']
312 .local pmc stages, result, it
314 stages = getattribute self, '@stages'
316 if stagestats goto stagestats_loop
319 unless it goto have_result
320 .local string stagename
322 result = self.stagename(result, adverbs :flat :named)
323 if target == stagename goto have_result
327 unless it goto have_result
330 result = self.stagename(result, adverbs :flat :named)
334 $P1 = $P0.'stdhandle'(.PIO_STDERR_FILENO)
335 $P1.'print'("Stage '")
336 $P1.'print'(stagename)
338 $P2 = new ['ResizablePMCArray']
340 $S0 = sprintf "%.3f", $P2
342 $P1.'print'(" sec\n")
343 if target == stagename goto have_result
351 =item parse(source [, "option" => value, ...])
353 Parse C<source> using the compiler's C<parsegrammar> according
354 to any options and return the resulting parse tree.
360 .param pmc adverbs :slurpy :named
361 .local pmc parsegrammar, top
364 tcode = adverbs['transcode']
365 unless tcode goto transcode_done
367 $P0 = split ' ', tcode
370 unless tcode_it goto transcode_done
371 tcode = shift tcode_it
373 $I0 = find_charset tcode
375 $S0 = trans_charset $S0, $I0
382 $I0 = find_encoding tcode
384 $S0 = trans_encoding $S0, $I0
394 target = adverbs['target']
395 target = downcase target
397 parsegrammar = self.'parsegrammar'()
398 $I0 = can parsegrammar, 'TOP'
399 unless $I0 goto parsegrammar_string
400 top = find_method parsegrammar, 'TOP'
403 $S0 = typeof parsegrammar
404 eq $S0, 'NameSpace', parsegrammar_ns
405 $P0 = self.'parse_name'(parsegrammar)
407 $P1 = get_hll_global $P0, $S0
409 unless $I0 goto parsegrammar_ns_string
410 top = find_method $P1, 'TOP'
412 parsegrammar_ns_string:
413 $P0 = self.'parse_name'(parsegrammar)
414 top = get_hll_global $P0, 'TOP'
415 unless null top goto have_top
418 top = parsegrammar['TOP']
419 unless null top goto have_top
421 self.'panic'('Cannot find TOP regex in ', parsegrammar)
423 .local pmc parseactions, action
425 if target == 'parse' goto have_action
426 parseactions = self.'parseactions'()
427 $I0 = isa parseactions, ['Undef']
428 if $I0 goto have_action
429 ## if parseactions is a protoobject, use it directly
430 $I0 = isa parseactions, 'P6protoobject'
431 if $I0 goto action_exact
432 ## if parseactions is a Class or array, make action directly from that
433 $I0 = isa parseactions, 'Class'
434 if $I0 goto action_make
435 $I0 = isa parseactions, 'NameSpace'
436 if $I0 goto action_namespace
437 $I0 = does parseactions, 'array'
438 if $I0 goto action_make
439 ## if parseactions is not a String, use it directly.
440 $I0 = isa parseactions, 'String'
441 if $I0 goto action_string
443 action = parseactions
446 $P0 = get_class parseactions
450 ## Try the string itself, if that fails try splitting on '::'
451 $P0 = get_class parseactions
452 unless null $P0 goto action_make
454 parseactions = split '::', $S0
455 push_eh err_bad_parseactions
456 $P0 = get_class parseactions
457 if null $P0 goto err_bad_parseactions
460 action = new parseactions
463 match = top(source, 'grammar' => parsegrammar, 'action' => action)
464 unless match goto err_failedparse
468 self.'panic'('Missing parsegrammar in compiler')
471 self.'panic'('Failed to parse source')
473 err_bad_parseactions:
475 $P0 = self.'parseactions'()
476 self.'panic'('Unable to find action grammar ', $P0)
481 =item past(source [, "option" => value, ...])
483 Transform C<source> into PAST using the compiler's
484 C<astgrammar> according to any options, and return the
491 .param pmc adverbs :slurpy :named
494 .local pmc astgrammar_name
495 astgrammar_name = self.'astgrammar'()
496 $S0 = typeof astgrammar_name
497 eq $S0, 'NameSpace', astgrammar_ns
498 unless astgrammar_name goto compile_match
500 .local pmc astgrammar_namelist
501 .local pmc astgrammar, astbuilder
502 astgrammar_namelist = self.'parse_name'(astgrammar_name)
503 unless astgrammar_namelist goto err_past
504 astgrammar = new astgrammar_namelist
505 astbuilder = astgrammar.'apply'(source)
506 .tailcall astbuilder.'get'('past')
508 $P0 = get_class astgrammar_name
510 astbuilder = astgrammar.'apply'(source)
511 .tailcall astbuilder.'get'('past')
518 $I0 = isa ast, ['PAST';'Node']
519 unless $I0 goto err_past
525 .tailcall self.'panic'('Unable to obtain PAST from ', $S0)
529 =item post(source [, adverbs :slurpy :named])
531 Transform PAST C<source> into POST.
537 .param pmc adverbs :slurpy :named
539 .tailcall $P0.'to_post'(source, adverbs :flat :named)
545 .param pmc adverbs :slurpy :named
548 .tailcall $P0.'to_pir'(source, adverbs :flat :named)
552 .sub 'evalpmc' :method
554 .param pmc adverbs :slurpy :named
563 =item eval(code [, "option" => value, ...])
565 Compile and execute the given C<code> taking into account any
572 .param pmc args :slurpy
573 .param pmc adverbs :slurpy :named
575 unless null args goto have_args
576 args = new 'ResizablePMCArray'
578 unless null adverbs goto have_adverbs
582 $P0 = self.'compile'(code, adverbs :flat :named)
583 $I0 = isa $P0, 'String'
586 target = adverbs['target']
587 if target != '' goto end
588 .local pmc outer_ctx, outer
589 outer_ctx = adverbs['outer_ctx']
590 if null outer_ctx goto outer_done
591 outer = outer_ctx['current_sub']
593 $P1.'set_outer'(outer)
595 $I0 = adverbs['trace']
597 $P0 = $P0(args :flat)
604 =item interactive(["encoding" => encoding] [, "option" => value, ...])
606 Runs an interactive compilation session -- reads lines of input
607 from the standard input and evaluates each. The C<encoding> option
608 specifies the encoding to use for the input (e.g., "utf8").
612 .sub 'interactive' :method
613 .param pmc adverbs :slurpy :named
614 .local string target, encoding
615 target = adverbs['target']
616 target = downcase target
618 # on startup show the welcome message
619 $P0 = self.'commandline_banner'()
621 $P2 = $P1.'stdhandle'(.PIO_STDERR_FILENO)
625 .local int has_readline
627 stdin = $P0.'stdhandle'(.PIO_STDIN_FILENO)
628 encoding = adverbs['encoding']
629 if encoding == 'fixed_8' goto interactive_loop
630 unless encoding goto interactive_loop
631 stdin.'encoding'(encoding)
634 unless stdin goto interactive_end
638 $P0 = self.'commandline_prompt'()
640 unless $I0 goto have_prompt
644 ## display a prompt ourselves if readline isn't present
646 code = stdin.'readline_interactive'(prompt)
647 if null code goto interactive_end
648 unless code goto interactive_loop
650 push_eh interactive_trap
651 $P0 = self.'eval'(code, adverbs :flat :named)
653 if null $P0 goto interactive_loop
654 unless target goto interactive_loop
655 if target == 'pir' goto target_pir
656 self.'dumper'($P0, target, adverbs :flat :named)
657 goto interactive_loop
660 goto interactive_loop
665 if $S0 == '' goto have_newline
666 $S1 = substr $S0, -1, 1
667 $I0 = is_cclass .CCLASS_NEWLINE, $S1, 0
668 if $I0 goto have_newline
669 $S0 = concat $S0, "\n"
672 goto interactive_loop
678 =item EXPORTALL(source, destination)
680 Export all namespace entries from the default export namespace for source
681 (source::EXPORT::ALL) to the destination namespace.
685 .sub 'EXPORTALL' :method
688 .local pmc ns_iter, item, export_list
690 source = source['EXPORT']
691 unless source, no_namespace_error
692 source = source['ALL']
693 unless source, no_namespace_error
695 ns_iter = iter source
696 export_list = new 'ResizablePMCArray'
698 unless ns_iter, export_loop_end
700 push export_list, item
704 source.'export_to'(dest,export_list)
708 $P0 = new 'Exception'
709 $P0 = 'Missing EXPORT::ALL NameSpace'
714 =item evalfiles(files [, args] [, "encoding" => encoding] [, "option" => value, ...])
716 Compile and evaluate a file or files. The C<files> argument may
717 be either a single filename or an array of files to be processed
718 as a single compilation unit. The C<encoding> option specifies
719 the encoding to use when reading the files, and any remaining
720 options are passed to the evaluator.
724 .sub 'evalfiles' :method
726 .param pmc args :slurpy
727 .param pmc adverbs :slurpy :named
729 unless null adverbs goto have_adverbs
733 target = adverbs['target']
734 target = downcase target
735 .local string encoding
736 encoding = adverbs['encoding']
737 $I0 = does files, 'array'
738 if $I0 goto have_files_array
739 $P0 = new 'ResizablePMCArray'
748 unless it goto iter_end
752 ifh = new 'FileHandle'
753 unless encoding == 'utf8' goto iter_loop_1
754 ifh.'encoding'(encoding)
756 $S0 = ifh.'readall'(iname)
761 $S0 = join ' ', files
764 $P0 = self.'eval'(code, args :flat, adverbs :flat :named)
765 if target == '' goto end
766 if target == 'pir' goto end
767 self.'dumper'($P0, target, adverbs :flat :named)
772 .tailcall self.'panic'('Error: file cannot be read: ', iname)
776 =item process_args(PMC args)
778 Performs option processing of command-line args
782 .sub 'process_args' :method
785 load_bytecode 'Getopt/Obj.pbc'
790 getopts = new ['Getopt';'Obj']
791 getopts.'notOptStop'(1)
792 $P0 = getattribute self, '@cmdoptions'
796 unless it goto getopts_end
801 .tailcall getopts.'get_options'(args)
805 =item command_line(PMC args)
807 Generic method for compilers invoked from a shell command line.
811 .include 'except_severity.pasm'
812 .sub 'command_line' :method
814 .param pmc adverbs :slurpy :named
816 ## this bizarre piece of code causes the compiler to
817 ## immediately abort if it looks like it's being run
818 ## from Perl's Test::Harness. (Test::Harness versions 2.64
819 ## and earlier have a hardwired commandline option that is
820 ## always passed to an initial run of the interpreter binary,
821 ## whether you want it or not.) We expect to remove this
822 ## check eventually (or make it a lot smarter than it is here).
824 $I0 = index $S0, '@INC'
825 if $I0 < 0 goto not_harness
829 load_bytecode 'dumper.pbc'
830 load_bytecode 'PGE/Dumper.pbc'
832 ## get the name of the program
836 ## perform option processing of command-line args
838 opts = self.'process_args'(args)
840 ## merge command-line args with defaults passed in from caller
844 unless it goto mergeopts_end
851 $I0 = adverbs['help']
854 $I0 = adverbs['version']
857 .local int can_backtrace
858 can_backtrace = can self, 'backtrace'
859 unless can_backtrace goto no_push_eh
860 push_eh uncaught_exception
864 $I0 = exists adverbs['e']
865 if $I0 goto eval_line
868 unless args goto interactive
869 $I0 = adverbs['combine']
872 result = self.'evalfiles'($S0, args :flat, adverbs :flat :named)
875 result = self.'evalfiles'(args, adverbs :flat :named)
878 self.'interactive'(args :flat, adverbs :flat :named)
881 result = self.'eval'($S0, '-e', args :flat, adverbs :flat :named)
884 unless can_backtrace goto no_pop_eh
887 if null result goto end
891 target = adverbs['target']
892 target = downcase target
893 if target != 'pir' goto end
897 ofh = $P0.'stdhandle'(.PIO_STDOUT_FILENO)
898 output = adverbs['output']
899 if output == '' goto save_output_1
900 if output == '-' goto save_output_1
901 ofh = new ['FileHandle']
902 ofh.'open'(output, 'w')
903 unless ofh goto err_output
911 .tailcall self.'panic'('Error: file cannot be written: ', output)
919 # If we get an uncaught exception in the program and the HLL provides
920 # a backtrace method, we end up here. We pass it the exception object
921 # so it can render a backtrace, unless the severity is exit or warning
922 # in which case it needs special handling.
927 $P1 = $P1.'stdhandle'(.PIO_STDERR_FILENO)
928 $I0 = $P0['severity']
929 if $I0 == .EXCEPT_EXIT goto do_exit
930 $S0 = self.'backtrace'($P0)
932 if $I0 <= .EXCEPT_WARNING goto do_warning
935 $I0 = $P0['exit_code']
939 push_eh uncaught_exception # Otherwise we get errors about no handler to delete
944 =item parse_name(string name)
946 Split C<name> into its component namespace parts, as
947 required by pdd21. The default is simply to split the name
948 based on double-colon separators.
952 .sub 'parse_name' :method
954 $P0 = split '::', name
958 =item lineof(target, pos [, cache :named('cache')])
960 Return the line number of offset C<pos> within C<target>. The return
961 value uses zero for the first line. If C<cache> is true, then
962 memoize the line offsets as a C<!lineof> property on C<target>.
966 .sub 'lineof' :method
969 .param int cache :optional :named('cache')
972 # If we've previously cached C<linepos> for target, we use it.
973 unless cache goto linepos_build
974 linepos = getprop '!linepos', target
975 unless null linepos goto linepos_done
977 # calculate a new linepos array.
979 linepos = new ['ResizableIntegerArray']
980 unless cache goto linepos_build_1
981 setprop target, '!linepos', linepos
988 # Search for all of the newline markers in C<target>. When we
989 # find one, mark the ending offset of the line in C<linepos>.
991 jpos = find_cclass .CCLASS_NEWLINE, s, jpos, eos
992 unless jpos < eos goto linepos_done
996 # Treat \r\n as a single logical newline.
997 if $I0 != 13 goto linepos_loop
999 if $I0 != 10 goto linepos_loop
1004 # We have C<linepos>, so now we search the array for the largest
1005 # element that is not greater than C<pos>. The index of that
1006 # element is the line number to be returned.
1007 # (Potential optimization: use a binary search.)
1008 .local int line, count
1009 count = elements linepos
1012 if line >= count goto line_done
1014 if $I0 > pos goto line_done
1022 =item dumper(obj, name, options)
1024 Dump C<obj> with C<name> according to C<options>.
1028 .sub 'dumper' :method
1031 .param pmc options :slurpy :named
1033 $S0 = options['dumper']
1034 if $S0 goto load_dumper
1035 .tailcall '_dumper'(obj, name)
1038 load_bytecode 'PCT/Dumper.pbc'
1040 $P0 = get_hll_global ['PCT';'Dumper'], $S0
1041 .tailcall $P0(obj, name)
1051 .sub 'usage' :method
1052 .param string name :optional
1053 .param int has_name :opt_flag
1055 unless has_name goto no_name
1058 $P0 = getattribute self, '$usage'
1066 Display compiler version information.
1070 .sub 'version' :method
1071 $P0 = getattribute self, '$version'
1081 Patrick R. Michaud <pmichaud@pobox.com>
1090 # vim: expandtab shiftwidth=4 ft=pir: