464 lines
16 KiB
Plaintext
464 lines
16 KiB
Plaintext
1. Описание
|
||
-----------
|
||
|
||
Модуль pascalc.dcu представляет собой интерпретатор Pascal - подобного
|
||
языка для Delphi. Основные отличия его от стандартного Паскаля следующие:
|
||
|
||
- Для значений переменных используеися тип Variant, что позволяет хранить
|
||
разнообразные типы данных.
|
||
|
||
- Переменные обьявлять не надо, они при небходимости создаются динамически
|
||
при присваивании им значений. Тип определяется по последнему присвоенному
|
||
значению, типовый контроль при присваивании не выполняется. То есть если
|
||
существующей числовой переменной присвоить строку, ее тип изменится.
|
||
Символам строки можно присваивать числовые значения в диапазоне 0..255 или
|
||
символы (строки длиной 1).
|
||
|
||
- В выражениях поддерживаются следующие операции:
|
||
арифметические: +, -, *, /, ^ (возведение в степень), SHL, SHR
|
||
операции с битами: BITOR,BITAND,BITXOR,BITNOT
|
||
логические: >, <, >=, <=, =, <>, AND, OR, NOT, константы TRUE и FALSE.
|
||
Tакже можно использовать скобки. Порядок выполненния операций стандартный.
|
||
|
||
- В интерпретаторе поддерживаются операторы:
|
||
BEGIN ... END
|
||
IF ... THEN ... ELSE
|
||
CASE
|
||
FOR ... TO/DOWNTO ... DO
|
||
WHILE ... DO
|
||
REPEAT ... UNTIL
|
||
CONTINUE
|
||
BREAK
|
||
GOTO
|
||
EXIT
|
||
USES
|
||
INCLUDE
|
||
|
||
- Все зарезервированные слова объявлены в interface как массив строк.
|
||
При желании вы можете изменить их на любые другие, например сделать
|
||
русскими. Допускаются одинаковые операторы присваивания и равенства.
|
||
Интерпретатор может сам различать их в зависимости от контекста. Все
|
||
остальные зарезервированные слова должны отличаться друг от друга.
|
||
|
||
- Поддерживаются многомерные массивы, которые хранятся как набор переменных.
|
||
Имена таких переменных состоят из имени массива и индексов в квадратных
|
||
скобках. Имя массива должно быть уникальным, не допускается одновременное
|
||
существование обычной переменной и переменной массива с тем же именем.
|
||
(например переменная с именем MyArr и элемент массива MyArr[1]).
|
||
Так как элементы массивов хранятся как обычные переменные, на них
|
||
распространяются все правила применимые к обычным переменнм. Массивы не надо
|
||
обьявлять, элементы одного массива могут хранить как строки так и числа.
|
||
Для диапазона индексов массивов нет никаких ограничений. Непрерывность
|
||
элементов массива также не обязательна. Чтобы создать непрерывную
|
||
последовательность элементов массива им надо просто присвоить (например в цикле)
|
||
какие - либо значения. Кроме того, к символам строк можно обращаться как к
|
||
массиву символов. Так например MyArr[1][2] значит обрашение ко 2-му символу
|
||
1-й строки массива MyArr. Запись MyArr[1] может значить либо первый символ
|
||
обычной строковой переменной MyArr, либо первый элемент массива. Но так как
|
||
элемент массива и переменная с таким же именем одновременно существовать не могут,
|
||
неоднозначность не возникает.
|
||
|
||
- Все встроенные функции интерпретатора user-defined. Mодуль pasfunc.pas содержит
|
||
библиотеку в которую включены аналоги многих функций Delphi. Пример ее
|
||
использования есть в demo-программе. Вы можете использовать эту библиотеку,
|
||
дополнив при необходимости любым количеством своих функций снужными вам
|
||
параметрами. Параметры функций заранее не описываются, поэтому их количество
|
||
и типы ничем не ограничены (подобно процедуре write паскаля). Интерпретатор
|
||
вычисляет все параметры конкретного вызова и передает их функции как список
|
||
значений. При необходимости вы можете реализовать типовый контроль внутри
|
||
своей реализации функции. Если в качестве параметра функции передается
|
||
переменная интерпретатора, этот пареметр считается VAR-параметром, и его
|
||
изменение в коде функции вызовет изменение значения соответствующей переменной
|
||
интерпретатора. Тип возвращаемого функцией значения тоже может быть разным при
|
||
разных параметрах вызова. В модуле pasfunc.pas есть пример реализации таких
|
||
функций (ф-ции Max, Min). Функции можно вызывать как процедуры, не используя
|
||
возвращаемое значение.
|
||
|
||
- Поддерживаются процедуры и функции на языке интерпретатора. В списке параметров
|
||
процедур и функций необходимо только перечислить имена формальных параметров.
|
||
Типы параметров и способ передачи указывать не надо, также в объявлении функций
|
||
не надо указывать тип возвращаемого значения. Для возврата значения функции
|
||
используется переменная "result". Если в качестве параметра функции или процедуры
|
||
передается глобальная переменная интерпретатора, этот параметр для данного вызова
|
||
считается VAR-параметром и его изменение внутри функции изменит значение
|
||
глобальной переменной. В то же время все глобальные переменные интерпретатора
|
||
внутри функции или процедуры ведут себя, как локальные переменные, которым
|
||
присвоены начальные значения совпадающие со значениями одноименных глобальных
|
||
переменных. Их значения доступны в процедурах и функциях, их можно изменить,
|
||
но после выхода из процедуры или функции значение глобальной переменной не
|
||
изменится. Все новые переменные, созданные внутри процедур и функций являются
|
||
локальными, и после выхода из процедуры/функции исчезнут. Таким образом, в
|
||
процедурах и функциях можно использовать любые имена для локальных переменных,
|
||
не опасаясь что они совпадут с именами глобальных переменных.
|
||
|
||
Для реализации библиотек процедур можно использовать операторы USES и INCLUDE.
|
||
Их синтаксис : USES 'имя файла';
|
||
INCLUDE 'имя файла';
|
||
|
||
Операторы USES и INCLUDE обрабатываются препроцессором, до начала выполнения
|
||
скрипта. Оператор INCLUDE вставляет в скрипт текст из файла. Оператор USES
|
||
только загружает реализации процедур и функций, чтобы их можно было
|
||
использовать при выполнении скрипта.
|
||
|
||
|
||
2. Лицензия.
|
||
------------
|
||
|
||
Интерпретатор PASCALC распространяется по принципу "как есть".
|
||
При этом не предусматривается никаких гарантий, явных или подразумеваемых.
|
||
Вы используете его на свой собственный риск. Автор не отвечает за потери
|
||
данных, повреждения, потери прибыли или любые другие виды потерь, связанные
|
||
с использованием (правильным или неправильным) этого программного продукта.
|
||
|
||
http://alexboiko.da.ru
|
||
http://alexboiko.chat.ru
|
||
|
||
|
||
3. Интерфейс модуля интерпретатора:
|
||
-----------------------------------
|
||
|
||
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. Функции (unit PASFUNC.PAS)
|
||
------------------------------
|
||
|
||
// Функции для работы со строками
|
||
|
||
Val
|
||
IntToStr
|
||
StrToInt
|
||
FloatToStr
|
||
StrToFloat
|
||
Copy
|
||
Pos
|
||
Length
|
||
Insert
|
||
Delete
|
||
Trim
|
||
TrimLeft
|
||
TrimRight
|
||
UpperCase
|
||
LowerCase
|
||
Format
|
||
|
||
// Функции для работы с датой и временем
|
||
|
||
Now
|
||
Date
|
||
Time
|
||
DateToStr
|
||
StrToDate
|
||
TimeToStr
|
||
StrToTime
|
||
FormatDateTime
|
||
DayOfWeek
|
||
IncMonth
|
||
DecodeDate
|
||
DecodeTime
|
||
EncodeDate
|
||
EncodeTime
|
||
|
||
// Математические функции
|
||
|
||
Abs
|
||
Int
|
||
Frac
|
||
Round
|
||
Ceil
|
||
Floor
|
||
Trunc
|
||
Sin
|
||
Cos
|
||
Tan
|
||
ArcSin
|
||
ArcCos
|
||
ArcTan
|
||
Exp
|
||
Ln
|
||
IntPower
|
||
Sqr
|
||
Sqrt
|
||
Min
|
||
Max
|
||
Inc
|
||
Dec
|
||
|
||
// Функции PASCALC для работы с переменными.
|
||
// Позволяют обращаться к переменной по вычисляемому имени.
|
||
|
||
SetVar
|
||
GetVar
|
||
|
||
|
||
5. Использование интерпретатора
|
||
-------------------------------
|
||
|
||
Пример использования есть в demo-программе. В скриптах интерпретатора вы
|
||
можете использовать готовую библиотеку функций из модуля pasfunc.pas, или
|
||
напиcать собственную реализацию нужных вам функций, которых нет в библиотеке.
|
||
Функция должна записать результат в поле Value переменной R:TVar и вернуть true.
|
||
В случае ошибки (например при вызове с недопустимыми параметрами) функция
|
||
должна вернуть false.
|
||
|
||
Потом создайте экземпляр класса TPasCalc. С помошью метода SetProcessProc
|
||
можно задать указатель на процедуру которая будет регулярно вызываться в
|
||
циклах интерпретатора. С ее помощью можно например реализовать таймаут.
|
||
Если вы задали такую процедуру, то в ней стоит предусмотреть вызов обработчика
|
||
сообщений (Application.ProcessMessages например). Если ее не задали, то вместо
|
||
нее будет использоваться метод Process, в котором предусмотрена обработка сообщений.
|
||
|
||
Затем вызовами TPasCalc.SetFunction надо зарегистрировать функции которые вы
|
||
хотите использовать и задать им имена. В модуле pasfunc.pas регистрация делается
|
||
вызовом процедуры SetFunctions. Также можно задать предопределенные переменные
|
||
вызовами SetVarNum или SetVarStr.
|
||
|
||
Метод Calculate вычисляет значение математического выражения и возвращает
|
||
результат в переменной R:TVar, а метод Execute выполняет текст скрипта и
|
||
заполняет список переменных. С помощью методов VarCount, VarByIndex или
|
||
VarByName вы можете получить их значения. При возникновении ошибок их код
|
||
возврашается в ErrCode. В ErrMsg и ErrLine возвращается сообщение об ошибке
|
||
и строка ее вызвавшая.
|
||
|
||
При следуюших вызовах Calculate и Execute список переменных не очищается и
|
||
их значения можно использовать в выражениях. При желании вы можете сами
|
||
очистить список переменных вызвав ClearVars. Очистить список функций можно с
|
||
помощью ClearFuncs. Если возникла необходимость принудительно прервать
|
||
интерпретацию, надо присвоить значение true перменной TPasCalc.Stop.
|
||
|
||
Чтобы ускорить выполнение скриптов их можно предварительно подготовить
|
||
с помощью метода Parse, который возвращает разобранную строку.
|
||
Ее можно сохранить и затем выполнять теми же методами Calculate и Execute.
|
||
|
||
|
||
6. Коды ошибок (ErrCode)
|
||
------------------------
|
||
|
||
0 - O.K.
|
||
1 - ошибка в выражении
|
||
2 - непарные скобки
|
||
3 - переменная не найдена
|
||
4 - недопустимое имя переменной или функции
|
||
5 - неверный тип операнда
|
||
6 - невернaя строковая константе
|
||
7 - неверный вызов функции
|
||
8 - функция не найдена
|
||
9 - неизвестный оператор
|
||
10 - нехватает END
|
||
11 - лишний END
|
||
12 - нехватает TO или DOWNTO
|
||
13 - нет переменной цикла
|
||
14 - нехватает DO
|
||
15 - BREAK вне цикла
|
||
16 - нехватает UNTIL
|
||
17 - лишний UNTIL
|
||
18 - метка не найдена
|
||
19 - индекс за границей диапазона
|
||
20 - значение за границей диапазона
|
||
21 - нехватает ']'
|
||
22 - нехватает '['
|
||
23 - деление на 0
|
||
24 - Дублируется имя переменной/массива
|
||
25 - Ошибка открытия файла
|
||
26 - Функция должна возвращать значение
|
||
27 - нет OF в операторе CASE
|
||
28 - несколько ELSE в операторе CASE
|
||
29 - неверный диапазон значений в операторе CASE
|
||
|
||
|
||
7. Автор:
|
||
---------
|
||
|
||
Алексей Бойко
|
||
alexboiko@mtu-net.ru
|
||
http://alexboiko.da.ru
|