dos_compilers/Borland Turbo Pascal v5/MCPARSER.PAS

579 lines
14 KiB
Plaintext
Raw Normal View History

2024-07-02 15:16:37 +02:00
{ Copyright (c) 1985, 88 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 }
end.