home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / modu1096.zip / sample / tautology.mod < prev    next >
Text File  |  1995-03-29  |  25KB  |  936 lines

  1. (****************************************************************)
  2. (*                                *)
  3. (*     (c) copyright 1990 Faculty of Information Technology    *)
  4. (*        Queensland University of Technology        *)
  5. (*                                *)
  6. (*     Permission is granted to use, copy and change this    *)
  7. (*     program as long as the copyright message is left intact    *)
  8. (*                                *)
  9. (****************************************************************)
  10.  
  11. MODULE Tautology;
  12.  
  13. IMPORT Storage;
  14. IMPORT Terminal;
  15. IMPORT GenSequenceSupport;
  16. FROM Terminal IMPORT WriteString, WriteLn, Write;
  17.  
  18.   CONST EOL = 012C;
  19.         nul = 0C;
  20.         bs  = 010C;
  21.         del = 177C;
  22.  
  23.   TYPE SymbolType = (idSy, andSy, check, evSy, fSy, input, notSy,
  24.             eqSy, orSy, quit, tSy, lPar, rPar, errSy, endSy);
  25.  
  26.   VAR  errors : BOOLEAN;
  27.  
  28.  
  29.   PROCEDURE Error(s : ARRAY OF CHAR);
  30.     VAR i : CARDINAL;
  31.   BEGIN
  32.     (* only give pointer for first call *)
  33.     IF NOT errors THEN
  34.      TermSkip; 
  35.      FOR i := 1 TO pos DO Write(' ') END;
  36.       Write('^'); WriteLn;
  37.     END;
  38.     WriteString(s); WriteLn;
  39.     errors := TRUE;
  40.   END Error;
  41.  
  42. (*******************************************************************)
  43. (* This module contains all the input/output to the console        *)
  44. (* It keeps track of positions in the input for the benefit of the *)
  45. (* procedure Error, and defines the output line type.              *)
  46. (*******************************************************************)
  47.  
  48. MODULE IOHandler;
  49. IMPORT Terminal, EOL, nul, bs, del,
  50.        WriteString, WriteLn, Write;
  51. EXPORT    GetCh, Line,
  52.     TermSkip, ch, pos, lnMx;
  53.  
  54.   CONST lnMx = 79;
  55.   TYPE  Line = ARRAY [0..lnMx] OF CHAR;
  56.   VAR   ch   : CHAR;
  57.         pos  : CARDINAL;
  58.     inputLine: Line;
  59.     lineLength: CARDINAL;
  60.  
  61.   PROCEDURE GetLine;
  62.     VAR index : CARDINAL;
  63.         inChr : CHAR;
  64.   BEGIN
  65.     index := 0;
  66.     Terminal.Read(inChr);
  67.     WHILE (inChr <> EOL) AND (index < lnMx) DO
  68. (*
  69.    new code follows
  70. *)
  71.       inputLine[index] := inChr;
  72.       INC(index);
  73.       Terminal.Read(inChr);
  74.     END;
  75.     inputLine[index] := nul;
  76.     pos := 0;
  77. (*
  78.    UNIX does the echo for us, so delete all this ...
  79.  
  80.       IF inChr <> bs THEN
  81.         inputLine[index] := inChr;
  82.         Write(inChr);
  83.         INC(index);
  84.       ELSIF index > 0 THEN
  85.         Write(del);
  86.         DEC(index);
  87.       END;
  88.       Terminal.Read(inChr);
  89.     END;
  90.     inputLine[index] := nul;
  91.     WriteLn;
  92.     pos := 0;
  93. *)
  94.   END GetLine;
  95.  
  96.   PROCEDURE GetCh;
  97.   BEGIN
  98.     IF ch = nul THEN GetLine END;
  99.     ch:=inputLine[pos];
  100.     INC(pos);
  101.   END GetCh;
  102.  
  103.   PROCEDURE TermSkip;
  104.   (* this procedure corrects alignment of error messages    *)
  105.   BEGIN
  106.     WriteString("     ");
  107.   END TermSkip;
  108.  
  109. BEGIN
  110.   ch := nul;
  111.   pos := 0;
  112. END IOHandler;
  113.  
  114. (*******************************************************************)
  115. (* This module provides the symbol table facilities for the system *)
  116. (* and also performs string handling for the lexical scanner.       *)
  117. (* In order to offload the string matching task of the scanner, it *)
  118. (* needs to know about the Symbol type and their representations.  *)
  119. (*******************************************************************)
  120.  
  121. MODULE SymTab; (******** SYMBOL TABLE *********)
  122. IMPORT    Error, SymbolType;
  123. EXPORT    InitSymTab, InvalidateEntries, DescriptorIndex,
  124.     PushDescriptor, Descriptor, IdRange, Lookup,
  125.     top, eNumber, symtab;
  126.  
  127.   CONST  maxId = 8;
  128.  
  129.   TYPE     IdRange = [0..maxId];
  130.      Descriptor = RECORD
  131.             idRep : CHAR;
  132.             valid : BOOLEAN;
  133.             value : BOOLEAN;
  134.             columnPos : CARDINAL;
  135.               END;
  136.  
  137.   VAR    top    : IdRange;
  138.     topindex: CARDINAL;
  139.     symtab : ARRAY [0..maxId - 1] OF Descriptor;
  140.     strtab : ARRAY [0..47] OF CHAR;
  141.  
  142.   VAR    eNumber : CARDINAL; (* number of extra columns in header *)
  143.  
  144.     PROCEDURE Lookup(str : ARRAY OF CHAR;
  145.              VAR sy : SymbolType);
  146.  
  147.       PROCEDURE compare(index : CARDINAL) : BOOLEAN;
  148.     VAR i : CARDINAL;
  149.       BEGIN (* assert: both arrays have a blank before the end *)
  150.     i := 0;
  151.     WHILE (str[i] = strtab[index]) AND (str[i] <> ' ') DO
  152.       INC(i); INC(index);
  153.     END;
  154.     RETURN (str[i] = strtab[index]);
  155.       END compare;
  156.  
  157.     BEGIN (* lookup *)
  158.     (* In this case (and quite by accident) the first *)
  159.     (* character of each string is a perfect hash     *)
  160.     (* index for the set of possible word symbols.    *)
  161.       sy := errSy;
  162.       CASE str[0] OF
  163.     'A' : IF compare(0)  THEN sy := andSy END;
  164.       | 'C' : IF compare(4)  THEN sy := check END;
  165.       | 'E' : IF compare(10) THEN sy := evSy  END;
  166.       | 'F' : IF compare(19) THEN sy := fSy   END;
  167.       | 'I' : IF compare(25) THEN sy := input END;
  168.       | 'N' : IF compare(31) THEN sy := notSy END;
  169.       | 'O' : IF compare(35) THEN sy := orSy  END;
  170.       | 'Q' : IF compare(38) THEN sy := quit  END;
  171.       | 'T' : IF compare(43) THEN sy := tSy   END
  172.       ELSE
  173.       END;
  174.     END Lookup;
  175.  
  176.     PROCEDURE PushDescriptor(ch : CHAR);
  177.     BEGIN
  178.       IF top >= maxId THEN
  179.     Error('Too Many Identifiers');
  180.       ELSE
  181.     WITH symtab[top] DO
  182.       idRep := ch;
  183.       valid := FALSE;
  184.     END;
  185.     INC(top)
  186.       END
  187.     END PushDescriptor;
  188.  
  189.     PROCEDURE DescriptorIndex(ch : CHAR) : IdRange;
  190.       VAR I : IdRange;
  191.     BEGIN
  192.       FOR I := 0 TO top-1 DO
  193.     IF symtab[I].idRep = ch THEN RETURN I END;
  194.       END; (* if not found then return top *)
  195.       RETURN top;
  196.     END DescriptorIndex;
  197.  
  198.     PROCEDURE InvalidateEntries;
  199.       VAR I : IdRange;
  200.     BEGIN
  201.       FOR I := 2 TO maxId - 1 DO symtab[I].valid := FALSE END
  202.     END InvalidateEntries;
  203.  
  204.     PROCEDURE InitSymTab;
  205.     BEGIN
  206.       top := 2;
  207.     END InitSymTab;
  208.  
  209. BEGIN (* insert static values, these are never changed *)
  210.   WITH symtab[0] DO
  211.     idRep := 'F';
  212.     valid := TRUE;
  213.     value := FALSE;
  214.   END;
  215.   WITH symtab[1] DO
  216.     idRep := 'T';
  217.     valid := TRUE;
  218.     value := TRUE;
  219.   END;
  220.   FOR topindex := 2 TO maxId - 1 DO
  221.     top:=topindex;
  222.     symtab[top].columnPos := top * 2 - 2
  223.   END;
  224.   strtab := 'AND CHECK EVALUATE FALSE INPUT NOT OR QUIT TRUE ';
  225. END SymTab;
  226.  
  227. (**********************************************************************)
  228. (* Module HeaderHandler creates and manipulates the output formats    *)
  229. (* and lines which are required for the truth tables. These procs.    *)
  230. (* are mainly used by the tree builder procedures.              *)
  231. (* The system builds a line with column markers as soon as the number *)
  232. (* of variables is known. Later analysis of the syntax tree determ-   *)
  233. (* ines how many extra columns are required and trims the line length *)
  234. (**********************************************************************)
  235.  
  236. MODULE HeaderHandler;
  237.  
  238.   IMPORT Descriptor, top, symtab, WriteLn, WriteString, Line, lnMx, nul;
  239.   EXPORT InitHeader, TrimLine, WriteHeader, WriteLowEdge,
  240.      blank, InsertInHeader;
  241.  
  242.   VAR    topEdge, lowEdge, midEdge, blank, header : Line;
  243.  
  244.   PROCEDURE InsertInHeader(str : ARRAY OF CHAR; col : CARDINAL);
  245.     VAR I : CARDINAL;
  246.   BEGIN
  247.     FOR I := 0 TO HIGH(str) DO header[col + I] := str[I] END
  248.   END InsertInHeader;
  249.  
  250.   PROCEDURE TrimLine(max : CARDINAL);
  251.   BEGIN
  252.     blank[max]   := nul; header[max]  := nul;
  253.     topEdge[max] := nul; midEdge[max] := nul; lowEdge[max] := nul;
  254.     DEC(max);
  255.     blank[max]   := '|'; header[max]  := '|';
  256.     topEdge[max] := '+'; midEdge[max] := '+'; lowEdge[max] := '+';
  257.   END TrimLine;
  258.  
  259.   PROCEDURE InitHeader;
  260.     VAR I : CARDINAL;
  261.   BEGIN
  262.     blank[0]   := '|'; header[0] := '|';
  263.     topEdge[0] := '+'; midEdge[0] := '+'; lowEdge[0] := '+';
  264.     FOR I := 1 TO lnMx DO
  265.       blank[I]   := ' '; header[I] := ' ';
  266.       topEdge[I] := '-'; midEdge[I] := '-'; lowEdge[I] := '-'
  267.     END;
  268.     FOR I := 2 TO top - 1 DO
  269.       WITH symtab[I] DO
  270.     header[columnPos] := idRep;
  271.     blank[columnPos]  := '*';
  272.       END;
  273.     END;
  274.     I := 2 * (top -1);
  275.     topEdge[I] := '+'; midEdge[I] := '+'; lowEdge[I] := '+';
  276.     blank[I] := '|'; header[I] := '|';
  277.     FOR I := top * 2 TO lnMx BY 4 DO blank[I] := '*' END;
  278.   END InitHeader;
  279.  
  280.   PROCEDURE WriteLowEdge;
  281.   BEGIN
  282.     WriteString(lowEdge); WriteLn;
  283.   END WriteLowEdge;
  284.  
  285.   PROCEDURE WriteHeader;
  286.   BEGIN
  287.     WriteLn;
  288.     WriteString(topEdge); WriteLn;
  289.     WriteString(header);  WriteLn;
  290.     WriteString(midEdge);  WriteLn;
  291.   END WriteHeader;
  292.  
  293. END HeaderHandler;
  294.  
  295. (*******************************************************************)
  296. (* This is the lexical scanner. Pretty straightforward. Uses the   *)
  297. (* Symbol Table module to do most of the tricky work. It contains  *)
  298. (* an attribute lexValue for idSy's which is an index into the     *)
  299. (* descriptor table, so that all other attributes may be obtained  *)
  300. (* at tree-building time and at evaluation time.           *)
  301. (*******************************************************************)
  302.  
  303.   MODULE Scanner;
  304.   IMPORT ch, GetCh, Write, Lookup, top,
  305.      PushDescriptor, DescriptorIndex, IdRange,
  306.      SymbolType, Error, nul;
  307.   EXPORT symbol, lexValue, GetSymbol, InitScanner;
  308.  
  309.     VAR  symbol : SymbolType;
  310.  
  311.     VAR lexValue : IdRange;
  312.  
  313.     PROCEDURE IsAlpha(ch : CHAR) : BOOLEAN;
  314.     BEGIN RETURN (ch >= 'A') AND (ch <= 'Z') END IsAlpha;
  315.  
  316.     (* The precondition of the GetSymbol procedure is that *)
  317.     (* the current character does not belong to the last   *)
  318.     (* symbol. Note that this does not match the post-     *)
  319.     (* condition in the case that the last symbol was an   *)
  320.     (* endSy symbol. Logically endSy is the string end,    *)
  321.     (* and it is necessary to call InitScanner to start on *)
  322.     (* the scanning of the next string of input symbols.   *)
  323.  
  324.     PROCEDURE GetSymbol;
  325.       CONST max = 9; (* maximum symbol length + 1 *)
  326.       VAR   old : CHAR;
  327.         str : ARRAY [0..max] OF CHAR;
  328.  
  329.       PROCEDURE StringRecognize;
  330.     VAR pos : [0..max];
  331.       BEGIN
  332.     pos := 1;
  333.     WHILE IsAlpha(CAP(ch)) AND (pos < max) DO
  334.       str[pos] := CAP(ch); INC(pos); GetCh;
  335.     END;
  336.     str[pos] := ' ';
  337.     Lookup(str,symbol);
  338.     IF (pos = max) OR (symbol = errSy) THEN
  339.       Error('Invalid word');
  340.     END;
  341.       END StringRecognize;
  342.  
  343.     BEGIN
  344.       WHILE ch = ' ' DO GetCh END;
  345.       IF ch = nul THEN symbol := endSy
  346.       ELSE
  347.     old := ch; GetCh;
  348.     CASE old OF
  349.       '(' : symbol := lPar;
  350.     | ')' : symbol := rPar;
  351.     | '=' : symbol := eqSy;
  352.     | 'a'..'z', 'A'..'Z' :
  353.         IF IsAlpha(CAP(ch)) THEN
  354.           str[0] := CAP(old);
  355.           StringRecognize;
  356.         ELSE (* is isolated alpha. char. *)
  357.           symbol := idSy; old := CAP(old);
  358.           IF DescriptorIndex(old) = top THEN
  359.             PushDescriptor(old)
  360.           END;
  361.           lexValue := DescriptorIndex(old);
  362.         END
  363.     ELSE Error('Invalid character');
  364.     END;
  365.     (* assert : either symbol = endSy or current ch
  366.             is past last of symbol.         *)
  367.       END;
  368.     END GetSymbol;
  369.  
  370.     PROCEDURE InitScanner;
  371.     BEGIN
  372.       GetCh;
  373.       GetSymbol;
  374.     END InitScanner;
  375.  
  376.   END Scanner;
  377.  
  378. (*******************************************************************)
  379. (* This module implements the abstract syntax tree form of the       *)
  380. (* permissible expressions. The tree builder is intertwined with   *)
  381. (* the recursive descent parser.                   *)
  382. (*******************************************************************)
  383.  
  384.   MODULE TreeSystem;
  385.  
  386.     IMPORT (* local symbols *)
  387.        SymbolType, Error, errors,
  388.        (* from IOHandler *)
  389.        Write, WriteString, WriteLn, Line,
  390.        (* from HeaderHandler *)
  391.        InitHeader, TrimLine, blank, InsertInHeader,
  392.        WriteHeader, WriteLowEdge,
  393.        (* from Scanner *)
  394.        symbol, lexValue, GetSymbol, InitScanner,
  395.        (* from SymbolTable *)
  396.        symtab, top, IdRange, Descriptor, InitSymTab,
  397.        InvalidateEntries;
  398.  
  399.     FROM GenSequenceSupport IMPORT
  400.         Sequence, ElemPtr, Ended, InitSequence, DisposeList,
  401.         LinkLeft, LinkRight, InitCursor, GetFirst, GetNext;
  402.  
  403.     FROM Storage IMPORT ALLOCATE, DEALLOCATE;
  404.  
  405.     EXPORT Parse, TreeExists, Check, Evaluate;
  406.  
  407.     TYPE TagType = (conjunction, disjunction, equality,
  408.              negation, atom);
  409.  
  410. (* The abstract syntax of the tree, in IDL is given by --
  411.  
  412.     structure Boolexpr root EXPR is
  413.     EXPR ::= conjunction | disjunction | equality
  414.          | negation | atom;
  415.     equality =>           -- lhs "equals" rhs
  416.         asLHS : EXPR,
  417.         asRHS : EXPR,
  418.         lxColumn : CARDINAL; -- pos. of column in truth table
  419.         lxName     : String;    -- column header
  420.     conjunction =>        -- a sequence of ANDs
  421.         asTerms  : seq of EXPR,
  422.         lxName     : String;    -- column header
  423.         lxColumn : CARDINAL;
  424.     disjunction =>        -- a sequence of ORs
  425.         asFactors : seq of EXPR,
  426.         lxName      : String;   -- column header
  427.         lxColumn  : CARDINAL;
  428.     negation =>
  429.         asExp     : EXPR,
  430.         lxName     : String;    -- column header
  431.         lxColumn : CARDINAL;
  432.     atom => lxName    : CHAR,   -- accessed via the descriptor index
  433.         smValue : BOOLEAN;   -- accessed via the descriptor
  434.         lxName    : String;     -- usually not needed
  435.     end. -- of IDL description.
  436. *)
  437.      TYPE String3 = ARRAY[0..2] OF CHAR;
  438.      Expr = POINTER TO Node;
  439.      Node = RECORD
  440.           lxName : String3;
  441.           column : CARDINAL; (* 0 => not allocated *)
  442.           CASE tag : TagType OF
  443.             conjunction, disjunction :
  444.               seq  : Sequence; (* of Expr *)
  445.           | equality :
  446.               lhs, rhs : Expr;
  447.           | negation :
  448.               exp  : Expr;
  449.           | atom : desc : IdRange;
  450.           END;
  451.         END;
  452.  
  453.     VAR  root : Expr;
  454.     VAR  colSequence : Sequence; (* nodes in column order *)
  455.  
  456.     PROCEDURE TreeExists() : BOOLEAN;
  457.     BEGIN RETURN root <> NIL END TreeExists;
  458.  
  459.     PROCEDURE Create(VAR ptr : Expr; t : TagType);
  460.     BEGIN
  461. (*
  462.       ALLOCATE(ptr,SIZE(ptr^));
  463. *)
  464.       NEW(ptr);
  465.       ptr^.tag := t;
  466.       ptr^.lxName := '   ';
  467.       ptr^.column := 0;
  468.     END Create;
  469.  
  470.     PROCEDURE DisposeTree;
  471.  
  472.       PROCEDURE Release(p : Expr);
  473.     VAR cursor : ElemPtr;
  474.         next   : Expr;
  475.       BEGIN
  476.     CASE p^.tag OF
  477.       atom :
  478.     | negation :
  479.         Release(p^.exp);
  480.     | equality :
  481.         Release(p^.lhs);
  482.         Release(p^.rhs);
  483.     | conjunction, disjunction :
  484.         InitCursor(p^.seq,cursor);
  485.         WHILE NOT Ended(cursor) DO
  486.           GetNext(cursor,next);
  487.           Release(next);
  488.         END;
  489.         DisposeList(p^.seq);
  490.     END;
  491. (*
  492.         DEALLOCATE(p,SIZE(p^));
  493. *)
  494.         DISPOSE(p);
  495.       END Release;
  496.  
  497.     BEGIN
  498.       errors := FALSE;
  499.       IF root <> NIL THEN
  500.     Release(root);
  501.     root := NIL;
  502.       END;
  503.       DisposeList(colSequence);
  504.     END DisposeTree;
  505.  
  506.   (* The key idea of the following procedure is to walk the      *)
  507.   (* syntax tree breadth-first, and then to allocate column      *)
  508.   (* positions in the truth table to the nodes in the reverse      *)
  509.   (* order to that in which they were visited. This ensures that  *)
  510.   (* the value of the subexpression in any column can only depend *)
  511.   (* on the value of other columns to that column's left.         *)
  512.  
  513.   PROCEDURE AllocateColumns;
  514.  
  515.       MODULE Queue;
  516.  
  517.       (*-----------------------------------------------*)
  518.       (* Note that these are dynamic modules, i.e. are *)
  519.       (* nested inside a procedure. When the procedure *)
  520.       (* returns the variables are lost, and when the  *)
  521.       (* procedure is called the init. code is run.    *)
  522.       (*-----------------------------------------------*)
  523.  
  524.       IMPORT Expr, colSequence, LinkLeft, WriteString;
  525.       EXPORT Push, Next;
  526.       VAR arr : ARRAY[0..15] OF Expr;
  527.       sp, mk : CARDINAL;
  528.  
  529.     PROCEDURE Push(p : Expr);  (* no overflow check   *)
  530.     BEGIN               (* is made here. Maybe *)
  531.       arr[sp] := p; INC(sp);   (* with a single line  *)
  532.       LinkLeft(colSequence,p); (* input 16 is enough? *)
  533.     END Push;
  534.  
  535.     PROCEDURE Next(VAR p : Expr);
  536.     BEGIN
  537.       IF mk < sp THEN
  538.         p := arr[mk]; INC(mk);
  539.       ELSE p := NIL
  540.       END
  541.     END Next;
  542.  
  543.       BEGIN
  544.     sp := 0;
  545.     mk := 0;
  546.       END Queue;
  547.  
  548.     (*--------------------------------------------------*)
  549.     (* this module generates unique subexpression names *)
  550.     (*--------------------------------------------------*)
  551.  
  552.       MODULE Names;
  553.       IMPORT String3, WriteString;
  554.       EXPORT PopName;
  555.       VAR  name : String3;
  556.  
  557.     PROCEDURE PopName(VAR str : String3);
  558.     BEGIN
  559.       str := name;
  560.       name[2] := CHR(ORD(name[2]) + 1);
  561.     END PopName;
  562.  
  563.       BEGIN
  564.     name := 'ex1';
  565.       END Names;
  566.  
  567.     (*--------------------------------------------------*)
  568.  
  569.     (* local variables of AllocateColumns. *)
  570.       VAR cursor : ElemPtr;
  571.       n, p     : Expr;
  572.  
  573.       PROCEDURE InsertNamesAndNumbers;
  574.     VAR crsr : ElemPtr;
  575.         col  : CARDINAL;
  576.         node : Expr;
  577.       BEGIN
  578.     col := top * 2;
  579.     InitCursor(colSequence,crsr);
  580.     WHILE NOT Ended(crsr) DO
  581.       GetNext(crsr,node);
  582.       WITH node^ DO
  583.         IF lxName[0] = ' ' THEN (* is unnamed *)
  584.           PopName(lxName);
  585.         END;
  586.         (* and in any case ... *)
  587.         InsertInHeader(lxName,col-1);
  588.         column := col; INC(col,4);
  589.       END;
  590.     END;
  591.     TrimLine(col-1);
  592.       END InsertNamesAndNumbers;
  593.  
  594.     BEGIN (* allocate columns *)
  595.       InitSequence(colSequence);
  596.       Push(root); root^.lxName := 'res';
  597.       Next(n); (* queue discipline gives breadth first search *)
  598.       WHILE n <> NIL DO
  599.     WITH n^ DO
  600.       CASE tag OF
  601.         equality : (* always push lhs & rhs *)
  602.           Push(rhs); Push(lhs);
  603.           rhs^.lxName := 'rhs'; lhs^.lxName := 'lhs';
  604.       | conjunction, disjunction :
  605.           InitCursor(seq,cursor);
  606.           WHILE NOT Ended(cursor) DO
  607.         GetNext(cursor,p); (* don't push atoms *)
  608.         IF p^.tag <> atom THEN Push(p) ELSE END;
  609.           END;
  610.       | negation :
  611.           IF exp^.tag <> atom THEN Push(exp) ELSE END;
  612.       | atom : (* nothing *)
  613.       END;
  614.     END;
  615.     Next(n);
  616.       END;
  617.       (* nodes are in breadth-first order in colSeq. *)
  618.       InsertNamesAndNumbers;
  619.     END AllocateColumns;
  620.  
  621.      PROCEDURE WriteTree(p : Expr);
  622.       VAR exp     : Expr;
  623.       cursor : ElemPtr;
  624.       op     : ARRAY [0..4] OF CHAR;
  625.     BEGIN
  626.       CASE p^.tag OF
  627.     atom : Write(symtab[p^.desc].idRep);
  628.       | negation : WriteString('not ');
  629.            WriteTree(p^.exp);
  630.       | equality : WriteTree(p^.lhs);
  631.            WriteString(' = ');
  632.            WriteTree(p^.rhs);
  633.       | conjunction, disjunction :
  634.            IF p^.tag = disjunction
  635.              THEN op := ' or ';
  636.              ELSE op := ' and '
  637.            END;
  638.            Write('(');
  639.            GetFirst(p^.seq,cursor,exp);
  640.            WriteTree(exp);
  641.            WHILE NOT Ended(cursor) DO
  642.              WriteString(op);
  643.              GetNext(cursor,exp);
  644.              WriteTree(exp)
  645.            END;
  646.            Write(')');
  647.       END; (* case *)
  648.     END WriteTree;
  649.  
  650.     PROCEDURE WalkSubTree(p : Expr);
  651.       VAR exp     : Expr;
  652.       cursor : ElemPtr;
  653.       op     : ARRAY [0..4] OF CHAR;
  654.  
  655.       PROCEDURE WriteName(p : Expr);
  656.       BEGIN
  657.     IF p^.lxName[0] = ' ' THEN WalkSubTree(p);
  658.     ELSE WriteString(p^.lxName);
  659.     END;
  660.       END WriteName;
  661.  
  662.     BEGIN
  663.       CASE p^.tag OF
  664.     atom : Write(symtab[p^.desc].idRep);
  665.       | negation : WriteString('not ');
  666.            WriteName(p^.exp);
  667.       | equality : WriteName(p^.lhs);
  668.            WriteString(' = ');
  669.            WriteName(p^.rhs);
  670.       | conjunction, disjunction :
  671.            IF p^.tag = disjunction
  672.              THEN op := ' or ';
  673.              ELSE op := ' and '
  674.            END;
  675.            Write('(');
  676.            GetFirst(p^.seq,cursor,exp);
  677.            WriteName(exp);
  678.            WHILE NOT Ended(cursor) DO
  679.              WriteString(op);
  680.              GetNext(cursor,exp);
  681.              WriteName(exp);
  682.            END;
  683.            Write(')');
  684.       END;
  685.     END WalkSubTree;
  686.  
  687.     PROCEDURE WriteLegend;
  688.       VAR curs : ElemPtr;
  689.       node : Expr;
  690.     BEGIN
  691.       WriteLn;
  692.       WriteTree(root);
  693.       WriteLn;
  694.       WriteString('Legend --'); WriteLn;
  695.       InitCursor(colSequence,curs);
  696.       WHILE NOT Ended(curs) DO
  697.     GetNext(curs,node); Write(' ');
  698.     WriteString(node^.lxName);
  699.     WriteString(' == ');
  700.     WalkSubTree(node);
  701.     WriteLn;
  702.       END;
  703.     END WriteLegend;
  704.  
  705.     (* level-0 variables for use by Evaluate and Check *)
  706.     VAR  values   : Line;
  707.      constRep : ARRAY BOOLEAN OF CHAR;
  708.  
  709.     PROCEDURE NodeValue(p : Expr) : BOOLEAN;
  710.       VAR node     : Expr;
  711.       cursor : ElemPtr;
  712.       result : BOOLEAN;
  713.  
  714.           PROCEDURE PromptInput(d : IdRange) : BOOLEAN;
  715.         VAR v : CHAR;
  716.           BEGIN (* assert: value is not valid *)
  717.         WITH symtab[d] DO
  718.           REPEAT (* until valid *)
  719.             Write(idRep); Write('?'); Write(' ');
  720.             InitScanner;
  721.             CASE symbol OF
  722.               tSy  : valid := TRUE; value := TRUE;
  723.             | fSy  : valid := TRUE; value := FALSE;
  724.             | idSy : valid := symtab[lexValue].valid;
  725.                  value := symtab[lexValue].value;
  726.             ELSE (* nothing *)
  727.             END;
  728.           UNTIL valid;
  729.           values[columnPos] := constRep[value];
  730.           RETURN value;
  731.         END (* with *)
  732.           END PromptInput;
  733.  
  734. (* Body of NodeValue appears on the following page  *)
  735. (* It is the main tree evaluation routine, and is   *)
  736. (* called once by Evaluate, and repeatedly by Check *)
  737.      BEGIN (* NodeValue *)
  738.       WITH p^ DO
  739.     CASE tag OF
  740.       atom       : IF symtab[desc].valid THEN
  741.                result := symtab[desc].value
  742.              ELSE result := PromptInput(desc);
  743.              END;
  744.     | negation : result := NOT NodeValue(exp);
  745.     | equality : result := NodeValue(lhs) = NodeValue(rhs);
  746.     | conjunction :
  747.         GetFirst(seq,cursor,node);
  748.         result := NodeValue(node);
  749.         WHILE NOT Ended(cursor) AND result DO
  750.           GetNext(cursor,node);
  751.           result := NodeValue(node);
  752.         END; (* short circuit evaluation ! *)
  753.     | disjunction :
  754.         GetFirst(seq,cursor,node);
  755.         result := NodeValue(node);
  756.         WHILE NOT Ended(cursor) AND NOT result DO
  757.           GetNext(cursor,node);
  758.           result := NodeValue(node);
  759.         END;
  760.     END; (* case *)
  761.     IF column <> 0 THEN values[column] := constRep[result] END;
  762.     RETURN result;
  763.       END (* with *)
  764.     END NodeValue;
  765.  
  766.     PROCEDURE Evaluate;
  767.       VAR dummy : BOOLEAN;
  768.     BEGIN
  769.       InvalidateEntries; values := blank;
  770.       dummy := NodeValue(root);
  771.       WriteHeader;
  772.       WriteString(values); WriteLn;
  773.       WriteLowEdge;
  774.       WriteLegend;
  775.     END Evaluate;
  776.  
  777.     PROCEDURE Check;
  778.       VAR dummy : BOOLEAN;
  779.       pos    : IdRange;
  780.       trick : RECORD CASE (* no tag *) : BOOLEAN OF
  781.             TRUE  : bits : BITSET;
  782.           | FALSE : card : CARDINAL;
  783.           END END; (* case and record *)
  784.     BEGIN
  785.       trick.bits := BITSET{}; (* i.e. all false *)
  786.       WriteHeader;
  787.       WHILE NOT(top IN trick.bits) DO (* always do once at least *)
  788.     values := blank;
  789.     FOR pos := 2 TO top-1 DO      (* never if no variables ! *)
  790.       WITH symtab[top + 1 - pos] DO     (* bit reverse order *)
  791.         value := pos IN trick.bits;
  792.         valid := TRUE;
  793.         values[columnPos] := constRep[value];
  794.       END;
  795.     END;
  796.     dummy := NodeValue(root);
  797.     WriteString(values); WriteLn;
  798.     INC(trick.card,4);
  799.       END;
  800.       WriteLowEdge; WriteLegend;
  801.     END Check;
  802.  
  803. (*************************************************************)
  804. (* Classical recursive descent parser. Procedures are nested *)
  805. (* within each other so that no difficulty with "forward"    *)
  806. (* arises even in mplementations which use a single pass.    *)
  807. (*************************************************************)
  808.  
  809.  
  810.     PROCEDURE Parse;
  811.       VAR p : Expr;
  812.  
  813.       PROCEDURE SimpleExpr(VAR r : Expr);
  814.     VAR t : Expr;
  815.  
  816.     PROCEDURE Term(VAR r : Expr);
  817.       VAR f : Expr;
  818.  
  819.       PROCEDURE Factor(VAR r : Expr);
  820.       BEGIN
  821.         CASE symbol OF
  822.           idSy :
  823.         Create(r,atom);
  824.         r^.desc := lexValue;
  825.         GetSymbol;
  826.         | fSy :
  827.         Create(r,atom);
  828.         r^.desc := 0;
  829.         GetSymbol;
  830.         | tSy :
  831.         Create(r,atom);
  832.         r^.desc := 1;
  833.         GetSymbol;
  834.         | notSy :
  835.         Create(r,negation);
  836.         GetSymbol;
  837.         Factor(r^.exp);
  838.         | lPar :
  839.         GetSymbol;
  840.         SimpleExpr(r);
  841.         IF symbol = rPar THEN GetSymbol
  842.         ELSE Error('Missing ")"')
  843.         END;
  844.         ELSE Error('Expected name or expression');
  845.         END
  846.         END Factor;
  847.  
  848.     BEGIN (* term *)
  849.       Factor(f);
  850.       IF symbol = andSy THEN
  851.         Create(r,conjunction);
  852.         InitSequence(r^.seq);
  853.         LinkLeft(r^.seq,f);
  854.         WHILE symbol = andSy DO
  855.           GetSymbol;
  856.           Factor(f);
  857.           LinkRight(r^.seq,f);
  858.         END;
  859.       ELSE r := f;
  860.       END;
  861.     END Term;
  862.  
  863.        BEGIN (* simple expression *)
  864.     Term(t);
  865.     IF symbol = orSy THEN
  866.       Create(r,disjunction);
  867.       InitSequence(r^.seq);
  868.       LinkLeft(r^.seq,t);
  869.       WHILE symbol = orSy DO
  870.         GetSymbol;
  871.         Term(t);
  872.         LinkRight(r^.seq,t);
  873.       END;
  874.     ELSE r := t;
  875.     END;
  876.       END SimpleExpr;
  877.  
  878.     BEGIN (* parse *)
  879.       WriteString("EXPR : ");
  880.       DisposeTree;
  881.       InitSymTab;
  882.       InitScanner;
  883.       IF symbol = endSy THEN InitScanner END; (* 1 retry only *)
  884.       SimpleExpr(p);
  885.       IF symbol = eqSy THEN
  886.     Create(root,equality); GetSymbol;
  887.     SimpleExpr(root^.rhs); root^.lhs := p;
  888.       ELSE root := p;
  889.       END;
  890.       IF symbol <> endSy THEN
  891.     Error('Extra symbols followed expression end.');
  892.     REPEAT GetSymbol UNTIL symbol = endSy;
  893.       END;
  894.       IF errors THEN root := NIL
  895.       ELSE
  896.     InitHeader;
  897.     AllocateColumns;
  898.       END;
  899.     END Parse;
  900.  
  901.   BEGIN (* initialization of module TreeSystem *)
  902.     root := NIL;
  903.     errors := FALSE;
  904.     InitSequence(colSequence);
  905.     constRep[TRUE] := '1'; constRep[FALSE] := '0';
  906.   END TreeSystem;
  907.  
  908. (*******************************************************************)
  909.  
  910. (*******************************************************************)
  911. (******************** Mainline code follows ************************)
  912. (*******************************************************************)
  913. BEGIN (* mainline *)
  914.   LOOP
  915.     WriteLn;
  916.     WriteString('OK > ');
  917.     InitScanner;
  918.     CASE symbol OF
  919.       quit  : EXIT;
  920.     | evSy  : IF TreeExists() THEN Evaluate
  921.           ELSE Error('No valid expression exists')
  922.           END;
  923.     | check : IF TreeExists() THEN Check
  924.           ELSE Error('No valid expression exists')
  925.           END;
  926.     | input : Parse;
  927.     ELSE Error('Input, evaluate, check or quit')
  928.     END;
  929.   END; (* loop *)
  930. END Tautology.
  931.  
  932. (*******************************************************************)
  933. (*******************************************************************)
  934. (*******************************************************************)
  935. 
  936.