ktoklib/src/BracedCode.m3


 Copyright (c) 2000 California Institute of Technology 
 All rights reserved. See the file COPYRIGHT for a full description. 
 $Id: BracedCode.m3,v 1.2 2001-09-19 15:03:34 wagner Exp $ 

MODULE BracedCode;
IMPORT CharCodes;
IMPORT CharRange;
IMPORT Rd, Thread;
IMPORT FileRdErr;
<* FATAL Rd.Failure, Thread.Alerted *>

PROCEDURE FindChar(rd: Rd.T; which: CharRange.T) RAISES {Rd.EndOfFile} =
  VAR
    inQuote: BOOLEAN := FALSE;
    braceLevel, commentLevel: INTEGER := 0;
    ch: CHAR;
  PROCEDURE CheckComment(char1, char2: CHAR; levelDelta: INTEGER)
    RAISES {Rd.EndOfFile} =
    VAR
      c: CHAR;
    BEGIN
      IF NOT inQuote THEN
        REPEAT
          c := Rd.GetChar(rd);
        UNTIL c # char1;
        IF c = char2 THEN
          commentLevel := MAX(commentLevel + levelDelta, 0);
        ELSE
          Rd.UnGetChar(rd);
        END;
      END;
    END CheckComment;
  BEGIN
    REPEAT
      ch := Rd.GetChar(rd);
      IF NOT inQuote AND braceLevel = 0 AND
        commentLevel = 0 AND ch IN which THEN
        braceLevel := -1;
      ELSE
        CASE ch OF
        | '\134' => EVAL Rd.GetChar(rd); (* \\ *)
        | '\042' => IF commentLevel=0 THEN inQuote := NOT inQuote; END;
        | '\047' => EVAL CharCodes.GetChar(rd); EVAL Rd.GetChar(rd); (* 'c' *)
        | '(' => CheckComment('(', '*', 1);
        | '*' => CheckComment('*', ')', -1);
        | '{' => IF NOT inQuote AND commentLevel = 0 THEN INC(braceLevel); END;
        | '}' => IF NOT inQuote AND commentLevel = 0 THEN DEC(braceLevel); END;
        ELSE
        END;
      END;
    UNTIL braceLevel < 0;
  END FindChar;

PROCEDURE Match(rd: Rd.T): TEXT =
  VAR
    oldPos, len: INTEGER;
    result: TEXT;
  BEGIN
    TRY
      oldPos := Rd.Index(rd);
      FindChar(rd, CharRange.T{'}'});
      len := Rd.Index(rd) - oldPos - 1;
      Rd.Seek(rd, oldPos);
      result := Rd.GetText(rd, len);
      EVAL Rd.GetChar(rd);
      RETURN result;
    EXCEPT
      Rd.EndOfFile =>
      Rd.Seek(rd, oldPos);
      FileRdErr.E(rd, "can't find closing '}'");
      RETURN NIL;
    END;
  END Match;

PROCEDURE GetAhead(rd: Rd.T): TEXT =
  VAR
    c: CHAR;
    oldPos := Rd.Index(rd);
  BEGIN
    TRY
      REPEAT
        c := Rd.GetChar(rd);
      UNTIL NOT c IN CharRange.WhiteSpace;
      IF c = '{' THEN
        RETURN Match(rd);
      END;
    EXCEPT
      Rd.EndOfFile =>
    END;
    Rd.Seek(rd, oldPos);
    RETURN "";
  END GetAhead;

BEGIN
END BracedCode.