-- file Pass2.Mesa
-- last modified by Satterthwaite, November 2, 1979  8:47 AM

DIRECTORY
  ComData: FROM "comdata"
    USING [
      bodyIndex, bodyRoot, defBodyLimit, idINTEGER, idLOCK,
      importCtx, mainBody, mainCtx, moduleCtx,
      monitored, nBodies, nSigCodes, nTypeCodes, textIndex, typeMapId],
  CompilerUtil: FROM "compilerutil",
  Log: FROM "log" USING [Error, ErrorHti],
  Symbols: FROM "symbols"
    USING [
      BodyLink, BodyInfo, BodyRecord, ContextLevel, SERecord, TransferMode,
      HTIndex, SEIndex, CSEIndex, ISEIndex, RecordSEIndex,
      CTXIndex, BTIndex, CBTIndex,
      HTNull,  SENull,  CSENull,  ISENull,  RecordSENull,
      CTXNull,  BTNull,  CBTNull,
      lG, lL, lZ, typeANY, seType, ctxType, bodyType],
  SymbolOps: FROM "symbolops"
    USING [
      FillCtxSe, NewCtx, MakeNonCtxSe, MakeSeChain, NameClash,
      NextLevel, NextSe, StaticNestError],
  Table: FROM "table"
    USING [Base, Notifier, AddNotify, Allocate, DropNotify, Bounds],
  Tree: FROM "tree" USING [Index, Link, Map, Null, NullIndex, Scan, treeType],
  TreeOps: FROM "treeops"
    USING [
      FreeNode, GetNode, ListHead, ListLength,
      ScanList, TestTree, UpdateList];

