kext/src/ExtBody.m3


 Copyright (c) 2000 California Institute of Technology 
 All rights reserved. See the file COPYRIGHT for a full description. 
 $Id: ExtBody.m3.html,v 1.3 2010-04-29 17:18:48 wagner Exp $ 

MODULE ExtBody;
IMPORT FileRdErr;
IMPORT Scan;
IMPORT ExtSection;
IMPORT Override;
IMPORT BracedCode;
IMPORT CharRange;
IMPORT LoadSpec;
IMPORT TextSubs;
IMPORT TextReader;
IMPORT Pragma;
IMPORT PragmaRead;
IMPORT Text;
IMPORT TextTextTbl;
IMPORT Wr, TextWr;
IMPORT Rd, TextRd;
IMPORT Thread;
IMPORT Import;
IMPORT FloatMode, Lex;
IMPORT Fmt;
IMPORT InitGen;
IMPORT Term;

<* FATAL Rd.EndOfFile, Rd.Failure, Wr.Failure, Thread.Alerted *>
<* FATAL FloatMode.Trap, Lex.Error *>

TYPE
  Self = OBJECT
    secondPass: TextSubs.T;
    type, proc: Override.T;
    pragSubs: TextTextTbl.T;
    spec: LoadSpec.Info;
    typeExt := "";
  END;

  SelfPragma = Pragma.T OBJECT
    self: Self;
    key: TEXT;
  END;

PROCEDURE PrintAlloc(self: Self; body: TEXT; VAR cur: INTEGER; wr: Wr.T) =
  VAR
    rd := TextRd.New(body);
    start, len: INTEGER;
    name: TEXT;
  BEGIN
    (* hopefully only lexers use this *)
    Rd.Seek(rd, cur);
    WHILE Rd.GetChar(rd) IN CharRange.AlphaNum DO END; Rd.UnGetChar(rd);
    WHILE Rd.GetChar(rd) IN CharRange.WhiteSpace DO END; Rd.UnGetChar(rd);
    start := Rd.Index(rd);
    TRY
      WHILE Rd.GetChar(rd) IN CharRange.AlphaNum DO END; Rd.UnGetChar(rd);
    EXCEPT
      Rd.EndOfFile =>
    END;
    len := Rd.Index(rd) - start;
    name := Text.Sub(body, start, len);
    EVAL self.spec.allocTypes.put(name, NIL);
    Wr.PutText(wr, "VAR result:" & name & ":=NewPT(self.allocate_" & name &
      ",TYPECODE(" & name & "));BEGIN BEGIN\n    ");
    Wr.PutText(wr, FixTheBody(self, BracedCode.GetAhead(rd), 0, NIL));
    Wr.PutText(wr, "\n   END;RETURN result;END");
    cur := Rd.Index(rd);
  END PrintAlloc;

PROCEDURE FixTheBody(self: Self; body: TEXT; argCount: INTEGER;rd:Rd.T): TEXT =
  VAR
    argUsed := NEW(REF ARRAY OF BOOLEAN, argCount+1);
    cur, last, save: INTEGER := 0;
    c: CHAR;
    wr := TextWr.New();
    pre: TEXT := "";
  PROCEDURE DoVal() =
    BEGIN
      IF cur = Text.Length(body) OR Text.GetChar(body, cur) # '.' THEN
        Wr.PutText(wr, ".val");
      END;
    END DoVal;
  BEGIN
    FOR i := 1 TO LAST(argUsed^) DO argUsed[i] := FALSE; END;
    WHILE cur # Text.Length(body) DO
      cur := Text.FindChar(body, '$', last);
      IF cur = -1 THEN
        cur := Text.Length(body);
      END;
      Wr.PutText(wr, Text.Sub(body, last, cur-last));
      IF cur < Text.Length(body) THEN
        INC(cur);
        IF cur = Text.Length(body) THEN
          c := ' ';
        ELSE
          c := Text.GetChar(body, cur);
        END;
        CASE c OF
        | '1'..'9' =>
          save := cur;
          Wr.PutChar(wr, 'n');
          WHILE cur < Text.Length(body) AND
            Text.GetChar(body, cur) IN CharRange.Digit DO
            Wr.PutChar(wr, Text.GetChar(body, cur));
            INC(cur);
          END;
          save := Scan.Int(Text.Sub(body, save, cur-save));
          IF save > argCount THEN
            FileRdErr.E(rd, "parameter out of range: $" & Fmt.Int(save));
          END;
          argUsed[save] := TRUE;
          DoVal();
        | 'R' =>
          PrintAlloc(self, body, cur, wr);
        | '$' =>
          INC(cur);
          Wr.PutText(wr, "result");
          DoVal();
        ELSE
          Wr.PutText(wr, "self.getText()");
        END;
        last := cur;
      END;
    END;
    FOR i := 1 TO argCount DO
      IF NOT argUsed[i] THEN
        pre := pre & "EVAL n" & Fmt.Int(i) & ";";
      END;
    END;
    RETURN pre & TextWr.ToText(wr);
  END FixTheBody;

