m3tohtml/src/M3DB.m3


 Copyright (C) 1992, Digital Equipment Corporation                         
 All rights reserved.                                                      
 See the file COPYRIGHT for a full description.                            
                                                                           
 Last modified on Thu Dec 15 09:44:34 PST 1994 by kalsow                   

MODULE M3DB;

IMPORT Rd, TextList, TextDB, M3Scanner, Text;

VAR
  db := NEW (TextDB.T).init ();
  imports     : TextDB.Relation;
  exports     : TextDB.Relation;
  revealsTo   : TextDB.Relation;
  revealsType : TextDB.Relation;
  definesType : TextDB.Relation;
  definesProc : TextDB.Relation;
  interface   : TextDB.Relation;
  module      : TextDB.Relation;
  genericIntf : TextDB.Relation;
  genericMod  : TextDB.Relation;

PROCEDURE Open (path: TEXT) =
  BEGIN
    db.load (path);
    imports     := db.create_relation ("imports");
    exports     := db.create_relation ("exports");
    revealsTo   := db.create_relation ("revealsTo");
    revealsType := db.create_relation ("revealsType");
    definesType := db.create_relation ("type");
    definesProc := db.create_relation ("procedure");
    interface   := db.create_relation ("interface");
    module      := db.create_relation ("module");
    genericIntf := db.create_relation ("genericInterface");
    genericMod  := db.create_relation ("genericModule");
  END Open;

PROCEDURE Dump (path: TEXT) =
  BEGIN
    db.dump (path);
  END Dump;
--------------------------------------------------------------- queries ---

PROCEDURE Imports (interface: TEXT): TextList.T =
  BEGIN
    RETURN imports.getValue (interface);
  END Imports;

PROCEDURE Exports (interface: TEXT): TextList.T =
  BEGIN
    RETURN exports.getValue (interface);
  END Exports;

PROCEDURE RevealsTo (interface: TEXT): TextList.T =
  BEGIN
    RETURN revealsTo.getValue (interface);
  END RevealsTo;

PROCEDURE RevealsType (type: TEXT): TextList.T =
  BEGIN
    RETURN revealsType.getValue (type);
  END RevealsType;

PROCEDURE DefinesType (type: TEXT): TextList.T =
  BEGIN
    RETURN definesType.getValue (type);
  END DefinesType;

PROCEDURE DefinesProc (proc: TEXT): TextList.T =
  BEGIN
    RETURN definesProc.getValue (proc);
  END DefinesProc;

PROCEDURE Interface (nm: TEXT): TextList.T =
  BEGIN
    RETURN interface.getValue (nm);
  END Interface;

PROCEDURE Module (nm: TEXT): TextList.T =
  BEGIN
    RETURN module.getValue (nm);
  END Module;

PROCEDURE GenericIntf (nm: TEXT): TextList.T =
  BEGIN
    RETURN genericIntf.getValue (nm);
  END GenericIntf;

PROCEDURE GenericMod (nm: TEXT): TextList.T =
  BEGIN
    RETURN genericMod.getValue (nm);
  END GenericMod;
---------------------------------------------------------------- update ---

