Re2c
| Re2c | |
|---|---|
| Тип | свободное и открытое программное обеспечение и генератор лексического анализатора[вд] |
| Написана на | Си и C++ |
| Операционная система | кроссплатформенность |
| Дата выпуска | 1994 |
| Последняя версия | |
| Репозиторий | github.com/skvadrik/re2c |
| Состояние | активное |
| Сайт | re2c.org (англ.) |
re2c (regular expression to c, regular expression to code) — это свободная утилита–генератор с открытым исходным кодом, она генерирует быстрые и легко встраиваемые лексеры, ориентирована на работу совместно с языками: Си, C++, D, Go, Haskell, Java, JavaScript, OCaml, Python, Rust, Swift, V[англ.] и Zig.
Изначально утилита была создана Питером Бамбулисом (англ. Peter Bumbulis) и описана в его статье[2], позже re2c был передан в общественное достояние и с тех пор поддерживается добровольцами[3].
Утилита отличается от своих более известных аналогов (таких как lex и flex) тем, что имеет гибкий интерфейс взаимодействия (сгенерированный код взаимодействует с внешней программой с помощью примитивов), генерирует оптимизированные нетабличные лексеры, поддерживает захваты (submatch extraction) на основе детерминированных конечных автоматов с тэгами (TDFA).
Утилита в основном распространена в проектах, где требуется высокая скорость анализа синтаксиса, например Ninja[4] и PHP[5].
Философия
Основная цель re2c — генерировать быстрые лексеры[2], по крайней мере настолько же быстрые, как и разумно оптимизированные лексеры, написанные вручную на языке Си. Вместо использования традиционного табличного подхода re2c кодирует сгенерированный конечный автомат непосредственно в форме условных переходов и сравнений. В результате программа работает быстрее, чем её аналог на основе таблиц[2], и её гораздо проще отлаживать и понимать. Более того, такой подход часто приводит к уменьшению размера лексеров[2], поскольку re2c применяет ряд оптимизаций, таких как минимизация ДКА и построение туннельного автомата[6]. Еще одной отличительной особенностью re2c является его гибкий интерфейс. Вместо того, чтобы использовать фиксированный шаблон программы, re2c позволяет программисту написать большую часть кода интерфейса и адаптировать сгенерированный лексер к любой конкретной среде. Основная идея заключается в том, что re2c должен быть абстракцией с нулевыми затратами для программиста, использование утилиты никогда не должно приводить к более медленной работе программы, чем соответствующая реализация с вручную написанным кодом.
Возможности
- Захваты submatch extraction[7] — re2c поддерживает как группы захвата, совместимые с POSIX, так и отдельные тэги[8].
Реализация основана на алгоритме «lookahead-TDFA»[9][10][11];
- Поддержка различных кодировок[12] — re2c поддерживает ASCII, UTF-8, UTF-16, UTF-32, UCS-2 и EBCDIC;
- Гибкий пользовательский интерфейс[13] — сгенерированный код использует несколько примитивных операций для взаимодействия с окружающей средой (считывание входных символов, переход к следующей позиции ввода и т. д.). Пользователи могут переопределять эти примитивы так, как им необходимо;
- Сохраняемое состояние[14] — re2c поддерживает как лексеры pull-модели (когда лексер работает без прерываний и при необходимости извлекает больше входных данных), так и лексеры push-модели (когда лексер периодически останавливается и возобновляется для анализа новых блоков ввода);
- Условия запуска[15] — re2c может генерировать несколько взаимосвязанных уровней, где каждый лексер запускается определенным условием в программе;
- Самопроверка[16] — re2c имеет специальный режим, в котором он игнорирует весь код интерфейса, определенный пользователем, и генерирует автономную программу-скелет. Кроме того, re2c генерирует два файла — один со строками ввода, полученными из обычной грамматики, и один со сжатыми результатами сверки, которые используются для проверки поведения лексера на всех входах. Входные строки генерируются так, чтобы они широко охватывали переходы и пути ДКА. Генерация данных происходит сразу после построения ДКА и до любых оптимизаций, но сам лексер полностью оптимизирован, поэтому программы-скелеты способны выявлять любые ошибки в оптимизации и генерации кода;
- Система предупреждений[17] — re2c выполняет статический анализ программы и предупреждает своих пользователей о возможных неопределённостях или ошибках, таких как неопределённый поток управления, недостижимый код, неправильно экранированные escape-символы и потенциальное неправильное использование примитивов интерфейса;
- Отладка — помимо создания удобочитаемых лексеров, re2c имеет ряд опций, которые выводят различные промежуточные представления сгенерированного лексера, такие как НКА, несколько этапов ДКА и результирующий программный график в формате языка DOT[18].
Синтаксис
Программа re2c может содержать любое количество блоков /*!re2c ... */. Каждый блок состоит из последовательности правил, определений и конфигураций (их можно смешивать, но, как правило, лучше сначала размещать конфигурации, затем — определения, а затем — правила). Правила имеют вид — REGEXP { CODE } или REGEXP := CODE;, где REGEXP — регулярное выражение, а CODE — является блоком кода на языке Си. Когда REGEXP совпадает с входной строкой, поток управления передаётся соответствующему блоку CODE. Существует одно специальное правило: правило по умолчанию с * вместо REGEXP, оно срабатывает, если никакие другие правила не совпадают. re2c имеет семантику жадного соответствия — если несколько правил совпадают, предпочтительным является правило, соответствующее более длинному префиксу, если конфликтующие правила соответствуют одному и тому же префиксу, то более раннее правило имеет приоритет. Определения имеют вид NAME = REGEXP; (и, соответственно, NAME { REGEXP } в Flex-совместимом режиме). Конфигурации имеют вид re2c:CONFIG = VALUE;, где CONFIG является именем конкретной конфигурации и VALUE является числом или строкой. Для более расширенного использования ознакомьтесь с официальным руководством re2c[19].
Регулярные выражения
re2c использует следующий синтаксис для регулярных выражений:
"foo"строковый литерал с чувствительностью к регистру;'foo'строковый литерал без чувствительности к регистру;[a-xyz],[^a-xyz]класс символов (с возможностью отрицания);.любой возможный символ, кроме символа новой строки;R \ Sразница в классах символов;R*нуль или большее количество совпадений с символомR;R+одно или большее количество совпадений с символомR;R?необязательное совпадение с символомR(нуль или одно);R{n}повторениеRточноnраз;R{n,}повторениеRпо крайней мереnраз;R{n,m}повторениеRотnдоmраз;(R)простоR(круглые скобки используются для переопределения приоритета или для соответствия в стиле POSIX);R SконкатенацияR, за которой следуетS;R | SальтернативаRилиS;R / Sпоиск с опережением (англ. lookahead)R, за которой следуетS;nameрегулярное выражение, определенное какname(за исключением режима совместимости с Flex);@stags-метка (с англ. tag — метка или тэг) — сохраняет последнюю позицию ввода, в которой@stagсовпадает с переменной с именемstag;#mtagm-метка — сохраняет все позиции ввода, в которых#mtagсовпадает с переменной с именемmtag.
Классы символов и строковые литералы могут содержать следующие escape-последовательности: \a, \b, \f, \n, \r, \t, \v, \\, восьмеричного вида \ooo и шестнадцатеричного вида \xhh, \uhhhh, \Uhhhhhhhh.
Примеры кода
Далее приведён пример простой re2c-программы в файле example.re. Она проверяет, что все входные аргументы являются десятеричными числами. Код для re2c обрамлён в комментариях /*!re2c ... */[20].
Си:
// re2c $INPUT -o $OUTPUT -i --case-ranges
#include <assert.h>
bool lex(const char *s) {
const char *YYCURSOR = s;
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = char;
number = [1-9][0-9]*;
number { return true; }
* { return false; }
*/
}
int main() {
assert(lex("1234"));
return 0;
}
Команда $ re2c -is -o example.c example.re генерирует приведенный ниже код (example.c). Содержание комментария /*!re2c ... */ заменяются детерминированным конечным автоматом, закодированным в виде условных переходов и сравнений, остальная часть программы дословно копируется в выходной файл. Существует несколько вариантов генерации кода, обычно re2c использует оператор switch, но он может использовать вложенные операторы if (как в этом примере с опцией -s) или генерировать битовые карты и таблицы переходов. Какой вариант лучше, зависит от компилятора языка Си, пользователям re2c предлагается проэкспериментировать.
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -i --case-ranges
#include <assert.h>
bool lex(const char *s) {
const char *YYCURSOR = s;
{
char yych;
yych = *YYCURSOR;
switch (yych) {
case '1' ... '9': goto yy2;
default: goto yy1;
}
yy1:
++YYCURSOR;
{ return false; }
yy2:
yych = *++YYCURSOR;
switch (yych) {
case '0' ... '9': goto yy2;
default: goto yy3;
}
yy3:
{ return true; }
}
}
int main() {
assert(lex("1234"));
return 0;
}
Go:
//go:generate re2go $INPUT -o $OUTPUT -i
package main
func lex(str string) {
var cursor int
/*!re2c
re2c:define:YYCTYPE = byte;
re2c:define:YYPEEK = "str[cursor]";
re2c:define:YYSKIP = "cursor += 1";
re2c:yyfill:enable = 0;
number = [1-9][0-9]*;
number { return }
* { panic("error!") }
*/
}
func main() {
lex("1234\x00")
}
// Code generated by re2c, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT -i
package main
func lex(str string) {
var cursor int
{
var yych byte
yych = str[cursor]
switch (yych) {
case '1','2','3','4','5','6','7','8','9':
goto yy2
default:
goto yy1
}
yy1:
cursor += 1
{ panic("error!") }
yy2:
cursor += 1
yych = str[cursor]
switch (yych) {
case '0','1','2','3','4','5','6','7','8','9':
goto yy2
default:
goto yy3
}
yy3:
{ return }
}
}
func main() {
lex("1234\x00")
}
Rust:
// re2rust $INPUT -o $OUTPUT
fn lex(s: &[u8]) -> bool {
let mut cursor = 0;
/*!re2c
re2c:define:YYCTYPE = u8;
re2c:define:YYPEEK = "*s.get_unchecked(cursor)";
re2c:define:YYSKIP = "cursor += 1;";
re2c:yyfill:enable = 0;
number = [1-9][0-9]*;
number { return true; }
* { return false; }
*/
}
fn main() {
assert!(lex(b"1234\0"));
}
/* Generated by re2c */
// re2rust $INPUT -o $OUTPUT
fn lex(s: &[u8]) -> bool {
let mut cursor = 0;
{
#[allow(unused_assignments)]
let mut yych : u8 = 0;
let mut yystate : usize = 0;
'yyl: loop {
match yystate {
0 => {
yych = unsafe {*s.get_unchecked(cursor)};
cursor += 1;
match yych {
0x31 ..= 0x39 => {
yystate = 2;
continue 'yyl;
}
_ => {
yystate = 1;
continue 'yyl;
}
}
}
1 => { return false; }
2 => {
yych = unsafe {*s.get_unchecked(cursor)};
match yych {
0x30 ..= 0x39 => {
cursor += 1;
yystate = 2;
continue 'yyl;
}
_ => {
yystate = 3;
continue 'yyl;
}
}
}
3 => { return true; }
_ => {
panic!("internal lexer error")
}
}
}
}
}
fn main() {
assert!(lex(b"1234\0"));
}
Программные проекты, использующие re2c
- PHP — популярный язык сценариев общего назначения[5];
- Ninja — система сборки, ориентированная на скорость[4];
- SpamAssassin — программа для фильтрации спама электронной почты[21];
- BRL-CAD — программа 3D-моделирования (САПР)[22];
- STEPCode — имплементация стандарта ISO 10303[23];
- Yasm — модульный ассемблер, полная переработка NASM[24];
- Wake — инструмент для сборки от SiFive[25].
См. также
Примечания
- ↑ Release 4.5.1 — 2026.
- ↑ 1 2 3 4 Bumbulis Peter, Donald D. Cowan. RE2C: a more versatile scanner generator (англ.) // Association for Computing Machinery, New York, NY, United States : журнал. — 1993. — 3—12 (vol. 2, no. 1—4). — P. 70—84. — ISSN 1057-4514. — doi:10.1145/176454.176487.
- ↑ re2c: authors (англ.). Дата обращения: 11 февраля 2022. Архивировано 21 июля 2011 года.
- ↑ 1 2 Ninja: build.ninja (англ.). Ninja. Дата обращения: 11 февраля 2022. Архивировано 5 мая 2022 года.
- ↑ 1 2 Building PHP (англ.). PHP Internals Book. Дата обращения: 11 февраля 2022. Архивировано 8 мая 2021 года.
- ↑ Joseph Grosch. Efficient Generation of Table-Driven Scanners (англ.) // Software: Practice and Experience 19 : журнал. — 1989. — P. 1089—1103.
- ↑ Submatch extraction, re2c documentation (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ Ville Laurikari. NFAs with tagged transitions, their conversion to deterministic automata and application to regular expressions (англ.) // Seventh International Symposium on String Processing and Information Retrieval, 2000. SPIRE 2000. Proceedings. : журнал. — 2000. Архивировано 8 февраля 2022 года.
- ↑
Ulya Trofimovich (2017). Tagged Deterministic Finite Automata with Lookahead. arXiv:1907.08837.
{{cite journal}}: Cite journal требует|journal=(справка) - ↑ Ulya Trofimovich. RE2C — a lexer generator based on lookahead TDFA (англ.) // Software Impacts : журнал. — 2020. — Vol. 6. — doi:10.1016/j.simpa.2020.100027.
- ↑ Ulya, Trofimovich. Lookahead TDFA in pictures (slides) (англ.) (PDF) (2021). Дата обращения: 11 февраля 2022. Архивировано 27 января 2022 года.
- ↑ re2c: Encoding support (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ re2c: Program interface (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ re2c: Storable state (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ re2c: Start conditions (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ re2c: Skeleton (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ re2c: Warnings (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ Visualization, re2c documentation (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ re2c: User manual (C) (англ.). Дата обращения: 11 февраля 2022. Архивировано 31 января 2022 года.
- ↑ re2c. Дата обращения: 11 февраля 2022. Архивировано 16 февраля 2022 года.
- ↑ SpamAssassin (sa-compile) (англ.). Дата обращения: 11 февраля 2022. Архивировано 20 января 2022 года.
- ↑ BRL-CAD (tools: re2c) (англ.). Дата обращения: 11 февраля 2022. Архивировано 11 февраля 2022 года.
- ↑ Build Process (англ.). Дата обращения: 11 февраля 2022. Архивировано 20 января 2022 года.
- ↑ The Yasm Modular Assembler Project: Key Internal Features (англ.). Дата обращения: 11 февраля 2022. Архивировано 20 января 2022 года.
- ↑ wake (англ.). Дата обращения: 11 февраля 2022. Архивировано 11 февраля 2022 года.
Ссылки
- Официальный сайт (англ.)
- Репозиторий проекта (англ.)
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.