<> <> <> 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; <> ErrorLoc: PROC[m: SMOps.MS, severity: {warning, error}_$error] ~ { IF severity = $error THEN m.errors _ TRUE; m.out.PutF["%g at %d: ", IO.rope[IF severity=$warning THEN "Warning" ELSE "Error"], IO.card[m.loc]]; }; <> 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]]]}; <> 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 <> 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]]}; PushDeclElem: PROC[tm: TreeOps.TM, declElem: Tree.Link] ~ { <> tm.PushName[DeclName[declElem]]; tm.PushTree[DeclType[declElem]]; tm.PushNode[$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; }; <> ValOf: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[Tree.Link] ~ { WITH t SELECT FROM id: Tree.Id => { IF ~id.mark2 THEN EvalId[m, id]; RETURN[IF id.mark2 AND id.db.name IN Tree.BindOp THEN ValOf[m, DBtoG[id.db].son[id.p]] ELSE id] }; node: Tree.Handle => RETURN[IF TreeOps.OpName[node] = $subscript THEN Select[m, node] ELSE t]; ENDCASE => RETURN[t] }; Select: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[Tree.Link] ~ { name: Tree.Name ~ TreeOps.GetName[TreeOps.NthSon[t, 2]]; SubIndex: PROC[b: Tree.Link] RETURNS[v: Tree.Link] ~ { bv: Tree.Link ~ ValOf[m, b]; SELECT TRUE FROM IsBinding[bv] => { b: Tree.Handle ~ NARROW[bv]; p: NAT ~ SearchD[name, DBtoD[b]]; v _ (IF p # 0 THEN TreeOps.NthSon[DBtoG[b], p] ELSE t)}; TreeOps.OpName[bv] = $let => { s: Tree.Link ~ SubIndex[TreeOps.NthSon[bv, 2]]; v _ (IF s # t THEN s ELSE t)}; ENDCASE => v _ t; RETURN}; RETURN[SubIndex[TreeOps.NthSon[t, 1]]]}; <> 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 { <> 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[ Eq[vType, type] OR (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] ) OR TreeOps.OpName[ValOf[m, v]] = $nil] }; <> IsLambda: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[BOOL] ~ { RETURN[SELECT TreeOps.OpName[ValOf[m, t]] FROM $lambda => TRUE, $let => IsLambda[m, TreeOps.NthSon[t, 2]], ENDCASE => FALSE] }; BetaReduce: PROC[m: SMOps.MS, rator, rands: Tree.Link, e: Env] RETURNS[Tree.Link] ~ { tm: TreeOps.TM ~ m.tm; operator: Tree.Link ~ ValOf[m, rator]; SELECT TreeOps.OpName[operator] FROM $lambda => { decl: Tree.Link ~ ValOf[m, TreeOps.NthSon[operator, 1]]; closure: Closure ~ NARROW[TreeOps.GetExt[operator]]; b: Tree.Handle; tm.PushTree[tm.CopyTree[decl, TreeOps.IdentityMap]]; tm.SetAttr[1, FALSE]; tm.PushTree[rands]; b _ NARROW[tm.MakeNode[$nBind, 2]]; TreeOps.PutExt[b, e]; MakeIds[tm, b, TRUE]; tm.PushTree[b]; tm.PushTree[Eval[m, closure.body, ConsEnv[tm, b, closure.e]]]}; $let => { tm.PushTree[TreeOps.NthSon[operator, 1]]; tm.PushTree[BetaReduce[m, TreeOps.NthSon[operator, 2], rands, e]]}; ENDCASE => ERROR; RETURN[tm.MakeNode[$let, 2]]}; <> ToInterface: PROC[m: SMOps.MS, type: Tree.Link] RETURNS[Tree.Link] ~ { t: Tree.Link ~ ValOf[m, type]; SELECT TreeOps.OpName[t] FROM $cross => { s: Tree.Link ~ ValOf[m, TreeOps.NthSon[t, 1]]; RETURN[SELECT TreeOps.OpName[s] FROM IN Tree.ApplOp, $stamp => s, $cross => ToInterface[m, TreeOps.NthSon[s, 1]], ENDCASE => t] }; ENDCASE; RETURN[t]}; <> Eval: PUBLIC PROC[m: SMOps.MS, t: Tree.Link, e: Env] RETURNS[v: Tree.Link] ~ { WITH t SELECT FROM name: Tree.Name => { v _ LookUp[name, e]; IF v = Tree.nullId THEN { ErrorLoc[m]; m.out.PutF["%g is undeclared\n", IO.atom[name]]}; }; id: Tree.Id => v _ id; node: Tree.Handle => { tm: TreeOps.TM ~ m.tm; BindSon: TreeOps.Map~{RETURN[Eval[m, t, e]]}; saveLoc: Tree.Info ~ m.loc; m.loc _ node.info; SELECT node.name FROM -- can't use OpName (see $locator) $lambda => { -- type of result is NOT currently checked decl: Tree.Link ~ EvalToDecl[m, TreeOps.NthSon[node, 1], e]; tm.PushTree[decl]; IF IsDecl[decl] THEN { -- require literal decl for now d: Tree.Handle ~ NARROW[decl]; newE: Env; MakeIds[tm, d, TRUE]; newE _ ConsEnv[tm, d, e]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], newE]]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 3], newE]]} ELSE { ErrorLoc[m]; m.out.PutRope["decl required in LAMBDA\n"]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], e]]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 3], e]]}; v _ tm.MakeNode[$lambda, 3]; TreeOps.PutExt[v, ConsClosure[tm, TreeOps.NthSon[node, 3], e]]}; $let => { binding: Tree.Link ~ Eval[m, TreeOps.NthSon[node, 1], e]; IF NullGroup[binding] THEN { tm.PushNode[$bind, 0]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], e]]} ELSE IF IsBinding[binding] THEN { -- require literal binding b: Tree.Handle ~ NARROW[binding]; tm.PushTree[binding]; IF TreeOps.OpName[b] # $nBindRec THEN MakeIds[tm, b, TRUE]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], ConsEnv[tm, b, e]]]} ELSE { ErrorLoc[m]; m.out.PutRope["binding required in LET\n"]; tm.PushTree[binding]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], e]]}; v _ tm.MakeNode[$let, 2]}; $arrow, $arrow2 => { decl: Tree.Link ~ EvalToDecl[m, TreeOps.NthSon[node, 1], e]; tm.PushTree[decl]; IF IsDecl[decl] THEN { -- require literal decl for now d: Tree.Handle ~ NARROW[decl]; MakeIds[tm, d, TRUE]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], ConsEnv[tm, d, e]]]} ELSE { ErrorLoc[m]; m.out.PutRope["decl required in ->\n"]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], e]]}; v _ tm.MakeNode[node.name, 2]; IF node.name = $arrow2 THEN TreeOps.PutExt[v, ConsClosure[tm, TreeOps.NthSon[node, 2], e]]; }; $apply, $applyDefault => v _ Appl[m, node, e, node.name=$applyDefault]; $subscript => { binding: Tree.Link ~ Eval[m, TreeOps.NthSon[node, 1], e]; bv: Tree.Link ~ ValOf[m, binding]; name: Tree.Name ~ TreeOps.GetName[TreeOps.NthSon[node, 2]]; -- treat as quoted IF IsBinding[bv] THEN { b: Tree.Handle ~ NARROW[bv]; p: NAT ~ SearchD[name, DBtoD[b]]; IF p = 0 THEN { ErrorLoc[m]; m.out.PutF["Invalid field %g\n", IO.atom[name]]}; }; tm.PushTree[binding]; tm.PushName[name]; v _ tm.MakeNode[$subscript, 2]}; $union, $then => { rand1: Tree.Link ~ Eval[m, TreeOps.NthSon[node, 1], e]; rand2: Tree.Link ~ Eval[m, TreeOps.NthSon[node, 2], e]; IF ~HasType[m, rand2, ToInterface[m, Type[m, rand1]]] THEN { ErrorLoc[m]; m.out.PutF["Invalid argument type for + or THEN\n"]}; tm.PushTree[rand1]; tm.PushTree[rand2]; v _ tm.MakeNode[node.name, 2]}; $group => v _ tm.CopyTree[t, BindSon]; $decl => { IF node.attrs[1] THEN { -- not part of a binding v _ NormalizeDecl[tm, t, e]; EvalDecl[m, v, e]} ELSE NULL}; -- part of binding, scope already created and included in e $bind => { v _ NormalizeBinding[tm, t, e]; EvalBinding[m, v, e]}; $bindRec => { b: Tree.Handle; v _ NormalizeBinding[tm, t, e]; b _ NARROW[v]; MakeIds[tm, b, FALSE]; EvalRecBinding[m, v, NARROW[TreeOps.GetExt[v]]]}; $type => { qn: Tree.Link ~ TreeOps.NthSon[node, 1]; IF qn = Tree.null THEN tm.PushTree[qn] ELSE tm.PushName[TreeOps.GetName[qn]]; v _ tm.MakeNode[$type, 1]}; $env => {v _ tm.MakeNode[$env, 0]; TreeOps.PutExt[v, e]}; $nil, $control => v _ tm.MakeNode[node.name, 0]; $unitId => v _ BindFile[m, t]; $typeSTRING, $stamp, $cross => v _ tm.CopyTree[t, BindSon]; $cross2 => { decl: Tree.Link ~ EvalToDecl[m, TreeOps.NthSon[node, 1], e]; tm.PushTree[decl]; IF IsDecl[decl] THEN { d: Tree.Handle ~ NARROW[decl]; MakeIds[tm, d, TRUE]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], ConsEnv[tm, d, e]]]} ELSE { -- require literal decl for now ErrorLoc[m]; m.out.PutRope["decl required in xx\n"]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], e]]}; v _ tm.MakeNode[$cross2, 2]; TreeOps.PutExt[v, ConsClosure[tm, TreeOps.NthSon[node, 2], e]]}; $locator => v _ Eval[m, node.son[1], e]; ENDCASE => { ErrorLoc[m]; m.out.PutF["Unimplemented construct\n"]; v _ tm.CopyTree[t, BindSon]}; m.loc _ saveLoc}; ENDCASE => v _ t; RETURN}; EvalToDecl: PROC[m: SMOps.MS, t: Tree.Link, e: Env] RETURNS[Tree.Link] ~ { v: Tree.Link ~ Eval[m, t, e]; RETURN[IF NullGroup[v] THEN (m.tm).MakeNode[$decl, 0] ELSE v]}; <> BindFile: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[v: Tree.Link] ~ { shortName: Rope.ROPE ~ NARROW[TreeOps.NthSon[t, 3]]; SELECT TRUE FROM CS.EndsIn[shortName, ".mesa"], CS.EndsIn[shortName, ".bcd"] => { tree: Tree.Link ~ SMFIOps.EvaluateUnitId[m, t]; v _ Eval[m, tree, NIL]; -- currently a no-op IF Type[m, v] = Tree.null THEN { ErrorLoc[m]; m.out.PutF["%g has unknown type\n", IO.rope[SMFIOps.UnitToRope[t]]]}; }; CS.EndsIn[shortName, ".model"], CS.EndsIn[shortName, ".modelBcd"] => { unitId: Rope.ROPE ~ SMFIOps.UnitToRope[t]; subModel: Tree.Link _ NARROW[TreeOps.GetExt[t]]; IF subModel = Tree.null THEN { source: IO.STREAM _ NIL; source _ FS.StreamOpen[fileName~unitId, wDir~m.wDir ! FS.Error => {CONTINUE}]; IF source = NIL THEN { ErrorLoc[m]; m.out.PutF["%g could not be opened\n", IO.rope[unitId]]; v _ t} ELSE { newM: SMOps.MS ~ SMOps.NewModel[in~m.in, out~m.out, msgout~m.msgOut]; newM.wDir _ (IF CS.PartialName[unitId] THEN m.wDir ELSE CS.DirectoryPart[unitId]); (newM.tm).Initialize; IF CS.EndsIn[unitId, ".modelBcd"] THEN -- advance input stream SMBcd.ReadModelBcdPrefix[newM, source]; subModel _ SMUtil.ParseStream[newM, source]; TreeOps.PutExt[t, subModel]; v _ Eval[newM, subModel, NIL]; IF newM.errors THEN m.errors _ TRUE; (newM.tm).Finalize} } ELSE v _ Eval[m, subModel, NIL]}; ENDCASE => v _ t }; <> Type: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[type: Tree.Link] ~ { WITH t SELECT FROM id: Tree.Id => { d: Tree.Handle; IF ~id.mark2 THEN EvalId[m, id]; d _ DBtoD[id.db]; type _ DeclType[d.son[id.p]]; IF type = Tree.null THEN { v: Tree.Link ~ ValOf[m, id]; IF v # id THEN type _ Type[m, v]}; }; node: Tree.Handle => { type _ node.type; IF type = Tree.null THEN { SELECT TreeOps.OpName[node] FROM $lambda => { (m.tm).PushTree[TreeOps.NthSon[node, 1]]; (m.tm).PushTree[TreeOps.NthSon[node, 2]]; type _ (m.tm).MakeNode[$arrow, 2]}; $let => type _ Type[m, TreeOps.NthSon[node, 2]]; IN Tree.ApplOp => { rator: Tree.Link ~ TreeOps.NthSon[node, 1]; type _ Range[m, Type[m, rator], node]}; $subscript => { s: Tree.Link ~ Select[m, t]; IF s # t THEN type _ Type[m, s] ELSE { subType: Tree.Link ~ Type[m, TreeOps.NthSon[t, 1]]; index: Tree.Name ~ TreeOps.GetName[TreeOps.NthSon[t, 2]]; IF IsDecl[subType] THEN { d: Tree.Handle ~ NARROW[subType]; p: NAT ~ SearchD[index, d]; type _ (IF p#0 THEN DeclType[d.son[p]] ELSE Tree.null)} ELSE { (m.tm).PushTree[subType]; (m.tm).PushName[index]; type _ Select[m, (m.tm).MakeNode[$subscript, 2]]}; }; }; $union, $then => type _ ToInterface[m, Type[m, TreeOps.NthSon[t, 1]]]; $decl => type _ (m.tm).MakeNode[$typeDECL, 0]; IN Tree.BindOp => type _ DBtoD[node]; ENDCASE => type _ Tree.null; node.type _ type}; }; fiSrc: SMFI.SrcFileInfo => type _ fiSrc.type; fiBcd: SMFI.BcdFileInfo => type _ fiBcd.type; ENDCASE => type _ Tree.null; RETURN}; <> Domain: PROC[t: Tree.Link] RETURNS[Tree.Link] ~ { RETURN[SELECT TreeOps.OpName[t] FROM $arrow, $arrow2 => TreeOps.NthSon[t, 1], ENDCASE => Tree.null] }; Range: PROC[m: SMOps.MS, type, t: Tree.Link] RETURNS[range: Tree.Link] ~ { SELECT TreeOps.OpName[type] FROM $arrow => range _ TreeOps.NthSon[type, 2]; $arrow2 => { tm: TreeOps.TM ~ m.tm; decl: Tree.Link ~ TreeOps.NthSon[type, 1]; rands: Tree.Link ~ ValOf[m, TreeOps.NthSon[t, 2]]; protoRange: Closure ~ NARROW[TreeOps.GetExt[type]]; b: Tree.Handle; e: Env; tm.PushTree[tm.CopyTree[decl, TreeOps.IdentityMap]]; tm.SetAttr[1, FALSE]; tm.PushTree[rands]; b _ NARROW[tm.MakeNode[$nBind, 2]]; e _ ConsEnv[tm, b, protoRange.e]; TreeOps.PutExt[b, e]; MakeIds[tm, b, TRUE]; range _ Eval[m, protoRange.body, e]}; ENDCASE => range _ Tree.null; RETURN}; <> ValOfNthSon: PROC[m: SMOps.MS, t: Tree.Link, n: Tree.SonId] RETURNS[Tree.Link] ~ { RETURN[ValOf[m, TreeOps.NthSon[t, n]]]}; Eq: PROC[type1, type2: Tree.Link] RETURNS[BOOL] ~ INLINE { RETURN[type1 = type2]}; Equiv: PUBLIC PROC[m: SMOps.MS, type1, type2: Tree.Link] RETURNS[BOOL] ~ { RETURN[Equal[m, type1, type2 ! MatchedDB => {RESUME[FALSE]}]]}; MatchedDB: SIGNAL[d1, d2: Tree.Link] RETURNS[BOOL] ~ CODE; Equal: PROC[m: SMOps.MS, type1, type2: Tree.Link] RETURNS[BOOL] ~ { OpPair: TYPE = RECORD[op1, op2: Tree.NodeName]; RETURN[ Eq[type1, type2] OR (SELECT OpPair[TreeOps.OpName[type1], TreeOps.OpName[type2]] FROM [$arrow, $arrow] => EquivDecls[m, ValOfNthSon[m, type1, 1], ValOfNthSon[m, type2, 1]] AND Equal[m, ValOfNthSon[m, type1, 2], ValOfNthSon[m, type2, 2]], [$arrow2, $arrow2] => EquivDecls[m, ValOfNthSon[m, type1, 1], ValOfNthSon[m, type2, 1]] AND Equal[m, ValOfNthSon[m, type1, 2], ValOfNthSon[m, type2, 2] ! MatchedDB => { IF d1 = ValOfNthSon[m, type1, 1] AND d2 = ValOfNthSon[m, type2, 1] THEN RESUME[TRUE]} ], [$cross, $cross] => Equal[m, ValOfNthSon[m, type1, 1], ValOfNthSon[m, type2, 1]] AND Equal[m, ValOfNthSon[m, type1, 2], ValOfNthSon[m, type2, 2]], [$cross2, $cross2] => EquivDecls[m, ValOfNthSon[m, type1, 1], ValOfNthSon[m, type2, 1]] AND Equal[m, ValOfNthSon[m, type1, 2], ValOfNthSon[m, type2, 2] ! MatchedDB => { IF d1 = ValOfNthSon[m, type1, 1] AND d2 = ValOfNthSon[m, type2, 1] THEN RESUME[TRUE]} ], [$type, $type] => EquivTypes[m, type1, type2], [$typeSTRING, $typeSTRING] => TRUE, [$apply, $apply] => EquivAppls[m, type1, type2], [$apply, $stamp] => Equal[m, type2, type1], [$stamp, $stamp] => EquivStamps[m, type1, type2], [$stamp, $apply] => TRUE, -- temporary loophole, to avoid stamp computation here [$stamp, $none] => Equal[m, type2, type1], [$subscript, $subscript] => TreeOps.GetName[TreeOps.NthSon[type1, 2]] = TreeOps.GetName[TreeOps.NthSon[type2, 2]] AND Equal[m, ValOfNthSon[m, type1, 1], ValOfNthSon[m, type2, 1]], [$control, $control] => TRUE, [$nil, $nil] => TRUE, ENDCASE => WITH type1 SELECT FROM id1: Tree.Id => WITH type2 SELECT FROM id2: Tree.Id => (IdP[id1] = IdP[id2]) AND (SIGNAL MatchedDB[IdDB[id1], IdDB[id2]]), ENDCASE => FALSE, fiBcd: SMFI.BcdFileInfo => SELECT TreeOps.OpName[type2] FROM $stamp => GetStamp[TreeOps.NthSon[type2, 1]]^ = fiBcd.stamp, ENDCASE => FALSE, ENDCASE => FALSE )] }; Implies: PUBLIC PROC[m: SMOps.MS, type1, type2: Tree.Link] RETURNS[BOOL] ~ { RETURN[ Eq[type1, type2] OR Equal[m, type1, type2 ! MatchedDB => {RESUME[FALSE]}] OR (SELECT TreeOps.OpName[type1] FROM $type => (SELECT TreeOps.OpName[type2] FROM $type => TreeOps.NthSon[type2, 1] = Tree.null, ENDCASE => FALSE), ENDCASE => FALSE)] }; CoercesTo: PROC[m: SMOps.MS, v1, type1, type2: Tree.Link] RETURNS[BOOL] ~ { RETURN[ Implies[m, type1, type2] OR (SELECT TreeOps.OpName[type1] FROM $decl => OneImplies[m, type1, type2], $cross => CoercesTo[m, v1, ValOfNthSon[m, type1, 1], type2] OR CoercesTo[m, v1, ValOfNthSon[m, type1, 2], type2], $cross2 => TreeOps.OpName[type2] = $control OR OneImplies[m, TreeOps.NthSon[type1, 1], type2] OR CoercesTo[m, v1, ValOf[m, BindCross2[m, type1, v1]], type2], -- v1 not right here ENDCASE => FALSE)] }; 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] }; ImpliesAll: PROC[m: SMOps.MS, v1, type1, type2: Tree.Link] RETURNS[BOOL] ~ { SELECT TreeOps.OpName[type2] FROM $decl => { FOR i: Tree.SonId IN [1 .. TreeOps.NSons[type2]] DO subType: Tree.Link ~ ValOf[m, DeclType[TreeOps.NthSon[type2, i]]]; IF ~CoercesTo[m, v1, type1, subType] THEN RETURN[FALSE] ENDLOOP; RETURN[TRUE]} ENDCASE => RETURN[FALSE] }; EquivTypes: PROC[m: SMOps.MS, t1, t2: Tree.Link] RETURNS[BOOL] ~ { name1: Tree.Link ~ TreeOps.NthSon[t1, 1]; name2: Tree.Link ~ TreeOps.NthSon[t2, 1]; RETURN[ ((name1 = Tree.null) = (name2 = Tree.null)) AND (name1 = Tree.null OR TreeOps.GetName[name1] = TreeOps.GetName[name2])] }; EquivDecls: PROC[m: SMOps.MS, decl1, decl2: Tree.Link] RETURNS[BOOL] ~ { n: NAT ~ TreeOps.NSons[decl1]; IF n # TreeOps.NSons[decl2] THEN RETURN[FALSE]; FOR i: Tree.SonId IN [1 .. n] DO elem1: Tree.Link ~ ValOfNthSon[m, decl1, i]; elem2: Tree.Link ~ ValOfNthSon[m, decl2, i]; IF --(TreeOps.NthSon[elem1, 1] # TreeOps.NthSon[elem2, 1]) <> ~Equal[m, ValOfNthSon[m, elem1, 2], ValOfNthSon[m, elem2, 2]] THEN RETURN[FALSE] ENDLOOP; RETURN[TRUE]}; EquivAppls: PROC[m: SMOps.MS, t1, t2: Tree.Link] RETURNS[BOOL] ~ { rator1: Tree.Link ~ ValOfNthSon[m, t1, 1]; rator2: Tree.Link ~ ValOfNthSon[m, t2, 1]; RETURN[WITH rator1 SELECT FROM fiSrc1: SMFI.SrcFileInfo => (WITH rator2 SELECT FROM fiSrc2: SMFI.SrcFileInfo => fiSrc1.create = fiSrc2.create AND EquivArgs[m, ValOfNthSon[m, t1, 2], ValOfNthSon[m, t2, 2]], ENDCASE => FALSE), fiBcd1: SMFI.BcdFileInfo => (WITH rator2 SELECT FROM fiBcd2: SMFI.BcdFileInfo => fiBcd1.stamp = fiBcd2.stamp, ENDCASE => FALSE), node1: Tree.Handle => (WITH rator2 SELECT FROM node2: Tree.Handle => TreeOps.OpName[node1] IN Tree.ApplOp AND TreeOps.OpName[node2] IN Tree.ApplOp AND EquivAppls[m, rator1, rator2], ENDCASE => FALSE), ENDCASE => FALSE] }; EquivArgs: PROC[m: SMOps.MS, args1, args2: Tree.Link] RETURNS[BOOL] ~ { IF TreeOps.OpName[args1] # $group OR TreeOps.OpName[args2] # $group THEN RETURN[FALSE]; IF TreeOps.NSons[args1] # TreeOps.NSons[args2] THEN RETURN[FALSE]; FOR i: Tree.SonId IN [1 .. TreeOps.NSons[args1]] DO arg1: Tree.Link ~ ValOfNthSon[m, args1, i]; arg2: Tree.Link ~ ValOfNthSon[m, args2, i]; WITH arg1 SELECT FROM node: Tree.Handle => IF TreeOps.OpName[arg1] NOT IN Tree.ApplOp OR TreeOps.OpName[arg2] NOT IN Tree.ApplOp OR ~EquivAppls[m, arg1, arg2] THEN RETURN[FALSE]; switches1: Tree.Text => WITH arg2 SELECT FROM switches2: Tree.Text => IF ~switches1.Equal[switches2, FALSE] THEN RETURN[FALSE]; ENDCASE => RETURN[FALSE]; ENDCASE => RETURN[FALSE] ENDLOOP; RETURN[TRUE] }; GetStamp: PROC[t: Tree.Link] RETURNS[Tree.Stamp] ~ INLINE { RETURN[NARROW[t]]}; EquivStamps: PROC[m: SMOps.MS, t1, t2: Tree.Link] RETURNS[BOOL] ~ { RETURN[GetStamp[TreeOps.NthSon[t1, 1]]^ = GetStamp[TreeOps.NthSon[t2, 1]]^]}; }.