std/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 или назад (последний захват до первого) от ^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 |
Буквальное сопоставление строк для обратной ссылки. |
. |
Любой символ: Если впереди есть символ, потребляйте его и укажите успех. В противном случае (то есть в конце ввода) укажите неудачу. |
_ |
Любой символ Unicode: Если впереди есть символ 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 |
любая буква Unicode |
\upper |
любая заглавная буква Unicode |
\lower |
любая строчная буква Unicode |
\title |
любая буква Unicode с заголовком |
\white |
любой символ пробела Unicode |
Обратный слэш, за которым следует буква, — это встроенный макрос, в противном случае он используется для обычного экранирования:
| нотация | значение |
|---|---|
\\ |
одиночный обратный слэш |
\* |
то же самое, что и '*'
|
\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 ")") /
(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, decode_helpers, unicode
Типы
Captures = object
- содержит захваченные подстроки. Исходный код Редактировать
EInvalidPeg = object of ValueError
- генерируется, если обнаружен неверный PEG Исходный код Редактировать
NonTerminal = ref NonTerminalObj
- Исходный код Редактировать
NonTerminalFlag = enum ntDeclared, ntUsed
- Исходный код Редактировать
Peg {.shallow.} = object case of pkEmpty .. pkWhitespace: nil of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: of pkChar, pkGreedyRepChar: of pkCharChoice, pkGreedyRepSet: of pkNonTerminal: of pkBackRef .. pkBackRefIgnoreStyle: else:- тип, представляющий PEG Исходный код Редактировать
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()- Исходный код Редактировать
Константы
MaxSubpatterns = 20
- определяет максимальное количество подпаттернов, которые могут быть захвачены. Больше подпаттернов захватить нельзя! Исходный код Редактировать
Процедуры
func `!`(a: Peg): Peg {....gcsafe, extern: "npegsNotPredicate", raises: [], tags: [], forbids: [].}- создаёт предикат "не" с PEG
aИсходный код Редактировать func `!*`(a: Peg): Peg {....gcsafe, extern: "npegsSearch", raises: [], tags: [], forbids: [].}- создаёт поиск для PEG
aИсходный код Редактировать func `!*\`(a: Peg): Peg {....gcsafe, extern: "npgegsCapturedSearch", raises: [], tags: [], forbids: [].}- создаёт поиск "с захватом" для PEG
aИсходный код Редактировать func `$`(r: Peg): string {....gcsafe, extern: "npegsToString", raises: [], tags: [], forbids: [].}- преобразует PEG в строковое представление Исходный код Редактировать
func `&`(a: Peg): Peg {....gcsafe, extern: "npegsAndPredicate", raises: [], tags: [], forbids: [].}- создаёт предикат "и" с PEG
aИсходный код Редактировать func `*`(a: Peg): Peg {....gcsafe, extern: "npegsGreedyRep", raises: [], tags: [], forbids: [].}- создаёт "жадное повторение" для PEG
aИсходный код Редактировать func `+`(a: Peg): Peg {....gcsafe, extern: "npegsGreedyPosRep", raises: [], tags: [], forbids: [].}- создаёт "жадное положительное повторение" с PEG
aИсходный код Редактировать func `/`(a: varargs[Peg]): Peg {....gcsafe, extern: "npegsOrderedChoice", raises: [], tags: [], forbids: [].}- создаёт упорядоченный выбор с PEGs в
aИсходный код Редактировать func `?`(a: Peg): Peg {....gcsafe, extern: "npegsOptional", raises: [], tags: [], forbids: [].}- создаёт необязательное значение для PEG
aИсходный код Редактировать func any(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- создаёт PEG любой символ (
.) Исходный код Редактировать func anyRune(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- создаёт PEG любой символ (
_) Исходный код Редактировать func backref(index: range[1 .. MaxSubpatterns]; reverse: bool = false): Peg {. ...gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт обратную ссылку на заданный
index. Счёт начинается с 1.indexуказывает, начинается ли индексирование с конца списка захватов. Исходный код Редактировать func backrefIgnoreCase(index: range[1 .. MaxSubpatterns]; reverse: bool = false): Peg {. ...gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт обратную ссылку на заданный
index. Счёт начинается с 1.indexуказывает, начинается ли индексирование с конца списка захватов. Игнорирует регистр при сопоставлении. Исходный код Редактировать func backrefIgnoreStyle(index: range[1 .. MaxSubpatterns]; reverse: bool = false): Peg {. ...gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт обратную ссылку на заданный
index. Счёт начинается с 1.indexуказывает, начинается ли индексирование с конца списка захватов. Игнорирует стиль при сопоставлении. Исходный код Редактировать func bounds(c: Captures; i: range[0 .. 20 - 1]): tuple[first, last: int] {. ...raises: [], tags: [], forbids: [].}- возвращает границы
[first..last]захватаi. Исходный код Редактировать func capture(a: Peg = Peg(kind: pkEmpty)): Peg {....gcsafe, extern: "npegsCapture", raises: [], tags: [], forbids: [].}- создаёт захват с PEG
aИсходный код Редактировать func ch(p: Peg): char {....raises: [], tags: [], forbids: [].}- Возвращает представление char объекта Peg, если он присутствует. Исходный код Редактировать
func charChoice(p: Peg): ref set[char] {....raises: [], tags: [], forbids: [].}- Возвращает поле charChoice объекта Peg, если оно присутствует. Исходный код Редактировать
func charSet(s: set[char]): Peg {....gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- строит PEG из набора символов
sИсходный код Редактировать func col(nt: NonTerminal): int {....raises: [], tags: [], forbids: [].}- Возвращает номер столбца определения родительского объекта Peg для данного NonTerminal. Исходный код Редактировать
func contains(s: string; pattern: Peg; matches: var openArray[string]; start = 0): bool {. ...gcsafe, extern: "npegs$1Capture", raises: [], tags: [RootEffect], forbids: [].}- то же, что и
find(s, pattern, matches, start) >= 0Исходный код Редактировать func contains(s: string; pattern: Peg; start = 0): bool {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- то же, что и
find(s, pattern, start) >= 0Исходный код Редактировать func endAnchor(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- создаёт PEG
$, который соответствует концу входных данных. Исходный код Редактировать func endsWith(s: string; suffix: Peg; start = 0): bool {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- возвращает true, если
sзаканчивается на шаблонsuffixИсходный код Редактировать func escapePeg(s: string): string {....raises: [], tags: [], forbids: [].}- экранирует
s, чтобы он соответствовал буквально, когда используется как peg. Исходный код Редактировать func find(s: string; pattern: Peg; matches: var openArray[string]; start = 0): int {. ...gcsafe, extern: "npegs$1Capture", raises: [], tags: [RootEffect], forbids: [].}- возвращает начальную позицию
patternвsи захваченные подстроки в массивеmatches. Если не совпадает, ничего не записывается вmatchesи возвращается -1. Исходный код Редактировать func find(s: string; pattern: Peg; start = 0): int {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- возвращает начальную позицию
patternвs. Если не совпадает, возвращается -1. Исходный код Редактировать func findAll(s: string; pattern: Peg; start = 0): seq[string] {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- возвращает все совпадающие подстроки
s, которые соответствуютpattern. Если не совпадает, возвращается@[]. Исходный код Редактировать
func findBounds(s: string; pattern: Peg; matches: var openArray[string]; start = 0): tuple[first, last: int] {....gcsafe, extern: "npegs$1Capture", raises: [], tags: [RootEffect], forbids: [].}- возвращает начальную и конечную позиции
patternвsи захваченные подстроки в массивеmatches. Если совпадения нет, ничего не записывается вmatchesи возвращается (-1,0). Исходный код Редактировать func flags(nt: NonTerminal): set[NonTerminalFlag] {....raises: [], tags: [], forbids: [].}- Получает поле флагов типа NonTerminalFlag родительского объекта варианта Peg для заданного объекта NonTerminal. Исходный код Редактировать
func index(p: Peg): range[-20 .. 20 - 1] {....raises: [], tags: [], forbids: [].}- Возвращает индекс обратной ссылки на захваченный подшаблон в объекте Captures для заданного объекта варианта Peg, если он присутствует. Исходный код Редактировать
func kind(p: Peg): PegKind {....raises: [], tags: [], forbids: [].}- Возвращает PegKind для данного объекта Peg. Исходный код Редактировать
func line(nt: NonTerminal): int {....raises: [], tags: [], forbids: [].}- Получает номер строки определения родительского объекта варианта Peg для заданного NonTerminal. Исходный код Редактировать
func match(s: string; pattern: Peg; matches: var openArray[string]; start = 0): bool {. ...gcsafe, extern: "npegs$1Capture", raises: [], tags: [RootEffect], forbids: [].}- возвращает
trueеслиs[start..]совпадает сpatternи захваченные подстроки в массивеmatches. Если совпадения нет, ничего не записывается вmatchesи возвращаетсяfalse. Исходный код Редактировать func match(s: string; pattern: Peg; start = 0): bool {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- возвращает
trueеслиsсовпадает сpatternначиная сstart. Исходный код Редактировать func matchLen(s: string; pattern: Peg; matches: var openArray[string]; start = 0): int {. ...gcsafe, extern: "npegs$1Capture", raises: [], tags: [RootEffect], forbids: [].}- то же, что и
match, но возвращает длину совпадения, если совпадения нет, возвращается -1. Обратите внимание, что длина совпадения может быть нулевой. Возможно, что суффиксsостаётся, и он не принадлежит совпадению. Исходный код Редактировать func matchLen(s: string; pattern: Peg; start = 0): int {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- то же, что и
match, но возвращает длину совпадения, если совпадения нет, возвращается -1. Обратите внимание, что длина совпадения может быть нулевой. Возможно, что суффиксsостаётся, и он не принадлежит совпадению. Исходный код Редактировать func name(nt: NonTerminal): string {....raises: [], tags: [], forbids: [].}- Получает имя символа, представленного родительским объектом варианта Peg для данного NonTerminal. Исходный код Редактировать
func newLine(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- создаёт PEG перевод_строки (
\n) Исходный код Редактировать func newNonTerminal(name: string; line, column: int): NonTerminal {....gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт нетерминальный символ Исходный код Редактировать
func nonterminal(n: NonTerminal): Peg {....gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт PEG, состоящий из нетерминального символа Исходный код Редактировать
func nt(p: Peg): NonTerminal {....raises: [], tags: [], forbids: [].}- Возвращает объект NonTerminal заданного объекта варианта Peg, если он присутствует. Исходный код Редактировать
func parallelReplace(s: string; subs: varargs[tuple[pattern: Peg, repl: string]]): string {. ...gcsafe, extern: "npegs$1", raises: [ValueError], tags: [RootEffect], forbids: [].}- Возвращает изменённую копию
sс параллельно применёнными заменами изsubs. Исходный код Редактировать func parsePeg(pattern: string; filename = "pattern"; line = 1; col = 0): Peg {. ...raises: [ValueError, EInvalidPeg, Exception], tags: [RootEffect], forbids: [].}- создаёт объект Peg из
pattern.filename,line,colиспользуются для сообщений об ошибках, но предоставляют только начальные смещения.parsePegотслеживает номера строк и столбцов внутриpattern. Исходный код Редактировать func peg(pattern: string): Peg {....raises: [ValueError, EInvalidPeg, Exception], tags: [RootEffect], forbids: [].}- создаёт объект Peg из
pattern. Короткое имя выбрано для использования его как модификатора строковых литералов:peg"{\ident} \s* '=' \s* {.*}"Исходный код Редактировать func rawMatch(s: string; p: Peg; start: int; c: var Captures): int {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- процедура низкого уровня для сопоставления, реализующая интерпретатор PEG. Используйте её для максимальной эффективности (любая другая операция PEG в конечном итоге вызывает эту процедуру). Возвращает -1, если сопоставления нет, в противном случае возвращает длину совпадения Исходный код Редактировать
func replace(s: string; sub: Peg; cb: proc (match: int; cnt: int; caps: openArray[string]): string): string {. ...gcsafe, extern: "npegs$1cb", effectsOf: cb, ...raises: [], tags: [RootEffect], forbids: [].}-
Заменяет
subвsполученными строками из обратного вызова. Процедура обратного вызова получает индекс текущего совпадения (начиная с 0), количество захватов и массив захватов каждого совпадения. Примеры:func 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: ''"
Исходный код Редактировать func replace(s: string; sub: Peg; by = ""): string {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- Заменяет
subвsна строкуby. К захватам нельзя получить доступ вby. Исходный код Редактировать func replacef(s: string; sub: Peg; by: string): string {....gcsafe, extern: "npegs$1", raises: [ValueError], tags: [RootEffect], forbids: [].}-
Заменяет
subвsна строкуby. К захватам можно получить доступ вbyс помощью обозначений$iи$#(см. strutils.%). Примеры:"var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2")Результат:
"var1<-keykey; val2<-key2key2"
Исходный код Редактировать func rule(nt: NonTerminal): Peg {....raises: [], tags: [], forbids: [].}- Получает объект Peg, представляющий определение правила родительского объекта варианта Peg для данного NonTerminal. Исходный код Редактировать
func sequence(a: varargs[Peg]): Peg {....gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт последовательность со всеми PEG из
aИсходный код Редактировать
func split(s: string; sep: Peg): seq[string] {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- Разделяет строку
sна подстроки. Исходный код Редактировать func startAnchor(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- строит PEG
^, который соответствует началу входных данных. Исходный код Редактировать func startsWith(s: string; prefix: Peg; start = 0): bool {....gcsafe, extern: "npegs$1", raises: [], tags: [RootEffect], forbids: [].}- возвращает true, если
sначинается с шаблонаprefix. Исходный код Редактировать func term(p: Peg): string {....raises: [], tags: [], forbids: [].}- Возвращает строковое представление объекта Peg, если он присутствует. Исходный код Редактировать
func term(t: char): Peg {....gcsafe, extern: "npegs$1Char", raises: [], tags: [], forbids: [].}- создаёт PEG из терминального символа Исходный код Редактировать
func term(t: string): Peg {....gcsafe, extern: "npegs$1Str", raises: [], tags: [], forbids: [].}- создаёт PEG из терминальной строки Исходный код Редактировать
func termIgnoreCase(t: string): Peg {....gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт PEG из терминальной строки; игнорируется регистр при сопоставлении Исходный код Редактировать
func termIgnoreStyle(t: string): Peg {....gcsafe, extern: "npegs$1", raises: [], tags: [], forbids: [].}- создаёт PEG из терминальной строки; игнорируется стиль при сопоставлении Исходный код Редактировать
proc transformFile(infile, outfile: string; subs: varargs[tuple[pattern: Peg, repl: string]]) {....gcsafe, extern: "npegs$1", raises: [IOError, ValueError], tags: [ReadIOEffect, WriteIOEffect, RootEffect], forbids: [].}-
считывает файл
infile, выполняет параллельное замещение (вызываетparallelReplace) и записывает обратно вoutfile. ВыбрасываетIOErrorпри возникновении ошибки. Предполагается, что это используется для быстрого скриптинга.Примечание: эта процедура не существует при использовании JS-бекенда.
Исходный код Редактировать func unicodeLetter(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- строит PEG
\letter, который соответствует любому символу Unicode. Исходный код Редактировать func unicodeLower(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- строит PEG
\lower, который соответствует любому символу Unicode в нижнем регистре. Исходный код Редактировать func unicodeTitle(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- строит PEG
\title, который соответствует любому заглавному символу Unicode. Исходный код Редактировать func unicodeUpper(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- строит PEG
\upper, который соответствует любому символу Unicode в верхнем регистре. Исходный код Редактировать func unicodeWhitespace(): Peg {.inline, ...raises: [], tags: [], forbids: [].}- строит PEG
\white, который соответствует любому пробельному символу Unicode. Исходный код Редактировать
Итераторы
iterator findAll(s: string; pattern: Peg; start = 0): string {....raises: [], tags: [RootEffect], forbids: [].}- возвращает все совпадающие подстроки
s, которые соответствуютpattern. Исходный код Редактировать iterator items(p: Peg): Peg {.inline, ...raises: [], tags: [], forbids: [].}- Возвращает дочерние узлы объекта Peg, если они присутствуют. Исходный код Редактировать
iterator pairs(p: Peg): (int, Peg) {.inline, ...raises: [], tags: [], forbids: [].}- Возвращает индексы и дочерние узлы объекта Peg, если они присутствуют. Исходный код Редактировать
iterator split(s: string; sep: Peg): string {....raises: [], tags: [RootEffect], forbids: [].}-
Разделяет строку
sна подстроки.Подстроки разделены PEG
sep. Примеры:for word in split("00232this02939is39an22example111", peg"\d+"): writeLine(stdout, word)Результаты:
"this" "is" "an" "example"
Исходный код Редактировать
Шаблоны
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")Исходный код Изменить template digits(): Peg
- расширяется до
charset({'0'..'9'})Исходный код Изменить template eventParser(pegAst, handlers: untyped): (proc (s: string): int)
-
Генерирует интерпретирующий парсер событий proc в соответствии с указанным AST PEG и блоками обработчиков кода. Proc может быть вызван со строкой для разбора и выполнит блоки обработчика кода всякий раз, когда будет сопоставлен соответствующий элемент грамматики. Возвращает -1, если строка не соответствует, в противном случае возвращает длину всего соответствия. Следующий пример кода оценивает арифметическое выражение, определённое простым PEG:
import std/[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 может получить доступ к конкретному узлу AST PEG, который сопоставляется как p, всей обработанной строке как s и позиции совпавшего фрагмента текста в s как start. Обработчик leave может получить доступ к p, s, start, а также длине сопоставленного фрагмента текста как length. Для неудачного соответствия обработчики enter и leave будут выполнены, с length, установленным в -1.
Символы, объявленные в обработчике enter, могут быть доступны в соответствующем обработчике leave, если пометить их предикатом inject.
Исходный код Изменить template ident(): Peg
- то же, что и
[a-zA-Z_][a-zA-z_0-9]*; стандартный идентификатор Исходный код Изменить template identChars(): Peg
- расширяется до
charset({'a'..'z', 'A'..'Z', '0'..'9', '_'})Исходный код Изменить template identStartChars(): Peg
- расширяется до
charset({'A'..'Z', 'a'..'z', '_'})Исходный код Изменить template letters(): Peg
- расширяется до
charset({'A'..'Z', 'a'..'z'})Исходный код Изменить template natural(): Peg
- то же, что и
\d+Исходный код Изменить template whitespace(): Peg
- расширяется до
charset({' ', '\9'..'\13'})Исходный код Изменить
© 2006–2024 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/pegs.html