DIRECTORY
A3: 
TYPE 
USING [
LongPath, MarkedType, OperandType, PermanentType, TypeForTree, VarType],
 
Alloc: TYPE USING [Notifier, AddNotify, DropNotify],
ComData: 
TYPE 
USING [
interface, moduleCtx, seAnon, switches, table, textIndex, typeAtomRecord],
 
Copier: TYPE USING [CompleteContext, Delink, SearchFileCtx],
Log: TYPE USING [Error, ErrorHti, ErrorSei, ErrorTree, WarningSei],
P3: 
TYPE 
USING [
Attr, fullAttr, voidAttr, mark, MergeNP, phraseNP,
And, Exp, FirstId, InterfaceCtx, MakeRefType, ResolveType, ResolveValue,
RPop, RPush, RType, SetDefaultImport, VariantUnionType, VoidExp],
 
P3S: TYPE USING [currentBody, currentScope, safety],
Symbols: 
TYPE 
USING [
seType, ctxType, mdType,
Base, CTXRecord, ExtensionType,
MDIndex, HTIndex, SEIndex, ISEIndex, CSEIndex, ArraySEIndex, RecordSEIndex,
CTXIndex, IncludedCTXIndex, Closure,
HTNull, SENull, ISENull, RecordSENull, CTXNull, IncludedCTXNull,
lG, lZ, StandardContext, typeTYPE, typeANY],
 
SymbolOps: 
TYPE 
USING [
ArgCtx, ConstantId, EnterExtension, FindExtension, FirstCtxSe, LinkMode,
NextSe, NormalType, RCType, RecordRoot, ReferentType, SearchContext, SetSeLink,
TypeLink, TypeRoot, UnderType],
 
SymLiteralOps: TYPE USING [EnterAtom, EnterType],
Tree: TYPE USING [Base, Index, Link, Map, Scan, Test, Null, NullIndex, treeType],
TreeOps: 
TYPE 
USING [
CopyTree, FreeTree, GetHash, GetNode, GetSe, ListLength, MarkShared,
NthSon, OpName, PopTree, PushTree, PushNode, ScanList, ScanSons, SearchList,
SetAttr, SetInfo, UpdateList, UpdateLeaves];
 
 
RefItem: TYPE = RECORD [kind: {seal, rhs, lhs}, sei: ISEIndex];
RefSeal: RefItem = [kind:seal, sei:ISENull];
RefSequence: TYPE = RECORD [SEQUENCE length: CARDINAL OF RefItem];
RefStack: TYPE = REF RefSequence;
refStack: RefStack;
refIndex: CARDINAL;
AdjustRefStack: 
PROC [n: 
CARDINAL] = {
oldStack: RefStack ← refStack;
refStack ← NEW[RefSequence[n]];
FOR i: CARDINAL IN [0..refIndex) DO refStack[i] ← oldStack[i] ENDLOOP;
oldStack ← NIL};
 
