m3tools/src/M3Scope.m3


 Copyright 1996-2000 Critical Mass, Inc. All rights reserved.    
 See file COPYRIGHT-CMASS for details. 

MODULE M3Scope;

IMPORT M3ID, M3AST;

TYPE
  ScopeInfo = BRANDED "M3Scope.ScopeInfo" REF RECORD
    ast  : M3AST.T;
    n_scopes : CARDINAL;
    scopes   : REF ARRAY OF ScopeDesc;
    n_defns  : CARDINAL;
    defns    : REF ARRAY OF SymDef;
  END;

  ScopeDesc = RECORD
    loc    : Range;
    defn   : Range;
    parent : CARDINAL;  (* index into scopes, LAST(CARD) for root scope *)
  END;

  Range = RECORD start, stop: CARDINAL; END;

  SymDef = RECORD
    sym   : M3ID.T;
    class : Class;
    loc   : M3AST.NodeIndex;
    info  : REFANY;
  END;

PROCEDURE LookUp (ast: M3AST.T;  loc: M3AST.NodeIndex;  sym: M3ID.T;
                  VAR(*OUT*) defn: Defn): BOOLEAN =
  VAR si: ScopeInfo;  sx: CARDINAL;
  BEGIN
    IF (loc < 0) OR (loc >= NUMBER (ast.nodes^)) THEN
      RETURN FALSE;
    END;

    IF (ast.scope_info = NIL) THEN Bind (ast); END;
    si := ast.scope_info;

    sx := FindScope (si, loc);
    WHILE (sx < si.n_scopes) DO
      WITH scope = si.scopes [sx] DO
        FOR i := scope.defn.start TO scope.defn.stop DO
          WITH z = si.defns[i] DO
            IF (z.sym = sym) THEN
              defn.ast   := ast;
              defn.loc   := z.loc;
              defn.class := z.class;
              defn.info  := z.info;
              defn.uid   := i;
              RETURN TRUE;
            END;
          END;
        END;
        sx := scope.parent;
      END; (* WITH *)
    END;

    RETURN FALSE;
  END LookUp;

PROCEDURE FindScope (si: ScopeInfo;  loc: M3AST.NodeIndex): CARDINAL =
  VAR
    lo  : CARDINAL := 0;
    hi  : CARDINAL := si.n_scopes;
    mid : CARDINAL;
  BEGIN
    IF (loc = 0) THEN
      (* skip the outermost scope that includes the imports *)
      IF    hi > 1 THEN  loc := si.scopes[1].loc.start;
      ELSIF hi > 0 THEN  loc := si.scopes[0].loc.start;
      END;
    END;

    (* find the first scope begining after "loc" *)
    WHILE (lo < hi) DO
      mid := (lo + hi) DIV 2;
      IF loc < si.scopes[mid].loc.start
        THEN hi := mid;
        ELSE lo := mid + 1;
      END;
    END;

    (* search backwards until we find a scope containing "loc" *)
    FOR i := MIN (lo, si.n_scopes-1) TO 0 BY -1 DO
      WITH z = si.scopes [i] DO
        IF (z.loc.start <= loc) AND (loc <= z.loc.stop) THEN RETURN i; END;
      END;
    END;

    (* failed, return an out-of-range index *)
    RETURN si.n_scopes;
  END FindScope;

PROCEDURE Define (READONLY defn: Defn;  info: REFANY) =
  VAR si: ScopeInfo;  ast := defn.ast;
  BEGIN
    IF (ast.scope_info = NIL) THEN Bind (ast); END;
    si := ast.scope_info;
    <*ASSERT (0 <= defn.uid) AND (defn.uid < si.n_defns) *>
    si.defns[defn.uid].info := info;
  END Define;
----------------------------------------------------- scope construction ---

TYPE
  BindState = RECORD
    ast     : M3AST.T;
    si      : ScopeInfo;
    n_temps : CARDINAL;
    temps   : REF ARRAY OF SymDef;
  END;

