Простой REPL на Lua

Простой REPL на Lua

REPL раскрывается как Read—Eval—Print Loop. Вводишь код — он тут же исполняется, результат показывается.

while true do
  io.write("lua> ")
  local input = io.read("*l") -- <1>

  if not input then -- <2>
    os.exit()
  end

  local chunk, reason = load("return " .. input, "=stdin", "t") -- <3>

  if not chunk then -- <4>
    chunk, reason = load(input, "=stdin", "t")
  end

  if not chunk then -- <5>
    io.stderr:write("Syntax error: " .. reason .. "\n")
  else
    local result = table.pack(xpcall(chunk, debug.traceback)) -- <6>
    local success = table.remove(result, 1)
    result.n = result.n - 1

    if not success then
      io.stderr:write("Runtime error: " .. result[1] .. "\n")
    elseif result.n > 0 then -- <7>
      print(table.unpack(result, 1, result.n))
    end
  end
end

Комментарии:

  1. io.read("*l") читает одну строку без \n в конце.
  2. Возвращается nil, если юзер нажмёт ^D. Закрываемся.

  3. load грузит код в фукцию.

    Здесь мы сначала пытаемся подставить в начало return. Это обрабатывает код типа 20 * math.log(math.sqrt(10), 10) — то есть все выражения.

    Значение "=stdin" используется для подстановки в сообщения об ошибках:

    > load([[error("test")]], "=test", "t")()
    test:1: test
    stack traceback:
            [C]: in function 'error'
            test:1: in main chunk
            (...tail calls...)
            [C]: in ?
    
  4. Если load вернуло ошибку, был дан некорректный синтаксис. Так как добавили return сначала, ошибка могла возникнуть от этого. Например, если ввели код вроде pi = 3 — все стейтменты.

    Пробуем скомпилировать без return.

  5. Если и теперь не скомпировалось, то во введённом коде синтаксическая ошибка, о чём мы пишем.

  6. Тут два момента.

    1. xpcall выполняет функцию (скомпилированный код) так, что при ошибке программа не крашится, а вызывает debug.traceback для получения трейсбэка. Мы его потом напишем.

    2. table.pack используем вместо {}, чтобы ловить nil в выхлопных значениях функции. Эта функция записывает в таблицу число всех элементов, ей переданных: table.pack(nil, nil, nil).n == 3.

  7. Если код (pi = e) не вернул какого-либо значения, даже nil, то result.n будет равен нулю. Тогда мы ничего не пишем.

Результат:

$ lua5.3 repl.lua
lua> 1, 2
1       2
lua> nil, 2
nil     2
lua> nil, nil, nil, 4, nil, nil
nil     nil     nil     nil     4       nil
lua> print("Hello!")
Hello!
lua> blah
nil
lua> synt@x 3rr0r
Syntax error: stdin:1: syntax error near '@'
lua> runtimeError()
Runtime error: stdin:1: attempt to call a nil value (global 'runtimeError')
stack traceback:
        stdin:1: in main chunk
        [C]: in function 'xpcall'
        repl.lua:24: in main chunk
        [C]: in ?
lua> os.exit()

Подробнее

results for ""

    No results matching ""