0Pricing
TypeScript Academy · Урок

Основы комбинаторов анализаторов

Составляйте крупные анализаторы из небольших

«Основы комбинаторов анализаторов» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.

Что такое анализатор

Анализатор читает входные данные (обычно строку) и создаёт структурированный результат либо завершается неудачей. Мы начнём с уровня значений, чтобы сформировать интуитивное понимание, а затем перенесём те же идеи в систему типов.

type Parser<T> = (input: string) => { value: T; rest: string } | null;
// Consumes part of the input, returns a value and the leftover

Небольшой анализатор

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

function literal(prefix: string): Parser<string> {
  return input =>
    input.startsWith(prefix)
      ? { value: prefix, rest: input.slice(prefix.length) }
      : null;
}
console.log(literal("ab")("abc"));

Комбинаторы

Комбинатор принимает небольшие анализаторы и создаёт из них более крупные. Именно поэтому такой подход называется комбинаторами анализаторов: сложные анализаторы собираются из простых повторно используемых частей.

// Small parsers: digit, letter, literal
// Combinators: sequence, choice, many
// Compose them into: number, identifier, expression

Последовательное выполнение

Комбинатор последовательности запускает один анализатор, затем другой для оставшихся данных и объединяет их результаты. Оба анализатора должны завершиться успешно, чтобы последовательность считалась успешной.

function seq<A, B>(pa: Parser<A>, pb: Parser<B>): Parser<[A, B]> {
  return input => {
    const ra = pa(input);
    if (!ra) return null;
    const rb = pb(ra.rest);
    if (!rb) return null;
    return { value: [ra.value, rb.value], rest: rb.rest };
  };
}

Использование последовательности

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

const ab = seq(literal("a"), literal("b"));
const r = ab("abc");
console.log(r); // { value: ["a","b"], rest: "c" }

Выбор

Комбинатор выбора сначала применяет первый анализатор, а при его сбое — второй. Так выражаются альтернативы, например «digit или буква».

function alt<T>(p1: Parser<T>, p2: Parser<T>): Parser<T> {
  return input => p1(input) ?? p2(input);
}

Повторение

Комбинатор many многократно применяет анализатор, пока тот не завершится с ошибкой, и собирает все результаты. С его помощью разбираются списки, последовательности digits или пробелы.

function many<T>(p: Parser<T>): Parser<T[]> {
  return input => {
    const out: T[] = [];
    let rest = input;
    let r = p(rest);
    while (r) { out.push(r.value); rest = r.rest; r = p(rest); }
    return { value: out, rest };
  };
}

Отображение результатов

Комбинатор map преобразует результат анализатора, не изменяя потребляемый им ввод, например превращая сопоставленные символы digit в число.

function map<A, B>(p: Parser<A>, f: (a: A) => B): Parser<B> {
  return input => {
    const r = p(input);
    return r ? { value: f(r.value), rest: r.rest } : null;
  };
}

Построение

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

const digit = alt(literal("0"), literal("1")); // toy digit
const digits = many(digit);
console.log(digits("0110x"));

От значений к типам

Ключевая идея заключается в том, что та же композиционная структура работает и на уровне типов. Там «вводом» является строковый тип-литерал, а анализаторы представляют собой условные типы, использующие вывод по шаблону. Далее мы перейдём к этому уровню.

// Value level: (input: string) => { value, rest } | null
// Type level:  conditional types over string literal types

Зачем нужен разбор на уровне типов

Анализаторы на уровне типов позволяют компилятору понимать структуру строковых литералов: пути маршрутов, строки форматов и ключи запросов. Сформированное здесь представление о комбинаторах напрямую связано с приёмами на уровне типов, которые используются в остальной части курса.

// Goal: parse "users/:id" into { id: string } at compile time

Быстрая проверка

Проверьте, насколько хорошо Вы понимаете комбинаторы анализаторов.

Итоги

Вы сформировали представление об анализаторах на уровне значений.

  • Анализатор потребляет ввод и возвращает значение вместе с остатком либо завершается с ошибкой.
  • Комбинаторы (последовательность, выбор, повторение и отображение) объединяют небольшие анализаторы в более крупные.
  • Та же структура применяется и на уровне типов.

Далее: разбиение строк в системе типов.

Часто задаваемые вопросы

Урок «Основы комбинаторов анализаторов» бесплатный?

Да — полный текст урока «Основы комбинаторов анализаторов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.

Чему я научусь в уроке «Основы комбинаторов анализаторов»?

Составляйте крупные анализаторы из небольших Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать TypeScript Academy?

Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Основы комбинаторов анализаторов»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке TypeScript Academy?

Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Основы комбинаторов анализаторов
  2. Разделение строк на уровне типов
  3. Разбор с помощью шаблонных литералов
  4. Мини-анализатор маршрутов на уровне типов
← Назад к TypeScript Academy