PROCEDURE Bind (ast: M3AST.T) =
  VAR s: BindState;
  BEGIN
    s.ast         := ast;
    s.si          := NEW (ScopeInfo);
    s.si.ast      := ast;
    s.si.n_scopes := 0;
    s.si.scopes   := NEW (REF ARRAY OF ScopeDesc, 16);
    s.si.n_defns  := 1;
    s.si.defns    := NEW (REF ARRAY OF SymDef, 32);
    s.n_temps     := 0;
    s.temps       := NEW (REF ARRAY OF SymDef, 32);

    BindScope (s, 0, NUMBER (ast.nodes^), LAST (CARDINAL));
    ast.scope_info := s.si;
  END Bind;

PROCEDURE BindScope (VAR s: BindState;  start, stop: M3AST.NodeIndex;
                     parent: CARDINAL) =
  VAR
    self := s.si.n_scopes;
    si   := s.si;
  BEGIN
    (* push the new scope *)
    IF si.n_scopes >= NUMBER (si.scopes^) THEN ExpandScopes (si); END;
    WITH z = si.scopes [self] DO
      z.loc.start  := start;
      z.loc.stop   := stop-1;
      z.defn.start := s.n_temps;
      z.defn.stop  := s.n_temps;
      z.parent     := parent;
    END;
    INC (si.n_scopes);

    BindNodes (s, start, stop, self);

    (* pop the new scope & all of its definitions *)
    WITH z = si.scopes [self], cnt = s.n_temps - z.defn.start DO
      IF (cnt > 0) THEN
        WHILE (si.n_defns+cnt >= NUMBER (si.defns^)) DO ExpandDefns (si); END;
        SUBARRAY (si.defns^, si.n_defns, cnt)
          := SUBARRAY (s.temps^, z.defn.start, cnt);
      END;
      s.n_temps := z.defn.start;
      z.defn.start := si.n_defns;
      z.defn.stop  := si.n_defns + cnt - 1;
      INC (si.n_defns, cnt);
    END;
  END BindScope;

