/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* $Header: b3int.c,v 1.4 85/08/22 16:58:27 timo Exp $ */ /* B interpreter using theaded trees */ #include "b.h" #include "b0fea.h" #include "b1mem.h" #include "b1obj.h" #include "b2nod.h" #include "b3err.h" #include "b3sem.h" #include "b3env.h" #include "b3int.h" #include "b3in2.h" #include "b3sta.h" /* Relicts from old system: */ Visible value resval; Visible bool terminated; /* Shorthands: */ #define Pop2(fun) (w = pop(), v = pop(), fun(v, w), release(v), release(w)) #define Pop1(fun) (v = pop(), fun(v), release(v)) #define Dyop(funvw) \ (w = pop(), v = pop(), push(funvw), release(v), release(w)) #define Monop(funv) (v = pop(), push(funv), release(v)) #define Flagged() (Thread2(pc) != NilTree) #define LocFlagged() (Thread2(pc) != NilTree && !noloc) #define ValOrLoc(feval, floc) (LocFlagged() ? (floc) : (feval)) #define Jump() (tracing && tr_jump(), next = Thread2(pc)) #define Comp(op) (w = pop(), v = pop(), report = (compare(v, w) op 0), Comp2()) #define Comp2() (release(v), !Flagged() ? release(w) : Comp3()) #define Comp3() (report ? push(w) : (Jump(), release(w))) #define F(n) ((value)*Branch(pc, (n))) /* Execute a threaded tree until the end or until a terminating-command. The boolean argument 'wantvalue' tells whether it must deliver a value or not. */ Hidden value run(start, wantvalue) parsetree start; bool wantvalue; { value u, v, w; int k; bool X, Y; int call_stop= call_level; #ifdef IBMPC int loopcnt= 0; #endif parsetree old_next= next; /* While run can be used recursively, save some state info */ next= start; for (;;) { #ifdef IBMPC if (loopcnt++ == 100) { bdos(0x2c, 0, 0); /* forcing a DOS function call (get time) */ /* so that a break interrupt can be executed */ loopcnt= 0; } #endif if (!still_ok) break; pc= next; if (pc == Halt) { error(MESS(3500, "unexpected program halt")); break; } if (!Is_parsetree(pc)) { if (pc == Stop) { if (call_level == call_stop) break; ret(); continue; } if (!Is_number(pc)) syserr(MESS(3501, "run: bad thread")); switch (intval(pc)) { case 0: pc= Stop; break; case 1: error( MESS(3502, "none of the alternative tests of SELECT succeeds")); break; case 2: if (resexp == Rep) error(MESS(3503, "TEST-unit reports no outcome")); else error(MESS(3504, "YIELD-unit returns no value")); break; case 3: if (resexp == Rep) error(MESS(3505, "test-refinement reports no outcome")); else error(MESS(3506, "refinement returns no value")); /* "expression-" seems superfluous here */ break; default: v= convert(pc, No, No); error3(MESS(3507, "run-time error "), v, 0); release(v); } continue; } next = Thread(pc); if (tracing) tr_node(pc); /* <<<<<<<<<<<<<<<< */ switch (Nodetype(pc)) { case HOW_TO: case REFINEMENT: error(MESS(3508, "run: cannot execute unit-definition")); break; case YIELD: case TEST: switch (Nodetype(F(FPR_FORMALS))) { case TAG: break; case MONF: case MONPRD: w= pop(); v= pop(); put(v, w); release(v); release(w); break; case DYAF: case DYAPRD: w= pop(); v= pop(); u= pop(); put(u, w); release(u); release(w); u= pop(); put(u, v); release(u); release(v); break; default: syserr(MESS(3509, "bad FPR_FORMAL")); } break; /* Commands */ case SUITE: curlino = F(SUI_LINO); curline = F(SUI_CMD); break; case IF: case AND: case WHILE: case TEST_SUITE: if (!report) Jump(); break; case OR: if (report) Jump(); break; case FOR: w= pop(); v= pop(); if (!in_ranger(v, &w)) { release(v); release(w); Jump(); } else { push(v); push(w); } break; case PUT: Pop2(put_with_check); break; case INSERT: Pop2(l_insert); break; case REMOVE: Pop2(l_remove); break; case CHOOSE: Pop2(choose); break; case DRAW: Pop1(draw); break; case SET_RANDOM: Pop1(set_random); break; case DELETE: Pop1(l_delete); break; case CHECK: if (!report) checkerr(); break; case WRITE: nl(F(WRT_L_LINES)); if (F(WRT_EXPR)) { v = pop(); writ(v); release(v); } nl(F(WRT_R_LINES)); break; case READ: Pop2(read_eg); break; case READ_RAW: Pop1(read_raw); break; case QUIT: if (resexp != Voi) error(MESS(3510, "QUIT may only occur in a HOW'TO or command-refinement")); if (call_level == 0 && still_ok) terminated= Yes; next= Stop; break; case RETURN: if (resexp != Ret) error(MESS(3511, "RETURN may only occur in a YIELD or expression-refinement")); resval = pop(); next= Stop; break; case REPORT: if (resexp != Rep) error(MESS(3512, "REPORT may only occur in a TEST-unit or test-refinement")); next= Stop; break; case SUCCEED: if (resexp != Rep) error(MESS(3513, "SUCCEED may only occur in a TEST-unit or test-refinement")); report = Yes; next= Stop; break; case FAIL: if (resexp != Rep) error(MESS(3514, "FAIL may only occur in a TEST-unit or test-refinement")); report = No; next= Stop; break; case USER_COMMAND: x_user_command(F(UCMD_NAME), F(UCMD_ACTUALS), F(UCMD_DEF)); break; case EXTENDED_COMMAND: #ifdef EXT_COMMAND x_extended_command(F(ECMD_NAME), F(ECMD_ACTUALS)); #endif break; /* Expressions, targets */ case COLLATERAL: v = mk_compound(k= Nfields(F(COLL_SEQ))); while (--k >= 0) *Field(v, k) = pop(); push(v); break; /* Expressions, targets */ case SELECTION: Dyop(ValOrLoc(associate(v, w), tbsel_loc(v, w))); break; case BEHEAD: w= pop(); v= pop(); push(LocFlagged() ? trim_loc(v, w, '@') : behead(v, w)); release(v); release(w); break; case CURTAIL: w= pop(); v= pop(); push(LocFlagged() ? trim_loc(v, w, '|') : curtail(v, w)); release(v); release(w); break; case MONF: v = pop(); formula(Vnil, F(MON_NAME), v, F(MON_FCT)); release(v); break; case DYAF: w = pop(); v = pop(); formula(v, F(DYA_NAME), w, F(DYA_FCT)); release(v); release(w); break; case TEXT_LIT: v= F(XLIT_TEXT); if (F(XLIT_NEXT)) { w= pop(); v= concat(v, w); release(w); } else copy(v); push(v); break; case TEXT_CONV: if (F(XCON_NEXT)) w= pop(); u= pop(); v= convert(u, Yes, Yes); release(u); if (F(XCON_NEXT)) { v= concat(u= v, w); release(u); release(w); } push(v); break; case ELT_DIS: push(mk_elt()); break; case LIST_DIS: u = mk_elt(); k= Nfields(F(LDIS_SEQ)); while (--k >= 0) { insert(v = pop(), &u); release(v); } push(u); break; case RANGE_DIS: Dyop(mk_range(v, w)); break; case TAB_DIS: u = mk_elt(); k= Nfields(F(TDIS_SEQ)); while ((k -= 2) >= 0) { w = pop(); v = pop(); /* Should check for same key with different associate */ replace(w, &u, v); release(v); release(w); } push(u); break; /* Tests */ case NOT: report = !report; break; /* Quantifiers can be described as follows: Report X at first test which reports Y. If no test reports Y, report !X. type X Y SOME Yes Yes EACH No No NO No Yes. */ case EACH_IN: X= Y= No; goto quant; case NO_IN: X= No; Y= Yes; goto quant; case SOME_IN: X= Y= Yes; quant: w= pop(); v= pop(); if (Is_compound(w) && report == Y) { report= X; Jump(); } else if (!in_ranger(v, &w)) { report= !X; Jump(); } else { push(v); push(w); break; } release(v); release(w); break; case EACH_PARSING: X= Y= No; goto parse; case NO_PARSING: X= No; Y= Yes; goto parse; case SOME_PARSING: X= Y= Yes; parse: w= pop(); v= pop(); if (Is_compound(w) && report == Y) { report= X; Jump(); } else if (!pa_ranger(v, &w)) { report= !X; Jump(); } else { push(v); push(w); break; } release(v); release(w); break; case MONPRD: v = pop(); proposition(Vnil, F(MON_NAME), v, F(MON_FCT)); release(v); break; case DYAPRD: w = pop(); v = pop(); proposition(v, F(DYA_NAME), w, F(DYA_FCT)); release(v); release(w); break; case LESS_THAN: Comp(<); break; case AT_MOST: Comp(<=); break; case GREATER_THAN: Comp(>); break; case AT_LEAST: Comp(>=); break; case EQUAL: Comp(==); break; case UNEQUAL: Comp(!=); break; case TAGformal: call_formal(F(TAG_NAME), F(TAG_ID), LocFlagged()); break; case TAGlocal: push(ValOrLoc(v_local(F(TAG_NAME), F(TAG_ID)), local_loc(F(TAG_ID)))); break; case TAGglobal: push(ValOrLoc(v_global(F(TAG_NAME)), global_loc(F(TAG_NAME)))); break; case TAGmystery: if (LocFlagged()) push(l_mystery(F(TAG_NAME), F(TAG_ID))); else v_mystery(F(TAG_NAME), F(TAG_ID)); break; case TAGrefinement: call_refinement(F(TAG_NAME), F(TAG_ID), Flagged()); break; case TAGzerfun: formula(Vnil, F(TAG_NAME), Vnil, F(TAG_ID)); break; case TAGzerprd: proposition(Vnil, F(TAG_NAME), Vnil, F(TAG_ID)); break; case NUMBER: push(copy(F(NUM_VALUE))); break; default: syserr(MESS(3515, "run: bad node type")); } /* >>>>>>>>>>>>>>>> */ } v = Vnil; if (wantvalue && still_ok) v = pop(); /* Unwind stack when stopped by error: */ while (call_level != call_stop) ret(); next= old_next; return v; } /* External interfaces: */ Visible Procedure execthread(start) parsetree start; { run(start, No); } Visible value evalthread(start) parsetree start; { return run(start, Yes); } Visible Procedure initint() { /* Dummy, relict */ }