DIRECTORY
Atom: TYPE USING [MakeAtom],
Convert: TYPE USING [RopeFromCard],
CS: TYPE USING [DirectoryPart, EndsIn, PartialName],
FS: TYPE USING [Error, StreamOpen],
IO: TYPE USING [STREAM, atom, card, rope, PutF, PutRope],
Rope: TYPE USING [Concat, Equal, ROPE],
SMBcd: TYPE USING [ReadModelBcdPrefix],
SMEval: TYPE USING [],
SMFI: TYPE USING [BcdFileInfo, SrcFileInfo],
SMFIOps: TYPE USING [EvaluateUnitId, UnitToRope],
SMOps: TYPE USING [MS, NewModel],
SMTree:
TYPE Tree
USING [
ApplOp, BindOp, Handle, Id, IdInfo, Info, Link, Name, NodeName, SonId, Stamp, Text,
null, nullHandle, nullId],
SMTreeOps:
TYPE TreeOps
USING [
Map, Scan, TM,
CopyTree, Finalize, GetExt, GetName, IdentityMap, Initialize, OpName,
MakeNode, NSons, NthSon, PopTree, PushId, PushName, PushNode, PushTree,
PutExt, PutNthSon, ScanSons, SetAttr, SetExt, Zone],
SMUtil: TYPE USING [ParseStream],
TimeStamp: TYPE USING [Stamp];
SMEvalImpl:
CEDAR
PROGRAM
IMPORTS Atom, Convert, CS, FS, IO, Rope, SMBcd, SMFIOps, SMOps, SMTreeOps, SMUtil
EXPORTS SMEval ~ {
OPEN Tree~~SMTree, TreeOps~~SMTreeOps;
Env: TYPE ~ REF ScopeDescriptor;
ScopeDescriptor:
PUBLIC
TYPE ~
RECORD[
db: Tree.Handle, -- decl or binding
level: NAT,
parent: Env];
Closure: TYPE ~ REF ClosureObject;
ClosureObject:
TYPE ~
RECORD[
body: Tree.Link, -- the lambda body
e: Env]; -- its environment (no formals)
ConsEnv:
PROC[tm: TreeOps.
TM, db: Tree.Handle, parent: Env]
RETURNS[Env] ~ {
level: NAT ~ (IF parent = NIL THEN 1 ELSE parent.level+1);
RETURN[(tm.Zone).NEW[ScopeDescriptor ← [parent~parent, level~level, db~db]]]};
ConsClosure:
PROC[tm: TreeOps.
TM, body: Tree.Link, e: Env]
RETURNS[Closure] ~ {
RETURN[(tm.Zone).NEW[ClosureObject ← [body~body, e~e]]]};
note: to expedite Id handling, several procs in this module know about Tree.Node and Tree.Id internals
DBtoD:
PROC[db: Tree.Handle]
RETURNS[Tree.Handle] ~
INLINE {
RETURN[IF db.name = $decl THEN db ELSE NARROW[db.son[1]]]}; -- BindOp
DBtoG:
PROC[db: Tree.Handle]
RETURNS[g: Tree.Handle] ~ {
IF db.name
IN Tree.BindOp
THEN {
node: Tree.Handle ~ NARROW[db.son[2]];
SELECT node.name
FROM
$group => g ← node;
$let => g ← NARROW[node.son[2]];
ENDCASE => ERROR;
}
ELSE g ← Tree.nullHandle;
RETURN};
GenSym:
PROC[n:
NAT]
RETURNS[Tree.Name] ~ {
-- move to SMUtil
prefix: Rope.ROPE ~ "&";
RETURN[Atom.MakeAtom[prefix.Concat[Convert.RopeFromCard[n]]]]};
MakeIds:
PROC[tm: TreeOps.
TM, db: Tree.Handle, mark:
BOOL] ~ {
d: Tree.Handle ~ DBtoD[db];
FOR p: Tree.SonId
IN [1 .. d.sonLimit)
DO
all Id's should be allocated here to preserve sharing
TreeOps.PutExt[d.son[p],
(tm.Zone).NEW[Tree.IdInfo ← [db~db, p~p, mark1~mark, mark2~mark]]]
ENDLOOP;
};
NthId:
PROC[db: Tree.Handle, n:
NAT]
RETURNS[Tree.Id] ~ {
RETURN[NARROW[TreeOps.GetExt[DBtoD[db].son[n]]]]};
SearchD:
PROC[name: Tree.Name, d: Tree.Handle]
RETURNS[
NAT] ~ {
FOR p: Tree.SonId
IN [1 .. d.sonLimit)
DO
declElem: Tree.Handle ~ NARROW[d.son[p]];
IF declElem.son[1] = name THEN RETURN[p];
ENDLOOP;
RETURN[0]};
SearchDB:
PROC[name: Tree.Name, db: Tree.Handle]
RETURNS[Tree.Id] ~ {
d: Tree.Handle ~ DBtoD[db];
FOR p: Tree.SonId
IN [1 .. d.sonLimit)
DO
declElem: Tree.Handle ~ NARROW[d.son[p]];
IF declElem.son[1] = name THEN RETURN[NARROW[declElem.ext]];
ENDLOOP;
RETURN[Tree.nullId]};
LookUp:
PROC[name: Tree.Name, e: Env]
RETURNS[Tree.Id] ~ {
FOR scope: Env ← e, scope.parent
UNTIL scope =
NIL
DO
id: Tree.Id ~ SearchDB[name, scope.db];
IF id # Tree.nullId THEN RETURN[id];
ENDLOOP;
RETURN[Tree.nullId]};
DeclName:
PROC[declElem: Tree.Link]
RETURNS[Tree.Name] ~
INLINE {
RETURN[TreeOps.GetName[TreeOps.NthSon[declElem, 1]]]};
DeclType:
PROC[declElem: Tree.Link]
RETURNS[Tree.Link] ~
INLINE {
RETURN[TreeOps.NthSon[declElem, 2]]};
NormalizeDecl:
PROC[tm: TreeOps.
TM, d: Tree.Link, e: Env]
RETURNS[Tree.Link] ~ {
nD: NAT ← 0;
PushD: TreeOps.Scan ~ {
PushDeclElem[tm, t]; nD ← nD + 1};
TreeOps.ScanSons[d, PushD];
tm.PushNode[$decl, nD]; tm.SetAttr[1, TRUE]; tm.SetExt[e];
RETURN[tm.PopTree]};
NormalizeBinding:
PROC[tm: TreeOps.
TM, b: Tree.Link, e: Env]
RETURNS[Tree.Link] ~ {
nLB: NAT ← 0;
letB: Tree.Handle ← Tree.nullHandle;
LBindElemD:
PROC[bindElem: Tree.Link] ~ {
decl: Tree.Link ~ TreeOps.NthSon[bindElem, 1];
IF TreeOps.NSons[decl] # 1
THEN {
nLB ← nLB + 1;
tm.PushName[GenSym[nLB]];
tm.PushTree[decl]; tm.SetAttr[1, TRUE];
tm.PushNode[$declElem, 2]};
};
LBindElemV:
PROC[bindElem: Tree.Link] ~ {
decl: Tree.Link ~ TreeOps.NthSon[bindElem, 1];
IF TreeOps.NSons[decl] # 1 THEN tm.PushTree[TreeOps.NthSon[bindElem, 2]]};
nD, nLV: NAT ← 0;
BindElemD:
PROC[bindElem: Tree.Link] ~ {
decl: Tree.Link ~ TreeOps.NthSon[bindElem, 1];
PushD: TreeOps.Scan ~ {PushDeclElem[tm, t]; nD ← nD + 1};
TreeOps.ScanSons[decl, PushD]};
BindElemV:
PROC[bindElem: Tree.Link] ~ {
decl: Tree.Link ~ TreeOps.NthSon[bindElem, 1];
IF TreeOps.NSons[decl] # 1
THEN {
PushIndexed:
PROC[declElem: Tree.Link] ~ {
tm.PushId[NthId[letB, nLV]];
tm.PushName[DeclName[declElem]];
tm.PushNode[$subscript, 2]};
nLV ← nLV + 1;
TreeOps.ScanSons[decl, PushIndexed]}
ELSE tm.PushTree[TreeOps.NthSon[bindElem, 2]]};
newE: Env;
newB: Tree.Link;
TreeOps.ScanSons[b, LBindElemD];
IF nLB # 0
THEN {
tm.PushNode[$decl, nLB]; tm.SetAttr[1, FALSE];
TreeOps.ScanSons[b, LBindElemV];
tm.PushNode[$group, nLB];
letB ← NARROW[tm.MakeNode[$nBind, 2]];
MakeIds[tm, letB, FALSE]};
TreeOps.ScanSons[b, BindElemD];
tm.PushNode[$decl, nD]; tm.SetAttr[1, FALSE];
TreeOps.ScanSons[b, BindElemV];
tm.PushNode[$group, nD];
IF nLB # 0 THEN {tm.PushTree[letB]; tm.PushNode[$let, -2]};
IF TreeOps.OpName[b] = $bindRec
THEN {
newB ← tm.MakeNode[$nBindRec, 2];
newE ← ConsEnv[tm, NARROW[newB], e]}
ELSE {newB ← tm.MakeNode[$nBind, 2]; newE ← e};
TreeOps.PutExt[newB, newE];
IF letB # Tree.nullHandle THEN TreeOps.PutExt[letB, newE];
RETURN[newB]};
IsBinding:
PROC[t: Tree.Link]
RETURNS[
BOOL] ~
INLINE {
RETURN[TreeOps.OpName[t] IN Tree.BindOp]};
IsDecl:
PROC[t: Tree.Link]
RETURNS[
BOOL] ~
INLINE {
RETURN[TreeOps.OpName[t] = $decl]};
NullGroup:
PROC[t: Tree.Link]
RETURNS[
BOOL] ~
INLINE {
RETURN[TreeOps.OpName[t] = $group AND TreeOps.NSons[t] = 0]};
IdName:
PROC[id: Tree.Id]
RETURNS[Tree.Name] ~ {
RETURN[DeclName[DBtoD[id.db].son[id.p]]]};
IdDB:
PROC[id: Tree.Id]
RETURNS[Tree.Link] ~
INLINE {
RETURN[id.db]};
IdP:
PROC[id: Tree.Id]
RETURNS[
NAT] ~
INLINE {
RETURN[id.p]};
BindDecl:
PROC[m: SMOps.
MS, elem: Tree.Link, e: Env] ~ {
t: Tree.Link ~ DeclType[elem];
v: Tree.Link;
IF TreeOps.OpName[t] = $type
AND TreeOps.NthSon[t, 1] = Tree.null
THEN {
(m.tm).PushName[DeclName[elem]];
v ← (m.tm).MakeNode[$type, 1]}
ELSE v ← Eval[m, t, e];
TreeOps.PutNthSon[elem, 2, v]};
EvalId:
PROC[m: SMOps.
MS, id: Tree.Id] ~ {
IF ~id.mark2
THEN {
IF ~id.mark1
THEN {
d: Tree.Handle ~ DBtoD[id.db];
idE: Env ~ NARROW[TreeOps.GetExt[id.db]];
id.mark1 ← TRUE;
BindDecl[m, d.son[id.p], idE];
IF id.db.name
IN Tree.BindOp
THEN {
g: Tree.Handle ~ DBtoG[id.db];
g.son[id.p] ← Eval[m, g.son[id.p], idE]};
id.mark2 ← TRUE}
ELSE NULL}; -- check for legal circularity
};
EvalDecl:
PROC[m: SMOps.
MS, decl: Tree.Link, e: Env] ~ {
d: Tree.Handle ~ NARROW[decl];
FOR p: Tree.SonId
IN [1 .. d.sonLimit)
DO
BindDecl[m, d.son[p], e];
ENDLOOP;
EvalBinding:
PROC[m: SMOps.
MS, binding: Tree.Link, e: Env] ~ {
b: Tree.Handle ~ NARROW[binding];
g: Tree.Handle ~ DBtoG[b];
EvalDecl[m, DBtoD[b], e];
FOR p: Tree.SonId
IN [1 .. g.sonLimit)
DO
g.son[p] ← Eval[m, g.son[p], e];
ENDLOOP;
CheckBinding[m, b]};
EvalRecBinding:
PROC[m: SMOps.
MS, binding: Tree.Link, e: Env] ~ {
b: Tree.Handle ~ NARROW[binding];
d: Tree.Handle ~ DBtoD[b];
FOR p: Tree.SonId
IN [1 .. d.sonLimit)
DO
EvalId[m, NARROW[TreeOps.GetExt[d.son[p]]]];
ENDLOOP;
CheckBinding[m, b]};
CheckBinding:
PROC[m: SMOps.
MS, b: Tree.Handle] ~ {
d: Tree.Handle ~ DBtoD[b];
v: Tree.Handle ← NARROW[b.son[2]];
WHILE v.name = $let
DO
CheckBinding[m, NARROW[v.son[1]]];
v ← NARROW[v.son[2]]
ENDLOOP;
FOR p: Tree.SonId
IN [1 .. v.sonLimit)
DO
t: Tree.Link ~ DeclType[d.son[p]];
IF t # Tree.null
AND ~CheckArgType[m, ValOf[m, v.son[p]], t]
THEN {
ErrorLoc[m, $warning];
m.out.PutF[
"Value of %g incompatible with its declared type\n", IO.atom[DeclName[d.son[p]]]]
}
ENDLOOP;
};
Appl:
PROC[m: SMOps.
MS, node: Tree.Handle, e: Env, default:
BOOL]
RETURNS[v: Tree.Link] ~ {
rator: Tree.Link ~ Eval[m, TreeOps.NthSon[node, 1], e];
rands: Tree.Link ~
CheckArgs[m, Eval[m, TreeOps.NthSon[node, 2], e], Domain[Type[m, rator]], e, default];
IF IsLambda[m, rator]
AND ~m.errors
THEN
v ← BetaReduce[m~m, rator~rator, rands~rands, e~e]
ELSE {
(m.tm).PushTree[rator]; (m.tm).PushTree[rands];
v ← (m.tm).MakeNode[$apply, 2]; TreeOps.PutExt[v, node]};
RETURN};
CheckArgs:
PROC[m: SMOps.
MS, args: Tree.Link, decl: Tree.Link, e: Env, default:
BOOL]
RETURNS[Tree.Link] ~ {
tm: TreeOps.TM ~ m.tm;
IF decl = Tree.null THEN tm.PushTree[args]
ELSE {
d: Tree.Handle ~ NARROW[decl];
nD: NAT ~ TreeOps.NSons[decl];
CheckLength:
PROC[n:
NAT] ~ {
IF n # nD
THEN {
ErrorLoc[m];
m.out.PutF["%d too %g elements in argument list\n",
IO.card[(nD-n).ABS], IO.rope[IF n > nD THEN "many" ELSE "few"]];
};
};
SELECT TreeOps.OpName[args]
FROM
IN Tree.BindOp => {
bD: Tree.Handle ~ DBtoD[NARROW[args]];
bG: Tree.Handle ~ DBtoG[NARROW[args]];
nG: NAT ~ TreeOps.NSons[bG];
IF ~default THEN CheckLength[nG];
FOR i: Tree.SonId
IN [1 .. nD]
DO
name: Tree.Name ~ DeclName[d.son[i]];
p: NAT ~ SearchD[name, bD];
v: Tree.Link ~ (
SELECT
TRUE
FROM
p # 0 => bG.son[p],
default => BindDefault[m, name, e],
ENDCASE => Tree.null);
IF v = Tree.null
THEN {
ErrorLoc[m];
m.out.PutF["No %g for %g in argument list\n",
IO.rope[IF default THEN "available default" ELSE "binding"], IO.atom[name]]}
ELSE
IF ~CheckArgType[m, v, DeclType[d.son[i]]]
THEN {
ErrorLoc[m];
m.out.PutF["%g for %g has wrong type\n",
IO.rope[IF p # 0 THEN "Actual" ELSE "Default"], IO.atom[name]]
};
tm.PushTree[v];
ENDLOOP;
tm.PushNode[$group, nD]};
$group => {
g: Tree.Handle ~ NARROW[args];
IF ~default THEN CheckLength[TreeOps.NSons[args]];
FOR i: Tree.SonId
IN [1 .. nD]
DO
type: Tree.Link ~ ValOf[m, DeclType[d.son[i]]];
v: Tree.Link;
n: NAT;
[v, n] ← MatchByType[m, g, type];
IF n = 0
AND default
THEN {
take care of possible binding-to-binding coercion directly
name: Tree.Name ~ DeclName[d.son[i]];
v ← BindDefault[m, name, e];
IF v # Tree.null
THEN {
IF ~HasType[m, v, type]
THEN {
ErrorLoc[m]; m.out.PutF["Default for %g has wrong type\n", IO.atom[name]]};
n ← 1};
};
IF n # 1
THEN {
ErrorLoc[m];
m.out.PutF["%d values have type compatible with %g in argument list\n",
IO.card[n], IO.atom[DeclName[d.son[i]]]];
};
tm.PushTree[v];
ENDLOOP;
FOR i: Tree.SonId
IN [1 .. g.sonLimit)
DO
IF g.son[i] # Tree.null
THEN {
ErrorLoc[m];
m.out.PutF["Actual %d is not type compatible with any formal\n", IO.card[i]]};
ENDLOOP;
tm.PushNode[$group, nD]};
ENDCASE => {
ErrorLoc[m]; m.out.PutRope["Group or binding required in appl\n"];
tm.PushTree[args]};
};
RETURN[tm.PopTree]};
BindDefault:
PROC[m: SMOps.
MS, name: Tree.Name, e: Env]
RETURNS[Tree.Link] ~ {
v: Tree.Link ~ LookUp[name, e];
RETURN[IF v # Tree.nullId THEN v ELSE Tree.null]};
MatchByType:
PROC[m: SMOps.
MS, g: Tree.Handle, type: Tree.Link]
RETURNS[v: Tree.Link, matches: NAT ← 0] ~ {
FOR i: Tree.SonId
IN [1 .. g.sonLimit)
DO
IF g.son[i] # Tree.null
AND HasType[m, g.son[i], type]
THEN {
IF matches = 0 THEN v ← g.son[i];
matches ← matches + 1; g.son[i] ← Tree.null};
ENDLOOP;
RETURN};
CheckArgType:
PROC[m: SMOps.
MS, arg, type: Tree.Link]
RETURNS[
BOOL] ~
INLINE {
RETURN[HasType[m, arg, ValOf[m, type]]]};
HasType:
PROC[m: SMOps.
MS, v: Tree.Link, type: Tree.Link]
RETURNS[
BOOL] ~ {
vType: Tree.Link ~ ValOf[m, Type[m, v]];
RETURN[
(
SELECT TreeOps.OpName[type]
FROM
$typeSTRING =>
(TreeOps.OpName[vType] = $typeSTRING OR ISTYPE[ValOf[m, v], Tree.Text]),
$decl => ImpliesAll[m, v, vType, type],
ENDCASE => CoercesTo[m, v, vType, type]
TreeOps.OpName[ValOf[m, v]] = $nil]
};
BindCross2:
PROC[m: SMOps.
MS, type, t: Tree.Link]
RETURNS[Tree.Link] ~ {
tm: TreeOps.TM ~ m.tm;
decl: Tree.Link ~ TreeOps.NthSon[type, 1];
index: Tree.Name ~ IdName[NthId[NARROW[decl], 1]];
protoRange: Closure ~ NARROW[TreeOps.GetExt[type]];
b: Tree.Handle;
e: Env;
tm.PushTree[tm.CopyTree[decl, TreeOps.IdentityMap]]; tm.SetAttr[1, FALSE];
tm.PushTree[t]; tm.PushName[index]; tm.PushNode[$subscript, 2];
tm.PushNode[$group, 1];
b ← NARROW[tm.MakeNode[$nBind, 2]];
e ← ConsEnv[tm, b, protoRange.e]; TreeOps.PutExt[b, e];
MakeIds[tm, b, TRUE];
RETURN[Eval[m, protoRange.body, e]]};
OneImplies:
PROC[m: SMOps.
MS, type1, type2: Tree.Link]
RETURNS[
BOOL] ~ {
SELECT TreeOps.OpName[type1]
FROM
$decl => {
FOR i: Tree.SonId
IN [1 .. TreeOps.NSons[type1]]
DO
subType: Tree.Link ~ ValOf[m, DeclType[TreeOps.NthSon[type1, i]]];
IF Implies[m, subType, type2] THEN RETURN[TRUE]
ENDLOOP;
RETURN[FALSE]}
ENDCASE => RETURN[FALSE]