Приоритет токенайзеров

Библиотека Marked — это современный парсер Markdown для JavaScript, позволяющий конвертировать Markdown-текст в HTML с высокой производительностью. Основой работы Marked является концепция токенайзеров, которые разбивают исходный текст на структурные элементы — токены, соответствующие заголовкам, спискам, ссылкам, кодовым блокам и другим конструкциям Markdown.

Каждый токен содержит информацию о типе элемента, его содержимом и дополнительных параметрах. Например, токен для заголовка хранит уровень заголовка и текст, а токен списка — массив элементов списка и информацию о нумерации.

Структура токенайзеров

Marked использует два уровня токенизации:

  1. Лексическая токенизация (Lexer) На этом уровне текст разбивается на блоки и строки, формируются базовые токены:

    • Заголовки (heading)
    • Параграфы (paragraph)
    • Списки (list) и элементы списка (list_item)
    • Цитаты (blockquote)
    • Кодовые блоки (code)
    • Горизонтальные линии (hr)
  2. Синтаксическая токенизация (Inline Lexer) Каждый блок, полученный на первом этапе, дополнительно обрабатывается на уровне inline-токенов:

    • Ссылки (link) и изображения (image)
    • Жирный и курсивный текст (strong, em)
    • Код в строке (codespan)
    • Страйк (del)

Разделение на блоки и inline позволяет эффективно управлять разбором сложного текста и применять приоритеты токенов.

Приоритет токенов

Приоритет токенов определяет порядок, в котором Marked проверяет возможность соответствия правилам. Он необходим, чтобы однозначно интерпретировать сложные конструкции Markdown, где один и тот же фрагмент текста может подходить под несколько правил.

Примеры влияния приоритета:

  • Тройные звездочки ***текст*** могут трактоваться как жирный и курсив одновременно. Если токен strong имеет более высокий приоритет, сначала будет найден жирный текст, а оставшийся символ * обрабатывается как курсив.
  • Символ > в начале строки может быть как частью цитаты, так и простой стрелкой. Токен blockquote имеет приоритет выше, чем обычный текст, поэтому Marked сначала проверяет возможность создания цитаты.

Приоритеты задаются в виде массива правил, где элементы ранжированы сверху вниз. Lexer проверяет текст последовательно по этим правилам, и первый совпавший токен становится активным.

Настройка и расширение токенайзеров

Marked предоставляет возможность определять собственные токены и правила, расширяя стандартный набор. Это особенно важно для специфических Markdown-диалектов:

import { Lexer, Parser } from 'marked';

const customLexer = new Lexer({
  // Настройка стандартных правил
  gfm: true,
  breaks: true,
  smartLists: true
});

// Добавление пользовательского токена
customLexer.rules.customTag = /^:::(\w+)\s([\s\S]+?):::/;

const tokens = customLexer.lex(":::note Текст заметки :::");

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

Влияние приоритета на производительность

Правильная настройка приоритета токенов не только обеспечивает корректный разбор текста, но и сокращает время обработки, особенно на больших документах. Если часто встречающийся токен находится ниже по приоритету, Lexer будет проверять все предыдущие правила для каждой строки, что увеличивает нагрузку.

Рекомендации по оптимизации:

  • Часто встречающиеся токены размещать выше в массиве правил.
  • Пользовательские токены ставить после основных блоков, если они редкие.
  • Для inline-токенов использовать жадный или ленивый режим (g vs y) в регулярных выражениях, чтобы ускорить поиск совпадений.

Интеграция с Parser

Lexer генерирует массив токенов, а Parser превращает их в HTML. Parser также учитывает приоритет, так как последовательность токенов определяет вложенность элементов. Например, вложенный список в цитате будет корректно отрисован только если blockquote обработан раньше list.

import { marked } from 'marked';

const markdown = "> - Элемент списка в цитате";
const html = marked.parse(markdown);

console.log(html);
// <blockquote><ul><li>Элемент списка в цитате</li></ul></blockquote>

Правильный порядок токенов обеспечивает корректную вложенность и предотвращает неправильное разбиение на блоки.

Заключение по приоритетам

Приоритет токенов в Marked — это ключевой механизм управления точностью и эффективностью разбора Markdown. Он определяет, какие правила применяются первыми, гарантируя корректное преобразование текста в HTML. Понимание структуры Lexer и Inline Lexer, а также умение настраивать пользовательские токены, позволяет создавать расширяемые и высокопроизводительные Markdown-парсеры в JavaScript.