(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* *) (* Last modified on Sat Nov 12 18:54:18 PST 1994 by heydon *) (* modified on Fri Aug 7 21:54:05 PDT 1992 by myers *) MODULE JunoScope; IMPORT JunoAST, JunoASTUtils, StackTbl, JunoUnparse, JunoCompileErr; IMPORT JunoRT; IMPORT Atom, AtomRefTbl; IMPORT Wr, Fmt, Stdio; FROM Thread IMPORT Alerted; <* FATAL Wr.Failure, Alerted *> REVEAL T = AtomRefTbl.Default BRANDED "JunoScope.T" OBJECT parent: T := NIL; END; REVEAL Entity = BRANDED "Entity" OBJECT END; Temp = LocalValue BRANDED "Temp" OBJECT END; Arg = LocalParam BRANDED "Arg" OBJECT END; Const = Value BRANDED "Const" OBJECT END; Var = Value BRANDED "Var" OBJECT END; Pred = PredCode BRANDED "Pred" OBJECT END; Func = PredCode BRANDED "Func" OBJECT END; Proc = ProcCode BRANDED "Proc" OBJECT END; Mod = Unit BRANDED "ModEntity" OBJECT END; PROCEDURE New(p: T; size: CARDINAL := 1): T = BEGIN RETURN NEW(T, parent := p).init(size) END New; PROCEDURE Parent(scp: T): T = BEGIN RETURN scp.parent END Parent; PROCEDURE SetParent(scp, parent: T) = BEGIN scp.parent := parent END SetParent; PROCEDURE Lookup(scp: T; id: Atom.T; localOnly := FALSE): Entity = VAR ref: REFANY; BEGIN WHILE scp # NIL DO IF scp.get(id, ref) THEN RETURN NARROW(ref, Entity) ELSIF localOnly THEN EXIT ELSE scp := scp.parent END END; RETURN NIL; END Lookup; PROCEDURE LookupQId( scp: T; qid: JunoAST.QId; VAR (*OUT*) unit: Entity): Entity = BEGIN IF qid.id0 = JunoAST.NilId THEN unit := NIL; RETURN Lookup(scp, qid.id1) ELSE unit := Lookup(scp, qid.id0); TYPECASE unit OF NULL => RETURN NIL | Mod(m) => RETURN Lookup(m.public_scp, qid.id1) ELSE RETURN NIL END END END LookupQId; PROCEDURE Names(scp: T): REF ARRAY OF Atom.T = VAR it := scp.iterate(); key: Atom.T; dummy: REFANY; res := NEW(REF ARRAY OF Atom.T, scp.size()); i := 0; BEGIN WHILE it.next(key, dummy) DO res[i] := key; INC(i) END; RETURN res END Names; PROCEDURE LocalArgs(scp: T; kinds: SET OF ArgKind): JunoAST.IdList = VAR it := scp.iterate(); key: Atom.T; value: REFANY; res := NEW(JunoAST.IdList); BEGIN WHILE it.next(key, value) DO TYPECASE value OF Arg (arg) => IF arg.kind IN kinds THEN VAR link := NEW(JunoAST.IdLink, id := key, index := arg.offset, next := res.head); BEGIN res.head := link; INC(res.size) END END ELSE (* SKIP *) END; END; RETURN res END LocalArgs; PROCEDURE Bind(scp: T; id: Atom.T; e: Entity) RAISES {NameClash} = VAR dummy: REFANY; BEGIN IF NOT scp.get(id, dummy) THEN EVAL scp.put(id, e) ELSE RAISE NameClash END END Bind; PROCEDURE Rebind(scp: T; id: Atom.T; e: Entity) = BEGIN EVAL scp.put(id, e) END Rebind; PROCEDURE Unbind(scp: T; id: Atom.T): Entity RAISES { NotFound } = VAR res: REFANY; BEGIN IF NOT scp.delete(id, res) THEN RAISE NotFound END; RETURN NARROW(res, Entity) END Unbind; CONST Tab = 2; PROCEDURE Indent(wr: Wr.T; indent: CARDINAL) = BEGIN WHILE indent > 0 DO Wr.PutChar(wr, ' '); DEC(indent) END END Indent; PROCEDURE Debug(scp: T; level: CARDINAL := 0) = BEGIN Print(Stdio.stderr, scp, level, 2) END Debug; PROCEDURE Print(wr: Wr.T; scp: T; level, indent: CARDINAL := 0) = BEGIN IF scp = NIL THEN Indent(wr, indent); Wr.PutText(wr, "\n") ELSE VAR key := Names(scp); BEGIN FOR i := 0 TO NUMBER(key^) - 1 DO Indent(wr, indent); Wr.PutText(wr, "\"" & Atom.ToText(key[i]) & "\": "); PrintEntity(wr, Lookup(scp, key[i]), level, indent + Tab); END END END; Wr.Flush(wr) END Print; PROCEDURE PrintEntity(wr: Wr.T; ent: Entity; level, indent: CARDINAL) = PROCEDURE PrintInt(wr: Wr.T; field: TEXT; val: INTEGER; in: CARDINAL) = BEGIN Indent(wr, in); Wr.PutText(wr, field & " = " & Fmt.Int(val) & "\n"); END PrintInt; PROCEDURE PrintArgKind(wr: Wr.T; field: TEXT; val: ArgKind; in: CARDINAL) = BEGIN Indent(wr, in); Wr.PutText(wr, field & " = "); CASE val OF | ArgKind.Out => Wr.PutText(wr, "OUT\n") | ArgKind.InOut => Wr.PutText(wr, "INOUT\n") | ArgKind.In => Wr.PutText(wr, "IN\n") END END PrintArgKind; PROCEDURE PrintExpr(wr: Wr.T; field: TEXT; e: JunoAST.Expr; in: CARDINAL) = BEGIN Indent(wr, in); Wr.PutText(wr, field & " = "); JunoUnparse.Expr(wr, e, LAST(INTEGER), indent := 0); Wr.PutChar(wr, '\n'); END PrintExpr; PROCEDURE PrintScope(wr: Wr.T; field: TEXT; val: T; in: CARDINAL) = BEGIN Indent(wr, in); Wr.PutText(wr, field); IF level > 0 THEN Wr.PutText(wr, " =\n"); Print(wr, val, level - 1, in + Tab) ELSE Wr.PutText(wr, " (elided)\n") END END PrintScope; (* PrintEntity *) BEGIN TYPECASE ent OF <* NOWARN *> | Temp (e) => Wr.PutText(wr, "Temp\n"); PrintInt(wr, "Offset", e.offset, indent) | Arg (e) => Wr.PutText(wr, "Arg\n"); PrintInt(wr, "Offset", e.offset, indent); PrintArgKind(wr, "Kind", e.kind, indent) | Const (e) => Wr.PutText(wr, "CONST\n"); PrintInt(wr, "Index", e.index, indent); PrintExpr(wr, "Init", e.init, indent) | Var (e) => Wr.PutText(wr, "VAR\n"); PrintInt(wr, "Index", e.index, indent); IF e.init # JunoAST.NilExpr THEN PrintExpr(wr, "Init", e.init, indent) END | Pred (e) => Wr.PutText(wr, "PREDICATE\n"); PrintInt(wr, "Index", e.index, indent); PrintInt(wr, "In-Cnt", e.in_cnt, indent); PrintScope(wr, "Argument Scope", e.formals, indent); | Func (e) => Wr.PutText(wr, "FUNCTION\n"); PrintInt(wr, "Index", e.index, indent); PrintInt(wr, "In-Cnt", e.in_cnt, indent); PrintScope(wr, "Argument Scope", e.formals, indent); | Proc (e) => Wr.PutText(wr, "PROCEDURE\n"); PrintInt(wr, "Index", e.index, indent); PrintInt(wr, "In-Cnt", e.in_cnt, indent); PrintInt(wr, "InOut-Cnt", e.inout_cnt, indent); PrintInt(wr, "Out-Cnt", e.out_cnt, indent); PrintScope(wr, "Argument Scope", e.formals, indent); | Mod (e) => Wr.PutText(wr, "MODULE\n"); PrintScope(wr, "Public Scope", e.public_scp, indent); PrintScope(wr, "Private Scope", e.scp, indent); END END PrintEntity; PROCEDURE NewPred(pred: JunoAST.PredDecl; mod: JunoAST.Id): Pred RAISES {JunoCompileErr.Error} = VAR tbl := NEW(StackTbl.T).init(); header := pred.header; slot := JunoRT.GetCodeIndex(JunoRT.ProcAttr{ mod, header.name, JunoRT.Sig{0, 0, header.ins.size}}); BEGIN RETURN NEW(Pred, index := slot, body := pred.body, tbl := tbl, formals := ArgScope(tbl, header, header.ins, NIL, NIL), in_cnt := header.ins.size, normal_form := NIL) END NewPred; PROCEDURE NewFunc(func: JunoAST.FuncDecl; mod: JunoAST.Id): Func RAISES {JunoCompileErr.Error} = VAR tbl := NEW(StackTbl.T).init(); header := func.header; slot := JunoRT.GetCodeIndex(JunoRT.ProcAttr{ mod, header.name, JunoRT.Sig{1, 0, header.ins.size}}); outs := JunoASTUtils.NewIdList(header.result, index := -(header.ins.size + 1)); BEGIN RETURN NEW(Func, index := slot, body := func.body, tbl := tbl, formals := ArgScope(tbl, header, header.ins, NIL, outs), in_cnt := header.ins.size, normal_form := NIL) END NewFunc; PROCEDURE NewProc(proc: JunoAST.ProcDecl; mod: JunoAST.Id): Proc RAISES {JunoCompileErr.Error} = VAR tbl := NEW(StackTbl.T).init(); header := proc.header; slot := JunoRT.GetCodeIndex(JunoRT.ProcAttr{mod, header.name, JunoRT.Sig{header.outs.size, header.inouts.size, header.ins.size}}); BEGIN RETURN NEW(Proc, index := slot, body := proc.body, formals := ArgScope(tbl, header, header.ins, header.inouts, header.outs), in_cnt := header.ins.size, inout_cnt := header.inouts.size, out_cnt := header.outs.size, tbl := tbl) END NewProc; PROCEDURE ArgScope( tbl: StackTbl.T; header: JunoAST.Header; ins, inouts, outs: JunoAST.IdList): T RAISES {JunoCompileErr.Error} = (* Return a new "JunoScope.T" containing one "JunoScope.Arg" for each formal in the lists "ins", "inouts", and "outs". This procedure has the side-effect of assigning each formal an index according to "tbl.next_formal"; it also adds the formals to "tbl". The indices are assigned first to "ins", then to "inouts", and finally to "outs", but within each group, indices are assigned in reverse order. Therefore, if "tbl" is a newly initialized table, the indices start at -1 and decrease from right to left in the order in which the arguments appear in the declaration. For example, in the declaration: | PROCEDURE a,b := (c,d):P(e,f) the arguments are assigned indices as follows: f => -1, e => -2, d => -3, c => -4, b => -5, and a => -6. Raises "JunoCompileErr.Error" with argument AST "header" in the event that the same name is used for more than one formal in the header. *) VAR res := New(NIL); PROCEDURE AddArgs(ids: JunoAST.IdLink; kind: ArgKind) RAISES {NameClash} = (* IMPLEMENTATION: Use recursion to add arguments in reverse order. *) BEGIN IF ids # NIL THEN AddArgs(ids.next, kind); Bind(res, ids.id, NEW(Arg, kind := kind, offset := tbl.next_formal)); ids.index := tbl.next_formal; StackTbl.PushFormal(tbl, ids.id) END END AddArgs; BEGIN TRY AddArgs(ins.head, ArgKind.In); IF inouts # NIL THEN AddArgs(inouts.head, ArgKind.InOut) END; IF outs # NIL THEN AddArgs(outs.head, ArgKind.Out) END EXCEPT NameClash => JunoCompileErr.Raise( "This declaration contains duplicate formal names", header) END; RETURN res END ArgScope; BEGIN END JunoScope.