Компилятор языка формул

Модуль занимается полным циклом работ с формулами JetCalc (лексический анализ, семантический анализ и трансляция формул в исполняемый код).

Основные задачи модуля

  1. Анализ синтаксиса формул (parser.jison)

  2. Упрощение формул, в зависимости от контекста (column.jison, compile.jison)

  3. Вычисление формул (calculator.jison)

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

Модуль написан с использованиемJISON(наиболее известный пример использования JISON - CoffeScript).

В качестве дополнительных инструментов для отладки и расширения функциональности модуля может использоватьсяJISON debugger

JISON состоит из следующих частей:

  1. Лексический анализатор (токенайзер)

  2. Парсер выражений.

  3. Реализация вычислений

Лексический анализатор (токенайзер)

%lex
%parse-param CONTEXT
%%

\s+                                            return '';
\+\s\+                                         return '+';
"*"                                            return '*';
"/"                                            return '/';
"-"                                            return '-';
"+"                                            return '+';
"^"                                            return '^';
"("                                            return '(';
")"                                            return ')';
"}"                                            return '}';
","                                            return ',';
"{"                                            return '{';
"AND"|"and"                                    return 'AND';
"OR"|"or"                                      return 'OR';
"NOT"|"not"|"!"                                return 'NOT';
"!="                                           return 'NE'; 
"<="                                           return 'LTE';
">="                                           return 'GTE';
">"                                            return 'GT';
"<"                                            return 'LT';
"=="                                           return 'EQ';
"true"|"TRUE"                                  return 'TRUE';
"false"|"FALSE"                                return 'FALSE'
"NULL"|"null"                                  return 'NULL';


periodin
|colin
|objin
|grpin
|divin
|coltagin
|objtagin
|rowtagin
|groupin                                       return 'BOOLFUNC'; 

treetagin
|pathin
|rowin
|rowgroupin
|rowsumgrpin                                   return 'U_BOOLFUNC';

[0-9]*[1-9]+[0-9]*("."[0-9]+)?\b                     
|[0]+"."[0-9]*[1-9]+[0-9]*?\b                  return 'NUMBER';

[0]+("."[0]*)?\b                               return 'ZNUMBER';

\".*?\" 
|\'.*?\'                                       return 'LITERAL';

'f.If'
|'if'                                          return 'IF';

[$@.].*?\?                                     return 'VARIABLE';

':'                                            return  'CASEDELIM';
';'                                            return  'EOC';

'forcol'
|'forobj'
|'fordiv'                                      return 'SWITCH_FUNC';

[A--Я0-9]+(?=^|$|[^\p{L}])                   return 'MIXED';

<<EOF>>                                        return 'EOF';

/lex

%left '+' '-'
%left '*' '/'
%left '^'
%left UMINUS
%left OR
%left AND
%left NOT

%%

Стоит обратить внимание на то, что в коде присутствуют токены начинающиеся с "U" (Uncalculated -невычисляемый), при этом они имеют зеркальные токены без такой приставки. Например, BOOLFUNC, U_BOOLFUNC. Позднее в парсере выражений - невычисляемые токены остаются такими, какими были, а вычисляемые вычисляются. Все парсеры между собой отличаются только перечислением функций которые нужно вычсислять и какие нужно оставить для более позднего вычисления.

Например в коде парсера колонок (column.jison) это выглядит - так:

Все булевые функции, которые зависят от рядов - остаются, другие - вычисляются сразу. Крайние случаи parser.jison и calculator.jison: если в первом все функции и переменные невычисляемые, то во втором невычисляемых - нет.

Парсер выражений

В данном блоке стоит обратить внимание на то, что модуль имеет отдельную структуру для работы с "0". С одной стороны это особенность, что деление на ноль здесь будет не бесконечность, а ноль. С другой стороны, в коде прописаны ситуации когда реализация простых правил ("что угодно" умножить на ноль = получается ноль и т.д.) позволяет значительно сократить объемы вычислений (не вычислять это "что угодно")

Как происходит вычисление: расмотрим простой пример 2+2*2

Поскольку мы не описали правил для сложений или умножения 2-х типов Number, то их тип повышается постепенно до Numeric и Math (на уровне Math арифметические операции мы уже описали) Почему произошло сначала умножение, а потом сложение - потому, что мы это описали в правилах приоритетов вычислений:

Последняя часть модуля - реализация вычислений

В этом модуле прописано, как ведет себя if, if...else, switch и прочии конструкции, но в целом - ничего интересного нет.

Несколько слов по копиляции парсеров. После того, как были внесены изменения в код jison парсеров, необходимо запустить консольную админку (node admin.js) и выбрать пункт компиляция парсеров

Окончание процесса компиляции сопровождается запуском mocha-тестов по проверке работы парсера Сами тесты находятся в JSON файле ./classes/calculator/jison/mocha/Documents/blank.js и имеют следующий формат:

Все тесты - должны проходиться

Last updated

Was this helpful?