1 ;# $Id: eval.pl,v 3.0.1.1 1995/01/30 14:48:37 ram Exp $
3 ;# Copyright (c) 1991-1993, Raphael Manfredi
5 ;# You may redistribute only under the terms of the Artistic Licence,
6 ;# as specified in the README file that comes with the distribution.
7 ;# You may reuse parts of this distribution only within the terms of
8 ;# that same Artistic Licence; a copy of which may be found at the root
9 ;# of the source tree for dist 3.0.
12 ;# Revision 3.0.1.1 1995/01/30 14:48:37 ram
13 ;# patch49: removed old "do name()" routine call constructs
15 ;# Revision 3.0 1993/08/18 12:10:22 ram
16 ;# Baseline for dist 3.0 netwide release.
19 ;# The built-in interpreter
23 # States used by our interpeter -- in sync with @Keep
25 # Status in which we keep lines -- $Keep[$status]
26 @Keep = (0, 1, 1, 0, 1);
28 # Available status ($status)
36 # Priorities for operators -- magic numbers :-)
37 sub main'init_priority {
42 # Initializes the state stack of the interpreter
43 sub main'init_interp {
48 # Print error messages -- asssumes $unit and $. correctly set.
50 warn "\"$main'file\", line $.: @_.\n";
53 # If some states are still in the stack, warn the user
54 sub main'check_state {
55 &error("one statement pending") if $#state == 1;
56 &error("$#state statements pending") if $#state > 1;
59 # Add a value on the stack, modified by all the monadic operators.
60 # We use the locals @val and @mono from eval_expr.
62 local($val) = shift(@_);
64 # Cheat... the only monadic operator is '!'.
71 # Execute a stacked operation, leave result in stack.
72 # We use the locals @val and @op from eval_expr.
73 # If the value stack holds only one operand, do nothing.
75 return unless $#val > 0;
76 local($op) = pop(@op);
77 local($val1) = pop(@val);
78 local($val2) = pop(@val);
79 push(@val, eval("$val1 $op $val2") ? 1: 0);
82 # Given an operator, either we add it in the stack @op, because its
83 # priority is lower than the one on top of the stack, or we first execute
84 # the stacked operations until we reach the end of stack or an operand
85 # whose priority is lower than ours.
86 # We use the locals @val and @op from eval_expr.
88 local($op) = shift(@_); # Operator
89 if (!$Priority{$op}) {
90 &error("illegal operator $op");
94 &error("missing first operand for '$op' (diadic operator)");
97 # Because of the special behaviour of do-SUBR with the while modifier,
98 # I'm using a while-BLOCK construct. I consider this to be a bug of perl
99 # 4.0 PL19, although it is clearly documented in the man page.
101 $Priority{$op[$#op]} > $Priority{$op} # Higher priority op
102 && $#val > 0 # At least 2 values
104 &execute; # Execute an higher priority stacked operation
106 push(@op, $op); # Everything at higher priority has been executed
110 # This is the heart of our little interpreter. Here, we evaluate
111 # a logical expression and return its value.
113 local(*expr) = shift(@_); # Expression to parse
114 local(@val) = (); # Stack of values
115 local(@op) = (); # Stack of diadic operators
116 local(@mono) =(); # Stack of monadic operators
120 s/^\s+//; # Remove spaces between words
123 &push_val(&eval_expr(*_));
124 # A final '\' indicates an end of line
125 &error("missing final parenthesis") if !s/^\\//;
127 # Found a ')' or end of line
128 elsif (/^\)/ || /^$/) {
129 s/^\)/\\/; # Signals: left parenthesis found
130 $expr = $_; # Remove interpreted stuff
131 &execute() while $#val > 0; # Executed stacked operations
134 &error("missing second operand for '$_' (diadic operator)");
138 # A perl statement '{{'
142 ('perl','-e', "if ($1) {exit 0;} else {exit 1;}"
145 &error("incomplete perl statement");
148 # A shell statement '{'
152 ("if $1 >/dev/null 2>&1; then exit 0; else exit 1; fi"
155 &error("incomplete shell statement");
158 # Operator '||' and '&&'
159 elsif (s/^(\|\||&&)//) {
160 $tmp = $1; # Save for perl5 (Dataloaded update_stack)
167 # Everything else is a test for a defined value
168 elsif (s/^([\?%]?\w+)//) {
171 if ($tmp =~ s/^\?//) {
172 &push_val(($main'symwanted{$tmp})? 1 : 0);
174 # Test for conditionally wanted
175 elsif ($tmp =~ s/^%//) {
176 &push_val(($main'condwanted{$tmp})? 1 : 0);
178 # Default: test for definition (see op @define)
181 $main'symwanted{$tmp} ||
182 $main'cmaster{$tmp} ||
183 $main'userdef{$tmp}) ? 1 : 0);
186 # An error occured -- we did not recognize the expression
188 s/^([^\s\(\)\{\|&!]+)//; # Skip until next meaningful char
193 # Given an expression in a '@' command, returns a boolean which is
194 # the result of the evaluation. Evaluate is collecting all the lines
195 # in the expression into a single string, and then calls eval_expr to
196 # really evaluate it.
198 local($val); # Value returned
199 local($expr) = ""; # Expression to be parsed
201 while (s/\\$//) { # While end of line escaped
203 $_ = <UNIT>; # Fetch next line
205 &error("EOF in expression");
211 while ($expr ne '') {
212 $val = &eval_expr(*expr); # Expression will be modified
213 # We return from eval_expr either when a closing parenthisis
214 # is found, or when the expression has been fully analysed.
215 &error("extra closing parenthesis ignored") if $expr ne '';
220 # Given a line, we search for commands (lines starting with '@').
221 # If there is no command in the line, then we return the boolean state.
222 # Otherwise, the command is analysed and a new state is computed.
223 # The returned value of interpret is 1 if the line is to be printed.
226 local($status) = $state[$#state]; # Current status
227 if (s|^\s*@\s*(\w+)\s*(.*)|$2|) {
229 $cmd =~ y/A-Z/a-z/; # Canonicalize to lower case
230 # The 'define' command
231 if ($cmd eq 'define') {
233 $userdef{$_}++ if $Keep[$status];
237 elsif ($cmd eq 'if') {
238 # We always evaluate, in order to find possible errors
239 $value = &evaluate($_);
240 if (!$Keep[$status]) {
241 # We have to skip until next 'end'
242 push(@state, $SKIP); # Record structure
254 elsif ($cmd eq 'else') {
255 &error("expression after 'else' ignored") if /\S/;
256 $state[$#state] = $SKIP if $state[$#state] == $IF;
257 return 0 if $state[$#state] == $SKIP;
258 if ($state[$#state] == $OUT) {
259 &error("unexpected 'else'");
262 $state[$#state] = $ELSE;
265 # The 'elsif' command
266 elsif ($cmd eq 'elsif') {
267 # We always evaluate, in order to find possible errors
268 $value = &evaluate($_);
269 $state[$#state] = $SKIP if $state[$#state] == $IF;
270 return 0 if $state[$#state] == $SKIP;
271 if ($state[$#state] == $OUT) {
272 &error("unexpected 'elsif'");
276 $state[$#state] = $IF;
279 $state[$#state] = $NOT;
284 elsif ($cmd eq 'end') {
285 &error("expression after 'end' ignored") if /\S/;
287 &error("unexpected 'end'") if $#state < 0;
292 &error("unknown command '$cmd'");