Нетривиальный Repl

Это плавный переход от eval к loop к циклам и где уместно вспомнить, что эта тема уже затрагивалась в контексте самой часто употребляемой функции for и рекурсии. Repl как функция не анонсирована в документации, но есть авторская классификация в файлах репозитария, где кроме неё указаны ещё две функции, которые в документации есть. Это remark и complete. Естественно, что к функции нет формы обращения, но зато на ееё примере, мы, наконец, приведем полное описание определения функции. Кроме самого кода определения полный список зависимостей, а это, все-таки не только функции, но и глобальные параметры. Кстати, у меня есть точка зрения на функции как на параметры, но это отдельная тема. Итак код опреления
(de repl (Exe (i8* . Prmt) X)
(if (and (symb? X) (== (firstByte X) (char "-")))
(let E (save (parse (xName X) YES (hex "5D0A") 0)) # '\n', ']', EOF
(evList E) )
(let
(Lnk (val $NsLink)
Tr1 (save (val $Transient))
Tr2 (save (val 2 $Transient))
Pr1 (save (val $PrivT))
Pr2 (save (val 2 $PrivT))
V (link (push -ZERO NIL))
Raw (val Termio) )
(save (val $Intern))
(set $NsLink (val $Link))
(when (nil? X)
(setCooked)
(unless (val $Repl)
(set $Repl YES)
(iSignal (val SIGINT Sig) (fun sig)) ) )
(rdOpen Exe X (b8+ (ioFrame T)))
(set $PrivT (set 2 $PrivT $Nil))
(set $Rule (set $Transient (set 2 $Transient $Nil)))
(setq X $Nil)
(if (== (val $InFile) (val (val $InFiles))) # Stdin
(until (nil? (stdRead Prmt))
(let Y (set V @)
(setq X
(if (and (=0 (val $Chr)) Prmt)
(stdEval Y)
(eval Y) ) ) ) )
(until (nil? (read1 0))
(setq X (eval (set V @))) ) )
(popInFiles)
(set $Intern (val (val $NsLink)))
(when Raw
(setRaw) )
(set 2 $PrivT Pr2)
(set $PrivT Pr1)
(set 2 $Transient Tr2)
(set $Transient Tr1)
(set $NsLink Lnk)
X ) ) )
https://picolisp-manual.tiddlyhost.com/#repl
А теперь комментарии
Определение функции
repl
:Copy(de repl (Exe (i8* . Prmt) X)
Функция
repl
принимает три аргумента:Exe
,Prmt
иX
.Exe
— это исполняемый файл,Prmt
— это подсказка (prompt), аX
— это входное выражение.Проверка символа:
Copy(if (and (symb? X) (== (firstByte X) (char "-")))
Если
X
является символом и начинается с "-", то выполняется следующий блок кода.Обработка символа:
Copy(let E (save (parse (xName X) YES (hex "5D0A") 0)) (evList E))
Здесь происходит разбор и выполнение выражения
X
.parse
разбирает имя символа, аevList
выполняет список выражений.Основной блок REPL:
Copy(let (Lnk (val $NsLink) Tr1 (save (val $Transient)) Tr2 (save (val 2 $Transient)) Pr1 (save (val $PrivT)) Pr2 (save (val 2 $PrivT)) V (link (push -ZERO NIL)) Raw (val Termio))
Здесь сохраняются текущие значения различных системных переменных и создается новая переменная
V
.Настройка окружения:
Copy(save (val $Intern)) (set $NsLink (val $Link)) (when (nil? X) (setCooked) (unless (val $Repl) (set $Repl YES) (iSignal (val SIGINT Sig) (fun sig))))
Сохраняются текущие значения и настраивается окружение для выполнения REPL. Если
X
равенnil
, то устанавливается обработчик сигналаSIGINT
.Чтение и выполнение выражений:
Copy(rdOpen Exe X (b8+ (ioFrame T))) (set $PrivT (set 2 $PrivT $Nil)) (set $Rule (set $Transient (set 2 $Transient $Nil))) (setq X $Nil)
Открывается поток ввода и настраиваются переменные окружения.
Цикл чтения и выполнения:
Copy(if (== (val $InFile) (val (val $InFiles))) (until (nil? (stdRead Prmt)) (let Y (set V @) (setq X (if (and (=0 (val $Chr)) Prmt) (stdEval Y) (eval Y))))) (until (nil? (read1 0)) (setq X (eval (set V @)))))
В зависимости от источника ввода (стандартный ввод или файл), выполняется цикл чтения и выполнения выражений.
Восстановление окружения:
Copy(popInFiles) (set $Intern (val (val $NsLink))) (when Raw (setRaw)) (set 2 $PrivT Pr2) (set $PrivT Pr1) (set 2 $Transient Tr2) (set $Transient Tr1) (set $NsLink Lnk) X
Восстанавливаются сохраненные значения системных переменных и возвращается результат выполнения последнего выражения.
И, наконец, список зависимостей
Системные переменные:
$NsLink
: Используется для хранения и восстановления текущего пространства имен.$Transient
: Используется для временных данных.$PrivT
: Используется для приватных данных.$Intern
: Используется для внутренних данных.$Repl
: Флаг, указывающий, что REPL активен.$InFile
: Указывает на текущий входной файл.$InFiles
: Список входных файлов.$Chr
: Текущий символ.$Link
: Текущая связь.$Rule
: Правила, используемые в окружении.
Функции:
symb?
: Проверяет, является ли объект символом.firstByte
: Возвращает первый байт символа.save
: Сохраняет текущее значение переменной.parse
: Разбирает строку или символ.evList
: Выполняет список выражений.setCooked
: Устанавливает режим cooked для ввода.iSignal
: Устанавливает обработчик сигнала.rdOpen
: Открывает поток ввода.ioFrame
: Создает фрейм ввода-вывода.stdRead
: Читает стандартный ввод.stdEval
: Выполняет выражение из стандартного ввода.eval
: Выполняет выражение.read1
: Читает одно выражение.popInFiles
: Удаляет текущий входной файл из стека.setRaw
: Устанавливает режим raw для ввода.
По пути и прокомментируем код и других двух, без указания зависимостей, поскольку их код, все-таки, не такой большой.
Этот код на языке PicoLisp определяет функцию remark
, которая, вероятно, используется для аннотирования или комментирования данных. Давайте разберем его по частям:
Определение функции
remark
:Copy(de remark ("X")
Функция
remark
принимает один аргумент"X"
, который может быть числом, символом или списком.Локальная переменная
Lst
:Copy(let? Lst
Объявляется локальная переменная
Lst
, которая будет использоваться для хранения результатов.Рекурсивная функция
recur
:Copy(recur ("X")
Внутри
remark
определена рекурсивная функцияrecur
, которая также принимает один аргумент"X"
.Создание списка с помощью
make
:Copy(make (cond
Используется функция
make
для создания списка на основе условий.Условия для чисел:
Copy((num? "X") (when (>= 799999 "X" 700000) (link (dat$ "X" "-")) ) (unless (=0 *Scl) (link (format "X" *Scl)) ) )
Если
"X"
является числом, то:Если число находится в диапазоне от 700000 до 799999, добавляется элемент с использованием функции
dat$
.Если переменная
*Scl
не равна нулю, добавляется отформатированное значение"X"
с учетом*Scl
.
Условия для символов:
Copy((sym? "X") (let? Nsp (nsp "X") (or (== 'pico Nsp) (== 'priv Nsp) (link (pack (sym Nsp) "~" (sym "X"))) ) ) (when (type "X") (link (sym @)) ) )
Если
"X"
является символом, то:Определяется пространство имен
Nsp
символа"X"
с помощью функцииnsp
.Если пространство имен равно
'pico
или'priv
, ничего не добавляется.В противном случае добавляется упакованная строка, состоящая из символа пространства имен, символа
~
и символа"X"
.Если у символа есть тип, добавляется символ этого типа.
Условие по умолчанию:
Copy(T (and (recurse (car "X")) (chain @)))
Если
"X"
не является ни числом, ни символом, то предполагается, что это список. В этом случае рекурсивно обрабатывается первый элемент списка(car "X")
, и результат добавляется в цепочку.Вывод результата:
Copy(prin " " (and (=1 (%@ "isatty" 'I (fd))) "\e[0;36m") "#" ) (for X Lst (prin " " X) ) (when (=1 (%@ "isatty" 'I (fd))) (prin "\e[0m") )
Выводится строка с отступом и символом
#
.Если стандартный ввод является терминалом, добавляется ANSI-код для изменения цвета текста на голубой.
Затем выводятся все элементы списка
Lst
.Если стандартный ввод является терминалом, сбрасывается цвет текста с помощью ANSI-кода.
Таким образом, функция remark
обрабатывает входные данные, создавая список аннотаций или комментариев, и выводит их в формате, который может включать цветовое оформление, если вывод направлен в терминал.
Определение функции
complete
:Copy(de complete (S)
Функция
complete
принимает один аргументS
, который, вероятно, является строкой или символом.Проверка аргумента
S
:Copy(when S
Если
S
не равенnil
(то есть, еслиS
имеет значение), выполняется следующий блок кода.Установка значения переменной
*Cmpl
:Copy(setq "*Cmpl" (if (=T S) (list " ") (flip (all* S)) ) )
Здесь происходит проверка значения
S
:Если
S
равноT
(истина), то переменной*Cmpl
присваивается список, содержащий строку из трех пробелов" "
.В противном случае, переменной
*Cmpl
присваивается результат выполнения функцииflip
над результатом функцииall*
, примененной кS
.
Функции
all*
иflip
:all*
: Эта функция, вероятно, возвращает список всех возможных дополнений или вариантов для строкиS
. Точное поведение зависит от реализации, но обычно это может быть поиск по шаблону или регулярному выражению.flip
: Эта функция, вероятно, изменяет порядок элементов в списке на обратный.
Извлечение значения переменной
*Cmpl
:Copy(pop '"*Cmpl")
Функция
pop
извлекает и возвращает значение переменной*Cmpl
. В PicoLisppop
обычно используется для извлечения значения из списка или стека, но в данном случае она используется для извлечения значения переменной.
Таким образом, функция complete
устанавливает значение переменной *Cmpl
в зависимости от входного аргумента S
и затем извлекает это значение. Если S
равно T
, то возвращается список с тремя пробелами, иначе возвращается перевернутый список всех возможных дополнений для S
.
Subscribe to my newsletter
Read articles from Sergey Shishkin directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Sergey Shishkin
Sergey Shishkin
Всегда чему-то учусь!