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.