1. Description ------------------- The module PASCALC.DCU is the Pascal-like language interpreter for Delphi. The basic differences from standard Pascal the following: - All variables stored as variants. - It's no need to declare variables, labels and functions. PASCALC creates variables dynamically on first assignment. Variable type depends on the last value assigned, type checking is not carried out. - Expressions syntax: Arithmetic operators: +, -, *, /, ^ (power), SHL, SHR Bitwise operators: BITOR,BITAND,BITXOR,BITNOT Logical operators: >, <, >=, <=, =, <>, AND, OR, NOT, constants TRUE and FALSE. Operators precedence standard, you can use parentheses. - Statements supported: BEGIN... END IF... THEN... ELSE CASE FOR... TO/DOWNTO... DO WHILE... DO REPEAT... UNTIL BREAK CONTINUE GOTO EXIT USES INCLUDE - All reserved words are declared in interface section as string array. You can change them to anyone others. All identifiers should be different (except assignment and equation, they may be the same). - Multi-dimensional arrays supported. Array items are variables whose names consist from array name and indexes (in brackets). Array items properties same as the simple variables. It's no need to declare arrays. Array index continuity is not requred. It's possible to access string as char array. You can assign chars or numbers in range 0..255 to string characters. Array name should be unique, simple variable MyArr and array item MyArr[1] can't exist at the same time. This rule allows to distinguish access to 1-st character of string variable MyArr (MyArr[1]) from the accees to the first item of array MyArr. To access character of string array item, character index should be written in brackets after item name, for example MyArr[1,2][3]. - All build-in functions are user-defined. Module pasfunc.pas contain example of PASCALC implementations for common Delphi functions. You can use this functions and write your own implementations for necessary functions. There is no need to declare parameters for user-defined functions, therefore it's types and amount are not limited. If necessary you can provide type checking inside function code. Interpreter calculates all parameter values and creates temporary values list (TVarList). Names of var-parameters in this list are 'VAR', otherwise 'VALUE'. After function call interpreter updates values for var-parameters. Function result type not checked, one function can return number as well as string. Functions can be called as procedures (without result usage). - Procedures and functions (in sense of the subroutines on interpreter language) are supported. The function heading specifies only names of formal parameters, without parameters types and pass method (var or value). Also, function's result types are not declared. To return function result you should assign value to variable "result". If actual parameter is variable (not expression), it considered as var-parameter. In this case assigning new value to this parameter inside procedure or function will affect the actual parameter variable. At the same time, all interpreter global variables inside a function or procedures looks like local variables, with the same initial values as global ones. You can change this values inside procedures (functions), but it's don't affect the global variables. All new variables created inside procedures and functions are local, and will be destroyed after exiting. Therefore, it is possible to use any names for local variables, without conflicts with the global variables. For libraries implementation, it is possible to use operators USES and INCLUDE. Syntax: USES 'filename'; INCLUDE 'filename'; INCLIDE and USES are parsed by pre-processor before script execution. INCLUDE inserts text from file 'filename' into the script, USES loads procedures and functions declarations. 2. License ---------- This software is provided as it is, without any kind of warranty given. The author can not be held responsible for any kind of damage, problems etc, arised from using this software. PASCALC interpreter is shareware product. Unregistered version has no time or functionality limitations, but can show info screen. If you want get source code, you should register PASCALC and get registration code. Use your registration code as password for source archive (Source\pascsrc.zip). Address for online registration (price $20): https://www.regnow.com/softsell/nph-softsell.cgi?item=2608-2 You can modify PASCALC source code as you need, distribute executable files compiled with PASCALC source. But you can't distribute original or modified PASCALC source code, .DCU units or libraries based on PASCALC source code. 3. PASCALC inteface ------------------- unit pascalc; {$F+,B-,R-} interface uses Windows, Messages, SysUtils, Classes, Math; type TToken = (tEMPTY, tVR, tCON, tTRUE, tFALSE, tEQU, tOR, tAND, tNOT, tXOR, tCOMMA, tLBL, tNEQ, tGT, tLS, tGTE, tLSE, tADD, tSUB, tMUL, tDIV, tPWR, tLBR, tRBR, tLARR, tRARR, tSEMI, tREM, tREMB, tREME, tASSIGN, tBEGIN, tEND, tIF, tTHEN, tELSE, tFOR, tTO, tDOWNTO, tDO, tWHILE, tREPEAT, tUNTIL, tBREAK, tCONTINUE, tEXIT, tGOTO, tSHL, tSHR, tPROC, tFUNCT, tUSES, tINCLUDE, tCASE, tOF, tCOMMA2); type TTokenSet = set of TToken; const ResWords : array[TToken] of string[10] = ('', '', '', 'TRUE', 'FALSE', '=', 'OR', 'AND', 'NOT', 'XOR', ',', ':', '<>', '>', '<', '>=', '<=', '+', '-', '*', '/', '^', '(', ')', '[', ']', ';', '//', '{', '}', ':=', 'BEGIN', 'END', 'IF', 'THEN', 'ELSE', 'FOR', 'TO', 'DOWNTO', 'DO', 'WHILE', 'REPEAT', 'UNTIL', 'BREAK', 'CONTINUE', 'EXIT', 'GOTO', 'SHL', 'SHR', 'PROCEDURE', 'FUNCTION', 'USES', 'INCLUDE', 'CASE', 'OF', '..'); const Alpha : set of char = ['_','0'..'9','a'..'z','A'..'Z','ā'..'˙','¸','Ā'..'ß','¨']; StrDelimiter : char = ''''; DecimalPoint : char = '.'; TokenDelimiter : char = #127; type TVar = record Name : string; Value : variant; end; type TPVar = ^TVar; type TVarList = class (TList) destructor Destroy; override; procedure ClearAll; function AddVar(V:TVar) : boolean; function AddValue(N:string; V:variant) : boolean; function VarExist(N:string):boolean; function VarIndex(N:string):integer; function VarByName(N:string;var V:TVar) : boolean; function SetVar(V:TVar) : boolean; function SetValue(N:string; V:variant) : boolean; procedure CopyTo(VL:TVarList); end; type TPVarList = ^TVarList; type PProcessProc = procedure; type PFunction = function(Sender:TObject; var A:TVarList; var R:TVar) : boolean; type TFunc = record Name : string; Func : Pointer; end; type TPFunc = ^TFunc; type TFuncList = class (TList) destructor Destroy; override; procedure ClearAll; function AddFunction(N:string; F:Pointer) : boolean; end; type TProcedure = record Name : string; Body : string; Params : string; Result : boolean; end; type TPProcedure = ^TProcedure; type TProcList = class(TList) destructor Destroy; override; procedure ClearAll; function AddProc(Proc:TProcedure):boolean; function ProcIndex(Name:string):integer; function ProcByName(Name:string; var Proc:TProcedure):boolean; end; type TPasCalc = class constructor Create; destructor Destroy; override; procedure ClearVars; function VarCount : integer; function VarIndex(N:string) : integer; function VarByName(N:string; var V:TVar) : boolean; function VarByIndex(I:integer; var V:TVar) : boolean; function SetVar(V:TVar) : boolean; function SetValue(N:string; V:variant):boolean; procedure ClearFuncs; function SetFunction(N:string; F:Pointer) : boolean; procedure SetProcessProc(P:Pointer); function Parse(S:string) : string; function Calculate(S:string; var R:TVar) : boolean; function Execute(S:string):boolean; private Expr : string; ExprIndex : integer; Token : string; TokenCode : TToken; BlockLevel : integer; BlockCmd : TToken; GotoLabel : string; VarList : TVarList; FuncList : TFuncList; ProcList : TProcList; ProcessProc : PProcessProc; LastString : string; LastParsed : string; procedure Clear; procedure Process; procedure Error(Msg,Line:string; Code:integer); procedure Level1(var R:TVar); procedure Level2(var R:TVar); procedure Level3(var R:TVar); procedure Level4(var R:TVar); procedure Level5(var R:TVar); procedure Level6(var R:TVar); procedure Level7(var R:TVar); procedure Level8(var R:TVar); procedure Arith(o : TToken; var R,H:TVar); procedure Unary(o : TToken; var R:TVar); function GetIndex(S:string; var Index:integer; var T:TToken) : string; function GetFuncParams(S:string; var Index:integer) : string; function FindFunc(N:string) : integer; function FindArray(N:string) : boolean; procedure SetVarDirect(var R:TVar); function CallFunc(N:string; A:string; var V:TVar) : boolean; function CallProc(N:string; A:string; var V:TVar) : boolean; function GetTextToken(S: string; var Index : integer; var Code : TToken) : string; function TokenStr(T:TToken;S:string) : string; function GetToken(S:string; var Index : integer; var Code : TToken) : string; function GetTokenCode(S: string; var Index:integer; var Code:TToken) : integer; function GetTokenLine(S:string; var Index:integer; var Code:TToken; StopToken:TTokenSet) : string; function NextToken(S:string; Index:integer) : TToken; function GetOperator(Txt:string; var Index : integer; EndToken:TTokenSet) : string; function ParseOperator(Txt:string; var Cmd,Line,Lbl : string) : TToken; function DelRemarks(S:string) : string; function UnParse(S:string; Show:boolean) : string; function PreProcess(Txt:string):string; function Calc(S:string; var R:TVar) : boolean; procedure Exec(Txt:string); procedure DoSet(CmdLine,Cmd,Line:string); procedure DoIf(CmdLine,Line:string); procedure DoBegin(CmdLine,Line:string); procedure DoFor(CmdLine,Line:string); procedure DoBreak(CmdLine,Line:string); procedure DoContinue(CmdLine,Line:string); procedure DoExit(CmdLine,Line:string); procedure DoWhile(CmdLine,Line:string); procedure DoRepeat(CmdLine,Line:string); procedure DoGoto(CmdLine,Line:string); procedure DoCase(CmdLine,Line:string); public Stop : boolean; ErrCode : integer; ErrMsg : string; ErrLine : string; end; 4. Functions (unit PASFUNC.PAS) ------------------------------ // String functions Val IntToStr StrToInt FloatToStr StrToFloat Copy Pos Length Insert Delete Trim TrimLeft TrimRight UpperCase LowerCase Format // DateTime functions Now Date Time DateToStr StrToDate TimeToStr StrToTime FormatDateTime DayOfWeek IncMonth DecodeDate DecodeTime EncodeDate EncodeTime YearDays YearFrac // Math functions Abs Int Frac Round Ceil Floor Trunc Sin Cos Tan ArcSin ArcCos ArcTan Exp Ln IntPower Sqr Sqrt Min Max Inc Dec // Variable handling functions. // Allows get or set variable value by calculated variable name. SetVar GetVar // Misc functions Decode (simular with the Oracle Decode function) 5. Interpreter usage -------------------- See usage example in demo program (demo.dpr). At first you should write user-defined functions you want to use in scripts and expressions. Function should set result value (field Value of variable R:TVar) and return true. On errors (wrong parameters, etc) function should return false. In your program you should create an instance of TPasCalc class. Then associate functions with names (TPasCalc.SetFunction). To register pasfunc.pas function library, call SetFunctions. You can set predefined variables (TPasCalc.SetVarNum or TPasCalc.SetVarStr). Method TPasCalc.Calculate evaluates expression and returns result in variable R:TVar. Method TPasCalc.Execute executes script and fills variable list. You can access variables using TPasCalc.VarCount, TPasCalc.VarByIndex or TPasCalc.VarByName methods. TPasCalc.ErrCode, TPasCalc.ErrMsg and TPasCalc.ErrLine indicates last error code, error message and line that raises error. Variable list not cleared in Calculate and Execute methods. You can reset all variables using TPasCalc.ClearVars. TPasCalc.ClearFuncs resets function list. Method TPasCalc.SetProcessProc sets pointer to user-defined callback procedure. Interpreter calls this procedure in internal cycles. In this procedure you can check some condition (timeout for example) and break execution by assigning TPasCalc.Stop:=TRUE. It is useful to call Application.ProcessMessages in this procedure. If callback procedure not assigned, interpreter uses Process method instead. Process method performs only messages processing. To improve perfomance you can prepare script using Parse method, and then execute previously prepared script. 6. Error codes -------------- 0 - O.K. 1 - Error in expression 2 - Unpaired parentheses 3 - Variable not found 4 - Invalid variable/function/procedure name 5 - Invalid typecast 6 - Invalid string constant 7 - Incorrect function call 8 - Function not found 9 - Invalid operator 10 - END expected 11 - Too many END 12 - TO or DOWNTO expected 13 - FOR-loop variable expected 14 - DO expected 15 - BREAK outside a loop 16 - UNTIL expected 17 - Too many UNTIL 18 - Label not found 19 - Index out of range 20 - Value out of range 21 - ']' expected 22 - Too many '[' 23 - Division by zero 24 - Variable or array name duplicated 25 - File îpen error 26 - Function must return result 27 - CASE without OF 28 - Too many ELSE in CASE statement 29 - Case range expected 7. Author --------- Alex Boiko alexboiko@mtu-net.ru http://alexboiko.da.ru http://alexboiko.chat.ru