1 # Aux. functions needed by the parser. (For cleanliness)
3 # Copyright (C) 2003-2007, The Perl Foundation.
6 use vars
qw( %usertypes );
7 use vars qw( %arrays );
8 use vars qw( $funcname $subname );
9 use vars qw( %labels $branchseq @selects);
10 use vars qw( @data $sourceline %common );
11 use vars qw( %code $debug $runtime_jump);
22 my $targ = $syms[CURR];
24 feedme() while ( $syms[CURR] =~ /[(),]/ );
25 my $source = $syms[CURR];
27 feedme() while ( $syms[CURR] =~ /[(),]/ );
30 push @{ $code{$seg}->{code} }, <<KEYS;
31 _ARRAY_KEYS("$source","$targ")
37 while ( $type[CURR
] !~ /COMP|COMM|STMT/ ) {
42 if ( $syms[CURR
] eq "(" ) {
44 while ( $syms[CURR
] ne ")" ) {
49 $var =~ s/\$$/_string/;
50 push @
{ $code{$seg}->{code
} }, "\t# $var was declared COMMON\n";
52 $main::code
{$main::seg
}->{declarations
}->{$var} = "COMMON";
59 sub parse_shared
{ # Keyword only
62 while ( $type[CURR
] !~ /COMP|COMM|STMT/ ) {
67 if ( $syms[CURR
] eq "(" ) {
69 while ( $syms[CURR
] ne ")" ) {
76 if ( $syms[CURR
] eq "as" ) {
87 if ( exists $th{$stype} ) {
91 unless ( exists $usertypes{$stype} ) {
92 die "User type $stype not found at source line $sourceline";
98 $stype = typeof
($var);
100 $arrays{$var} = 1 if ($array);
102 #print "Flagging (assumed) $var as array\n";
110 #print "Type: $stype User: $user Var: $var Array: $array\n";
111 if ( $user and !$array ) {
112 print CODE
<<SHARINGU;
113 # Sharing $stype (user) $var with main
116 P0= P1["$var"] # Pull the original
119 P3["$var"]= P0 # Hack in the alias
123 elsif ( $user and $array ) {
125 # TODO TODO TODO TODO
127 # Sharing $stype $var with main (array=$array)
130 P0= P1["$var"] # Pull the original
133 P3["$var"]= P0 # Hack in the alias
139 # Sharing $stype $var with main (array=$array)
142 P0= P1["$var"] # Pull the original
145 P3["$var"]= P0 # Hack in the alias
154 sub input_read_assign
{
155 my ( $prompt, $filedesc, $noreloop ) = @_;
158 push @
{ $code{$seg}->{code
} }, $prompt;
160 $sf = 0 if ($filedesc);
162 push @
{ $code{$seg}->{code
} }, <<INP1;
163 \$S0 = _READLINE($filedesc)
164 \$P99 = _SPLITLINE(\$S0,$sf)
168 # Bug here...FIXME.. I'm using $vars before it's set.
171 push @{ $code{$seg}->{code} }, "\t#ne \$I0, $vars, ERR_INPFIELDS\n";
174 push @{ $code{$seg}->{code} }, "\tne \$I0, $vars, INPUT_$inputcounts # Re-prompt\n";
178 barf(); # We're too far in already...
179 while ( $type[CURR] !~ /COMP|COMM|STMT/ ) {
181 push @{ $code{$seg}->{code} }, "\tpop \$S99, \$P99\n";
182 push @{ $code{$seg}->{code} }, "\t\$N99= \$S99\n";
184 my ( $result, $type, @code ) = EXPRESSION( { stuff => "\$X99", choose => 1 } );
185 push @{ $code{$seg}->{code} }, "@code";
187 if ( $syms[CURR] eq "," ) {
198 if ( $syms[CURR] eq ";" ) {
199 feedme(); # Ignore this form for now.
201 if ( $syms[CURR] eq "#" ) {
205 die "Expecting ," unless $syms[CURR] eq ",";
207 input_read_assign( $promptcode, $fd, 'noreprompt' );
210 $promptcode = "INPUT_$inputcounts:\n";
212 # BASIC is unambiguous here. The next thing must be a quoted string or
213 # no prompting is done. However, QB says that if no prompt, use " ?"
214 if ( $type[CURR] eq "STRING" ) {
215 $promptcode .= qq{\tprint "$syms[CURR]"\n};
217 if ( $syms[CURR] eq "," ) {
218 $promptcode .= qq{\tnoop # No ?\n};
220 elsif ( $syms[CURR] eq ";" ) {
221 $promptcode .= qq{\tprint "?"\n};
224 die "Syntax error, expected ',' or ';'";
229 $promptcode .= qq{\tprint "? "\n};
231 input_read_assign( $promptcode, 0, 0 );
238 my ( $result, $type, @code ) = EXPRESSION; # The switch...
240 my $branch = $syms[CURR]; # goto or gosub
241 push @{ $code{$seg}->{code} }, "\t# On X $branch...\n";
245 while ( $type[CURR] !~ /COMP|COMM|STMT/ ) {
246 die if $loop++ > 255;
247 if ( $syms[CURR] eq "," ) {
251 die "Only labels/numbers allowed" unless ( $type[CURR] =~ /BARE|INT/ );
253 push( @onlab, $syms[CURR] );
256 push @{ $code{$seg}->{code} }, <<ON;
257 @code lt $result, 0.0, ONERR_${ons}
258 gt $result, 255.0, ONERR_${ons}
261 print "On...goto/gosub out of range at $sourceline\\n"
267 for my $jumps (@onlab) {
268 push @
{ $code{$seg}->{code
} }, "\tne $result, $i.0, ON_${ons}_$i\n";
269 if ( $branch eq "gosub" ) {
270 push @
{ $code{$seg}->{code
} }, qq{\tbsr
$labels{$jumps}\t# $branch $jumps\n};
271 push @
{ $code{$seg}->{code
} }, qq{\t#RTJ ne S0, "", RUNTIME_JUMP\n};
272 push @
{ $code{$seg}->{code
} }, qq{\tgoto ON_END_
$ons\n};
274 elsif ( $branch eq "goto" ) {
275 push @
{ $code{$seg}->{code
} }, qq{\tgoto
$labels{$jumps}\t# $branch $jumps\n};
278 die "Illegal branch type, only GOSUB/GOTO allowed";
280 push @
{ $code{$seg}->{code
} }, "ON_${ons}_$i:\n";
283 if ( $branch eq "gosub" ) {
284 push @
{ $code{$seg}->{code
} }, "\tnoop\nON_END_${ons}:\n";
289 sub parse_randomize
{
290 if ( $type[NEXT] =~ /STMT|COMM|COMP/ ) { # No arg version
292 print CODE
<<PROMPTRND;
293 print "Random-number seed (-32768 to 32767)?"
300 ( $result, $type, @code ) = EXPRESSION
();
301 push @
{ $code{$seg}->{code
} }, <<EOR;
302 @code find_global \$P0, "RANDSEED"
305 store_global "RANDSEED", \$P0
311 sub parse_locate
{ # locate x,y | locate x | locate ,y
314 my ( $resulty, $typey, @codey );
315 my ( $resultx, $typex, @codex );
316 if ( $type[NEXT] =~ /PUN/ ) { # Y only
318 ( $resulty, $typey, @codey ) = EXPRESSION
(); # Y (only)
321 ( $resultx, $typex, @codex ) = EXPRESSION
(); # X
322 if ( $type[NEXT] =~ /PUN/ ) {
324 ( $resulty, $typey, @codey ) = EXPRESSION
();
327 if ( @codey and @codex ) { # X and Y
329 push @
{ $code{$seg}->{code
} }, <<XANDY;
334 _screen_locate(\$N101,\$N100)
337 elsif ( @codey and not @codex ) {
338 push @
{ $code{$seg}->{code
} }, <<YNOTX;
340 _screen_locate($resulty)
343 elsif ( @codex and not @codey ) {
344 push @
{ $code{$seg}->{code
} }, <<XNOTY;
346 _screen_locate($resultx)
353 my ( $resultb, $typeb, @codeb );
354 my ( $resultf, $typef, @codef );
356 if ( $type[NEXT] =~ /PUN/ ) { # Back only
358 ( $resultb, $typeb, @codeb ) = EXPRESSION
(); # Back (only)
361 ( $resultf, $typef, @codef ) = EXPRESSION
(); # Fore
362 if ( $type[NEXT] =~ /PUN/ ) {
364 ( $resultb, $typeb, @codeb ) = EXPRESSION
();
367 if ( @codeb and @codef ) { # F and B
368 push @
{ $code{$seg}->{code
} }, <<FANDB;
369 @codeb \$N100= $resultb
370 @codef \$N101= $resultf
371 _screen_color(\$N101,\$N100)
374 elsif ( @codeb and not @codef ) {
375 push @
{ $code{$seg}->{code
} }, <<BNOTF;
377 _screen_color($resultb) # Broke!
380 elsif ( @codef and not @codeb ) {
381 push @
{ $code{$seg}->{code
} }, <<FNOTB;
383 _screen_color($resultf) # Broke!
389 if ( !$type[NEXT] =~ /STMT|COMM|COMP/ ) { # No arg version
392 push @
{ $code{$seg}->{code
} }, <<CLS;
399 ( $result, $type, @code ) = EXPRESSION
();
401 die "Expecting FOR at $sourceline" unless $syms[CURR
] eq "for";
404 if ( $syms[CURR
] eq "input" ) {
407 elsif ( $syms[CURR
] eq "output" ) {
410 elsif ( $syms[CURR
] eq "random" ) {
411 die "random file i/o not implemented yet at $sourceline";
414 die "Expected input/output/random at $sourceline";
417 die "Expecting AS" unless $syms[CURR
] eq "as";
419 die "Expecting #" unless $syms[CURR
] eq "#";
422 push @
{ $code{$seg}->{code
} }, <<OPEN;
424 _OPEN($result,"$mode",$fd)
430 die "Expecting # at $sourceline" unless $syms[CURR
] eq "#";
433 push @
{ $code{$seg}->{code
} }, <<CLOSE;
439 my ( $fd, $string ) = @_;
441 push @
{ $code{$seg}->{code
} }, <<PRINT;
442 _WRITE($fd,1,"$string")
446 if ( $string ne "\\n" ) {
447 push @
{ $code{$seg}->{code
} }, <<PRINT;
448 _BUILTIN_DISPLAY(1,"$string")
452 push @
{ $code{$seg}->{code
} }, <<PRINT;
453 find_global \$P0, "PRINTCOL"
455 store_global "PRINTCOL", \$P0
468 my ( $result, $type, @CODE );
470 if ( $syms[CURR
] eq "#" and $type[CURR
] eq "PUN" ) {
476 if ( $syms[CURR
] eq "using" ) {
477 print "WARNING: PRINT USING not yet supported\n";
482 if ( $type[CURR
] eq "STMT" or $type[CURR
] eq "COMP" or $type[CURR
] eq "COMM" ) {
485 last if ( iskeyword
( $syms[CURR
] ) and not isbuiltin
( $syms[CURR
] ) );
486 die "LOOP" if $c++ > 100;
487 if ( $type[CURR
] eq "STRING"
489 and ( $type[NEXT] eq "STMT" or $type[NEXT] eq "COMP" or $type[NEXT] eq "COMM" ) )
492 fdprint
( $fd, $syms[CURR
] );
497 if ( $syms[CURR
] eq ";" ) {
503 if ( $syms[CURR
] eq "," ) {
504 fdprint
( $fd, "\\t" );
511 ( $result, $type, @code ) = EXPRESSION
( { nofeed
=> 1 } );
514 push @
{ $code{$seg}->{code
} }, <<PRINT;
516 _WRITE($fd,1,$result)
520 push @
{ $code{$seg}->{code
} }, <<PRINT;
522 _BUILTIN_DISPLAY(1,$result)
526 #print "After Expression have $type[CURR] $syms[CURR]\n";
532 fdprint
( $fd, "\\n" );
538 while ( $type[CURR
] !~ /COMP|COMM|STMT/ ) {
539 push @
{ $code{$seg}->{code
} }, <<EOASS;
543 ( $result, $type, @code ) = EXPRESSION
( { stuff
=> '$X99', choose
=> 1 } );
545 push @
{ $code{$seg}->{code
} }, <<EOASS;
548 if ( $syms[CURR
] eq "," ) {
559 my ( $leftres, $lefttype, @left ) = EXPRESSION
( { lhs
=> 1 } );
561 die "Expected ',': $syms[CURR]" unless $syms[CURR
] eq ",";
563 my ( $rightres, $righttype, @right ) = EXPRESSION
( { lhs
=> 1 } );
564 push @
{ $code{$seg}->{code
} }, <<SWAP;
565 \$${righttype}99 = $rightres
567 $leftres = \$${righttype}99
574 print "Stopped at source line "
584 $currline = "" unless defined $currline;
587 OUTDATA
: while ( $type[CURR
] !~ /COMP|COMM|STMT/ ) {
588 if ( $syms[CURR
] eq "," ) {
592 if ( $type[CURR
] eq "PUN" and $syms[CURR
] =~ /-/ ) {
593 if ( $type[NEXT] =~ /INT|FLO/ ) {
595 $syms[CURR
] = "-$syms[CURR]";
598 if ( $type[CURR
] =~ /STRING|BARE|INT|FLO/ ) {
599 push @ld, { type
=> "STRING", value
=> qq{"$syms[CURR]"} };
601 elsif ( $type[CURR
] eq "PUN" ) {
605 if ( $syms[CURR
] eq "," ) {
606 push @ld, { type
=> "STRING", value
=> qq{"$s"} };
609 if ( $type[CURR
] =~ /COMP|COMM|STMT/ ) {
610 push @ld, { type
=> "STRING", value
=> qq{"$s"} };
617 die "Cannot use $type[CURR]/$syms[CURR] in DATA";
621 push( @data, { line
=> $currline, data
=> \
@ld } );
626 if ( $type[NEXT] eq "BARE" or $type[NEXT] eq "INT" ) {
629 push @args, qq{"$labels{$syms[CURR]}"};
635 push @
{ $code{$seg}->{code
} }, "\t_RESTORE(" . join( ",", @args ) . ")\n";
639 if ( $syms[NEXT] eq "for" ) {
641 $foo = $fors[$scopes]->[-1];
642 push @
{ $code{$seg}->{code
} }, "\tgoto AFTER_NEXT_$foo->{num}\n";
644 elsif ( $syms[NEXT] eq "function" ) {
645 push @
{ $code{$seg}->{code
} }, qq{\tgoto END_
$seg\n};
648 #$_=english_func($funcname);
649 #print CODE "\tgoto FUNC_EXIT_$_\n";
651 elsif ( $syms[NEXT] eq "sub" ) {
652 push @
{ $code{$seg}->{code
} }, qq{\tgoto END_
$seg\n};
655 #print CODE "\tgoto SUB_EXIT_$subname\n";
657 elsif ( $syms[NEXT] eq "do" ) {
660 push @
{ $code{$seg}->{code
} }, "\tgoto AFTERDO_$foo->{jump}\n";
663 die "Unknown EXIT type source line $sourceline";
670 die "Expected 'case'" if ( $syms[NEXT] ne "case" );
672 push( @selects, { jump
=> ++$selectcounter, incase
=> 0 } );
673 my @a = EXPRESSION
();
674 print CODE
<<SELECTSTART;
676 @a bsr DEREF # De-reference variables and whatnot.
679 P1["$selectcounter"]= P6 # Store for later.
680 goto CASE_${selectcounter}_0
684 # Honestly the next thing needs to be a case statement.
685 # I don't enforce it though. Honor system! :)
690 my $s = $selects[-1];
691 my ( $jump, $incase ) = ( $s->{jump
}, $s->{incase
} );
693 if ( $syms[NEXT] eq "else" ) {
695 print CODE
"\t goto CASE_${jump}_FIN\n";
696 print CODE
"CASE_${jump}_${incase}:\t# Default\n";
697 $selects[-1]->{incase
} = $incase + 1;
705 P5= P1["$jump"] # Stored value.
707 print CODE
<<CASE_SETUP;
708 goto CASE_${jump}_FIN
709 CASE_${jump}_${incase}:
710 new P12, .ResizablePMCArray # OR
711 new P13, .ResizablePMCArray # TO
712 new P14, .ResizablePMCArray # Operators. Op first, then value
714 my ( $ors, $tos, $ops ) = ( 0, 0, 0 );
716 if ( $type[CURR] eq "COMM" ) {
717 print CODE "\t#$syms[CURR]\n";
720 last if ( $type[CURR] eq "STMT" or $type[CURR] eq "COMP" );
721 if ( $syms[NEXT] =~ /^>|>=|<|<=|=|<>$/ ) { # Relop
734 @a = EXPRESSION
(); # CURR = "to", "," or EOL.
736 if ( $syms[CURR
] eq ","
737 or $type[CURR
] eq "STMT"
738 or $type[CURR
] eq "COMP" )
742 push P12, P6 # Save result for later.
746 if ( $syms[CURR
] eq "to" ) {
747 my @b = EXPRESSION
();
759 last if ( $type[CURR
] eq "STMT" or $type[CURR
] eq "COMP" );
761 print CODE
"\tbsr EXPRINIT\n";
766 CASE_${jump}_${incase}_STARTOR:
767 eq I5, 1, CASE_${jump}_${incase}_NO_OR
770 CASE_${jump}_${incase}_NO_OR:
773 P0= P5 # The "constant"
778 gt I5, 0, CASE_${jump}_${incase}_STARTOR
781 if ( $ors and $tos ) {
782 print CODE
qq{\tunshift P9
, "OP"\n};
783 print CODE
qq{\tunshift P9
, "or"\n};
789 CASE_${jump}_${incase}_STARTTO:
790 eq I5, 1, CASE_${jump}_${incase}_NO_TO
793 CASE_${jump}_${incase}_NO_TO:
809 gt I5, 0, CASE_${jump}_${incase}_STARTTO
812 if ( $ops and ( $tos or $ors ) ) {
813 print CODE
qq{\tunshift P9
, "OP"\n};
814 print CODE
qq{\tunshift P9
, "or"\n};
820 CASE_${jump}_${incase}_STARTOPS:
821 eq I5, 1, CASE_${jump}_${incase}_NO_OP
824 CASE_${jump}_${incase}_NO_OP:
834 gt I5, 0, CASE_${jump}_${incase}_STARTOPS
841 ne I1, 1, CASE_${jump}_${incase}
843 $selects[-1]->{incase
} = $incase;
847 my $whilecounter = 0;
851 push( @whiles, { jump
=> $whilecounter } );
852 my ( $result, $type, @code ) = EXPRESSION
();
854 $false = qq{""} if ( $type eq "S" );
856 push @
{ $code{$seg}->{code
} }, "WHILE_$whilecounter:\n";
857 push @
{ $code{$seg}->{code
} }, <<BRANCH;
858 @code eq $result, $false, AFTERWHILE_$whilecounter
865 push @
{ $code{$seg}->{code
} }, <<LOOPUP;
874 if ( $syms[NEXT] eq "until" or $syms[NEXT] eq "while" ) {
876 feedme
(); # At the while/until
877 my $which = $syms[CURR
];
878 push @
{ $code{$seg}->{code
} }, "DO_$docounter:\n";
879 my ( $result, $type, @code ) = EXPRESSION
();
880 $false = qq{""} if $type eq "S";
881 if ( $which eq "while" ) {
882 $_ = "@code eq $result, $false, AFTERDO_$docounter";
885 $_ = "@code ne $result, $false, AFTERDO_$docounter";
887 push @
{ $code{$seg}->{code
} }, "\t$_\n";
888 push( @dos, { jump
=> $docounter, needstmt
=> 0 } );
891 push @
{ $code{$seg}->{code
} }, "DO_$docounter:\n";
892 push( @dos, { jump
=> $docounter, needstmt
=> 1 } );
899 push @
{ $code{$seg}->{code
} }, "\tgoto $labels{$syms[CURR]}\t# Goto $syms[CURR]\n";
906 push @
{ $code{$seg}->{code
} }, <<GOSUB;
907 bsr $labels{$syms[CURR]}\t# GOSUB $syms[CURR
]
908 #RTJ ne JUMPLABEL, "", RUNTIME_JUMP
913 if ( $type[NEXT] ne "BARE" ) {
914 push @
{ $code{$seg}->{code
} }, <<RETURN1;
920 feedme(); # Special "Return Label"
921 push @{ $code{$seg}->{code} }, <<RETURN2;
922 JUMPLABEL
= "$labels{$syms[CURR]}" # Return $syms[CURR]
925 if ( !$runtime_jump ) {
926 warn "Note: RETURN x causes slow IMCC compilation
\n";
934 if ( $do->{needstmt} and not( $syms[NEXT] =~ /while|until/ ) ) {
935 push @{ $code{$seg}->{code} }, "\ngoto DO_
$do->{jump
}\t# Unconditional\n";
936 push @
{ $code{$seg}->{code
} }, "AFTERDO_$do->{jump}:\n";
939 if ( $do->{needstmt
} ) {
942 my $which = $syms[CURR
];
943 my ( $result, $type, @code ) = EXPRESSION
();
944 $false = qq{""} if $type eq "S";
945 if ( $which eq "while" ) {
946 $_ = "@code ne $result, $false, DO_$do->{jump}";
949 $_ = "@code eq $result, $false, DO_$do->{jump}";
951 push @
{ $code{$seg}->{code
} }, "\t$_\n";
954 push @
{ $code{$seg}->{code
} }, "\tgoto DO_$do->{jump}\n";
956 push @
{ $code{$seg}->{code
} }, "AFTERDO_$do->{jump}:\n";
962 my $typename = $syms[CURR
];
966 while ( $syms[CURR
] ne "end" ) {
968 die "Syntax error in type $type[CURR]/$syms[CURR] (source line $sourceline)"
969 unless $type[CURR
] eq "BARE";
972 die "Expected 'as' got $syms[CURR] (source line $sourceline)" unless $syms[CURR
] eq 'as';
982 die "Unknown type $type (source line $sourceline)"
983 unless ( exists $th{$type}
984 or exists $usertypes{$type} );
987 if ( exists $th{$type} ) {
988 $marker = $th{$type};
993 push( @types, [ $name, $type, $marker ] );
995 if ( $type[CURR
] eq "PUN" and $syms[CURR
] eq "*" ) {
996 print "WARNING: * in TYPE not supported yet for $name\n";
1000 while ( $type[CURR
] eq "STMT" or $type[CURR
] eq "COMM" ) {
1001 print CODE
"\t# $syms[CURR]\n" if $type[CURR
] eq "COMM";
1006 feedme
; # Collect the "type"
1007 $usertypes{$typename} = [@types];
1009 # Type definition for $typename
1010 new P0, .ResizablePMCArray
1015 P1["name"]= '$_->[0]'
1016 P1["type"]= '$_->[1]'
1024 goto OUTOF_$typename
1026 #print "Dimensioning $typename\\n"
1031 my %val = ( INT
=> 0, FLO
=> '0.0', STRING
=> '""' );
1032 if ( $_->[2] ne "USER" ) {
1033 print CODE
<<NOTUSER;
1035 P1["name"]= '$_->[0]'
1036 P1["type"]= '$_->[2]'
1037 P1["value"]= $val{$_->[2]}
1042 print CODE
<<USERTYPE;
1044 P1["name"]= '$_->[0]'
1048 P1["_type"], '$_->[1]'
1059 COPY_$typename: # Source in P6 Dest in P1 (don't trash P0)
1060 #print "--Copying a $typename\\n"
1061 pushp # Makes an internal mess of P2, P3, P4, P5, P6 (popped)
1062 new P3, .Hash # Uses S0, I0, N0
1066 my %val = ( INT
=> 'I0', FLO
=> 'N0', STRING
=> 'S0' );
1067 if ( $_->[2] ne "USER" ) {
1068 print CODE
<<NOTUSER;
1070 P2["name"]= '$_->[0]'
1071 P2["type"]= '$_->[2]'
1074 $val{$_->[2]}= P5["value"]
1075 P2["value"]= $val{$_->[2]}
1076 #print "-> Copied value for "
1077 #print $val{$_->[2]}
1085 P2["name"]= '$_->[0]'
1087 P5= P6 # Remember where we were...
1092 P6= P5 # Go back to where we were!
1093 P2["_type
"]= '$_->[1]'
1095 #print "Finished substruct
\\n
"
1101 #print "Out of copy
$typename\\n
"
1115 if ( $syms[NEXT] eq "as" ) {
1116 my $var = $syms[CURR
];
1119 die "Unknown type $syms[CURR]" unless exists $usertypes{ $syms[CURR
] };
1120 my $type = $syms[CURR
];
1121 die "SIGIL not allowed here" unless ( $var =~ /\w$/ );
1122 print CODE
<<DIMTYPE;
1127 P1["_type"]= '$type'
1132 if ( $syms[NEXT] eq "," ) {
1137 elsif ( $syms[NEXT] eq "(" ) {
1138 my $var = $syms[CURR
];
1141 last if $syms[CURR
] eq ")";
1160 if ( $syms[NEXT] eq "as" ) {
1163 if ( exists $th{ $syms[CURR
] } ) {
1164 $type = $th{ $syms[CURR
] };
1166 elsif ( exists $usertypes{ $syms[CURR
] } ) {
1168 $ut = qq{\tP
2["usertype"]= "$syms[CURR]"\n};
1171 die "Unknown type $syms[CURR]";
1175 $_ = substr( $var, -1, 1 );
1176 if ( exists $sigilmap{$_} ) {
1177 $type = $th{ $sigilmap{$_} };
1180 $arrays{"${var}${seg}"} = 1;
1182 #print STDERR "Marking ${var}${seg}\n";
1183 push @
{ $code{$seg}->{code
} }, <<DIMARR;
1184 # Set aside storage for Array $var
1186 \$P2 = new .ResizablePMCArray
1190 find_global \$P1, "BASICARR"
1191 \$P1["$var$seg"]= \$P3
1192 store_global "BASICARR", \$P1
1195 if ( $syms[NEXT] eq "," ) {
1200 elsif ( $syms[CURR
] eq "shared" ) {
1201 print "WARNING: SHARED keyword currently ignored\n";
1205 die "Unknown dim type: $syms[CURR] at source line $sourceline";
1210 sub parse_for
{ # for var = start to finish [step increment]
1211 my ( $endexpr, $stepexpr, @stepcode );
1216 # The initial assignment. Type of course will be a float.
1217 ( $result, $type, @code ) = EXPRESSION
( { lhs
=> 1, assign
=> 1 } );
1218 push @
{ $code{$seg}->{code
} }, @code;
1220 die "TO expected at source line $sourceline" unless ( $syms[CURR
] ne "to" );
1224 # The destination value
1225 ( $endexpr, $type, @code ) = EXPRESSION
();
1228 if ( $syms[CURR
] eq "step" ) {
1229 ( $stepexpr, $type, @stepcode ) = EXPRESSION
();
1234 $main::code
{$main::seg
}->{declarations
}->{"FORLOOP_END_$forloop"} = 1;
1235 $main::code
{$main::seg
}->{declarations
}->{"FORLOOP_STEP_$forloop"} = 1;
1236 push @
{ $code{$seg}->{code
} }, <<COND;
1237 @code FORLOOP_END_$forloop= $endexpr
1239 FORLOOP_STEP_$forloop= $stepexpr
1241 gt FORLOOP_STEP_$forloop, 0.0, FOR_GT_$forloop
1242 lt $result, FORLOOP_END_$forloop, AFTER_NEXT_$forloop
1243 goto FOR_LOOP_BODY_$forloop
1245 gt $result, FORLOOP_END_$forloop, AFTER_NEXT_$forloop
1246 FOR_LOOP_BODY_$forloop:
1250 push @
{ $fors[$scopes] }, { var
=> $result, num
=> $forloop, inc
=> $stepexpr };
1253 sub parse_next
{ # next [a[,b[,c]...]
1255 my ( $var, $vartype, $ovar );
1257 $ps = pop @
{ $fors[$scopes] };
1259 if ( $type[CURR
] ne "BARE" ) { # next (no variable)
1260 push @
{ $code{$seg}->{code
} }, <<NEXT;
1261 add $ps->{var}, $ps->{var}, FORLOOP_STEP_$ps->{num}
1263 AFTER_NEXT_$ps->{num}: noop
1268 push @
{ $code{$seg}->{code
} }, <<NEXT;
1269 add $ps->{var}, $ps->{var}, FORLOOP_STEP_$ps->{num}
1271 AFTER_NEXT_$ps->{num}: noop
1273 if ( $syms[NEXT] eq "," ) {
1276 $ps = pop @
{ $fors[$scopes] };
1286 # Subroutines are disguised as user-defined functions,
1287 # except that there's no return value to deal with.
1289 if ( !exists $subs{ $syms[CURR
] } ) {
1290 die "Subroutine $syms[CURR] not found at line $sourceline\n";
1292 my $sub = $syms[CURR
];
1295 # print STDERR "Processing call $sub\n";
1296 ( $result, $type, @code ) = EXPRESSION
( { ignorecomma
=> 1 } );
1298 # print STDERR "Got back @code\n";
1299 push @
{ $code{$seg}->{code
} }, <<CALLSUB;
1306 # Deja-vu from functions.
1309 $funcname = $syms[CURR
];
1310 my $englishname = english_func
($funcname);
1311 $subs{$funcname} = $englishname;
1312 $functions{$funcname} = $englishname;
1314 $f = "_USERFUNC_$funcname";
1315 $f = changename
($f);
1316 $f =~ s/\$/_string/g;
1319 CALL_BODY
( $englishname, "SUB" );
1322 sub parse_function
{
1325 $funcname = $syms[CURR
];
1326 my $englishname = english_func
($funcname);
1327 $functions{$funcname} = $englishname;
1329 $f = "_USERFUNC_$funcname";
1330 $f = changename
($f);
1331 $f =~ s/\$/_string/g;
1334 CALL_BODY
( $englishname, "UF" );
1338 my ( $englishname, $prefix ) = @_;
1340 while ( feedme
() ) {
1341 last if ( $type[CURR
] eq "STMT" );
1342 last if ( $type[CURR
] eq "PUN" and $syms[CURR
] eq ")" );
1343 next if ( $type[CURR
] eq "PUN" );
1344 if ( $type[CURR
] eq "BARE" ) { # Variable name parameter
1346 if ( $syms[NEXT] eq "as" ) {
1347 feedme
(); # Get the as
1349 push( @params, $syms[CURR
], $a );
1351 elsif ( $syms[NEXT] eq "(" ) {
1353 while ( $syms[CURR
] ne ")" ) {
1356 push( @params, "()$a" );
1359 push( @params, $a ); # Always here?
1363 my $argcnt = @params;
1365 # The outer compiler will provide the framework for the
1366 # function call. We just have to unwind its arguments.
1367 $_ = scalar @params;
1368 push @
{ $code{$seg}->{code
} }, <<EOH;
1370 eq argc, $_, ${englishname}_ARGOK
1371 print "Function $englishname received "
1373 print " arguments expected $_\\n"
1374 _platform_shutdown()
1376 ${englishname}_ARGOK:
1378 $main::code
{$main::seg
}->{declarations
}->{$englishname} = 1;
1383 $t = "string" if $t eq "STRING";
1384 $t = "float" if $t eq "FLO";
1385 $_ = changename
($_);
1386 $_ =~ s/\$/_string/g;
1387 push @
{ $code{$seg}->{code
} }, qq{\t.param
$t $_\n};
1388 push @
{ $code{$seg}->{args
} }, $_;
1392 $_ = changename
($_);
1394 #print STDERR "Marking ${_}${seg}\n";
1395 $arrays{"${_}${seg}"} = 1;
1396 push @
{ $code{$seg}->{code
} }, <<PUSHARR;
1397 .param pmc array_$englishname
1398 find_global \$P1, "BASICARR"
1399 \$P1["${_}$seg"]= array_$englishname
1400 store_global "BASICARR", \$P1
1403 # push @{$code{$seg}->{args}}, $_;
1412 $seg =~ s/^_//; # Remove the _
1413 $seg =~ tr/A-Z/a-z/; # lowercase
1414 $seg =~ s/userfunc_//;
1415 push @
{ $code{$t}->{code
} }, "END_$t:\n";
1416 if ( exists $code{$t}->{args
} ) {
1417 foreach ( @
{ $code{$t}->{args
} } ) {
1418 push @
{ $code{$t}->{code
} }, "\t.return $_\t# Returning arg\n";
1421 push @
{ $code{$t}->{code
} }, "\t.return $seg\n";
1422 $seg = "_basicmain";
1428 goto &parse_endfunc
;
1431 sub parse_function_dispatch
{
1433 print FUNC
<<FUNCDISP;
1436 # User function dispatch routine
1442 foreach ( keys %functions ) {
1443 print FUNC
qq{\teq S0
, "$_", UF_
}, english_func
($_), qq{\n};
1446 print FUNC
<<FUNCEND;
1447 goto UF_DISPATCH_END
1449 #print "Ending user function, stack depth now "
1454 print FUNC
<<SUBDISP;
1458 foreach ( keys %subs ) {
1459 print FUNC
qq{\teq S0
, "$_", SUB_
$_\n};
1462 goto SUB_DISPATCH_END
1468 sub parse_struct_copy_dispatch
{
1470 print CODE
<<SCOPYDIS;
1473 # Structure copy dispatch routine
1474 # Call with S0 set to the type
1476 # Dest returned in P1
1479 foreach ( keys %usertypes ) {
1481 eq S0, "$_", COPY_$_
1485 print "Structure type of "
1487 print " not found\\n"
1488 _platform_shutdown()
1492 print CODE <<SCOPYDIS;
1494 # Structure create dispatch routine
1495 # Call with S0 set to the type
1496 # Dest returned in P0
1499 foreach ( keys %usertypes ) {
1505 print "Structure type of "
1507 print " not found\\n"
1508 _platform_shutdown()
1512 push @{ $code{$seg}->{code} }, <<RTB;
1513 # Several statements need to make branches
1514 # that are only discovered at runtime.
1517 if ($runtime_jump) {
1518 foreach ( sort keys %labels ) {
1519 push @
{ $code{$seg}->{code
} }, qq|\teq JUMPLABEL
, "$labels{$_}", $labels{$_}\n|;
1523 push @
{ $code{$seg}->{code
} }, <<RTBE;
1524 print "Runtime branch of "
1526 print " not found\\n"
1527 _platform_shutdown()
1532 sub parse_data_setup
{
1533 push @
{ $code{_data
}->{code
} }, <<DATAPREP;
1534 # Prepare the Read/Data stuff
1535 find_global \$P1, "RESTOREINFO"
1536 find_global \$P2, "READDATA"
1539 foreach my $ld (@data) {
1540 my $line = $ld->{line
};
1541 if ( length $line ) {
1542 push @
{ $code{_data
}->{code
} }, qq{\t\
$P1["$line"]= $counter\n};
1544 foreach ( @
{ $ld->{data
} } ) {
1545 my ( $t, $v ) = ( $_->{type
}, $_->{value
} );
1546 push @
{ $code{_data
}->{code
} }, <<ADDDATA;
1553 push @
{ $code{_data
}->{code
} }, <<DATADONE;
1554 store_global "RESTOREINFO", \$P1
1555 store_global "READDATA", \$P2
1561 return "FLO" if ( $var =~ /[!#%&]$/ );
1562 return "STRING" if ( $var =~ /\$$/ );
1570 # cperl-indent-level: 4
1573 # vim: expandtab shiftwidth=4: