Regular Expressions For All (REFA)
Основная идея
Существует множество систем для поиска подстрок отвечающих определенной маске. К сожалению они теряют свою мощь как только приходится учитывать многие факторы. Конструкции становятся громозкими, непонятными и трудноподдерживающими.
Именно для этого я попытался создать аналог – REFA. Регулярные выражения для всех.
Его идея в следующем. Как только регулярное выражение перестает быть очевидным – разбить его на два. Оптимизатор при возможности все равно сведет его в одно, таким образом в скорости потерь не будет, но зато код станет яснее.
Для удобного чтения ge.tt/9snPkzG/v/0 (формат .odt)
Примеры
Поиск функций С++
Найти реализацию всех методов класса dummy.
Считается что на вход подается большая строка со всем кодом проекта. Можно сделать чтение из файла, но это затруднит понимание примера.
PROGRAM “FindMethods”
^name^ = ~w?[w|d]*~
BLOCK “FindClass” // поиск декларации класса
PUSH
BLOCKVAR $regexp = “class ”+%classname%+”s*{.*}.*;”
MATCH $regexp
CATCH MATCH_FAIL
RETURN array() AS $list;
RETURN array() AS $result;
FINISH
BLOCKVAR $class_code = MATCHED
INCOMING = $class_code
BLOCKVAR $method = ^name^+~w*~+^name^+~([^name^w*^name^w*,?]*)w*~
BLOCKVAR $declarations = array();
BLOCKVAR $realisations = array();
TRY
WHILE 1
MATCH PASS LIMIT 1 $method
IF select(0,1) INCOMING != “;”
CALL “SearchEndOfFunction” REMAINED
$realisations ADD (MATCHED + RESULT $body)
ELSE
$declarations ADD MATCHED
ENDIF
END
ON MATCH_FAIL OR END_OF_STRING
RETURN $declarations AS $list
RETURN $realisations AS $result
FINISH
POP
ENDBLOCK
BLOCK “SearchEndOfFunction”
BLOCKVAR UINT $level = 0
MATCH ~[{|}]~
FOREACH ALL_MATCHED AS $t
IF $t == “{}
$level++;
ELSE
$level--;
ENDIF
IF $level == 0
BLOCKVAR STRING $ret = select(ALL_MATCHED[0], ALL_MATCHED[ITERATION]) INCOMING_BLOCK
RETURN $ret AS $body
ENDIF
END
ENDBLOCK
BLOCK “AddClassName”
MATCH PASS LIMIT 1 ^name^+”w*”
BLOCKVAR $ret = MATCHED
$ret += “[^name^w*::w*]*”+%classname%+”w*::w*”
$ret += REMAIN
RETURN $ret
ENDBLOCK
BLOCK “SearchDeclaredFunctions”
BLOCKVAR $dec = %declared%
IMPLODE ($dec, “|”) $string
$string = “[“+$string+”]”
MATCH $string
BLOCVAR $realistaions = array()
FOREACH ALL_TILES as $tile
IF ITERATION % 2 == 1
IF select(0,1) INCOMING != “;”
CALL “SearchEndOfFunction” ALL_TILES[ITERATION + 1]
$realisations ADD (ALL_TILES[ITERATION] + RESULT $body)
ENDIF
ENDIF
END
RETURN $realisations AS $result
ENDBLOCK
// код программы
BLOCKVAR $classname = $arg1
CALL “FindClass”
BLOCKVAR $ret = RESULT $result
BLOCKVAR $declared = RESULT $list
CALL “SearchDeclaredFunctions”
$ret ADD RESULT $result
RETURN $ret
ENDPROGRAM
Программа получилась не очень маленькой, но хотя бы боле-менее понятной. Регулярное выражение аналогичное данному… не советую.
Документация
Типы данных
INT
Тип по умолчанию. Целочисленный. Диапазон −2^31 до +2^31-1. Значение по умолчанию 0.
LONG
Целочисленный. Диапазон −2^63 до +2^63-1. Значение по умолчанию 0
UINT
ULONG
STRING
Строка. Максимальная длина UINT. Приватные поля START и COUNT.
Значение по умолчанию не существует и вызывает исключительную ситуацию.
TILE
Часть строки. Приватные поля START, END, COUNT, PARENT_STRING.
Предопределенные переменные
INCOMING
Строка для обработки. Подставляется если никакая переменная не указана.
ICOMING это синоним INCOMING_CURRENT
- INCOMING_PROGRAM – пришедшее в программу
- INCOMING_BLOCK – пришедшее в блок
- INCOMING_CURRENT – актуальная строка
- INCOMING_LAST – до последнего изменения
MATCHED
Первое совпадение подошедшее в последнем match.
ALL_MATCHED
Массив со всеми совпадениями последнего выражения.
REMAINED
Первый символ после MATCHED
ALL_REMAINED
Первые символы после каждого в ALL_MATCHED
ALL_TILES
Все нечетные это ALL_MATCHED. Остальное – недостающие строки, в правильном порядке до строки.
ITERATION
Номер итерации в текущем цикле. Для получения номера итерации в внешнем – сохраняйте в отдельную переменную.
CALLSTACK
Стек вызовов с параметрами
QUERY_LOG
Лог команд, влиявших так или иначе на строки. Обязательно запоминать копирование строк(вдруг была последующая обработка). Входящие данные хранить в единственном экземпляре.
EXCEPTION_STRING
Строка обьясняющая суть ошибки. Место вознекновения, входящие параметры, результат.
Минимальный набор
Необходим для простейшего использования системы
Match
MATCH [IGNORE {ignore_count|FIRST}] [PASS] [LIMIT {limit_count}] reg_exp [processing_string]
Проверить reg_exp, сдвинуть START в processing_string в MATCHED (по умолчанию)
IGNORE – пропустить первые несколько совпадений. По умолчанию IGNORE 0
PASS – передвинуть START в последний ALL_REMAINED
LIMIT – максимальное количество совпадений, после которых подпрограмма оборвется. По умолчанию LIMIT 0, значит будет работать до конца файла.
reg_exp – может быть регулярное выражение заданное между ~, может быть переменной.
processing_string – строка для обработки. По умолчанию INCOMING
Echo
ECHO string
Вывод строки в результат.
Простейший пример замены регулярного выражения:
MATCH PASS ~some_regexp~
FOREACH ALL_TILES AS $tile
IF ITERATION % 2
// все совпавшие кусочки заменим на строку
ECHO “REPLACED”
ELSE
// все куски между совпавшими вернем в неизменном виде
ECHO $tile
ENDIF
END
IF ELSE ENDIF
IF expr then [ELSE else] ENDIF
Если выражение expr не равно нулю, то выполнится код then, иначе else
Расширенный набор
PROGRAM
Программа – атомарный набор исполняемых команд, выполняющих необходимую задачу. Только программы могут иметь параметры отличные от “по умолчанию”.
Вообще говоря это может быть отдельным процессом(или потоком) и выполняться параллельно. Нет никакой возможности обратиться из одной программы к другой. Но можно использовать(если они декларированны) методы соседней программы. Программы могут вызывать программы.
Программа является областью видимости для всех блоков.
По умолчанию все команды заключаются в программу с нулевым именем (ее нельзя вызвать из других программ)
PROGRAM name arg0 [arg1 arg2 … ] code ENDPROGRAM
name – имя программы
arg0 – строка для обработки. Становится INCOMING_PROGRAM
code – код программы, включая декларации.
Доступ к блокам кода осуществляется с помощью конструкции
program_name::block_name.
BLOCK
BLOCK name [string]
Несамостоятельный участок кода. Едентичен двум прыжкам goto. Если указан string – перед запуском происходит смена соответсвующих INCOMING, после возвращение.
PUSH POP
PUSH [var1 var2]
Сохранить состояние системных переменных. Также можно добавить для сохранения локальные переменные (перечислением), и явно исключить некоторые системные с помощью конструкции !var
POP – восстанавливает состояние к моменту до PUSH
BLOCKVAR
Временная переменная доступная только в текущей области видимости, и уничтожаемая при выходе.
RETURN RESULT
Служит для возвращения значения временных переменных из блока/программы.
RETURN name
Для доступа к переменной в вызывающей конструкции используется RESULT name
Значение валидно до вызова следующего блока.
Обработка ошибок
Во время исполнения скрипта возможны различные исключительные ситуации, которые не должны влиять на ход исполнения. Для этого существует система исключений.
exceptions: exception_name [OR exception_name … ]
CATCH FINISH
CATCH exceptions code [CATCH exceptions code …] FINISH
Необходим для отлова ошибки произошедшей на строке предыдущей к первому блоку CATCH.
Используется в ситуациях если исключительная ситуация в этом участке ожидаема и подлежит обработке.
TRY ON FINISH
TRY code ON exceptions code [ON exceptions code …] FINISH
THROW
THROW exception
Сгенерировать ошибку вручную
Виды ошибок
- MATCH_FAIL – не удалось найти ни одного вхождения regexp
- END_OF_STRING – конец достигнут до того как что то удалось найти(подразумевает под собой MATCH_FAIL)
- WRONG_REGEXP – не удалось скомпилировать регулярное выражение
- VARIABLE_OVERFLOW – переполнение переменной
- UNSIGNED_NEGATIVE – внесение отрицательного значения в беззнаковое число
- WRONG_STRING_INDEXES – попытка доступа к строке по индексам выходящим за границы строки
- OUT_OF_ARRAY – попытка доступа к несуществующим элементам массива (вне)
Специальные конструкции
~regexp~
Содержимое – регулярное выражение
%name%
Во время исполнения подменится копией значения переменной $name. (ближайшей по стеку)
#name#
Аналог define
^name^
Ссылка на регулярное выражение. Работает внутри ~~ как ^
^hello^ = ~hel{2}o~
~^hello^ world~
Работа с строками
array{tile} SPLIT(delimeter) [string]
tile SELECT(start, end) [string]
PASS(count) [&string]
CUT(count) [&string]
CUT_AFTER(index) [&string]
IMPLODE (array[, delimeter]) &string
Автор: Offenso