Regular Expressions For All (REFA)
Существует множество систем для поиска подстрок отвечающих определенной маске. К сожалению они теряют свою мощь как только приходится учитывать многие факторы. Конструкции становятся громозкими, непонятными и трудноподдерживающими.
Именно для этого я попытался создать аналог – REFA. Регулярные выражения для всех.
Его идея в следующем. Как только регулярное выражение перестает быть очевидным – разбить его на два. Оптимизатор при возможности все равно сведет его в одно, таким образом в скорости потерь не будет, но зато код станет яснее.
Для удобного чтения ge.tt/9snPkzG/v/0 (формат \.odt)
Считается что на вход подается большая строка со всем кодом проекта. Можно сделать чтение из файла, но это затруднит понимание примера.
Программа получилась не очень маленькой, но хотя бы боле-менее понятной. Регулярное выражение аналогичное данному… не советую.
Тип по умолчанию. Целочисленный. Диапазон −2^31 до +2^31-1. Значение по умолчанию 0.
Целочисленный. Диапазон −2^63 до +2^63-1. Значение по умолчанию 0
Строка. Максимальная длина UINT. Приватные поля START и COUNT.
Значение по умолчанию не существует и вызывает исключительную ситуацию.
Часть строки. Приватные поля START, END, COUNT, PARENT_STRING.
Строка для обработки. Подставляется если никакая переменная не указана.
ICOMING это синоним INCOMING_CURRENT
Первое совпадение подошедшее в последнем match.
Массив со всеми совпадениями последнего выражения.
Первый символ после MATCHED
Первые символы после каждого в ALL_MATCHED
Все нечетные это ALL_MATCHED. Остальное – недостающие строки, в правильном порядке до строки.
Номер итерации в текущем цикле. Для получения номера итерации в внешнем – сохраняйте в отдельную переменную.
Стек вызовов с параметрами
Лог команд, влиявших так или иначе на строки. Обязательно запоминать копирование строк(вдруг была последующая обработка). Входящие данные хранить в единственном экземпляре.
Строка обьясняющая суть ошибки. Место вознекновения, входящие параметры, результат.
Необходим для простейшего использования системы
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 string
Вывод строки в результат.
Простейший пример замены регулярного выражения:
MATCH PASS ~some_regexp~
FOREACH ALL_TILES AS $tile
IF ITERATION % 2
// все совпавшие кусочки заменим на строку
ECHO “REPLACED”
ELSE
// все куски между совпавшими вернем в неизменном виде
ECHO $tile
ENDIF
END
IF expr then [ELSE else] ENDIF
Если выражение expr не равно нулю, то выполнится код then, иначе else
Программа – атомарный набор исполняемых команд, выполняющих необходимую задачу. Только программы могут иметь параметры отличные от “по умолчанию”.
Вообще говоря это может быть отдельным процессом(или потоком) и выполняться параллельно. Нет никакой возможности обратиться из одной программы к другой. Но можно использовать(если они декларированны) методы соседней программы. Программы могут вызывать программы.
Программа является областью видимости для всех блоков.
По умолчанию все команды заключаются в программу с нулевым именем (ее нельзя вызвать из других программ)
PROGRAM name arg0 [arg1 arg2 … ] code ENDPROGRAM
name – имя программы
arg0 – строка для обработки. Становится INCOMING_PROGRAM
code – код программы, включая декларации.
Доступ к блокам кода осуществляется с помощью конструкции
program_name::block_name.
BLOCK name [string]
Несамостоятельный участок кода. Едентичен двум прыжкам goto. Если указан string – перед запуском происходит смена соответсвующих INCOMING, после возвращение.
PUSH [var1 var2]
Сохранить состояние системных переменных. Также можно добавить для сохранения локальные переменные (перечислением), и явно исключить некоторые системные с помощью конструкции !var
POP – восстанавливает состояние к моменту до PUSH
Временная переменная доступная только в текущей области видимости, и уничтожаемая при выходе.
Служит для возвращения значения временных переменных из блока/программы.
RETURN name
Для доступа к переменной в вызывающей конструкции используется RESULT name
Значение валидно до вызова следующего блока.
Во время исполнения скрипта возможны различные исключительные ситуации, которые не должны влиять на ход исполнения. Для этого существует система исключений.
exceptions: exception_name [OR exception_name … ]
CATCH exceptions code [CATCH exceptions code …] FINISH
Необходим для отлова ошибки произошедшей на строке предыдущей к первому блоку CATCH.
Используется в ситуациях если исключительная ситуация в этом участке ожидаема и подлежит обработке.
TRY code ON exceptions code [ON exceptions code …] FINISH
THROW exception
Сгенерировать ошибку вручную
Содержимое – регулярное выражение
Во время исполнения подменится копией значения переменной $name. (ближайшей по стеку)
Аналог define
Ссылка на регулярное выражение. Работает внутри ~~ как \^
^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
Основная идея
Существует множество систем для поиска подстрок отвечающих определенной маске. К сожалению они теряют свою мощь как только приходится учитывать многие факторы. Конструкции становятся громозкими, непонятными и трудноподдерживающими.
Именно для этого я попытался создать аналог – 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
