| 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 |