1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
   2: 
   3: /* $Header: b2gen.c,v 1.4 85/08/27 10:57:31 timo Exp $ */
   4: 
   5: /* Code generation */
   6: 
   7: #include "b.h"
   8: #include "b0fea.h"
   9: #include "b1obj.h"
  10: #include "b2exp.h"
  11: #include "b2nod.h"
  12: #include "b2gen.h" /* Must be after b2nod.h */
  13: #include "b3err.h"
  14: #include "b3env.h"
  15: #include "b3int.h"
  16: #include "b3sem.h"
  17: #include "b3sou.h"
  18: 
  19: Visible Procedure fix_nodes(pt, code) parsetree *pt; parsetree *code; {
  20:     context c; value *setup(), *su;
  21:     sv_context(&c);
  22:     curline= *pt; curlino= one;
  23:     su= setup(*pt);
  24:     if (su) analyze(*pt, su);
  25:     curline= *pt; curlino= one;
  26:     inithreads();
  27:     fix(pt, su ? 'x' : 'v');
  28:     endthreads(code);
  29:     cleanup();
  30: #ifdef TYPE_CHECK
  31:     if (cntxt != In_prmnv) type_check(*pt);
  32: #endif
  33:     set_context(&c);
  34: }
  35: 
  36: /* ******************************************************************** */
  37: 
  38: /* Utilities used by threading. */
  39: 
  40: /* A 'threaded tree' is, in our case, a fixed(*) parse tree with extra links
  41:    that are used by the interpreter to determine the execution order.
  42:    __________
  43:    (*) 'Fixed' means: processed by 'fix_nodes', which removes UNPARSED
  44:        nodes and distinguishes TAG nodes into local, global tags etc.
  45:        fix_nodes also creates the threads, but this is accidental, not
  46:        essential.  For UNPARSED nodes, the threads are actually laid
  47:        in a second pass through the subtree that was UNPARSED.
  48:    __________
  49: 
  50:    A small example: the parse tree for the expression  'a+b*c'  looks like
  51: 
  52: 	(DYOP,
  53: 		(TAGlocal, "a"),
  54: 		"+",
  55: 		(DYOP,
  56: 			(TAGlocal, "b"),
  57: 			"*",
  58: 			(TAGlocal, "c"))).
  59: 
  60:    The required execution order is here:
  61: 
  62: 	1) (TAGlocal, "a")
  63: 	2) (TAGlocal, "b")
  64: 	3) (TAGlocal, "c")
  65: 	4) (DYOP, ..., "*", ...)
  66: 	5) (DYOP, ..., "+", ...)
  67: 
  68:    Of course, the result of each operation (if it has a result) is pushed
  69:    on a stack, and the operands are popped from this same stack.  Think of
  70:    reversed polish notation (well-known by owners of HP pocket calculators).
  71: 
  72:    The 'threads' are explicit links from each node to its successor in this
  73:    execution order.  Conditional operations like IF and AND have two threads,
  74:    one for success and one for failure.  Loops can be made by having the
  75:    thread from the last node of the loop body point to the head of the loop.
  76: 
  77:    Threading expressions, locations and simple-commands is easy: recursively
  78:    thread each of the subtrees, then lay a thread from the last threaded
  79:    to the current node.  Nodes occurring in a 'location' context are
  80:    marked, so that the interpreter knows when to push a 'location' on
  81:    the stack.
  82: 
  83:    Tests and looping commands cause most of the complexity of the threading
  84:    utilities.  The basic technique is 'backpatching'.
  85:    Nodes that need a conditional forward jump are chained together in a
  86:    linked list, and when their destination is reached, all nodes in the
  87:    chain get its 'address' patched into their secondary thread.  There is
  88:    one such chain, called 'bpchain', which at all times contains those nodes
  89:    whose secondary destination would be the next generated instruction.
  90:    This is used by IF, WHILE, test-suites, AND and OR.
  91: 
  92:    To generate a loop, both this chain and the last normal instruction
  93:    (if any) are diverted to the node where the loop continues.
  94: 
  95:    For test-suites, we also need to be capable of jumping unconditionally
  96:    forward (over the remainder of the SELECT-command).  This is done by
  97:    saving both the backpatch chain and the last node visited, and restoring
  98:    them after the remainder has been processed.
  99: */
 100: 
 101: /* Implementation tricks: in order not to show circular lists to 'release',
 102:    parse tree nodes are generated as compounds where there is room for two
 103:    more fields than their length indicates.
 104: */
 105: 
 106: #define Flag (MkSmallInt(1))
 107:     /* Flag used to indicate Location or TestRefinement node */
 108: 
 109: Hidden parsetree start; /* First instruction.  Picked up by endthreads() */
 110: 
 111: Hidden parsetree last; /* Last visited node */
 112: 
 113: Hidden parsetree bpchain; /* Backpatch chain for conditional goto's */
 114: Hidden parsetree *wanthere; /* Chain of requests to return next tree */
 115: 
 116: extern string opcodes[];
 117: 
 118: 
 119: /* Start threading */
 120: 
 121: Hidden Procedure inithreads() {
 122:     bpchain= NilTree;
 123:     wanthere= 0;
 124:     last= 0;
 125:     here(&start);
 126: }
 127: 
 128: /* Finish threading */
 129: 
 130: Hidden Procedure endthreads(code) parsetree *code; {
 131:     jumpto(Stop);
 132:     if (!still_ok) start= NilTree;
 133:     *code= start;
 134: }
 135: 
 136: 
 137: /* Fill 't' as secondary thread for all nodes in the backpatch chain,
 138:    leaving the chain empty. */
 139: 
 140: Hidden Procedure backpatch(t) parsetree t; {
 141:     parsetree u;
 142:     while (bpchain != NilTree) {
 143:         u= Thread2(bpchain);
 144:         Thread2(bpchain)= t;
 145:         bpchain= u;
 146:     }
 147: }
 148: 
 149: Visible Procedure jumpto(t) parsetree t; {
 150:     parsetree u;
 151:     if (!still_ok) return;
 152:     while (wanthere != 0) {
 153:         u= *wanthere;
 154:         *wanthere= t;
 155:         wanthere= (parsetree*)u;
 156:     }
 157:     while (last != NilTree) {
 158:         u= Thread(last);
 159:         Thread(last)= t;
 160:         last= u;
 161:     }
 162:     backpatch(t);
 163: }
 164: 
 165: Hidden parsetree seterr(n) int n; {
 166:     return (parsetree)MkSmallInt(n);
 167: }
 168: 
 169: /* Visit node 't', and set its secondary thread to 't2'. */
 170: 
 171: Hidden Procedure visit2(t, t2) parsetree t, t2; {
 172:     if (!still_ok) return;
 173:     jumpto(t);
 174:     Thread2(t)= t2;
 175: #ifdef DEBUG
 176:     fprintf(stderr, "\tvisit %s %s\n", opcodes[Nodetype(t)],
 177:         t2 == NilTree ? "" : "[*]");
 178: #endif DEBUG
 179:     Thread(t)= NilTree;
 180:     last= t;
 181: }
 182: 
 183: /* Visit node 't' */
 184: 
 185: Hidden Procedure visit(t) parsetree t; {
 186:     visit2(t, NilTree);
 187: }
 188: 
 189: /* Visit node 't' and flag it as a location (or test-refinement). */
 190: 
 191: Hidden Procedure lvisit(t) parsetree t; {
 192:     visit2(t, Flag);
 193: }
 194: 
 195: #ifdef NOT_USED
 196: Hidden Procedure jumphere(t) parsetree t; {
 197:     Thread(t)= last;
 198:     last= t;
 199: }
 200: #endif
 201: 
 202: /* Add node 't' to the backpatch chain. */
 203: 
 204: Hidden Procedure jump2here(t) parsetree t; {
 205:     if (!still_ok) return;
 206:     Thread2(t)= bpchain;
 207:     bpchain= t;
 208: }
 209: 
 210: Hidden Procedure here(pl) parsetree *pl; {
 211:     if (!still_ok) return;
 212:     *pl= (parsetree) wanthere;
 213:     wanthere= pl;
 214: }
 215: 
 216: Visible Procedure hold(pl) struct state *pl; {
 217:     if (!still_ok) return;
 218:     pl->h_last= last; pl->h_bpchain= bpchain; pl->h_wanthere= wanthere;
 219:     last= bpchain= NilTree; wanthere= 0;
 220: }
 221: 
 222: Visible Procedure let_go(pl) struct state *pl; {
 223:     parsetree p, *w;
 224:     if (!still_ok) return;
 225:     if (last) {
 226:         for (p= last; Thread(p) != NilTree; p= Thread(p))
 227:             ;
 228:         Thread(p)= pl->h_last;
 229:     }
 230:     else last= pl->h_last;
 231:     if (bpchain) {
 232:         for (p= bpchain; Thread2(p) != NilTree; p= Thread2(p))
 233:             ;
 234:         Thread2(p)= pl->h_bpchain;
 235:     }
 236:     else bpchain= pl->h_bpchain;
 237:     if (wanthere) {
 238:         for (w= wanthere; *w != 0; w= (parsetree*) *w)
 239:             ;
 240:         *w= (parsetree) pl->h_wanthere;
 241:     }
 242:     else wanthere= pl->h_wanthere;
 243: }
 244: 
 245: Hidden bool reachable() {
 246:     return last != NilTree || bpchain != 0 || wanthere != 0;
 247: }
 248: 
 249: 
 250: /* ******************************************************************** */
 251: /* *********************** code generation **************************** */
 252: /* ******************************************************************** */
 253: 
 254: Forward bool is_variable();
 255: Forward bool is_cmd_ref();
 256: Forward value copydef();
 257: 
 258: Visible Procedure fix(pt, flag) parsetree *pt; char flag; {
 259:     struct state st; value v, function; parsetree t, l1= NilTree;
 260:     typenode nt; string s; char c; int n, k, len;
 261: 
 262:     t= *pt;
 263:     if (!Is_node(t) || !still_ok) return;
 264:     nt= Nodetype(t);
 265:     if (nt < 0 || nt >= NTYPES) syserr(MESS(2200, "fix bad tree"));
 266:     s= gentab[nt];
 267:     if (s == NULL) return;
 268:     n= First_fieldnr;
 269:     if (flag == 'x') curline= t;
 270:     while ((c= *s++) != '\0' && still_ok) {
 271:         switch (c) {
 272:         case '0':
 273:         case '1':
 274:         case '2':
 275:         case '3':
 276:         case '4':
 277:         case '5':
 278:         case '6':
 279:         case '7':
 280:         case '8':
 281:         case '9':
 282:             n= (c - '0') + First_fieldnr;
 283:             break;
 284:         case 'c':
 285:             v= *Branch(t, n);
 286:             if (v != Vnil) {
 287:                 len= Nfields(v);
 288:                 for (k= 0; k < len; ++k)
 289:                     fix(Field(v, k), flag);
 290:             }
 291:             ++n;
 292:             break;
 293:         case '#':
 294:             curlino= *Branch(t, n);
 295:             ++n;
 296:             break;
 297:         case 'g':
 298:         case 'h':
 299:             ++n;
 300:             break;
 301:         case 'a':
 302:         case 'l':
 303:             if (flag == 'v' || flag == 't')
 304:                 c= flag;
 305:             /* Fall through */
 306:         case '!':
 307:         case 't':
 308:         case 'u':
 309:         case 'v':
 310:         case 'x':
 311:             fix(Branch(t, n), c);
 312:             ++n;
 313:             break;
 314:         case 'f':
 315:             f_fpr_formals(*Branch(t, n));
 316:             ++n;
 317:             break;
 318: 
 319:         case '?':
 320:             if (flag == 'v')
 321:                 f_eunparsed(pt);
 322:             else if (flag == 't')
 323:                 f_cunparsed(pt);
 324:             else
 325:               syserr(MESS(2201, "fix unparsed with bad flag"));
 326:             fix(pt, flag);
 327:             break;
 328:         case 'C':
 329:             v= *Branch(t, REL_LEFT);
 330:             if (Comparison(Nodetype(v)))
 331:                 jump2here(v);
 332:             break;
 333:         case 'D':
 334:             v= (value)*Branch(t, DYA_NAME);
 335:             if (!is_dyafun(v, &function))
 336:               fixerr2(v, MESS(2202, " isn't a dyadic function"));
 337:             else
 338:                 *Branch(t, DYA_FCT)= copydef(function);
 339:             break;
 340:         case 'E':
 341:             v= (value)*Branch(t, DYA_NAME);
 342:             if (!is_dyaprd(v, &function))
 343:               fixerr2(v, MESS(2203, " isn't a dyadic predicate"));
 344:             else
 345:                 *Branch(t, DYA_FCT)= copydef(function);
 346:             break;
 347:         case 'G':
 348:             jumpto(l1);
 349:             break;
 350:         case 'H':
 351:             here(&l1);
 352:             break;
 353:         case 'I':
 354:             if (*Branch(t, n) == NilTree)
 355:                 break;
 356:             /* Else fall through */
 357:         case 'J':
 358:             jump2here(t);
 359:             break;
 360:         case 'K':
 361:             hold(&st);
 362:             break;
 363:         case 'L':
 364:             let_go(&st);
 365:             break;
 366:         case 'M':
 367:             v= (value)*Branch(t, MON_NAME);
 368:             if (is_variable(v) || !is_monfun(v, &function))
 369:               fixerr2(v, MESS(2204, " isn't a monadic function"));
 370:             else
 371:                 *Branch(t, MON_FCT)= copydef(function);
 372:             break;
 373:         case 'N':
 374:             v= (value)*Branch(t, MON_NAME);
 375:             if (is_variable(v) || !is_monprd(v, &function))
 376:               fixerr2(v, MESS(2205, " isn't a monadic predicate"));
 377:             else
 378:                 *Branch(t, MON_FCT)= copydef(function);
 379:             break;
 380: #ifdef REACH
 381:         case 'R':
 382:             if (*Branch(t, n) != NilTree && !reachable())
 383:                 fixerr(MESS(2206, "command cannot be reached"));
 384:             break;
 385: #endif
 386:         case 'S':
 387:             jumpto(Stop);
 388:             break;
 389:         case 'T':
 390:             if (flag == 't')
 391:                 f_ctag(pt);
 392:             else if (flag == 'v' || flag == 'x')
 393:                 f_etag(pt);
 394:             else
 395:                 f_ttag(pt);
 396:             break;
 397:         case 'U':
 398:             f_ucommand(pt);
 399:             break;
 400:         case 'V':
 401:             visit(t);
 402:             break;
 403:         case 'X':
 404:             if (flag == 'a' || flag == 'l' || flag == '!')
 405:                 lvisit(t);
 406:             else
 407:                 visit(t);
 408:             break;
 409:         case 'W':
 410: /*!*/           visit2(t, seterr(1));
 411:             break;
 412:         case 'Y':
 413:             if (still_ok && reachable()) {
 414:               if (nt == YIELD)
 415:                 fixerr(MESS(2207, "YIELD-unit returns no value"));
 416:               else
 417:                 fixerr(MESS(2208, "TEST-unit reports no outcome"));
 418:             }
 419:             break;
 420:         case 'Z':
 421:             if (!is_cmd_ref(t) && still_ok && reachable())
 422:   fixerr(MESS(2209, "refinement returns no value c.q. reports no outcome"));
 423:             *Branch(t, REF_START)= copy(l1);
 424:             break;
 425:         }
 426:     }
 427: }
 428: 
 429: /* ******************************************************************** */
 430: 
 431: Hidden bool is_cmd_ref(t) parsetree t; { /* HACK */
 432:     value name= *Branch(t, REF_NAME);
 433:     string s= strval(name);
 434:     /* return isupper(*s); */
 435:     return *s <= 'Z' && *s >= 'A';
 436: }
 437: 
 438: Visible value copydef(f) value f; {
 439:     funprd *fpr= Funprd(f);
 440:     if (fpr->pre == Use) return Vnil;
 441:     return copy(f);
 442: }
 443: 
 444: Hidden bool is_basic_target(v) value v; {
 445:     return envassoc(formals, v) ||
 446:         locals != Vnil && envassoc(locals, v) ||
 447:         envassoc(globals, v) ||
 448:         envassoc(mysteries, v);
 449: }
 450: 
 451: Hidden bool is_variable(v) value v; {
 452:     value f;
 453:     return is_basic_target(v) ||
 454:         envassoc(refinements, v) ||
 455:         is_zerfun(v, &f);
 456: }
 457: 
 458: Hidden bool is_target(p) parsetree p; {
 459:     value v= *Branch(p, First_fieldnr); int k, len;
 460:     switch (Nodetype(p)) {
 461: 
 462:     case TAG:
 463:         return is_basic_target(v);
 464: 
 465:     case SELECTION:
 466:     case BEHEAD:
 467:     case CURTAIL:
 468:     case COMPOUND:
 469:         return is_target(v);
 470: 
 471:     case COLLATERAL:
 472:         len= Nfields(v);
 473:         k_Overfields {
 474:             if (!is_target(*Field(v, k))) return No;
 475:         }
 476:         return Yes;
 477: 
 478:     default:
 479:         return No;
 480: 
 481:     }
 482: }
 483: 
 484: /* ******************************************************************** */
 485: 
 486: Hidden Procedure f_actuals(formals, pactuals) parsetree formals, *pactuals; {
 487:     /* name, actual, next */
 488:     value actuals= *pactuals, act, form, next_a, next_f, kw, *pact;
 489:     kw= *Branch(actuals, ACT_KEYW);
 490:     pact= Branch(actuals, ACT_EXPR); act= *pact;
 491:     form= *Branch(formals, FML_TAG);
 492:     next_a= *Branch(actuals, ACT_NEXT); next_f= *Branch(formals, FML_NEXT);
 493:     if (compare(*Branch(formals, FML_KEYW), kw) != 0)
 494:         fixerr3(MESS(2210, "wrong keyword "), kw, 0);
 495:     else if (act == Vnil && form != Vnil)
 496:         fixerr3(MESS(2211, "missing actual after "), kw, 0);
 497:     else if (next_a == Vnil && next_f != Vnil)
 498:         fixerr3(MESS(2212, "can't find expected "),
 499:             *Branch(next_f, FML_KEYW), 0);
 500:     else if (act != Vnil && form == Vnil)
 501:         fixerr3(MESS(2213, "unexpected actual after "), kw, 0);
 502:     else if (next_a != Vnil && next_f == Vnil)
 503:         fixerr3(MESS(2214, "unexpected keyword "),
 504:             *Branch(next_a, ACT_KEYW), 0);
 505:     else {
 506:         if (act != Vnil) {
 507:             parsetree st; struct state save;
 508:             hold(&save); here(&st);
 509:             if (is_target(act)) f_targ(pact);
 510:             else f_expr(pact);
 511:             jumpto(Stop); let_go(&save);
 512:             *Branch(actuals, ACT_START)= copy(st);
 513:         }
 514:         if (still_ok && next_a != Vnil)
 515:             f_actuals(next_f, Branch(actuals, ACT_NEXT));
 516:     }
 517: }
 518: 
 519: Hidden Procedure f_ucommand(pt) parsetree *pt; {
 520:     value t= *pt, *aa;
 521:     parsetree u, *f1= Branch(t, UCMD_NAME), *f2= Branch(t, UCMD_ACTUALS);
 522:     release(*Branch(t, UCMD_DEF));
 523:     *Branch(t, UCMD_DEF)= Vnil;
 524:     if ((aa= envassoc(refinements, *f1)) != Pnil) {
 525:         if (*Branch(*f2, ACT_EXPR) != Vnil
 526:                 || *Branch(*f2, ACT_NEXT) != Vnil)
 527:             fixerr(MESS(2215, "refinement with parameters"));
 528:         else *Branch(t, UCMD_DEF)= copy(*aa);
 529:     }
 530:     else if (is_unit(*f1, How, &aa)) {
 531:         u= How_to(*aa)->unit;
 532:         f_actuals(*Branch(u, HOW_FORMALS), f2);
 533:     }
 534:     else if (still_ok)
 535:         fixerr3(MESS(2216, "you haven't told me HOW'TO "), *f1, 0);
 536: }
 537: 
 538: Hidden Procedure f_fpr_formals(t) parsetree t; {
 539:     switch (Nodetype(t)) {
 540:     case TAG:
 541:         break;
 542:     case MONF: case MONPRD:
 543:         f_targ(Branch(t, MON_RIGHT));
 544:         break;
 545:     case DYAF: case DYAPRD:
 546:         f_targ(Branch(t, DYA_LEFT));
 547:         f_targ(Branch(t, DYA_RIGHT));
 548:         break;
 549:     default:
 550:         syserr(MESS(2217, "f_fpr_formals"));
 551:     }
 552: }
 553: 
 554: Visible bool modify_tag(name, tag) parsetree *tag; value name; {
 555:     value *aa, function;
 556:     *tag= NilTree;
 557:     if (aa= envassoc(formals, name))
 558:         *tag= node3(TAGformal, name, copy(*aa));
 559:     else if (locals != Vnil && (aa= envassoc(locals, name)))
 560:         *tag= node3(TAGlocal, name, copy(*aa));
 561:     else if (aa= envassoc(globals, name))
 562:         *tag= node2(TAGglobal, name);
 563:     else if (aa= envassoc(mysteries, name))
 564:         *tag= node3(TAGmystery, name, copy(*aa));
 565:     else if (aa= envassoc(refinements, name))
 566:         *tag= node3(TAGrefinement, name, copy(*aa));
 567:     else if (is_zerfun(name, &function))
 568:         *tag= node3(TAGzerfun, name, copydef(function));
 569:     else if (is_zerprd(name, &function))
 570:         *tag= node3(TAGzerprd, name, copydef(function));
 571:     else return No;
 572:     return Yes;
 573: }
 574: 
 575: Hidden Procedure f_etag(pt) parsetree *pt; {
 576:     parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
 577:     if (modify_tag(name, &t)) {
 578:         release(*pt);
 579:         *pt= t;
 580:         if (Nodetype(t) == TAGzerprd)
 581:             fixerr2(name, MESS(2218, " cannot be used in an expression"));
 582:         else
 583:             visit(t);
 584:     } else {
 585:         fixerr2(name, MESS(2219, " has not yet received a value"));
 586:         release(name);
 587:     }
 588: }
 589: 
 590: Hidden Procedure f_ttag(pt) parsetree *pt; {
 591:     parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
 592:     if (modify_tag(name, &t)) {
 593:         release(*pt);
 594:         *pt= t;
 595:         switch (Nodetype(t)) {
 596:         case TAGrefinement:
 597:             fixerr(MESS(2220, "a refinement may not be used as a target"));
 598:             break;
 599:         case TAGzerfun:
 600:         case TAGzerprd:
 601:             fixerr2(name, MESS(2221, " hasn't been initialised or defined"));
 602:             break;
 603:         default:
 604:             lvisit(t);
 605:             break;
 606:         }
 607:     } else {
 608:         fixerr2(name, MESS(2222, " hasn't been initialised or defined"));
 609:         release(name);
 610:     }
 611: }
 612: 
 613: Hidden Procedure f_ctag(pt) parsetree *pt; {
 614:     parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
 615:     if (modify_tag(name, &t)) {
 616:         release(*pt);
 617:         *pt= t;
 618:         switch (Nodetype(t)) {
 619:         case TAGrefinement:
 620:             lvisit(t); /* 'Loc' flag here means 'Test' */
 621:             break;
 622:         case TAGzerprd:
 623:             visit(t);
 624:             break;
 625:         default:
 626:             fixerr2(name, MESS(2223, " is neither a refined test nor a zeroadic predicate"));
 627:             break;
 628:         }
 629:     } else {
 630:         fixerr2(name, MESS(2224, " is neither a refined test nor a zeroadic predicate"));
 631:         release(name);
 632:     }
 633: }

