File Coverage

blib/lib/Language/Basic/Statement.pm
Criterion Covered Total %
statement 285 503 56.6
branch 66 146 45.2
condition 7 18 38.8
subroutine 46 63 73.0
pod 0 6 0.0
total 404 736 54.8


line stmt bran cond sub pod time code
1             package Language::Basic::Statement;
2              
3             # Part of Language::Basic by Amir Karger (See Basic.pm for details)
4              
5             =pod
6              
7             =head1 NAME
8              
9             Language::Basic::Statement - Package to handle parsing and implementing single
10             BASIC statements.
11              
12             =head1 SYNOPSIS
13              
14             See L for the overview of how the Language::Basic module
15             works. This pod page is more technical.
16              
17             A Statement is something like 'GOTO 20' or 'PRINT "HELLO"'. A line of
18             BASIC code is made up of one or more Statements.
19              
20             # Create the statement from an LB::Token::Group and
21             # bless it to an LBS::* subclass
22             my $statement = new Language::Basic::Statement $token_group;
23             $statement->parse; # Parse the statement
24             $statement->implement; # Implement the statement
25              
26             # Return a string containing the Perl equivalent of the statement
27             $str = $statement->output_perl;
28              
29             =head1 DESCRIPTION
30              
31             Take a program like:
32              
33             5 LET A = 2
34              
35             10 IF A >= 3 THEN GOTO 20 ELSE PRINT "IT'S SMALLER"
36              
37             Line 5 has just one statement. Line 10 actually contains three. The first
38             is an IF statement, but the results of the THEN and the ELSE are entire
39             statements in themselves.
40              
41             Each type of statement in BASIC has an associated LB::Statement class.
42             For example, there's LB::Statement::Let and LB::Statement::If. (But no
43             LB::Statement::Then! Instead the "then" field of the LB::Statement::If
44             object will point to another statement. In the above program, it would
45             point to a LB::Statement::Goto.)
46              
47             Parsing a line of BASIC starts with removing the line number and lexing
48             the line, breaking it into Tokens which are held in an LB::Token::Group.
49             LB::Statement::new, refine, and parse, are all called with a Token::Group
50             argument. These methods gradually "eat" their way through the Tokens.
51              
52             LBS::new simply creates an LBS object. However, it then calls LBS::refine,
53             which looks at the first Token of the command and blesses the object to
54             the correct LBS::* subclass.
55              
56             Each LBS subclass then has (at least) the methods parse, implement,
57             and output_perl.
58              
59             The parse method goes through the text and digests it and sets various
60             fields in the object, which are used by implement and output_perl. The
61             implement method actually implements the BASIC command. The
62             output_perl method returns a string (with ; but not \n at the end) of the Perl
63             equivalent of the BASIC statement.
64              
65             =cut
66              
67 16     16   101 use strict;
  16         28  
  16         667  
68 16     16   87 use Language::Basic::Common;
  16         29  
  16         8662  