PROCEDURE ProcSubs(self: Self; form, body: TEXT; argCount: INTEGER;
                   rd: Rd.T): TEXT =
  VAR
    subs := NEW(TextSubs.T).init();
  BEGIN
    (*    Term.WrLn("ProcSubs: " & form & "/" & body); *)
    subs.add("\\\n", "");
    subs.add("%body", FixTheBody(self, body, argCount, rd));
    subs.add("%yaccName", self.spec.methMN);
    RETURN subs.apply(form);
  END ProcSubs;

PROCEDURE ParseProc(p: SelfPragma; rd: Rd.T) =
  VAR
    self := p.self;
    name, mn, bodyform := "";
    c: CHAR;
    frag: TEXT;
    argCount: INTEGER;
  BEGIN
    WHILE Rd.GetChar(rd) IN CharRange.WhiteSpace DO END;
    Rd.UnGetChar(rd);
    REPEAT
      c := Rd.GetChar(rd);
      IF c IN CharRange.AlphaNum THEN name := name & Text.FromChar(c);END;
    UNTIL NOT c IN CharRange.AlphaNum;
    IF c = '{' THEN Rd.UnGetChar(rd);END;
    frag := BracedCode.GetAhead(rd);
    IF c = ':' THEN
      IF Text.Length(frag) # 0 THEN
        EVAL self.spec.types.get(name, mn);
        self.type.add(name, "  " & name & " = " & mn & "." & name &
          " BRANDED \"" & self.spec.outMN & "." & name & "\"" &
          " OBJECT\n    " & frag & "\n  END;\n", rd);
        frag := InitGen.Get("\n    result.%name :=%val;",frag);
        IF self.spec.kind # 'y' THEN
          IF NOT Text.Equal(frag, "") THEN
            FileRdErr.E(rd,"Token fields cannot be automatically initialized");
          END;
        END;
        self.secondPass.add("(*%TYPEINIT%" & name & "%*)",frag);
      END;
      IF self.spec.kind#'l' THEN self.typeExt := "_" & name;END;
    ELSE
      IF Text.Length(frag) # 0 THEN
        name := name & self.typeExt;
        EVAL self.spec.procs.get(name, bodyform);
        argCount := 0;
        EVAL self.spec.argCount.get(name, argCount);
        self.proc.add(name, ProcSubs(self, bodyform, frag, argCount,rd), rd);
      END;
    END;
  END ParseProc;

PROCEDURE ExtraOver(self: Self) =
  VAR
    argCount: INTEGER;
    proc, body, type: TEXT;
    iter := self.spec.procs.iterate();
  BEGIN
    IF self.spec.kind = 'y' THEN
      WHILE iter.next(proc, body) DO
        IF NOT self.proc.overridden(proc) THEN
          EVAL self.spec.retType.get(proc, type);
          EVAL self.spec.argCount.get(proc, argCount);
          IF self.type.overridden(type) THEN
            self.proc.add(proc,
                          ProcSubs(self, body,
                                   "(* just allocating the new type *)",
                                   argCount, NIL), NIL);
          END;
        END;
      END;
    END;
  END ExtraOver;

