x
x
x


Do so in a way which:

 does not involve string manipulation of the input source code
 is plausibly extensible to a runtime-chosen set of bindings rather than just x
 does not make x a global variable


or note that these are impossible.



See also

 For more general examples and language-specific details, see Eval.
 Dynamic variable names is a similar task.


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.
 