PROCEDURE BindNodes (VAR s: BindState;  start, stop: M3AST.NodeIndex;
                     self: CARDINAL) =
  VAR x:= start;  op: M3AST.OP;
  BEGIN
    WHILE (x < stop) DO
      WITH z = s.ast.nodes[x] DO
        op := z.op;  (* force a range check here so we know the CASE
                        is handling all the legal values *)
        CASE op OF
        | M3AST.OP_Block       => BindScope (s, x+1, x + z.width, self);
        | M3AST.OP_GenericArg  => PushDefn (s, x, z.info, Class.GenericArg);
        | M3AST.OP_Import      => PushDefn (s, x, z.info, Class.Import);
        | M3AST.OP_ImportAs    => PushDefn (s, x, z.info, Class.Import);
        | M3AST.OP_FromImport  => PushDefn (s, x, z.info, Class.Import);
        | M3AST.OP_ConstDecl   => PushDefn (s, x, z.info, Class.Const);
        | M3AST.OP_TypeDecl    => PushDefn (s, x, z.info, Class.Type);
        | M3AST.OP_OpaqueDecl  => PushDefn (s, x, z.info, Class.Type);
        | M3AST.OP_ExceptDecl  => PushDefn (s, x, z.info, Class.Exception);
        | M3AST.OP_VarDefn     => PushDefn (s, x, z.info, Class.Var);
        | M3AST.OP_FormalDefn  => PushDefn (s, x, z.info, Class.Formal);

        | M3AST.OP_ProcDecl    => PushDefn (s, x, z.info, Class.Procedure);
                                  BindScope (s, x+1, x + z.width, self);

        | M3AST.OP_For1, M3AST.OP_ForN, M3AST.OP_TryHandlerVar,
          M3AST.OP_TypeCaseVar, M3AST.OP_With =>
            IF (start = x)
              THEN PushDefn (s, x, z.info, Class.Var);
              ELSE BindScope (s, x, x + z.width, self);
            END;

        | M3AST.OP_Unit, M3AST.OP_Generic, M3AST.OP_GenInstance, M3AST.OP_VarDecl,
          M3AST.OP_Case, M3AST.OP_CaseBranch, M3AST.OP_CaseElse,
          M3AST.OP_If, M3AST.OP_IfClause, M3AST.OP_IfElse, M3AST.OP_Lock,
          M3AST.OP_Loop, M3AST.OP_Repeat, M3AST.OP_TryFinally,
          M3AST.OP_TryExcept, M3AST.OP_TryHandler, M3AST.OP_TryElse,
          M3AST.OP_TypeCase, M3AST.OP_TypeCaseArm, M3AST.OP_TypeCaseElse,
          M3AST.OP_While, M3AST.OP_ProcType, M3AST.OP_Formal, M3AST.OP_StmtList =>
            (* visit the subtrees *)
            BindNodes (s, x + 1, x + z.width, self);

        | M3AST.OP_Empty, M3AST.OP_Export,
          M3AST.OP_Reveal, M3AST.OP_RevealPartial,
          M3AST.OP_Assign, M3AST.OP_Assert, M3AST.OP_CallStmt,
          M3AST.OP_CaseLabel, M3AST.OP_CaseRange, M3AST.OP_Exit,
          M3AST.OP_Eval, M3AST.OP_Raise, M3AST.OP_RaiseValue,
          M3AST.OP_Return, M3AST.OP_ReturnValue, M3AST.OP_Array,
          M3AST.OP_OpenArray, M3AST.OP_Enum, M3AST.OP_EnumDefn,
          M3AST.OP_NamedType, M3AST.OP_Method, M3AST.OP_Override,
          M3AST.OP_Packed, M3AST.OP_Field, M3AST.OP_FieldDefn,
          M3AST.OP_Raises, M3AST.OP_RaisesAny, M3AST.OP_Object,
          M3AST.OP_NoBrand, M3AST.OP_DefaultBrand, M3AST.OP_Record,
          M3AST.OP_Ref, M3AST.OP_Root, M3AST.OP_Set, M3AST.OP_Subrange,
          M3AST.OP_UntracedRef, M3AST.OP_UntracedRoot,
          M3AST.OP_Or .. M3AST.OP_Qualify,          (* expr operators *)
          M3AST.OP_Id ..M3AST.OP_Text,              (* literals *)
          M3AST.OP_Attributes .. M3AST.OP_FatalAny  (* pragmas *)
          =>
            (* skip this node and any subtrees *)

        END; (* CASE *)
        x := x + z.width;
      END;
    END;
  END BindNodes;

PROCEDURE PushDefn (VAR s: BindState;  loc: M3AST.NodeIndex;
                    sym: M3ID.T;  class: Class) =
  BEGIN
    IF (s.n_temps >= NUMBER (s.temps^)) THEN ExpandTemps (s); END;
    WITH z = s.temps [s.n_temps] DO
      <*ASSERT sym # M3ID.NoID *>
      z.sym   := sym;
      z.class := class;
      z.loc   := loc;
    END;
    INC (s.n_temps);
  END PushDefn;

PROCEDURE ExpandScopes (si: ScopeInfo) =
  VAR n := NUMBER (si.scopes^);  xx := NEW (REF ARRAY OF ScopeDesc, n+n);
  BEGIN
    SUBARRAY (xx^, 0, n) := si.scopes^;
    si.scopes := xx;
  END ExpandScopes;

PROCEDURE ExpandDefns (si: ScopeInfo) =
  VAR n := NUMBER (si.defns^);  xx := NEW (REF ARRAY OF SymDef, n+n);
  BEGIN
    SUBARRAY (xx^, 0, n) := si.defns^;
    si.defns := xx;
  END ExpandDefns;

PROCEDURE ExpandTemps (VAR s: BindState) =
  VAR n := NUMBER (s.temps^);  xx := NEW (REF ARRAY OF SymDef, n+n);
  BEGIN
    SUBARRAY (xx^, 0, n) := s.temps^;
    s.temps := xx;
  END ExpandTemps;

BEGIN
END M3Scope.

interface M3ID is in:


interface M3AST is in: