---
# System prepended metadata

title: Подходы к определению парсинга

---

# Подходы к определению парсинга

Говоря о разборе каких-либо данных в соответствии с каким-либо форматом, необходимо понимать, что именно имеется в виду под разбором: что мы имеем и что хотим получить.

Ниже предлагается способ формализации этих понятий и их связь с различными математическими теориями.


## Теоретико-множественная формулировка

***Буфер*** $M$ — массив элеметов: $M = [b_1, b_2, ..., b_n]$

*Часто элементами являются байты, однако это не требуется, разбор можно проводить хоть для последовательности табуреток.*

***Сегмент*** $s(i, j)$ буфера $M$ — непрерывное непустое подмножество (отрезок) $M$: $s = [b_i, ..., b_j], \quad 1 \le i \le j \le n$

![](https://i.imgur.com/PVqZ5gB.png =400x)

Т.о. сегмент определяется двумя индексами в буфере. Одновременно с этим сегменты можно определять и другой парой чисел:
$$
\mathbf{offset}(s) := i - 1 \quad(0 \le \mathbf{offset}(s) \le n-1) \\
\mathbf{size}(s) := j - i +1 \quad (1 \le \mathbf{size}(s) \le n) \\
\mathbf{offset}(s) + \mathbf{size}(s) \le n
$$

* $S(M)$ — множество всех возможных сегментов буфера $M$
  * $|S(M)| = \frac{n(n^2+3n-2)}{2}$, где $n$ — размер буфера
* Сегменты, как подмножества $M$, могут *пересекаться* и *вкладываться*: 

$$
s_1 \cap s_2, \quad s_3 \subset s_4
$$

***Разбор*** буфера $P$ — непустое множество сегментов $P \subset S(M)$

***Покрытие разбора*** $c(P)$ — объединение сегментов разбора

$$
c(P) = \bigcup_{s \in P} s
$$

*Пример разбора:*
![](https://i.imgur.com/6AU3Eqe.png =650x)

Но нас будут интересовать не просто разборы, а *правильные* разборы!

***Множество правильных разборов*** — такие разборы, что:
1. Разбор из одного сегмента $P = \{s\}$ — правильный
2. Если $P$ — правильный, то $P' = P \cup\{s\}$ — тоже правильный, где $s$ — сегмент, содержащий покрытие $P$: $c(P) \subset s$
3. Если $P_1$ и $P_2$ — два правильных разбора, покрытия которых не пересекаются ($c(P_1) \cap c(P_2) = \emptyset$), то $P = P_1 \cup P_2$ — тоже правильный разбор.

Такое оределение очень похоже на определение *правильных скобочных последовательностей*, и это не случайно. Можно рассматривать сегмент как пару "скобок", стоящих в определенном месте между элементами буфера, тогда разбор — это некая скобочная последовательность, "привязанная" к буферу, а правильный разбор — правильная скобочная последовательность.

***Полный разбор*** — разбор, в котором для каждого элемента буфера существует сегмент, содержащий его и при этом не содержащий других сегментов.

***Корневой разбор*** — разбор, содержащий сегмент $s(1, n)$, т.е. весь буфер.

Все приведенные понятия никак не опираются на свойства элементов буфера и по сути воспринимают буфер как абстрактное множество. Это значит, что свойства правильности, полноты и т.д. никак не связаны со *значениями* элементов буфера (значениями байтов). Буфер с точки зрения этих понятий определяется только размером $n$.

На данном этапе можно сказать, что задача парсинга включает в себя отыскание некоторого **полного правильного корневого разбора** буфера:

***Разборщик*** — это алгоритм, который на вход получает буфер и либо завершается успешно и возвращает полный правильный корневой разбор буфера, либо завершается неуспешно (ошибка разбора).

Встает вопрос: как именно разборщик выбирает, какой из полных правильных разборов вернуть и в каком случае завершиться с ошибкой? Здесь уже начинают играть роль *значения* элементов буфера.

***Язык разборщика*** — множество буферов, принимаемых данным разборщиком без ошибки.

В данном разделе мы не затронем вопроса, как именно определяется язык разборщика. Здесь мы лишь предъявили требования к результату разбора, определили структуру, которую хотим видеть на выходе алгоритма. Разговор о языках разборщиков пойдет ниже.

## Формулировка в терминах грамматик

В рамках теории формальных языков, формат данных — это некий язык. В качестве алфавита логично выбрать двоичный алфавит: $A = \{0, 1\}$. Соответственно буферы (бинарные файлы) — это слова, которые могут принадлежать языку, или не принадлежать. 

## Связь формулировок, дерево разбора

У правильных разборов есть удобное представление в виде **дерева**. Действительно, в правильном разборе сегменты не могут пересекаться, они могут только *вкладываться* друг в друга. Это дает возможность говорить о том, что один сегмент является *дочерним* для другого, а тот — *родительским*. Такое дерево называется **деревом разбора**.

*Пример представления разбора в виде дерева разбора:*

![](https://i.imgur.com/PEJJpkL.png)

В данном примере в дереве 13 узлов — в разборе 13 сегментов:
ROOT, MAGIC, HEADERS, BODY, HDR1-2, CHUNK1-3, K, V (две пары).
У каждой вершины есть соответствующий ей сегмент. У некоторых сегментов есть дочерние сегменты, которые вкладываются в него.