m3tk/src/ast/M3ASTScope.m3


 Copyright (C) 1990, Digital Equipment Corporation.         
 All rights reserved.                                       
 See the file COPYRIGHT for a full description.             
 Last modified on Mon Aug  8 16:05:15 PDT 1994 by kalsow    
      modified on Fri Jan 28 10:57:17 PST 1994 by detlefs   

MODULE M3ASTScope;

IMPORT ASTWalk, AST, M3AST_AS, M3AST_SM;
IMPORT SeqM3AST_AS_DEF_ID, SeqM3AST_AS_Binding;
IMPORT M3AST_AS_F, M3AST_SM_F;

REVEAL Closure = Closure_public BRANDED OBJECT
    for_scope: M3AST_SM.SCOPE := NIL;
    for_end: AST.NODE := NIL;
  END;

PROCEDURE Set(cl: Closure; n: AST.NODE; vm: ASTWalk.VisitMode)=
  BEGIN
    TYPECASE n OF
    | M3AST_AS.UNIT(unit) =>
        IF vm = ASTWalk.VisitMode.Entry THEN
          cl.scope := unit.as_id.vSCOPE
        ELSE
          cl.scope := NIL
        END;

    | M3AST_AS.Proc_decl(p) =>
        (* Only when we enter the Block, if any, do the
           formals come into scope, so we let the Block
           node do the work on entry. On exit, we reset
           the scope to the enclosing scope. *)
        IF vm = ASTWalk.VisitMode.Exit AND p.as_body # NIL THEN
          cl.scope := cl.scope.sm_enc_scope;
        END;

    | M3AST_AS.Block(block) =>
        IF vm = ASTWalk.VisitMode.Entry THEN
          cl.scope := block.vSCOPE
        ELSE
          cl.scope := cl.scope.sm_enc_scope;
        END;

    | M3AST_AS.Handler(h) =>
        IF vm = ASTWalk.VisitMode.Exit AND h.as_id # NIL THEN
          cl.scope := cl.scope.sm_enc_scope;
        END;

    | M3AST_AS.Handler_id(id) =>
        IF vm = ASTWalk.VisitMode.Entry THEN
          cl.scope := id.vSCOPE;
        END;

    | M3AST_AS.Tcase(h) =>
        IF vm = ASTWalk.VisitMode.Exit AND h.as_id # NIL THEN
          cl.scope := cl.scope.sm_enc_scope;
        END;

    | M3AST_AS.Tcase_id(id) =>
        IF vm = ASTWalk.VisitMode.Entry THEN
          cl.scope := id.vSCOPE;
        END;

    | M3AST_AS.Binding(b) =>
        (* As we exit a Binding, the With_id comes into
           scope (for the next Binding, for example).
           They all go out of scope as we exit the With_st. *)
        IF vm = ASTWalk.VisitMode.Exit THEN
          cl.scope := b.as_id.vSCOPE;
        END;

    | M3AST_AS.With_st(w) =>
        (* scope reverts to encloser of that asssociated with
           first binding *)
        IF vm = ASTWalk.VisitMode.Exit THEN
          VAR iter := SeqM3AST_AS_Binding.NewIter(w.as_binding_s);
            b: M3AST_AS.Binding;
          BEGIN
            IF SeqM3AST_AS_Binding.Next(iter, b) THEN
              cl.scope := b.as_id.vSCOPE.sm_enc_scope;
            END;
          END;
        END;

    | M3AST_AS.For_st(for_st) =>
        (* Somewhat tricky; the For_id does not come into
           scope until the statement sequence, and we have
           an optional BY node. *)
        IF vm = ASTWalk.VisitMode.Entry THEN
          IF for_st.as_by # NIL THEN cl.for_end := for_st.as_by
          ELSE cl.for_end := for_st.as_from;
          END;
          cl.for_scope := for_st.as_id.vSCOPE;
        ELSE
          cl.scope := for_st.as_id.vSCOPE.sm_enc_scope;
        END;

    ELSE
      IF vm = ASTWalk.VisitMode.Exit THEN
        IF n = cl.for_end THEN
          cl.scope := cl.for_scope;
          cl.for_end := NIL;
        END;
      END;
    END
  END Set;

PROCEDURE Lookup(s: M3AST_SM.SCOPE;
    used_id: M3AST_AS.USED_ID): M3AST_AS.DEF_ID=
  VAR ps: M3AST_SM.SCOPE;
  BEGIN
    IF used_id.lx_symrep = NIL THEN RETURN NIL END;
    WHILE s # NIL DO
      VAR
        iter := SeqM3AST_AS_DEF_ID.NewIter(s.sm_def_id_s);
        def_id: M3AST_AS.DEF_ID;
      BEGIN
        WHILE SeqM3AST_AS_DEF_ID.Next(iter, def_id) DO
          IF used_id.lx_symrep = def_id.lx_symrep THEN
            RETURN def_id;
          END;
        END;
        ps := s;
        s := s.sm_enc_scope;
        IF (s = NIL OR (s.sm_level # ps.sm_level)) THEN
          RETURN NIL
        END;
      END;
    END;
    <*ASSERT FALSE*>
  END Lookup;

BEGIN
END M3ASTScope.