File Coverage

blib/lib/Language/Basic/Expression.pm
Criterion Covered Total %
statement 335 458 73.1
branch 94 146 64.3
condition 23 30 76.6
subroutine 37 50 74.0
pod 0 1 0.0
total 489 685 71.3


line stmt bran cond sub pod time code
1             package Language::Basic::Expression;
2             # Part of Language::Basic by Amir Karger (See Basic.pm for details)
3              
4             =pod
5              
6             =head1 NAME
7              
8             Language::Basic::Expression - Package to handle string, numeric, and
9             boolean expressions.
10              
11             =head1 SYNOPSIS
12              
13             See L for the overview of how the Language::Basic module
14             works. This pod page is more technical.
15              
16             # Given an LB::Token::Group, create an expression I parse it
17             my $exp = new LB::Expression::Arithmetic $token_group;
18             # What's the value of the expression?
19             print $exp->evaluate;
20             # Perl equivalent of the BASIC expression
21             print $exp->output_perl;
22              
23             Expressions are basically the building blocks of Statements, in that every
24             BASIC statement is made up of keywords (like GOTO, TO, STEP) and expressions.
25             So expressions include not just the standard arithmetic and boolean expressions
26             (like 1 + 2), but also lvalues (scalar variables or arrays), functions, and
27             constants. See L for details on the way expressions
28             are built.
29              
30             =head1 DESCRIPTION
31              
32             BASIC expressions are represented by various objects of subclasses of
33             Language::Basic::Expression. Most LB::Expressions are in turn made up of other
34             LB::Expressions. For example an LBE::Arithmetic may be made up of two
35             LBE::Multiplicative and a "plus". "Atoms" (indivisible LBE's) include
36             things like LBE::Constants and LBE::Lvalues (variables).
37              
38             =cut
39              
40 16     16   96 use strict;
  16         45  
  16         786  
41 16     16   87 use Language::Basic::Common;
  16         31  
  16         8841  
42              
43             # sub-packages
44             {
45             package Language::Basic::Expression::Logical_Or;
46             package Language::Basic::Expression::Logical_And;
47             package Language::Basic::Expression::Relational;
48              
49             package Language::Basic::Expression::Arithmetic;
50             package Language::Basic::Expression::Multiplicative;
51             package Language::Basic::Expression::Unary;
52              
53             package Language::Basic::Expression::Lvalue;
54             package Language::Basic::Expression::Arglist;
55             package Language::Basic::Expression::Function;
56             package Language::Basic::Expression::Constant;
57              
58             package Language::Basic::Expression::Numeric;
59             package Language::Basic::Expression::String;
60             package Language::Basic::Expression::Boolean;
61             }
62              
63             # No sub new. Each class must have its own
64              
65             # Most expressions have a "return type" that's String, Boolean, or Numeric.
66             # (Arglists don't, since they hold a list of expressions.)
67             #
68             # An arithmetic expression is a LBE::Arithmetic::Numeric if it's made up
69             # of LBE::Multiplicative::Numeric expressions, but LBE::Arithmetic::String
70             # if it's got a LBE::Unary::String in it. We never mix
71             # expression types (except within Arglists)
72             #
73             # This sub therefore blesses an object to its String/Numeric/Boolean subclass
74             # depending on the type of the sub-expression (and returns the newly blessed
75             # object.)
76             #
77             # Usually the sub-expression is itself an LB::Expression, but not always.
78             # We test for subexps of, e.g., LB::String rather than LBE::String,
79             # because we may be setting return type based on a LB::Variable::String or
80             # LB::Function::String, which aren't LB::Expressions.
81             #
82             # Arg0 is the thing to bless, arg1 is the subexp
83             sub set_return_type {
84 132     132 0 234 my $self = shift;
85 132         210 my $class = ref($self);
86             # If we already are blessed, don't rebless!
87 132         255 foreach (qw(String Numeric Boolean)) {
88 396 50       3657 return $self if $self->isa("Language::Basic::Expression::$_");
89             }
90              
91 132         224 my $subexp = shift;
92 132         170 my $type; # Return type
93 132         659 foreach (qw(String Numeric Boolean)) {
94             # LB::Function::String
95 242 100       1761 if ($subexp->isa("Language::Basic::$_")) {
96 132         181 $type = $_;
97 132         190 last;
98             }
99             }
100 132 50       328 unless (defined $type) {die "Error refining $class to ",ref($subexp),"\n";}
  0         0  
101              
102             #print "self, class, type is 1 $self 2 $class 3 $type\n";
103             # Note: "$class::$type" breaks!
104 132         275 my $subclass = $class . "::$type";
105             # TODO assert that class actually exists! E.g., call
106             # $self->isa(LBE)
107 132         448 bless $self, $subclass;
108             }
109              
110             ######################################################################
111              
112             =pod
113              
114             =head2 The LBE hierarchy
115              
116             A bunch of LBE subclasses represent various kinds of BASIC expressions.
117             These subclasses closely follow the BASIC syntax diagram.
118              
119             Expressions can be classified in two ways, which are sort of vertical and
120             horizontal. One classification method is what subexpressions (if any) an
121             expression is made of. For example, an Arith. Exp. is made up of one or more
122             Mult. Exps. connected by plus or minus signs, while a Mult. Exp. is made up of
123             one or more Unary Exps. This is a hierarchical (or vertical) distinction,
124             important for building up the tree of objects that represent a BASIC
125             expression.
126              
127             (Note that not all levels in the hierarchy have to be filled. We don't
128             bother making an Arith. Exp. which contains just one Mult. Exp. which contains
129             just one Unary Exp. Instead, we just use the Unary Exp. itself (when it's
130             safe to do so!)
131              
132             The second way of classifying expressions is by their return type. A String
133             Exp. is a string constant, a string variable, a string function, or some other
134             expression whose value when evaluated will be a string. A Numeric Exp.
135             evaluates to a number, and a Boolean to a True or False value. This
136             distinction is important for typechecking and finding syntax errors in BASIC
137             code. (Note that in BASIC -- unlike Perl or C -- you can't "cast" a boolean
138             value into an integer or string. This actually makes parsing more difficult.)
139              
140             Some expressions don't exactly fit any of these distinctions. For example, an
141             Arglist evaluates to a list of expressions, each of which may be Numeric or
142             Boolean.
143              
144              
145             =head2 subclass methods
146              
147             Each subclass has (at least) three methods:
148              
149             =over 4
150              
151             =item new
152              
153             The "new" method takes a class and a Token::Group (and possibly some other
154             args). It eats one or more Tokens from it, parsing them, creating a new object
155             of that class I, and setting various fields in that object, which it returns.
156             If the tokens don't match the class, "new" returns undef.
157              
158             If an expression contains just one subexpression often we'll just return the
159             subexpression. So if an Arith. Exp. contains just one Mult. Exp., we'll just
160             return the LBE::Multiplicative object and I an LBE::Arithmetic object.
161              
162             =item evaluate
163              
164             Actually calculates the value of the expression. For a string
165             or numeric constant or variable, that just means taking the stored value
166             of that object. For other Expressions, you actually need to do math.
167              
168             =item output_perl
169              
170             Gives a string with the Perl equivalent to a BASIC expression. "1+2" is
171             converted to "1+2", but "A" becomes "$a", "A$" becomes "$a_str", and
172             function calls may be even more complicated.
173              
174             =back
175              
176             =head2 LBE subclasses
177              
178             The hierarchical list of subclasses follows:
179              
180             =over 4
181              
182             =cut
183              
184             =item Arithmetic
185              
186             An arithmetic expression is a set of multiplicative expressions connected by
187             plus or minus signs. (String expressions can only be connected by plus,
188             which is the BASIC concatenation operator.)
189              
190             =cut
191              
192             # In BASIC, Arithmetic expressions can't contain Boolean expressions.
193             # However, parentheses can confuse things.
194             # LBE::Relational is one of:
195             # (1) LBE::Arithmetic Rel. Op. LBE::Arithmetic
196             # (2) (Logical Or)
197             # It calls LBE::Arithmetic::new with "maybe_boolean" sometimes, to tell
198             # LBEA::new that if it finds a (parenthesized) Boolean expression, it's
199             # just case #2 above. (Otherwise, a Boolean subexpression is an error.)
200             {
201             package Language::Basic::Expression::Arithmetic;
202             @Language::Basic::Expression::Arithmetic::ISA = qw(Language::Basic::Expression);
203 16     16   115 use Language::Basic::Common;
  16         1654  
  16         25113  
204              
205             sub new {
206             # The while loop is necessary in case we have an expression like 1+2+3
207             # It will effectively evaluate the +, - operators left to right
208 280     280   418 my $class = shift;
209 280         334 my $token_group = shift;
210 280         322 my $maybe_boolean = shift;
211 280 50 66     815 if (defined($maybe_boolean) && $maybe_boolean ne "maybe_boolean") {
212 0         0 Exit_Error("Internal Error: Weird arg '$maybe_boolean' to LBE::Arithmetic::new");
213             }
214              
215 280         1434 my $exp = new Language::Basic::Expression::Multiplicative
216             ($token_group, $maybe_boolean);
217 280 100       1756 if ($exp->isa("Language::Basic::Expression::Boolean")) {
218 2 50       7 if ($maybe_boolean) {
219 2         13 return $exp;
220             } else {
221 0         0 Exit_Error("Syntax Error: Expected non-Boolean Expression!");
222             }
223             }
224              
225 278         371 my (@exps, @ops);
226 278         381 push @exps, $exp;
227 278         764 while (defined (my $tok =
228             $token_group->eat_if_class("Arithmetic_Operator"))) {
229 11         46 push @ops, $tok->text;
230 11         41 $exp = new Language::Basic::Expression::Multiplicative $token_group;
231 11 50       84 if ($exp->isa("Language::Basic::Expression::Boolean")) {
232 0         0 Exit_Error("Syntax Error: Expected non-Boolean Expression!");
233             }
234 11         80 push @exps, $exp;
235             } # end while
236              
237             # Don't bother making an Arith. Exp. object if there's just one Mult. Exp.
238             # Return the Mult. Exp. instead.
239 278 100       1703 return $exp unless @ops;
240              
241             # Otherwise, we want to create the Arith. Exp.
242 10         17 my $self = {};
243 10         28 $self->{"expressions"} = \@exps;
244 10         23 $self->{"operations"} = \@ops;
245 10         30 bless $self, $class;
246              
247             # Bless to LBEA::String or Numeric
248 10         40 $self->set_return_type($exp);
249 10         95 return $self;
250             } # end sub Language::Basic::Expression::Arithmetic::new
251              
252             package Language::Basic::Expression::Arithmetic::String;
253             @Language::Basic::Expression::Arithmetic::String::ISA =
254             qw(Language::Basic::Expression::Arithmetic
255             Language::Basic::Expression::String);
256              
257             sub evaluate {
258 28     28   41 my $self = shift;
259 28         35 my @exps = @{$self->{"expressions"}};
  28         78  
260             # Ops ought to be all pluses, since that's all BASIC can do.
261 28         31 my @ops = @{$self->{"operations"}};
  28         71  
262              
263 28         67 my $exp = (shift @exps)->evaluate;
264 28         18800 while (my $op = shift @ops) {
265 28         74 my $exp2 = (shift @exps)->evaluate;
266 28 50       93 if ($op eq '+') {
267 28         98 $exp .= $exp2;
268             } else {
269 0         0 die "Unknown op in LBE::Arithmetic::String::evaluate!\n";
270             }
271             } # end while
272 28         88 return($exp);
273             } # end sub Language::Basic::Expression::Arithmetic::String::evaluate
274              
275             sub output_perl {
276 0     0   0 my $self = shift;
277 0         0 my @exps = @{$self->{"expressions"}};
  0         0  
278 0         0 my @ops = @{$self->{"operations"}};
  0         0  
279              
280 0         0 my $ret = (shift @exps)->output_perl;
281 0         0 while (my $op = shift @ops) {
282 0 0       0 if ($op eq "+") {
283 0         0 my $exp = (shift @exps)->output_perl;
284 0         0 $ret .= " . " . $exp;
285             } else {
286 0         0 die "Unknown op in LBE::Arithmetic::String::output_perl!\n";
287             }
288             } # end while
289 0         0 return($ret);
290             } # end sub Language::Basic::Expression::Arithmetic::String::output_perl
291              
292             package Language::Basic::Expression::Arithmetic::Numeric;
293             @Language::Basic::Expression::Arithmetic::Numeric::ISA =
294             qw(Language::Basic::Expression::Arithmetic
295             Language::Basic::Expression::Numeric);
296              
297             sub evaluate {
298 6     6   9 my $self = shift;
299 6         9 my @exps = @{$self->{"expressions"}};
  6         21  
300 6         8 my @ops = @{$self->{"operations"}};
  6         15  
301              
302 6         20 my $exp = (shift @exps)->evaluate;
303 6         23 while (my $op = shift @ops) {
304 7         21 my $exp2 = (shift @exps)->evaluate;
305 7 50       20 if ($op eq '+') {
306 7         25 $exp = $exp + $exp2;
307             } else { # minus
308 0         0 $exp = $exp - $exp2;
309             }
310             } # end while
311 6         16 return($exp);
312             } # end sub Language::Basic::Expression::Arithmetic::Numeric::evaluate
313              
314             sub output_perl {
315 0     0   0 my $self = shift;
316 0         0 my @exps = @{$self->{"expressions"}};
  0         0  
317 0         0 my @ops = @{$self->{"operations"}};
  0         0  
318              
319 0         0 my $ret = (shift @exps)->output_perl;
320 0         0 while (my $op = shift @ops) {
321 0         0 my $exp = (shift @exps)->output_perl;
322 0         0 $ret .= $op . $exp;
323             } # end while
324 0         0 return($ret);
325             } # end sub Language::Basic::Expression::Arithmetic::Numeric::output_perl
326              
327             } # end package Language::Basic::Expression::Arithmetic
328              
329             =item Multiplicative
330              
331             a set of unary expressions connected by '*' or '/'.
332              
333             =cut
334              
335             {
336             package Language::Basic::Expression::Multiplicative;
337             @Language::Basic::Expression::Multiplicative::ISA = qw(Language::Basic::Expression);
338 16     16   139 use Language::Basic::Common;
  16         27  
  16         10625  
339              
340             sub new {
341 291     291   793 my $class = shift;
342 291         312 my $token_group = shift;
343 291         335 my $maybe_boolean = shift;
344 291 50 66     1488 if (defined($maybe_boolean) && $maybe_boolean ne "maybe_boolean") {
345 0         0 Exit_Error("Internal Error: Weird arg '$maybe_boolean' to LBE::Multiplicative::new");
346             }
347              
348 291         1002 my $exp = new Language::Basic::Expression::Unary
349             ($token_group, $maybe_boolean);
350 291 100       2278 if ($exp->isa("Language::Basic::Expression::Boolean")) {
351 2 50       6 if ($maybe_boolean) {
352 2         4 return $exp;
353             } else {
354 0         0 Exit_Error("Syntax Error: Expected non-Boolean Expression!");
355             }
356             }
357              
358 289         327 my (@exps, @ops);
359 289         438 push @exps, $exp;
360 289         838 while (defined (my $tok =
361             $token_group->eat_if_class("Multiplicative_Operator"))) {
362 7         25 push @ops, $tok->text;
363 7         22 $exp = new Language::Basic::Expression::Unary $token_group;
364 7 50       54 if ($exp->isa("Language::Basic::Expression::Boolean")) {
365 0         0 Exit_Error("Syntax Error: Expected non-Boolean Expression!");
366             }
367 7         52 push @exps, $exp;
368             } # end while
369              
370             # Don't bother making a Mult. Exp. object if there's just one Unary Exp.
371             # Return the Unary Exp. instead.
372             # Note that this will definitely happen if $exp is a String.
373 289 100       1051 return $exp unless @ops;
374              
375             # Otherwise, we want to create the Mult. Exp.
376 5         85 my $self = {};
377 5         15 $self->{"expressions"} = \@exps;
378 5         11 $self->{"operations"} = \@ops;
379 5         14 bless $self, $class;
380              
381             # Bless to LBEM::String or Numeric
382 5         25 $self->set_return_type($exp);
383 5         13 return $self;
384             } # end sub Language::Basic::Expression::Multiplicative::new
385              
386             sub evaluate {
387 8     8   15 my $self = shift;
388 8         10 my @exps = @{$self->{"expressions"}};
  8         96  
389 8         14 my @ops = @{$self->{"operations"}};
  8         22  
390              
391 8         23 my $exp = (shift @exps)->evaluate;
392 8         29 while (my $op = shift @ops) {
393 10         25 my $exp2 = (shift @exps)->evaluate;
394 10 100       29 if ($op eq '*') {
395 8         84 $exp = $exp * $exp2;
396             } else {
397 2         8 $exp = $exp / $exp2;
398             }
399             } # end while
400 8         22 return($exp);
401             } # end sub Language::Basic::Expression::Multiplicative::evaluate
402              
403             sub output_perl {
404 0     0   0 my $self = shift;
405 0         0 my @exps = @{$self->{"expressions"}};
  0         0  
406 0         0 my @ops = @{$self->{"operations"}};
  0         0  
407              
408 0         0 my $ret = (shift @exps)->output_perl;
409 0         0 while (my $op = shift @ops) {
410 0         0 my $exp = (shift @exps)->output_perl;
411 0         0 $ret .= $op . $exp;
412             } # end while
413 0         0 return($ret);
414             } # end sub Language::Basic::Expression::Multiplicative::output_perl
415              
416             # Sub packages
417             package Language::Basic::Expression::Multiplicative::Numeric;
418             @Language::Basic::Expression::Multiplicative::Numeric::ISA =
419             qw(Language::Basic::Expression::Multiplicative
420             Language::Basic::Expression::Numeric);
421             # Note that there can't possibly be an LBEM::String. LBEM::new will just
422             # return an LBE::Unary, since there are no string multiplying ops to find.
423             } # end package Language::Basic::Expression::Multiplicative
424              
425             =item Unary
426              
427             a variable, a function, a string or numeric constant, or an arithmetic
428             expression in parentheses, potentially with a unary minus sign.
429              
430             =cut
431              
432             {
433             package Language::Basic::Expression::Unary;
434             @Language::Basic::Expression::Unary::ISA = qw(Language::Basic::Expression);
435 16     16   111 use Language::Basic::Common;
  16         133  
  16         41402  
436              
437             sub new {
438 298     298   412 my $class = shift;
439 298         322 my $token_group = shift;
440             # Fields:
441             # nested This Expression contains a parenthesized exp.
442             # minus This Exp. has a unary minus in front of it
443 298         970 my $self = {
444             "nested" => "",
445             "minus" => "",
446             };
447              
448             # If we're inside a Relational Exp., then a parenthetical exp. may
449             # be either Boolean or non-Boolean. Otherwise, it has to be non-Boolean
450 298         423 my $maybe_boolean = shift;
451 298 50 66     832 if (defined($maybe_boolean) && $maybe_boolean ne "maybe_boolean") {
452 0         0 Exit_Error("Internal Error: Weird arg '$maybe_boolean' to LBE::Unary::new");
453             }
454              
455             # unary minus in the expression?
456 298 100       1247 $self->{"minus"} = 1 if defined($token_group->eat_if_string("-"));
457              
458 298         381 my $unary;
459             my $try;
460             # if a parentheses, (recursively) parse what's inside
461             # If $maybe_boolean, then a paren'ed expression might be a Boolean exp.,
462             # so call LBE::Logical_Or (highest level Boolean exp.)
463             # However, in most cases, it'll be a non-Boolean, so call with
464             # "maybe_arithmetic" flag, which tells LBE::LO not to be surprised
465             # if it finds an arithmetic exp.
466 298 100       794 if (defined($token_group->eat_if_class("Left_Paren"))) {
    100          
    100          
    50          
467 8         14 $self->{"nested"} = 1;
468 8         35 $try = new Language::Basic::Expression::Logical_Or
469             ($token_group, "maybe_arithmetic");
470             # Skip End Paren
471 8 50       22 defined($token_group->eat_if_class("Right_Paren")) or
472             Exit_Error("Expected ')' to match '('!");
473             # if we found a Boolean, make sure we're allowed to have one.
474 8 50 66     74 if ($try->isa("Language::Basic::Expression::Boolean") &&
475             !$maybe_boolean) {
476 0         0 Exit_Error("Syntax Error: Expected non-Boolean Expression!");
477             }
478 8         49 $unary = $try
479              
480             # OR it's a String or Numeric function
481             # NOTE that LBEF::new had better not eat the word if it returns undef!
482             } elsif (defined ($try =
483             new Language::Basic::Expression::Function $token_group)) {
484 12         22 $unary = $try;
485              
486             # OR it's a String or Numeric variable
487             } elsif (defined ($try =
488             new Language::Basic::Expression::Lvalue $token_group)) {
489 42         69 $unary = $try;
490              
491             # OR it's a String or Numeric constant
492             } elsif (defined ($try =
493             new Language::Basic::Expression::Constant $token_group)) {
494 236         312 $unary = $try;
495              
496             # Or die
497             } else {
498 0 0       0 my $tok = $token_group->lookahead or
499             Exit_Error("Found nothing when expected Unary Expression!");
500 0         0 Exit_Error("Unknown unary expression starts with '", $tok->text,"'");
501             }
502             #print "unary ref is ",ref($unary),"\n";
503              
504             # If it's just an Lvalue, say, then return the Lvalue object rather
505             # than making a Unary out of it. Can't do that if we're nested or minused.
506 298 100 100     1678 if ($self->{"nested"} || $self->{"minus"}) {
507 12         23 $self->{"expression"} = $unary;
508 12         34 bless $self, $class;
509             # Bless to LBEU::String or Numeric or Boolean
510 12         47 $self->set_return_type($unary);
511 12         30 return $self;
512             } else {
513 286         827 return $unary;
514             }
515             } # end Language::Basic::Expression::Unary::new
516              
517             sub evaluate {
518 16     16   23 my $self = shift;
519 16         32 my $exp = $self->{"expression"};
520              
521 16         48 my $value = $exp->evaluate;
522 16 100       47 $value = -$value if $self->{"minus"};
523 16         60 return($value);
524             } # end sub Language::Basic::Expression::Unary::evaluate
525              
526             sub output_perl {
527 0     0   0 my $self = shift;
528 0 0       0 my $ret = $self->{"minus"} ? "-" : "";
529 0         0 my $exp = $self->{"expression"};
530 0         0 my $out = $exp->output_perl;
531 0 0       0 if ($self->{"nested"}) {
532 0         0 $out = "(" . $out . ")";
533             }
534 0         0 $ret .= $out;
535 0         0 return($ret);
536             } # end sub Language::Basic::Expression::Unary::output_perl
537              
538             # Sub packages
539             package Language::Basic::Expression::Unary::Numeric;
540             @Language::Basic::Expression::Unary::Numeric::ISA =
541             qw(Language::Basic::Expression::Unary
542             Language::Basic::Expression::Numeric);
543             package Language::Basic::Expression::Unary::String;
544             @Language::Basic::Expression::Unary::String::ISA =
545             qw(Language::Basic::Expression::Unary
546             Language::Basic::Expression::String);
547             package Language::Basic::Expression::Unary::Boolean;
548             @Language::Basic::Expression::Unary::Boolean::ISA =
549             qw(Language::Basic::Expression::Unary
550             Language::Basic::Expression::Boolean);
551             } # end package Language::Basic::Expression::Unary
552              
553             ######################################################################
554              
555             =item Constant
556              
557             a string or numeric constant, like "17" or 32.4
558              
559             =cut
560              
561             {
562             package Language::Basic::Expression::Constant;
563             @Language::Basic::Expression::Constant::ISA = qw(Language::Basic::Expression);
564              
565             # Returns a LBE::Constant::* subclass or undef
566             sub new {
567 259     259   353 my $class = shift;
568 259         880 my $token_group = shift;
569 259         280 my ($const, $try);
570 259 100       922 if (defined ($try =
    50          
571             new Language::Basic::Expression::Constant::Numeric $token_group)) {
572 221         294 $const = $try;
573             } elsif (defined ($try =
574             new Language::Basic::Expression::Constant::String $token_group)) {
575 38         56 $const = $try;
576             } else {
577 0         0 return undef;
578             }
579              
580 259         1184 return $const;
581             } # end Language::Basic::Expression::Constant::new
582              
583 0     0   0 sub evaluate {return shift->{"expression"}->evaluate; }
584              
585             package Language::Basic::Expression::Constant::Numeric;
586             @Language::Basic::Expression::Constant::Numeric::ISA =
587             qw(Language::Basic::Expression::Constant
588             Language::Basic::Expression::Numeric);
589              
590             sub new {
591 259     259   289 my $class = shift;
592 259         309 my $token_group = shift;
593 259 100       815 if (defined (my $tok =
594             $token_group->eat_if_class("Numeric_Constant"))) {
595 221         646 my $self = {"value" => $tok->text + 0};
596 221         1272 bless $self, $class; # and return it
597             } else {
598 38         242 return undef;
599             }
600             } # end sub Language::Basic::Expression::Constant::Numeric::new
601              
602 316     316   921 sub evaluate { return shift->{"value"} }
603              
604 0     0   0 sub output_perl {return shift->{"value"}}
605              
606             package Language::Basic::Expression::Constant::String;
607             @Language::Basic::Expression::Constant::String::ISA =
608             qw(Language::Basic::Expression::Constant
609             Language::Basic::Expression::String);
610              
611             sub new {
612 38     38   63 my $class = shift;
613 38         56 my $token_group = shift;
614 38 50       113 if (defined (my $tok =
615             $token_group->eat_if_class("String_Constant"))) {
616 38         115 (my $text = $tok->text) =~ s/^"(.*?)"/$1/;
617 38         122 my $self = {"value" => $text};
618 38         224 bless $self, $class; # and return it
619             } else {
620             # TODO handle unquoted string for Input, Data statements
621 0         0 warn "Currently only understand quoted strings for String Constant";
622 0         0 return undef;
623             }
624             } # end sub Language::Basic::Expression::Constant::String::new
625              
626 42     42   150 sub evaluate { return shift->{"value"} }
627              
628             # Don't return in single quotes, because single quotes may be in a BASIC
629             # string constant. Instead use quotemeta. But don't really use quotemeta,
630             # because it quotes too much.
631             sub output_perl {
632 0     0   0 my $self = shift;
633 0         0 my $str = $self->{"value"};
634 0         0 $str =~ s/([\$%@*&])/\\$1/g; # poor man's quotemeta
635 0         0 return '"' . $str . '"';
636             } # end sub Language::Basic::Expression::Constant::String::output_perl
637              
638             } # end package Language::Basic::Expression::Constant
639              
640             ######################################################################
641              
642             =item Lvalue
643              
644             a settable expression: a variable, X, or one cell in an array, A(17,Q). The
645             "variable" method returns the actual LB::Variable::Scalar object referenced by
646             this Lvalue.
647              
648             =cut
649              
650             {
651             package Language::Basic::Expression::Lvalue;
652             @Language::Basic::Expression::Lvalue::ISA = qw(Language::Basic::Expression);
653 16     16   145 use Language::Basic::Common;
  16         34  
  16         11443  
654              
655             # Sub-packages
656             {
657             package Language::Basic::Expression::Lvalue::Numeric;
658             @Language::Basic::Expression::Lvalue::Numeric::ISA =
659             qw(Language::Basic::Expression::Lvalue
660             Language::Basic::Expression::Numeric);
661             package Language::Basic::Expression::Lvalue::String;
662             @Language::Basic::Expression::Lvalue::String::ISA =
663             qw(Language::Basic::Expression::Lvalue
664             Language::Basic::Expression::String);
665             }
666              
667             # Fields:
668             # varptr - ref to the LB::Variable (::Array or ::Scalar) object. Note
669             # that it does NOT ref a particular cell in an LBV::Array object!
670             # arglist - a set of Arithmetic Expressions describing which exact cell
671             # in an LBV::Array to get. undef for a LBV::Scalar
672             sub new {
673 326     326   449 my $class = shift;
674 326         518 my $token_group = shift;
675 326         676 my $self = {};
676              
677 326 100       1046 defined (my $tok =
678             $token_group->eat_if_class("Identifier")) or
679             return undef;
680 90         316 my $name = $tok->text;
681              
682             # read ( Arglist ) if it exists
683             # By default, though, it's a scalar, and has no ()
684 90         216 $self->{"arglist"} = undef;
685 90 100       880 if (defined (my $arglist =
686             new Language::Basic::Expression::Arglist $token_group)) {
687 13         28 $self->{"arglist"} = $arglist;
688             }
689              
690             # Look up the variable by name in the (Array or Scalar) variable storage.
691             # (Also, create the Variable if it doesn't yet exist.)
692 90         351 my $var = &Language::Basic::Variable::lookup($name, $self->{"arglist"});
693 90         171 $self->{"varptr"} = $var;
694 90         197 $self->{"name"} = $name;
695              
696 90         243 bless $self, $class;
697             # Is it a string or numeric lvalue?
698 90         275 $self->set_return_type($var);
699 90         675 return $self;
700             } # end sub Language::Basic::Expression::Lvalue::new
701              
702             sub evaluate {
703 196     196   231 my $self = shift;
704             # This automatically gets the correct array cell if necessary
705 196         370 my $var = $self->variable;
706 196         612 my $value = $var->value;
707 196         650 return $value;
708             } # end sub Language::Basic::Expression::Lvalue::evaluate
709              
710             # returns a variable, e.g. for setting in a Let or changing in a Next
711             # Note that it always returns a LB::Variable::Scalar object. If the
712             # variable in this expression is an Array, it returns one cell from the array.
713             sub variable {
714 366     366   585 my $self = shift;
715 366         500 my $var = $self->{"varptr"};
716             # if Arglist exists, evaluate each arith. exp. in it and get that cell
717             # from the Array
718 366 100       827 if (defined (my $arglist = $self->{"arglist"})) {
719 54         109 my @args = $arglist->evaluate;
720 54         271 $var = $var->get_cell(@args);
721             }
722              
723 366         1182 return $var;
724             } # end sub Language::Basic::Expression::Lvalue::variable
725              
726             sub output_perl {
727 0     0   0 my $self = shift;
728 0         0 my $name = $self->{"name"};
729 0         0 $name =~ s/\$$/_str/; # make name perl-like
730 0         0 my $ret = '$' . lc($name);
731 0 0       0 if (defined $self->{"arglist"}) {
732 0         0 my $args = join("][", ($self->{"arglist"}->output_perl));
733 0         0 $ret .= "[" . $args . "]";
734             }
735 0         0 return $ret;
736             } # end sub Language::Basic::Expression::Lvalue::output_perl
737              
738             } # end package Language::Basic::Expression::Lvalue
739              
740             ######################################################################
741              
742             =item Function
743              
744             Either an Intrinsic or a User-Defined function.
745              
746             =cut
747              
748             #
749             # Fields:
750             # function - ref to the LB::Function (::Intrinsic or ::Defined) used
751             # by this expression
752             # arglist - a set of Arithmetic Expressions describing the arguments
753             # to pass to the function
754             {
755             package Language::Basic::Expression::Function;
756             @Language::Basic::Expression::Function::ISA = qw(Language::Basic::Expression);
757 16     16   112 use Language::Basic::Common;
  16         32  
  16         17617  
758              
759             # Sub-packages
760             {
761             package Language::Basic::Expression::Function::Numeric;
762             @Language::Basic::Expression::Function::Numeric::ISA =
763             qw(Language::Basic::Expression::Function
764             Language::Basic::Expression::Numeric);
765             package Language::Basic::Expression::Function::String;
766             @Language::Basic::Expression::Function::String::ISA =
767             qw(Language::Basic::Expression::Function
768             Language::Basic::Expression::String);
769             }
770              
771             # Arg0, Arg1 are the object and a ref to the string being parsed, as usual.
772             # Arg2, if it exists, says we're in a DEF statement, so that if the
773             # function doesn't exist, we should create it rather than returning undef.
774             sub new {
775 293     293   603 my $class = shift;
776 293         348 my $token_group = shift;
777 293         518 my $self = {};
778              
779             # Don't eat it if it's not a true function name (could be an lvalue)
780 293         816 my $tok = $token_group->lookahead;
781 293 100       3093 return undef unless $tok->isa("Language::Basic::Token::Identifier");
782 57         175 my $name = $tok->text;
783 57         118 my $defining = (defined (my $exp = shift));
784              
785             # Look up the function name
786             # If the function doesn't exist, the word is a variable or something...
787             # Alternatively, if there was a second argument to parse, then we're
788             # in a DEF statement & should create the function.
789 57         74 my $func;
790 57 100       138 if ($defining) {
791             # TODO should this check be somewhere else, so that we can
792             # give a more descriptive error message in Statement::Def::new?
793 3 50       16 return undef unless $name =~ /^FN/;
794 3         224 $func = new Language::Basic::Function::Defined $name;
795             } else {
796 54 100       350 $func = &Language::Basic::Function::lookup($name) or return undef;
797             }
798 15         51 $self->{"function"} = $func;
799              
800             #Now that we know it's a function, eat the token
801 15         138 $token_group->eat;
802              
803             # read ( Arglist )
804             # TODO Actually, whether or not we're defining, we should just read
805             # an LBE::Arglist here. If $defining, define() can make sure all args
806             # are actually Lvalues containing Scalar Variables. However, this
807             # requires that Arglist has Lvalues, rather than Arith. Exp.'s
808             # containing (ME's containing...) Lvalues.
809 15 100       50 if ($defining) {
810             # Empty parens aren't allowed! (and \s* has been removed by lexing)
811 3 50       14 defined($token_group->eat_if_class("Left_Paren")) or
812             Exit_Error("Function must take at least one argument.");
813 3         18 my @args;
814 3         6 do {
815 4         22 my $arg = new Language::Basic::Expression::Lvalue $token_group;
816 4         26 push @args, $arg;
817             } while (defined $token_group->eat_if_string(","));
818 3 50       11 defined($token_group->eat_if_class("Right_Paren")) or
819             Exit_Error("Expected ')' to match '('!");
820              
821             # Declare the number & type of args in the subroutine
822 3         32 $func->declare (\@args);
823              
824             } else {
825 12 50       61 my $arglist = new Language::Basic::Expression::Arglist $token_group
826             or Exit_Error("Function without arglist!");
827             # check if the number or type of args is wrong.
828 12         230 $func->check_args($arglist);
829 12         177 $self->{"arglist"} = $arglist;
830             }
831              
832 15         54 bless $self, $class;
833             # Is it a string or numeric Function?
834 15         61 $self->set_return_type($func);
835 15         97 return $self;
836             } # end sub Language::Basic::Expression::Function::new
837              
838             sub evaluate {
839 34     34   48 my $self = shift;
840 34         59 my $func = $self->{"function"};
841 34         217 my $arglist = $self->{"arglist"};
842             # Note we tested number & type of args in new
843 34         83 my @args = $arglist->evaluate;
844 34         140 my $value = $func->evaluate(@args);
845 34         98 return $value;
846             } # end sub Language::Basic::Expression::Function::evaluate
847              
848             sub output_perl {
849 0     0   0 my $self = shift;
850             # Function name
851 0         0 my $func = $self->{"function"};
852 0         0 my $ret = $func->output_perl;
853             # If it's either a user-defined function or a BASIC intrinsic (that
854             # doesn't have a Perl equivalent), add a &
855 0 0       0 if ($ret =~ /(fun|bas)$/) {$ret = '&' . $ret}
  0         0  
856              
857             # Function args
858 0         0 $ret .= "(";
859            
860 0         0 my @args = $self->{"arglist"}->output_perl;
861 0         0 $ret .= join(", ", @args);
862 0         0 $ret .= ")";
863 0         0 return $ret;
864             }
865              
866             } # end package Language::Basic::Expression::Function
867              
868             =item Arglist
869              
870             a list of arguments to an array or function
871              
872             =cut
873              
874             {
875             package Language::Basic::Expression::Arglist;
876             @Language::Basic::Expression::Arglist::ISA = qw(Language::Basic::Expression);
877 16     16   109 use Language::Basic::Common;
  16         37  
  16         21607  
878              
879             sub new {
880 102     102   152 my $class = shift;
881 102         151 my $token_group = shift;
882 102         158 my $self = {};
883              
884             # Has to start with paren
885 102 100       297 defined($token_group->eat_if_class("Left_Paren")) or
886             return undef;
887             # Eat args
888 25         177 my @args = ();
889 25         41 do {
890 38         179 my $arg = new Language::Basic::Expression::Arithmetic $token_group;
891             # TODO test that arg is a Scalar!
892 38         178 push @args, $arg;
893             } while (defined($token_group->eat_if_string(",")));
894              
895             # Has to end with paren
896 25 50       83 defined($token_group->eat_if_class("Right_Paren")) or
897             Exit_Error("Arglist without ')' at end!");
898 25 50       115 unless (@args) {Exit_Error("Empty argument list ().")}
  0         0  
899              
900 25         64 $self->{"arguments"} = \@args;
901 25         155 bless $self, $class;
902             } # end sub Language::Basic::Expression::Arglist::new
903              
904             # Returns a LIST of values
905             sub evaluate {
906 91     91   110 my $self = shift;
907 91         92 my @values = map {$_->evaluate} @{$self->{"arguments"}};
  148         309  
  91         197  
908 91         240 return @values;
909             } # end sub Language::Basic::Expression::Arglist::evaluate
910              
911             # Note this returns an ARRAY of args. Messes up the output_perl paradigm,but
912             # functions & arrays need to do different things to the args.
913             sub output_perl {
914 0     0   0 my $self = shift;
915 0         0 return map {$_->output_perl} @{$self->{"arguments"}};
  0         0  
  0         0  
916             } # end sub Language::Basic::Expression::Arglist::output_perl
917             } # end package Language::Basic::Expression::Arglist
918              
919             ######################################################################
920             # Boolean stuff
921             # Booleans don't care whether the stuff in them is String or Numeric,
922             # so no sub-packages are needed.
923              
924             =item Logical_Or
925              
926             a set of Logical_And expressions connected by "OR"
927              
928             =cut
929              
930             # In BASIC, Boolean expressions can't contain non-Boolean expressions
931             # except for Relational Exps. (which have two Arithmetic Exps. separated by
932             # a Rel. Op.)
933             # However, parentheses can confuse things.
934             # LBE::Unary is one of:
935             # (1) A constant, variable, function, etc.
936             # (2) (Arithmetic Exp.)
937             # (3) (Logical Or)
938             # Unary::new calls LBE::Logical_Or::new with "maybe_arithmetic" sometimes, to
939             # tell LBELO::new that if it finds a (parenthesized) non-Boolean expression,
940             # it's just case #2 above. (Otherwise, a non-Boolean subexpression is an error.)
941             {
942             package Language::Basic::Expression::Logical_Or;
943             @Language::Basic::Expression::Logical_Or::ISA =
944             qw(Language::Basic::Expression::Boolean);
945 16     16   107 use Language::Basic::Common;
  16         37  
  16         9903  
946              
947             sub new {
948             # No "operators" field is necessary since operators must all be "OR"
949 36     36   56 my $class = shift;
950 36         49 my $token_group = shift;
951 36         47 my $maybe_arithmetic = shift;
952 36 50 66     133 if (defined($maybe_arithmetic) && $maybe_arithmetic ne "maybe_arithmetic") {
953 0         0 Exit_Error("Internal Error: Weird arg '$maybe_arithmetic' to LBE::Logical_Or::new");
954             }
955              
956 36         148 my $exp = new Language::Basic::Expression::Logical_And
957             ($token_group, $maybe_arithmetic); # TODO ... or Error...
958 36 100       187 if (! $exp->isa("Language::Basic::Expression::Boolean")) {
959 6 50       12 if ($maybe_arithmetic) {
960 6         13 return $exp;
961             } else {
962 0         0 Exit_Error("Syntax Error: Expected Boolean Expression");
963             }
964             }
965              
966 30         37 my @exps;
967 30         40 push @exps, $exp;
968 30         85 while (defined ($token_group->eat_if_string("OR"))) {
969 6         26 $exp = new Language::Basic::Expression::Logical_And $token_group;
970 6 50       24 if (! $exp->isa("Language::Basic::Expression::Boolean")) {
971 0         0 Exit_Error("Syntax Error: Expected Boolean Expression!");
972             }
973 6         21 push @exps, $exp;
974             } # end while
975              
976             # Don't bother making a Logical_Or object if there's just one Logical_And
977 30 100       177 return $exp if @exps == 1;
978              
979             # Otherwise, we want to create the Logical_Or
980 6         17 my $self = {"expressions" => \@exps};
981 6         42 bless $self, $class;
982             } # end sub Language::Basic::Expression::Logical_Or::new
983              
984             sub evaluate {
985 6     6   7 my $self = shift;
986 6         8 my @exps = @{$self->{"expressions"}};
  6         15  
987              
988 6         14 my $exp = (shift @exps)->evaluate;
989             # TODO stop calculating when we find a true one?
990 6         18 while (defined(my $exp2 = shift @exps)) {
991 6   100     21 $exp = $exp || $exp2->evaluate;
992             } # end while
993 6         20 return($exp);
994             } # end sub Language::Basic::Expression::Logical_Or::evaluate
995              
996             sub output_perl {
997 0     0   0 my $self = shift;
998 0         0 my @exps = @{$self->{"expressions"}};
  0         0  
999              
1000 0         0 my $ret = (shift @exps)->output_perl;
1001 0         0 while (defined(my $exp = shift @exps)) {
1002 0         0 $ret .= " || " . $exp->output_perl;
1003             } # end while
1004 0         0 return($ret);
1005             } # end sub Language::Basic::Expression::Logical_Or::output_perl
1006              
1007             } # end package Language::Basic::Expression::Logical_Or
1008              
1009             =item Logical_And
1010              
1011             a set of Relational expressions connected by "AND"
1012              
1013             =cut
1014              
1015             {
1016             package Language::Basic::Expression::Logical_And;
1017             @Language::Basic::Expression::Logical_And::ISA =
1018             qw(Language::Basic::Expression::Boolean);
1019 16     16   101 use Language::Basic::Common;
  16         41  
  16         9721  
1020              
1021             sub new {
1022             # No "operators" field is necessary since operators must all be "AND"
1023 42     42   104 my $class = shift;
1024 42         54 my $token_group = shift;
1025 42         52 my $maybe_arithmetic = shift;
1026 42 50 66     145 if (defined($maybe_arithmetic) && $maybe_arithmetic ne "maybe_arithmetic") {
1027 0         0 Exit_Error("Internal Error: Weird arg '$maybe_arithmetic' to LBE::Logical_And::new");
1028             }
1029              
1030 42         153 my $exp = new Language::Basic::Expression::Relational
1031             ($token_group, $maybe_arithmetic);
1032 42 100       213 if (! $exp->isa("Language::Basic::Expression::Boolean")) {
1033 6 50       13 if ($maybe_arithmetic) {
1034 6         13 return $exp;
1035             } else {
1036 0         0 Exit_Error("Syntax Error: Expected Boolean Expression!");
1037             }
1038             }
1039              
1040 36         48 my @exps;
1041 36         61 push @exps, $exp;
1042 36         103 while (defined ($token_group->eat_if_string("AND"))) {
1043 6         25 $exp = new Language::Basic::Expression::Relational $token_group;
1044 6 50       28 if (! $exp->isa("Language::Basic::Expression::Boolean")) {
1045 0         0 Exit_Error("Syntax Error: Expected Boolean Expression!");
1046             }
1047 6         20 push @exps, $exp;
1048             } # end while
1049              
1050             # Don't bother making a Logical_And object if there's just one Relational
1051 36 100       136 return $exp if @exps == 1;
1052              
1053             # Otherwise, we want to create the Logical_And
1054 6         19 my $self = {"expressions" => \@exps};
1055 6         22 bless $self, $class;
1056             } # end sub Language::Basic::Expression::Logical_And::new
1057              
1058             sub evaluate {
1059 6     6   8 my $self = shift;
1060 6         10 my @exps = @{$self->{"expressions"}};
  6         17  
1061              
1062 6         15 my $exp = (shift @exps)->evaluate;
1063             # TODO stop calculating when we find a true one?
1064 6         22 while (defined(my $exp2 = shift @exps)) {
1065 6   100     24 $exp = $exp && $exp2->evaluate;
1066             } # end while
1067 6         23 return($exp);
1068             } # end sub Language::Basic::Expression::Logical_And::evaluate
1069              
1070             sub output_perl {
1071 0     0   0 my $self = shift;
1072 0         0 my @exps = @{$self->{"expressions"}};
  0         0  
1073              
1074 0         0 my $ret = (shift @exps)->output_perl;
1075 0         0 while (defined(my $exp = shift @exps)) {
1076 0         0 $ret .= " && " . $exp->output_perl;
1077             } # end while
1078 0         0 return($ret);
1079             } # end sub Language::Basic::Expression::Logical_And::output_perl
1080              
1081             } # end package Language::Basic::Expression::Logical_And
1082              
1083             =item Relational
1084              
1085             A relational expression, like "A>B+C", optionally with a NOT in front of it.
1086              
1087             =cut
1088              
1089             {
1090             package Language::Basic::Expression::Relational;
1091             @Language::Basic::Expression::Relational::ISA =
1092             qw(Language::Basic::Expression::Boolean);
1093 16     16   102 use Language::Basic::Common;
  16         49  
  16         19429  
1094              
1095             # Usually, an LBE::Relational is just LBE::Arithmetic Rel. Op. LBE::Arithmetic
1096             # However, if the first sub-expression in the LBE::Relational is parenthesized,
1097             # it could be either
1098             # (1) (Logical Or Exp.) --- E.g. IF (A>B OR C>D) THEN...
1099             # (2) (Arith. Exp.) --- E.g. IF (A+1)>B THEN...
1100             # So we call the first LBE::Arithmetic::new with "maybe_boolean", so that
1101             # it knows it may find a Boolean sub-expression
1102             # Note that in case (1), we don't need to look for a Rel. Op., because
1103             # IF (A>B OR C>D) > 2 is illegal.
1104             #
1105             # Rel. Exp. usually has two expressions in the "expressions" field, and
1106             # an operator in the "operator" field. However, in case (1) above, there will
1107             # only be one (Boolean) expression, and no op.
1108              
1109             sub new {
1110 48     48   85 my ($class, $token_group) = (shift, shift);
1111 48         75 my $self = {};
1112 48         70 my $maybe_arithmetic = shift;
1113 48 50 66     134 if (defined($maybe_arithmetic) && $maybe_arithmetic ne "maybe_arithmetic") {
1114 0         0 Exit_Error("Internal Error: Weird arg '$maybe_arithmetic' to LBE::Relational::new");
1115             }
1116              
1117             # "NOT" in the expression?
1118 48         145 $self->{"not"} = defined($token_group->eat_if_string("NOT"));
1119              
1120 48 50       176 my $e = new Language::Basic::Expression::Arithmetic
1121             ($token_group, "maybe_boolean")
1122             or Exit_Error("Unexpected text at beginning of Rel. Exp.");
1123 48         110 push @{$self->{"expressions"}}, $e;
  48         120  
1124              
1125             # Did we find a parenthesized Boolean exp? Then just return it.
1126             # Don't even look for a rel. op. since it would be illegal!
1127 48 100       292 if ($e->isa("Language::Basic::Expression::Boolean")) {
1128             # TODO return $e instead of blessing unless self->not?
1129 2         4 bless $self, $class;
1130 2         4 return $self;
1131             }
1132              
1133             # Read the Rel. Op.
1134 46         63 my $tok;
1135 46 100       118 if (!defined ($tok = $token_group->eat_if_class("Relational_Operator"))) {
1136             # Found a parenthesized Arithmetic Exp.?
1137 6 50       12 if ($maybe_arithmetic) {
1138 6         21 return $e; # Don't bother blessing & returning $self
1139             } else {
1140 0         0 Exit_Error("Syntax Error: No Relational Operator in Rel. Exp.");
1141             }
1142             }
1143              
1144 40         125 my $op = $tok->text;
1145              
1146             # Note: $e2 isn't allowed to be arithmetic, so no maybe_arithmetic arg
1147 40 50       111 my $e2 = new Language::Basic::Expression::Arithmetic $token_group or
1148             Exit_Error("Unexpected text in Rel. Exp. after '$op'");
1149 40         58 push @{$self->{"expressions"}}, $e2;
  40         76  
1150              
1151             # Convert BASIC ops to perlops
1152 40         201 my $num_op = {
1153             "=" => "==",
1154             ">" => ">",
1155             "<" => "<",
1156             ">=" => ">=",
1157             "<=" => "<=",
1158             "<>" => "!=",
1159             };
1160 40         150 my $string_op = {
1161             "=" => "eq",
1162             ">" => "gt",
1163             "<" => "lt",
1164             ">=" => "ge",
1165             "<=" => "le",
1166             "<>" => "ne",
1167             };
1168 40 50       277 my $trans = ($e->isa("Language::Basic::Expression::String")
1169             ? $string_op : $num_op);
1170 40 50       114 my $perlop = $trans->{$op} or Exit_Error("Unrecognized Rel. op. '$op'");
1171 40         62 $self->{"operator"} = $perlop;
1172 40         258 bless $self, $class;
1173             } # end sub Language::Basic::Expression::Relational::new
1174              
1175             sub evaluate {
1176             # If this Rel. Exp. has a nested Boolean Exp. inside it, then just
1177             # evaluate that (and NOT it if nec.)
1178             # Otherwise, evaluate the two sides of the Rel. Exp. (each is non-Boolean
1179             # exp -- either they're both arithmetic or they're both string) and
1180             # compare them.
1181 38     38   48 my $self = shift;
1182              
1183 38         46 my @exps = @{$self->{"expressions"}};
  38         102  
1184 38         52 my $exp = shift @exps;
1185 38         86 my $e = $exp->evaluate;
1186 38         42 my $value;
1187 38 100       249 if (! $exp->isa("Language::Basic::Expression::Boolean")) {
1188 36         46 my $exp2 = shift @exps;
1189 36         71 my $e2 = $exp2->evaluate;
1190              
1191 36         75 my $perlop = $self->{"operator"};
1192             # I'm vainly hoping that Perl eval will get the same result BASIC would
1193             # Need to use \Q in case we say IF A$ = "\", which should really compare
1194             # with \\.
1195 36         106 my $perlexp = "\"\Q$e\E\" " . $perlop . " \"\Q$e2\E\"";
1196 36         2198 $value = eval $perlexp;
1197             #print "exp is '$perlexp', value is '$value'\n";
1198              
1199             } else { # exp has a nested Boolean Exp. in it. There is no exp2
1200 2         2 $value = $e;
1201             }
1202            
1203 38 100       151 $value = !$value if $self->{"not"};
1204 38         143 return $value;
1205             } # end sub Language::Basic::Expression::Relational::evaluate
1206              
1207             sub output_perl {
1208 0     0     my $self = shift;
1209 0           my @exps = @{$self->{"expressions"}};
  0            
1210 0           my $exp = shift @exps;
1211 0           my $e = $exp->output_perl;
1212              
1213 0           my $ret;
1214             # "Normal" Rel. Exp., or nested Boolean exp.?
1215 0 0         if (! $exp->isa("Language::Basic::Expression::Boolean")) {
1216 0           my $exp2 = shift @exps;
1217 0           my $e2 = $exp2->output_perl;
1218              
1219 0           my $perlop = $self->{"operator"};
1220 0           $ret = join(" ",$e, $perlop, $e2);
1221             } else {
1222 0           $ret = $e;
1223             }
1224              
1225 0 0         if ($self->{"not"}) {
1226             # Don't add parens if it's already paren'd
1227 0 0         $ret = "(" . $ret . ")" unless $ret =~ /^\(.*\)$/;
1228 0           $ret = "!" . $ret;
1229             }
1230              
1231 0           return($ret);
1232             } # end sub Language::Basic::Expression::Relational::output_perl
1233              
1234             } # end package Language::Basic::Expression::Relational
1235              
1236             {
1237             # set ISA for "return type" classes
1238             package Language::Basic::Expression::Numeric;
1239             @Language::Basic::Expression::Numeric::ISA = qw
1240             (Language::Basic::Expression Language::Basic::Numeric);
1241             package Language::Basic::Expression::String;
1242             @Language::Basic::Expression::String::ISA = qw
1243             (Language::Basic::Expression Language::Basic::String);
1244             package Language::Basic::Expression::Boolean;
1245             @Language::Basic::Expression::Boolean::ISA = qw
1246             (Language::Basic::Expression Language::Basic::Boolean);
1247             }
1248              
1249             =pod
1250              
1251             =back
1252              
1253             =cut
1254              
1255             1; # end package Language::Basic::Expression