0Pricing
Lua Academy · Урок

Простой анализатор выражений

Создайте анализатор арифметических выражений методом рекурсивного спуска на чистом Lua.

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

Цель: разбор арифметических выражений

Создайте парсер с рекурсивным спуском на чистом Lua, который разбирает и вычисляет выражения вроде 3 + 4 * (2 - 1), соблюдая приоритет операторов.

Токенизация

Сначала разбейте входные данные на числа, операторы и круглые скобки.

local function tokenize(expr)
  local tokens = {}
  for tok in expr:gmatch("[%d%.]+|[%+%-%*/%(%)%^]|%S") do
    tokens[#tokens+1] = tok
  end
  return tokens
end

Улучшенный токенизатор

Более надёжный токенизатор с использованием шаблонов gmatch.

local function tokenize(expr)
  local tokens = {}
  for tok in expr:gmatch("%d+%.?%d*|[%+%-%*/%(%)%^]") do
    tokens[#tokens+1] = tok
  end
  return tokens
end

Состояние парсера

Парсер хранит список токенов и позицию. Вспомогательные функции peek и consume перемещаются по токенам.

local function newParser(tokens)
  local pos = 1
  local function peek() return tokens[pos] end
  local function consume() pos = pos + 1; return tokens[pos-1] end
  return {peek=peek, consume=consume}
end

Грамматика выражений

Грамматика соблюдает приоритет:

  • выражение = член ((+ | -) член)*
  • член = множитель ((* | /) множитель)*
  • множитель = число | (выражение) | -множитель

Разбор членов выражения

Разбирайте выражения сложения и вычитания, разбирая члены и последовательно слева направо объединяя результаты операций + и -.

local parseExpr, parseTerm, parseFactor
parseExpr = function(p)
  local val = parseTerm(p)
  while p.peek() == "+" or p.peek() == "-" do
    local op = p.consume()
    local right = parseTerm(p)
    if op == "+" then val = val + right
    else val = val - right end
  end
  return val
end

Разбор множителей

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

parseTerm = function(p)
  local val = parseFactor(p)
  while p.peek() == "*" or p.peek() == "/" do
    local op = p.consume()
    local right = parseFactor(p)
    if op == "*" then val = val * right
    else val = val / right end
  end
  return val
end

Разбор чисел и скобок

Разбирайте атомарные элементы: литералы, подвыражения в скобках и унарное отрицание.

parseFactor = function(p)
  local tok = p.peek()
  if tok == "(" then
    p.consume()  -- (
    local val = parseExpr(p)
    p.consume()  -- )
    return val
  elseif tok == "-" then
    p.consume()
    return -parseFactor(p)
  else
    return tonumber(p.consume())
  end
end

Собираем всё вместе

Выполните токенизацию, разберите и вычислите выражение.

local function eval(expr)
  local tokens = tokenize(expr)
  local parser = newParser(tokens)
  return parseExpr(parser)
end
print(eval("3 + 4 * 2"))      -- 11
print(eval("(3 + 4) * 2"))    -- 14
print(eval("2 ^ 10"))         -- 1024 (if ^ is supported)

Расширение парсера

Добавляйте переменные, функции, операторы сравнения и строковые литералы, расширяя грамматику и добавляя новые варианты в parseFactor.

Вывод сообщений об ошибках

Отслеживайте номер строки и столбца в токенизаторе. Выдавайте понятные ошибки: "Expected ')' at position 7, got '+'".

Генераторы парсеров

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

Вопрос о парсере

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

Итоги: простой парсер выражений

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. Принципы проектирования DSL в Lua
  2. Перегрузка операторов для удобной работы с DSL
  3. Создание DSL конфигурации
  4. Простой анализатор выражений
← Назад к Lua Academy