|
|
1.1 root 1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
2:
3: /*
4: $Header: b3sta.c,v 1.4 85/08/22 16:59:30 timo Exp $
5: */
6:
7: /* Stacks used by the interpreter */
8:
9: /* Scratch-pad copying.
10:
11: One of the hairiest details of B is scratch-pad copying and its
12: interaction with formal parameters (to HOW'TO units).
13: Via formal parameters one can peek and poke into the local environment
14: of the HOW'TO's in the call chain. When a parameter is changed from
15: within an expression- or test-refinement, the scratch-pad copying
16: prescribes that the whole chain of local environments is restored
17: to its original state when the refinement exits. Example:
18:
19: >>> HOW'TO X fp:
20: WRITE fp, ref, fp /
21: ref:
22: PUT fp+1 IN fp
23: RETURN fp
24: >>> HOW'TO Y fp:
25: X fp
26: >>> HOW'TO Z:
27: PUT 1 IN t
28: Y t
29: WRITE t
30: >>> Z
31: 1 2 1
32: 1
33:
34: It is clear that the scratch-pad copying for the call of ref in X
35: must save the local environments of Y and Z, and restore them when
36: ref exits.
37: For similar reasons we must save the permanent environment.
38: All this also interacts with the practice of 'locating' a target.
39: All targets eventually refer to (one or more) basic targets.
40: The location of a basic target is represented as a pair (env, key)
41: where 'env' is the address of the environment in which the target
42: resides and 'key' is the target's name (for permanent targets) or
43: its number (for local targets). When we consider the PUT fp+1 IN fp
44: line in unit X above, we can see that the (local) environment
45: for the location returned by 'fp' is the local environment of Z.
46: Therefore this whole chain must still be intact.
47: There can be even trickier cases, where a location is saved for a
48: long time on the execution stack while the environment it refers to
49: is subject to scratch-pad copying and restoring; when the location
50: is finally popped off the stack, it must still refer to the correct
51: environment.
52:
53: Another detail to consider is that for the permanent environment,
54: we need access to the 'real' permanent environment, i.e., its value
55: before any scratch-pad copying occurred. (Example:
56:
57: >>> YIELD f:
58: SHARE x
59: PUT x+1 IN x
60: READ t EG 0
61: RETURN t
62: >>> PUT 0 IN x
63: >>> WRITE x, f, x
64: ??? x
65: 0, 0, 0
66: >>>
67:
68: Even though at the time the READ is called, x has been given the value
69: 1 temporarily, the value of x used in the evaluation of the input
70: expression is the original value, 0.)
71:
72: A final detail to be observed is the passing back of 'bound tags'
73: when a refined test is called.
74:
75: The chosen implementation is as follows:
76: - Environments are saved in a linked list of structures (envchain) with
77: two fields: tab, the actual environment (a table or compound) and
78: inv_env, the link to the previous entry in the list.
79: - The routines newenvchain and popenvchain push and pop such lists.
80: - There is one list for the permanent environment, whose head is prmnv,
81: and one list for the current environment, whose head is usually curnv.
82: The last element of both lists is actually the same, because at the
83: immediate command level the current environment is the permanent
84: environment. When we are evaluating or locating a formal parameter,
85: 'curnv' points somewhere in the middle of its chain, to the local
86: environment of the caller.
87: The two lists are manipulated separately:
88: - Prmnv is pushed (with a copy of itself) for each scratch-pad copy,
89: and popped whe a scratch-pad is thrown away.
90: - Curnv is pushed for each unit invocation, with the new local
91: environment, and popped when the unit exits.
92: - When a scratch-pad copy is required, the chain headed by curnv
93: is walked until a local environment is found without HOW'TO formal
94: parameters, and a compound containing copies of all the local
95: environments thus found is saved on the general-purpose value stack.
96: This value is popped off that stack again and the local environments
97: in the chain are restored when the scratch-pad copy has to be thrown
98: away. (Thus we work on the real thing and save and restore a copy
99: of it, while the DP prescribes that the system work on a copy.
100: The effect is the same, of course.)
101: - There is a third list for bound tags whose treatment is left as an
102: exercise for the reader.
103: - When a formal parameter is called, the current value of 'curnv' must
104: be saved somewhere, so that it can be restored later; in this case
105: it doesn't follow the stack-wise discipline of the chain.
106: - Finally note thate that when a YIELD unit is called during the
107: evaluation of a formal parameter, the chain of local environments
108: "splices" temorarily, because the new local environment is linked
109: to curnv which is not the end of the chain. No problem!
110:
111: All this nonsense can be avoided when a copy-restore parameter mechanism
112: is used instead: then there are no accesses to other local environments
113: that the current, except a transfer between two "adjacent" ones at call
114: and return time. Maybe ABC will have such a parameter mechanism...
115:
116: */
117:
118: #include "b.h"
119: #include "b1mem.h"
120: #include "b1obj.h"
121: #include "b2nod.h"
122: #include "b3env.h"
123: #include "b3err.h"
124: #include "b3int.h"
125: #include "b3sem.h"
126: #include "b3sou.h" /* for permkey() and get_pname() */
127: #include "b3sta.h"
128:
129: /* Fundamental registers: (shared only between this file and b3int.c) */
130:
131: Visible parsetree pc; /* 'Program counter', current parsetree node */
132: Visible parsetree next; /* Next parsetree node (changed by jumps) */
133: Visible bool report; /* 'Condition code register', outcome of last test */
134:
135: Visible bool noloc; /* Set while evaluating (as opposed to locating)
136: formal parameters of HOW'TOs */
137:
138: Hidden env boundtags; /* Holds bound tags chain */
139:
140: /* Value stack: */
141:
142: /* The run-time value stack grows upward, sp points to the next free entry.
143: Allocated stack space lies between st_base and st_top.
144: In the current invocation, the stack pointer (sp) must lie between
145: st_bottom and st_top.
146: Stack overflow is corrected by growing st_top, underflow is a fatal
147: error (generated code is wrong).
148: */
149:
150: Hidden value *st_base, *st_bottom, *st_top, *sp;
151: Visible int call_level; /* While run() can be called recursively */
152:
153: #define EmptyStack() (sp == st_bottom)
154: #define BotOffset() (st_bottom - st_base)
155: #define SetBotOffset(n) (st_bottom= st_base + (n))
156:
157: #define INCREMENT 100
158:
159: Hidden Procedure st_grow(incr) int incr; {
160: if (!st_base) { /* First time ever */
161: st_bottom= sp= st_base=
162: (value*) getmem((unsigned) incr * sizeof(value *));
163: st_top= st_base + incr;
164: }
165: else {
166: int syze= (st_top - st_base) + incr;
167: int n_bottom= BotOffset();
168: int n_sp= sp - st_base;
169: regetmem((ptr*) &st_base, (unsigned) syze * sizeof(value *));
170: sp = st_base + n_sp;
171: SetBotOffset(n_bottom);
172: st_top= st_base + syze;
173: }
174: }
175:
176: Visible value pop() {
177: if (sp <= st_bottom) {
178: syserr(MESS(4100, "stack underflow"));
179: return Vnil;
180: }
181: return *--sp;
182: }
183:
184: Visible Procedure push(v) value v; {
185: if (sp >= st_top) st_grow(INCREMENT);
186: *sp++ = (v);
187: }
188:
189: /* - - - */
190:
191: /* Various call types, used as index in array: */
192:
193: #define C_prmnv 0
194: #define C_immexp 1
195: #define C_immcmd 2
196: #define C_read 3
197:
198: #define C_howto 4
199: #define C_yield 5
200: #define C_test 6
201:
202: #define C_refcmd 7
203: #define C_refexp 8
204: #define C_reftest 9
205:
206: #define C_formal 10
207:
208:
209: /* What can happen to a thing: */
210:
211: #define Old 'o'
212: #define Cpy 'c'
213: #define New 'n'
214: #define Non '-'
215:
216: typedef struct {
217: literal do_cur;
218: literal do_prm;
219: literal do_bnd;
220: literal do_for;
221: literal do_cntxt;
222: literal do_resexp;
223: } dorecord;
224:
225:
226: /* Table encoding what to save/restore for various call/return types: */
227: /* (Special cases are handled elsewhere.) */
228:
229: Hidden dorecord doo[] = {
230: /* cur prm bnd for cntxt resexp */
231:
232: /* prmnv */ {Old, Old, Old, Old, In_prmnv, Voi},
233: /* imm expr */ {Old, Old, Old, Old, In_command, Voi},
234: /* imm cmd */ {Old, Old, Old, Old, In_command, Voi},
235: /* READ EG */ {Non, Non, Non, Non, In_read, Voi},
236:
237: /* HOW-TO */ {New, Old, Non, New, In_unit, Voi},
238: /* YIELD */ {New, Cpy, Non, Non, In_unit, Ret},
239: /* TEST */ {New, Cpy, Non, Non, In_unit, Rep},
240:
241: /* REF-CMD */ {Old, Old, Old, Old, In_unit, Voi},
242: /* ref-expr */ {Cpy, Cpy, Non, Old, In_unit, Ret},
243: /* ref-test */ {Cpy, Cpy, New, Old, In_unit, Rep},
244:
245: /* formal */ {Non, Old, Non, Non, In_formal, Voi},
246: };
247:
248: #define MAXTYPE ((sizeof doo) / (sizeof doo[0]))
249:
250: #define Checksum(type) (12345 - (type)) /* Reversible */
251:
252:
253: #define Ipush(n) push(MkSmallInt(n))
254: #define Ipop() SmallIntVal(pop())
255:
256:
257: Hidden env newenv(tab, inv_env) envtab tab; env inv_env; {
258: env e= (env) getmem(sizeof(envchain));
259: e->tab= tab; /* Eats a reference to tab! */
260: e->inv_env= inv_env;
261: return e;
262: }
263:
264:
265: Hidden Procedure popenv(pe) env *pe; {
266: env e= *pe;
267: *pe= e->inv_env;
268: release(e->tab);
269: freemem((ptr) e);
270: }
271:
272:
273: Forward value save_curnv_chain();
274:
275: Hidden Procedure call(type, new_pc) intlet type; parsetree new_pc; {
276: if (type < 0 || type >= MAXTYPE) syserr(MESS(4101, "bad call type"));
277: if (tracing) tr_call();
278:
279: /* Push other stacks */
280:
281: if (doo[type].do_bnd != Old) {
282: boundtags= newenv(
283: (doo[type].do_bnd == New) ? mk_elt() : Vnil,
284: boundtags);
285: bndtgs= &boundtags->tab;
286: }
287: switch (doo[type].do_cur) {
288:
289: case New:
290: curnv= newenv(Vnil, curnv);
291: break;
292:
293: case Cpy:
294: push(save_curnv_chain());
295: break;
296:
297: case Non:
298: push(mk_int((double) ((int) curnv)));
299: /* PORTABILITY?!?! */
300: break;
301:
302: }
303: if (doo[type].do_prm != Old) {
304: prmnv= newenv(
305: (doo[type].do_prm == Cpy) ? copy(prmnv->tab) : Vnil,
306: prmnv);
307: }
308:
309: /* Push those things that depend on the call type: */
310:
311: if (doo[type].do_for != Old) {
312: /* Formal parameter context and unit name/type */
313: /* FP removed */
314: push(uname); uname= Vnil;
315: }
316:
317: /* Push miscellaneous context info: */
318: push(curline);
319: push(curlino);
320: Ipush(noloc); noloc= No;
321: Ipush(resexp); resexp= doo[type].do_resexp;
322: Ipush(cntxt); cntxt= doo[type].do_cntxt;
323: resval= Vnil;
324:
325: /* Push vital data: */
326: push(next);
327: Ipush(BotOffset()); ++call_level;
328: Ipush(Checksum(type)); /* Kind of checksum */
329:
330: /* Set st_bottom and jump: */
331: st_bottom= sp;
332: next= new_pc;
333: }
334:
335:
336: Visible Procedure ret() {
337: int type; value rv= resval; literal re= resexp;
338: value oldcurnvtab= Vnil, oldbtl= Vnil;
339:
340: if (tracing) tr_ret();
341: if (cntxt == In_formal && still_ok) { rv= pop(); re= Ret; }
342:
343: /* Clear stack: */
344: while (!EmptyStack()) release(pop());
345:
346: /* Pop type and hope it's good: */
347: st_bottom= st_base; /* Trick to allow popping the return info */
348: type= Checksum(Ipop());
349: if (type < 0 || type >= MAXTYPE) syserr(MESS(4102, "stack clobbered"));
350:
351: /* Pop vital data: */
352: SetBotOffset(Ipop()); --call_level;
353: next= pop();
354:
355: /* Pop context info: */
356: cntxt= Ipop();
357: resexp= Ipop();
358: noloc= Ipop();
359: curlino= pop();
360: curline= pop();
361:
362: /* Variable part: */
363: if (doo[type].do_for != Old) {
364: release(uname); uname= pop();
365: /* FP removed */
366: }
367: if (doo[type].do_prm != Old)
368: popenv(&prmnv);
369: switch (doo[type].do_cur) {
370:
371: case Cpy:
372: oldcurnvtab= copy(curnv->tab);
373: rest_curnv_chain(pop());
374: break;
375:
376: case New:
377: oldcurnvtab= copy(curnv->tab);
378: popenv(&curnv);
379: break;
380:
381: case Non:
382: { value v= pop();
383: curnv= (env) intval(v);
384: release(v);
385: }
386: break;
387:
388: }
389: if (doo[type].do_bnd != Old) {
390: oldbtl= copy(*bndtgs);
391: popenv(&boundtags);
392: bndtgs= &boundtags->tab;
393: }
394:
395: /* Fiddle bound tags */
396: if (oldbtl != Vnil) {
397: extbnd_tags(oldbtl, oldcurnvtab);
398: release(oldbtl);
399: }
400: if (oldcurnvtab != Vnil) release(oldcurnvtab);
401: if (call_level == 0) re_env(); /* Resets bndtgs */
402:
403: /* Push return value (if any): */
404: if (re == Ret && still_ok) push(rv);
405: }
406:
407: /* - - - */
408:
409: Visible Procedure call_formal(name, number, targ)
410: value name, number; bool targ; {
411: value *aa= envassoc(curnv->tab, number); formal *ff= Formal(*aa);
412: literal ct;
413: if (aa == Pnil || !Is_formal(*aa)) syserr(MESS(4103, "formal gone"));
414: if (cntxt != In_formal) {
415: release(how_context.uname);
416: sv_context(&how_context); /* for error messages */
417: }
418: call(C_formal, ff->fp);
419:
420: /* The following should be different, but for now... */
421: curnv= ff->con.curnv;
422: release(uname); uname= copy(ff->con.uname);
423: curline= ff->con.cur_line; curlino= ff->con.cur_lino;
424: ct= cntxt; cntxt= ff->con.cntxt;
425: release(act_context.uname);
426: sv_context(&act_context); cntxt= ct; /* for error messages */
427:
428: if (!targ) noloc= Yes;
429: else if (!Thread2(next)) error(MESS(4104, "expression used as target"));
430: }
431:
432: Visible Procedure call_refinement(name, def, test)
433: value name; parsetree def; bool test; {
434: call(test ? C_reftest : C_refexp,
435: *Branch(Refinement(def)->rp, REF_START));
436: }
437:
438: #define YOU_TEST MESS(4105, "You haven't told me how to TEST ")
439: #define YOU_YIELD MESS(4106, "You haven't told me how to YIELD ")
440:
441: Hidden Procedure udfpr(nd1, name, nd2, isfunc)
442: value nd1, name, nd2; bool isfunc; {
443: value *aa;
444: parsetree u; int k, nlocals; funprd *fpr;
445: int adicity= nd1 ? Dya : nd2 ? Mon : Zer;
446: if (!is_unit(name, adicity, &aa)
447: || !(isfunc ? Is_function(*aa) : Is_predicate(*aa))) {
448: error3(isfunc ? YOU_YIELD : YOU_TEST, name, 0);
449: return;
450: }
451: fpr= Funprd(*aa);
452: if (!(fpr->adic==Zer ? nd2==Vnil : (fpr->adic==Mon) == (nd1==Vnil)))
453: syserr(MESS(4107, "invoked unit has other adicity than invoker"));
454: if (fpr->pre != Use) syserr(MESS(4108, "udfpr with predefined unit"));
455:
456: u= fpr->unit;
457: if (fpr->unparsed) fix_nodes(&u, &fpr->code);
458: if (!still_ok) { rem_unit(u); return; }
459: fpr->unparsed= No;
460: nlocals= intval(*Branch(u, FPR_NLOCALS));
461: call(isfunc ? C_yield : C_test, fpr->code);
462: curnv->tab= mk_compound(nlocals);
463: for (k= 0; k < nlocals; ++k) *Field(curnv->tab, k)= Vnil;
464: release(uname); uname= get_pname(u);
465: if (nd1 != Vnil) push(copy(nd1));
466: if (nd2 != Vnil) push(copy(nd2));
467: }
468:
469: Visible Procedure formula(nd1, name, nd2, tor) value nd1, name, nd2, tor; {
470: if (tor == Vnil) udfpr(nd1, name, nd2, Yes);
471: else {
472: if (!Is_function(tor))
473: syserr(MESS(4109, "formula called with non-function"));
474: push(pre_fun(nd1, Funprd(tor)->pre, nd2));
475: }
476: }
477:
478: Visible Procedure proposition(nd1, name, nd2, pred) value nd1, name, nd2, pred; {
479: if (pred == Vnil) udfpr(nd1, name, nd2, No);
480: else {
481: if (!Is_predicate(pred))
482: syserr(MESS(4110, "proposition called with non-predicate"));
483: report= pre_prop(nd1, Funprd(pred)->pre, nd2);
484: }
485: }
486:
487: Visible Procedure v_mystery(name, number) value name, number; {
488: value *aa; fun f;
489: aa= envassoc(curnv->tab, Is_compound(curnv->tab) ? number : name);
490: if (aa != Pnil) push(copy(*aa));
491: else if (is_zerfun(name, &f)) {
492: if (Funprd(f)->pre == Use) f= Vnil;
493: formula(Vnil, name, Vnil, f);
494: }
495: else error3(0, name, MESS(4111, " has not yet received a value"));
496: }
497:
498: Hidden value mk_formal(pt) parsetree pt; {
499: value f= grab_for(); formal *ff= Formal(f);
500: sv_context(&ff->con); ff->fp= pt;
501: return f;
502: }
503:
504: Visible Procedure x_user_command(name, actuals, def)
505: value name; parsetree actuals; value def;
506: {
507: how *h; parsetree u; value *aa;
508: value v, formals; int k, len;
509: if (def != Vnil) {
510: if (!Is_refinement(def)) syserr(MESS(4112, "bad def in x_user_command"));
511: call(C_refcmd, *Branch(Refinement(def)->rp, REF_START));
512: return;
513: }
514: if (!is_unit(name, How, &aa)) {
515: error3(MESS(4113, "You haven't told me HOW'TO "), name, 0);
516: return;
517: }
518: u= (h= How_to(*aa))->unit;
519: if (h->unparsed) fix_nodes(&u, &h->code);
520: if (!still_ok) { rem_unit(u); return; }
521: h->unparsed= No;
522: formals= *Branch(u, HOW_FORMALS);
523: len= intval(*Branch(u, HOW_NLOCALS)); k= 0;
524: v= mk_compound(len);
525: while (actuals != Vnil && formals != Vnil) { /* Save actuals */
526: if (*Branch(actuals, ACT_EXPR) != Vnil) {
527: if (k >= len) syserr(MESS(4114, "too many actuals"));
528: *Field(v, k++)= mk_formal(*Branch(actuals, ACT_START));
529: }
530: actuals= *Branch(actuals, ACT_NEXT);
531: formals= *Branch(formals, FML_NEXT);
532: }
533: for (; k < len; ++k) { *Field(v, k)= Vnil; }
534:
535: call(C_howto, h->code);
536:
537: curnv->tab= v;
538: release(uname); uname= permkey(name, How);
539: }
540:
541: Visible Procedure endsta() {
542: if (st_base) {
543: freemem((ptr) st_base);
544: st_base= Pnil;
545: }
546: }
547:
548: Hidden value save_curnv_chain() {
549: value pad;
550: value c, f;
551: formal *ff;
552: int cnt, k;
553:
554: /* Count how many */
555: c= curnv->tab;
556: for (cnt= 0; ; ) {
557: if (!Is_compound(c)) break;
558: ++cnt;
559: f= *Field(c, 0);
560: if (!Is_formal(f)) break;
561: ff= Formal(f);
562: c= ff->con.curnv->tab;
563: }
564:
565: pad= mk_compound(cnt);
566:
567: /* Do the copy */
568: c= curnv->tab;
569: for (k= 0; ; ) {
570: if (!Is_compound(c)) break;
571: *Field(pad, k)= copy(c);
572: if (++k >= cnt) break;
573: f= *Field(c, 0);
574: if (!Is_formal(f)) break;
575: ff= Formal(f);
576: c= ff->con.curnv->tab;
577: }
578: if (k != cnt)
579: syserr(MESS(4115, "save_curnv_chain: phase error"));
580:
581: return pad;
582: }
583:
584: Hidden rest_curnv_chain(pad) value pad; {
585: int k, cnt;
586: value f, *c= &curnv->tab;
587: formal *ff;
588:
589: if (pad == Vnil || !Is_compound(pad))
590: syserr(MESS(4116, "rest_curnv_chain: bad pad"));
591: cnt= Nfields(pad);
592: for (k= 0; ; ) {
593: if (!Is_compound(*c)) break;
594: release(*c);
595: *c= copy(*Field(pad, k));
596: if (++k >= cnt) break;
597: f= *Field(*c, 0);
598: if (!Is_formal(f)) break;
599: ff= Formal(f);
600: c= &ff->con.curnv->tab;
601: }
602: if (k != cnt)
603: syserr(MESS(4117, "rest_curnv_chain: phase error"));
604: release(pad);
605: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.