# eval --- expression evaluator subroutine eval_cmd define (MOD_OP,'%'c) define (DIVIDE_OP,'/'c) define (MULTIPLY_OP,'*'c) define (PLUS_OP,'+'c) define (MINUS_OP,'-'c) define (LESS_OP,'<'c) define (EQUAL_OP,'='c) define (NOT_OP,'~'c) define (OR_OP,'|'c) define (GREATER_OP,'>'c) define (AND_OP,'&'c) define (XOR_OP,'^'c) define (EXP_OP,-101) define (NOT_EQUAL_OP,-102) define (LESS_EQUAL_OP,-103) define (GREATER_EQUAL_OP,-104) define (LEFT_SHIFT_OP,-105) define (RIGHT_SHIFT_OP,-106) define (CONSTANT,-107) define (IDENT,-108) define (MAX_STACK,16) longint Evalue, Intval integer Estatus, Ebuf (MAXLINE), Ep, Token, Global_call integer getlin character Id (MAXLINE) string syntax_error "syntax error in expression" procedure concatenate_args forward procedure get_token forward procedure search_id forward procedure check_end_of_expr forward procedure expression recursive MAXSTACK forward procedure expr1 recursive MAXSTACK forward procedure expr2 recursive MAXSTACK forward procedure expr3 recursive MAXSTACK forward procedure expr4 recursive MAXSTACK forward procedure term recursive MAXSTACK forward procedure term1 recursive MAXSTACK forward procedure factor recursive MAXSTACK forward procedure factor1 recursive MAXSTACK forward concatenate_args # collect arguments if (Ebuf (1) ~= EOS) { # evaluate expr on command line Estatus = OK get_token Global_call = YES expression if (Estatus == OK) check_end_of_expr if (Estatus == OK) call print (STDOUT, "*l*n"p, Evalue) } else # read expressions from STDIN for (Estatus = OK; getlin (Ebuf, STDIN) ~= EOF; Estatus = OK) { Ep = 1 get_token if (Token == NEWLINE) # ignore blank lines next Global_call = YES expression check_end_of_expr if (Estatus == OK) call print (STDOUT, "*l*n"p, Evalue) } if (Estatus ~= OK) call seterr (1000) stop # concatenate_args --- concatenate arguments into a single string procedure concatenate_args { local j, l, n integer j, l, n integer getarg for ({l = 0; j = 1}; l < MAXLINE - 1; {l += n; j += 1}) { if (l > 0) { # insert blank between args l += 1 Ebuf (l) = ' 'c } n = getarg (j, Ebuf (l + 1), MAXLINE - l) if (n == EOF) break } Ebuf (l + 1) = EOS Ep = 1 # setup character pointer } # get_token --- get next basic token from argument string procedure get_token { local j integer j longint gctol SKIPBL (Ebuf, Ep) select (Ebuf (Ep)) when (SET_OF_LETTERS, '_'c, '$'c) { # identifier Token = IDENT j = 1 repeat { Id (j) = Ebuf (Ep) Ep += 1 j += 1 } until ( ~ ( IS_LETTER (Ebuf (Ep)) || IS_DIGIT (Ebuf (Ep)) || Ebuf (Ep) == '_'c || Ebuf (Ep) == '$'c)) Id (j) = EOS } when (SET_OF_DIGITS) { # constant Token = CONSTANT Intval = gctol (Ebuf, Ep, 10) } when ('<'c) { # < <= << <> Ep += 1 select (Ebuf (Ep)) when ('='c) Token = LESS_EQUAL_OP when ('<'c) Token = LEFT_SHIFT_OP when ('>'c) Token = NOT_EQUAL_OP ifany Ep += 1 else Token = LESS_OP } when ('>'c) { # > >= >> Ep += 1 select (Ebuf (Ep)) when ('='c) Token = GREATER_EQUAL_OP when ('>'c) Token = RIGHT_SHIFT_OP ifany Ep += 1 else Token = GREATER_OP } when ('~'c, '!'c) { # ~ ~= ~< ~> ! != !< !> Ep += 1 select (Ebuf (Ep)) when ('='c) Token = NOT_EQUAL_OP when ('<'c) Token = GREATER_EQUAL_OP when ('>'c) Token = LESS_EQUAL_OP ifany Ep += 1 else Token = NOT_OP } when ('='c) { # = == Ep += 1 if (Ebuf (Ep) == '='c) Ep += 1 Token = EQUAL_OP } when ('*'c) { # * ** Ep += 1 if (Ebuf (Ep) == '*'c) { # exponentiation Ep += 1 Token = EXP_OP } else Token = MULTIPLY_OP } when (EOS, NEWLINE) Token = NEWLINE else { # single character operator Token = Ebuf (Ep) Ep += 1 } } # search_id --- return numeric value of a shell variable procedure search_id { local str, i character str (32) integer i integer svget longint gctol if (svget (Id, str, 32) == EOF) { # fetch variable Intval = 0 call print (ERROUT, "*s: undefined*n"p, Id) Estatus = ERR } else { i = 1 Intval = gctol (str, i, 10) # convert to integer if (str (i) ~= EOS) { Intval = 0 call print (ERROUT, "*s: non-numeric (*s)*n"p, Id, str) Estatus = ERR } } } # check_end_of_expr --- make sure there's no garbage left over procedure check_end_of_expr { if (Token ~= NEWLINE) { Estatus = ERR call print (ERROUT, "*,#s*n"p, Ep - 1, Ebuf) call remark (syntax_error) } } # expression --- evaluate an expression procedure expression { # This procedure evaluates the construct: # # ::= { '|' } local val, op, sp integer sp, op (MAX_STACK) longint val (MAX_STACK) if (Estatus ~= ERR) { if (Global_call ~= NO) sp = 0 sp += 1 expr1 val (sp) = Evalue while (Token == OR_OP || Token == XOR_OP) { op (sp) = Token get_token expr1 select (op (sp)) when (OR_OP) val (sp) |= Evalue when (XOR_OP) val (sp) ^= Evalue else { call remark ("in expression: can't happen"p) Estatus = ERR break } } Evalue = val (sp) sp -= 1 } } # expr1 --- evaluate a single primary expression procedure expr1 { # This procedure evaluates the construct: # # ::= { '&' } local val, sp integer sp longint val (MAX_STACK) if (Estatus ~= ERR) { if (Global_call ~= NO) sp = 0 sp += 1 expr2 val (sp) = Evalue while (Token == AND_OP) { get_token expr2 val (sp) &= Evalue } Evalue = val (sp) sp -= 1 } } # expr2 --- evaluate a single tertiary expression procedure expr2 { # This procedure evaluates the construct: # # ::= [ ] if (Estatus ~= ERR) if (Token == NOT_OP) { get_token expr3 Evalue = not (Evalue) } else expr3 } # expr3 --- evaluate a single secondary expression procedure expr3 { # This procedure evaluates the construct: # # ::= { } local val, op, sp integer sp, op (MAX_STACK) longint val (MAX_STACK) if (Estatus ~= ERR) { if (Global_call ~= NO) sp = 0 sp += 1 expr4 val (sp) = Evalue while (Token == LESS_OP || Token == LESS_EQUAL_OP || Token == EQUAL_OP || Token == NOT_EQUAL_OP || Token == GREATER_EQUAL_OP || Token == GREATER_OP) { op (sp) = Token # save operator get_token expr4 select (op (sp)) when (LESS_OP) val (sp) = rt (val (sp) < Evalue, 1) when (LESS_EQUAL_OP) val (sp) = rt (val (sp) <= Evalue, 1) when (EQUAL_OP) val (sp) = rt (val (sp) == Evalue, 1) when (NOT_EQUAL_OP) val (sp) = rt (val (sp) ~= Evalue, 1) when (GREATER_EQUAL_OP) val (sp) = rt (val (sp) >= Evalue, 1) when (GREATER_OP) val (sp) = rt (val (sp) > Evalue, 1) else { call remark ("in expr2: can't happen"p) Estatus = ERR break } } # while (...) Evalue = val (sp) sp -= 1 } } # expr4 --- evaluate a simple expression procedure expr4 { # This procedure evaluates the construct: # # ::= { } local val, op, sp integer sp, op (MAX_STACK) longint val (MAX_STACK) if (Estatus ~= ERR) { if (Global_call ~= NO) sp = 0 sp += 1 term val (sp) = Evalue while (Token == PLUS_OP || Token == MINUS_OP) { op (sp) = Token get_token term select (op (sp)) when (PLUS_OP) val (sp) += Evalue when (MINUS_OP) val (sp) -= Evalue else { call remark ("in expr4: can't happen"p) Estatus = ERR break } } Evalue = val (sp) sp -= 1 } } # term --- evaluate a single term procedure term { # This procedure evaluates the construct: # # ::= { } local val, op, sp integer sp, op (MAX_STACK) longint val (MAX_STACK) if (Estatus ~= ERR) { if (Global_call ~= NO) sp = 0 sp += 1 term1 val (sp) = Evalue while (Token == MULTIPLY_OP || Token == DIVIDE_OP || Token == MOD_OP || Token == LEFT_SHIFT_OP || Token == RIGHT_SHIFT_OP) { op (sp) = Token # save operator get_token term1 select (op (sp)) when (MULTIPLY_OP) val (sp) *= Evalue when (DIVIDE_OP) val (sp) /= Evalue when (MOD_OP) val (sp) %= Evalue when (LEFT_SHIFT_OP) val (sp) = ls (val (sp), Evalue) when (RIGHT_SHIFT_OP) val (sp) = rs (val (sp), Evalue) else { call remark ("in term: can't happen"p) Estatus = ERR break } } Evalue = val (sp) sp -= 1 } } # term1 --- evaluate one secondary term procedure term1 { # This procedure evaluates the construct: # # ::= { } local val, sp integer sp longint val (MAX_STACK) if (Estatus ~= ERR) { if (Global_call ~= NO) sp = 0 sp += 1 factor val (sp) = Evalue while (Token == EXP_OP) { get_token factor val (sp) = val (sp) ** Evalue } Evalue = val (sp) sp -= 1 } } # factor --- evaluate a single factor procedure factor { # This procedure evaluates the construct: # # ::= [ ] if (Estatus ~= ERR) if (Token == MINUS_OP) { get_token factor1 Evalue = - Evalue } else factor1 } # factor1 --- evaluate a single primary factor procedure factor1 { # This procedure evaluates the construct: # # ::= | | '(' ')' if (Estatus ~= ERR) if (Token == IDENT || Token == CONSTANT) { if (Token == IDENT) search_id Evalue = Intval get_token } elif (Token == '('c) { get_token Global_call = NO # indicate that this is a recursive call expression if (Token == ')'c) get_token else { call remark ("missing right parenthesis"p) Estatus = ERR } } else { call print (ERROUT, "*,#s*n"p, Ep - 1, Ebuf) call remark (syntax_error) Estatus = ERR } } end