expertcad/POWERCAD30/UNITS/P10Build.pas
2025-05-12 10:07:51 +03:00

1662 lines
55 KiB
ObjectPascal

unit P10Build;
{.$DEFINE DEBUG}
{$IFNDEF DEBUG}
{$D-} {$L-} {$Q-} {$R-} {$S-}
{$ENDIF}
{$IFDEF Win32}
{$LONGSTRINGS ON}
{$S-}
{$ENDIF}
{$I+} { I/O checking is always on }
interface
uses
Parser10,
SysUtils, Classes;
procedure ParseFunction( FunctionString: string; { the unparsed string }
Variables: TStringlist; { list of variables }
{ lists of available functions }
FunctionOne, { functions with ONE argument, e.g. exp() }
FunctionTwo: TStringList; { functions with TWO arguments, e.g. max(,) }
UsePascalNumbers: boolean; { true: -> Val; false: StrToFloat }
{ return pointer to tree, number of performed operations and error state }
var FirstOP : POperation;
var Error : boolean);
{ error actually is superfluous as we are now using exceptions }
implementation
{$IFDEF VER100}
resourcestring
{$ELSE}
const
{$ENDIF}
msgErrBlanks = 'Expression has blanks';
msgMissingBrackets = 'Missing brackets in expression';
msgParseError = 'Error parsing expression:';
msgNestings = 'Expression contains too many nestings';
msgTooComplex = 'Expression is too complex';
msgInternalError = 'TParser internal error';
const
TokenOperators = [ sum, diff, prod, divis, modulo, IntDiv,
integerpower, realpower];
type
TermString = {$IFDEF Win32} string {$ELSE} PString {$ENDIF};
procedure ParseFunction( FunctionString: string;
Variables: TStringList;
FunctionOne,
FunctionTwo: TStringList;
UsePascalNumbers: boolean;
var FirstOP: POperation;
var Error: boolean);
function CheckNumberBrackets(const s: string): boolean; forward;
{ checks whether number of ( = number of ) }
function CheckNumber(const s: string; var FloatNumber: ParserFloat): boolean; forward;
{ checks whether s is a number }
function CheckVariable(const s: string; var VariableID: integer): boolean; forward;
{ checks whether s is a variable string }
function CheckTerm(var s1: string): boolean; forward;
{ checks whether s is a valid term }
function CheckBracket(const s: string; var s1: string): boolean; forward;
{ checks whether s =(...(s1)...) and s1 is a valid term }
function CheckNegate(const s: string; var s1: string): boolean; forward;
{checks whether s denotes the negative value of a valid operation}
function CheckAdd(const s: string; var s1, s2: string): boolean; forward;
{checks whether + is the primary operation in s}
function CheckSubtract(const s: string; var s1, s2: string): boolean; forward;
{checks whether - is the primary operation in s}
function CheckMultiply(const s: string; var s1, s2: string): boolean; forward;
{checks whether * is the primary operation in s}
function CheckIntegerDiv(const s: string; var s1, s2: string): boolean; forward;
{checks whether DIV is the primary TOperation in s}
function CheckModulo(const s: string; var s1, s2: string): boolean; forward;
{checks whether MOD is the primary TOperation in s}
function CheckRealDivision(const s: string; var s1, s2: string): boolean; forward;
{checks whether / is the primary operation in s}
function CheckFuncTwoVar(const s: string; var s1, s2: string): boolean; forward;
{checks whether s=f(s1,s2); s1,s2 being valid terms}
function CheckFuncOneVar(const s: string; var s1: string): boolean; forward;
{checks whether s denotes the evaluation of a function fsort(s1)}
function CheckPower(const s: string; var s1, s2: string; var AToken: TToken): boolean; forward;
function CheckNumberBrackets(const s: string):boolean;
{checks whether # of '(' equ. # of ')'}
var
counter,
bracket : integer;
begin
bracket := 0;
counter := length(s);
while counter <> 0 do
begin
case s[counter] of
'(': inc(bracket);
')': dec(bracket);
end;
dec(counter);
end;
Result := bracket = 0;
end;
function CheckNumber(const s: string; var FloatNumber: ParserFloat):boolean;
{checks whether s is a number}
var
code: integer;
{$IFDEF Debug} { prevent debugger from showing conversion errors }
SaveClass : TClass;
{$ENDIF}
//Tolik 24/01/2022 --
function StrToFloat_My(const S: string; const AFormatSettings: TFormatSettings): Extended;
var ss: string;
begin
ss := s;
if formatSettings.DecimalSeparator = ',' then
StringReplace(ss, '.', formatSettings.DecimalSeparator, [rfReplaceAll])
else
if formatSettings.DecimalSeparator = '.' then
StringReplace(ss, ',', formatSettings.DecimalSeparator, [rfReplaceAll]);
Result := StrToFloat(ss, AFormatSettings);
end;
//
begin
if s = 'PI' then
begin
FloatNumber := Pi;
Result := true;
end
else
if s = '-PI' then
begin
FloatNumber := -Pi;
Result := true;
end
else
begin
if UsePascalNumbers then
begin
val(s, FloatNumber, code);
Result := code = 0;
end
else
begin
{$IFDEF Debug}
SaveClass := ExceptionClass;
ExceptionClass := nil;
try
{$ENDIF}
try
FloatNumber := StrToFloat_My(s, FormatSettings);
Result := true
except
on E: Exception do
begin
Result := false;
end;
end;
{$IFDEF Debug}
finally
ExceptionClass := SaveClass;
end;
{$ENDIF}
end;
end;
end;
function CheckVariable(const s: string; var VariableID: integer): boolean;
{checks whether s is a variable string}
begin
Result := Variables.Find(s, VariableID);
end;
function CheckTerm(var s1: string) :boolean;
{ checks whether s is a valid term }
var
s2, s3: TermString;
FloatNumber: ParserFloat;
fsort: TToken;
VariableID: integer;
begin
Result := false;
if length(s1) = 0 then
exit;
{$IFNDEF Win32}
new(s2);
new(s3);
try
{$ENDIF}
if CheckNumber(s1, FloatNumber) or
CheckVariable(s1, VariableID) or
CheckNegate(s1, s2{$IFNDEF Win32}^{$ENDIF}) or
CheckAdd(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckSubtract(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckMultiply(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncTwoVar(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncOneVar(s1, s2{$IFNDEF Win32}^{$ENDIF})
then
Result := true
else
if CheckBracket(s1, s2{$IFNDEF Win32}^{$ENDIF}) then
begin
s1 := s2{$IFNDEF Win32}^{$ENDIF};
Result := true
end;
{$IFNDEF Win32}
finally
dispose(s2);
dispose(s3);
end;
{$ENDIF}
end;
function CheckBracket(const s: string; var s1: string): boolean;
{checks whether s =(...(s1)...) and s1 is a valid term}
var
SLen : integer;
begin
Result := false;
SLen := Length(s);
if (SLen > 0) and (s[SLen] = ')') and (s[1] = '(') then
begin
s1 := copy(s, 2, SLen-2);
Result := CheckTerm(s1);
end;
end;
function CheckNegate(const s: string; var s1: string) :boolean;
{checks whether s denotes the negative value of a valid TOperation}
var
s2, s3: TermString;
fsort: TToken;
VariableID: integer;
begin
Result := false;
if (length(s) <> 0) and (s[1] = '-') then
begin
{$IFNDEF Win32}
new(s2);
new(s3);
try
{$ENDIF}
s1 := copy(s, 2, length(s)-1);
if CheckBracket(s1, s2{$IFNDEF Win32}^{$ENDIF}) then
begin
s1 := s2{$IFNDEF Win32}^{$ENDIF};
Result := true;
end
else
Result :=
CheckVariable(s1, VariableID) or
CheckPower(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s1, s2{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s2{$IFNDEF Win32}^{$ENDIF}, s3{$IFNDEF Win32}^{$ENDIF});
{$IFNDEF Win32}
finally
dispose(s2);
dispose(s3);
end;
{$ENDIF}
end;
end;
function CheckAdd(const s: string; var s1, s2: string): boolean;
{checks whether '+' is the primary TOperation in s}
var
s3, s4: TermString;
i, j: integer;
FloatNumber: ParserFloat;
fsort: TToken;
VariableID: integer;
begin
Result := false;
i := 0;
j := length(s);
repeat
while i <> j do
begin
inc(i);
if s[i] = '+' then
break;
end;
if (i > 1) and (i < j) then
begin
s1 := copy(s, 1, i-1);
s2 := copy(s, i+1, j-i);
Result := CheckNumberBrackets(s1) and CheckNumberBrackets(s2);
if Result then
begin
Result := CheckVariable(s1, VariableID) or CheckNumber(s1, FloatNumber);
{$IFNDEF Win32}
new(s3);
new(s4);
try
{$ENDIF}
if not Result then
begin
Result := CheckBracket(s1, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s1 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
Result := CheckNegate(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckSubtract(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckMultiply(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
if Result then
begin
Result := CheckVariable(s2, VariableID) or CheckNumber(s2, FloatNumber);
if not Result then
begin
Result := CheckBracket(s2, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s2 := s3{$IFNDEF Win32}^{$ENDIF}
else
Result := CheckAdd(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckSubtract(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckMultiply(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s2, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
end;
end;
{$IFNDEF Win32}
finally
dispose(s3);
dispose(s4);
end;
{$ENDIF}
end;
end
else
break;
until Result;
end;
function CheckSubtract(const s: string; var s1, s2: string): boolean;
{checks whether '-' is the primary TOperation in s}
var
s3, s4: TermString;
i, j: integer;
FloatNumber: ParserFloat;
fsort: TToken;
VariableID: integer;
begin
Result := false;
i := 0;
j := length(s);
repeat
while i <> j do
begin
inc(i);
if s[i] = '-' then
break;
end;
if (i > 1) and (i < j) then
begin
s1 := copy(s, 1, i-1);
s2 := copy(s, i+1, j-i);
Result := CheckNumberBrackets(s1) and CheckNumberBrackets(s2);
if Result then
begin
Result := CheckVariable(s1, VariableID) or CheckNumber(s1, FloatNumber);
{$IFNDEF Win32}
new(s3);
new(s4);
try
{$ENDIF}
if not Result then
begin
Result := CheckBracket(s1, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s1 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
Result := CheckNegate(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckSubtract(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckMultiply(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
if Result then
begin
Result := CheckVariable(s2, VariableID) or CheckNumber(s2, FloatNumber);
if not Result then
begin
Result := CheckBracket(s2, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s2 := s3{$IFNDEF Win32}^{$ENDIF}
else
Result := CheckMultiply(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s2, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
end;
end;
{$IFNDEF Win32}
finally
dispose(s3);
dispose(s4);
end;
{$ENDIF}
end;
end
else
break;
until Result;
end;
function CheckMultiply(const s: string; var s1, s2: string): boolean;
{checks whether '*' is the primary TOperation in s}
var
s3, s4: TermString;
i, j: integer;
FloatNumber: ParserFloat;
fsort: TToken;
VariableID: integer;
begin
Result := false;
i := 0;
j := length(s);
repeat
while i <> j do
begin
inc(i);
if s[i] = '*' then
break;
end;
if (i > 1) and (i < j) then
begin
s1 := copy(s, 1, i-1);
s2 := copy(s, i+1, j-i);
Result := CheckNumberBrackets(s1) and CheckNumberBrackets(s2);
if Result then
begin
Result := CheckVariable(s1, VariableID) or
CheckNumber(s1, FloatNumber);
{$IFNDEF Win32}
new(s3);
new(s4);
try
{$ENDIF}
if not Result then
begin
Result := CheckBracket(s1, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s1 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
Result := CheckNegate(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
if Result then
begin
Result := CheckVariable(s2, VariableID) or
CheckNumber(s2, FloatNumber);
if not Result then
begin
Result := CheckBracket(s2, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s2 := s3{$IFNDEF Win32}^{$ENDIF}
else
Result := CheckMultiply(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s2, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
end;
end;
{$IFNDEF Win32}
finally
dispose(s3);
dispose(s4);
end;
{$ENDIF}
end;
end
else
break;
until Result;
end;
function CheckIntegerDiv(const s: string; var s1, s2: string): boolean;
{checks whether 'DIV' is the primary TOperation in s}
var
s3, s4: TermString;
i, j: integer;
VariableID: integer;
FloatNumber: ParserFloat;
fsort: TToken;
begin
Result := false;
i := 0;
repeat
j := pos('DIV', copy(s, i+1, length(s)-i));
if j > 0 then
begin
inc(i, j);
if (i > 1) and (i < length(s)) then
begin
s1 := copy(s, 1, i-1);
s2 := copy(s, i+3, length(s)-i-2);
Result := CheckNumberBrackets(s1) and CheckNumberBrackets(s2);
if Result then
begin
Result := CheckVariable(s1, VariableID) or
CheckNumber(s1, FloatNumber);
{$IFNDEF Win32}
new(s3);
new(s4);
try
{$ENDIF}
if not Result then
begin
Result := CheckBracket(s1, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s1 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
Result := CheckNegate(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
if Result then
begin
Result := CheckVariable(s2,VariableID) or
CheckNumber(s2,FloatNumber);
if not Result then
begin
Result := CheckBracket(s2, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s2 := s3{$IFNDEF Win32}^{$ENDIF}
else
Result := CheckPower(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s2, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
end;
end;
{$IFNDEF Win32}
finally
dispose(s3);
dispose(s4);
end;
{$ENDIF}
end;
end;
end;
until Result or (j = 0) or (i >= length(s));
end;
function CheckModulo(const s: string; var s1, s2: string): boolean;
{checks whether 'MOD' is the primary TOperation in s}
var
s3, s4: TermString;
i, j: integer;
VariableID: integer;
FloatNumber: ParserFloat;
fsort: TToken;
begin
Result := false;
i := 0;
repeat
j := pos('MOD', copy(s, i+1, length(s)-i));
if j > 0 then
begin
inc(i, j);
if (i > 1) and (i < length(s)) then
begin
s1 := copy(s, 1, i-1);
s2 := copy(s, i+3, length(s)-i-2);
Result := CheckNumberBrackets(s1) and CheckNumberBrackets(s2);
if Result then
begin
Result := CheckVariable(s1, VariableID) or
CheckNumber(s1, FloatNumber);
{$IFNDEF Win32}
new(s3);
new(s4);
try
{$ENDIF}
if not Result then
begin
Result := CheckBracket(s1, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s1 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
Result := CheckNegate(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
if Result then
begin
Result := CheckVariable(s2, VariableID) or
CheckNumber(s2, FloatNumber);
if not Result then
begin
Result := CheckBracket(s2, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s2 := s3{$IFNDEF Win32}^{$ENDIF}
else
Result := CheckPower(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s2, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
end
end;
{$IFNDEF Win32}
finally
dispose(s3);
dispose(s4);
end;
{$ENDIF}
end;
end;
end;
until Result or (j = 0) or (i >= length(s));
end;
function CheckRealDivision(const s: string; var s1, s2: string): boolean;
{checks whether '/' is the primary TOperation in s}
var
s3, s4: TermString;
i, j: integer;
VariableID: integer;
FloatNumber: ParserFloat;
fsort: TToken;
begin
Result := false;
i := 0;
j := length(s);
repeat
while i <> j do
begin
inc(i);
if s[i] = '/' then
break;
end;
if (i > 1) and (i < j) then
begin
s1 := copy(s, 1, i-1);
s2 := copy(s, i+1, j-i);
Result := CheckNumberBrackets(s1) and CheckNumberBrackets(s2);
if Result then
begin
Result := CheckVariable(s1, VariableID) or
CheckNumber(s1, FloatNumber);
{$IFNDEF Win32}
new(s3);
new(s4);
try
{$ENDIF}
if not Result then
begin
Result := CheckBracket(s1, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s1 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
Result := CheckNegate(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckIntegerDiv(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckModulo(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckRealDivision(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}) or
CheckPower(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
if Result then
begin
Result := CheckVariable(s2, VariableID) or
CheckNumber(s2, FloatNumber);
if not Result then
begin
Result := CheckBracket(s2, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s2 := s3{$IFNDEF Win32}^{$ENDIF}
else
Result := CheckPower(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF}, fsort) or
CheckFuncOneVar(s2, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
end;
end;
{$IFNDEF Win32}
finally
dispose(s3);
dispose(s4);
end;
{$ENDIF}
end;
end
else
break;
until Result;
end;
function CheckFuncTwoVar(const s: string; var s1, s2: string): boolean;
{checks whether s=f(s1,s2); s1,s2 being valid terms}
function CheckComma(const s: string; var s1, s2: string): boolean;
var
i, j: integer;
begin
Result := false;
i := 0;
j := length(s);
repeat
while i <> j do
begin
inc(i);
if s[i] = ',' then
break;
end;
if (i > 1) and (i < j) then
begin
s1 := copy(s, 1, i-1);
if CheckTerm(s1) then
begin
s2 := copy(s, i+1, j-i);
Result := CheckTerm(s2);
end;
end
else
break;
until Result;
end;
var
SLen,
counter : integer;
begin
Result := false;
SLen := Pos('(', s);
dec(SLen);
if (SLen > 0) and (s[length(s)] = ')') then
begin
if FunctionTwo.Find(copy(s, 1, SLen), counter) then
begin
inc(SLen, 2);
Result := CheckComma( copy(s, SLen, length(s)-SLen), s1, s2);
end;
end;
end;
function CheckFuncOneVar(const s: string; var s1: string): boolean;
{checks whether s denotes the evaluation of a function fsort(s1)}
var
{$IFNDEF Win32}
s2: TermString;
{$ENDIF}
counter: integer;
SLen: integer;
begin
Result := false;
SLen := Pos('(', s);
dec(SLen);
if (SLen > 0) then
begin
if FunctionOne.Find(copy(s, 1, SLen), counter) then
begin
{$IFNDEF Win32}
new(s2);
try
s2^ := copy(s, SLen+1, length(s)-SLen);
Result := CheckBracket(s2^, s1);
finally
dispose(s2);
end;
{$ELSE}
Result := CheckBracket(copy(s, SLen+1, length(s)-SLen), s1);
{$ENDIF}
end;
end;
end;
function CheckPower(const s: string; var s1, s2: string; var AToken: TToken): boolean;
var
s3, s4: TermString;
i, j: integer;
FloatNumber: ParserFloat;
VariableID: integer;
begin
Result := false;
i := 0;
j := length(s);
repeat
while i <> j do
begin
inc(i);
if s[i] = '^' then
break;
end;
if (i > 1) and (i < j) then
begin
s1 := copy(s, 1, i-1);
s2 := copy(s, i+1, j-i);
Result := CheckNumberBrackets(s1) and CheckNumberBrackets(s2);
if Result then
begin
Result := CheckVariable(s1, VariableID) or CheckNumber(s1, FloatNumber);
{$IFNDEF Win32}
new(s3);
new(s4);
try
{$ENDIF}
if not Result then
begin
Result := CheckBracket(s1, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s1 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
Result := CheckFuncOneVar(s1, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s1, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
if Result then
begin
if CheckNumber(s2, FloatNumber) then
begin
i := trunc(FloatNumber);
if (i <> FloatNumber) then
begin
{ this is a real number }
AToken := realpower;
end
else
begin
case i of
2: AToken := square;
3: AToken := third;
4: AToken := fourth;
else
AToken := integerpower;
end;
end;
end
else
begin
Result := CheckVariable(s2, VariableID);
if not Result then
begin
Result := CheckBracket(s2, s3{$IFNDEF Win32}^{$ENDIF});
if Result then
s2 := s3{$IFNDEF Win32}^{$ENDIF};
end;
if not Result then
begin
Result := CheckFuncOneVar(s2, s3{$IFNDEF Win32}^{$ENDIF}) or
CheckFuncTwoVar(s2, s3{$IFNDEF Win32}^{$ENDIF}, s4{$IFNDEF Win32}^{$ENDIF});
end;
if Result then
AToken := realPower;
end;
end;
{$IFNDEF Win32}
finally
dispose(s3);
dispose(s4);
end;
{$ENDIF}
end;
end
else
break;
until Result;
end;
function CreateOperation(const Term: TToken; const Proc: Pointer): POperation;
begin
new(Result);
with Result^ do
begin
Arg1 := nil;
Arg2 := nil;
Dest := nil;
NextOperation := nil;
Token := Term;
Operation := TMathProcedure(Proc);
end;
end;
const
BlankString = ' ';
type
PTermRecord = ^TermRecord;
TermRecord = record
{ this usage of string is a bit inefficient,
as in 16bit always 256 bytes are consumed.
But since we
a) are allocating memory dynamically and
b) this will be released immediately when
finished with parsing
this seems to be OK
One COULD create a "TermClass" where this is handled }
StartString: string;
LeftString, RightString: string;
Token: TToken;
Position: array[1..3] of integer;
Next1,
Next2,
Previous: PTermRecord;
end;
const
{ side effect: for each bracketing level added
SizeOf(integer) bytes additional stack usage
maxLevelWidth*SizeOf(Pointer) additional global memory used }
maxBracketLevels = 20;
{ side effect: for each additional (complexity) level width
maxBracketLevels*SizeOf(Pointer) additional global memory used }
maxLevelWidth = 50;
type
LevelArray = array[0..maxBracketLevels] of integer;
OperationPointerArray = array[0..maxBracketLevels, 1..maxLevelWidth] of POperation;
POperationPointerArray = ^OperationPointerArray;
var
Matrix: POperationPointerArray;
{ bracket positions }
CurrentBracket,
i,
CurBracketLevels: integer;
BracketLevel: LevelArray;
LastOP: POperation;
FloatNumber: ParserFloat;
VariableID: integer;
ANewTerm, { need this particlar pointer to guarantee a good, flawless memory cleanup in except }
FirstTerm,
Next1Term,
Next2Term,
LastTerm: PTermRecord;
counter1,
counter2: integer;
begin
{ initialize local variables for safe checking in try..finally..end}
{ FirstTerm := nil; } { not necessary since not freed in finally }
LastTerm := nil;
ANewTerm := nil;
Next1Term := nil;
Next2Term := nil;
Error := false;
FillChar(BracketLevel, SizeOf(BracketLevel), 0); { initialize bracket array }
BracketLevel[0] := 1;
CurBracketLevels := 0;
new(Matrix);
try { this block protects the whole of ALL assignments...}
FillChar(Matrix^, SizeOf(Matrix^), 0);
new(ANewTerm);
with ANewTerm^ do
begin
StartString := UpperCase(FunctionString);
{ remove leading and trailing spaces }
counter1 := 1;
counter2 := length(StartString);
while counter1 <= counter2 do
if StartString[counter1] <> ' ' then
break
else
inc(counter1);
counter2 := length(StartString);
while counter2 > counter1 do
if StartString[counter2] <> ' ' then
break
else
dec(counter2);
StartString := Copy(StartString, counter1, counter2 - counter1 + 1);
if Pos(' ', StartString) > 0 then
raise EExpressionHasBlanks.Create(msgErrBlanks);
{
Old code:
StartString := RemoveBlanks(UpperCase(FunctionString));
...do not use! Using it would create the following situation:
Passed string: "e xp(12)"
Modified string: "exp(12)"
This MAY or may not be the desired meaning - there may well exist
a variable "e" and a function "xp" and just the operator would be missing.
Conclusion: the above line has the potential of changing the meaning
of an expression.
}
if not CheckNumberBrackets(StartString) then
raise EMissMatchingBracket.Create(msgMissingBrackets);
{ remove enclosing brackets, e.g. ((pi)) }
while CheckBracket(StartString, FunctionString) do
StartString := FunctionString;
LeftString := BlankString;
RightString := BlankString;
Token := variab;
Next1 := nil;
Next2 := nil;
Previous := nil;
end;
Matrix^[0,1] := CreateOperation(variab, nil);
LastTerm := ANewTerm;
FirstTerm := ANewTerm;
ANewTerm := nil;
with LastTerm^ do
begin
Position[1] := 0;
Position[2] := 1;
Position[3] := 1;
end;
repeat
repeat
with LastTerm^ do
begin
CurrentBracket := Position[1];
i := Position[2];
if Next1 = nil then
begin
if CheckVariable(StartString, VariableID) then
begin
Token := variab;
if Position[3] = 1 then
Matrix^[CurrentBracket, i]^.Arg1 := PParserFloat(Variables.Objects[VariableID])
else
Matrix^[CurrentBracket, i]^.Arg2 := PParserFloat(Variables.Objects[VariableID])
end
else
begin
if CheckNumber(StartString, FloatNumber) then
begin
Token := constant;
if Position[3] = 1 then
begin
new(Matrix^[CurrentBracket, i]^.Arg1);
Matrix^[CurrentBracket, i]^.Arg1^ := FloatNumber;
end
else
begin
new(Matrix^[CurrentBracket, i]^.Arg2);
Matrix^[CurrentBracket, i]^.Arg2^ := FloatNumber;
end;
end
else
begin
if CheckNegate(StartString, LeftString) then
Token := minus
else
begin
if CheckAdd(StartString, LeftString, RightString) then
Token := sum
else
begin
if CheckSubtract(StartString, LeftString, RightString) then
Token := diff
else
begin
if CheckMultiply(StartString, LeftString, RightString) then
Token := prod
else
begin
if CheckIntegerDiv(StartString, LeftString, RightString) then
Token := IntDiv
else
begin
if CheckModulo(StartString, LeftString, RightString) then
Token := modulo
else
begin
if CheckRealDivision(StartString, LeftString, RightString) then
Token := divis
else
begin
if not CheckPower(StartString, LeftString, RightString, Token) then
begin
if CheckFuncOneVar(StartString, LeftString) then
Token := FuncOneVar
else
begin
if CheckFuncTwoVar(StartString, LeftString, RightString) then
Token := FuncTwoVar
else
begin
Error := true; {with an exception raised this is meaningless...}
if (LeftString = BlankString) and (RightString = BlankString) then
raise ESyntaxError.CreateFmt(
msgParseError+#13'%s', [StartString]
)
else
raise ESyntaxError.CreateFmt(
msgParseError+#13'%s'#13'%s', [Leftstring, RightString]
)
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end;
end; { with LastTerm^ }
if LastTerm^.Token in ( [minus, square, third, fourth, FuncOneVar, FuncTwoVar] + TokenOperators) then
begin
if LastTerm^.Next1 = nil then
begin
try
Next1Term := nil;
new(Next1Term);
inc(CurrentBracket);
if CurrentBracket > maxBracketLevels then
begin
Error := true;
raise ETooManyNestings.Create(msgNestings);
end;
if CurBracketLevels < CurrentBracket then
CurBracketLevels := CurrentBracket;
i := BracketLevel[CurrentBracket] + 1;
if i > maxLevelWidth then
begin
Error := true;
raise EExpressionTooComplex.Create(msgTooComplex);
end;
with Next1Term^ do
begin
StartString := LastTerm^.LeftString;
LeftString := BlankString;
RightString := BlankString;
Position[1] := CurrentBracket;
Position[2] := i;
Position[3] := 1;
Token := variab;
Previous := LastTerm;
Next1 := nil;
Next2 := nil;
end;
with LastTerm^ do
begin
case Token of
FuncOneVar:
with FunctionOne do
Matrix^[CurrentBracket, i] := CreateOperation(
Token,
Objects[IndexOf(copy(StartString, 1, pos('(', StartString)-1))]
);
FuncTwoVar:
with FunctionTwo do
Matrix^[CurrentBracket, i] := CreateOperation(
Token,
Objects[IndexOf(copy(StartString, 1, pos('(', StartString)-1))]
);
else
Matrix^[CurrentBracket, i] := CreateOperation(Token, nil);
end;
new(Matrix^[CurrentBracket, i]^.Dest);
Matrix^[CurrentBracket, i]^.Dest^ := 0;
if Position[3] = 1 then
Matrix^[Position[1], Position[2]]^.Arg1 := Matrix^[CurrentBracket, i]^.Dest
else
Matrix^[Position[1], Position[2]]^.Arg2 := Matrix^[CurrentBracket, i]^.Dest;
Next1 := Next1Term;
Next1Term := nil;
end;
if LastTerm^.Token in [minus, square, third, fourth, FuncOneVar] then
inc(BracketLevel[CurrentBracket]);
except
on E: Exception do
begin
if assigned(Next1Term) then
begin
dispose(Next1Term);
Next1Term := nil;
end;
raise;
end;
end;
end
else
begin
if LastTerm^.Token in (TokenOperators + [FuncTwoVar]) then
begin
try
Next2Term := nil;
new(Next2Term);
inc(CurrentBracket);
if CurrentBracket > maxBracketLevels then
begin
Error := true;
raise ETooManyNestings.Create(msgNestings);
end;
if CurBracketLevels < CurrentBracket then
CurBracketLevels := CurrentBracket;
i := BracketLevel[CurrentBracket] + 1;
if i > maxLevelWidth then
begin
Error := true;
raise EExpressionTooComplex.Create(msgTooComplex);
end;
with Next2Term^ do
begin
StartString := LastTerm^.RightString;
LeftString := BlankString;
RightString := BlankString;
Token := variab;
Position[1] := CurrentBracket;
Position[2] := i;
Position[3] := 2;
Previous := LastTerm;
Next1 := nil;
Next2 := nil;
end;
LastTerm^.Next2 := Next2Term;
Next2Term := nil;
inc(BracketLevel[CurrentBracket]);
except
on E: Exception do
begin
if assigned(Next2Term) then
begin
dispose(Next2Term);
Next2Term := nil;
end;
end;
end;
end
else
raise EParserInternalError.Create(msgInternalError);
end;
end;
with LastTerm^ do
if Next1 = nil then
begin
{ we are done with THIS loop }
break;
end
else
if Next2 = nil then
LastTerm := Next1
else
LastTerm := Next2;
until false; { endless loop, break'ed 7 lines above }
if LastTerm = FirstTerm then
begin
dispose(LastTerm);
FirstTerm := nil;
break; { OK - that is it, we did not find any more terms}
end;
repeat
with LastTerm^ do { cannot use "with LastTerm^" OUTSIDE loop }
begin
if Next1 <> nil then
begin
dispose(Next1);
Next1 := nil;
end;
if Next2 <> nil then
begin
dispose(Next2);
Next2 := nil;
end;
LastTerm := Previous;
end;
until ((LastTerm^.Token in (TokenOperators + [FuncTwoVar])) and (LastTerm^.Next2 = nil)) or
(LastTerm = FirstTerm);
with FirstTerm^ do
if (LastTerm = FirstTerm) and
( (Token in [minus, square, third, fourth, FuncOneVar]) or
((Token in (TokenOperators + [FuncTwoVar])) and Assigned(Next2))
) then
begin
break;
end;
until false;
{ after having built the expression matrix, translate it into a tree/list }
with FirstTerm^ do
if FirstTerm <> nil then
begin
if Next1 <> nil then
begin
dispose(Next1);
Next1 := nil;
end;
if Next2 <> nil then
begin
dispose(Next2);
Next2 := nil;
end;
dispose(FirstTerm);
end;
BracketLevel[0] := 1;
if CurBracketLevels = 0 then
begin
FirstOP := Matrix^[0,1];
Matrix^[0,1] := nil;
FirstOP^.Dest := FirstOP^.Arg1;
end
else
begin
FirstOP := Matrix^[CurBracketLevels, 1];
LastOP := FirstOP;
for counter2 := 2 to BracketLevel[CurBracketLevels] do
begin
LastOP^.NextOperation := Matrix^[CurBracketLevels, counter2];
LastOP := LastOP^.NextOperation;
end;
for counter1 := CurBracketLevels-1 downto 1 do
for counter2 := 1 to BracketLevel[counter1] do
begin
LastOP^.NextOperation := Matrix^[counter1, counter2];
LastOP := LastOP^.NextOperation;
end;
with Matrix^[0,1]^ do
begin
Arg1 := nil;
Arg2 := nil;
Dest := nil;
end;
dispose(Matrix^[0,1]);
end;
dispose(Matrix);
except
on E: Exception do
begin
if Assigned(Matrix) then
begin
if assigned(Matrix^[0,1]) then
dispose(Matrix^[0,1]);
for counter1 := CurBracketLevels downto 1 do
for counter2 := 1 to BracketLevel[counter1] do
if Assigned(Matrix^[counter1, counter2]) then
dispose(Matrix^[counter1, counter2]);
dispose(Matrix);
end;
if Assigned(Next1Term) then
dispose(Next1Term);
if Assigned(Next2Term) then
dispose(Next2Term);
{ do NOT kill this one at it is possibly the same as LastTerm (see below)!
if Assigned(FirstTerm) then
dispose(FirstTerm);
instead, DO kill ANewTerm, which will only be <> nil if it has NOT passed
its value to some other pointer already so it can safely be freed
}
if Assigned(ANewTerm) then
dispose(ANewTerm);
if Assigned(LastTerm) and (LastTerm <> Next2Term) and (LastTerm <> Next1Term) then
dispose(LastTerm);
FirstOP := nil;
raise; { re-raise exception }
end; { on E:Exception do }
end;
end;
end.