PROCEDURE DeleteUnit (path: TEXT) =
  VAR x := db.all_relations ();
  BEGIN
    WHILE (x # NIL) DO
      NARROW (x.head, TextDB.Relation).deleteValue (path);
      x := x.tail;
    END;
  END DeleteUnit;

PROCEDURE AddUnit (rd: Rd.T;  path: TEXT) =
  VAR lex := NEW (M3Scanner.Default).initFromRd (rd,
                                           skip_comments := TRUE,
                                           split_pragmas := FALSE);
  BEGIN
    NextToken (lex); (* skip the initial comment *)
    UpdateUnit (lex, path);
  END AddUnit;

PROCEDURE UpdateUnit (lex: M3Scanner.T;  path: TEXT) =
  VAR unit: TEXT;
  BEGIN
    IF lex.token = M3Scanner.TK_Unsafe THEN NextToken (lex); END;

    IF lex.token = M3Scanner.TK_Generic THEN
      NextToken (lex); (*GENERIC*)
      IF lex.token = M3Scanner.TK_Interface THEN
        NextToken (lex); (*INTERFACE*)
        IF NOT GetID (lex, unit) THEN RETURN; END;
        genericIntf.insert (unit, path);
        SkipToSemi (lex);
        UpdateImports (lex, path);
        UpdateDecls (lex, path, TRUE);

      ELSIF lex.token = M3Scanner.TK_Module THEN
        NextToken (lex); (*MODULE*)
        IF NOT GetID (lex, unit) THEN RETURN; END;
        genericMod.insert (unit, path);
        SkipToSemi (lex);
        UpdateImports (lex, path);
        UpdateDecls (lex, path, FALSE);

      ELSE (* error *)
        RETURN;
      END;

    ELSIF lex.token = M3Scanner.TK_Interface THEN
      NextToken (lex); (*INTERFACE*)
      IF NOT GetID (lex, unit) THEN RETURN; END;
      interface.insert (unit, path);
      IF lex.token = M3Scanner.TK_Semi THEN
        NextToken (lex); (* ; *)
        UpdateImports (lex, path);
        UpdateDecls (lex, path, TRUE);
      ELSIF lex.token = M3Scanner.TK_Equal THEN
        NextToken (lex); (* = *)
        UpdateGenericInstance (lex, path);
      ELSE RETURN;
      END;

    ELSIF lex.token = M3Scanner.TK_Module THEN
      NextToken (lex); (*MODULE*)
      IF NOT GetID (lex, unit) THEN RETURN; END;
      module.insert (unit, path);
      UpdateExports (lex, unit, path);
      IF lex.token = M3Scanner.TK_Semi THEN
        NextToken (lex); (* ; *)
        UpdateImports (lex, path);
        UpdateDecls (lex, path, FALSE);
      ELSIF lex.token = M3Scanner.TK_Equal THEN
        NextToken (lex); (* = *)
        UpdateGenericInstance (lex, path);
      ELSE RETURN;
      END;

    ELSE (* error *)
      RETURN;
    END;
  END UpdateUnit;

PROCEDURE UpdateExports (lex: M3Scanner.T;  unit, path: TEXT) =
  VAR id: TEXT;
  BEGIN
    IF lex.token # M3Scanner.TK_Exports THEN
      exports.insert (unit, path);
    ELSE
      NextToken (lex); (*EXPORTS*)
      LOOP
        IF NOT GetID (lex, id) THEN EXIT END;
        exports.insert (id, path);
        IF lex.token # M3Scanner.TK_Comma THEN EXIT END;
        NextToken (lex); (* , *)
      END;
    END;
  END UpdateExports;

PROCEDURE UpdateImports (lex: M3Scanner.T;  path: TEXT) =
  VAR id: TEXT;
  BEGIN
    LOOP
      IF lex.token = M3Scanner.TK_Import THEN
        NextToken (lex); (*IMPORT*)
        LOOP
          IF NOT GetID (lex, id) THEN EXIT END;
          imports.insert (id, path);
          IF lex.token = M3Scanner.TK_As THEN
            NextToken (lex); (*IMPORT*)
            IF lex.token = M3Scanner.TK_Ident THEN
              NextToken (lex); (*ID*)
            END;
          END;
          IF lex.token # M3Scanner.TK_Comma THEN EXIT END;
          NextToken (lex); (* , *)
        END;
      ELSIF lex.token = M3Scanner.TK_From THEN
        NextToken (lex); (*FROM*)
        IF GetID (lex, id) THEN imports.insert (id, path); END;
      ELSE EXIT;
      END;
      SkipToSemi (lex);
    END;
  END UpdateImports;

PROCEDURE UpdateGenericInstance (lex: M3Scanner.T;  path: TEXT) =
  VAR id: TEXT;
  BEGIN
    IF NOT GetID (lex, id) THEN RETURN END;
    imports.insert (id, path);
    IF lex.token # M3Scanner.TK_L_paren THEN RETURN END;
    NextToken (lex); (* ( *)
    LOOP
      IF NOT GetID (lex, id) THEN EXIT END;
      imports.insert (id, path);
      IF lex.token # M3Scanner.TK_Comma THEN EXIT END;
      NextToken (lex); (* , *)
    END;
  END UpdateGenericInstance;

PROCEDURE UpdateDecls (lex: M3Scanner.T;  path: TEXT;  is_intf: BOOLEAN) =
  VAR id, unit: TEXT;  eq: BOOLEAN;
  BEGIN
    LOOP
      CASE lex.token OF
      | M3Scanner.TK_Type =>
          NextToken (lex); (*TYPE*)
          WHILE FindTypeID (lex, id, unit, eq) DO
            definesType.insert (id, path);
          END;

      | M3Scanner.TK_Procedure =>
          NextToken (lex); (*PROCEDURE*)
          IF is_intf THEN
            SkipToSemi (lex);
          ELSE
            IF GetID (lex, id) THEN
              definesProc.insert (id, path);
              SkipProc (lex, id);
            END;
          END;

      | M3Scanner.TK_Reveal =>
          NextToken (lex); (*REVEALS*)
          WHILE FindTypeID (lex, id, unit, eq) DO
            IF eq THEN
              IF (unit # NIL) THEN revealsTo.insert (unit, path); END;
              revealsType.insert (id, path);
            END;
          END;

      | M3Scanner.TK_EOF, M3Scanner.TK_Error => EXIT;
      ELSE NextToken (lex);
      END;
    END;
  END UpdateDecls;

PROCEDURE FindTypeID (lex: M3Scanner.T;  VAR id, unit: TEXT;
                      VAR eq: BOOLEAN): BOOLEAN =
  BEGIN
    LOOP
      id := NIL;
      unit := NIL;
      CASE lex.token OF
      | M3Scanner.TK_Ident =>
          EVAL GetID (lex, id);
          IF lex.token = M3Scanner.TK_Dot THEN
            NextToken (lex); (* . *)
            IF lex.token = M3Scanner.TK_Ident THEN
              unit := id;
              EVAL GetID (lex, id);
            END;
          END;
          IF lex.token = M3Scanner.TK_Equal THEN
            NextToken (lex); (* = *)
            eq := TRUE;
            RETURN TRUE;
          ELSIF lex.token = M3Scanner.TK_Subtype THEN
            NextToken (lex); (* <: *)
            eq := FALSE;
            RETURN TRUE;
          ELSE (* skip *)
          END;

      | M3Scanner.TK_L_paren =>
          SkipParens (lex);

      | M3Scanner.TK_Const, M3Scanner.TK_Type, M3Scanner.TK_Exception, M3Scanner.TK_Var, M3Scanner.TK_Procedure, M3Scanner.TK_Reveal,
        M3Scanner.TK_Begin, M3Scanner.TK_EOF, M3Scanner.TK_Error =>
          RETURN FALSE;

      ELSE
          NextToken (lex);
      END; (*CASE*)
    END; (*LOOP*)
  END FindTypeID;

PROCEDURE SkipParens (lex: M3Scanner.T) =
  VAR depth: INTEGER := 0;
  BEGIN
    LOOP
      IF lex.token = M3Scanner.TK_L_paren THEN
        INC (depth);
      ELSIF lex.token = M3Scanner.TK_R_paren THEN
        DEC (depth);
        IF (depth <= 0) THEN NextToken (lex); RETURN END;
      ELSIF lex.token = M3Scanner.TK_EOF THEN
        RETURN;
      ELSIF lex.token = M3Scanner.TK_Error THEN
        RETURN;
      END;
      NextToken (lex);
    END;
  END SkipParens;

PROCEDURE SkipProc (lex: M3Scanner.T;  proc_id: TEXT) =
  VAR id: TEXT;
  BEGIN
    LOOP
      IF lex.token = M3Scanner.TK_End THEN
        NextToken (lex); (*END*)
        IF GetID (lex, id) AND Text.Equal (proc_id, id) THEN EXIT; END;
      ELSIF lex.token = M3Scanner.TK_EOF OR lex.token = M3Scanner.TK_Error THEN
        EXIT;
      ELSE
        NextToken (lex);
      END;
    END;
  END SkipProc;

PROCEDURE GetID (lex: M3Scanner.T;  VAR id: TEXT): BOOLEAN =
  BEGIN
    IF lex.token # M3Scanner.TK_Ident THEN  id := NIL; RETURN FALSE;  END;
    id := Text.FromChars (SUBARRAY(lex.buffer^, lex.offset,lex.length));
    NextToken (lex);
    RETURN TRUE;
  END GetID;

PROCEDURE SkipToSemi (lex: M3Scanner.T) =
  BEGIN
    WHILE (lex.token # M3Scanner.TK_Semi)
      AND (lex.token # M3Scanner.TK_EOF)
      AND (lex.token # M3Scanner.TK_Error) DO
      NextToken (lex);
    END;
    IF (lex.token = M3Scanner.TK_Semi) THEN NextToken (lex); END;
  END SkipToSemi;

PROCEDURE NextToken (lex: M3Scanner.T) =
  BEGIN
    REPEAT
      lex.next ();
    UNTIL (lex.token # M3Scanner.TK_Begin_pragma);
  END NextToken;

BEGIN
END M3DB.

interface M3Scanner is in: