m3front/src/stmts/DebugStmt.m3


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

MODULE DebugStmt;

IMPORT CG, Expr, Token, Scanner, Stmt, StmtRep, Error, M3RT;
IMPORT Host, EnumExpr, Type, Bool, Target, TInt, ErrType;
IMPORT Textt, Procedure, NarrowExpr, Module, AssignStmt, RunTyme;

TYPE
  P = Stmt.T OBJECT
        cond   : Expr.T;
        n_msgs : INTEGER;
        msgs   : REF ARRAY OF Expr.T;
      OVERRIDES
        check       := Check;
        compile     := Compile;
        outcomes    := GetOutcome;
      END;

PROCEDURE Parse (): Stmt.T =
  VAR p: P;
  BEGIN
    p := NEW (P);
    StmtRep.Init (p);
    Scanner.Match (Token.T.tDEBUG);
    p.cond   := NIL;
    p.n_msgs := 0;
    p.msgs   := NEW (REF ARRAY OF Expr.T, 4);
    WHILE (Scanner.cur.token # Token.T.tENDPRAGMA) DO
      IF (p.n_msgs >= NUMBER (p.msgs^)) THEN ExpandMsgs (p); END;
      p.msgs[p.n_msgs] := Expr.Parse ();
      INC (p.n_msgs);
      IF (p.n_msgs = 1) THEN
        IF Scanner.cur.token = Token.T.tWITH THEN
          p.cond := p.msgs[0];  p.n_msgs := 0;
        ELSIF Scanner.cur.token # Token.T.tCOMMA THEN
          EXIT;
        END;
      ELSE
        IF Scanner.cur.token # Token.T.tCOMMA THEN EXIT; END;
      END;
      Scanner.GetToken ();  (* "," or "WITH" *)
    END;
    IF (Scanner.cur.token # Token.T.tENDPRAGMA) THEN
      Scanner.Fail ("missing \'*>\'");
    END;
    Scanner.cur.token := Token.T.tSEMI;  (* for the statement parser *)
    RETURN p;
  END Parse;

PROCEDURE ExpandMsgs (p: P) =
  VAR n := NUMBER (p.msgs^);  new := NEW (REF ARRAY OF Expr.T, n+n);
  BEGIN
    SUBARRAY (new^, 0, n) := p.msgs^;
    p.msgs := new;
  END ExpandMsgs;

PROCEDURE Check (p: P;  VAR cs: Stmt.CheckState) =
  VAR t: Type.T;  shift := FALSE;
  BEGIN
    IF (p.cond # NIL) THEN
      Expr.TypeCheck (p.cond, cs);
      t := Type.Base (Expr.TypeOf (p.cond));
      IF (t # Bool.T) AND (t # ErrType.T) THEN
        Error.Msg ("ASSERT condition must be a BOOLEAN");
      END;
    END;

    FOR i := 0 TO p.n_msgs-1 DO
      Expr.TypeCheck (p.msgs[i], cs);
      t := Type.Base (Expr.TypeOf (p.msgs[i]));
      IF (i = 0) AND (p.cond = NIL) AND (t = Bool.T) THEN
        (* the first "msg" is really the condition to test *)
        shift := TRUE;
      ELSIF Type.IsAssignable (Textt.T, t) THEN
        p.msgs[i] := CheckArg (Textt.T, t, p.msgs[i], cs);
      ELSE
        Error.Msg ("DEBUG message must be assignable to TEXT");
      END;
    END;

    IF (shift) THEN
      p.cond := p.msgs[0];
      FOR i := 0 TO p.n_msgs-1 DO
        p.msgs[i] := p.msgs[i+1];
      END;
      DEC (p.n_msgs);
    END;
  END Check;

PROCEDURE CheckArg (tlhs, trhs: Type.T;  e: Expr.T;
                    VAR cs: Stmt.CheckState): Expr.T =
  BEGIN
    AssignStmt.Check (tlhs, e, cs);
    IF Host.doNarrowChk AND NOT Type.IsSubtype (trhs, tlhs) THEN
      e := NarrowExpr.New (e, tlhs);
      Expr.TypeCheck (e, cs);
    END;
    RETURN e;
  END CheckArg;

PROCEDURE Compile (p: P): Stmt.Outcomes =
  VAR v: Expr.T;  i: Target.Int;  u: Type.T;   skip: CG.Label;
  BEGIN
    IF Host.doDebugs THEN
      i := TInt.MOne;
      v := Expr.ConstValue (p.cond);
      IF (p.cond = NIL) THEN
        (* DEBUG with no test condition *)
        PrepMsgs (p);
        EmitDebug (p);
      ELSIF (v = NIL) THEN
        (* DEBUG with non-constant test condition *)
        skip := CG.Next_label ();
        PrepMsgs (p);
        Expr.PrepBranch (p.cond, CG.No_label, skip, CG.Always);
        Expr.CompileBranch (p.cond, CG.No_label, skip, CG.Always);
        EmitDebug (p);
        CG.Set_label (skip);
      ELSIF EnumExpr.Split (v, i, u) AND TInt.EQ (i, TInt.Zero) THEN
        (* DEBUG (FALSE) *)
      ELSE <* ASSERT TInt.EQ (i, TInt.One) *>
        (* DEBUG (TRUE) *)
        PrepMsgs (p);
        EmitDebug (p);
      END;
    END;
    RETURN Stmt.Outcomes {Stmt.Outcome.FallThrough};
  END Compile;

PROCEDURE PrepMsgs (p: P) =
  BEGIN
    FOR i := 0 TO p.n_msgs-1 DO Expr.Prep (p.msgs[i]); END;
  END PrepMsgs;

PROCEDURE EmitDebug (p: P) =
  VAR
    proc      : Procedure.T;
    this_file : TEXT;
    this_line : INTEGER;
    msgs      := CG.Declare_temp (Target.Address.pack + Target.Integer.pack
                                    + p.n_msgs*Target.Address.pack,
                                  Target.Address.align, CG.Type.Struct,
                                  in_memory := TRUE);
    offset: INTEGER;
  BEGIN
    Scanner.Here (this_file, this_line);

    (* initialize the open-array pointer to the msgs *)
    CG.Load_addr_of (msgs, M3RT.OA_size_1, Target.Address.align);
    CG.Store_addr (msgs, M3RT.OA_elt_ptr);

    (* initialize the count of array sizes *)
    CG.Load_intt (p.n_msgs);
    CG.Store_int (Target.Integer.cg_type, msgs, M3RT.OA_size_0);

    (* initialize each message *)
    offset := M3RT.OA_size_1;
    FOR i := 0 TO p.n_msgs-1 DO
      Expr.Compile (p.msgs[i]);
      CG.Store_addr (msgs, offset);
      INC (offset, Target.Address.pack);
    END;

    proc := RunTyme.LookUpProc (RunTyme.Hook.DebugMsg);
    Procedure.StartCall (proc);
    IF Target.DefaultCall.args_left_to_right THEN
      (* module data pointer *)
        CG.Load_addr_of (Module.GlobalData (FALSE), 0, CG.Max_alignment);
        CG.Pop_param (CG.Type.Addr);
      (* line number *)
        CG.Load_intt (this_line);
        CG.Pop_param (Target.Integer.cg_type);
      (* messages *)
        CG.Load_addr_of (msgs, 0, Target.Address.align);
        CG.Pop_param (CG.Type.Addr);
    ELSE
      (* messages *)
        CG.Load_addr_of (msgs, 0, Target.Address.align);
        CG.Pop_param (CG.Type.Addr);
      (* line number *)
        CG.Load_intt (this_line);
        CG.Pop_param (Target.Integer.cg_type);
      (* module data pointer *)
        CG.Load_addr_of (Module.GlobalData (FALSE), 0, CG.Max_alignment);
        CG.Pop_param (CG.Type.Addr);
    END;
    Procedure.EmitCall (proc);
    CG.Free_temp (msgs);
  END EmitDebug;

PROCEDURE GetOutcome (<*UNUSED*> p: P): Stmt.Outcomes =
  BEGIN
    RETURN Stmt.Outcomes {Stmt.Outcome.FallThrough};
  END GetOutcome;

BEGIN
END DebugStmt.

interface Token is in:


interface Type is in: