|
|
Initial revision
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* $Header: /var/lib/cvsd/repos/CSRG/43BSD/contrib/B/src/bint/b2gen.c,v 1.1 2018/04/24 16:12:54 root Exp $ */
/* Code generation */
#include "b.h"
#include "b0fea.h"
#include "b1obj.h"
#include "b2exp.h"
#include "b2nod.h"
#include "b2gen.h" /* Must be after b2nod.h */
#include "b3err.h"
#include "b3env.h"
#include "b3int.h"
#include "b3sem.h"
#include "b3sou.h"
Visible Procedure fix_nodes(pt, code) parsetree *pt; parsetree *code; {
context c; value *setup(), *su;
sv_context(&c);
curline= *pt; curlino= one;
su= setup(*pt);
if (su) analyze(*pt, su);
curline= *pt; curlino= one;
inithreads();
fix(pt, su ? 'x' : 'v');
endthreads(code);
cleanup();
#ifdef TYPE_CHECK
if (cntxt != In_prmnv) type_check(*pt);
#endif
set_context(&c);
}
/* ******************************************************************** */
/* Utilities used by threading. */
/* A 'threaded tree' is, in our case, a fixed(*) parse tree with extra links
that are used by the interpreter to determine the execution order.
__________
(*) 'Fixed' means: processed by 'fix_nodes', which removes UNPARSED
nodes and distinguishes TAG nodes into local, global tags etc.
fix_nodes also creates the threads, but this is accidental, not
essential. For UNPARSED nodes, the threads are actually laid
in a second pass through the subtree that was UNPARSED.
__________
A small example: the parse tree for the expression 'a+b*c' looks like
(DYOP,
(TAGlocal, "a"),
"+",
(DYOP,
(TAGlocal, "b"),
"*",
(TAGlocal, "c"))).
The required execution order is here:
1) (TAGlocal, "a")
2) (TAGlocal, "b")
3) (TAGlocal, "c")
4) (DYOP, ..., "*", ...)
5) (DYOP, ..., "+", ...)
Of course, the result of each operation (if it has a result) is pushed
on a stack, and the operands are popped from this same stack. Think of
reversed polish notation (well-known by owners of HP pocket calculators).
The 'threads' are explicit links from each node to its successor in this
execution order. Conditional operations like IF and AND have two threads,
one for success and one for failure. Loops can be made by having the
thread from the last node of the loop body point to the head of the loop.
Threading expressions, locations and simple-commands is easy: recursively
thread each of the subtrees, then lay a thread from the last threaded
to the current node. Nodes occurring in a 'location' context are
marked, so that the interpreter knows when to push a 'location' on
the stack.
Tests and looping commands cause most of the complexity of the threading
utilities. The basic technique is 'backpatching'.
Nodes that need a conditional forward jump are chained together in a
linked list, and when their destination is reached, all nodes in the
chain get its 'address' patched into their secondary thread. There is
one such chain, called 'bpchain', which at all times contains those nodes
whose secondary destination would be the next generated instruction.
This is used by IF, WHILE, test-suites, AND and OR.
To generate a loop, both this chain and the last normal instruction
(if any) are diverted to the node where the loop continues.
For test-suites, we also need to be capable of jumping unconditionally
forward (over the remainder of the SELECT-command). This is done by
saving both the backpatch chain and the last node visited, and restoring
them after the remainder has been processed.
*/
/* Implementation tricks: in order not to show circular lists to 'release',
parse tree nodes are generated as compounds where there is room for two
more fields than their length indicates.
*/
#define Flag (MkSmallInt(1))
/* Flag used to indicate Location or TestRefinement node */
Hidden parsetree start; /* First instruction. Picked up by endthreads() */
Hidden parsetree last; /* Last visited node */
Hidden parsetree bpchain; /* Backpatch chain for conditional goto's */
Hidden parsetree *wanthere; /* Chain of requests to return next tree */
extern string opcodes[];
/* Start threading */
Hidden Procedure inithreads() {
bpchain= NilTree;
wanthere= 0;
last= 0;
here(&start);
}
/* Finish threading */
Hidden Procedure endthreads(code) parsetree *code; {
jumpto(Stop);
if (!still_ok) start= NilTree;
*code= start;
}
/* Fill 't' as secondary thread for all nodes in the backpatch chain,
leaving the chain empty. */
Hidden Procedure backpatch(t) parsetree t; {
parsetree u;
while (bpchain != NilTree) {
u= Thread2(bpchain);
Thread2(bpchain)= t;
bpchain= u;
}
}
Visible Procedure jumpto(t) parsetree t; {
parsetree u;
if (!still_ok) return;
while (wanthere != 0) {
u= *wanthere;
*wanthere= t;
wanthere= (parsetree*)u;
}
while (last != NilTree) {
u= Thread(last);
Thread(last)= t;
last= u;
}
backpatch(t);
}
Hidden parsetree seterr(n) int n; {
return (parsetree)MkSmallInt(n);
}
/* Visit node 't', and set its secondary thread to 't2'. */
Hidden Procedure visit2(t, t2) parsetree t, t2; {
if (!still_ok) return;
jumpto(t);
Thread2(t)= t2;
#ifdef DEBUG
fprintf(stderr, "\tvisit %s %s\n", opcodes[Nodetype(t)],
t2 == NilTree ? "" : "[*]");
#endif DEBUG
Thread(t)= NilTree;
last= t;
}
/* Visit node 't' */
Hidden Procedure visit(t) parsetree t; {
visit2(t, NilTree);
}
/* Visit node 't' and flag it as a location (or test-refinement). */
Hidden Procedure lvisit(t) parsetree t; {
visit2(t, Flag);
}
#ifdef NOT_USED
Hidden Procedure jumphere(t) parsetree t; {
Thread(t)= last;
last= t;
}
#endif
/* Add node 't' to the backpatch chain. */
Hidden Procedure jump2here(t) parsetree t; {
if (!still_ok) return;
Thread2(t)= bpchain;
bpchain= t;
}
Hidden Procedure here(pl) parsetree *pl; {
if (!still_ok) return;
*pl= (parsetree) wanthere;
wanthere= pl;
}
Visible Procedure hold(pl) struct state *pl; {
if (!still_ok) return;
pl->h_last= last; pl->h_bpchain= bpchain; pl->h_wanthere= wanthere;
last= bpchain= NilTree; wanthere= 0;
}
Visible Procedure let_go(pl) struct state *pl; {
parsetree p, *w;
if (!still_ok) return;
if (last) {
for (p= last; Thread(p) != NilTree; p= Thread(p))
;
Thread(p)= pl->h_last;
}
else last= pl->h_last;
if (bpchain) {
for (p= bpchain; Thread2(p) != NilTree; p= Thread2(p))
;
Thread2(p)= pl->h_bpchain;
}
else bpchain= pl->h_bpchain;
if (wanthere) {
for (w= wanthere; *w != 0; w= (parsetree*) *w)
;
*w= (parsetree) pl->h_wanthere;
}
else wanthere= pl->h_wanthere;
}
Hidden bool reachable() {
return last != NilTree || bpchain != 0 || wanthere != 0;
}
/* ******************************************************************** */
/* *********************** code generation **************************** */
/* ******************************************************************** */
Forward bool is_variable();
Forward bool is_cmd_ref();
Forward value copydef();
Visible Procedure fix(pt, flag) parsetree *pt; char flag; {
struct state st; value v, function; parsetree t, l1= NilTree;
typenode nt; string s; char c; int n, k, len;
t= *pt;
if (!Is_node(t) || !still_ok) return;
nt= Nodetype(t);
if (nt < 0 || nt >= NTYPES) syserr(MESS(2200, "fix bad tree"));
s= gentab[nt];
if (s == NULL) return;
n= First_fieldnr;
if (flag == 'x') curline= t;
while ((c= *s++) != '\0' && still_ok) {
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
n= (c - '0') + First_fieldnr;
break;
case 'c':
v= *Branch(t, n);
if (v != Vnil) {
len= Nfields(v);
for (k= 0; k < len; ++k)
fix(Field(v, k), flag);
}
++n;
break;
case '#':
curlino= *Branch(t, n);
++n;
break;
case 'g':
case 'h':
++n;
break;
case 'a':
case 'l':
if (flag == 'v' || flag == 't')
c= flag;
/* Fall through */
case '!':
case 't':
case 'u':
case 'v':
case 'x':
fix(Branch(t, n), c);
++n;
break;
case 'f':
f_fpr_formals(*Branch(t, n));
++n;
break;
case '?':
if (flag == 'v')
f_eunparsed(pt);
else if (flag == 't')
f_cunparsed(pt);
else
syserr(MESS(2201, "fix unparsed with bad flag"));
fix(pt, flag);
break;
case 'C':
v= *Branch(t, REL_LEFT);
if (Comparison(Nodetype(v)))
jump2here(v);
break;
case 'D':
v= (value)*Branch(t, DYA_NAME);
if (!is_dyafun(v, &function))
fixerr2(v, MESS(2202, " isn't a dyadic function"));
else
*Branch(t, DYA_FCT)= copydef(function);
break;
case 'E':
v= (value)*Branch(t, DYA_NAME);
if (!is_dyaprd(v, &function))
fixerr2(v, MESS(2203, " isn't a dyadic predicate"));
else
*Branch(t, DYA_FCT)= copydef(function);
break;
case 'G':
jumpto(l1);
break;
case 'H':
here(&l1);
break;
case 'I':
if (*Branch(t, n) == NilTree)
break;
/* Else fall through */
case 'J':
jump2here(t);
break;
case 'K':
hold(&st);
break;
case 'L':
let_go(&st);
break;
case 'M':
v= (value)*Branch(t, MON_NAME);
if (is_variable(v) || !is_monfun(v, &function))
fixerr2(v, MESS(2204, " isn't a monadic function"));
else
*Branch(t, MON_FCT)= copydef(function);
break;
case 'N':
v= (value)*Branch(t, MON_NAME);
if (is_variable(v) || !is_monprd(v, &function))
fixerr2(v, MESS(2205, " isn't a monadic predicate"));
else
*Branch(t, MON_FCT)= copydef(function);
break;
#ifdef REACH
case 'R':
if (*Branch(t, n) != NilTree && !reachable())
fixerr(MESS(2206, "command cannot be reached"));
break;
#endif
case 'S':
jumpto(Stop);
break;
case 'T':
if (flag == 't')
f_ctag(pt);
else if (flag == 'v' || flag == 'x')
f_etag(pt);
else
f_ttag(pt);
break;
case 'U':
f_ucommand(pt);
break;
case 'V':
visit(t);
break;
case 'X':
if (flag == 'a' || flag == 'l' || flag == '!')
lvisit(t);
else
visit(t);
break;
case 'W':
/*!*/ visit2(t, seterr(1));
break;
case 'Y':
if (still_ok && reachable()) {
if (nt == YIELD)
fixerr(MESS(2207, "YIELD-unit returns no value"));
else
fixerr(MESS(2208, "TEST-unit reports no outcome"));
}
break;
case 'Z':
if (!is_cmd_ref(t) && still_ok && reachable())
fixerr(MESS(2209, "refinement returns no value c.q. reports no outcome"));
*Branch(t, REF_START)= copy(l1);
break;
}
}
}
/* ******************************************************************** */
Hidden bool is_cmd_ref(t) parsetree t; { /* HACK */
value name= *Branch(t, REF_NAME);
string s= strval(name);
/* return isupper(*s); */
return *s <= 'Z' && *s >= 'A';
}
Visible value copydef(f) value f; {
funprd *fpr= Funprd(f);
if (fpr->pre == Use) return Vnil;
return copy(f);
}
Hidden bool is_basic_target(v) value v; {
return envassoc(formals, v) ||
locals != Vnil && envassoc(locals, v) ||
envassoc(globals, v) ||
envassoc(mysteries, v);
}
Hidden bool is_variable(v) value v; {
value f;
return is_basic_target(v) ||
envassoc(refinements, v) ||
is_zerfun(v, &f);
}
Hidden bool is_target(p) parsetree p; {
value v= *Branch(p, First_fieldnr); int k, len;
switch (Nodetype(p)) {
case TAG:
return is_basic_target(v);
case SELECTION:
case BEHEAD:
case CURTAIL:
case COMPOUND:
return is_target(v);
case COLLATERAL:
len= Nfields(v);
k_Overfields {
if (!is_target(*Field(v, k))) return No;
}
return Yes;
default:
return No;
}
}
/* ******************************************************************** */
Hidden Procedure f_actuals(formals, pactuals) parsetree formals, *pactuals; {
/* name, actual, next */
value actuals= *pactuals, act, form, next_a, next_f, kw, *pact;
kw= *Branch(actuals, ACT_KEYW);
pact= Branch(actuals, ACT_EXPR); act= *pact;
form= *Branch(formals, FML_TAG);
next_a= *Branch(actuals, ACT_NEXT); next_f= *Branch(formals, FML_NEXT);
if (compare(*Branch(formals, FML_KEYW), kw) != 0)
fixerr3(MESS(2210, "wrong keyword "), kw, 0);
else if (act == Vnil && form != Vnil)
fixerr3(MESS(2211, "missing actual after "), kw, 0);
else if (next_a == Vnil && next_f != Vnil)
fixerr3(MESS(2212, "can't find expected "),
*Branch(next_f, FML_KEYW), 0);
else if (act != Vnil && form == Vnil)
fixerr3(MESS(2213, "unexpected actual after "), kw, 0);
else if (next_a != Vnil && next_f == Vnil)
fixerr3(MESS(2214, "unexpected keyword "),
*Branch(next_a, ACT_KEYW), 0);
else {
if (act != Vnil) {
parsetree st; struct state save;
hold(&save); here(&st);
if (is_target(act)) f_targ(pact);
else f_expr(pact);
jumpto(Stop); let_go(&save);
*Branch(actuals, ACT_START)= copy(st);
}
if (still_ok && next_a != Vnil)
f_actuals(next_f, Branch(actuals, ACT_NEXT));
}
}
Hidden Procedure f_ucommand(pt) parsetree *pt; {
value t= *pt, *aa;
parsetree u, *f1= Branch(t, UCMD_NAME), *f2= Branch(t, UCMD_ACTUALS);
release(*Branch(t, UCMD_DEF));
*Branch(t, UCMD_DEF)= Vnil;
if ((aa= envassoc(refinements, *f1)) != Pnil) {
if (*Branch(*f2, ACT_EXPR) != Vnil
|| *Branch(*f2, ACT_NEXT) != Vnil)
fixerr(MESS(2215, "refinement with parameters"));
else *Branch(t, UCMD_DEF)= copy(*aa);
}
else if (is_unit(*f1, How, &aa)) {
u= How_to(*aa)->unit;
f_actuals(*Branch(u, HOW_FORMALS), f2);
}
else if (still_ok)
fixerr3(MESS(2216, "you haven't told me HOW'TO "), *f1, 0);
}
Hidden Procedure f_fpr_formals(t) parsetree t; {
switch (Nodetype(t)) {
case TAG:
break;
case MONF: case MONPRD:
f_targ(Branch(t, MON_RIGHT));
break;
case DYAF: case DYAPRD:
f_targ(Branch(t, DYA_LEFT));
f_targ(Branch(t, DYA_RIGHT));
break;
default:
syserr(MESS(2217, "f_fpr_formals"));
}
}
Visible bool modify_tag(name, tag) parsetree *tag; value name; {
value *aa, function;
*tag= NilTree;
if (aa= envassoc(formals, name))
*tag= node3(TAGformal, name, copy(*aa));
else if (locals != Vnil && (aa= envassoc(locals, name)))
*tag= node3(TAGlocal, name, copy(*aa));
else if (aa= envassoc(globals, name))
*tag= node2(TAGglobal, name);
else if (aa= envassoc(mysteries, name))
*tag= node3(TAGmystery, name, copy(*aa));
else if (aa= envassoc(refinements, name))
*tag= node3(TAGrefinement, name, copy(*aa));
else if (is_zerfun(name, &function))
*tag= node3(TAGzerfun, name, copydef(function));
else if (is_zerprd(name, &function))
*tag= node3(TAGzerprd, name, copydef(function));
else return No;
return Yes;
}
Hidden Procedure f_etag(pt) parsetree *pt; {
parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
if (modify_tag(name, &t)) {
release(*pt);
*pt= t;
if (Nodetype(t) == TAGzerprd)
fixerr2(name, MESS(2218, " cannot be used in an expression"));
else
visit(t);
} else {
fixerr2(name, MESS(2219, " has not yet received a value"));
release(name);
}
}
Hidden Procedure f_ttag(pt) parsetree *pt; {
parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
if (modify_tag(name, &t)) {
release(*pt);
*pt= t;
switch (Nodetype(t)) {
case TAGrefinement:
fixerr(MESS(2220, "a refinement may not be used as a target"));
break;
case TAGzerfun:
case TAGzerprd:
fixerr2(name, MESS(2221, " hasn't been initialised or defined"));
break;
default:
lvisit(t);
break;
}
} else {
fixerr2(name, MESS(2222, " hasn't been initialised or defined"));
release(name);
}
}
Hidden Procedure f_ctag(pt) parsetree *pt; {
parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
if (modify_tag(name, &t)) {
release(*pt);
*pt= t;
switch (Nodetype(t)) {
case TAGrefinement:
lvisit(t); /* 'Loc' flag here means 'Test' */
break;
case TAGzerprd:
visit(t);
break;
default:
fixerr2(name, MESS(2223, " is neither a refined test nor a zeroadic predicate"));
break;
}
} else {
fixerr2(name, MESS(2224, " is neither a refined test nor a zeroadic predicate"));
release(name);
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.