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