Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works

Random project

unCalc

View on GitHub

Anyone may press Interesting, signed in or not. It helps others find this project.

Member rating

No ratings yet.

Sign in to rate this project.

README

unCalc

image

Тексты не приглажены, а взяты как есть из проекта. Поэтому, возможно, Вам придется кое-что не нужное удалять от туда самостоятельно.

Для выделения из программы баз данных бизнес логики в отдельный программный слой, который, конечно должен иметь возможность полностью настраиваться без перекомпиляции программы, можно использовать какой-нибудь интерпретатор. Тогда появится возможность просто менять программу для интерпретатора и тем самым менять бизнес логику программы. Давайте посмотрим, как можно сделать простейший интерпретатор. Для простоты синтаксиса, а значит и программной реализации, мы не будем придерживаться какого-то известного языка программирования. Назовем наш интерпретатор калькулятором – это нейтрально и непритязательно, так сказать без особых амбиций…

Калькулятор представляет собой двухпроходный интерпретатор, способный выполнять математические, логические, и другие операции, вызывать встроенные или внешние функции. Встроенные функции заложены в калькулятор при его создании, а внешние – создаются программистом при создании приложения. Обычно внешние функции нужны для выполнения действий над данными, которые обрабатывает программа, поэтому приходится каким-то образом дать доступ калькулятора к этим данным, что и вызвало появление внешних функций.

В калькуляторе используется простой синтаксис без понятия блока программного кода и области видимости. Т.е. все переменные являются глобальными, так же нет блоков, наподобие begin … end или { … }, которые присутствуют в языках высокого уровня Паскаль и C++. Несмотря на свои скромные языковые конструкции, калькулятор может выполнять все те же действия, что и вышеизложенные языки, за исключением определения типов пользователя.

Такой синтаксис не случаен. Дело в том, что область применения калькулятора – это обработка событий, вызванных действиями пользователя, и реакций на происходящие события в программе. Время реакции на эти события должно быть минимальным, поэтому процесс пре компиляции и выполнения должен проходить быстро, что требует приближения синтаксиса к оптимальному с точки зрения машины, а не человека. Но, т.к. в этих случаях обычно очень сложных программ писать не приходится, то с некоторой бедностью синтаксиса можно смирится.

Алфавит встроенного калькулятора. Алфавит встроенного калькулятора включает в себя следующие символы:

Все буквы латинского алфавита.

Цифры от 0 до 9

Специальные символы:

pascal
???                   - ?????????????????? ?????????????????? ??????????????????
pascal
;           - ?????????????????? ????????????????
pascal
:           - ???????????? ?????????? ??????????
pascal
,          - ?????????????????? ??????????????????, ???????????????????????? ????????????????
pascal
+                   - ????????????????
  • pascal
    - ??????????????????
  • pascal
    - ??????????????????

/ - деление

( и ) - скобки в операторах или скобки вокруг параметров, передаваемых функции

< - меньше

pascal
- ????????????

= - равно

А так же составные символы:

pascal
= =    - ??????????????????
pascal
>=     - ???????????? ?????? ??????????
pascal
<=     - ???????????? ?????? ??????????
pascal
<>     - ???? ??????????

Словарь языка встроенного калькулятора. Как в большинстве языков программирования, неделимые последовательности знаков алфавита образуют слова, отделяемые друг от друга разделителями, и несущими определенный смысл. Разделителями могут служить символ пробела, конца строки, конца оператора, т.е. точка с запятой, комментарии, другие специальные символы и их комбинации.

pascal
?????? ?????????? ???????????????????????????? ????:
pascal
???????????????? ??????????,
pascal
?????????????????????? ????????????????????????????,
pascal
???????????????????????????? ????????????????????????.

Ключевые слова являются составной частью языка, имеют фиксированное написание и однозначно определенный смысл:

End – конец программы.

Not – отрицание.

Or – объединение.

Стандартные идентификаторы служат для обозначения заранее определенных конструкций:

