home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / turbo55 / tp55 / tcparser.pas < prev    next >
Pascal/Delphi Source File  |  1989-05-02  |  18KB  |  677 lines

  1.  
  2. { Copyright (c) 1989 by Borland International, Inc. }
  3.  
  4. unit TCParser;
  5. { Turbo Pascal 5.5 object-oriented example parser.
  6.   This unit is used by TCALC.PAS.
  7.   See TCALC.DOC for an more information about this example.
  8. }
  9.  
  10. {$N+,S-}
  11.  
  12. interface
  13.  
  14. uses Crt, Dos, TCUtil, TCScreen, TCCell, TCCellSp, TCLStr;
  15.  
  16. const
  17.   ParserStackSize = 10;
  18.   MaxFuncNameLen = 5;
  19.   ExpLimit = 11356;
  20.   SqrLimit = 1E2466;
  21.   MaxExpLen = 4;
  22.   TotalErrors = 7;
  23.   ErrParserStack = 1;
  24.   ErrBadRange = 2;
  25.   ErrExpression = 3;
  26.   ErrOperator = 4;
  27.   ErrOpenParen = 5;
  28.   ErrCell = 6;
  29.   ErrOpCloseParen = 7;
  30.   ErrorMessages : array[1..TotalErrors] of String[33] =
  31.     ('Parser stack overflow', 'Bad cell range', 'Expected expression',
  32.      'Expected operator', 'Expected open paren', 'Expected cell',
  33.      'Expected operator or closed paren');
  34.  
  35. type
  36.   ErrorRange = 0..TotalErrors;
  37.   TokenTypes = (Plus, Minus, Times, Divide, Expo, Colon, OParen, CParen,
  38.                 Num, CellT, Func, EOL, Bad);
  39.   TokenRec = record
  40.     State : Byte;
  41.     case Byte of
  42.       0 : (Value : Extended);
  43.       1 : (CP : CellPos);
  44.       2 : (FuncName : String[MaxFuncNameLen]);
  45.   end;
  46.   ParserObj = object
  47.     Inp : LStringPtr;
  48.     ParserHash : CellHashTablePtr;
  49.     PMaxCols : Word;
  50.     PMaxRows : Word;
  51.     Position : Word;
  52.     CurrToken : TokenRec;
  53.     StackTop : 0..ParserStackSize;
  54.     TokenError : ErrorRange;
  55.     ParseError : Boolean;
  56.     CType : CellTypes;
  57.     ParseValue : Extended;
  58.     Stack : array[1..ParserStackSize] of TokenRec;
  59.     TokenType : TokenTypes;
  60.     TokenLen : Word;
  61.     MathError, IsFormula : Boolean;
  62.     constructor Init(InitHash : CellHashTablePtr; InitInp : LStringPtr;
  63.                      InitPMaxCols, InitPMaxRows : Word);
  64.     function IsFunc(S : String) : Boolean;
  65.     procedure Push(Token : TokenRec);
  66.     procedure Pop(var Token : TokenRec);
  67.     function GotoState(Production : Word) : Word;
  68.     procedure Shift(State : Word);
  69.     procedure Reduce(Reduction : Word);
  70.     function NextToken : TokenTypes;
  71.     procedure Parse;
  72.     function CellValue(P : CellPos) : Extended;
  73.   end;
  74.  
  75. var
  76.   Parser : ParserObj;
  77.  
  78. implementation
  79.  
  80. constructor ParserObj.Init(InitHash : CellHashTablePtr;
  81.                            InitInp : LStringPtr;
  82.                            InitPMaxCols, InitPMaxRows : Word);
  83. { Initializes the parser }
  84. begin
  85.   ParserHash := InitHash;
  86.   Inp := InitInp;
  87.   PMaxCols := InitPMaxCols;
  88.   PMaxRows := InitPMaxRows;
  89.   Position := 1;
  90.   StackTop := 0;
  91.   TokenError := 0;
  92.   MathError := False;
  93.   IsFormula := False;
  94.   ParseError := False;
  95. end; { ParserObj.Init }
  96.  
  97. function ParserObj.IsFunc(S : String) : Boolean;
  98. { Checks to see if the parser is about to read a function }
  99. var
  100.   Counter, SLen : Word;
  101. begin
  102.   with Inp^ do
  103.   begin
  104.     SLen := System.Length(S);
  105.     for Counter := 1 to System.Length(S) do
  106.     begin
  107.       if UpCase(Data^[Pred(Position + Counter)]) <> S[Counter] then
  108.       begin
  109.         IsFunc := False;
  110.         Exit;
  111.       end;
  112.     end;
  113.     CurrToken.FuncName := UpperCase(Copy(Position, SLen));
  114.     Inc(Position, SLen);
  115.     IsFunc := True;
  116.   end; { with }
  117. end; { IsFunc }
  118.  
  119. function ParserObj.NextToken : TokenTypes;
  120. { Gets the next Token from the Input stream }
  121. var
  122.   NumString : String[80];
  123.   FormLen, Place, TLen, NumLen, Check : Word;
  124.   Ch, FirstChar : Char;
  125.   Decimal : Boolean;
  126. begin
  127.   with Inp^ do
  128.   begin
  129.     while (Position <= Length) and (Data^[Position] = ' ') do
  130.       Inc(Position);
  131.     TokenLen := Position;
  132.     if Position > Length then
  133.     begin
  134.       NextToken := EOL;
  135.       TokenLen := 0;
  136.       Exit;
  137.     end;
  138.     Ch := UpCase(Data^[Position]);
  139.     if Ch in ['0'..'9', '.'] then
  140.     begin
  141.       NumString := '';
  142.       TLen := Position;
  143.       Decimal := False;
  144.       while (TLen <= Length) and
  145.             ((Data^[TLen] in ['0'..'9']) or
  146.              ((Data^[TLen] = '.') and (not Decimal))) do
  147.       begin
  148.         NumString := NumString + Data^[TLen];
  149.         if Ch = '.' then
  150.           Decimal := True;
  151.         Inc(TLen);
  152.       end;
  153.       if (TLen = 2) and (Ch = '.') then
  154.       begin
  155.         NextToken := BAD;
  156.         TokenLen := 0;
  157.         Exit;
  158.       end;
  159.       if (TLen <= Length) and ((Data^[TLen] = 'E') or
  160.                                (Data^[TLen] = 'e')) then
  161.       begin
  162.         NumString := NumString + 'E';
  163.         Inc(TLen);
  164.         if Data^[TLen] in ['+', '-'] then
  165.         begin
  166.           NumString := NumString + Data^[TLen];
  167.           Inc(TLen);
  168.         end;
  169.         NumLen := 1;
  170.         while (TLen <= Length) and (Data^[TLen] in ['0'..'9']) and
  171.               (NumLen <= MaxExpLen) do
  172.         begin
  173.           NumString := NumString + Data^[TLen];
  174.           Inc(NumLen);
  175.           Inc(TLen);
  176.         end;
  177.       end;
  178.       if NumString[1] = '.' then
  179.         NumString := '0' + NumString;
  180.       Val(NumString, CurrToken.Value, Check);
  181.       if Check <> 0 then
  182.         MathError := True;
  183.       NextToken := NUM;
  184.       Inc(Position, System.Length(NumString));
  185.       TokenLen := Position - TokenLen;
  186.       Exit;
  187.     end
  188.     else if Ch in Letters then
  189.     begin
  190.       if IsFunc('ABS') or
  191.          IsFunc('ATAN') or
  192.          IsFunc('COS') or
  193.          IsFunc('EXP') or
  194.          IsFunc('LN') or
  195.          IsFunc('ROUND') or
  196.          IsFunc('SIN') or
  197.          IsFunc('SQRT') or
  198.          IsFunc('SQR') or
  199.          IsFunc('TRUNC') then
  200.       begin
  201.         NextToken := FUNC;
  202.         TokenLen := Position - TokenLen;
  203.         Exit;
  204.       end;
  205.       if FormulaStart(Inp, Position, PMaxCols, PMaxRows, CurrToken.CP,
  206.                       FormLen) then
  207.       begin
  208.         Inc(Position, FormLen);
  209.         IsFormula := True;
  210.         NextToken := CELLT;
  211.         TokenLen := Position - TokenLen;
  212.         Exit;
  213.       end
  214.       else begin
  215.         NextToken := BAD;
  216.         TokenLen := 0;
  217.         Exit;
  218.       end;
  219.     end
  220.     else begin
  221.       case Ch of
  222.         '+' : NextToken := PLUS;
  223.         '-' : NextToken := MINUS;
  224.         '*' : NextToken := TIMES;
  225.         '/' : NextToken := DIVIDE;
  226.         '^' : NextToken := EXPO;
  227.         ':' : NextToken := COLON;
  228.         '(' : NextToken := OPAREN;
  229.         ')' : NextToken := CPAREN;
  230.         else begin
  231.           NextToken := BAD;
  232.           TokenLen := 0;
  233.           Exit;
  234.         end;
  235.       end;
  236.       Inc(Position);
  237.       TokenLen := Position - TokenLen;
  238.       Exit;
  239.     end; { case }
  240.   end; { with }
  241. end; { ParserObj.NextToken }
  242.  
  243. procedure ParserObj.Push(Token : TokenRec);
  244. { Pushes a new Token onto the stack }
  245. begin
  246.   if StackTop = ParserStackSize then
  247.     TokenError := ErrParserStack
  248.   else begin
  249.     Inc(StackTop);
  250.     Stack[StackTop] := Token;
  251.   end;
  252. end; { ParserObj.Push }
  253.  
  254. procedure ParserObj.Pop(var Token : TokenRec);
  255. { Pops the top Token off of the stack }
  256. begin
  257.   Token := Stack[StackTop];
  258.   Dec(StackTop);
  259. end; { ParserObj.Pop }
  260.  
  261. function ParserObj.GotoState(Production : Word) : Word;
  262. { Finds the new state based on the just-completed production and the
  263.    top state.
  264. }
  265. var
  266.   State : Word;
  267. begin
  268.   State := Stack[StackTop].State;
  269.   if (Production <= 3) then
  270.   begin
  271.     case State of
  272.       0 : GotoState := 1;
  273.       9 : GotoState := 19;
  274.       20 : GotoState := 28;
  275.     end; { case }
  276.   end
  277.   else if Production <= 6 then
  278.   begin
  279.     case State of
  280.       0, 9, 20 : GotoState := 2;
  281.       12 : GotoState := 21;
  282.       13 : GotoState := 22;
  283.     end; { case }
  284.   end
  285.   else if Production <= 8 then
  286.   begin
  287.     case State of
  288.       0, 9, 12, 13, 20 : GotoState := 3;
  289.       14 : GotoState := 23;
  290.       15 : GotoState := 24;
  291.       16 : GotoState := 25;
  292.     end; { case }
  293.   end
  294.   else if Production <= 10 then
  295.   begin
  296.     case State of
  297.       0, 9, 12..16, 20 : GotoState := 4;
  298.     end; { case }
  299.   end
  300.   else if Production <= 12 then
  301.   begin
  302.     case State of
  303.       0, 9, 12..16, 20 : GotoState := 6;
  304.       5 : GotoState := 17;
  305.     end; { case }
  306.   end
  307.   else begin
  308.     case State of
  309.       0, 5, 9, 12..16, 20 : GotoState := 8;
  310.     end; { case }
  311.   end;
  312. end; { ParserObj.GotoState }
  313.  
  314. function ParserObj.CellValue(P : CellPos) : Extended;
  315. { Returns the value of a cell }
  316. var
  317.   CPtr : CellPtr;
  318. begin
  319.   CPtr := ParserHash^.Search(P);
  320.   with CPtr^ do
  321.   begin
  322.     if (not LegalValue) or HasError then
  323.     begin
  324.       MathError := True;
  325.       CellValue := 0;
  326.     end
  327.     else
  328.       CellValue := CurrValue;
  329.   end; { with }
  330. end; { ParserObj.CellValue }
  331.  
  332. procedure ParserObj.Shift(State : Word);
  333. { Shifts a Token onto the stack }
  334. begin
  335.   CurrToken.State := State;
  336.   Push(CurrToken);
  337.   TokenType := NextToken;
  338. end; { ParserObj.Shift }
  339.  
  340. procedure ParserObj.Reduce(Reduction : Word);
  341. { Completes a reduction }
  342. var
  343.   Token1, Token2 : TokenRec;
  344.   Counter : CellPos;
  345. begin
  346.   case Reduction of
  347.     1 : begin
  348.       Pop(Token1);
  349.       Pop(Token2);
  350.       Pop(Token2);
  351.       CurrToken.Value := Token1.Value + Token2.Value;
  352.     end;
  353.     2 : begin
  354.       Pop(Token1);
  355.       Pop(Token2);
  356.       Pop(Token2);
  357.       CurrToken.Value := Token2.Value - Token1.Value;
  358.     end;
  359.     4 : begin
  360.       Pop(Token1);
  361.       Pop(Token2);
  362.       Pop(Token2);
  363.       CurrToken.Value := Token1.Value * Token2.Value;
  364.     end;
  365.     5 : begin
  366.       Pop(Token1);
  367.       Pop(Token2);
  368.       Pop(Token2);
  369.       if Token1.Value = 0 then
  370.         MathError := True
  371.       else
  372.         CurrToken.Value := Token2.Value / Token1.Value;
  373.     end;
  374.     7 : begin
  375.       Pop(Token1);
  376.       Pop(Token2);
  377.       Pop(Token2);
  378.       if Token2.Value <= 0 then
  379.         MathError := True
  380.       else if (Token1.Value * Ln(Token2.Value) < -ExpLimit) or
  381.               (Token1.Value * Ln(Token2.Value) > ExpLimit) then
  382.         MathError := True
  383.       else
  384.         CurrToken.Value := Exp(Token1.Value * Ln(Token2.Value));
  385.     end;
  386.     9 : begin
  387.       Pop(Token1);
  388.       Pop(Token2);
  389.       CurrToken.Value := -Token1.Value;
  390.     end;
  391.     11 : begin
  392.       Pop(Token1);
  393.       Pop(Token2);
  394.       Pop(Token2);
  395.       CurrToken.Value := 0;
  396.       if Token1.CP.Row = Token2.CP.Row then
  397.       begin
  398.         if Token1.CP.Col < Token2.CP.Col then
  399.           TokenError := ErrBadRange
  400.         else begin
  401.           Counter.Row := Token1.CP.Row;
  402.           for Counter.Col := Token2.CP.Col to Token1.CP.Col do
  403.             CurrToken.Value := CurrToken.Value + CellValue(Counter);
  404.         end;
  405.       end
  406.       else if Token1.CP.Col = Token2.CP.Col then
  407.       begin
  408.         if Token1.CP.Row < Token2.CP.Row then
  409.           TokenError := ErrBadRange
  410.         else begin
  411.           Counter.Col := Token1.CP.Col;
  412.           for Counter.Row := Token2.CP.Row to Token1.CP.Row do
  413.             CurrToken.Value := CurrToken.Value + CellValue(Counter);
  414.         end;
  415.       end
  416.       else if (Token1.CP.Col >= Token2.CP.Col) and
  417.               (Token1.CP.Row >= Token2.CP.Row) then
  418.       begin
  419.         for Counter.Row := Token2.CP.Row to Token1.CP.Row do
  420.         begin
  421.           for Counter.Col := Token2.CP.Col to Token1.CP.Col do
  422.             CurrToken.Value := CurrToken.Value + CellValue(Counter);
  423.         end;
  424.       end
  425.       else
  426.         TokenError := ErrBadRange;
  427.     end;
  428.     13 : begin
  429.       Pop(CurrToken);
  430.       CurrToken.Value := CellValue(CurrToken.CP);
  431.     end;
  432.     14 : begin
  433.       Pop(Token1);
  434.       Pop(CurrToken);
  435.       Pop(Token1);
  436.     end;
  437.     16 : begin
  438.       Pop(Token1);
  439.       Pop(CurrToken);
  440.       Pop(Token1);
  441.       Pop(Token1);
  442.       if Token1.FuncName = 'ABS' then
  443.         CurrToken.Value := Abs(CurrToken.Value)
  444.       else if Token1.FuncName = 'ATAN' then
  445.         CurrToken.Value := ArcTan(CurrToken.Value)
  446.       else if Token1.FuncName = 'COS' then
  447.         CurrToken.Value := Cos(CurrToken.Value)
  448.       else if Token1.FuncName = 'EXP' then
  449.       begin
  450.         if (CurrToken.Value < -ExpLimit) or (CurrToken.Value > ExpLimit) then
  451.           MathError := True
  452.         else
  453.           CurrToken.Value := Exp(CurrToken.Value);
  454.       end
  455.       else if Token1.FuncName = 'LN' then
  456.       begin
  457.         if CurrToken.Value <= 0 then
  458.           MathError := True
  459.         else
  460.           CurrToken.Value := Ln(CurrToken.Value);
  461.       end
  462.       else if Token1.FuncName = 'ROUND' then
  463.       begin
  464.         if (CurrToken.Value < -1E9) or (CurrToken.Value > 1E9) then
  465.           MathError := True
  466.         else
  467.           CurrToken.Value := Round(CurrToken.Value);
  468.       end
  469.       else if Token1.FuncName = 'SIN' then
  470.         CurrToken.Value := Sin(CurrToken.Value)
  471.       else if Token1.FuncName = 'SQRT' then
  472.       begin
  473.         if CurrToken.Value < 0 then
  474.           MathError := True
  475.         else
  476.           CurrToken.Value := Sqrt(CurrToken.Value);
  477.       end
  478.       else if Token1.FuncName = 'SQR' then
  479.       begin
  480.         if (CurrToken.Value < -SQRLIMIT) or (CurrToken.Value > SQRLIMIT) then
  481.           MathError := True
  482.         else
  483.           CurrToken.Value := Sqr(CurrToken.Value);
  484.       end
  485.       else if Token1.FuncName = 'TRUNC' then
  486.       begin
  487.         if (CurrToken.Value < -1E9) or (CurrToken.Value > 1E9) then
  488.           MathError := True
  489.         else
  490.           CurrToken.Value := Trunc(CurrToken.Value);
  491.       end;
  492.     end;
  493.     3, 6, 8, 10, 12, 15 : Pop(CurrToken);
  494.   end; { case }
  495.   CurrToken.State := GotoState(Reduction);
  496.   Push(CurrToken);
  497. end; { ParserObj.Reduce }
  498.  
  499. procedure ParserObj.Parse;
  500. { Parses an input stream }
  501. var
  502.   FirstToken : TokenRec;
  503.   Accepted : Boolean;
  504. begin
  505.   Position := 1;
  506.   StackTop := 0;
  507.   TokenError := 0;
  508.   MathError := False;
  509.   IsFormula := False;
  510.   ParseError := False;
  511.   with Inp^ do
  512.   begin
  513.     if (Length = 2) and (Data^[1] = RepeatFirstChar) then
  514.     begin
  515.       CType := ClRepeat;
  516.       Exit;
  517.     end;
  518.     if Data^[1] = TextFirstChar then
  519.     begin
  520.       CType := ClText;
  521.       Exit;
  522.     end;
  523.   end; { with }
  524.   Accepted := False;
  525.   FirstToken.State := 0;
  526.   FirstToken.Value := 0;
  527.   Push(FirstToken);
  528.   TokenType := NextToken;
  529.   repeat
  530.     case Stack[StackTop].State of
  531.       0, 9, 12..16, 20 : begin
  532.         if TokenType = NUM then
  533.           Shift(10)
  534.         else if TokenType = CELLT then
  535.           Shift(7)
  536.         else if TokenType = FUNC then
  537.           Shift(11)
  538.         else if TokenType = MINUS then
  539.           Shift(5)
  540.         else if TokenType = OPAREN then
  541.           Shift(9)
  542.         else begin
  543.           TokenError := ErrExpression;
  544.           Dec(Position, TokenLen);
  545.         end;
  546.       end;
  547.       1 : begin
  548.         if TokenType = EOL then
  549.           Accepted := True
  550.         else if TokenType = PLUS then
  551.           Shift(12)
  552.         else if TokenType = MINUS then
  553.           Shift(13)
  554.         else begin
  555.           TokenError := ErrOperator;
  556.           Dec(Position, TokenLen);
  557.         end;
  558.       end;
  559.       2 : begin
  560.         if TokenType = TIMES then
  561.           Shift(14)
  562.         else if TokenType = DIVIDE then
  563.           Shift(15)
  564.         else
  565.           Reduce(3);
  566.       end;
  567.       3 : Reduce(6);
  568.       4 : begin
  569.        if TokenType = EXPO then
  570.          Shift(16)
  571.        else
  572.          Reduce(8);
  573.       end;
  574.       5 : begin
  575.         if TokenType = NUM then
  576.           Shift(10)
  577.         else if TokenType = CELLT then
  578.           Shift(7)
  579.         else if TokenType = FUNC then
  580.           Shift(11)
  581.         else if TokenType = OPAREN then
  582.           Shift(9)
  583.         else
  584.           TokenError := ErrExpression;
  585.       end;
  586.       6 : Reduce(10);
  587.       7 : begin
  588.         if TokenType = COLON then
  589.           Shift(18)
  590.         else
  591.           Reduce(13);
  592.       end;
  593.       8 : Reduce(12);
  594.       10 : Reduce(15);
  595.       11 : begin
  596.         if TokenType = OPAREN then
  597.           Shift(20)
  598.         else
  599.           TokenError := ErrOpenParen;
  600.       end;
  601.       17 : Reduce(9);
  602.       18 : begin
  603.         if TokenType = CELLT then
  604.           Shift(26)
  605.         else
  606.           TokenError := ErrCell;
  607.       end;
  608.       19 : begin
  609.         if TokenType = PLUS then
  610.           Shift(12)
  611.         else if TokenType = MINUS then
  612.           Shift(13)
  613.         else if TokenType = CPAREN then
  614.           Shift(27)
  615.         else
  616.           TokenError := ErrOpCloseParen;
  617.       end;
  618.       21 : begin
  619.         if TokenType = TIMES then
  620.           Shift(14)
  621.         else if TokenType = DIVIDE then
  622.           Shift(15)
  623.         else
  624.           Reduce(1);
  625.       end;
  626.       22 : begin
  627.         if TokenType = TIMES then
  628.           Shift(14)
  629.         else if TokenType = DIVIDE then
  630.           Shift(15)
  631.         else
  632.           Reduce(2);
  633.       end;
  634.       23 : Reduce(4);
  635.       24 : Reduce(5);
  636.       25 : Reduce(7);
  637.       26 : Reduce(11);
  638.       27 : Reduce(14);
  639.       28 : begin
  640.         if TokenType = PLUS then
  641.           Shift(12)
  642.         else if TokenType = MINUS then
  643.           Shift(13)
  644.         else if TokenType = CPAREN then
  645.           Shift(29)
  646.         else
  647.           TokenError := ErrOpCloseParen;
  648.       end;
  649.       29 : Reduce(16);
  650.     end; { case }
  651.   until Accepted or (TokenError <> 0);
  652.   if TokenError <> 0 then
  653.   begin
  654.     with Scr do
  655.     begin
  656.       if TokenError = ErrBadRange then
  657.         Dec(Position, TokenLen);
  658.       PrintError(ErrorMessages[TokenError]);
  659.       Exit;
  660.     end; { with }
  661.   end;
  662.   if IsFormula then
  663.     CType := ClFormula
  664.   else
  665.     CType := ClValue;
  666.   if MathError then
  667.   begin
  668.     ParseError := True;
  669.     ParseValue := 0;
  670.     Exit;
  671.   end;
  672.   ParseError := False;
  673.   ParseValue := Stack[StackTop].Value;
  674. end; { ParserObj.Parse }
  675.  
  676. end.
  677.