home *** CD-ROM | disk | FTP | other *** search
-
- { Copyright (c) 1985, 87 by Borland International, Inc. }
-
- unit MCPARSER;
-
- interface
-
- uses Crt, Dos, MCVars, MCUtil, MCDisply;
-
- function CellValue(Col, Row : Word) : Real;
- { Finds the Value of a particular cell }
-
- function Parse(S : String; var Att : Word) : Real;
- { Parses the string s - returns the Value of the evaluated string, and puts
- the attribute in Att: TXT = 0, CONSTANT = 1, FORMULA = 2, +4 = ERROR.
- }
-
- implementation
-
- const
- PLUS = 0;
- MINUS = 1;
- TIMES = 2;
- DIVIDE = 3;
- EXPO = 4;
- COLON = 5;
- OPAREN = 6;
- CPAREN = 7;
- NUM = 8;
- CELLT = 9;
- FUNC = 10;
- EOL = 11;
- BAD = 12;
- MAXFUNCNAMELEN = 5;
-
- type
- TokenRec = record
- State : Byte;
- case Byte of
- 0 : (Value : Real);
- 1 : (Row, Col : Word);
- 2 : (FuncName : String[MAXFUNCNAMELEN]);
- end;
-
- var
- Stack : array [1..PARSERSTACKSIZE] of TokenRec;
- CurToken : TokenRec;
- StackTop, TokenType : Word;
- MathError, TokenError, IsFormula : Boolean;
- Input : IString;
-
- function IsFunc(S : String) : Boolean;
- { Checks to see if the start of the Input string is a legal function.
- Returns TRUE if it is, FALSE otherwise.
- }
- var
- Len : Word;
- begin
- Len := Length(S);
- if Pos(S, Input) = 1 then
- begin
- CurToken.FuncName := Copy(Input, 1, Len);
- Delete(Input, 1, Len);
- IsFunc := True;
- end
- else
- IsFunc := False;
- end; { IsFunc }
-
- function NextToken : Word;
- { Gets the next Token from the Input stream }
- var
- NumString : String[80];
- FormLen, Place, Len, NumLen, Check : Word;
- FirstChar : Char;
- Decimal : Boolean;
- begin
- if Input = '' then
- begin
- NextToken := EOL;
- Exit;
- end;
- while (Input <> '') and (Input[1] = ' ') do
- Delete(Input, 1, 1);
- if Input[1] in ['0'..'9', '.'] then
- begin
- NumString := '';
- Len := 1;
- Decimal := False;
- while (Len <= Length(Input)) and
- ((Input[Len] in ['0'..'9']) or
- ((Input[Len] = '.') and (not Decimal))) do
- begin
- NumString := NumString + Input[Len];
- if Input[1] = '.' then
- Decimal := True;
- Inc(Len);
- end;
- if (Len = 2) and (Input[1] = '.') then
- begin
- NextToken := BAD;
- Exit;
- end;
- if (Len <= Length(Input)) and (Input[Len] = 'E') then
- begin
- NumString := NumString + 'E';
- Inc(Len);
- if Input[Len] in ['+', '-'] then
- begin
- NumString := NumString + Input[Len];
- Inc(Len);
- end;
- NumLen := 1;
- while (Len <= Length(Input)) and (Input[Len] in ['0'..'9']) and
- (NumLen <= MAXEXPLEN) do
- begin
- NumString := NumString + Input[Len];
- Inc(NumLen);
- Inc(Len);
- end;
- end;
- if NumString[1] = '.' then
- NumString := '0' + NumString;
- Val(NumString, CurToken.Value, Check);
- if Check <> 0 then
- MathError := True;
- NextToken := NUM;
- Delete(Input, 1, Length(NumString));
- Exit;
- end
- else if Input[1] in LETTERS then
- begin
- if IsFunc('ABS') or
- IsFunc('ATAN') or
- IsFunc('COS') or
- IsFunc('EXP') or
- IsFunc('LN') or
- IsFunc('ROUND') or
- IsFunc('SIN') or
- IsFunc('SQRT') or
- IsFunc('SQR') or
- IsFunc('TRUNC') then
- begin
- NextToken := FUNC;
- Exit;
- end;
- if FormulaStart(Input, 1, CurToken.Col, CurToken.Row, FormLen) then
- begin
- Delete(Input, 1, FormLen);
- IsFormula := True;
- NextToken := CELLT;
- Exit;
- end
- else begin
- NextToken := BAD;
- Exit;
- end;
- end
- else begin
- case Input[1] of
- '+' : NextToken := PLUS;
- '-' : NextToken := MINUS;
- '*' : NextToken := TIMES;
- '/' : NextToken := DIVIDE;
- '^' : NextToken := EXPO;
- ':' : NextToken := COLON;
- '(' : NextToken := OPAREN;
- ')' : NextToken := CPAREN;
- else
- NextToken := BAD;
- end;
- Delete(Input, 1, 1);
- Exit;
- end; { case }
- end; { NextToken }
-
- procedure Push(Token : TokenRec);
- { Pushes a new Token onto the stack }
- begin
- if StackTop = PARSERSTACKSIZE then
- begin
- ErrorMsg(MSGSTACKERROR);
- TokenError := True;
- end
- else begin
- Inc(StackTop);
- Stack[StackTop] := Token;
- end;
- end; { Push }
-
- procedure Pop(var Token : TokenRec);
- { Pops the top Token off of the stack }
- begin
- Token := Stack[StackTop];
- Dec(StackTop);
- end; { Pop }
-
- function GotoState(Production : Word) : Word;
- { Finds the new state based on the just-completed production and the
- top state.
- }
- var
- State : Word;
- begin
- State := Stack[StackTop].State;
- if (Production <= 3) then
- begin
- case State of
- 0 : GotoState := 1;
- 9 : GotoState := 19;
- 20 : GotoState := 28;
- end; { case }
- end
- else if Production <= 6 then
- begin
- case State of
- 0, 9, 20 : GotoState := 2;
- 12 : GotoState := 21;
- 13 : GotoState := 22;
- end; { case }
- end
- else if Production <= 8 then
- begin
- case State of
- 0, 9, 12, 13, 20 : GotoState := 3;
- 14 : GotoState := 23;
- 15 : GotoState := 24;
- 16 : GotoState := 25;
- end; { case }
- end
- else if Production <= 10 then
- begin
- case State of
- 0, 9, 12..16, 20 : GotoState := 4;
- end; { case }
- end
- else if Production <= 12 then
- begin
- case State of
- 0, 9, 12..16, 20 : GotoState := 6;
- 5 : GotoState := 17;
- end; { case }
- end
- else begin
- case State of
- 0, 5, 9, 12..16, 20 : GotoState := 8;
- end; { case }
- end;
- end; { GotoState }
-
- function CellValue;
- var
- CPtr : CellPtr;
- begin
- CPtr := Cell[Col, Row];
- if (CPtr = nil) then
- CellValue := 0
- else begin
- if (CPtr^.Error) or (CPtr^.Attrib = TXT) then
- MathError := True;
- if CPtr^.Attrib = FORMULA then
- CellValue := CPtr^.FValue
- else
- CellValue := CPtr^.Value;
- end;
- end; { CellValue }
-
- procedure Shift(State : Word);
- { Shifts a Token onto the stack }
- begin
- CurToken.State := State;
- Push(CurToken);
- TokenType := NextToken;
- end; { Shift }
-
- procedure Reduce(Reduction : Word);
- { Completes a reduction }
- var
- Token1, Token2 : TokenRec;
- Counter : Word;
- begin
- case Reduction of
- 1 : begin
- Pop(Token1);
- Pop(Token2);
- Pop(Token2);
- CurToken.Value := Token1.Value + Token2.Value;
- end;
- 2 : begin
- Pop(Token1);
- Pop(Token2);
- Pop(Token2);
- CurToken.Value := Token2.Value - Token1.Value;
- end;
- 4 : begin
- Pop(Token1);
- Pop(Token2);
- Pop(Token2);
- CurToken.Value := Token1.Value * Token2.Value;
- end;
- 5 : begin
- Pop(Token1);
- Pop(Token2);
- Pop(Token2);
- if Token1.Value = 0 then
- MathError := True
- else
- CurToken.Value := Token2.Value / Token1.Value;
- end;
- 7 : begin
- Pop(Token1);
- Pop(Token2);
- Pop(Token2);
- if Token2.Value <= 0 then
- MathError := True
- else if (Token1.Value * Ln(Token2.Value) < -EXPLIMIT) or
- (Token1.Value * Ln(Token2.Value) > EXPLIMIT) then
- MathError := True
- else
- CurToken.Value := Exp(Token1.Value * Ln(Token2.Value));
- end;
- 9 : begin
- Pop(Token1);
- Pop(Token2);
- CurToken.Value := -Token1.Value;
- end;
- 11 : begin
- Pop(Token1);
- Pop(Token2);
- Pop(Token2);
- CurToken.Value := 0;
- if Token1.Row = Token2.Row then
- begin
- if Token1.Col < Token2.Col then
- TokenError := True
- else begin
- for Counter := Token2.Col to Token1.Col do
- CurToken.Value := CurToken.Value + CellValue(Counter, Token1.Row);
- end;
- end
- else if Token1.Col = Token2.Col then
- begin
- if Token1.Row < Token2.Row then
- TokenError := True
- else begin
- for Counter := Token2.Row to Token1.Row do
- CurToken.Value := CurToken.Value + CellValue(Token1.Col, Counter);
- end;
- end
- else
- TokenError := True;
- end;
- 13 : begin
- Pop(CurToken);
- CurToken.Value := CellValue(CurToken.Col, CurToken.Row);
- end;
- 14 : begin
- Pop(Token1);
- Pop(CurToken);
- Pop(Token1);
- end;
- 16 : begin
- Pop(Token1);
- Pop(CurToken);
- Pop(Token1);
- Pop(Token1);
- if Token1.FuncName = 'ABS' then
- CurToken.Value := Abs(CurToken.Value)
- else if Token1.FuncName = 'ATAN' then
- CurToken.Value := ArcTan(CurToken.Value)
- else if Token1.FuncName = 'COS' then
- CurToken.Value := Cos(CurToken.Value)
- else if Token1.FuncName = 'EXP' then
- begin
- if (CurToken.Value < -EXPLIMIT) or (CurToken.Value > EXPLIMIT) then
- MathError := True
- else
- CurToken.Value := Exp(CurToken.Value);
- end
- else if Token1.FuncName = 'LN' then
- begin
- if CurToken.Value <= 0 then
- MathError := True
- else
- CurToken.Value := Ln(CurToken.Value);
- end
- else if Token1.FuncName = 'ROUND' then
- begin
- if (CurToken.Value < -1E9) or (CurToken.Value > 1E9) then
- MathError := True
- else
- CurToken.Value := Round(CurToken.Value);
- end
- else if Token1.FuncName = 'SIN' then
- CurToken.Value := Sin(CurToken.Value)
- else if Token1.FuncName = 'SQRT' then
- begin
- if CurToken.Value < 0 then
- MathError := True
- else
- CurToken.Value := Sqrt(CurToken.Value);
- end
- else if Token1.FuncName = 'SQR' then
- begin
- if (CurToken.Value < -SQRLIMIT) or (CurToken.Value > SQRLIMIT) then
- MathError := True
- else
- CurToken.Value := Sqr(CurToken.Value);
- end
- else if Token1.FuncName = 'TRUNC' then
- begin
- if (CurToken.Value < -1E9) or (CurToken.Value > 1E9) then
- MathError := True
- else
- CurToken.Value := Trunc(CurToken.Value);
- end;
- end;
- 3, 6, 8, 10, 12, 15 : Pop(CurToken);
- end; { case }
- CurToken.State := GotoState(Reduction);
- Push(CurToken);
- end; { Reduce }
-
- function Parse;
- var
- FirstToken : TokenRec;
- Accepted : Boolean;
- Counter : Word;
- begin
- Accepted := False;
- TokenError := False;
- MathError := False;
- IsFormula := False;
- Input := UpperCase(S);
- StackTop := 0;
- FirstToken.State := 0;
- FirstToken.Value := 0;
- Push(FirstToken);
- TokenType := NextToken;
- repeat
- case Stack[StackTop].State of
- 0, 9, 12..16, 20 : begin
- if TokenType = NUM then
- Shift(10)
- else if TokenType = CELLT then
- Shift(7)
- else if TokenType = FUNC then
- Shift(11)
- else if TokenType = MINUS then
- Shift(5)
- else if TokenType = OPAREN then
- Shift(9)
- else
- TokenError := True;
- end;
- 1 : begin
- if TokenType = EOL then
- Accepted := True
- else if TokenType = PLUS then
- Shift(12)
- else if TokenType = MINUS then
- Shift(13)
- else
- TokenError := True;
- end;
- 2 : begin
- if TokenType = TIMES then
- Shift(14)
- else if TokenType = DIVIDE then
- Shift(15)
- else
- Reduce(3);
- end;
- 3 : Reduce(6);
- 4 : begin
- if TokenType = EXPO then
- Shift(16)
- else
- Reduce(8);
- end;
- 5 : begin
- if TokenType = NUM then
- Shift(10)
- else if TokenType = CELLT then
- Shift(7)
- else if TokenType = FUNC then
- Shift(11)
- else if TokenType = OPAREN then
- Shift(9)
- else
- TokenError := True;
- end;
- 6 : Reduce(10);
- 7 : begin
- if TokenType = COLON then
- Shift(18)
- else
- Reduce(13);
- end;
- 8 : Reduce(12);
- 10 : Reduce(15);
- 11 : begin
- if TokenType = OPAREN then
- Shift(20)
- else
- TokenError := True;
- end;
- 17 : Reduce(9);
- 18 : begin
- if TokenType = CELLT then
- Shift(26)
- else
- TokenError := True;
- end;
- 19 : begin
- if TokenType = PLUS then
- Shift(12)
- else if TokenType = MINUS then
- Shift(13)
- else if TokenType = CPAREN then
- Shift(27)
- else
- TokenError := True;
- end;
- 21 : begin
- if TokenType = TIMES then
- Shift(14)
- else if TokenType = DIVIDE then
- Shift(15)
- else
- Reduce(1);
- end;
- 22 : begin
- if TokenType = TIMES then
- Shift(14)
- else if TokenType = DIVIDE then
- Shift(15)
- else
- Reduce(2);
- end;
- 23 : Reduce(4);
- 24 : Reduce(5);
- 25 : Reduce(7);
- 26 : Reduce(11);
- 27 : Reduce(14);
- 28 : begin
- if TokenType = PLUS then
- Shift(12)
- else if TokenType = MINUS then
- Shift(13)
- else if TokenType = CPAREN then
- Shift(29)
- else
- TokenError := True;
- end;
- 29 : Reduce(16);
- end; { case }
- until Accepted or TokenError;
- if TokenError then
- begin
- Att := TXT;
- Parse := 0;
- Exit;
- end;
- if IsFormula then
- Att := FORMULA
- else
- Att := VALUE;
- if MathError then
- begin
- Inc(Att, 4);
- Parse := 0;
- Exit;
- end;
- Parse := Stack[StackTop].Value;
- end; { Parse }
-
- begin
- end.