1#! nqp 2# Copyright (C) 2009-2010, Parrot Foundation. 3 4class Ops::Compiler::Actions is HLL::Actions; 5 6our $OP; 7our $OPLIB; 8 9INIT { 10 pir::load_bytecode("nqp-setting.pbc"); 11 $OPLIB := 0; 12} 13 14method TOP($/) { 15 make $<body>.ast; 16} 17 18method body($/) { 19 my $past := PAST::Stmts.new( 20 :node($/) 21 ); 22 23 $past<preamble> := PAST::Stmts.new( 24 :node($/) 25 ); 26 $past<ops> := PAST::Stmts.new( 27 :node($/) 28 ); 29 30 for $<preamble> { 31 $past<preamble>.push($_<preamble_guts>); 32 } 33 34 for $<op> { 35 my $ops := $_.ast; 36 my $op_skip_table; 37 if $OPLIB { 38 $op_skip_table := $OPLIB.op_skip_table; 39 } 40 for @($ops) -> $op { 41 if $OPLIB && !$op_skip_table.exists($op.full_name) || !$OPLIB { 42 $past<ops>.push($op); 43 } 44 } 45 } 46 47 make $past; 48} 49 50method preamble($/) { 51 make PAST::Op.new( 52 :node($/), 53 :pasttype('preamble'), 54 ~$<preamble_guts> 55 ); 56} 57 58method op ($/, $key?) { 59 60 if $key eq 'start' { 61 # Handling flags. 62 my %flags := hash(); 63 for $<op_flag> { 64 %flags{~$_<identifier>} := 1; 65 } 66 67 my @args := @($<signature>.ast); 68 my @norm_args := normalize_args(@args); 69 70 $OP := Ops::Op.new( 71 :name(~$<op_name>), 72 ); 73 74 if ~$<op_name> eq 'runinterp' { 75 $OP.add_jump('PARROT_JUMP_RELATIVE'); 76 } 77 78 $OP<flags> := %flags; 79 $OP<args> := @args; 80 $OP<type> := ~$<op_type>; 81 $OP<normalized_args> := @norm_args; 82 } 83 else { 84 # Handle op body 85 $OP.push($<op_body>.ast); 86 87 if !$OP<flags><flow> { 88 $OP[0].push(self.make_write_barrier) if $OP.need_write_barrier; 89 90 my $goto_next := PAST::Op.new( 91 :pasttype('macro'), 92 :name('goto_offset'), 93 self.opsize, 94 ); 95 96 $OP[0].push($goto_next); 97 } 98 99 my $past := PAST::Stmts.new( 100 :node($/) 101 ); 102 103 # We have to clone @norm_args. Otherwise it will be destroyed... 104 my @variants := expand_args(pir::clone__PP($OP<normalized_args>)); 105 if @variants { 106 for @variants { 107 my $new_op := pir::clone__PP($OP); 108 $new_op<arg_types> := $_; 109 $past.push($new_op); 110 } 111 } 112 else { 113 $past.push($OP); 114 } 115 116 make $past; 117 } 118} 119 120# Normalize args 121# For each arg produce LoL of all available variants 122# E.g. "in" will produce "i" and "ic" variants 123# 124# type one of <i p s n> 125# direction one of <i o io> 126# is_label one of <0 1> 127 128sub normalize_args(@args) { 129 my @result; 130 for @args -> $arg { 131 my $res := PAST::Var.new( 132 :isdecl(1) 133 ); 134 135 if $arg<type> eq 'LABEL' { 136 $res<type> := 'i'; 137 $res<is_label> := 1; 138 } 139 else { 140 $res<is_label> := 0; 141 } 142 143 if $arg<type> eq 'INTKEY' { 144 $res<type> := 'ki'; 145 } 146 elsif $arg<type> ne 'LABEL' { 147 $res<type> := lc(substr($arg<type>, 0, 1)); 148 } 149 150 my $use := $arg<direction>; 151 152 if $use eq 'in' { 153 $res<variant> := $res<type> ~ "c"; 154 $res<direction> := 'i'; 155 } 156 elsif $use eq 'invar' { 157 $res<direction> := 'i'; 158 } 159 elsif $use eq 'inconst' { 160 $res<type> := $res<type> ~ "c"; 161 $res<direction> := 'i'; 162 } 163 elsif $use eq 'inout' { 164 $res<direction> := 'io'; 165 } 166 else { 167 $res<direction> := 'o'; 168 } 169 170 @result.push($res); 171 } 172 @result; 173} 174 175=begin 176 177=item C<expand_args(@args)> 178 179Given an argument list, returns a list of all the possible argument 180combinations. 181 182=end 183sub expand_args(@args) { 184 185 return list() unless @args; 186 187 my $arg := @args.shift; 188 189 my @var := list($arg<type>); 190 if $arg<variant> { 191 @var.push($arg<variant>); 192 } 193 194 my @list := expand_args(@args); 195 unless +@list { 196 return @var; 197 } 198 199 my @results; 200 for @list -> $l { 201 for @var -> $v { 202 # NQP can't handle it automagically. So wrap $l into list. 203 my @l := pir::does__IPS($l, 'array') ?? $l !! list($l); 204 @results.push(list($v, |@l)); 205 } 206 } 207 208 @results; 209} 210 211 212method signature($/) { 213 my $past := PAST::Stmts.new( 214 :node($/) 215 ); 216 217 for $<op_param> { 218 $past.push($_.ast); 219 } 220 221 make $past; 222} 223 224method op_param($/) { 225 my $past := PAST::Var.new( 226 :node($/), 227 :isdecl(1) 228 ); 229 230 # We have to store 2 "types". Just set 2 properties on Var for now 231 $past<direction> := ~$<op_param_direction>; 232 $past<type> := ~$<op_param_type>; 233 234 make $past; 235} 236 237method op_body($/) { 238 make $<blockoid>.ast; 239} 240 241method op_macro:sym<expr offset>($/) { 242 make PAST::Op.new( 243 :pasttype<macro>, 244 :name<expr_offset>, 245 $<arg>.ast, 246 ); 247} 248 249method op_macro:sym<goto offset>($/) { 250 $OP.add_jump('PARROT_JUMP_RELATIVE'); 251 252 my $past := PAST::Op.new( 253 :pasttype<macro>, 254 :name<goto_offset>, 255 $<arg>.ast, 256 ); 257 258 if $OP.need_write_barrier { 259 $past := PAST::Block.new( 260 self.make_write_barrier, 261 $past, 262 ); 263 } 264 265 make $past; 266} 267 268method op_macro:sym<expr address>($/) { 269 make PAST::Op.new( 270 :pasttype<macro>, 271 :name<expr_address>, 272 $<arg>.ast, 273 ); 274} 275 276method op_macro:sym<goto address>($/) { 277 my $past := PAST::Op.new( 278 :pasttype<macro>, 279 :name<goto_address>, 280 $<arg>.ast, 281 ); 282 283 if $OP.need_write_barrier { 284 $past := PAST::Block.new( 285 self.make_write_barrier, 286 $past, 287 ); 288 } 289 290 make $past; 291} 292 293method op_macro:sym<expr next>($/) { 294 make PAST::Op.new( 295 :pasttype<macro>, 296 :name<expr_offset>, 297 self.opsize, 298 ); 299} 300 301method op_macro:sym<goto next>($/) { 302 $OP.add_jump('PARROT_JUMP_RELATIVE'); 303 304 my $past := PAST::Op.new( 305 :pasttype<macro>, 306 :name<goto_offset>, 307 self.opsize, 308 ); 309 310 if $OP.need_write_barrier { 311 $past := PAST::Block.new( 312 self.make_write_barrier, 313 $past, 314 ); 315 } 316 317 make $past; 318} 319 320 321method op_macro:sym<restart next> ($/) { 322 #say('# op_macro'); 323 # restart NEXT() -> restart_offset(opsize()); goto_address(0) 324 my $past := PAST::Stmts.new( 325 PAST::Op.new( 326 :pasttype<macro>, 327 :name<restart_offset>, 328 self.opsize, 329 ), 330 PAST::Op.new( 331 :pasttype<macro>, 332 :name<goto_address>, 333 PAST::Val.new( 334 :value<0> 335 ) 336 ), 337 ); 338 339 $past.unshift(self.make_write_barrier) if $OP.need_write_barrier; 340 341 make $past; 342} 343 344method blockoid ($/) { 345 my $past := PAST::Block.new(:node($/)); 346 347 $past.push($_) for @($<mixed_content>.ast); 348 349 make $past; 350} 351 352method mixed_content ($/) { 353 my $past := PAST::Stmts.new(:node($/)); 354 355 @($_.ast).map(-> $_ { $past.push($_) }) for $<declarator>; 356 $past.push($_) for @($<statement_list>.ast); 357 358 make $past; 359} 360 361method declarator ($/) { 362 my $past := PAST::Stmts.new(:node($/)); 363 for $<declarator_name> { 364 my $decl := PAST::Var.new( 365 :node($_), 366 :isdecl(1), 367 :name(~$_<variable>), 368 :vivibase(~$<type_declarator>), 369 ); 370 371 $decl.viviself($_<EXPR>[0].ast) if $_<EXPR>[0]; 372 373 $decl<array_size> := ~$_<array_size><VALUE> if $_<array_size>; 374 $decl<pointer> := $_<pointer>.join(''); 375 $past.push($decl); 376 } 377 378 make $past; 379} 380 381method statement_list ($/) { 382 my $past := PAST::Stmts.new(:node($/)); 383 384 $past.push($_.ast) for $<labeled_statement>; 385 386 # Avoid wrapping single blockoid into Stmts. 387 make (+@($past) == 1) && ($past[0] ~~ PAST::Block) 388 ?? $past[0] 389 !! $past; 390} 391 392method labeled_statement ($/) { 393 # FIXME!!! 394 my $past := $<statement> 395 ?? $<statement>.ast 396 !! PAST::Op.new(); 397 398 # FIXME!!! We need some semantics here. 399 $past<label> := ~$<label> if $<label>; 400 401 make $past; 402} 403 404method statement ($/) { 405 my $past; 406 407 if $<statement_control> { 408 $past := $<statement_control>.ast; 409 } 410 elsif $<blockoid> { 411 $past := $<blockoid>.ast; 412 } 413 elsif $<EXPR> { 414 $past := $<EXPR>.ast; 415 } 416 elsif $<c_macro> { 417 $past := $<c_macro>.ast; 418 } 419 else { 420 $/.CURSOR.panic("Unknown content in statement"); 421 } 422 423 make $past; 424} 425 426method c_macro:sym<define> ($/) { 427 my $past := PAST::Op.new( 428 :pasttype<macro_define>, 429 $<name>, 430 ); 431 432 $past<macro_args> := $<c_macro_args>[0].ast if $<c_macro_args>; 433 $past<body> := $<body>[0].ast if $<body>; 434 435 make $past; 436} 437 438method c_macro:sym<if> ($/) { 439 my $past := PAST::Op.new( 440 :pasttype<macro_if>, 441 442 ~$<condition>, # FIXME! We have to parse condition somehow. 443 $<then>.ast, 444 ); 445 446 $past.push($<else>[0].ast) if $<else>; 447 448 make $past; 449} 450 451method c_macro:sym<ifdef> ($/) { 452 my $past := PAST::Op.new( 453 :pasttype<macro_if>, 454 455 'defined(' ~ ~$<name> ~ ')', # FIXME! We have to parse condition somehow. 456 $<then>.ast, 457 ); 458 459 $past.push($<else>[0].ast) if $<else>; 460 461 make $past; 462} 463 464method term:sym<concatenate_strings> ($/) { 465 make ~$<identifier> ~ ' ' ~ ~$<quote>; 466} 467 468method term:sym<identifier> ($/) { 469 # XXX Type vs Variable 470 make PAST::Var.new( 471 :name(~$/), 472 ); 473} 474 475method term:sym<call> ($/) { 476 my $past := PAST::Op.new( 477 :pasttype('call'), 478 :name(~$<identifier>), 479 ); 480 481 if ($<arglist><arg>) { 482 $past.push($_.ast) for $<arglist><arg>; 483 } 484 485 make $past; 486} 487 488method arg ($/) { 489 make $<type_declarator> 490 ?? ~$<type_declarator> 491 !! $<EXPR>.ast; 492} 493 494method term:sym<reg> ($/) { 495 make PAST::Var.new( 496 :name(+$<num>), 497 :node($/), 498 :scope('register'), # Special scope 499 ); 500} 501 502method term:sym<macro> ($/) { 503 make $<op_macro>.ast; 504} 505 506method term:sym<int> ($/) { 507 # TODO Handle type 508 make PAST::Val.new( 509 :value(~$/), 510 :returns<int> 511 ); 512} 513 514method term:sym<str> ($/) { 515 make PAST::Val.new( 516 :value(~$<quote>), 517 :returns<string> 518 ); 519} 520 521method term:sym<float_constant_long> ($/) { # longer to work-around lack of LTM 522 make PAST::Val.new( 523 :value(~$/[0]), 524 :returns<float> 525 ); 526} 527 528method infix:sym<?:> ($/) { 529 my $past := PAST::Op.new( 530 :pasttype<if>, 531 ); 532 # Override to emit ternary ops in .to_c 533 $past<ternary> := 1; 534 make $past; 535} 536 537method statement_control:sym<if> ($/) { 538 my $past := PAST::Op.new( 539 :pasttype<if>, 540 541 $<EXPR>.ast, 542 $<then>.ast, 543 ); 544 545 $past.push($<else>[0].ast) if $<else>; 546 547 make $past; 548} 549 550method statement_control:sym<while> ($/) { 551 my $past := PAST::Op.new( 552 :pasttype<while>, 553 554 $<condition>.ast, 555 $<statement_list>.ast, 556 ); 557 558 make $past; 559} 560 561method statement_control:sym<do-while> ($/) { 562 my $past := PAST::Op.new( 563 :pasttype<do-while>, 564 565 $<blockoid>.ast, 566 $<condition>.ast, 567 ); 568 569 make $past; 570} 571 572method statement_control:sym<for> ($/) { 573 my $past := PAST::Op.new( 574 :pasttype<for>, 575 576 $<init> ?? $<init>[0].ast !! undef, 577 $<test> ?? $<test>[0].ast !! undef, 578 $<step> ?? $<step>[0].ast !! undef, 579 $<statement_list>.ast, 580 ); 581 582 make $past; 583} 584 585# Not real "C" switch. Just close enough 586method statement_control:sym<switch> ($/) { 587 my $past := PAST::Op.new( 588 :pasttype<switch>, 589 $<test>.ast, 590 $<statement_list>.ast, 591 ); 592 make $past; 593} 594 595method statement_control:sym<break> ($/) { 596 my $past := PAST::Op.new(); 597 $past<control> := 'break'; 598 make $past; 599} 600 601method statement_control:sym<continue> ($/) { 602 my $past := PAST::Op.new(); 603 $past<control> := 'continue'; 604 make $past; 605} 606 607method statement_or_block ($/) { 608 $<labeled_statement> 609 ?? make PAST::Block.new( 610 $<labeled_statement>.ast 611 ) 612 !! make $<blockoid>.ast 613} 614 615method circumfix:sym<( )> ($/) { 616 my $past := $<EXPR>.ast; 617 618 # Indicate that we need wrapping. 619 $past<wrap> := 1; 620 621 make $past; 622} 623 624method postcircumfix:sym<[ ]> ($/) { 625 make PAST::Var.new( 626 $<EXPR>.ast, 627 :scope('keyed'), 628 ); 629} 630 631 632# For casting we just set "returns" of EXPR. 633method prefix:sym<( )> ($/) { 634 make PAST::Op.new( 635 :returns(~$<type_declarator>), 636 ); 637} 638 639# Helper method for generating PAST::Val with opsize 640method opsize () { 641 make PAST::Val.new( 642 :value($OP.size), 643 :returns('int'), 644 ); 645} 646 647method make_write_barrier () { 648 make PAST::Op.new( 649 :pasttype<call>, 650 :name<PARROT_GC_WRITE_BARRIER>, 651 PAST::Var.new( 652 :name<interp> 653 ), 654 PAST::Op.new( 655 :pasttype<call>, 656 :name<CURRENT_CONTEXT>, 657 PAST::Var.new( 658 :name<interp> 659 ) 660 ) 661 ); 662} 663 664# Local Variables: 665# mode: perl6 666# fill-column: 100 667# End: 668# vim: expandtab ft=perl6 shiftwidth=4: 669