Defined functions

backpatch defined in line 140; used 1 times
copydef defined in line 438; used 13 times
endthreads defined in line 130; used 1 times
  • in line 28
f_actuals defined in line 486; used 2 times
f_ctag defined in line 613; used 1 times
f_etag defined in line 575; used 1 times
f_fpr_formals defined in line 538; used 1 times
f_ttag defined in line 590; used 1 times
f_ucommand defined in line 519; used 1 times
fix defined in line 258; used 6 times
here defined in line 210; used 3 times
hold defined in line 216; used 3 times
inithreads defined in line 121; used 1 times
  • in line 26
is_basic_target defined in line 444; used 2 times
is_cmd_ref defined in line 431; used 2 times
is_target defined in line 458; used 3 times
is_variable defined in line 451; used 3 times
jump2here defined in line 204; used 2 times
jumphere defined in line 196; never used
jumpto defined in line 149; used 6 times
let_go defined in line 222; used 3 times
lvisit defined in line 191; used 3 times
modify_tag defined in line 554; used 5 times
reachable defined in line 245; used 3 times
seterr defined in line 165; used 1 times
visit defined in line 185; used 4 times
visit2 defined in line 171; used 3 times

Defined variables

Forward defined in line 254; never used
bpchain defined in line 113; used 13 times
last defined in line 111; used 14 times
start defined in line 109; used 3 times
wanthere defined in line 114; used 13 times

Defined macros

Flag defined in line 106; used 1 times
Last modified: 1985-08-27
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2442
Valid CSS Valid XHTML 1.0 Strict