RecordMention: 
PUBLIC 
PROC [sei: ISEIndex] = {
IF dataPtr.switches['u] 
AND (seb[sei].idInfo = 0 
AND ~seb[sei].mark4) 
THEN {
IF refIndex >= refStack.length THEN AdjustRefStack[refStack.length + 8];
refStack[refIndex] ← [kind:rhs, sei:sei];
refIndex ← refIndex + 1}
 
ELSE BumpCount[sei]};
 
RecordLhs: 
PUBLIC 
PROC [sei: ISEIndex] = {
key: RefItem;
IF dataPtr.switches['u] 
AND (seb[sei].idInfo = 0 
AND ~seb[sei].mark4) 
THEN {
key ← [kind:rhs, sei:sei];
FOR i: 
CARDINAL 
DECREASING 
IN [0..refIndex) 
DO
SELECT refStack[i] 
FROM
key => {refStack[i].kind ← lhs; EXIT};
RefSeal => EXIT;
ENDCASE;
 
ENDLOOP}};
 
 
 
SealRefStack: 
PUBLIC 
PROC = {
IF refIndex >= refStack.length THEN AdjustRefStack[refStack.length + 8];
refStack[refIndex] ← RefSeal;  refIndex ← refIndex + 1};
 
UnsealRefStack: 
PUBLIC 
PROC = {
ClearRefStack[];    refIndex ← refIndex - 1;
IF refStack[refIndex] # RefSeal THEN ERROR};
 
ClearRefStack: 
PUBLIC 
PROC = {
sei: ISEIndex;
FOR i: 
CARDINAL 
DECREASING 
IN [0..refIndex) 
UNTIL refStack[i] = RefSeal 
DO
sei ← refStack[i].sei;
IF refStack[i].kind = rhs
AND ~ConstantInit[sei]
AND (~dataPtr.interface 
OR ctxb[seb[sei].idCtx].level # lG) 
THEN
Log.WarningSei[uninitialized, sei];
 
 
BumpCount[sei];
refIndex ← refIndex - 1;
ENDLOOP;
 
IF refStack.length > 16 AND refIndex <= 16 THEN AdjustRefStack[16]};
 
ConstantInit: 
PROC [sei: ISEIndex] 
RETURNS [
BOOL] = {
node: Tree.Index;
IF seb[sei].constant THEN RETURN [TRUE];
node ← seb[sei].idValue;
RETURN [seb[sei].immutable
AND node # Tree.NullIndex AND OpName[tb[node].son[3]] = body]};
 
 
 
Id: 
PUBLIC 
PROC [hti: HTIndex] 
RETURNS [val: Tree.Link] = {
sei: ISEIndex;
type: CSEIndex;
ctx: CTXIndex;
baseV: Tree.Link;
attr: Attr ← voidAttr;
indirect: BOOL;
[sei, baseV, indirect] ← FindSe[hti];
IF sei # ISENull 
THEN {
IF baseV = Tree.Null THEN RecordMention[sei] ELSE BumpCount[sei];
IF ~seb[sei].mark3 THEN ResolveIdType[sei];
val ← [symbol[index: sei]];  type ← UnderType[seb[sei].idType];
ctx ← seb[sei].idCtx;
SELECT ctxb[ctx].ctxType 
FROM
included => {
attr.const ← ConstantId[sei];
IF baseV = Tree.Null 
AND (~attr.const 
OR ~InterfaceConst[sei]) 
THEN
Log.ErrorSei[notImported, sei]};
 
 
imported => {
IF ~dataPtr.interface AND seb[type].typeTag = ref THEN [val, type] ← DeRef[val, type];
attr.const ← FALSE};
 
ENDCASE => {
IF ~ConstResolved[sei] THEN ResolveIdValue[sei];
attr.const ← seb[sei].constant};
 
 
SELECT 
TRUE 
FROM
baseV = Tree.Null => {
IF ctx = P3S.currentBody.argCtx THEN phraseNP ← ref;
IF ctxb[ctx].level = lZ 
AND ~attr.const 
THEN
SELECT ctx 
FROM
IN StandardContext, dataPtr.moduleCtx => NULL;
ENDCASE => Log.ErrorSei[missingBase, sei]};
 
 
 
(~attr.const 
AND ctxb[ctx].ctxType # imported) => {
attr ← And[UpdateTreeAttr[baseV], attr];
PushTree[CopyTree[baseV]];  PushTree[val];
IF indirect THEN {PushNode[dot, 2]; SetAttr[2, seb[OperandType[baseV]].typeTag = long]}
ELSE {PushNode[dollar, 2]; SetAttr[2, LongPath[baseV]]};
SetInfo[type];  val ← PopTree[]};
 
ENDCASE;
 
IF VarType[type] THEN [val, type] ← DeRef[val, type, TRUE];
IF seb[sei].extended THEN attr ← And[UpdateExtension[sei], attr]}
 
ELSE {
attr ← And[UpdateTreeAttr[baseV], attr];  type ← OperandType[baseV];
IF indirect THEN [val, type] ← DeRef[CopyTree[baseV], type]
ELSE val ← CopyTree[baseV]};
 
RPush[type, attr];
RETURN};
 
DeRef: 
PROC [t: Tree.Link, type: CSEIndex, var: 
BOOL←
FALSE]
RETURNS [Tree.Link, CSEIndex] = {
rType: CSEIndex = ReferentType[type];
PushTree[t];
PushNode[uparrow, 1];
SetInfo[rType];  SetAttr[2, seb[type].typeTag = long]; SetAttr[3, var];
RETURN [PopTree[], rType]};
 
UpdateExtension: 
PROC [sei: ISEIndex] 
RETURNS [attr: Attr] = {
t: Tree.Link;
tag: ExtensionType;
[tag, t] ← FindExtension[sei];
IF tag # value THEN attr ← fullAttr
ELSE {attr ← UpdateTreeAttr[t]; attr.const ← TRUE};
RETURN};
 
FieldId: 
PUBLIC 
PROC [hti: HTIndex, type: RecordSEIndex] 
RETURNS [n: 
CARDINAL, sei: ISEIndex] = {
[n, sei] ← SearchRecord[hti, type];
IF n # 0 
THEN {
IF ~seb[sei].mark3 THEN ResolveIdType[sei];
IF ~ConstResolved[sei] THEN ResolveIdValue[sei];
BumpCount[sei]};
 
RETURN};
 
InterfaceId: 
PUBLIC 
PROC [hti: HTIndex, ctx: CTXIndex]
RETURNS [found: BOOL, sei: ISEIndex] = {
[found, sei] ← SearchCtxList[hti, ctx];
IF found  
THEN {
SELECT ctxb[seb[sei].idCtx].ctxType 
FROM
imported => NULL;
included =>
IF ~ConstantId[sei] OR ~InterfaceConst[sei] THEN Log.ErrorSei[notImported, sei];
ENDCASE => {
IF ~seb[sei].mark3 THEN ResolveIdType[sei];
IF ~ConstResolved[sei] THEN ResolveIdValue[sei]};
 
 
 
BumpCount[sei]};
 
RETURN};
 
ClusterId: 
PUBLIC 
PROC [hti: HTIndex, ctx: CTXIndex] 
RETURNS [found: BOOL, sei: ISEIndex] = {
WITH c: ctxb[ctx] 
SELECT 
FROM
simple => [found, sei] ← SearchCtxList[hti, ctx];
included => {
mdi: MDIndex = c.module;
IF mdb[mdi].defaultImport = CTXNull 
THEN {  
-- avoid creating spurious principal imports
IF SearchCtxList[hti, ctx].found 
THEN
[found, sei] ← SearchCtxList[hti, DefaultImport[LOOPHOLE[ctx], FALSE]]
 
ELSE {found ← FALSE; sei ← ISENull}}
 
ELSE [found, sei] ← SearchCtxList[hti, mdb[mdi].defaultImport]};
 
ENDCASE => {found ← FALSE; sei ← ISENull};
 
IF found 
THEN {
IF ~seb[sei].mark3 THEN ResolveIdType[sei];
BumpCount[sei]};
 
RETURN};
 
 
ContextEntry: 
TYPE = 
RECORD [
base: Tree.Link,  -- the basing expr (empty if none)
indirect: BOOL,  -- true iff basing expr is pointer
info: 
SELECT tag: * 
FROM
list => [ctx: CTXIndex],  -- a single context
record => [rSei: RecordSEIndex], -- a group of contexts
hash => [ctxHti: HTIndex], -- a single identifier
ENDCASE];
 
 
ContextSequence: TYPE = RECORD [SEQUENCE length: NAT OF ContextEntry];
ContextStack: TYPE = REF ContextSequence;
ctxStack: ContextStack;
ctxIndex: INTEGER;
ContextIncr: CARDINAL = 16;
ExpandStack: 
PROC = {
oldstack: ContextStack ← ctxStack;
ctxStack ← NEW[ContextSequence[oldstack.length + ContextIncr]];
FOR i: CARDINAL IN [0 .. oldstack.length) DO ctxStack[i] ← oldstack[i] ENDLOOP;
oldstack ← NIL};
 
PushCtx: 
PUBLIC 
PROC [ctx: CTXIndex] = {
IF (ctxIndex ← ctxIndex+1) >= ctxStack.length THEN ExpandStack[];
ctxStack[ctxIndex] ← [base:Tree.Null, indirect:FALSE, info:list[ctx]]};
 
SetCtxBase: 
PROC [base: Tree.Link, indirect: 
BOOL] = {
ctxStack[ctxIndex].base ← base; ctxStack[ctxIndex].indirect ← indirect};
 
PushRecordCtx: 
PUBLIC 
PROC [rSei: RecordSEIndex, base: Tree.Link, indirect: 
BOOL] = { 
IF (ctxIndex ← ctxIndex+1) >= ctxStack.length THEN ExpandStack[];
ctxStack[ctxIndex] ← [base:base, indirect:indirect, info:record[rSei]]};
 
UpdateRecordCtx: 
PUBLIC 
PROC [type: RecordSEIndex] = {
WITH ctxStack[ctxIndex] 
SELECT 
FROM
record => rSei ← type;
ENDCASE => ERROR};
 
 
PushHtCtx: 
PUBLIC 
PROC [hti: HTIndex, base: Tree.Link, indirect: 
BOOL] = {
IF (ctxIndex ← ctxIndex+1) >= ctxStack.length THEN ExpandStack[];
ctxStack[ctxIndex] ← [base:base, indirect:indirect, info:hash[hti]]};
 
PopCtx: PUBLIC PROC = {ctxIndex ← ctxIndex-1};
TopCtx: 
PUBLIC 
PROC 
RETURNS [CTXIndex] = {
RETURN [WITH ctxStack[ctxIndex] SELECT FROM list => ctx, ENDCASE => ERROR]};
 
 
Shared: 
PUBLIC 
PROC [ctx: CTXIndex] 
RETURNS [
BOOL] = {
RETURN [
WITH c: ctxb[ctx] 
SELECT 
FROM
included => mdb[c.module].shared,
imported => Shared[c.includeLink],
ENDCASE => TRUE]};
 
 
CtxRestriction: TYPE = RECORD [ctx: IncludedCTXIndex, list: Tree.Link];
CtxIdTable: TYPE = RECORD [SEQUENCE length: CARDINAL OF CtxRestriction];
ctxIdTable: REF CtxIdTable;
CtxHash: 
PROC [ctx: IncludedCTXIndex] 
RETURNS [
CARDINAL] = 
INLINE {
RETURN [(LOOPHOLE[ctx, CARDINAL]/CTXRecord.included.SIZE) MOD ctxIdTable.length]};
 
MakeIdTable: 
PUBLIC 
PROC [nIdLists: 
CARDINAL] = {
ctxIdTable ← NEW[CtxIdTable[nIdLists]];
FOR i: CARDINAL IN [0..nIdLists) DO ctxIdTable[i] ← [IncludedCTXNull, Tree.Null] ENDLOOP};
 
EnterIdList: 
PUBLIC 
PROC [ctx: IncludedCTXIndex, list: Tree.Link] = {
i: CARDINAL ← CtxHash[ctx];
DO
IF ctxIdTable[i].ctx = IncludedCTXNull THEN {ctxIdTable[i] ← [ctx, list]; EXIT};
IF (i ← i+1) = ctxIdTable.length THEN i ← 0;
ENDLOOP};
 
 
CheckRestrictedCtx: 
PROC [hti: HTIndex, ctx: IncludedCTXIndex]
RETURNS [sei: ISEIndex] = {
TestId: Tree.Test = {
WITH t 
SELECT 
FROM
hash => IF index = hti THEN sei ← dataPtr.seAnon;
symbol => IF seb[index].hash = hti THEN sei ← index;
ENDCASE;
 
RETURN [sei # ISENull]};
 
i: CARDINAL ← CtxHash[ctx];
DO
IF ctxIdTable[i].ctx = ctx THEN EXIT;
IF (i ← i+1) = ctxIdTable.length THEN i ← 0;
ENDLOOP;
 
sei ← ISENull;  SearchList[ctxIdTable[i].list, TestId];
IF sei # ISENull AND seb[sei].idCtx = CTXNull THEN seb[sei].idCtx ← ctx;
RETURN};
 
SearchRestrictedCtx: 
PROC [hti: HTIndex, ctx: IncludedCTXIndex]
RETURNS [sei: ISEIndex] = {
sei ← CheckRestrictedCtx[hti, ctx];
IF sei # ISENull 
AND sei # dataPtr.seAnon 
AND seb[sei].idCtx # ctx 
THEN {
[ , sei] ← Copier.SearchFileCtx[hti, ctx];
seb[sei].public ← TRUE}; -- second copy, access already checked
 
RETURN};
 
CheckDirectoryIds: Tree.Scan = {
CheckId: Tree.Scan = {
WITH t 
SELECT 
FROM
symbol => IF seb[index].idCtx = CTXNull THEN Log.WarningSei[unusedId, index];
ENDCASE};
 
 
node: Tree.Index = GetNode[t];
saveIndex: CARDINAL = dataPtr.textIndex;
dataPtr.textIndex ← tb[node].info;
ScanList[tb[node].son[3], CheckId];
dataPtr.textIndex ← saveIndex};
 
CheckDisjoint: 
PUBLIC 
PROC [ctx1, ctx2: CTXIndex] = {
hti: HTIndex;
saveIndex: CARDINAL = dataPtr.textIndex;
IF ctx1 # CTXNull 
AND ctx2 # CTXNull 
THEN
FOR sei: ISEIndex ← FirstCtxSe[ctx2], NextSe[sei] 
UNTIL sei = ISENull 
DO
hti ← seb[sei].hash;
IF hti # HTNull 
AND SearchContext[hti, ctx1] # ISENull 
THEN {
IF ~seb[sei].mark3 
THEN
dataPtr.textIndex ← tb[LOOPHOLE[seb[sei].idValue, Tree.Index]].info;
 
Log.ErrorHti[duplicateId, hti]};
 
ENDLOOP;
 
 
dataPtr.textIndex ← saveIndex};
 
 
OpenedType: 
PROC [type: CSEIndex] 
RETURNS [CSEIndex] = {
subType: CSEIndex = NormalType[type];
RETURN [
WITH seb[subType] 
SELECT 
FROM
ref => UnderType[refType],
ENDCASE => type]};
 
 
OpenPointer: 
PUBLIC 
PROC [t: Tree.Link, type: CSEIndex]
RETURNS [Tree.Link, CSEIndex] = {
nType, rType: CSEIndex;
nDerefs: CARDINAL ← 0;
DO
nType ← NormalType[type];
WITH p: seb[nType] 
SELECT 
FROM
ref => {
rType ← UnderType[p.refType];
IF P3S.safety = checked 
AND ~(p.counted 
OR PermanentType[p.refType]) 
THEN 
Log.ErrorTree[unsafeOperation, t];
 
IF seb[NormalType[rType]].typeTag # ref THEN EXIT;
IF (nDerefs ← nDerefs+1) > 63 THEN EXIT};
 
ENDCASE;
 
[t, type] ← DeRef[t, type];
ENDLOOP;
 
RETURN [t, rType]};
 
BaseTree: 
PUBLIC 
PROC [t: Tree.Link, type: CSEIndex] 
RETURNS [val: Tree.Link] = {
PushTree[t];  PushNode[openx, 1];  SetInfo[type];  SetAttr[1, FALSE];
val ← PopTree[];  MarkShared[val, TRUE];  RETURN};
 
OpenBase: 
PUBLIC 
PROC [t: Tree.Link, hti: HTIndex] 
RETURNS [v: Tree.Link] = {
type, vType, nType: CSEIndex;
OpenRecord: 
PROC [indirect: 
BOOL] = {
WITH seb[type] 
SELECT 
FROM
record => {
v ← BaseTree[v, vType];
IF hti # HTNull THEN PushHtCtx[hti, v, indirect]
ELSE PushRecordCtx[LOOPHOLE[type, RecordSEIndex], v, indirect]};
 
ENDCASE => IF type # typeANY THEN Log.ErrorTree[typeClash, v]};
 
 
v ← Exp[t, typeANY];
type ← vType ← RType[];  nType ← NormalType[vType];  RPop[];
WITH seb[nType] 
SELECT 
FROM
definition, transfer => {
ctx: CTXIndex = InterfaceCtx[nType, v];
IF ctx = CTXNull THEN OpenRecord[FALSE]
ELSE IF hti # HTNull THEN PushHtCtx[hti, v, FALSE]
ELSE PushCtx[ctx]};
 
ref => {
[v, type] ← OpenPointer[v, vType];  vType ← OperandType[v];
OpenRecord[TRUE]};
 
ENDCASE => OpenRecord[FALSE];
 
RETURN};
 
CloseBase: 
PUBLIC 
PROC [t: Tree.Link, hti: HTIndex] = {
type: CSEIndex;
CloseRecord: PROC = {WITH seb[type] SELECT FROM record => PopCtx[]; ENDCASE};
type ← NormalType[OperandType[t]];
WITH seb[type] 
SELECT 
FROM
definition => PopCtx[];
ref => {type ← UnderType[refType]; CloseRecord[]};
ENDCASE => CloseRecord[]};