# Aux. functions needed by the parser. (For cleanliness) # # Copyright (C) 2003-2007, The Perl Foundation. # $Id: COMP_parsefuncs.pm 21232 2007-09-12 19:30:46Z paultcochrane $ use vars qw( %usertypes ); use vars qw( %arrays ); use vars qw( $funcname $subname ); use vars qw( %labels $branchseq @selects); use vars qw( @data $sourceline %common ); use vars qw( %code $debug $runtime_jump); my @fors = (); my @whiles = (); my @dos = (); @selects = (); my $scopes = 0; my @data = (); sub parse_keys { feedme(); my $targ = $syms[CURR]; feedme(); feedme() while ( $syms[CURR] =~ /[(),]/ ); my $source = $syms[CURR]; feedme(); feedme() while ( $syms[CURR] =~ /[(),]/ ); $targ .= $seg; $source .= $seg; push @{ $code{$seg}->{code} }, <{code} }, "\t# $var was declared COMMON\n"; if ( !$array ) { $main::code{$main::seg}->{declarations}->{$var} = "COMMON"; $common{$var} = 1; } } } sub parse_shared { # Keyword only feedme(); $loop = 0; while ( $type[CURR] !~ /COMP|COMM|STMT/ ) { die if $loop++ > 20; $var = $syms[CURR]; feedme; my $array = 0; if ( $syms[CURR] eq "(" ) { $array = 1; while ( $syms[CURR] ne ")" ) { feedme; } feedme; } $stype = ""; $user = 0; if ( $syms[CURR] eq "as" ) { feedme; $stype = $syms[CURR]; feedme; # N my %th = ( single => 'FLO', double => 'FLO', long => 'INT', integer => 'INT', string => 'STRING' ); if ( exists $th{$stype} ) { $stype = $th{$stype}; } else { unless ( exists $usertypes{$stype} ) { die "User type $stype not found at source line $sourceline"; } $user = 1; } } unless ($stype) { $stype = typeof($var); } $arrays{$var} = 1 if ($array); #print "Flagging (assumed) $var as array\n"; if ($array) { $stype = "ARRAY"; } else { $var =~ s/\W$//g; } #print "Type: $stype User: $user Var: $var Array: $array\n"; if ( $user and !$array ) { print CODE<{code} }, $prompt; my $sf = 1; $sf = 0 if ($filedesc); push @{ $code{$seg}->{code} }, <{code} }, "\t#ne \$I0, $vars, ERR_INPFIELDS\n"; } else { push @{ $code{$seg}->{code} }, "\tne \$I0, $vars, INPUT_$inputcounts # Re-prompt\n"; } $loop = 0; barf(); # We're too far in already... while ( $type[CURR] !~ /COMP|COMM|STMT/ ) { die if $loop++ > 20; push @{ $code{$seg}->{code} }, "\tpop \$S99, \$P99\n"; push @{ $code{$seg}->{code} }, "\t\$N99= \$S99\n"; my ( $result, $type, @code ) = EXPRESSION( { stuff => "\$X99", choose => 1 } ); push @{ $code{$seg}->{code} }, "@code"; feedme(); if ( $syms[CURR] eq "," ) { feedme(); next; } } } sub parse_input { feedme(); my $promptcode = ""; if ( $syms[CURR] eq ";" ) { feedme(); # Ignore this form for now. } if ( $syms[CURR] eq "#" ) { feedme(); $fd = $syms[CURR]; feedme(); die "Expecting ," unless $syms[CURR] eq ","; feedme(); input_read_assign( $promptcode, $fd, 'noreprompt' ); return; } $promptcode = "INPUT_$inputcounts:\n"; # BASIC is unambiguous here. The next thing must be a quoted string or # no prompting is done. However, QB says that if no prompt, use " ?" if ( $type[CURR] eq "STRING" ) { $promptcode .= qq{\tprint "$syms[CURR]"\n}; feedme(); if ( $syms[CURR] eq "," ) { $promptcode .= qq{\tnoop # No ?\n}; } elsif ( $syms[CURR] eq ";" ) { $promptcode .= qq{\tprint "?"\n}; } else { die "Syntax error, expected ',' or ';'"; } feedme(); } else { $promptcode .= qq{\tprint "? "\n}; } input_read_assign( $promptcode, 0, 0 ); $inputcounts++; } my $ons = 0; sub parse_on { my ( $result, $type, @code ) = EXPRESSION; # The switch... feedme(); my $branch = $syms[CURR]; # goto or gosub push @{ $code{$seg}->{code} }, "\t# On X $branch...\n"; feedme(); $loop = 0; @onlab = (); while ( $type[CURR] !~ /COMP|COMM|STMT/ ) { die if $loop++ > 255; if ( $syms[CURR] eq "," ) { feedme(); next; } die "Only labels/numbers allowed" unless ( $type[CURR] =~ /BARE|INT/ ); create_label(); push( @onlab, $syms[CURR] ); feedme(); } push @{ $code{$seg}->{code} }, <{code} }, "\tne $result, $i.0, ON_${ons}_$i\n"; if ( $branch eq "gosub" ) { push @{ $code{$seg}->{code} }, qq{\tbsr $labels{$jumps}\t# $branch $jumps\n}; push @{ $code{$seg}->{code} }, qq{\t#RTJ ne S0, "", RUNTIME_JUMP\n}; push @{ $code{$seg}->{code} }, qq{\tgoto ON_END_$ons\n}; } elsif ( $branch eq "goto" ) { push @{ $code{$seg}->{code} }, qq{\tgoto $labels{$jumps}\t# $branch $jumps\n}; } else { die "Illegal branch type, only GOSUB/GOTO allowed"; } push @{ $code{$seg}->{code} }, "ON_${ons}_$i:\n"; $i++; } if ( $branch eq "gosub" ) { push @{ $code{$seg}->{code} }, "\tnoop\nON_END_${ons}:\n"; } $ons++; } sub parse_randomize { if ( $type[NEXT] =~ /STMT|COMM|COMP/ ) { # No arg version feedme(); print CODE<{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, <{code} }, < 100; if ( $type[CURR] eq "STRING" and not $fd and ( $type[NEXT] eq "STMT" or $type[NEXT] eq "COMP" or $type[NEXT] eq "COMM" ) ) { $eol = 0; fdprint( $fd, $syms[CURR] ); feedme(); $expr = 1; next; } if ( $syms[CURR] eq ";" ) { $eol = 1; feedme(); $expr = 0; next; } if ( $syms[CURR] eq "," ) { fdprint( $fd, "\\t" ); $eol = 1; feedme(); $expr = 0; next; } last if $expr; ( $result, $type, @code ) = EXPRESSION( { nofeed => 1 } ); feedme(); if ($fd) { push @{ $code{$seg}->{code} }, <{code} }, <{code} }, < '$X99', choose => 1 } ); feedme(); push @{ $code{$seg}->{code} }, < 1 } ); feedme(); die "Expected ',': $syms[CURR]" unless $syms[CURR] eq ","; feedme(); my ( $rightres, $righttype, @right ) = EXPRESSION( { lhs => 1 } ); push @{ $code{$seg}->{code} }, < "STRING", value => qq{"$syms[CURR]"} }; } elsif ( $type[CURR] eq "PUN" ) { my $s = $syms[CURR]; while (1) { feedme(); if ( $syms[CURR] eq "," ) { push @ld, { type => "STRING", value => qq{"$s"} }; redo OUTDATA; } if ( $type[CURR] =~ /COMP|COMM|STMT/ ) { push @ld, { type => "STRING", value => qq{"$s"} }; last OUTDATA; } $s .= $syms[CURR]; } } else { die "Cannot use $type[CURR]/$syms[CURR] in DATA"; } feedme(); } push( @data, { line => $currline, data => \@ld } ); } sub parse_restore { my @args; if ( $type[NEXT] eq "BARE" or $type[NEXT] eq "INT" ) { feedme(); create_label(); push @args, qq{"$labels{$syms[CURR]}"}; } else { push @args, qq{""}; } feedme(); push @{ $code{$seg}->{code} }, "\t_RESTORE(" . join( ",", @args ) . ")\n"; } sub parse_exit { if ( $syms[NEXT] eq "for" ) { feedme(); $foo = $fors[$scopes]->[-1]; push @{ $code{$seg}->{code} }, "\tgoto AFTER_NEXT_$foo->{num}\n"; } elsif ( $syms[NEXT] eq "function" ) { push @{ $code{$seg}->{code} }, qq{\tgoto END_$seg\n}; feedme(); #$_=english_func($funcname); #print CODE "\tgoto FUNC_EXIT_$_\n"; } elsif ( $syms[NEXT] eq "sub" ) { push @{ $code{$seg}->{code} }, qq{\tgoto END_$seg\n}; feedme(); #print CODE "\tgoto SUB_EXIT_$subname\n"; } elsif ( $syms[NEXT] eq "do" ) { feedme(); $foo = $dos[-1]; push @{ $code{$seg}->{code} }, "\tgoto AFTERDO_$foo->{jump}\n"; } else { die "Unknown EXIT type source line $sourceline"; } } $selectcounter = 0; sub parse_select { die "Expected 'case'" if ( $syms[NEXT] ne "case" ); feedme(); push( @selects, { jump => ++$selectcounter, incase => 0 } ); my @a = EXPRESSION(); print CODE<{jump}, $s->{incase} ); if ( $syms[NEXT] eq "else" ) { feedme(); print CODE "\t goto CASE_${jump}_FIN\n"; print CODE "CASE_${jump}_${incase}:\t# Default\n"; $selects[-1]->{incase} = $incase + 1; return; } my $lambda = <|>=|<|<=|=|<>$/ ) { # Relop feedme(); $op = $syms[CURR]; @a = EXPRESSION(); feedme(); print CODE<=" push P9, "OP" P0= P5 bsr RUNTIME_PUSH pop P0, P13 bsr RUNTIME_PUSH push P9, "<=" push P9, "OP" P0= P5 bsr RUNTIME_PUSH pop P0, P13 bsr RUNTIME_PUSH dec I5 gt I5, 0, CASE_${jump}_${incase}_STARTTO TOS } if ( $ops and ( $tos or $ors ) ) { print CODE qq{\tunshift P9, "OP"\n}; print CODE qq{\tunshift P9, "or"\n}; } if ($ops) { print CODE<{incase} = $incase; } my $false; my $whilecounter = 0; sub parse_while { $whilecounter++; push( @whiles, { jump => $whilecounter } ); my ( $result, $type, @code ) = EXPRESSION(); $false = "0.0"; $false = qq{""} if ( $type eq "S" ); push @{ $code{$seg}->{code} }, "WHILE_$whilecounter:\n"; push @{ $code{$seg}->{code} }, <{jump}; push @{ $code{$seg}->{code} }, <{code} }, "DO_$docounter:\n"; my ( $result, $type, @code ) = EXPRESSION(); $false = qq{""} if $type eq "S"; if ( $which eq "while" ) { $_ = "@code eq $result, $false, AFTERDO_$docounter"; } else { $_ = "@code ne $result, $false, AFTERDO_$docounter"; } push @{ $code{$seg}->{code} }, "\t$_\n"; push( @dos, { jump => $docounter, needstmt => 0 } ); } else { push @{ $code{$seg}->{code} }, "DO_$docounter:\n"; push( @dos, { jump => $docounter, needstmt => 1 } ); } } sub parse_goto { feedme; create_label(); push @{ $code{$seg}->{code} }, "\tgoto $labels{$syms[CURR]}\t# Goto $syms[CURR]\n"; } sub parse_gosub { feedme; create_label(); push @{ $code{$seg}->{code} }, <{code} }, <{code} }, <{needstmt} and not( $syms[NEXT] =~ /while|until/ ) ) { push @{ $code{$seg}->{code} }, "\ngoto DO_$do->{jump}\t# Unconditional\n"; push @{ $code{$seg}->{code} }, "AFTERDO_$do->{jump}:\n"; return; } if ( $do->{needstmt} ) { my $false = "0.0"; feedme(); my $which = $syms[CURR]; my ( $result, $type, @code ) = EXPRESSION(); $false = qq{""} if $type eq "S"; if ( $which eq "while" ) { $_ = "@code ne $result, $false, DO_$do->{jump}"; } else { $_ = "@code eq $result, $false, DO_$do->{jump}"; } push @{ $code{$seg}->{code} }, "\t$_\n"; } else { push @{ $code{$seg}->{code} }, "\tgoto DO_$do->{jump}\n"; } push @{ $code{$seg}->{code} }, "AFTERDO_$do->{jump}:\n"; } sub parse_type { my (@types); feedme; my $typename = $syms[CURR]; feedme; $sourceline++; feedme; while ( $syms[CURR] ne "end" ) { my ( $name, $type ); die "Syntax error in type $type[CURR]/$syms[CURR] (source line $sourceline)" unless $type[CURR] eq "BARE"; $name = $syms[CURR]; feedme; die "Expected 'as' got $syms[CURR] (source line $sourceline)" unless $syms[CURR] eq 'as'; feedme; $type = $syms[CURR]; my %th = ( single => 'FLO', double => 'FLO', long => 'INT', integer => 'INT', string => 'STRING' ); die "Unknown type $type (source line $sourceline)" unless ( exists $th{$type} or exists $usertypes{$type} ); my $marker; if ( exists $th{$type} ) { $marker = $th{$type}; } else { $marker = 'USER'; } push( @types, [ $name, $type, $marker ] ); feedme(); if ( $type[CURR] eq "PUN" and $syms[CURR] eq "*" ) { print "WARNING: * in TYPE not supported yet for $name\n"; feedme; feedme; } while ( $type[CURR] eq "STMT" or $type[CURR] eq "COMM" ) { print CODE "\t# $syms[CURR]\n" if $type[CURR] eq "COMM"; feedme; } $sourceline++; } feedme; # Collect the "type" $usertypes{$typename} = [@types]; print CODE < 0, FLO => '0.0', STRING => '""' ); if ( $_->[2] ne "USER" ) { print CODE<[2]} P2["$_->[0]"]= P1 NOTUSER } else { print CODE<[1] P1["storage"]= P0 P1["_type"], '$_->[1]' P2["$_->[0]"]= P1 USERTYPE } } print CODE< 'I0', FLO => 'N0', STRING => 'S0' ); if ( $_->[2] ne "USER" ) { print CODE<[0]"] $val{$_->[2]}= P5["value"] P2["value"]= $val{$_->[2]} #print "-> Copied value for " #print $val{$_->[2]} #print "\\n" P3["$_->[0]"]= P2 NOTUSER } else { print CODE<[0]"] bsr COPY_$_->[1] P2["storage"]= P1 P6= P5 # Go back to where we were! P2["_type"]= '$_->[1]' P3["$_->[0]"]= P2 #print "Finished substruct\\n" USER } } print CODE< 'FLO', double => 'FLO', long => 'INT', integer => 'INT', string => 'STRING' ); my %sigilmap = ( '%' => 'integer', '&' => 'long', '!' => 'single', '#' => 'double', '$' => 'string' ); my $ut = ""; if ( $syms[NEXT] eq "as" ) { feedme; # "as" feedme; # type... if ( exists $th{ $syms[CURR] } ) { $type = $th{ $syms[CURR] }; } elsif ( exists $usertypes{ $syms[CURR] } ) { $type = "USER"; $ut = qq{\tP2["usertype"]= "$syms[CURR]"\n}; } else { die "Unknown type $syms[CURR]"; } } else { $_ = substr( $var, -1, 1 ); if ( exists $sigilmap{$_} ) { $type = $th{ $sigilmap{$_} }; } } $arrays{"${var}${seg}"} = 1; #print STDERR "Marking ${var}${seg}\n"; push @{ $code{$seg}->{code} }, < 1, assign => 1 } ); push @{ $code{$seg}->{code} }, @code; die "TO expected at source line $sourceline" unless ( $syms[CURR] ne "to" ); feedme(); # The destination value ( $endexpr, $type, @code ) = EXPRESSION(); feedme(); if ( $syms[CURR] eq "step" ) { ( $stepexpr, $type, @stepcode ) = EXPRESSION(); } else { $stepexpr = "1.0"; } $main::code{$main::seg}->{declarations}->{"FORLOOP_END_$forloop"} = 1; $main::code{$main::seg}->{declarations}->{"FORLOOP_STEP_$forloop"} = 1; push @{ $code{$seg}->{code} }, < $result, num => $forloop, inc => $stepexpr }; } sub parse_next { # next [a[,b[,c]...] feedme(); my ( $var, $vartype, $ovar ); my $ps; $ps = pop @{ $fors[$scopes] }; if ( $type[CURR] ne "BARE" ) { # next (no variable) push @{ $code{$seg}->{code} }, <{var}, $ps->{var}, FORLOOP_STEP_$ps->{num} goto FOR_$ps->{num} AFTER_NEXT_$ps->{num}: noop NEXT } else { # next var while (1) { push @{ $code{$seg}->{code} }, <{var}, $ps->{var}, FORLOOP_STEP_$ps->{num} goto FOR_$ps->{num} AFTER_NEXT_$ps->{num}: noop NEXT if ( $syms[NEXT] eq "," ) { feedme(); feedme(); $ps = pop @{ $fors[$scopes] }; next; } last; } } } sub parse_call { # Subroutines are disguised as user-defined functions, # except that there's no return value to deal with. feedme(); if ( !exists $subs{ $syms[CURR] } ) { die "Subroutine $syms[CURR] not found at line $sourceline\n"; } my $sub = $syms[CURR]; barf(); # print STDERR "Processing call $sub\n"; ( $result, $type, @code ) = EXPRESSION( { ignorecomma => 1 } ); # print STDERR "Got back @code\n"; push @{ $code{$seg}->{code} }, <{code} }, <{declarations}->{$englishname} = 1; foreach (@params) { unless (/\(\)/) { my $t = typeof($_); $t = "string" if $t eq "STRING"; $t = "float" if $t eq "FLO"; $_ = changename($_); $_ =~ s/\$/_string/g; push @{ $code{$seg}->{code} }, qq{\t.param $t $_\n}; push @{ $code{$seg}->{args} }, $_; } else { s/\(\)//g; $_ = changename($_); #print STDERR "Marking ${_}${seg}\n"; $arrays{"${_}${seg}"} = 1; push @{ $code{$seg}->{code} }, <{args}}, $_; } } return; } sub parse_endfunc { feedme; my $t = $seg; $seg =~ s/^_//; # Remove the _ $seg =~ tr/A-Z/a-z/; # lowercase $seg =~ s/userfunc_//; push @{ $code{$t}->{code} }, "END_$t:\n"; if ( exists $code{$t}->{args} ) { foreach ( @{ $code{$t}->{args} } ) { push @{ $code{$t}->{code} }, "\t.return $_\t# Returning arg\n"; } } push @{ $code{$t}->{code} }, "\t.return $seg\n"; $seg = "_basicmain"; $funcname = ""; return; } sub parse_endsub { goto &parse_endfunc; } sub parse_function_dispatch { return; print FUNC<{code} }, <{code} }, qq|\teq JUMPLABEL, "$labels{$_}", $labels{$_}\n|; } } push @{ $code{$seg}->{code} }, <{code} }, <{line}; if ( length $line ) { push @{ $code{_data}->{code} }, qq{\t\$P1["$line"]= $counter\n}; } foreach ( @{ $ld->{data} } ) { my ( $t, $v ) = ( $_->{type}, $_->{value} ); push @{ $code{_data}->{code} }, <{code} }, <