BEGIN CLASS ENV; BEGIN CLASS ITEM(N, X); TEXT N; REAL X; BEGIN REF(ITEM) NEXT; NEXT :- HEAD; HEAD :- THIS ITEM; END ITEM; REF(ITEM) HEAD; REF(ITEM) PROCEDURE LOOKUP(V); TEXT V; BEGIN REF(ITEM) I; BOOLEAN FOUND; I :- HEAD; WHILE NOT FOUND DO IF I == NONE OR ELSE I.N = V THEN FOUND := TRUE ELSE I :- I.NEXT; LOOKUP :- I; END LOOKUP; REF(ENV) PROCEDURE SET(V, X); TEXT V; REAL X; BEGIN REF(ITEM) I; I :- LOOKUP(V); IF I == NONE THEN I :- NEW ITEM(V, X) ELSE I.X := X; SET :- THIS ENV; END SET; REAL PROCEDURE GET(V); TEXT V; GET := LOOKUP(V).X; END ENV; CLASS EXPR(EV); REF(ENV) EV; BEGIN REAL PROCEDURE POP; BEGIN IF STACKPOS > 0 THEN BEGIN STACKPOS := STACKPOS - 1; POP := STACK(STACKPOS); END; END POP; PROCEDURE PUSH(NEWTOP); REAL NEWTOP; BEGIN STACK(STACKPOS) := NEWTOP; STACKPOS := STACKPOS + 1; END PUSH; REAL PROCEDURE CALC(OPERATOR, ERR); CHARACTER OPERATOR; LABEL ERR; BEGIN REAL X, Y; X := POP; Y := POP; IF OPERATOR = '+' THEN PUSH(Y + X) ELSE IF OPERATOR = '-' THEN PUSH(Y - X) ELSE IF OPERATOR = '*' THEN PUSH(Y * X) ELSE IF OPERATOR = '/' THEN BEGIN IF X = 0 THEN BEGIN EVALUATEDERR :- "DIV BY ZERO"; GOTO ERR; END; PUSH(Y / X); END ELSE IF OPERATOR = '^' THEN PUSH(Y ** X) ELSE BEGIN EVALUATEDERR :- "UNKNOWN OPERATOR"; GOTO ERR; END END CALC; PROCEDURE READCHAR(CH); NAME CH; CHARACTER CH; BEGIN IF T.MORE THEN CH := T.GETCHAR ELSE CH := EOT; END READCHAR; PROCEDURE SKIPWHITESPACE(CH); NAME CH; CHARACTER CH; BEGIN WHILE (CH = SPACE) OR (CH = TAB) OR (CH = CR) OR (CH = LF) DO READCHAR(CH); END SKIPWHITESPACE; PROCEDURE BUSYBOX(OP, ERR); INTEGER OP; LABEL ERR; BEGIN CHARACTER OPERATOR; REAL NUMBR; BOOLEAN NEGATIVE; SKIPWHITESPACE(CH); IF OP = EXPRESSION THEN BEGIN NEGATIVE := FALSE; WHILE (CH = '+') OR (CH = '-') DO BEGIN IF CH = '-' THEN NEGATIVE := NOT NEGATIVE; READCHAR(CH); END; BUSYBOX(TERM, ERR); IF NEGATIVE THEN BEGIN NUMBR := POP; PUSH(0 - NUMBR); END; WHILE (CH = '+') OR (CH = '-') DO BEGIN OPERATOR := CH; READCHAR(CH); BUSYBOX(TERM, ERR); CALC(OPERATOR, ERR); END; END ELSE IF OP = TERM THEN BEGIN BUSYBOX(FACTOR, ERR); WHILE (CH = '*') OR (CH = '/') DO BEGIN OPERATOR := CH; READCHAR(CH); BUSYBOX(FACTOR, ERR); CALC(OPERATOR, ERR) END END ELSE IF OP = FACTOR THEN BEGIN BUSYBOX(POWER, ERR); WHILE CH = '^' DO BEGIN OPERATOR := CH; READCHAR(CH); BUSYBOX(POWER, ERR); CALC(OPERATOR, ERR) END END ELSE IF OP = POWER THEN BEGIN IF (CH = '+') OR (CH = '-') THEN BUSYBOX(EXPRESSION, ERR) ELSE IF (CH >= '0') AND (CH <= '9') THEN BUSYBOX(NUMBER, ERR) ELSE IF (CH >= 'A') AND (CH <= 'Z') THEN BUSYBOX(VARIABLE, ERR) ELSE IF CH = '(' THEN BEGIN READCHAR(CH); BUSYBOX(EXPRESSION, ERR); IF CH = ')' THEN READCHAR(CH) ELSE GOTO ERR; END ELSE GOTO ERR; END ELSE IF OP = VARIABLE THEN BEGIN TEXT VARNAM; VARNAM :- BLANKS(32); WHILE (CH >= 'A') AND (CH <= 'Z') OR (CH >= '0') AND (CH <= '9') DO BEGIN VARNAM.PUTCHAR(CH); READCHAR(CH); END; PUSH(EV.GET(VARNAM.STRIP)); END ELSE IF OP = NUMBER THEN BEGIN NUMBR := 0; WHILE (CH >= '0') AND (CH <= '9') DO BEGIN NUMBR := 10 * NUMBR + RANK(CH) - RANK('0'); READCHAR(CH); END; IF CH = '.' THEN BEGIN REAL FAKTOR; READCHAR(CH); FAKTOR := 10; WHILE (CH >= '0') AND (CH <= '9') DO BEGIN NUMBR := NUMBR + (RANK(CH) - RANK('0')) / FAKTOR; FAKTOR := 10 * FAKTOR; READCHAR(CH); END; END; PUSH(NUMBR); END; SKIPWHITESPACE(CH); END BUSYBOX; BOOLEAN PROCEDURE EVAL(INP); TEXT INP; BEGIN EVALUATEDERR :- NOTEXT; STACKPOS := 0; T :- COPY(INP.STRIP); READCHAR(CH); BUSYBOX(EXPRESSION, ERRORLABEL); IF NOT T.MORE AND STACKPOS = 1 AND CH = EOT THEN BEGIN EVALUATED := POP; EVAL := TRUE; GOTO NOERRORLABEL; END; ERRORLABEL: EVAL := FALSE; IF EVALUATEDERR = NOTEXT THEN EVALUATEDERR :- "INVALID EXPRESSION: " & INP; NOERRORLABEL: END EVAL; REAL PROCEDURE RESULT; RESULT := EVALUATED; TEXT PROCEDURE ERR; ERR :- EVALUATEDERR; INTEGER EXPRESSION, TERM, FACTOR, POWER, NUMBER, VARIABLE; CHARACTER TAB, LF, CR, SPACE, EOT, CH; REAL ARRAY STACK(0:31); INTEGER STACKPOS; REAL EVALUATED; TEXT EVALUATEDERR, T; EXPRESSION := 1; TERM := 2; FACTOR := 3; POWER := 4; NUMBER := 5; VARIABLE := 6; TAB := CHAR(9); LF := CHAR(10); CR := CHAR(13); SPACE := CHAR(32); EOT := CHAR(0); END EXPR; REF(EXPR) EXA, EXB; EXA :- NEW EXPR(NEW ENV.SET("X", 3)); EXB :- NEW EXPR(NEW ENV.SET("X", 5)); IF EXA.EVAL("2 ^ X") THEN BEGIN IF EXB.EVAL("2 ^ X") THEN OUTFIX(EXB.RESULT - EXA.RESULT, 3, 10) ELSE OUTTEXT(EXB.ERR) END ELSE OUTTEXT(EXA.ERR); OUTIMAGE; END.