Spec-Zone.ru › Nim

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:

  1. Разбор строки в AST, состоящий из узлов Peg с помощью процедуры peg.
  2. Непосредственное построение 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API