Pass2: PROGRAM
    IMPORTS
	Log, SymbolOps, Table, TreeOps,
	dataPtr: ComData
    EXPORTS CompilerUtil =
  BEGIN
  OPEN TreeOps, SymbolOps, Symbols;

  tb: Table.Base;	-- tree base (private copy)
  seb: Table.Base;	-- se table base (private copy)
  ctxb: Table.Base;	-- context table base (private copy)
  bb: Table.Base;	-- body table base (private copy)

  Notify: Table.Notifier =
    BEGIN  -- called by allocator whenever tables are repacked
    tb ← base[Tree.treeType];
    seb ← base[seType];  ctxb ← base[ctxType];  bb ← base[bodyType];
    END;

  ContextInfo: TYPE = RECORD [
    ctx: CTXIndex,
    staticLevel: ContextLevel,
    seChain: ISEIndex];

  current: ContextInfo;

  NewContext: PROCEDURE [level: ContextLevel, entries: CARDINAL, unique: BOOLEAN] =
    BEGIN
    OPEN current;
    staticLevel ← level;
    IF entries = 0 AND ~unique
      THEN  BEGIN  ctx ← CTXNull;  seChain ← ISENull  END
      ELSE
	BEGIN
	ctx ← NewCtx[level];
	ctxb[ctx].seList ← seChain ← MakeSeChain[ctx, entries, FALSE];
	END;
    END;


 -- main driver

  P2Unit: PUBLIC PROCEDURE [t: Tree.Link] RETURNS [Tree.Link] =
    BEGIN
    node: Tree.Index;
    Table.AddNotify[Notify];
    node ← GetNode[t];
      BEGIN
      ENABLE	-- default error reporting
	BEGIN
	NameClash => BEGIN Log.ErrorHti[duplicateId, hti]; RESUME END;
	StaticNestError => BEGIN Log.Error[staticNesting]; RESUME END;
	END;
      dataPtr.textIndex ← tb[node].info;
      NewContext[lZ, ListLength[tb[node].son[1]]+ListLength[tb[node].son[2]], FALSE];
      dataPtr.moduleCtx ← current.ctx;
      ScanList[tb[node].son[1], IdDefinition];
      ScanList[tb[node].son[2], Module];
      END;
    Table.DropNotify[Notify];
    RETURN [t]
    END;


  lockLambda: Tree.Index;

  Module: Tree.Scan =
    BEGIN
    saved: ContextInfo;
    saveIndex: CARDINAL = dataPtr.textIndex;
    node: Tree.Index = GetNode[t];
    dataPtr.bodyIndex ← CBTNull;  dataPtr.nBodies ← dataPtr.nSigCodes ← 0;
    btLink ← [which:parent, index:BTNull];
    dataPtr.textIndex ← tb[node].info;
    -- process import list
      saved ← current;
      NewContext[lG, ListLength[tb[node].son[1]], FALSE];
      dataPtr.importCtx ← current.ctx;
      ScanList[tb[node].son[1], IdDefinition];
      current ← saved;
    dataPtr.monitored ← tb[node].son[4] # Tree.Null;
    lockLambda ← Lambda[tb[node].son[4], lL];
    DeclList[tb[node].son[5], SENull];
    BodyList[dataPtr.bodyRoot];
    dataPtr.defBodyLimit ← Table.Bounds[bodyType].size;
    dataPtr.textIndex ← saveIndex;
    END;

  IdDefinition: Tree.Scan =
    BEGIN
    node: Tree.Index = GetNode[t];
    saveIndex: CARDINAL = dataPtr.textIndex;
    dataPtr.textIndex ← tb[node].info;
    tb[node].son[1] ← Ids[
			list: tb[node].son[1],
			public: FALSE,
			link: node];
    dataPtr.textIndex ← saveIndex;
    END;


 -- monitor lock processing

  Lambda: PROCEDURE [item: Tree.Link, level: ContextLevel] RETURNS [node: Tree.Index] =
    BEGIN
    saved: ContextInfo = current;
    node ← GetNode[item];
    IF node # Tree.NullIndex
      THEN
	BEGIN
	NewContext[level, CountIds[tb[node].son[1]], FALSE];
	tb[node].info ← current.ctx;
	DeclList[tb[node].son[1], SENull];
	IF tb[node].son[2] # Tree.Null THEN Exp[tb[node].son[2]];
	END;
    current ← saved;  RETURN
    END;

  ImplicitLock: PROCEDURE [sei: ISEIndex] =
    BEGIN
    WITH tb[lockLambda].son[2] SELECT FROM
      hash =>  FillCtxSe[sei, index, tb[lockLambda].attr2];
      ENDCASE => ERROR;
     BEGIN  OPEN seb[sei];
     public ← tb[lockLambda].attr2;
     extended ← immutable ← constant ← linkSpace ← FALSE;
     idType ← dataPtr.idLOCK;
     idInfo ← 1;  idValue ← Tree.NullIndex;
     mark3 ← TRUE;  mark4 ← FALSE;
     END;
    tb[lockLambda].son[2] ← [symbol[index: sei]];
    END;


 -- type map processing

  AllocateTypeMap: PROCEDURE [sei: ISEIndex] =
    BEGIN
    mapType, subType: CSEIndex;
    FillCtxSe[sei, HTNull, FALSE];
    subType ← MakeNonCtxSe[SIZE[subrange cons SERecord]];
    seb[subType].typeInfo ← subrange[
		filled: FALSE, empty: FALSE, flexible: FALSE,
		rangeType: dataPtr.idINTEGER,
		origin: , range: ];
    seb[subType].mark3 ← TRUE;
    mapType ← MakeNonCtxSe[SIZE[array cons SERecord]];
    seb[mapType].typeInfo ← array[
		oldPacked: FALSE, lengthUsed: TRUE, comparable: TRUE,
		indexType: subType,
		componentType: typeANY];
    seb[mapType].mark3 ← TRUE;
     BEGIN  OPEN seb[sei];
     public ← extended ← constant ← linkSpace ← FALSE;  immutable ← TRUE; 
     idType ← mapType;
     idInfo ← 1;  idValue ← Tree.NullIndex;
     mark3 ← TRUE;  mark4 ← FALSE;
     END;
    END;


 -- body processing

  btLink: BodyLink;

  AllocateBody: PROCEDURE [node: Tree.Index] RETURNS [bti: CBTIndex] =
    BEGIN	-- queue body for later processing
    -- force nesting message here
      SELECT NextLevel[current.staticLevel] FROM
	lG, lL =>
	  BEGIN
	  bti ← Table.Allocate[bodyType, SIZE[Outer Callable BodyRecord]];
	  bb[bti] ← BodyRecord[,,,,, Callable[,,,,,,,,,, Outer[]]];
	  END;
	ENDCASE =>
	  BEGIN
	  bti ← Table.Allocate[bodyType, SIZE[Inner Callable BodyRecord]];
	  bb[bti] ← BodyRecord[,,,,,Callable[,,,,,,,,,,Inner[frameOffset: ]]];
	  END;
    bb[bti].firstSon ← BTNull;
    bb[bti].info ← BodyInfo[Internal[
	bodyTree: node,
	sourceIndex: dataPtr.textIndex,
	thread: Tree.NullIndex,
	frameSize: ]];
    bb[bti].id ← IF tb[node].attr1 THEN FirstId[tb[node].son[1]]
				   ELSE ISENull;
    bb[bti].ioType ← typeANY;
    LinkBody[bti];  RETURN
    END;

  LinkBody: PROCEDURE [bti: BTIndex] =
    BEGIN
    IF btLink.which = parent
      THEN
	BEGIN
	bb[bti].link ← btLink;
	IF btLink.index = BTNull THEN dataPtr.bodyRoot ← bti
				 ELSE bb[btLink.index].firstSon ← bti;
	END
      ELSE
	BEGIN
	bb[bti].link ← bb[btLink.index].link;
	bb[btLink.index].link ← [which:sibling, index: bti];
	END;
    btLink ← [which:sibling, index: bti];
    END;


  BodyList: PROCEDURE [firstBti: BTIndex] =
    BEGIN
    bti: BTIndex;
    IF (bti ← firstBti) # BTNull
      THEN
	DO
	WITH bb[bti] SELECT FROM
	  Callable =>  Body[LOOPHOLE[bti, CBTIndex]];
	  ENDCASE => NULL;
	IF bb[bti].link.which = parent THEN EXIT;
	bti ← bb[bti].link.index;
	ENDLOOP;
    END;

  Body: PROCEDURE [bti: CBTIndex] =
    BEGIN
    node: Tree.Index;
    bodyLevel: ContextLevel;
    nLocks, nMaps: [0..1];
    oldBodyIndex: CBTIndex = dataPtr.bodyIndex;
    oldBtLink: BodyLink = btLink;
    saved: ContextInfo = current;
    dataPtr.bodyIndex ← bti;
    btLink ← [which:parent, index:bti];
    node ← WITH bb[bti].info SELECT FROM
      Internal =>  GetNode[tb[LOOPHOLE[bodyTree,Tree.Index]].son[3]],
      ENDCASE =>   ERROR;
    bodyLevel ← NextLevel[saved.staticLevel !StaticNestError => RESUME];
    nLocks ← IF dataPtr.monitored AND
	bodyLevel = lG AND tb[lockLambda].attr1 THEN 1 ELSE 0;
    nMaps ← IF bodyLevel = lG AND dataPtr.nTypeCodes # 0 THEN 1 ELSE 0;
    NewContext[
	level: bodyLevel,
	entries: nLocks + CountIds[tb[node].son[2]] + nMaps,
	unique: bodyLevel = lG];
    bb[bti].localCtx ← current.ctx;  bb[bti].level ← bodyLevel;
    bb[bti].monitored ← nLocks # 0;
    bb[bti].inline ← tb[node].attr3;
    IF bodyLevel = lG
      THEN
	BEGIN
	dataPtr.mainCtx ← current.ctx;  dataPtr.mainBody ← bti;
	dataPtr.typeMapId ← ISENull;
	END;
    ScanList[tb[node].son[1], Exp];
    IF nLocks # 0
      THEN
	BEGIN
	ImplicitLock[current.seChain];
	current.seChain ← NextSe[current.seChain]
	END;
    DeclList[tb[node].son[2], SENull];
    IF nMaps # 0
      THEN
	BEGIN
	dataPtr.typeMapId ← current.seChain;
	current.seChain ← NextSe[current.seChain];
	AllocateTypeMap[dataPtr.typeMapId];
	END;
    ScanList[tb[node].son[3], Stmt];
    BodyList[bb[bti].firstSon];
    current ← saved;  dataPtr.bodyIndex ← oldBodyIndex;  btLink ← oldBtLink;
    END;


  Inline: Tree.Scan = BEGIN ScanList[t, Exp] END;


 -- declarations

  DeclList: PROCEDURE [t: Tree.Link, linkId: SEIndex] =
    BEGIN

    DeclItem: Tree.Scan =
      BEGIN
      node: Tree.Index = GetNode[t];
      subNode: Tree.Index;
      saveIndex: CARDINAL = dataPtr.textIndex;
      dataPtr.textIndex ← tb[node].info;
      tb[node].son[1] ← Ids[
		list: tb[node].son[1],
		public: tb[node].attr2,
		readOnly: tb[node].attr3,
		link: node];
      tb[node].attr2 ← tb[node].attr3 ← FALSE;
      IF tb[node].name = typedecl
	THEN
	  BEGIN
	  TypeExp[tb[node].son[2], FirstId[tb[node].son[1]], linkId];
	  ScanList[tb[node].son[3], Exp];
	  END
	ELSE
	  BEGIN
	  TypeExp[tb[node].son[2], SENull, linkId];
	  IF tb[node].son[3] # Tree.Null AND tb[node].son[3].tag = subtree
	    THEN
	      BEGIN
	      subNode ← GetNode[tb[node].son[3]];
	      SELECT tb[subNode].name FROM
		entry, internal =>
		  BEGIN
		  IF ~dataPtr.monitored OR ~TestTree[tb[subNode].son[1], body]
		    THEN Log.Error[misplacedEntry]
		    ELSE
		      WITH tb[subNode].son[1] SELECT FROM
			subtree =>
			  SELECT tb[subNode].name FROM
			    entry =>  tb[index].attr1 ← TRUE;
			    internal =>  tb[index].attr2 ← TRUE;
			    ENDCASE;
			ENDCASE;
		  tb[node].son[3] ← tb[subNode].son[1];
		  tb[subNode].son[1] ← Tree.Null;  FreeNode[subNode];
		  END;
		ENDCASE;
	      END;
	  IF tb[node].son[3] # Tree.Null
	    THEN
	      WITH tb[node].son[3] SELECT FROM
		subtree =>
		  BEGIN  subNode ← index;
		  SELECT tb[subNode].name FROM
		    body =>
		      BEGIN
		      tb[subNode].info ← AllocateBody[node];
		      IF ~tb[subNode].attr3
			THEN  dataPtr.nBodies ← dataPtr.nBodies+1;
		      END;
		    signalinit =>
		      BEGIN
		      tb[subNode].info ← dataPtr.nSigCodes;
		      dataPtr.nSigCodes ← dataPtr.nSigCodes+1;
		      END;
		    inline => ScanList[tb[subNode].son[1], Inline];
		    ENDCASE => ScanList[tb[node].son[3], Exp];
		  END;
		ENDCASE =>  ScanList[tb[node].son[3], Exp];
	  END;
      dataPtr.textIndex ← saveIndex;
      END;

    ScanList[root:t, action:DeclItem];
    END;



  CountIds: PROCEDURE [declList: Tree.Link] RETURNS [n: CARDINAL] =
    BEGIN

    nIds: Tree.Scan =
      BEGIN
      node: Tree.Index = GetNode[t];
      n ← n + ListLength[tb[node].son[1]];
      END;

    n ← 0;  ScanList[declList, nIds];  RETURN
    END;


 -- id list manipulation

  Ids: PROCEDURE [
	list: Tree.Link,
	public: BOOLEAN,
	readOnly: BOOLEAN ← FALSE,
	link: Tree.Index]
      RETURNS [Tree.Link] =
    BEGIN

    Id: Tree.Map =
      BEGIN
      hti: HTIndex;
      sei: ISEIndex;
      ctx: CTXIndex = current.ctx;
      hti ← WITH t SELECT FROM
	hash => index,
	symbol => seb[index].hash,
	ENDCASE =>  ERROR;
      sei ← current.seChain;  current.seChain ← NextSe[current.seChain];
      FillCtxSe[sei, hti, public];
      v ← Tree.Link[symbol[index: sei]];
      seb[sei].idType ← typeANY;
      seb[sei].public ← public;  seb[sei].immutable ← readOnly;
      seb[sei].idValue ← link;  seb[sei].idInfo ← 0;
      seb[sei].extended ← seb[sei].linkSpace ← FALSE;
      RETURN
      END;

    RETURN [UpdateList[root:list, map:Id]]
    END;


  FirstId: PROCEDURE [t: Tree.Link] RETURNS [ISEIndex] =
    BEGIN
    head: Tree.Link = ListHead[t];
    RETURN [WITH head SELECT FROM  symbol => index,  ENDCASE => ERROR];
    END;


 -- type manipulation

  TypeExp: PROCEDURE [t: Tree.Link, typeId, linkId: SEIndex]  =
    BEGIN
    node: Tree.Index;
    sei: CSEIndex;
    tCtx: CTXIndex;
    nFields: CARDINAL;
    WITH t SELECT FROM
      subtree =>
	BEGIN  node ← index;
	SELECT tb[node].name FROM
	  enumeratedTC =>
	    BEGIN
	    sei ← MakeNonCtxSe[SIZE[enumerated cons SERecord]];
	    tCtx ← Enumeration[node];
	    seb[sei].typeInfo ← enumerated[
		ordered: TRUE,
		valueCtx: tCtx,
		nValues: ];
	    AssignValues[sei, IF typeId # SENull THEN typeId ELSE sei];
	    END;
	  recordTC, monitoredTC =>
	    BEGIN
	    sei ← MakeNonCtxSe[SIZE[notLinked record cons SERecord]];
	    [tCtx, nFields] ← FieldList[
		t: tb[node].son[1],
		level: lZ,
		typeId: IF typeId # SENull THEN typeId ELSE sei];
	    seb[sei].typeInfo ← record[
		machineDep: tb[node].attr1,
		argument: FALSE,
		hints: [
		  unifield: nFields = 1 AND ~tb[node].attr2,
		  variant: tb[node].attr2,
		  comparable: FALSE, privateFields: FALSE],
		length: ,
		lengthUsed: FALSE,
		fieldCtx: tCtx,
		monitored: tb[node].name = monitoredTC,
		linkPart: notLinked[]];
	    END;
	  variantTC =>
	    BEGIN
	    sei ← MakeNonCtxSe[SIZE[linked record cons SERecord]];
	    tCtx ← FieldList[t:tb[node].son[1], level:lZ, typeId:typeId].ctx;
	    seb[sei].typeInfo ← record[
		machineDep: tb[node].attr1,
		argument: FALSE,
		hints: [
		  variant: tb[node].attr2,
		  unifield: FALSE, comparable: FALSE, privateFields: FALSE],
		length: ,
		lengthUsed: FALSE,
		fieldCtx: tCtx,
		monitored: FALSE,
		linkPart: linked[linkId]];
	    END;
	  pointerTC =>
	    BEGIN  sei ← MakeNonCtxSe[SIZE[pointer cons SERecord]];
	    seb[sei].typeInfo ← pointer[
		ordered: tb[node].attr1,
		basing: tb[node].attr2,
		readOnly: tb[node].attr3,
		dereferenced: FALSE,
		refType: ];
	    TypeExp[tb[node].son[1], SENull, SENull];
	    END;
	  arrayTC =>
	    BEGIN  sei ← MakeNonCtxSe[SIZE[array cons SERecord]];
	    seb[sei].typeInfo ← array[
		oldPacked: tb[node].attr1,
		lengthUsed: FALSE,
		comparable: FALSE,
		indexType: ,
		componentType: ];
	    IF tb[node].son[1] # Tree.Null
	      THEN TypeExp[tb[node].son[1], SENull, SENull];
	    TypeExp[tb[node].son[2], SENull, SENull];
	    END;
	  arraydescTC =>
	    BEGIN  sei ← MakeNonCtxSe[SIZE[arraydesc cons SERecord]];
	    seb[sei].typeInfo ← arraydesc[
		readOnly: tb[node].attr3,
		describedType: ];
	    TypeExp[tb[node].son[1], SENull, SENull];
	    END;
	  procTC =>  sei ← Transfer[node, procedure];
	  portTC =>  sei ← Transfer[node, port];
	  signalTC =>  sei ← Transfer[node, signal];
	  errorTC =>  sei ← Transfer[node, error];
	  processTC =>  sei ← Transfer[node, process];
	  programTC =>  sei ← Transfer[node, program];
	  definitionTC => 
	    BEGIN
	    sei ← MakeNonCtxSe[SIZE[definition cons SERecord]];
	    seb[sei].typeInfo ← definition[nGfi: 1, named: FALSE, defCtx: ];
	    END;
	  unionTC =>  sei ← Union[node, linkId];
	  relativeTC =>
	    BEGIN
	    sei ← MakeNonCtxSe[SIZE[relative cons SERecord]];
	    seb[sei].typeInfo ← relative[
		baseType: ,
		offsetType: ,
		resultType: ];
	    TypeExp[tb[node].son[1], SENull, SENull];
	    TypeExp[tb[node].son[2], SENull, SENull];
	    END;
	  subrangeTC =>
	    BEGIN  sei ← MakeNonCtxSe[SIZE[subrange cons SERecord]];
	    seb[sei].typeInfo ← subrange[
		filled: FALSE,
		empty: FALSE,
		flexible: FALSE,
		rangeType: ,
		origin: ,
		range: ];
	    TypeExp[tb[node].son[1], SENull, SENull];
	    Interval[tb[node].son[2]];
	    END;
	  longTC =>
	    BEGIN  sei ← MakeNonCtxSe[SIZE[long cons SERecord]];
	    seb[sei].typeInfo ← long[rangeType: ];
	    TypeExp[tb[node].son[1], SENull, SENull];
	    END;
	  implicitTC, frameTC =>  sei ← CSENull;
	  dot, discrimTC =>
	    BEGIN TypeExp[tb[node].son[1], SENull, SENull]; sei ← CSENull END;
	  ENDCASE =>
	    BEGIN  sei ← CSENull;  Log.Error[nonTypeCons]  END;
	tb[node].info ← sei;
	END;
      ENDCASE => NULL;
    END;

  Enumeration: PROCEDURE [node: Tree.Index] RETURNS [ctx: CTXIndex] =
    BEGIN
    saved: ContextInfo = current;
    NewContext[lZ, ListLength[tb[node].son[1]], TRUE];  ctx ← current.ctx;
    tb[node].son[1] ← Ids[
	list: tb[node].son[1],
	public: tb[node].attr1,
	link: Tree.NullIndex];
    current ← saved;  RETURN
    END;

  AssignValues: PROCEDURE [type: CSEIndex, valueType: SEIndex] =
    BEGIN
    i: CARDINAL;
    sei: ISEIndex;
    WITH seb[type] SELECT FROM
      enumerated =>
	BEGIN  i ← 0;
	FOR sei ← ctxb[valueCtx].seList, NextSe[sei] UNTIL sei = SENull
	  DO  OPEN seb[sei];
	  idType ← valueType;  idInfo ← 0;
	  idValue ← i;  i ← i+1;
	  immutable ← constant ← mark3 ← mark4 ← TRUE;
	  ENDLOOP;
	nValues ← i;
	END;
      ENDCASE =>  ERROR;
    END;


  FieldList: PROCEDURE [t: Tree.Link, level: ContextLevel, typeId: SEIndex] 
      RETURNS [ctx: CTXIndex, nFields: CARDINAL] =
    BEGIN
    saved: ContextInfo = current;
    nFields ← CountIds[t];
    NewContext[level, nFields, TRUE];  ctx ← current.ctx; 
    DeclList[t, typeId];
    current ← saved;  RETURN
    END;

  Transfer: PROCEDURE [node: Tree.Index, mode: TransferMode] RETURNS [sei: CSEIndex] =
    BEGIN
    sei ← MakeNonCtxSe[SIZE[transfer cons SERecord]];
    seb[sei].typeInfo ← transfer[
	mode: mode,
	inRecord:  ArgList[tb[node].son[1]],
	outRecord: ArgList[tb[node].son[2]]];
    RETURN
    END;

  ArgList: PROCEDURE [t: Tree.Link] RETURNS [type: RecordSEIndex] =
    BEGIN
    tCtx: CTXIndex;
    nFields: CARDINAL;
    IF t = Tree.Null
      THEN type ← RecordSENull
      ELSE
	BEGIN 
	type ← LOOPHOLE[MakeNonCtxSe[SIZE[notLinked record cons SERecord]]];
	[tCtx, nFields] ← FieldList[t, lZ, type];
	seb[type].typeInfo ← record[
		machineDep: FALSE,
		argument: TRUE,
		hints: [
		  unifield: nFields = 1,
		  variant: FALSE, comparable: FALSE, privateFields: FALSE],
		length: ,
		lengthUsed: FALSE,
		fieldCtx: tCtx,
		monitored: FALSE,
		linkPart: notLinked[]];
	END;
    RETURN
    END;


  Union: PROCEDURE [node: Tree.Index, linkId: SEIndex] RETURNS [sei: CSEIndex] =
    BEGIN
    tagId: ISEIndex;
    subnode: Tree.Index;
    saved: ContextInfo = current;
    current.ctx ← CTXNull;  current.seChain ← MakeSeChain[CTXNull, 1, FALSE];
    DeclList[tb[node].son[1], SENull];
    subnode ← GetNode[tb[node].son[1]];
    tagId ← FirstId[tb[subnode].son[1]];
    WITH tb[subnode].son[2] SELECT FROM
      subtree =>
	IF tb[index].name = implicitTC
	  THEN tb[index].info ← MakeTagType[tb[node].son[2]];
      ENDCASE =>  NULL;
    NewContext[lZ, CountIds[tb[node].son[2]], TRUE];
    DeclList[tb[node].son[2], linkId
      !NameClash =>
	BEGIN  Log.ErrorHti[duplicateTag, hti];  RESUME  END];
    sei ← MakeNonCtxSe[SIZE[union cons SERecord]];
    seb[sei].typeInfo ← union[
		caseCtx: current.ctx,
		overlayed: tb[node].attr1,
		controlled: seb[tagId].hash # HTNull,
		tagSei: tagId,
		equalLengths: FALSE];
    current ← saved;  RETURN
    END;


  MakeTagType: PROCEDURE [t: Tree.Link] RETURNS [type: CSEIndex] =
    BEGIN
    saved: ContextInfo = current;

    CollectTags: Tree.Scan =
      BEGIN
      node: Tree.Index = GetNode[t];
      tb[node].son[1] ← Ids[
		list: tb[node].son[1],
		public: tb[node].attr2,
		link: Tree.NullIndex
	    !NameClash => RESUME];
      END;

    NewContext[lZ, CountIds[t], TRUE];
    type ← MakeNonCtxSe[SIZE[enumerated cons SERecord]];
    seb[type].typeInfo ← enumerated[
	ordered: FALSE,
	valueCtx: current.ctx,
	nValues: ];
    ScanList[t, CollectTags];
    AssignValues[type, type];
    current ← saved;  RETURN
    END;


 -- statements

  Stmt: PROCEDURE [stmt: Tree.Link] =
    BEGIN
    node, subNode: Tree.Index;
    saveIndex: CARDINAL = dataPtr.textIndex;
    IF stmt = Tree.Null THEN RETURN;
    WITH stmt SELECT FROM
      subtree =>
	BEGIN  node ← index;
	dataPtr.textIndex ← tb[node].info;
	SELECT tb[node].name FROM
	  assign =>  BEGIN  Exp[tb[node].son[1]];  Exp[tb[node].son[2]]  END;
	  extract =>
	    BEGIN  ScanList[tb[node].son[1], Exp];  Exp[tb[node].son[2]]  END;
	  apply =>
	    BEGIN
	    Exp[tb[node].son[1]];  ScanList[tb[node].son[2], Exp];
	    IF tb[node].nSons > 2 THEN CatchPhrase[tb[node].son[3]];
	    END;
	  block =>  Block[node];
	  if =>
	    BEGIN  OPEN tb[node];
	    Exp[son[1]];  ScanList[son[2], Stmt];  ScanList[son[3], Stmt];
	    END;
	  case =>
	    BEGIN  OPEN tb[node];
	    Exp[son[1]];  SelectionList[son[2], Stmt];  Stmt[son[3]];
	    END;
	  bind =>
	    BEGIN  OPEN tb[node];
	    Exp[son[1]];
	    IF son[2] # Tree.Null THEN Exp[son[2]];
	    SelectionList[son[3], Stmt];
	    Stmt[son[4]];
	    END;
	  do =>
	    BEGIN  OPEN tb[node];
	    IF son[1] # Tree.Null
	      THEN
		BEGIN  subNode ← GetNode[son[1]];
		IF tb[subNode].son[1] # Tree.Null
		  THEN Exp[tb[subNode].son[1]];
		SELECT tb[subNode].name FROM
		  forseq =>
		    BEGIN
		    Exp[tb[subNode].son[2]];  Exp[tb[subNode].son[3]];
		    END;
		  upthru, downthru =>  Range[tb[subNode].son[2]];
		  ENDCASE => ERROR;
		END;
	    IF son[2] # Tree.Null THEN Exp[son[2]];
	    ScanList[son[3], Exp];
	    ScanList[son[4], Stmt];
	    ScanList[son[5], Stmt]; ScanList[son[6], Stmt];
	    END;
	  return, resume =>  ScanList[tb[node].son[1], Exp];
	  label =>
	    BEGIN
	    ScanList[tb[node].son[1], Stmt];  ScanList[tb[node].son[2], Stmt];
	    END;
	  goto, exit, loop, continue, retry, syserror, null =>  NULL;
	  signal, error, xerror, start, restart,
	  join, wait, notify, broadcast, dst, lst, lstf =>
	    Exp[tb[node].son[1]];
	  stop =>
	    IF tb[node].son[1] # Tree.Null THEN CatchPhrase[tb[node].son[1]];
	  open =>
	    BEGIN
	    ScanList[tb[node].son[1], Exp];  ScanList[tb[node].son[2], Stmt];
	    END;
	  enable =>
	    BEGIN
	    CatchPhrase[tb[node].son[1]];  ScanList[tb[node].son[2], Stmt];
	    END;
          list =>  ScanList[stmt, Stmt];
          item =>  Stmt[tb[node].son[2]];
          ENDCASE =>  Log.Error[unimplemented];
	END;
      ENDCASE =>  NULL;
    dataPtr.textIndex ← saveIndex;
    END;


  Block: PROCEDURE [node: Tree.Index] =
    BEGIN
    bti: BTIndex;
    oldBtLink: BodyLink;
    saved: ContextInfo = current;
    NewContext[
	level: saved.staticLevel,
	entries: CountIds[tb[node].son[1]],
	unique: FALSE];
    bti ← Table.Allocate[bodyType, SIZE[Other BodyRecord]];
    bb[bti] ← BodyRecord[
	link: ,
	firstSon: BTNull,
	localCtx: current.ctx,
	level: current.staticLevel,
	info: BodyInfo[Internal[
	  bodyTree: node,
	  sourceIndex: tb[node].info,
	  thread: Tree.NullIndex,
	  frameSize: ]],
	extension: Other[]];
    LinkBody[bti];  oldBtLink ← btLink;  btLink ← [which:parent, index:bti];
    tb[node].info ← bti;
    DeclList[tb[node].son[1], SENull];
    ScanList[tb[node].son[2], Stmt];
    BodyList[bb[bti].firstSon];
    current ← saved;  btLink ← oldBtLink;
    END;


  SelectionList: PROCEDURE [t: Tree.Link, selection: Tree.Scan] =
    BEGIN

    Item: Tree.Scan =
      BEGIN
      node: Tree.Index = GetNode[t];
      saveIndex: CARDINAL = dataPtr.textIndex;
      dataPtr.textIndex ← tb[node].info;
      ScanList[tb[node].son[1], Exp];  selection[tb[node].son[2]];
      dataPtr.textIndex ← saveIndex;
      END;

    ScanList[t, Item];
    END;


  CatchPhrase: PROCEDURE [t: Tree.Link] =
    BEGIN
    node: Tree.Index = GetNode[t];
    saved: ContextInfo = current;
    NewContext[
	level: NextLevel[saved.staticLevel],
	entries: 0,
	unique: FALSE];
    SelectionList[tb[node].son[1], Stmt];
    IF tb[node].nSons > 1 THEN ScanList[tb[node].son[2], Stmt];
    current ← saved;
    END;


 -- expressions

  Exp: PROCEDURE [exp: Tree.Link] =
    BEGIN
    node, subNode: Tree.Index;
    IF exp = Tree.Null THEN RETURN;
    WITH exp SELECT FROM
      subtree =>
	BEGIN  node ← index;
	SELECT tb[node].name FROM
	  apply =>
	    BEGIN
	    Exp[tb[node].son[1]];  ScanList[tb[node].son[2], Exp];
	    IF tb[node].nSons > 2 THEN CatchPhrase[tb[node].son[3]];
	    END;
	  signalx, errorx, startx, fork, joinx,
	  dot, uparrow,
	  uminus, not, addr, new =>
	    Exp[tb[node].son[1]];
	  plus, minus, times, div, mod,
	  relE, relN, relL, relGE, relG, relLE,
	  or, and, assignx =>
	    BEGIN  Exp[tb[node].son[1]];  Exp[tb[node].son[2]]  END;
	  in, notin =>
	    BEGIN  Exp[tb[node].son[1]];  Range[tb[node].son[2]]  END;
	  ifx =>
	    BEGIN
	    Exp[tb[node].son[1]]; Exp[tb[node].son[2]]; Exp[tb[node].son[3]];
	    END;
	  casex =>
	    BEGIN  OPEN tb[node];
	    Exp[son[1]];  SelectionList[son[2], Exp];  Exp[son[3]];
	    END;
	  bindx =>
	    BEGIN  OPEN tb[node];
	    Exp[son[1]];
	    IF son[2] # Tree.Null THEN Exp[son[2]];
	    SelectionList[son[3], Exp];
	    Exp[son[4]];
	    END;
	  lengthen, float, abs, min, max, base, length, all =>
	    ScanList[tb[node].son[1], Exp];
	  arraydesc =>
	    SELECT ListLength[tb[node].son[1]] FROM
	      1 => Exp[tb[node].son[1]];
	      3 =>
		BEGIN
		subNode ← GetNode[tb[node].son[1]];
		Exp[tb[subNode].son[1]];  Exp[tb[subNode].son[2]];
		IF tb[subNode].son[3] # Tree.Null
		  THEN TypeExp[tb[subNode].son[3], SENull, SENull];
		END;
	      ENDCASE => ERROR;
	  void, clit, llit, mwconst, syserrorx =>  NULL;
	  loophole =>
	    BEGIN
	    Exp[tb[node].son[1]];
	    IF tb[node].son[2] # Tree.Null
	      THEN TypeExp[tb[node].son[2], SENull, SENull];
	    END;
	  size, first, last, typecode =>
	    TypeExp[tb[node].son[1], SENull, SENull];
	  item =>  Exp[tb[node].son[2]];
	  ENDCASE =>  Log.Error[unimplemented];
	END;
      ENDCASE => NULL;
    END;

  Interval: PROCEDURE [t: Tree.Link] =
    BEGIN
    node: Tree.Index = GetNode[t];
    Exp[tb[node].son[1]];  Exp[tb[node].son[2]];
    END;

  Range: PROCEDURE [t: Tree.Link] =
    BEGIN
    node: Tree.Index;
    WITH t SELECT FROM
      subtree =>
	BEGIN  node ← index;
	SELECT tb[node].name FROM
	  subrangeTC =>
	    BEGIN
	    TypeExp[tb[node].son[1], SENull, SENull];
	    Interval[tb[node].son[2]];
	    END;
	  IN [intOO .. intCC] => Interval[t];
	  ENDCASE => TypeExp[t, SENull, SENull];
	END;
      ENDCASE => TypeExp[t, SENull, SENull];
    END;

END.