pegs
Простое соответствие PEG (Parsing expression grammar). Не использует запоминание, но использует супероператоры и встраивание символов для повышения производительности. Примечание: производительность сопоставления, надеемся, сопоставима с оптимизированными движками регулярных выражений.
Синтаксис и семантика PEG
PEG (Parsing expression grammar) — это простая детерминированная грамматика, которая может быть напрямую использована для разбора. Текущая реализация разработана как более мощная замена регулярным выражениям. Поддерживается UTF-8.
Нотация, используемая для PEG, похожа на EBNF:
| нотация | значение |
|---|---|
A / ... / Z |
Последовательный выбор: Применяются выражения A, ..., Z, в этом порядке, к тексту впереди, пока одно из них не выполнится успешно и, возможно, не потребляет какой-то текст. Указывает успех, если одно из выражений выполнилось успешно. В противном случае не потребляет текст и указывает на неудачу. |
A ... Z |
Последовательность: Применяются выражения A, ..., Z, в этом порядке, для потребления последовательных частей текста впереди, до тех пор, пока они выполняются успешно. Указывает успех, если все они выполнились. В противном случае не потребляет текст и указывает на неудачу. Приоритет последовательности выше, чем приоритет последовательного выбора: A B / C означает (A B) / Z, а не A (B / Z) |
(E) |
Группирование: Скобки могут использоваться для изменения приоритета операторов. |
{E} |
Захват: Применить выражение E и сохранить подстроку, которая соответствовала E, в захват, к которому можно получить доступ после процесса сопоставления. |
$i |
Обращение к i-му захвату. i считает с 1. |
$ |
Якор: Сопоставляется в конце входных данных. Ни один символ не потребляется. То же, что и !.. |
^ |
Якор: Сопоставляется в начале входных данных. Ни один символ не потребляется. |
&E |
Предикат «и»: Указывает успех, если выражение E соответствует тексту впереди; в противном случае указывает на неудачу. Не потребляет текст. |
!E |
Предикат «не»: Указывает неудачу, если выражение E соответствует тексту впереди; в противном случае указывает успех. Не потребляет текст. |
E+ |
Один или более: Применяйте выражение E многократно, чтобы сопоставить текст впереди, пока это выполняется успешно. Потребляйте сопоставленный текст (если есть), и укажите успех, если было хотя бы одно совпадение. В противном случае укажите неудачу. |
E* |
Ноль или более: Применяйте выражение E многократно, чтобы сопоставить текст впереди, пока это выполняется успешно. Потребляйте сопоставленный текст (если есть). Всегда указывайте успех. |
E? |
Ноль или один: Если выражение E соответствует тексту впереди, потребляет его. Всегда указывайте успех. |
[s] |
Класс символов: Если символ впереди появляется в строке s, потребляйте его и укажите успех. В противном случае укажите неудачу. |
[a-b] |
Диапазон символов: Если символ впереди находится в диапазоне от a до b, потребляйте его и укажите успех. В противном случае укажите неудачу. |
's' |
Строка: Если текст впереди является строкой s, потребляйте его и укажите успех. В противном случае укажите неудачу. |
i's' |
Сопоставление строки, игнорируя регистр. |
y's' |
Сопоставление строки, игнорируя стиль. |
v's' |
Точное соответствие строке: Используйте это для переопределения глобального модификатора \i или \y. |
i$j |
Сопоставление строки, игнорируя регистр для обращений к ссылкам. |
y$j |
Сопоставление строки, игнорируя стиль для обращений к ссылкам. |
v$j |
Точное соответствие строке для обращений к ссылкам. |
. |
Любой символ: Если впереди есть символ, потребляйте его и укажите успех. В противном случае (то есть в конце входных данных) укажите неудачу. |
_ |
Любой символ Юникода: Если впереди есть символ UTF-8, потребляйте его и укажите успех. В противном случае укажите неудачу. |
@E |
Поиск: Краткая запись для (!E .)* E. (Цикл поиска шаблона E.) |
{@} E |
Захваченный поиск: Краткая запись для {(!E .)*} E. (Цикл поиска шаблона E.) Все до E и без него, захватывается. |
@@ E |
То же, что и {@} E. |
A <- E |
Правило: Связывает выражение E с нетерминальным символом A. Леворекурсивные правила невозможны и приводят к сбоям движка сопоставления.
|
\identifier |
Встроенный макрос для более длинного выражения. |
\ddd |
Символ с десятичным кодом ddd. |
\", и т.д. |
Литерал ", и т.д. |
Встроенные макросы
| макрос | значение |
|---|---|
\d |
любая десятичная цифра: [0-9]
|
\D |
любой символ, который не является десятичной цифрой: [^0-9]
|
\s |
любой пробельный символ: [ \9-\13]
|
\S |
любой символ, который не является пробельным: [^ \9-\13]
|
\w |
любой символ "слова": [a-zA-Z0-9_]
|
\W |
любой символ "не-слова": [^a-zA-Z0-9_]
|
\a |
то же, что и [a-zA-Z]
|
\A |
то же, что и [^a-zA-Z]
|
\n |
любая комбинация новой строки: \10 / \13\10 / \13
|
\i |
игнорировать регистр при сопоставлении; использовать в начале PEG |
\y |
игнорировать стиль при сопоставлении; использовать в начале PEG |
\skip pat |
пропустить шаблон pat перед попыткой сопоставить другие токены; это полезно для пропуска пробелов, например: \skip(\s*) {\ident} ':' {\ident} сопоставляет пары ключ-значение, игнорируя пробелы вокруг ':'. |
\ident |
стандартный ASCII идентификатор: [a-zA-Z_][a-zA-Z_0-9]*
|
\letter |
любая буква Юникода |
\upper |
любая заглавная буква Юникода |
\lower |
любая строчная буква Юникода |
\title |
любая буква Юникода в стиле заголовка |
\white |
любой пробельный символ Юникода |
Обратный слэш, за которым следует буква, является встроенным макросом, в противном случае он используется для обычного экранирования:
| нотация | значение |
|---|---|
\\ |
одиночный обратный слэш |
\* |
то же, что и '*'
|
\t |
не табулятор, а (неизвестный) встроенный элемент |
Поддерживаемая грамматика PEG
Парсер PEG реализует эту грамматику (написанную в синтаксисе PEG):
# Example grammar of PEG in PEG syntax.
# Comments start with '#'.
# First symbol is the start symbol.
grammar <- rule* / expr
identifier <- [A-Za-z][A-Za-z0-9_]*
charsetchar <- "\\" . / [^\]]
charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
"'" ("\\" . / [^'])* "'")
builtin <- "\\" identifier / [^\13\10]
comment <- '#' @ \n
ig <- (\s / comment)* # things to ignore
rule <- identifier \s* "<-" expr ig
identNoArrow <- identifier !(\s* "<-")
prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
literal <- ig identifier? '$' [0-9]+ / '$' / '^' /
ig identNoArrow /
ig charset /
ig stringlit /
ig builtin /
ig '.' /
ig '_' /
(ig "(" expr ig ")")
postfixOpr <- ig '?' / ig '*' / ig '+'
primary <- prefixOpr* (literal postfixOpr*)
# Concatenation has higher priority than choice:
# ``a b / c`` means ``(a b) / c``
seqExpr <- primary+
expr <- seqExpr (ig "/" expr)* Примечание: В качестве специального синтаксического расширения, если весь PEG — это только одно выражение, идентификаторы не интерпретируются как нетерминалы, а интерпретируются как точная строка:
abc =~ peg"abc" # is true
Поэтому не нужно писать peg" 'abc' " в приведенном выше примере.
Примеры
Проверка, соответствует ли s ключевому слову "while" в Nim:
s =~ peg" y'while'"
Обмен (ключ, значение)-парами:
"key: val; key2: val2".replacef(peg"{\ident} \s* ':' \s* {\ident}", "$2: $1")
Определение файлов с расширением #include, содержащихся в файле C:
for line in lines("myfile.c"):
if line =~ peg"""s <- ws '#include' ws '"' {[^"]+} '"' ws
comment <- '/*' @ '*/' / '//' .*
ws <- (comment / \s+)* """:
echo matches[0] PEG против регулярных выражений
Как регулярное выражение \[.*\] соответствует самому длинному возможному тексту между '[' и ']'. Как PEG, он никогда не соответствует чему-либо, потому что PEG детерминирован: .* потребляет остаток входных данных, поэтому \] никогда не соответствует. Как PEG это необходимо записать так: \[ ( !\] . )* \] (или \[ @ \]).
Обратите внимание, что регулярное выражение также не ведет себя как ожидалось: в примере * не должно быть жадным, поэтому \[.*?\] следует использовать вместо него.
Построение PEG
Существует два способа построения PEG в коде Nim:
- Разбор строки в AST, который состоит из
Pegузлов с процедуройpeg. - Непосредственное построение AST с помощью вызовов процедур. Этот метод не поддерживает построение правил, только простые выражения и не так удобен. Его единственное преимущество заключается в том, что он не подключает весь парсер PEG в ваш исполняемый файл.
Импорты
- strutils, macros, unicode
Типы
PegKind = enum pkEmpty, pkAny, ## any character (.) pkAnyRune, ## any Unicode character (_) pkNewLine, ## CR-LF, LF, CR pkLetter, ## Unicode letter pkLower, ## Unicode lower case letter pkUpper, ## Unicode upper case letter pkTitle, ## Unicode title character pkWhitespace, ## Unicode whitespace character pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, ## single character to match pkCharChoice, pkNonTerminal, pkSequence, ## a b c ... --> Internal DSL: peg(a, b, c) pkOrderedChoice, ## a / b / ... --> Internal DSL: a / b or /[a, b, c] pkGreedyRep, ## a* --> Internal DSL: *a ## a+ --> (a a*) pkGreedyRepChar, ## x* where x is a single character (superop) pkGreedyRepSet, ## [set]* (superop) pkGreedyAny, ## .* or _* (superop) pkOption, ## a? --> Internal DSL: ?a pkAndPredicate, ## &a --> Internal DSL: &a pkNotPredicate, ## !a --> Internal DSL: !a pkCapture, ## {a} --> Internal DSL: capture(a) pkBackRef, ## $i --> Internal DSL: backref(i) pkBackRefIgnoreCase, pkBackRefIgnoreStyle, pkSearch, ## @a --> Internal DSL: !*a pkCapturedSearch, ## {@} a --> Internal DSL: !*\a pkRule, ## a <- b pkList, ## a, b pkStartAnchor ## ^ --> Internal DSL: startAnchor()- Источник Редактировать
NonTerminalFlag = enum ntDeclared, ntUsed
- Источник Редактировать
Peg {...}{.shallow.} = object case kind: PegKind of pkEmpty .. pkWhitespace: nil of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: NonTerminal of pkBackRef .. pkBackRefIgnoreStyle: index: range[0 .. MaxSubpatterns] else: sons: seq[Peg]- тип, представляющий PEG Источник Редактировать
NonTerminal = ref NonTerminalObj
- Источник Редактировать
Captures = object matches: array[0 .. 20 - 1, tuple[first, last: int]] ml: int origStart: int
- содержит захваченные подстроки. Источник Редактировать
EInvalidPeg = object of ValueError
- выбрасывается, если обнаружен неверный PEG Источник Редактировать
Константы
MaxSubpatterns = 20
- определяет максимальное количество подпатернов, которые могут быть захвачены. Больше подпатернов захвачено быть не может! Исходный код Изменить
Процедуры
proc kind(p: Peg): PegKind {...}{.raises: [], tags: [].}- Возвращает PegKind заданного объекта Peg. Исходный код Редактировать
proc term(p: Peg): string {...}{.raises: [], tags: [].}- Возвращает строковое представление заданного объекта варианта Peg, если он есть. Исходный код Редактировать
proc ch(p: Peg): char {...}{.raises: [], tags: [].}- Возвращает представление char заданного объекта варианта Peg, если оно есть. Исходный код Редактировать
proc charChoice(p: Peg): ref set[char] {...}{.raises: [], tags: [].}- Возвращает поле charChoice заданного объекта варианта Peg, если оно есть. Исходный код Редактировать
proc nt(p: Peg): NonTerminal {...}{.raises: [], tags: [].}- Возвращает объект NonTerminal заданного объекта варианта Peg, если он есть. Исходный код Редактировать
proc index(p: Peg): range[0 .. MaxSubpatterns] {...}{.raises: [], tags: [].}- Возвращает индекс обратной ссылки захваченного под-образца в объекте Captures для заданного объекта варианта Peg, если он есть. Исходный код Редактировать
proc name(nt: NonTerminal): string {...}{.raises: [], tags: [].}- Возвращает имя символа, представленного родительским объектом Peg вариантом заданного NonTerminal. Исходный код Редактировать
proc line(nt: NonTerminal): int {...}{.raises: [], tags: [].}- Возвращает номер строки определения родительского объекта Peg варианта заданного NonTerminal. Исходный код Редактировать
proc col(nt: NonTerminal): int {...}{.raises: [], tags: [].}- Возвращает номер столбца определения родительского объекта Peg варианта заданного NonTerminal. Исходный код Редактировать
proc flags(nt: NonTerminal): set[NonTerminalFlag] {...}{.raises: [], tags: [].}- Возвращает поле флагов типа NonTerminalFlag родительского объекта варианта Peg заданного NonTerminal. Исходный код Редактировать
proc rule(nt: NonTerminal): Peg {...}{.raises: [], tags: [].}- Возвращает объект Peg, представляющий определение правила родительского объекта Peg варианта заданного NonTerminal. Исходный код Редактировать
proc term(t: string): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1Str", raises: [], tags: [].}- строит PEG из терминальной строки Исходный код Редактировать
proc termIgnoreCase(t: string): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- строит PEG из терминальной строки; игнорировать регистр при соответствии Исходный код Редактировать
proc termIgnoreStyle(t: string): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- строит PEG из терминальной строки; игнорировать стиль при соответствии Исходный код Редактировать
proc term(t: char): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1Char", raises: [], tags: [].}- строит PEG из терминального символа Исходный код Редактировать
proc charSet(s: set[char]): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- строит PEG из набора символов
sИсходный код Редактировать proc `/`(a: varargs[Peg]): Peg {...}{.noSideEffect, gcsafe, extern: "npegsOrderedChoice", raises: [], tags: [].}- строит упорядоченный выбор с PEGs в
aИсходный код Редактировать proc sequence(a: varargs[Peg]): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- строит последовательность со всеми PEGs из
aИсходный код Редактировать proc `?`(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npegsOptional", raises: [], tags: [].}- строит необязательное для PEG
aИсходный код Редактировать proc `*`(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npegsGreedyRep", raises: [], tags: [].}- строит "жадное повторение" для PEG
aИсходный код Редактировать proc `!*`(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npegsSearch", raises: [], tags: [].}- строит "поиск" для PEG
aИсходный код Редактировать proc `!*\`(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npgegsCapturedSearch", raises: [], tags: [].}- строит "захваченный поиск" для PEG
aИсходный код Редактировать proc `+`(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npegsGreedyPosRep", raises: [], tags: [].}- строит "жадное положительное повторение" с PEG
aИсходный код Редактировать proc `&`(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npegsAndPredicate", raises: [], tags: [].}- строит "предикат и" с PEG
aИсходный код Редактировать proc `!`(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npegsNotPredicate", raises: [], tags: [].}- строит "предикат не" с PEG
aИсходный код Редактировать proc any(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG любой символ (
.) Исходный код Редактировать proc anyRune(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG любой символ Юникод (
_) Исходный код Редактировать proc newLine(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG новая строка (
\n) Исходный код Редактировать proc unicodeLetter(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG
\letter, который соответствует любому буквенному символу Юникод. Исходный код Редактировать proc unicodeLower(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG
\lower, который соответствует любому строчному буквенному символу Юникод. Исходный код Редактировать proc unicodeUpper(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG
\upper, который соответствует любому прописному буквенному символу Юникод. Исходный код Редактировать proc unicodeTitle(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG
\title, который соответствует любому буквенному символу Юникод в верхнем регистре. Исходный код Редактировать proc unicodeWhitespace(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG
\white, который соответствует любому пробельному символу Юникод. Исходный код Редактировать proc startAnchor(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG
^, который соответствует началу входных данных. Исходный код Редактировать proc endAnchor(): Peg {...}{.inline, raises: [], tags: [].}- строит PEG
$, который соответствует концу входных данных. Исходный код Редактировать proc capture(a: Peg): Peg {...}{.noSideEffect, gcsafe, extern: "npegsCapture", raises: [], tags: [].}- строит захват с PEG
aИсходный код Редактировать proc backref(index: range[1 .. MaxSubpatterns]): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}
- создаёт обратную ссылку на указанный
index.indexначинается с 1. Исходный код Редактировать proc backrefIgnoreCase(index: range[1 .. MaxSubpatterns]): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- создаёт обратную ссылку на указанный
index.indexначинается с 1. Игнорирует регистр при сопоставлении. Исходный код Редактировать proc backrefIgnoreStyle(index: range[1 .. MaxSubpatterns]): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- создаёт обратную ссылку на указанный
index.indexначинается с 1. Игнорирует стиль при сопоставлении. Исходный код Редактировать proc nonterminal(n: NonTerminal): Peg {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- создаёт PEG, состоящий из нетерминального символа Исходный код Редактировать
proc newNonTerminal(name: string; line, column: int): NonTerminal {...}{. noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- создаёт нетерминальный символ Исходный код Редактировать
proc `$`(r: Peg): string {...}{.noSideEffect, gcsafe, extern: "npegsToString", raises: [], tags: [].}- преобразует PEG в строковое представление Исходный код Редактировать
proc bounds(c: Captures; i: range[0 .. 20 - 1]): tuple[first, last: int] {...}{. raises: [], tags: [].}- возвращает границы
[first..last]i-го захвата. Исходный код Редактировать proc rawMatch(s: string; p: Peg; start: int; c: var Captures): int {...}{. noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- Процедура низкого уровня для сопоставления, реализующая интерпретатор PEG. Используйте для максимальной эффективности (каждая другая операция PEG в конечном итоге вызывает эту процедуру). Возвращает -1, если не совпадает, в противном случае возвращает длину совпадения. Исходный код Редактировать
proc matchLen(s: string; pattern: Peg; matches: var openArray[string]; start = 0): int {...}{. noSideEffect, gcsafe, extern: "npegs$1Capture", raises: [], tags: [].}- то же самое, что и
match, но возвращает длину совпадения; если совпадения нет, возвращает -1. Обратите внимание, что длина совпадения может быть нулевой. Возможно, остаётся суффиксs, который не принадлежит совпадению. Исходный код Редактировать proc matchLen(s: string; pattern: Peg; start = 0): int {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- то же самое, что и
match, но возвращает длину совпадения; если совпадения нет, возвращает -1. Обратите внимание, что длина совпадения может быть нулевой. Возможно, остаётся суффиксs, который не принадлежит совпадению. Исходный код Редактировать proc match(s: string; pattern: Peg; matches: var openArray[string]; start = 0): bool {...}{. noSideEffect, gcsafe, extern: "npegs$1Capture", raises: [], tags: [].}- возвращает
true, еслиs[start..]соответствуетpattern, а захваченные подстроки находятся в массивеmatches. Если совпадение не найдено, ничего не записывается вmatches, и возвращаетсяfalse. Исходный код Редактировать proc match(s: string; pattern: Peg; start = 0): bool {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- возвращает
true, еслиsсоответствуетpattern, начиная сstart. Исходный код Редактировать proc find(s: string; pattern: Peg; matches: var openArray[string]; start = 0): int {...}{. noSideEffect, gcsafe, extern: "npegs$1Capture", raises: [], tags: [].}- возвращает начальную позицию
patternвsи захваченные подстроки в массивеmatches. Если совпадение не найдено, ничего не записывается вmatches, и возвращается -1. Исходный код Редактировать proc findBounds(s: string; pattern: Peg; matches: var openArray[string]; start = 0): tuple[first, last: int] {...}{.noSideEffect, gcsafe, extern: "npegs$1Capture", raises: [], tags: [].}- возвращает начальную и конечную позиции
patternвsи захваченные подстроки в массивеmatches. Если совпадение не найдено, ничего не записывается вmatches, и возвращается (-1,0). Исходный код Редактировать proc find(s: string; pattern: Peg; start = 0): int {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- возвращает начальную позицию
patternвs. Если совпадение не найдено, возвращается -1. Исходный код Редактировать proc findAll(s: string; pattern: Peg; start = 0): seq[string] {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- возвращает все совпадающие подстроки
s, которые соответствуютpattern. Если совпадения нет, возвращается @[]. Исходный код Редактировать proc contains(s: string; pattern: Peg; start = 0): bool {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- то же, что и
find(s, pattern, start) >= 0Исходный код Редактировать proc contains(s: string; pattern: Peg; matches: var openArray[string]; start = 0): bool {...}{. noSideEffect, gcsafe, extern: "npegs$1Capture", raises: [], tags: [].}- то же, что и
find(s, pattern, matches, start) >= 0Исходный код Редактировать proc startsWith(s: string; prefix: Peg; start = 0): bool {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- возвращает true, если
sначинается с шаблонаprefix. Исходный код Редактировать proc endsWith(s: string; suffix: Peg; start = 0): bool {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- возвращает true, если
sзаканчивается шаблономsuffix. Исходный код Редактировать proc replacef(s: string; sub: Peg; by: string): string {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [ValueError], tags: [].}- Заменяет
subвsстрокойby. К захватам можно получить доступ вbyс использованием обозначений$iи$#(см. strutils.`%`). Примеры:"var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2")Результаты:
"var1<-keykey; val2<-key2key2"
Исходный код Редактировать proc replace(s: string; sub: Peg; by = ""): string {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- Заменяет
subвsстрокойby. К захватам нельзя получить доступ вby. Исходный код Редактировать proc parallelReplace(s: string; subs: varargs[tuple[pattern: Peg, repl: string]]): string {...}{. noSideEffect, gcsafe, extern: "npegs$1", raises: [ValueError], tags: [].}- Возвращает изменённую копию
sс применёнными заменами изsubsпараллельно. Исходный код Редактировать proc replace(s: string; sub: Peg; cb: proc (match: int; cnt: int; caps: openArray[string]): string): string {...}{. gcsafe, extern: "npegs$1cb", raises: [], tags: [].}- Заменяет
subвsрезультирующими строками из обратного вызова. Процедура обратного вызова получает индекс текущего совпадения (начиная с 0), количество захватов и открытый массив с захватами каждого совпадения. Примеры:proc handleMatches*(m: int, n: int, c: openArray[string]): string = result = "" if m > 0: result.add ", " result.add case n: of 2: c[0].toLower & ": '" & c[1] & "'" of 1: c[0].toLower & ": ''" else: "" let s = "Var1=key1;var2=Key2; VAR3" echo s.replace(peg"{\ident}('='{\ident})* ';'* \s*", handleMatches)Результаты:
"var1: 'key1', var2: 'Key2', var3: ''"
Исходный код Редактировать proc transformFile(infile, outfile: string; subs: varargs[tuple[pattern: Peg, repl: string]]) {...}{.gcsafe, extern: "npegs$1", raises: [IOError, ValueError], tags: [ReadIOEffect, WriteIOEffect].}-
читает файл
infile, выполняет параллельную замену (вызываетparallelReplace) и записывает обратно вoutfile. ВызываетIOError, если произошла ошибка. Предполагается использование для быстрой работы со скриптами.Примечание: эта процедура не существует при использовании JS-бекенда.
Исходный код Редактировать proc split(s: string; sep: Peg): seq[string] {...}{.noSideEffect, gcsafe, extern: "npegs$1", raises: [], tags: [].}- Разделяет строку
sна подстроки. Исходный код Редактировать proc parsePeg(pattern: string; filename = "pattern"; line = 1; col = 0): Peg {...}{. raises: [ValueError, EInvalidPeg, Exception], tags: [RootEffect].}- строит объект Peg из
pattern.filename,line,colиспользуются для сообщений об ошибках, но они предоставляют только начальные смещения.parsePegотслеживает номера строк и столбцов вpattern. Исходный код Редактировать proc peg(pattern: string): Peg {...}{.raises: [ValueError, EInvalidPeg, Exception], tags: [RootEffect].}- строит объект Peg из
pattern. Короткий имя выбрано для того, чтобы стимулировать его использование в качестве модификатора строки без форматирования:peg"{\ident} \s* '=' \s* {.*}"Исходный код Редактировать proc escapePeg(s: string): string {...}{.raises: [], tags: [].}- эскэпирует
sтак, что он будет сопоставлен дословно, когда используется в качестве PEG. Исходный код Редактировать
Итераторы
iterator items(p: Peg): Peg {...}{.inline, raises: [], tags: [].}- Возвращает дочерние узлы объекта варианта Peg, если они присутствуют. Исходный код Изменить
iterator pairs(p: Peg): (int, Peg) {...}{.inline, raises: [], tags: [].}- Возвращает индексы и дочерние узлы объекта варианта Peg, если они присутствуют. Исходный код Изменить
iterator findAll(s: string; pattern: Peg; start = 0): string {...}{.raises: [], tags: [].}- возвращает все совпадающие подстроки
s, которые соответствуютpattern. Исходный код Изменить iterator split(s: string; sep: Peg): string {...}{.raises: [], tags: [].}-
Разбивает строку
sна подстроки.Подстроки разделены разделителем PEG
sep. Примеры:for word in split("00232this02939is39an22example111", peg"\d+"): writeLine(stdout, word)Результаты:
"this" "is" "an" "example"
Исходный код Изменить
Шаблоны
template letters(): Peg
- расширяется до
charset({'A'..'Z', 'a'..'z'})Исходный код Изменить template digits(): Peg
- расширяется до
charset({'0'..'9'})Исходный код Изменить template whitespace(): Peg
- расширяется до
charset({' ', '\9'..'\13'})Исходный код Изменить template identChars(): Peg
- расширяется до
charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})Исходный код Изменить template identStartChars(): Peg
- расширяется до
charset({'A'..'Z', 'a'..'z', '_'})Исходный код Изменить template ident(): Peg
- такое же, как
[a-zA-Z_][a-zA-z_0-9]*; стандартный идентификатор Исходный код Изменить template natural(): Peg
- такое же, как
\d+Исходный код Изменить template eventParser(pegAst, handlers: untyped): (proc (s: string): int)
- Генерирует интерпретирующий парсер событий proc в соответствии с заданным PEG AST и блоками кода обработчика. proc может вызываться со строкой для разбора и будет выполнять блоки кода обработчика всякий раз, когда будет сопоставлен соответствующий элемент грамматики. Возвращает -1, если строка не соответствует, в противном случае возвращает длину полного соответствия. Следующий пример кода оценивает арифметическое выражение, определенное простым PEG:
import strutils, pegs let pegAst = """ Expr <- Sum Sum <- Product (('+' / '-')Product)* Product <- Value (('*' / '/')Value)* Value <- [0-9]+ / '(' Expr ')' """.peg txt = "(5+3)/2-7*22" var pStack: seq[string] = @[] valStack: seq[float] = @[] opStack = "" let parseArithExpr = pegAst.eventParser: pkNonTerminal: enter: pStack.add p.nt.name leave: pStack.setLen pStack.high if length > 0: let matchStr = s.substr(start, start+length-1) case p.nt.name of "Value": try: valStack.add matchStr.parseFloat echo valStack except ValueError: discard of "Sum", "Product": try: let val = matchStr.parseFloat except ValueError: if valStack.len > 1 and opStack.len > 0: valStack[^2] = case opStack[^1] of '+': valStack[^2] + valStack[^1] of '-': valStack[^2] - valStack[^1] of '*': valStack[^2] * valStack[^1] else: valStack[^2] / valStack[^1] valStack.setLen valStack.high echo valStack opStack.setLen opStack.high echo opStack pkChar: leave: if length == 1 and "Value" != pStack[^1]: let matchChar = s[start] opStack.add matchChar echo opStack let pLen = parseArithExpr(txt)Параметр handlers содержит блоки кода для PegKinds, которые определяют элементы грамматики, представляющие интерес. Каждый блок может содержать код обработчика, который будет выполняться при входе парсера и выходе из совпадения текста с элементом грамматики. Обработчик enter может получить доступ к узлу конкретного PEG AST, сопоставляемому как p, к всей обработанной строке как s и к позиции совпадающего сегмента текста в s как start. Обработчик leave может получить доступ к p, s, start, а также к длине совпадающего сегмента текста как length. При неудачном сопоставлении обработчики enter и leave будут выполнены, при этом length будет установлено в -1.
Символы, объявленные в обработчике enter, могут стать видимыми в соответствующем обработчике leave, аннотируя их пragma inject.
Исходный код Изменить template `=~`(s: string; pattern: Peg): bool
- Это вызывает
matchс неявным объявленным массивомmatches, который может быть использован в области действия вызова=~:if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}": # matches a key=value pair: echo("Key: ", matches[0]) echo("Value: ", matches[1]) elif line =~ peg"\s*{'#'.*}": # matches a comment # note that the implicit ``matches`` array is different from the # ``matches`` array of the first branch echo("comment: ", matches[0]) else: echo("syntax error")Исходный код Изменить
Экспорт
- ==
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/pegs.html