True - истина

False - ложь

Pi - число пи = 3.1415926535897932385

Имена встроенных и внешних функций.

Идентификаторы пользователя применяются для обозначения имен меток, переменных, констант, процедур, определенных пользователем. Все идентификаторы должны удовлетворять следующим требованиям:

pascal
???????????????????????????? ???????? ???????????????????? ?? ????????????????, ???????????????????????? ?????????????????????????? ???? ???????????? ???????????????????? ?? ????????.
pascal
???????????????????????????? ???? ???????????? ???????????????????? ?? ???????????????? !, :, _ , ??.??. ???? ?????????? ???????????????????????? ?????? ?????????????? ?? ?????????????????? ??????????.
pascal
?????? ???????????????????? ???????????????????????? ?????????????? ?? ???????????????? ?? ?????????????????? ???????????????? ?? ???????????? ?????????????????????????????? ?????? ??????????????????.
pascal
?????????? ?????????? ???????????????????????????????? ???????????? ???????? ???????? ???? ???????? ??????????????????????.

Структура программы встроенного калькулятора. Программа встроенного калькулятора состоит из операторов. Операторы могут быть как одиночными, так и составными. Программа обязательно должна заканчиваться словом end .

Строковые константы обрамляются двойными кавычками. Все операторы должны заканчиваться знаком точка с запятой. Метки начинаются двоеточием как в теле программы, так и в строковой константе передаваемой в качестве параметра функциям, например:

…

GoTo(“:Label1”);

…

:Label1;

Все переменные являются без типовыми, а точнее вариантными типами, поэтому Вы можете присваивать одной и той же переменной различные значения разных типов, но при выполнении операций, при неправильных типах значений переменных могут происходить исключительные ситуации. Например:

A = “2”;

B = 3;

C = A + B; // даст в результате 5.

A = “Вася”;

B = 3;

C = A + B; // даст в результате строку “Вася3”

C = A / B // вызовет исключительную ситуацию.

В программе Вы можете использовать комментарии. Комментарии начинаются с двойной наклонной черты - //. И заканчиваются концом строки, т.е. как короткие комментарии в С++.

Порядок выполнения операций естественный, принятый в математике, т.е. сначала операторы в скобках и функции, умножение и деление, сложение и вычитание, булевы операторы, например: 2+2*2 = = 6, а не 8. Вы можете использовать скобки, чтобы переопределить очередность выполнения операций, например: (2 + 2)*2 = = 8.

В качестве параметров функциям можно передавать другие функции и т.д., например: C = sin(cos(Z));

В числовых константах используется в качестве разделителя дробной и целой части только точка при любых настройках Вашей операционной системы. В качестве разделителя в датах – точка, а в качестве разделителей времени – двоеточие.

Все остальное, даже циклы и безусловные переходы мы организуем либо встроенными, либо внешними функциями. Если функция возвращает значение, которое Вам не нужно, Вы можете его игнорировать, т.е. записывать её в программе как процедуру без присваивания.

Вот как выглядит типичная программа:

Если ошибка, то просто уходим из программы

OnExcept(":Exit");

Получаем код текущей таблицы. Здесь IdMain и IdType – встроенные переменные

ThisTable = tTableId(IdMain, IdType);

Получаем код записи, которую будем искать в справочнике при его открытии

FindValue = tGetValByFieldNum(ThisTable, 74);

Получаем код другой таблицы – таблицы справочника

Categors = tTableId(5, 3);

Подготавливаем справочник к открытию

SetOpenDoc(5, 3, "Категории социального положения");

Открываем справочник. OpenReference вернет true, если пользователь нажмет Ok

if(OpenReference(5, 3, FindValue), ":SetVal", ":Exit");

:SetVal;

Устанавливаем код из справочника в текущую запись

tSetValByFieldNum(ThisTable, 74, tGetValByName(Categors, "ID_NUM"));

:Exit;

