home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / TICALC.ZIP / TICALC.PAS
Encoding:
Pascal/Delphi Source File  |  1986-01-14  |  5.9 KB  |  283 lines

  1. (* This is a Texas Instruments style calculator.  It parses arithmetic
  2.    expressions using the usual precedence rules.  
  3.    Written by Jonathan Amsterdam, December 1984.
  4. *)
  5.    
  6. PROGRAM TICalc;
  7.  
  8.  
  9. CONST
  10.   endOfFile = 0;   (* special character signifying end of file *)
  11.   empty     = 127; (* character used to indicate that savedChar is empty *)
  12.   endOfLine = 13;  (* special character signifying end of line *)
  13.  
  14. TYPE 
  15.   nodetype = (binop, unop, number);
  16.   node = ^noderec;
  17.   noderec = RECORD
  18.           CASE tag:nodetype OF
  19.         binop: (operator:CHAR;
  20.             leftOperand, rightOperand:node);
  21.         unop: (uOperator:CHAR;
  22.                operand:node);
  23.         number:(num:INTEGER);
  24.         END;
  25.  
  26. VAR
  27.   savedChar: CHAR;
  28.   digits: SET OF CHAR;
  29.   
  30. (* input functions *)
  31.  
  32. FUNCTION getChar:CHAR;
  33. (* Useful low-level character input.  Returns special characters at
  34.    end of file and end of line. *)
  35. VAR c:CHAR;
  36. BEGIN
  37.   IF savedChar <> chr(empty) THEN BEGIN
  38.     getChar := savedChar;
  39.     savedChar := chr(empty);
  40.   END ELSE IF EOF THEN
  41.     getChar := chr(endOfFile)
  42.   ELSE IF EOLN THEN BEGIN
  43.     getChar := chr(endOfLine);
  44.     readln;
  45.   END ELSE BEGIN
  46.     read(c);
  47.     getChar := c;
  48.   END;
  49. END;
  50.  
  51.  
  52. PROCEDURE ungetChar(c:CHAR);
  53. (* Allows one character at a time to be pushed back on the input. *)
  54. BEGIN
  55.   IF savedChar = chr(empty) THEN
  56.     savedChar := c
  57.   ELSE
  58.     writeln('ungetChar: can''t unget more than one character at a time');
  59. END;
  60.  
  61.  
  62. FUNCTION nextChar:CHAR;
  63. (* Skips over blanks. *)
  64. VAR c:CHAR;
  65. BEGIN
  66.   REPEAT
  67.     c := getChar
  68.   UNTIL c <> ' ';
  69.   nextChar := c;
  70. END;
  71.  
  72.   
  73.  
  74. FUNCTION charToInt(c:CHAR):INTEGER;
  75. (* Converts a numeric character to an integer. *)
  76. BEGIN
  77.   IF NOT (c IN digits) THEN BEGIN
  78.     writeln('charToInt: ', c, 'is not a digit');
  79.     charToInt := 0;
  80.   END ELSE 
  81.     charToInt := ord(c) - ord('0');
  82. END;
  83.  
  84. FUNCTION getNum(c:CHAR):INTEGER;
  85. (* Reads a number from the input.  The first digit of the number has
  86.    already been read and is passed as an argument. *)
  87. VAR n:INTEGER;
  88. BEGIN
  89.   n := 0;
  90.   REPEAT
  91.     n := 10*n + charToInt(c);
  92.     c := getChar;
  93.   UNTIL NOT (c IN digits);
  94.   ungetChar(c);
  95.   getNum := n;
  96. END;
  97.  
  98.  
  99. (* node creation functions *)
  100. (* The following three functions create nodes for the parse tree.  The first
  101.    two each return NIL if their node arguments are NIL. *)
  102.    
  103. FUNCTION binopNode(opor:CHAR; lopand, ropand:node):node;
  104. VAR n: node;
  105. BEGIN
  106.   IF (lopand = NIL) OR (ropand = NIL) THEN 
  107.     binopNode := NIL
  108.   ELSE BEGIN
  109.     new(n, binop);
  110.     WITH n^ DO BEGIN
  111.       tag := binop;
  112.       operator := opor;
  113.       leftOperand := lopand;
  114.       rightOperand := ropand;
  115.     END;
  116.     binopNode := n;
  117.   END;
  118. END;
  119.  
  120.  
  121. FUNCTION unopNode(opor:CHAR; opand:node):node;
  122. VAR n:node;
  123. BEGIN
  124.   IF opand = NIL THEN
  125.     unopNode := NIL
  126.   ELSE BEGIN
  127.     new(n, unop);
  128.     WITH n^ DO BEGIN
  129.       tag := unop;
  130.       uOperator := opor;
  131.       operand := opand;
  132.     end;
  133.     unopNode := n;
  134.   END;
  135. END;
  136.  
  137. FUNCTION numberNode(i:INTEGER):node;
  138. VAR n:node;
  139. BEGIN
  140.   new(n, number);
  141.   WITH n^ DO BEGIN
  142.     tag := number;
  143.     num := i;
  144.   END;
  145.   numberNode := n;
  146. END;
  147.  
  148.  
  149. (* tree printing procedures *)
  150.  
  151. PROCEDURE ptree(n:node; depth:INTEGER);
  152. BEGIN
  153.   WITH n^ DO
  154.     CASE tag OF
  155.       binop: BEGIN
  156.            ptree(leftOperand, depth+2);
  157.            writeln(' ':depth,operator);
  158.            ptree(rightOperand, depth+2);
  159.          END;
  160.       unop: BEGIN
  161.           writeln(' ':depth,uoperator);
  162.           ptree(operand, depth+2);
  163.         END;
  164.       number: writeln(' ':depth,num);
  165.     END;
  166. END;
  167.  
  168.          
  169. PROCEDURE printTree(n:node);
  170. BEGIN
  171.   ptree(n, 0);
  172. END;
  173.  
  174.  
  175. (* parser *)
  176. (* Each of the three parsing functions returns NIL if an error occurs
  177.    in the parse. *)
  178.  
  179. FUNCTION term:node; FORWARD;
  180.  
  181. FUNCTION factor:node; FORWARD;
  182.  
  183. FUNCTION expr:node;
  184. (* An expression is either a term, or a term +,- an expression. *)
  185. VAR c:CHAR;
  186.     n:node;
  187. BEGIN
  188.   n := term;
  189.   expr := n;
  190.   IF n <> NIL THEN BEGIN
  191.     c := nextChar;
  192.     IF (c = '+') OR (c = '-') THEN
  193.       expr := binopNode(c, n, expr)
  194.     ELSE IF c <> chr(endOfLine) THEN
  195.       ungetChar(c);
  196.   END;
  197. END;
  198.  
  199.  
  200. FUNCTION term(*:node*);
  201. (* A term is either a factor, or a factor *,/ a term. *)
  202. VAR c:CHAR;
  203.     n:node;
  204. BEGIN
  205.   n := factor;
  206.   term := n;
  207.   IF n <> NIL THEN BEGIN
  208.     C := nextChar;
  209.     IF (c = '*') OR (c = '/') THEN
  210.       term := binopNode(c, n, term)
  211.     ELSE 
  212.       ungetChar(c);
  213.   END;
  214. END;
  215.  
  216. FUNCTION factor(*:node*);
  217. (* A factor is either a number, or a - followed by a factor, or
  218.    a parenthesized expression. *)
  219. VAR c:CHAR;
  220. BEGIN
  221.   c := nextChar;
  222.   IF c IN digits THEN
  223.     factor := numberNode(getNum(c))
  224.   ELSE IF c = '-' THEN
  225.     factor := unopNode(c, factor)
  226.   ELSE IF c = '(' THEN BEGIN
  227.     factor := expr;
  228.     IF nextChar <> ')' THEN
  229.       writeln('close parenthesis expected');
  230.   END ELSE BEGIN
  231.     writeln('illegal expression');
  232.     factor := NIL;
  233.   END;
  234. END;
  235.  
  236. FUNCTION eval(n:node):REAL;
  237. (* Evaluates a parse tree. Assumes that the only binary operations are
  238.    +, -, *, and / and that the only unary operation is -. *)
  239. VAR op1, op2:REAL;
  240. BEGIN
  241.   WITH n^ DO
  242.     CASE tag OF
  243.       binop: 
  244.     BEGIN
  245.       op1 := eval(leftOperand);
  246.       op2 := eval(rightOperand);
  247.       CASE operator OF
  248.         '+': eval := op1 + op2;
  249.         '-': eval := op1 - op2;
  250.         '*': eval := op1 * op2;
  251.         '/': eval := op1 / op2; 
  252.       END;
  253.     END;
  254.       unop: eval := -eval(operand);
  255.       number: eval := num;
  256.     END;
  257. END;
  258.  
  259. PROCEDURE run;
  260. VAR n:node;
  261.     c:CHAR;
  262. BEGIN
  263.   REPEAT
  264.     write('> ');
  265.     n := expr;
  266.     IF n <> NIL THEN BEGIN
  267.       writeln;
  268.       printTree(n);
  269.       writeln;
  270.       writeln(eval(n):0:2);
  271.     END;
  272.   UNTIL FALSE;
  273. END;
  274.   
  275.   
  276. BEGIN  (*** MAIN PROGRAM ***)
  277.   writeln('TI style calculator');
  278.   writeln('Enter an arithmetic expression and hit <RETURN>.');
  279.   writeln('I will print a parse tree and evaluate the expression.');
  280.   digits := ['0'..'9'];
  281.   run;
  282. END.
  283.