69              
70             # sub-packages
71             {
72             package Language::Basic::Statement::Data;
73             package Language::Basic::Statement::Def;
74             package Language::Basic::Statement::Dim;
75             package Language::Basic::Statement::End;
76             package Language::Basic::Statement::For;
77             package Language::Basic::Statement::Gosub;
78             package Language::Basic::Statement::Goto;
79             package Language::Basic::Statement::If;
80             package Language::Basic::Statement::Input;
81             package Language::Basic::Statement::Let;
82             package Language::Basic::Statement::Next;
83             package Language::Basic::Statement::On;
84             package Language::Basic::Statement::Print;
85             package Language::Basic::Statement::Read;
86             package Language::Basic::Statement::Rem;
87             package Language::Basic::Statement::Return;
88             }
89              
90             # Note: This sub first blesses itself to be class LB::Statement, but then
91             # class LB::Statement::refine, which blesses the object to a subclass
92             # depending on what sort of statement it is. The refined object is returned.
93             #
94             # Fields:
95             # next_statement - reference to next Statment on this Line. (or undef)
96             # Note that next doesn't point to an If's Then/Else sub-statements
97             #
98             # lvalue - an LB::Expression::Lvalue object, which represents an
99             # expression like X or AR(3+Q), which can be on the left hand
100             # side of an assignment statement
101             # expression - an LB::Expression:: subclass (e.g., Arithmetic or
102             # Relational.) Sometimes there are multiple expressions.
103             sub new {
104 193     193 0 290 my $class = shift;
105 193         290 my $token_group = shift;
106 193         270 my $line_num_ok = shift;
107 193         597 my $self = {
108             "next_statement" => undef,
109             "line_number" => undef,
110             };
111              
112 193         502 bless $self, $class;
113 193         598 $self->refine( $token_group, $line_num_ok );
114             } # end sub Language::Basic::Statement::new
115              
116             # Refine LB::Statement to the correct subclass
117             # I.e., Read the command this statement starts with, and bless the
118             # Statement to be a new subclass
119             sub refine {
120 193     193 0 329 my $self = shift;
121 193         245 my $token_group = shift;
122 193         230 my $line_num_ok = shift;
123 193 50 66     624 die "LBS::refine called with weird arg $line_num_ok" if
124             defined $line_num_ok && $line_num_ok ne "line_num_ok";
125              
126             # Valid BASIC statements
127 16         10378 use constant KEYWORDS =>
128             qw(DATA DEF DIM END FOR GOSUB GOTO IF INPUT
129 16     16   126 LET NEXT ON PRINT READ REM RETURN);
  16         59  
130             # TODO In theory, this would let us make STOP exactly synonymous
131             # with END, or CLEAR synonymous with CLS, etc.
132 193         481 my %keywords = map {$_, ucfirst(lc($_))} (KEYWORDS);
  3088         9111  
133              
134             # First word is a command, or a variable (implied LET statment)
135 193         996 my $tok = $token_group->lookahead;
136 193 50       448 Exit_Error("Empty statement?!") unless defined $tok;
137 193         218 my $command;
138 193         1344 (my $class = ref($tok)) =~ s/^Language::Basic::Token:://;
139 193 100 33     632 if ($class eq "Keyword") {
    100 33        
    100          
    50          
140 171         577 my $text = $tok->text;
141 171 50       465 if (exists $keywords{$text}) {
142 171         566 $token_group->eat;
143 171         583 $command = $keywords{$text};
144             } else {
145             # Statement started with, e.g., "TO" or "ELSE"
146 0         0 Exit_Error("Illegal reserved word '$text' at start of statement");
147             }
148              
149             } elsif ($class eq "Comment") {
150 6         13 $command = "Rem";
151             } elsif ($class eq "Identifier") {
152 14         24 $command = "Let";
153             # If we're in a THEN or ELSE, a line number means GOTO that line
154             } elsif ($line_num_ok &&
155             $class eq "Numeric_Constant" &&
156             $tok->text =~ /^\d+$/) {
157 2         4 $command = "Goto";
158             } else {
159 0         0 Exit_Error("Syntax Error: No Keyword or Identifier at start of statement!");
160             }
161 193         349 my $subclass = "Language::Basic::Statement::" . $command;
162             #print "New $subclass Statement\n";
163              
164 193         1985 bless $self, $subclass;
165             } # end sub Language::Basic::Statement::refine
166              
167             # By default, parsing does nothing. Useful, e.g., for REM
168 19     19 0 46 sub parse { }
169              
170             # By default, implementing does nothing. Useful, e.g., for REM
171 14     14 0 46 sub implement { }
172              
173             # By default, output an empty statement. Note that you need the semicolon,
174             # because we write a line label for each line.
175 0     0 0 0 sub output_perl {return ";";}
176              
177             sub set_line_number {
178 165     165 0 218 my $self = shift;
179 165         240 my $num = shift;
180 165         526 $self->{"line_number"} = $num;
181             }
182              
183             ######################################################################
184             # package Language::Basic::Statement::Data
185             # A DATA statement in a BASIC program.
186             {
187             package Language::Basic::Statement::Data;
188             @Language::Basic::Statement::Data::ISA = qw(Language::Basic::Statement);
189              
190             sub parse {
191 5     5   10 my $self = shift;
192 5         7 my $token_group = shift;
193 5         16 my $prog = &Language::Basic::Program::current_program;
194              
195             # The rest of the statement is things to dim and how big to dim them
196 5         9 do {
197 23         87 my $exp = new Language::Basic::Expression::Constant $token_group;
198 23         85 $prog->add_data($exp);
199             } while ($token_group->eat_if_string(","));
200             } # end sub Language::Basic::Statement::Data::parse
201              
202             # no sub implement nec.
203             # no sub output_perl nec.
204              
205             } # end package Language::Basic::Statement::Data
206              
207             ######################################################################
208             # package Language::Basic::Statement::Def
209             # A DEF statement in a BASIC program.
210             {
211             package Language::Basic::Statement::Def;
212             @Language::Basic::Statement::Def::ISA = qw(Language::Basic::Statement);
213 16     16   114 use Language::Basic::Common;
  16         29  
  16         12876  
214              
215             sub parse {
216 3     3   149 my $self = shift;
217 3         6 my $token_group = shift;
218              
219             # Function name (and args) is stuff up to equals
220             # Call LBE::Function::new with extra argument so it knows not to
221             # complain about an unknown function.
222 3 50       114 my $funcexp = new Language::Basic::Expression::Function
223             ($token_group, "defining")
224             or Exit_Error("Missing/Bad Function Name or Args in DEF!");
225 3 50       13 $token_group->eat_if_string("=") or Exit_Error("DEF missing '='!");
226              
227             # We don't actually want the LB::Expression, just the function
228             # we've declared.
229 3         84 my $func = $funcexp->{"function"};
230              
231             # Read function definition
232 3 50       21 my $exp = new Language::Basic::Expression::Arithmetic $token_group
233             or Exit_Error("Missing/Bad function definition in DEF!");
234              
235             # Now actually define the function
236 3         20 $func->define($exp);
237              
238 3         35 $self->{"function"} = $func;
239             # TODO note that output_perl may not work now
240             } # end sub Language::Basic::Statement::Def::parse
241              
242             # No sub implement: definition happens at compile time
243              
244             sub output_perl {
245 0     0   0 my $self = shift;
246 0         0 my $prog = &Language::Basic::Program::current_program;
247             # LB::Function::Defined object
248 0         0 my $func = $self->{"function"};
249              
250             # Function name
251 0         0 my $name = $func->output_perl;
252 0         0 my $desc = "{\n";
253 0         0 $desc .= "INDENT\n";
254              
255             # Function args
256 0         0 $desc .= "my (";
257 0         0 my @args = map {$_->output_perl} @{$func->{"arguments"}};
  0         0  
  0         0  
258 0         0 $desc .= join (", ", @args);
259 0         0 $desc .= ") = \@_;\n";
260              
261             # Function def
262 0         0 my $exp = $func->{"expression"}->output_perl;
263 0         0 $desc .= "return " . $exp . ";\n";
264 0         0 $desc .= "UNINDENT\n}";
265             # Tell program to print it out at the end of the perl script
266 0         0 $prog->need_sub($name, $desc);
267              
268 0         0 return (";"); # put empty statement in program here
269             } # end sub Language::Basic::Statement::Def::output_perl
270              
271             } # end package Language::Basic::Statement::Def
272              
273             ######################################################################
274             # package Language::Basic::Statement::Dim
275             # A DIM statement in a BASIC program.
276             {
277             package Language::Basic::Statement::Dim;
278             @Language::Basic::Statement::Dim::ISA = qw(Language::Basic::Statement);
279              
280             sub parse {
281 2     2   7 my $self = shift;
282 2         4 my $token_group = shift;
283              
284             # The rest of the statement is things to dim and how big to dim them
285 2         4 do {
286 3         26 my $exp = new Language::Basic::Expression::Lvalue $token_group;
287 3         5 push @{$self->{"arrays"}}, $exp;
  3         29  
288             # TODO test that dims are constants!
289             } while ($token_group->eat_if_string(","));
290             } # end sub Language::Basic::Statement::Dim::parse
291              
292             sub implement {
293 2     2   4 my $self = shift;
294 2         6 foreach (@{$self->{"arrays"}}) {
  2         10  
295             # The Lvalue's Array
296 3         9 my $array = $_->{"varptr"};
297 3         15 my @indices = $_->{"arglist"}->evaluate;
298 3         18 $array->dimension(@indices);
299             }
300             } # end sub Language::Basic::Statement::Dim::implement
301              
302             # no sub output_perl necessary
303              
304             } # end package Language::Basic::Statement::Dim
305              
306             ######################################################################
307             # package Language::Basic::Statement::End
308             # An END statement in a BASIC program.
309             {
310             package Language::Basic::Statement::End;
311             @Language::Basic::Statement::End::ISA = qw(Language::Basic::Statement);
312 16     16   103 use Language::Basic::Common;
  16         37  
  16         2596  
313              
314             sub implement {
315 7     7   26 my $prog = &Language::Basic::Program::current_program;
316 7         41 $prog->goto_line(undef);
317             } # end sub Language::Basic::Statement::End::implement
318              
319             sub output_perl {
320 0     0   0 return ("exit;");
321             } # end sub Language::Basic::Statement::End::output_perl
322              
323             } # end package Language::Basic::Statement::End
324              
325             ######################################################################
326             # package Language::Basic::Statement::For
327             # A FOR statement in a BASIC program.
328             {
329             package Language::Basic::Statement::For;
330             @Language::Basic::Statement::For::ISA = qw(Language::Basic::Statement);
331 16     16   103 use Language::Basic::Common;
  16         37  
  16         13284  
332              
333             sub parse {
334 10     10   25 my $self = shift;
335 10         20 my $token_group = shift;
336              
337             # Read variable name and "="
338 10 50       213 my $lvalue = new Language::Basic::Expression::Lvalue $token_group
339             or Exit_Error("Missing variable in FOR!");
340             # No strings allowed, at least for now
341 10 50       99 if ($lvalue->isa("Language::Basic::Expression::String")) {
342 0         0 Exit_Error("FOR statements can't use strings!");
343             }
344 10         58 $self->{"lvalue"} = $lvalue;
345              
346             # Read initialization value
347 10 50       42 $token_group->eat_if_string("=") or Exit_Error("FOR missing '='!");
348 10 50       136 $self->{"start"} =
349             new Language::Basic::Expression::Arithmetic::Numeric $token_group
350             or Exit_Error("Missing/Bad initialization expression in FOR!");
351 10 50       40 $token_group->eat_if_string("TO") or Exit_Error("FOR missing 'TO'!");
352              
353             # Until the token "step" OR the end of the statement, we're copying an
354             # expression, namely the variable's increment
355 10 50       57 $self->{"limit"} =
356             new Language::Basic::Expression::Arithmetic::Numeric $token_group
357             or Exit_Error("Missing/Bad limit expression in FOR!");
358              
359             # If there's anything left, it had better be a step...
360             # Otherwise, step = 1
361 10         19 my $step;
362 10 100       43 if ($token_group->eat_if_string("STEP")) {
363 2 50       11 $step = new Language::Basic::Expression::Arithmetic::Numeric
364             $token_group
365             or Exit_Error("Missing/Bad step expression in FOR!");
366             } else {
367 8 50       37 Exit_Error("Unknown stuff after limit expression in FOR!")
368             if $token_group->stuff_left;
369 8         53 my $foo = new Language::Basic::Token::Group;
370 8         30 $foo->lex("1");
371 8         41 $step = new Language::Basic::Expression::Arithmetic::Numeric $foo;
372             }
373 10         47 $self->{"step"} = $step;
374             } # end sub Language::Basic::Statement::For::parse
375              
376             sub implement {
377             # TODO BASIC doesn't check for start being greater than limit
378             # before doing a loop once. Might want to make a flag to do it.
379 12     12   22 my $self = shift;
380 12         40 my $prog = &Language::Basic::Program::current_program;
381 12         27 my $lvalue = $self->{"lvalue"};
382 12         38 my $var = $lvalue->variable;
383 12         54 $var->set($self->{"start"}->evaluate);
384             # Store this FOR statement, so that we can access it when we
385             # get to "NEXT var"
386 12         47 $prog->store_for($self);
387             } # end sub Language::Basic::Statement::For::implement
388              
389             # Outputs $var = start; and the beginning of a do {}
390             # We also have to set the step here, because we need to test in the loop
391             # whether it's positive or negative so we can know whether to test for
392             # being greater than or less than the limit!
393             sub output_perl {
394 0     0   0 my $self = shift;
395             # print var = start
396 0         0 my $lvalue = $self->{"lvalue"}->output_perl;
397 0         0 my $exp = $self->{"start"}->output_perl;
398 0         0 my $ret = join(" ", $lvalue, "=", $exp);
399 0         0 $ret .= ";\n";
400              
401             # set the step
402 0         0 my $step = $self->{"step"}->output_perl;
403 0         0 $lvalue =~ /\w+/;
404 0         0 my $vname = $&;
405 0         0 $ret .= join(" ", "\$step_for_$vname =", $step);
406 0         0 $ret .= ";\n";
407              
408             # set the limit
409 0         0 my $limit = $self->{"limit"}->output_perl;
410 0         0 $ret .= join(" ", "\$limit_for_$vname =", $limit);
411 0         0 $ret .= ";\n";
412              
413             # Now start the do loop
414 0         0 $ret .= "do {";
415 0         0 $ret .= "\nINDENT";
416 0         0 return $ret;
417             } # end sub Language::Basic::Statement::For::output_perl
418              
419             } # end package Language::Basic::Statement::For
420              
421             ######################################################################
422             # package Language::Basic::Statement::Gosub
423             # A GOSUB statement in a BASIC program.
424             {
425             package Language::Basic::Statement::Gosub;
426             @Language::Basic::Statement::Gosub::ISA = qw(Language::Basic::Statement);
427 16     16   111 use Language::Basic::Common;
  16         39  
  16         21249  
428              
429             sub parse {
430 5     5   10 my $self = shift;
431 5         9 my $token_group = shift;
432              
433             # The rest of the statement is an expression for the line to go to
434 5 50       26 $self->{"expression"} = new Language::Basic::Expression::Arithmetic $token_group
435             or Exit_Error("Bad expression in GOSUB!");
436             } # end sub Language::Basic::Statement::Gosub::parse
437              
438             sub implement {
439 7     7   11 my $self = shift;
440 7         20 my $prog = &Language::Basic::Program::current_program;
441 7         25 my $goto = $self->{"expression"}->evaluate;
442 7 50       45 if ($goto !~ /^\d+$/) {Exit_Error("Bad GOSUB: $goto")}
  0         0  
443             # Push the current statement onto the subroutine stack;
444 7         24 $prog->push_stack($self);
445             # Then GOTO the new line
446 7         31 $prog->goto_line($goto);
447             } # end sub Language::Basic::Statement::Gosub::implement
448              
449             sub output_perl {
450             # Perl script should print a label after the gosub. But before that,
451             # it pushes the label name onto the global gosub stack. THen when
452             # we hit the RETURN, we can pop the stack & goto back to this lable.
453 0     0   0 my $self = shift;
454 0         0 my $prog = &Language::Basic::Program::current_program;
455 0         0 my $exp = $self->{"expression"};
456 0         0 my $goto = $exp->output_perl;
457 0         0 my $ret = "";
458              
459             # Form the label name to return to
460 0         0 my $label = "AL" . $prog->current_line_number;
461 0         0 $ret .= "push \@Gosub_Stack, \"$label\";\n";
462              
463             # Form the label name to goto
464             # if it's just a number , don't use $tmp
465 0 0       0 if ($goto =~ /^\d+$/) {
466 0         0 $ret .= "goto L$goto;";
467             } else {
468             # Form the label name
469 0         0 $ret .= "\$Gosub_tmp = 'L' . " . $goto . ";\n";
470             # Go to it
471 0         0 $ret .= "goto \$Gosub_tmp;";
472             }
473              
474             # Write the return-to label after the goto
475 0         0 $ret .= "\n$label:;";
476              
477 0         0 return ($ret);
478             } # end sub Language::Basic::Statement::Gosub::output_perl
479             } # end package Language::Basic::Statement::Gosub
480              
481             ######################################################################
482             # package Language::Basic::Statement::Goto
483             # A GOTO statement in a BASIC program.
484             {
485             package Language::Basic::Statement::Goto;
486             @Language::Basic::Statement::Goto::ISA = qw(Language::Basic::Statement);
487 16     16   118 use Language::Basic::Common;
  16         31  
  16         13224  
488              
489             sub parse {
490 5     5   8 my $self = shift;
491 5         8 my $token_group = shift;
492              
493             # The rest of the statement is an expression for the line to go to
494 5 50       28 $self->{"expression"} = new Language::Basic::Expression::Arithmetic $token_group
495             or Exit_Error("Bad expression in GOTO!");
496             } # end sub Language::Basic::Statement::Goto::parse
497              
498             # Note that this sub allows "GOTO X+17/3", not just "GOTO 20"
499             sub implement {
500 3     3   7 my $self = shift;
501 3         8 my $prog = &Language::Basic::Program::current_program;
502 3         14 my $goto = $self->{"expression"}->evaluate;
503 3 50       19 if ($goto !~ /^\d+$/) {Exit_Error("Bad GOTO: $goto")}
  0         0  
504 3         10 $prog->goto_line($goto);
505             } # end sub Language::Basic::Statement::Goto::implement
506              
507             sub output_perl {
508 0     0   0 my $self = shift;
509             # if it's just a number , don't use $tmp
510 0         0 my $exp = $self->{"expression"};
511 0         0 my $goto = $exp->output_perl;
512 0         0 my $ret;
513 0 0       0 if ($goto =~ /^\d+$/) {
514 0         0 $ret = "goto L$goto;";
515             } else {
516             # Form the label name
517 0         0 $ret = "\$Goto_tmp = 'L' . " . $goto . ";\n";
518             # Go to it
519 0         0 $ret .= "goto \$Goto_tmp;";
520             }
521              
522 0         0 return ($ret);
523             } # end sub Language::Basic::Statement::Goto::output_perl
524             } # end package Language::Basic::Statement::Goto
525              
526             ######################################################################
527             # package Language::Basic::Statement::If
528             # An IF statement in a BASIC program.
529             {
530             package Language::Basic::Statement::If;
531             @Language::Basic::Statement::If::ISA = qw(Language::Basic::Statement);
532 16     16   101 use Language::Basic::Common;
  16         33  
  16         16045  
533              
534             sub parse {
535 28     28   56 my $self = shift;
536 28         40 my $token_group = shift;
537              
538             # Until the token "then", we're copying a conditional expression
539 28 50       142 my $exp = new Language::Basic::Expression::Logical_Or $token_group or
540             Exit_Error("Bad Condition in IF!");
541 28         166 $self->{"condition"} = $exp;
542 28 50       77 $token_group->eat_if_string("THEN") or Exit_Error("IF missing 'THEN'!");
543              
544             # Until the token "ELSE" or the end of the line, is one or more
545             # statements to do if the IF is true
546             # TODO we need to handle ELSE either within the same statement
547             # as the last THEN statement *OR* at the beginning of a statement.
548             # Also nested IFs?
549              
550             # Take everything up to ELSE into a separate Token::Group &
551             # call parsing with that so that other parse routines can complain if
552             # there's something left in their token_group. Right now, they'll have
553             # problem with ELSE token
554             # TODO need a Token::Group::split method or some such
555 28         126 my $t1 = new Language::Basic::Token::Group;
556 28         98 $t1->slurp($token_group, "ELSE");
557              
558             # Call new with an extra arg so it knows it's parsing a THEN/ELSE.
559             # That way, "THEN 20" gets parsed like "THEN GOTO 20"
560 28 50       85 my $then = new Language::Basic::Statement $t1, "line_num_ok" or
561             Exit_Error("No statement found after THEN");
562 28         87 $then->parse($t1);
563 28         56 my $oldst = $then;
564             # Eat [: Statement]*
565 28         86 while (defined($t1->eat_if_class("Statement_End"))) {
566             # Plain line number is only allowed in the *first* THEN/ELSE statement
567 0         0 my $st = new Language::Basic::Statement $t1;
568 0         0 $st->parse($t1);
569 0         0 $oldst->{"next_statement"} = $st;
570 0         0 $oldst = $st;
571             }
572             # Make sure we don't do the ELSE after the THEN!
573 28         50 $oldst->{"next_statement"} = undef;
574              
575             # If there's anything left in $token_group, it's the ELSE.
576 28         47 my $else;
577 28 100       76 if (defined($token_group->eat_if_string("ELSE"))) {
578             # Use up all the leftover tokens
579 8 50       30 $else = new Language::Basic::Statement $token_group, "line_num_ok" or
580             Exit_Error("No statement found after THEN");
581 8         27 $else->parse ($token_group);
582 8         13 $oldst = $else;
583 8         27 while (defined($token_group->eat_if_class("Statement_End"))) {
584 0         0 my $st = new Language::Basic::Statement $token_group;
585 0         0 $st->parse($token_group);
586 0         0 $oldst->{"next_statement"} = $st;
587 0         0 $oldst = $st;
588             }
589 8         16 $oldst->{"next_statement"} = undef;
590 8 50       29 Exit_Error("Unknown stuff after ELSE statement(s)") if
591             $token_group->stuff_left;
592             } else {
593 20 50       60 Exit_Error("Unknown stuff after THEN statement(s)") if
594             $token_group->stuff_left;
595             }
596              
597 28         68 $self->{"then_s"} = $then;
598 28         206 $self->{"else_s"} = $else; # may be undef
599             } # end sub Language::Basic::Statement::If::parse
600              
601             # Need to set line numbers for THEN and ELSE statements, so we can't
602             # use the default LBS::set_line_number
603             sub set_line_number {
604 28     28   49 my $self = shift;
605 28         45 my $num = shift;
606 28         59 $self->{"line_number"} = $num;
607 28         63 foreach ("then_s", "else_s") {
608 56         105 my $st = $self->{"$_"};
609 56         152 while (defined $st) {
610 36         103 $st->set_line_number($num);
611 36         118 $st = $st->{"next_statement"};
612             }
613             }
614             }
615              
616             sub implement {
617 30     30   38 my $self = shift;
618 30         71 my $prog = &Language::Basic::Program::current_program;
619              
620 30 100       115 if ($self->{"condition"}->evaluate) {
621 21         131 $prog->{"next_statement"} = $self->{"then_s"};
622             } else {
623             # This may be undef, in which case, code will just continue to next line
624 9         48 $prog->{"next_statement"} = $self->{"else_s"};
625             }
626             } # end sub Language::Basic::Statement::If::implement
627              
628             sub output_perl {
629 0     0   0 my $self = shift;
630 0         0 my $ret = "if (";
631 0         0 $ret .= $self->{"condition"}->output_perl;
632 0         0 $ret .= ") {\n";
633 0         0 $ret .= "INDENT";
634 0         0 my $st = $self->{"then_s"};
635 0         0 do {
636 0         0 $ret .= "\n" . $st->output_perl;
637             } while (defined ($st = $st->{"next_statement"}));
638              
639 0 0       0 if (defined $self->{"else_s"}) {
640             # TODO only double-\n if there's a long THEN
641 0         0 $ret .= "\n\nUNINDENT";
642 0         0 $ret .= "\n} else {\n";
643 0         0 $ret .= "INDENT";
644 0         0 $st = $self->{"else_s"};
645 0         0 do {
646 0         0 $ret .= "\n" . $st->output_perl;
647             } while (defined ($st = $st->{"next_statement"}));
648             }
649 0         0 $ret .= "\nUNINDENT";
650 0         0 $ret .= "\n}";
651              
652 0         0 return ($ret);
653             } # end sub Language::Basic::Statement::If::output_perl
654              
655             } # end package Language::Basic::Statement::If
656              
657             ######################################################################
658             # package Language::Basic::Statement::Input
659             # An INPUT statement in a BASIC program.
660             {
661             package Language::Basic::Statement::Input;
662             @Language::Basic::Statement::Input::ISA = qw(Language::Basic::Statement);
663 16     16   111 use Language::Basic::Common;
  16         34  
  16         16880  
664              
665             sub parse {
666 0     0   0 my $self = shift;
667 0         0 my $token_group = shift;
668              
669             # Handle INPUT "FOO"; BAR, BLAH
670             # TODO I should really just try to call LBE::Constant::String and not
671             # do anything if it returns undef. But currently that warns that what
672             # we're trying to input isn't a quoted string if there's not quotation
673             # mark.
674 0 0       0 if ($token_group->lookahead->
675             isa("Language::Basic::Token::String_Constant")) {
676 0         0 my $prompt = new
677             Language::Basic::Expression::Constant::String $token_group;
678 0         0 $self->{"to_print"} = $prompt;
679 0 0       0 $token_group->eat_if_string(";") or
680             Exit_Error("Expected ';' after INPUT prompt!");
681             }
682              
683             # The rest of the inputs will be separated by commas
684 0         0 do {
685 0 0       0 my $exp = new Language::Basic::Expression::Lvalue $token_group
686             or Exit_Error("Incorrect INPUT!");
687 0         0 push @{$self->{"lvalues"}}, $exp;
  0         0  
688             } while $token_group->eat_if_string(",");
689             } # end sub Language::Basic::Statement::Input::parse
690              
691             sub implement {
692 0     0   0 my $self = shift;
693 0 0       0 TRY_AGAIN:
694             # Print a prompt, if it exists
695             my $to_print = (exists $self->{"to_print"} ?
696             $self->{"to_print"}->evaluate :
697             "");
698 0         0 print "$to_print? ";
699              
700             # TODO set Program's "column" field to zero!
701             # Read the variables
702 0         0 my $in = <>;
703 0         0 chomp($in);
704             # TODO read Constants (String or Numeric) followed by commas if nec.
705             # TODO type checking: make sure a string is a string
706             # (this might be done by a different part of the program)
707             # TODO Use "EXTRA IGNORED?" to let user know they need to quote commas?
708 0         0 my @ins = split(/\s*,\s*/, $in);
709 0 0       0 if (@ins != @{$self->{"lvalues"}}) {
  0         0  
710 0         0 print "Not enough inputs! Try whole statement again...\n";
711             # Can't have a BASIC interpreter without a GOTO!
712 0         0 goto TRY_AGAIN;
713             }
714              
715             # set the variables to the inputted value
716 0         0 foreach (@{$self->{"lvalues"}}) {
  0         0  
717 0         0 my $var = $_->variable; # LB::Variable object
718             # TODO Print "??" if they don't input enough.
719 0         0 my $value = shift @ins;
720 0         0 $var->set($value);
721             }
722              
723 0         0 return $self->{"next_statement"};
724             } # end sub Language::Basic::Statement::Input::implement
725              
726             sub output_perl {
727 0     0   0 my $self = shift;
728             # Print the prompt
729 0         0 my $ret = "print ";
730 0 0       0 if (exists $self->{"to_print"}) {
731 0         0 $ret .= $self->{"to_print"}->output_perl;
732 0         0 $ret .= " . "; # concat with the ? below
733             }
734 0         0 $ret .= "\"? \";\n";
735              
736             # Input the line
737 0         0 $ret .= "\$input_tmp = <>;\n";
738 0         0 $ret .= "chomp(\$input_tmp);\n";
739              
740             # Set the values
741 0         0 my @lvalues = map {$_->output_perl} @{$self->{"lvalues"}};
  0         0  
  0         0  
742 0         0 my $tmp = join(", ", @lvalues);
743             # Make the code a bit simpler for just one input
744 0         0 my $multi = @lvalues > 1;
745 0 0       0 if ($multi) {
746 0         0 $ret .="($tmp) = split(/\\s*,\\s*/, \$input_tmp);";
747             } else {
748 0         0 $ret .="$tmp = \$input_tmp;";
749             }
750              
751 0         0 return $ret;
752             } # end sub Language::Basic::Statement::Input::output_perl
753              
754             } # end package Language::Basic::Statement::Input
755              
756             ######################################################################
757             # package Language::Basic::Statement::Let
758             # A LET statement in a BASIC program.
759             {
760             package Language::Basic::Statement::Let;
761             @Language::Basic::Statement::Let::ISA = qw(Language::Basic::Statement);
762 16     16   134 use Language::Basic::Common;
  16         41  
  16         6932  
763              
764             sub parse {
765 15     15   29 my $self = shift;
766 15         25 my $token_group = shift;
767              
768             # Read variable name and "="
769 15 50       89 my $lvalue = new Language::Basic::Expression::Lvalue $token_group
770             or Exit_Error("Missing variable in LET!");
771 15         61 $self->{"lvalue"} = $lvalue;
772              
773             # The rest of the statement is an expression to set the variable equal to
774 15 50       370 $token_group->eat_if_string("=") or Exit_Error("LET missing '='!");
775 15 50       116 $self->{"expression"} =
776             new Language::Basic::Expression::Arithmetic $token_group
777             or Exit_Error("Missing right side expression in LET!");
778             } # end sub Language::Basic::Statement::Let::parse
779              
780             sub implement {
781 49     49   64 my $self = shift;
782 49         76 my $lvalue = $self->{"lvalue"};
783 49         170 my $var = $lvalue->variable;
784 49         181 my $value = $self->{"expression"}->evaluate;
785 49         179 $var->set($value);
786              
787 49         192 return $self->{"next_statement"};
788             } # end sub Language::Basic::Statement::Let::implement
789              
790             sub output_perl {
791 0     0   0 my $self = shift;
792 0         0 my $lvalue = $self->{"lvalue"}->output_perl;
793 0         0 my $exp = $self->{"expression"}->output_perl;
794 0         0 my $ret = join(" ", $lvalue, "=", $exp);
795 0         0 $ret .= ";";
796              
797 0         0 return ($ret);
798             } # end sub Language::Basic::Statement::Let::output_perl
799              
800             } # end package Language::Basic::Statement::Let
801              
802             ######################################################################
803             # package Language::Basic::Statement::Next
804             # A NEXT statement in a BASIC program.
805             {
806             package Language::Basic::Statement::Next;
807             @Language::Basic::Statement::Next::ISA = qw(Language::Basic::Statement);
808 16     16   102 use Language::Basic::Common;
  16         38  
  16         8840  
809              
810             sub parse {
811 10     10   22 my $self = shift;
812 10         19 my $token_group = shift;
813              
814 10 50       52 my $lvalue = new Language::Basic::Expression::Lvalue $token_group
815             or Exit_Error("Incorrect NEXT!");
816             # No strings allowed, at least for now
817 10 50       78 if ($lvalue->variable->isa("Language::Basic::Variable::String")) {
818 0         0 Exit_Error("NEXT statements can't use strings!");
819             }
820 10         61 $self->{"lvalue"} = $lvalue;
821             } # end sub Language::Basic::Statement::Next::parse
822              
823             sub implement {
824 60     60   78 my $self = shift;
825 60         153 my $prog = &Language::Basic::Program::current_program;
826              
827             # Get the "FOR var" statement that this NEXT refers to.
828 60         173 my $for_statement = $prog->get_for($self);
829 120         438 my ($limit,$step) =
830 60         95 map {$for_statement->{$_}->evaluate} qw (limit step);
831              
832             # Increment the variable
833 60         99 my $lvalue = $self->{"lvalue"};
834 60         208 my $var = $lvalue->variable;
835 60         171 my $value = $var->value;
836 60         84 $value += $step;
837 60         160 $var->set($value);
838             #print "next: '$value' '$limit' '$step' '$goto'\n";
839              
840             #test
841 60 100       292 my $done = ($step > 0 ? $value > $limit : $value < $limit);
842 60 100       180 unless ($done) {
843             # Go to the statement *after* the statement the FOR started on
844 48         139 $prog->goto_after_statement($for_statement);
845             }
846             } # end sub Language::Basic::Statement::Next::implement
847              
848             # Outputs $var increment and end of do{}until block
849             sub output_perl {
850 0     0   0 my $self = shift;
851             # Increment variable
852 0         0 my $lvalue = $self->{"lvalue"};
853 0         0 my $lv = $lvalue->output_perl;
854 0         0 $lv =~ /\w+/;
855 0         0 my $vname = $&;
856             # Note that we add step_for even if it's negative.
857 0         0 my $ret = join(" ", $lv, "+=", "\$step_for_$vname");
858 0         0 $ret .= ";\n";
859 0         0 $ret .= "UNINDENT\n";
860              
861             # End the do {} block
862 0         0 $ret .= "} ";
863              
864             # test the until
865 0         0 $ret .= "until (\$step_for_$vname > 0 ? ";
866 0         0 $ret .= $lv . " > \$limit_for_$vname : " .$lv. " < \$limit_for_$vname);";
867 0         0 return $ret;
868             } # end sub Language::Basic::Statement::Next::output_perl
869              
870             } # end package Language::Basic::Statement::Next
871              
872             ######################################################################
873             # package Language::Basic::Statement::On
874             # An ON statement in a BASIC program.
875             {
876             package Language::Basic::Statement::On;
877             @Language::Basic::Statement::On::ISA = qw(Language::Basic::Statement);
878 16     16   110 use Language::Basic::Common;
  16         32  
  16         30264  
879              
880             sub parse {
881 2     2   6 my $self = shift;
882 2         5 my $token_group = shift;
883              
884 2 50       14 $self->{"expression"} =
885             new Language::Basic::Expression::Arithmetic $token_group
886             or Exit_Error("Missing Arith. Exp. in ON!");
887             # Until the token "GOSUB/GOTO", we're copying an arithmetic expression
888 2         9 my $tok = $token_group->eat_if_class("Keyword");
889 2 50 33     16 defined $tok and $tok->text =~ /^GO(SUB|TO)$/
890             or Exit_Error("ON missing GOSUB/GOTO!");
891 2         8 my $type = $tok->text;
892 2         6 $self->{"type"} = $type;
893              
894             # The rest of the inputs will be separated by commas
895 2         5 do {
896 6 50       24 my $exp =
897             new Language::Basic::Expression::Arithmetic::Numeric $token_group
898             or Exit_Error("Incorrect Expression in ON ... $type!");
899 6         9 push @{$self->{"gotos"}}, $exp;
  6         39  
900             } while $token_group->eat_if_string(",");
901             } # end sub Language::Basic::Statement::On::parse
902              
903             sub implement {
904 6     6   8 my $self = shift;
905 6         14 my $prog = &Language::Basic::Program::current_program;
906 6         14 my $type = $self->{"type"};
907 6         37 my $value = $self->{"expression"}->evaluate;
908 6 50 33     38 if ($value !~ /^\d+$/ || $value > @{$self->{"gotos"}}) {
  6         29  
909 0         0 Exit_Error("Bad value in ON: $value")
910             }
911              
912 6         10 my $goto = ${$self->{"gotos"}}[$value-1]->evaluate;
  6         25  
913 6 50       26 if ($goto !~ /^\d+$/) {Exit_Error("Bad GOTO in ON: $goto")}
  0         0  
914 6         21 $prog->goto_line($goto);
915              
916             # And if it's a GOSUB, push the program stack so we can get back
917 6 50       32 $prog->push_stack($self) if $type eq "GOSUB";
918             } # end sub Language::Basic::Statement::On::implement
919              
920             sub output_perl {
921 0     0   0 my $self = shift;
922 0         0 my $prog = &Language::Basic::Program::current_program;
923 0         0 my $type = $self->{"type"};
924              
925             # List of lines to go to
926 0         0 my @gotos = map {$_->output_perl} @{$self->{"gotos"}};
  0         0  
  0         0  
927 0         0 my $ret = "\@Gotos_tmp = map {'L' . ";
928             # If there's any expressions, be more fancy
929 0 0       0 if (grep {$_ !~ /^\d+$/} @gotos) {$ret .= "eval "}
  0         0  
  0         0  
930 0         0 $ret .= "\$_} (";
931 0         0 $ret .= join(", ", @gotos);
932 0         0 $ret .= ");\n";
933              
934             # Index in the list
935 0         0 my $branch = $self->{"expression"}->output_perl;
936 0         0 $ret .= "\$index_tmp = ";
937 0         0 $ret .= $branch . ";\n";
938              
939             # Form the label name to return to
940 0         0 my $label;
941 0 0       0 if ($type eq "GOSUB") {
942 0         0 $label = "AL" . $prog->current_line_number;
943 0         0 $ret .= "push \@Gosub_Stack, \"$label\";\n";
944             }
945              
946             # Go to it
947 0         0 $ret .= "goto \$Gotos_tmp[\$index_tmp-1];";
948              
949             # Write the return-to label after the goto
950 0 0       0 if ($type eq "GOSUB") {
951 0         0 $ret .= "\n$label:;";
952             }
953              
954 0         0 return ($ret);
955             } # end sub Language::Basic::Statement::On::output_perl
956              
957             } # end package Language::Basic::Statement::On
958              
959             ######################################################################
960             # package Language::Basic::Statement::Print
961             # A PRINT statement in a BASIC program.
962             {
963             package Language::Basic::Statement::Print;
964             @Language::Basic::Statement::Print::ISA = qw(Language::Basic::Statement);
965 16     16   148 use Language::Basic::Common;
  16         46  
  16         22229  
966              
967             sub parse {
968 80     80   132 my $self = shift;
969 80         256 my $token_group = shift;
970             # empty print statement?
971 80 100       281 unless ($token_group->stuff_left) {
972 2         8 $token_group = new Language::Basic::Token::Group;
973 2         8 $token_group->lex('""');
974             }
975              
976 80         142 my $endchar;
977 80         114 do {
978 88 50       486 my $exp = new Language::Basic::Expression::Arithmetic $token_group
979             or Exit_Error("Weird thing to print in PRINT statement!");
980 88         128 my $tok;
981 88 100       269 if ($tok = $token_group->eat_if_class("Separator")) {
    50          
982             # It's a comma or semicolon
983 73         244 $endchar = $tok->text;
984             } elsif (! $token_group->stuff_left) {
985 15         30 $endchar = "";
986             } else {
987 0         0 Exit_Error("Unexpected extra thing in PRINT statement!");
988             }
989 88         236 push @{$self->{"to_print"}}, [$exp , $endchar];
  88         786  
990              
991             } while ($token_group->stuff_left);
992             } # end sub Language::Basic::Statement::Print::parse
993              
994             sub implement {
995             # TODO More than one expression to print! Use an array of LB::Expressions
996 82     82   115 my $self = shift;
997 82         185 my $prog = &Language::Basic::Program::current_program;
998 82         112 foreach my $thing (@{$self->{"to_print"}}) {
  82         206  
999 90         196 my ($exp, $endchar) = @$thing;
1000 90         314 my $string = $exp->evaluate;
1001              
1002             # Never print after column 70
1003             # But "print ''" shouldn't print two \n's!
1004 90 50 33     448 if ($prog->{"column"} >= 70 && length($string)) {
1005 0         0 print "\n";
1006 0         0 $prog->{"column"} = 0;
1007             }
1008              
1009             # Print the string
1010 90         492 print $string;
1011 90         176 $prog->{"column"} += length($string);
1012              
1013             # Handle the thing after the string
1014 90 50       326 if ($endchar eq ",") {
    100          
1015             # Paraphrased from a BASIC manual:
1016             # If the printhead (!) is at char 56 or more after the expression,
1017             # print \n, else print spaces until the printhead is at the
1018             # beginning of the next 14-character field
1019 0 0       0 if ($prog->{"column"} >= 56) {
1020 0         0 print "\n";
1021 0         0 $prog->{"column"} = 0;
1022             } else {
1023 0         0 my $c = 14 - $prog->{"column"} % 14;
1024 0         0 print (" " x $c);
1025 0         0 $prog->{"column"} += $c;
1026             }
1027             } elsif ($endchar eq ";") {
1028             # In BASIC, you always print a space after numbers, but not
1029             # after strings. That seems a bit dumb, but that's how it is.
1030 70 100       393 if (ref($exp) =~ /::Numeric$/) {
1031 50         66 print " ";
1032 50         474 $prog->{"column"}++;
1033             }
1034             } else {
1035 20         1000 print "\n";
1036 20         147 $prog->{"column"} = 0;
1037             }
1038             } # end foreach loop over expressions to print
1039             } # end sub Language::Basic::Statement::Print::implement
1040              
1041             sub output_perl {
1042 0     0   0 my $self = shift;
1043 0         0 my $ret = "print(";
1044 0         0 my @to_print = @{$self->{"to_print"}};
  0         0  
1045             # TODO create a Print subroutine that takes exp/endchar array & prints
1046             # in the exact way BASIC does. (How do we make that subroutine print
1047             # a space after numerical expressions?!)
1048 0         0 while (my $thing = shift @to_print) {
1049 0         0 my ($exp, $endchar) = @$thing;
1050 0         0 my $string = $exp->output_perl;
1051 0         0 $ret .= $string;
1052 0 0       0 $ret .= ",' '" if ref($exp) =~ /Numeric$/;
1053 0 0       0 if ($endchar eq ",") {
    0          
1054 0         0 $ret .= ", \"\\t\"";
1055             } elsif ($endchar eq "") {
1056 0         0 $ret .= ", \"\\n\"";
1057             # This had better be the last exp!
1058 0 0       0 warn "Internal error: obj. w/out endchar isn't last!" if @to_print;
1059             } # otherwise it's ';', we hope
1060              
1061 0 0       0 if (@to_print) {
1062 0         0 $ret .= ", ";
1063             } else {
1064 0         0 $ret .= ");";
1065             }
1066             }
1067              
1068 0         0 return ($ret);
1069             } # end sub Language::Basic::Statement::Print::output_perl
1070              
1071             } # end package Language::Basic::Statement::Print
1072              
1073             ######################################################################
1074             # package Language::Basic::Statement::Read
1075             # A READ statement in a BASIC program.
1076             {
1077             package Language::Basic::Statement::Read;
1078             @Language::Basic::Statement::Read::ISA = qw(Language::Basic::Statement);
1079              
1080             sub parse {
1081 3     3   7 my $self = shift;
1082 3         6 my $token_group = shift;
1083              
1084             # The rest of the statement is lvalues to read in
1085 3         5 do {
1086 6 50       61 my $exp = new Language::Basic::Expression::Lvalue $token_group
1087             or Exit_Error("Incorrect READ statement!");
1088 6         11 push @{$self->{"lvalues"}}, $exp;
  6         63  
1089             } while $token_group->eat_if_string(",");
1090             } # end sub Language::Basic::Statement::Read::parse
1091              
1092             sub implement {
1093 20     20   27 my $self = shift;
1094 20         81 my $prog = &Language::Basic::Program::current_program;
1095 20         42 foreach (@{$self->{"lvalues"}}) {
  20         53  
1096 23         73 my $var = $_->variable;
1097 23         93 my $data = $prog->get_data();
1098             # Data will just be a LBE::Constant, but we still have to &evaluate it
1099 23         64 my $value = $data->evaluate;
1100 23         72 $var->set($value);
1101             }
1102             } # end sub Language::Basic::Statement::Read::implement
1103              
1104             sub output_perl {
1105 0     0   0 my $self = shift;
1106             # Set a list...
1107 0         0 my $ret = "(";
1108 0         0 my @lvalues = map {$_->output_perl} @{$self->{"lvalues"}};
  0         0  
  0         0  
1109 0         0 $ret .= join(", ", @lvalues);
1110 0         0 $ret .= ") = ";
1111              
1112             # equal to a splice from @Data
1113 0         0 my $num = @lvalues;
1114 0         0 $ret .= "splice(\@Data, 0, $num);";
1115              
1116 0         0 return ($ret);
1117             } # end sub Language::Basic::Statement::Read::output_perl
1118              
1119             } # end package Language::Basic::Statement::Read
1120              
1121             ######################################################################
1122             # package Language::Basic::Statement::Rem
1123             # A REM statement in a BASIC program.
1124             {
1125             package Language::Basic::Statement::Rem;
1126             @Language::Basic::Statement::Rem::ISA = qw(Language::Basic::Statement);
1127             sub parse {
1128             # Eat the whole line (including colons if any)
1129 6     6   11 my $self = shift;
1130 6         12 my $token_group = shift;
1131 6         24 my $tok = $token_group->eat_if_class("Comment");
1132             # Use original text to retain spaces and case.
1133 6         28 my $text = $tok->{"original_text"};
1134 6         27 $text =~ s/REM//;
1135 6         69 $self->{"comment"} = $text;
1136             } # end sub Language::Basic::Statement::Rem::parse
1137              
1138             sub output_perl {
1139 0     0   0 my $self = shift;
1140             # Need to have a semicolon because the line label requires a
1141             # statement after it. (And we need a line label in case we GOTO this line
1142 0         0 my $ret = "; # " . $self->{"comment"};
1143 0         0 return $ret;
1144             } # end sub Language::Basic::Statement::Rem::output_perl
1145              
1146             } # end package Language::Basic::Statement::Rem
1147              
1148             ######################################################################
1149             # package Language::Basic::Statement::Return
1150             # A RETURN statement in a BASIC program.
1151             {
1152             package Language::Basic::Statement::Return;
1153             @Language::Basic::Statement::Return::ISA = qw(Language::Basic::Statement);
1154 16     16   151 use Language::Basic::Common;
  16         44  
  16         4234  
1155              
1156             # No need to have a sub parse
1157              
1158             sub implement {
1159 13     13   19 my $self = shift;
1160 13         35 my $prog = &Language::Basic::Program::current_program;
1161 13 50       40 my $gosub = $prog->pop_stack or
1162             Exit_Error("RETURN without GOSUB");
1163             # Start at the statement *after* the GOSUB statement
1164 13         42 $prog->goto_after_statement($gosub);
1165             } # end sub Language::Basic::Statement::Return::implement
1166              
1167             sub output_perl {
1168 0     0     my $ret = "\$Return_tmp = pop \@Gosub_Stack;\n";
1169 0           $ret .= "goto \$Return_tmp;";
1170              
1171 0           return ($ret);
1172             } # end sub Language::Basic::Statement::Return::output_perl
1173              
1174             } # end package Language::Basic::Statement::Return
1175              
1176             1; # end of package Language::Basic::Statement