PROCEDURE ParseSubs(p: SelfPragma; rd: Rd.T) =
  VAR
    prev: TEXT;
  BEGIN
    EVAL p.self.pragSubs.get(p.key, prev);
    EVAL p.self.pragSubs.put(p.key, prev & BracedCode.Match(rd));
    Rd.UnGetChar(rd);
    (* PragmaRead will eat the '}' *)
  END ParseSubs;

PROCEDURE Parse(from: Rd.T; READONLY spec: LoadSpec.Info): T =
  VAR
    prag := NEW(PragmaRead.T).init();
    subs := NEW(TextSubs.T).init();
    self := NEW(Self,
                secondPass := NEW(TextSubs.T).init(),
                type := NEW(Override.T).init(spec.types),
                proc := NEW(Override.T).init(spec.procs),
                spec := spec,
                pragSubs := NEW(TextTextTbl.Default).init());
    parseProc := NEW(SelfPragma, self := self, do := ParseProc);
    import := NEW(Import.T).init();
  PROCEDURE SubsPrags() =
    VAR
      pragma := ExtSection.GetText(spec.kind, ExtSection.T.Pragma);
      cur := NEW(TextReader.T).init(pragma).shatter("\t\n ","",TRUE);
      parseSubs: SelfPragma;
    BEGIN
      WHILE cur # NIL DO
        EVAL self.pragSubs.put(cur.head, "");
        parseSubs := NEW(SelfPragma, self := self,
                         key := cur.head, do := ParseSubs);
        prag.add(parseSubs, cur.head);
        cur := cur.tail;
      END;
    END SubsPrags;
  PROCEDURE PragSubs() =
    VAR
      iter := self.pragSubs.iterate();
      key, value: TEXT;
    BEGIN
      WHILE iter.next(key, value) DO
        subs.add(key, value);
      END;
    END PragSubs;
  PROCEDURE AllocFmt(form: TEXT): TEXT =
    VAR
      subs: TextSubs.T;
      iter := self.spec.allocTypes.iterate();
      name, dummy: TEXT;
      wr := TextWr.New();
    BEGIN
      WHILE iter.next(name, dummy) DO
        subs := NEW(TextSubs.T).init();
       (* subs.add("%tok", self.spec.tokMN); *)
        subs.add("%name", name);
        Wr.PutText(wr, subs.apply(form));
      END;
      RETURN TextWr.ToText(wr);
    END AllocFmt;
  BEGIN
    prag.add(parseProc, "%proc");
    prag.add(parseProc, "");
    SubsPrags();      (* add pragmas that substitute text *)
    prag.apply(from); (* read input *)
    PragSubs();       (* make substitutions collected by SubsPrags *)
    ExtraOver(self);  (* override remaining procs returning ext types *)
    subs.add("\\\n","");
    subs.add("%gen","(* generated by kext *)");
    self.type.importRemaining();
    subs.add("%gnTypes", self.type.getText());
    subs.add("%gnProcs", self.secondPass.apply(self.proc.getText()));
    subs.add("%ovr", self.proc.getProcAssignText());
    subs.add("%tok", spec.tokMN);
    subs.add("%name", spec.outMN);
    subs.add("%orig", spec.orig);
    subs.add("%tokOrig", spec.tokOrig);
    subs.add("%tkimp", subs.apply(ExtSection.Res("extform.tokimport.i3")));
    import.addModule(spec.tokMN);
    IF spec.methMN # NIL THEN
      import.addModule(spec.methMN);
      subs.add("%meth", spec.methMN);
    END;
    subs.add("%import", import.toDeclaration());
   subs.add("%alloc",AllocFmt("    allocate_%name: Allocator := NIL;\n"));
    subs.add("%purge",AllocFmt("\n      + Purge(self.allocate_%name)"));
    RETURN subs;
  END Parse;

BEGIN
END ExtBody.