end;

Вот, собственно, все начальные условия, которые нам предстоит выполнить.

Проектирование калькулятора. Калькулятор должен иметь как минимум следующие блоки:

Компилятор, точнее что-то вроде анализатора текста ( парсер ), разбивающего текст на лексемы, и создающего нужные объекты для дальнейшего выполнения программы. Два стека – вычислительный и стек вызова подпрограмм. Я на всякий случай разделил их для простоты. Сам вычислитель, способный правильно выполнять программу, согласно правилам математики, вызывать функции и т.д. Контейнер переменных. Контейнер встроенных функций. Контейнер внешних функций, способный перед выполнением программы регистрировать внешние функции. Ну, и собственно, компонент – контейнер для всех этих объектов, который может согласовать их работу, который можно поставить на форму.

Тексты – разбор полетов.

Калькулятор. TunCalck = class(TComponent)

private

pascal
FCompiler: TunCompiler;
pascal
FStack: TunStack;
pascal
FSubStack: TunStack;
pascal
FVariables: TunVariables;
pascal
FFunctions: TunFunctions;
pascal
FStalker: TunStalker;
pascal
FExternals: TunExternals;
pascal
function GetCompProgramm: TStringList;
pascal
function GetProgramm: TStringList;
pascal
function GetVariable(const AName: string): Variant;
pascal
procedure SetVariable(const AName: string; const AVal: Variant);
pascal
function GetIsDebug: boolean;
pascal
procedure SetIsDebug(const Value: boolean);

protected

pascal
procedure DoCheckName(const VarName: string; var TypeName: TEnumToken;
pascal
var InternalName: string);

public

pascal
constructor Create(AOwner: TComponent); override;
pascal
destructor Destroy; override;
pascal
procedure Compile;
pascal
function Run: Variant;
pascal
property Programm: TStringList read GetProgramm;
pascal
property CompilerProgramm: TStringList read GetCompProgramm;
pascal
property Variable[const AName: string]: Variant read GetVariable write SetVariable;
pascal
property Variables: TunVariables read FVariables;
pascal
property Functions: TunFunctions read FFunctions;
pascal
property Externals: TunExternals read FExternals;

published

end;

Fcompiler – это наш компилятор, упомянутый как пункт 1. Fstack и FsubStack – два стека для вычислений и подпрограмм, соответственно. Fvariables – контейнер с переменными. Ffunctions – контейнер с встроенными функциями. Fexternals – контейнер внешних функций. И, наконец, Fstalker – сам вычислитель.

Посмотрим, что собственно, происходит, когда нам нужно что-то посчитать. Например, нужно посчитать видимость пункта меню для пользователя:

…

Присваиваем калькулятору программу.

pascal
Calck.Programm.Assign( Work[I].WorckCheckPrg);
pascal
?????????????????????????? ???????????????? ?????????? ???????????????????? ?? ??????????????????
pascal
Calck.Variable[???VISIBLE???]:=true;
pascal
Calck.Variable[???ID_TYPE???]:=TypeDescriptor.ID;
pascal
Calck.Variable[???ID_MAIN???]:=MainOwner;
pascal
?????????????????? ?????????????????? ???? ????????????????????.
pascal
Calck.Run;
pascal
???????????????? ??????????????????, ?????????????????????? ??????????????????????????.
pascal
isOk:=Calck.Variable[???VISIBLE???];
pascal
???

Здесь isOk – видимость пункта меню.

Это один из способов, но можно зарегистрировать внешние функции, и управлять поведением программы с их помощью. Этот вариант мы рассмотрим позже.

Вот метод Run:

function TunCalck.Run: Variant;

var AOldTimeSeparator: Char;

pascal
AOldDateSeparator: Char;
pascal
AOldDecimalSeparator: Char;

Begin

Если программа пуста, то просто выходим, иначе “компилируем”

if Trim(Programm.Text) <> '' then

pascal
FCompiler.Compile