-- File: ScriptShowImpl.mesa - last edit by
-- Karlton:	 2-Sep-82 14:21:31

DIRECTORY
  Ascii USING [CR, NUL, SP],
  Environment USING [Byte],
  Real USING [AppendReal],
  ScriptHash USING [AppendId, Handle, Hash],
  ScriptNode USING [Operator, QualifiedID, String],
  ScriptParse USING [Error],
  ScriptTree USING [Handle, RootNode, TreeHandle, Walk],
  Stream USING [Handle, PutBlock, PutChar],
  String USING [AppendLongDecimal];
  
ScriptShowImpl: PROGRAM
  IMPORTS Real, ScriptHash, ScriptParse, ScriptTree, Stream, String
  EXPORTS ScriptParse = {
  
  indentPerDepth: CARDINAL = 4;
  
  Nibble: TYPE = [0..15];
  HexChar: TYPE = MACHINE DEPENDENT RECORD [
    pad(0:0..7): Environment.Byte,
    left(0:8..11): Nibble,
    right(0:12..15): Nibble];

  Handle: TYPE = ScriptTree.Handle;
  Format: TYPE = {tree, script};
  Frame: TYPE = POINTER TO FrameObject;
  FrameObject: TYPE = RECORD [
    out: Stream.Handle,
    univ, id: ScriptHash.Handle,
    lastChar: CHARACTER,
    format: Format ← tree];
  
  ShowTree: PUBLIC PROCEDURE [
    out: Stream.Handle,
    univ, id: ScriptHash.Handle,
    tree: ScriptTree.TreeHandle] = {
    f: FrameObject ← [out, univ, id, Ascii.NUL, tree];
    ShowNode[@f, ScriptTree.RootNode[tree], 0]};
      
  Externalize: PUBLIC PROCEDURE [
    out: Stream.Handle,
    univ, id: ScriptHash.Handle,
    tree: ScriptTree.TreeHandle] = {
    f: FrameObject ← [out, univ, id, Ascii.NUL,  script];
    ShowString[@f, "Interscript/Reference/83 "L];
    ShowChar[@f, Ascii.CR];
    ShowNode[@f, ScriptTree.RootNode[tree], 0];
    ShowString[@f, "ENDSCRIPT"L];
    ShowChar[@f, Ascii.CR]};
      
  ShowSons: PROCEDURE [f: Frame, r: Handle, d: CARDINAL] = {
    son: Handle;
    son ← ScriptTree.Walk[r, down];
    WHILE son # NIL DO
      ShowNode[f, son, d + 1];
      son ← ScriptTree.Walk[son, right]
      ENDLOOP};
      
  NoSons: PROCEDURE [f: Frame, r: Handle] = {
    IF ScriptTree.Walk[r, down] # NIL THEN
      ERROR ScriptParse.Error[invalidTree, 0];
    ShowBreak[f]};
    
  GetSons: PROCEDURE [n: Handle, min, max: [0..3]]
    RETURNS [Handle, Handle, Handle] = {
    sons: ARRAY [0..4) OF Handle ← ALL[NIL];
    sons[0] ← ScriptTree.Walk[n, down];
    FOR i: CARDINAL IN [1..4) DO
      IF sons[i-1] = NIL THEN EXIT;
      sons[i] ← ScriptTree.Walk[sons[i-1], right];
      ENDLOOP;
    IF sons[max] # NIL THEN ERROR ScriptParse.Error[invalidTree, 0];
    IF sons[min - 1] = NIL THEN ERROR ScriptParse.Error[invalidTree, 0];
    RETURN[sons[0], sons[1], sons[2]]};
    
  ShowNode: PROCEDURE [f: Frame, n: Handle, d: CARDINAL] = {
    IF f.format = tree THEN
      THROUGH [0..d*indentPerDepth) DO ShowChar[f, Ascii.SP] ENDLOOP;
    ShowNodeContent[f, n, d]};
    
  ShowNodeContent: PROCEDURE [f: Frame, n: Handle, d: CARDINAL] = {
    first, second, third: Handle;
    WITH node: n.node SELECT FROM
      atom => {ShowId[f, node.atom, f.univ]; NoSons[f, n]};
      application => {
        ShowIds[f, node.ids, f.id];
	SELECT f.format FROM
	  script => {
	    ShowChar[f, '[]; ShowSons[f, n, d]; ShowChar[f, ']]; ShowBreak[f]};
	  tree => {ShowString[f, "[]"]; ShowBreak[f]; ShowSons[f, n, d]};
	  ENDCASE};
      boolean => {ShowBoolean[f, node.boolean]; NoSons[f, n]};
      choice => {
        [first, second, third] ← GetSons[n, 3, 3];
	SELECT f.format FROM
	  script => {
	    ShowChar[f, '(];
	    ShowNode[f, first, d + 1];
	    ShowChar[f, '|];
	    ShowNode[f, second, d + 1];
	    ShowChar[f, '|];
	    ShowNode[f, third, d + 1];
	    ShowChar[f, ')];
	    ShowBreak[f]};
	  tree => {
	    ShowString[f, "Selection"L];
	    ShowBreak[f];
	    ShowNode[f, first, d + 1];
	    ShowBreak[f];
	    ShowNode[f, second, d + 1];
	    ShowBreak[f];
	    ShowNode[f, third, d + 1];
	    ShowBreak[f]};
	  ENDCASE};
      dollar => {ShowIdLabel[f, node.label, f.univ, "$"L]; NoSons[f, n]};
      environment => {
        [first, second, ] ← GetSons[n, 2, 2];
	SELECT f.format FROM
	  script => {
	    ShowChar[f, '[];
	    ShowNode[f, first, d];
	    ShowChar[f, '|];
	    ShowNode[f, second, d];
	    ShowChar[f, ']];
	    ShowBreak[f]};
	  tree => {
	    ShowString[f, "Environment"L];
	    ShowBreak[f];
	    ShowNode[f, first, d];
	    ShowBreak[f];
	    ShowNode[f, second, d];
	    ShowBreak[f]};
	  ENDCASE};
      expression => {
        [first, second, ] ← GetSons[n, 1, 2];
        SELECT f.format FROM
	  script => {
	    IF second # NIL THEN ShowNode[f, first, d + 1];
	    ShowOperator[f, node.expression];
	    ShowNode[f, IF second = NIL THEN first ELSE second, d + 1];
	    ShowBreak[f]};
	  tree => {
	    ShowOperator[f, node.expression];
	    ShowBreak[f];
	    ShowNode[f, first, d + 1];
	    ShowBreak[f];
	    IF second # NIL THEN {ShowNode[f, second, d + 1]; ShowBreak[f]}};
	  ENDCASE};
      globalBind => {
	[first, , ] ← GetSons[n, 1, 1];
	ShowIds[f, node.lhs, IF node.univ THEN f.univ ELSE f.id];
	ShowString[f, " := "L];
	ShowNodeContent[f, first, d]};
      integer => {ShowInteger[f, node.integer]; NoSons[f, n]};
      links => {
        ShowString[f, "LINKS "L]; ShowId[f, node.label, f.id]; NoSons[f, n]};
      localBind => {
	[first, , ] ← GetSons[n, 1, 1];
	ShowIds[f, node.lhs, f.id];
	ShowString[f, " ← "L];
	ShowNodeContent[f, first, d]};
      node => {
        SELECT f.format FROM
	  script => {
	    ShowChar[f, '{];
	    ShowSons[f, n, d];
	    ShowChar[f, '}];
	    ShowChar[f, Ascii.CR]};
	  tree => {ShowString[f, "Node"L]; ShowBreak[f]; ShowSons[f, n, d]};
	  ENDCASE};
      percent => {ShowIdsLabel[f, node.ids, f.id, "%"L]; NoSons[f, n]};
      placeHolder => {
        IF f.format = tree THEN {ShowString[f, "Place Holder"L]; ShowBreak[f]};
	ShowSons[f, n, d]};
      qualifiedID => {ShowIds[f, node.ids, f.id]; NoSons[f, n]};
      quotedExpression => {
        SELECT f.format FROM
	  script => {
	    ShowChar[f, ''];
	    ShowSons[f, n, d];
	    ShowChar[f, ''];
	    ShowChar[f, Ascii.CR]};
	  tree => {
	    ShowString[f, "Quoted Expression"L]; ShowBreak[f]; ShowSons[f, n, d]};
	  ENDCASE};
      real => {ShowReal[f, node.real]; NoSons[f, n]};
      source => {
        ShowIds[f, node.ids, f.id]; ShowChar[f, '↑]; ShowBreak[f]; NoSons[f, n]};
      string => {ShowText[f, node.string]; NoSons[f, n]};
      target => {
        ShowIds[f, node.ids, f.id]; ShowChar[f, ':]; ShowBreak[f]; NoSons[f, n]};
      univApplication => {
        ShowId[f, node.atom, f.univ];
	SELECT f.format FROM
	  script => {
	    ShowChar[f, '[]; ShowSons[f, n, d]; ShowChar[f, ']]; ShowBreak[f]};
	  tree => {ShowString[f, "[]"]; ShowBreak[f]; ShowSons[f, n, d]};
	  ENDCASE};
      vector => {
        SELECT f.format FROM
	  script => {
	    ShowChar[f, '(]; ShowSons[f, n, d]; ShowChar[f, ')]; ShowBreak[f]};
	  tree => {ShowString[f, "Vector"L]; ShowBreak[f]; ShowSons[f, n, d]};
	  ENDCASE};
      ENDCASE => ERROR};
  
  ShowString: PROC [f: Frame, v: LONG STRING] = {
    f.out.PutBlock[[LOOPHOLE[@v.text], 0, v.length]];
    f.lastChar ← Ascii.NUL};
        
  ShowChar: PROC [f: Frame, v: CHARACTER] = INLINE {
    f.out.PutChar[v]; f.lastChar ← v};
        
  ShowBreak: PROC [f: Frame] = {
    char: CHARACTER = IF f.format = tree THEN Ascii.CR ELSE Ascii.SP;
    IF f.lastChar # char THEN ShowChar[f, char]};
      
  ShowHex: PROC [f: Frame, v: HexChar] = INLINE {
    ShowChar[f, 'A + v.left]; ShowChar[f, 'A + v.right]};
        
  ShowText: PROC [f: Frame, string: ScriptNode.String] = {
    ShowChar[f, '<];
    SELECT f.format FROM
      script => {
        IF string.allSimple THEN ShowString[f, string.string]
	ELSE {
	  OpenEscape: PROC = INLINE {
	    IF ~inEscape THEN {ShowChar[f, '=]; inEscape ← TRUE}};
	  CloseEscape: PROC = INLINE {
	    IF inEscape THEN {ShowChar[f, '=]; inEscape ← FALSE}};
	  set: CHARACTER ← 0C;
	  char: CHARACTER;
	  pos: CARDINAL;
	  inEscape: BOOLEAN ← FALSE;
	  WHILE pos < string.string.length DO
            SELECT (char ← string.string[pos]) FROM
              377C => {pos ← pos + 1; set ← string.string[pos]};
              ENDCASE => {
	        IF set # 0C THEN {
		  OpenEscape[];
		  ShowInteger[f, (set - 0C)*256 + (char - 0C)];
		  ShowChar[f, Ascii.SP]}
		ELSE SELECT char FROM
		  '=, '> => {OpenEscape[]; ShowHex[f, LOOPHOLE[char]]};
		  IN [40C..176C] => {CloseEscape[]; ShowChar[f, char]};
		  ENDCASE => {OpenEscape[]; ShowHex[f, LOOPHOLE[char]]}};
	    pos ← pos + 1;
	    ENDLOOP;
	    CloseEscape[]}};
      tree => {
        IF string.allSet0 THEN ShowString[f, string.string]
        ELSE {
          set: CHARACTER ← 0C;
          c: CHARACTER;
          pos: CARDINAL ← 0;
          WHILE pos < string.string.length DO
            SELECT (c ← string.string[pos]) FROM
              377C => {pos ← pos + 1; set ← string.string[pos]};
              ENDCASE => ShowChar[f, IF set = 0C THEN c ELSE 377C];
	    pos ← pos + 1;
	    ENDLOOP}};
      ENDCASE;
    ShowChar[f, '>]};
        
  ShowOperator: PROC [f: Frame, v: ScriptNode.Operator] = {
    ShowString[f, SELECT v FROM
      plus => "+ "L, minus => "- "L, divide => "/ "L, multiply => "* "L,
      ENDCASE => ERROR ScriptParse.Error[invalidTree, 0]]};
        
  ShowBoolean: PROC [f: Frame, v: BOOLEAN] = {
    SELECT v FROM
      TRUE => ShowString[f, IF f.format = script THEN "T"L ELSE "TRUE"L];
      FALSE => ShowString[f, IF f.format = script THEN "F"L ELSE "FALSE"L];
      ENDCASE};
        
  ShowInteger: PROC [f: Frame, v: LONG INTEGER] = {
    string: STRING = [20];
    String.AppendLongDecimal[string, v];
    ShowString[f, string]};
        
  ShowReal: PROC [f: Frame, v: REAL] = {
    string: STRING = [20];
    Real.AppendReal[s: string, r: v, forceE: TRUE];
    ShowString[f, string]};
        
  ShowId: PROC [f: Frame, hash: ScriptHash.Hash, table: ScriptHash.Handle] = {
    val: LONG STRING = [40];
    ScriptHash.AppendId[table, val, hash];
    ShowString[f, val]};
        
  ShowIdLabel: PROC [
    f: Frame, hash: ScriptHash.Hash, table: ScriptHash.Handle, label: STRING] = {
    IF f.format = tree THEN {ShowString[f, label]; ShowChar[f, Ascii.SP]};
    ShowId[f, hash, table];
    IF f.format = script THEN ShowString[f, label]};
        
  ShowIds: PROC [
    f: Frame, ids: ScriptNode.QualifiedID, table: ScriptHash.Handle] = {
    FOR i: CARDINAL IN [0..ids.length) DO
      IF i > 0 THEN ShowChar[f, '.]; ShowId[f, ids[i], table] ENDLOOP};
        
  ShowIdsLabel: PROC [
    f: Frame, ids: ScriptNode.QualifiedID, table: ScriptHash.Handle,
    label: STRING] = {
    IF f.format = tree THEN {ShowString[f, label]; ShowChar[f, Ascii.SP]};
    ShowIds[f, ids, table];
    IF f.format = script THEN ShowString[f, label]};
        
  }. -- of ScriptShowImpl