-- file SMEvalImpl.mesa -- last edited by Satterthwaite, August 11, 1983 1:17 pm DIRECTORY Atom: TYPE USING [MakeAtom], CS: TYPE USING [RopeFromCard], FileIO: TYPE USING [Open, OpenFailed], IO: TYPE USING [STREAM, atom, card, PutF, rope], Rope: TYPE USING [ROPE, Cat, Equal, Length], SMBcd: TYPE USING [ReadModelBcdPrefix], SMEval: TYPE USING [], SMFI: TYPE USING [BcdFileInfo, SrcFileInfo], SMFIOps: TYPE USING [EvaluateUnitId], SMOps: TYPE USING [MS, NewModel], SMUtil: TYPE USING [ParseStream], SMTree: TYPE Tree USING [ ApplOp, BindOp, Handle, Id, IdInfo, Info, Link, Name, NodeName, Text, null, nullHandle, nullId, nullName], SMTreeOps: TYPE --TreeOps-- USING [ Map, Scan, TM, CopyTree, Finalize, GetExt, GetName, IdentityMap, Initialize, MakeNode, NSons, NthSon, OpName, PopTree, PushId, PushName, PushNode, PushTree, PutExt, PutNthSon, ScanSons, SetAttr, SetExt, Zone]; SMEvalImpl: CEDAR PROGRAM IMPORTS Atom, CS, FileIO, IO, Rope, SMBcd, SMFIOps, SMOps, SMUtil, SMTreeOps EXPORTS SMEval ~ { OPEN Tree~~SMTree, TreeOps~~SMTreeOps; -- file utilities UnitToRope: PUBLIC PROC[unitId: Tree.Link] RETURNS[r: Rope.ROPE ← ""] ~ { AppendPart: PROC[t: Tree.Link] ~ { WITH t SELECT FROM part: Tree.Text => { IF r.Length # 0 THEN r ← r.Cat["."]; r ← r.Cat[part]}; node: Tree.Handle => SELECT TreeOps.OpName[node] FROM $uiList => TreeOps.ScanSons[node, AppendPart]; ENDCASE; ENDCASE}; TreeOps.ScanSons[unitId, AppendPart]; RETURN}; -- error handling (move to a new module) ErrorLoc: PROC[m: SMOps.MS, severity: {warning, error}←$error] ~ { IF severity = $error THEN m.errors ← TRUE; m.out.PutF["%s at %d: ", IO.rope[IF severity=$warning THEN "Warning" ELSE "Error"], IO.card[m.loc]]; }; -- the main evaluator 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["%s 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 { 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 { -- require literal decl for now ErrorLoc[m]; m.out.PutF["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.PutF["binding required in LET\n"]; tm.PushTree[binding]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], e]]}; v ← tm.MakeNode[$let, 2]}; $arrow => { 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.PutF["decl required in ->\n"]; tm.PushTree[Eval[m, TreeOps.NthSon[node, 2], e]]}; v ← tm.MakeNode[$arrow, 2]}; $apply, $applyDefault => v ← Appl[m, node, e, node.name=$applyDefault]; $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]]]}; IN Tree.BindOp => v ← t; -- ever found? $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 ERROR; -- botch within the modeller v ← tm.MakeNode[$cross2, 2]}; $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 %s\n", IO.atom[name]]}; }; tm.PushTree[binding]; tm.PushName[name]; v ← tm.MakeNode[$subscript, 2]}; $env => {v ← tm.MakeNode[$env, 0]; TreeOps.PutExt[v, e]}; $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]}; $unitId => v ← BindFile[m, t]; $locator => v ← Eval[m, node.son[1], e]; -- can't use NthSon ENDCASE => 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]}; -- environment representation and management Env: TYPE ~ REF ScopeDescriptor; ScopeDescriptor: PUBLIC TYPE ~ RECORD[ db: Tree.Handle, -- decl or binding parent: Env]; ClosureObject: TYPE~RECORD[ body: Tree.Link, -- the lambda body e: Env]; -- its environment (no formals) Closure: TYPE ~ REF ClosureObject; ConsEnv: PROC[tm: TreeOps.TM, db: Tree.Handle, parent: Env] RETURNS[Env] ~ { RETURN[(tm.Zone).NEW[ScopeDescriptor ← [parent~parent, 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, a several procs in this module know about Tree.Node internals DBtoD: PROC[db: Tree.Handle] RETURNS[Tree.Handle] ~ INLINE { RETURN[IF db.name = $decl THEN db ELSE NARROW[db.son[1]]]}; -- BindOp or cross2 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.Cat[CS.RopeFromCard[n]]]]}; MakeIds: PROC[tm: TreeOps.TM, db: Tree.Handle, mark: BOOL] ~ { d: Tree.Handle ~ DBtoD[db]; FOR p: NAT 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: NAT 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: NAT 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] ~ { -- make a copy of the declElem node 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]}; -- scope evaluation BindDecl: PROC[m: SMOps.MS, elem: Tree.Link, e: Env] ~ { TreeOps.PutNthSon[elem, 2, Eval[m, TreeOps.NthSon[elem, 2], e]]}; 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 if legal circularity? }; EvalDecl: PROC[m: SMOps.MS, decl: Tree.Link, e: Env] ~ { d: Tree.Handle ~ NARROW[decl]; FOR p: NAT 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: NAT IN [1 .. g.sonLimit) DO g.son[p] ← Eval[m, g.son[p], e]; ENDLOOP; }; EvalRecBinding: PROC[m: SMOps.MS, binding: Tree.Link, e: Env] ~ { d: Tree.Handle ~ DBtoD[NARROW[binding]]; FOR p: NAT IN [1 .. d.sonLimit) DO EvalId[m, NARROW[TreeOps.GetExt[d.son[p]]]] ENDLOOP; }; -- simplification 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[v: Tree.Link] ~ { bv: Tree.Link ~ ValOf[m, TreeOps.NthSon[t, 1]]; IF IsBinding[bv] THEN { b: Tree.Handle ~ NARROW[bv]; p: NAT ~ SearchD[TreeOps.GetName[TreeOps.NthSon[t, 2]], DBtoD[b]]; v ← (IF p # 0 THEN TreeOps.NthSon[DBtoG[b], p] ELSE t)} ELSE v ← t; RETURN}; -- file expansion BindFile: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[v: Tree.Link] ~ { nameList: Tree.Link ~ TreeOps.NthSon[t, 3]; -- other parts ignored for now version: Tree.Text ~ NARROW[TreeOps.NthSon[t, 4]]; ext: Tree.Text ~ NARROW[TreeOps.NthSon[nameList, TreeOps.NSons[nameList]]]; SELECT TRUE FROM ext.Equal["mesa", FALSE], ext.Equal["bcd", FALSE] => { tree: Tree.Link ~ SMFIOps.EvaluateUnitId[m, UnitToRope[nameList], version]; v ← Eval[m, tree, NIL]}; ext.Equal["model", FALSE], ext.Equal["modelBcd", FALSE] => { subModel: Tree.Link ← NARROW[TreeOps.GetExt[t]]; IF subModel = Tree.null THEN { source: IO.STREAM ← NIL; source ← FileIO.Open[UnitToRope[nameList] ! FileIO.OpenFailed => {CONTINUE}]; IF source = NIL THEN v ← t ELSE { -- new model to keep comments separate newM: SMOps.MS ~ SMOps.NewModel[in~m.in, out~m.out, msgout~m.msgOut]; (newM.tm).Initialize; IF ext.Equal["modelBcd", FALSE] THEN -- advance input stream SMBcd.ReadModelBcdPrefix[newM, source]; subModel ← SMUtil.ParseStream[newM, source]; TreeOps.PutExt[t, subModel]; v ← Eval[newM, subModel, NIL]; (newM.tm).Finalize}; } ELSE v ← Eval[m, subModel, NIL]}; ENDCASE => v ← t}; -- type attributes and predicates Type: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[type: Tree.Link] ~ { WITH t SELECT FROM id: Tree.Id => { d: Tree.Handle ~ 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 => 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 => type ← Range[Type[m, TreeOps.NthSon[node, 1]]]; $decl => type ← (m.tm).MakeNode[$typeDECL, 0]; IN Tree.BindOp => type ← DBtoD[node]; ENDCASE => type ← Tree.null; -- for now 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 => TreeOps.NthSon[t, 1], ENDCASE => Tree.null]}; Range: PROC[t: Tree.Link] RETURNS[Tree.Link] ~ { RETURN[SELECT TreeOps.OpName[t] FROM $arrow => TreeOps.NthSon[t, 2], ENDCASE => Tree.null]}; FormalRange: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[Tree.Link] ~ { -- does not bind from domain(s) RETURN[WITH ValOf[m, t] SELECT FROM node: Tree.Handle => SELECT TreeOps.OpName[node] FROM IN Tree.ApplOp => Type[m, node], $subscript => FormalRange[m, TreeOps.NthSon[node, 1]], ENDCASE => Tree.null, ENDCASE => Tree.null]}; HasName: PROC[type: Tree.Link, name: Tree.Name] RETURNS[BOOL] ~ { SELECT TreeOps.OpName[type] FROM $type => RETURN[TreeOps.GetName[TreeOps.NthSon[type, 1]] = name]; $cross => RETURN[ HasName[TreeOps.NthSon[type, 1], name] OR HasName[TreeOps.NthSon[type, 2], name]]; $cross2 => { decl: Tree.Link ~ TreeOps.NthSon[type, 1]; FOR i: NAT IN [1..TreeOps.NSons[decl]] DO IF HasName[DeclType[TreeOps.NthSon[decl, i]], name] THEN RETURN[TRUE]; ENDLOOP; RETURN[HasName[TreeOps.NthSon[type, 2], name]]}; ENDCASE => NULL; RETURN[FALSE]}; Implies: PROC[m: SMOps.MS, type1, type2: Tree.Link] RETURNS[BOOL] ~ { IF TreeOps.OpName[type2] = $type THEN { name: Tree.Name ~ TreeOps.GetName[TreeOps.NthSon[type2, 1]]; RETURN[name = Tree.nullName OR HasName[FormalRange[m, type1], name]]} ELSE RETURN[TRUE]}; -- for now -- MORE TO BE WRITTEN -- argument filling and checking 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] 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 %s 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: NAT 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 %s for %s 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["%s for %s has wrong type\n", IO.rope[IF p #0 THEN "Acutal" 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: NAT IN [1 .. nD] DO type: Tree.Link ~ ValOf[m, DeclType[d.son[i]]]; v: Tree.Link; n: NAT; [v, n] ← MatchByType[m, g, type, i]; 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 %s has wrong type\n", IO.atom[name]]}; n ← 1}; }; IF n # 1 THEN { ErrorLoc[m]; m.out.PutF["%d values have type compatible with %s in argument list\n", IO.card[n], IO.atom[DeclName[d.son[i]]]]; }; tm.PushTree[v]; ENDLOOP; tm.PushNode[$group, nD]}; ENDCASE => { ErrorLoc[m]; m.out.PutF["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, p: NAT] RETURNS[v: Tree.Link, matches: NAT ← 0] ~ { FOR i: NAT IN [1 .. g.sonLimit) DO IF HasType[m, g.son[i], type, i=p] THEN { -- fudge positional for don't know cases IF matches = 0 THEN v ← g.son[i]; matches ← matches + 1}; 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, default: BOOL←TRUE] RETURNS[BOOL] ~ { -- temporary code, adequate to check compile-time args (only) RETURN[SELECT TreeOps.OpName[type] FROM $type => Implies[m, v, type], $typeSTRING => (TreeOps.OpName[ValOf[m, Type[m, v]]] = $typeSTRING OR ISTYPE[ValOf[m, v], Tree.Text]), -- fudge until Type does literals ENDCASE => default] -- temporary fudge for don't know cases }; -- beta reduction IsLambda: PROC[m: SMOps.MS, t: Tree.Link] RETURNS[BOOL] ~ { RETURN[TreeOps.OpName[ValOf[m, t]] = $lambda]}; BetaReduce: PROC[m: SMOps.MS, rator, rands: Tree.Link, e: Env] RETURNS[Tree.Link] ~ { lambda: Tree.Link ~ ValOf[m, rator]; decl: Tree.Link ~ ValOf[m, TreeOps.NthSon[lambda, 1]]; tm: TreeOps.TM ~ m.tm; closure: Closure ~ NARROW[TreeOps.GetExt[lambda]]; 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]]]; RETURN[tm.MakeNode[$let, 2]]}; }.