Каковы задачи "читателя" в интерпретации Lisp?

Я задаюсь вопросом о цели или, возможно, более правильно, задачах "читателя" во время интерпретации/компиляции программ Lisp.

Из предварительного исследования вопросов, которое я только что сделал, мне кажется, что читатель (в частности, для Clojure в этом случае) можно рассматривать как "синтаксический препроцессор". Основными обязанностями являются расширение читательских макросов и примитивных форм. Итак, два примера:

'cheese         -->  (quote cheese)
{"a" 1 "b" 2}   -->  (array-map "a" 1 "b" 2)

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

Как далеко от истины это (и я слишком упростил весь процесс)? Какие еще задачи выполняет читатель? Учитывая достоинство Lisps - их гомоцикличность (код как данные), почему существует необходимость в лексическом анализе (если это действительно сопоставимо с работой читателя)?

Спасибо!

Ответ 1

Обычно читатель в Lisp считывает s-выражения и возвращает структуры данных. READ - операция ввода-вывода: Input - это поток символов, а вывод - Lisp.

Принтер выполняет обратное: он принимает данные Lisp и выводит их как поток символов. Таким образом, он также может печатать данные Lisp для внешних s-выражений.

Обратите внимание, что интерпретация означает что-то конкретное: выполнение кода интерпретатором. Но многие системы Lisp (включая Clojure) используют компилятор. Задачи вычисления значения для формы Lisp обычно называются оценкой. Оценка может быть выполнена путем интерпретации, путем компиляции или сочетанием обоих.

S-Expression: символические выражения. Внешнее текстовое представление данных. Внешние означает, что s-выражения - это то, что вы видите в текстовых файлах, строках и т.д. Таким образом, s-выражения сделаны из символов на некоторых, обычно внешних, средних.

Lisp структуры данных: символы, списки, строки, числа, символы,...

Reader: читает s-выражения и возвращает структуры данных Lisp.

Обратите внимание, что s-выражения также используются для кодирования исходного кода Lisp.

В некоторых диалектах Lisp читатель программируется и управляется таблицей (через так называемую таблицу чтения). Эта таблица чтения содержит функции считывателя для символов. Например, символ "quote" привязан к функции, которая считывает выражение и возвращает значение (выражение "quote quote" ). Числовые символы 0..9 привязаны к функциям, которые читают число (на самом деле это может быть более сложным, так как некоторые Lisps позволяют читать числа в разных базах).

S-выражения обеспечивают внешний синтаксис структур данных.

Lisp программы записываются во внешней форме с использованием s-выражений. Но не все s-выражения действительны Lisp программы:

(if a b c d e)   is usually not a valid Lisp program

синтаксис Lisp обычно определяется поверх данных Lisp.

IF имеет, например, следующий синтаксис (в Common Lisp http://www.lispworks.com/documentation/HyperSpec/Body/s_if.htm):

if test-form then-form [else-form]

Поэтому он ожидает тестовую форму, затем-форму и необязательную форму else.

В качестве s-выражений допустимы IF-выражения:

(if (foo) 1 2)
(if (bar) (foo))

Но так как программы Lisp являются формами, мы можем также построить эти формы с помощью программ Lisp:

(list 'if' (foo) 1 2) - это программа Lisp, которая возвращает допустимую форму IF.

CL-USER 24 > (describe (list 'if '(foo) 1 2))

(IF (FOO) 1 2) is a LIST
0      IF
1      (FOO)
2      1
3      2

Этот список может быть, например, выполнен с EVAL. EVAL ожидает формы списка, а не s-выражения. Помните, что s-выражения - это только внешнее представление. Чтобы создать форму Lisp, нам нужно ПРОЧИТАТЬ ее.

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

Таким образом, основной уровень взаимодействия верхнего уровня в Lisp называется REPL, Read Eval Print Loop. Это LOOP, который повторно считывает s-выражение, оценивает форму Lisp и печатает ее:

READ :  s-expression ->  lisp data
EVAL :  lisp form    ->  resulting lisp data
PRINT:  lisp data    ->  s-expression

Итак, самый примитивный REPL:

(loop (print (eval (read))))

Таким образом, с концептуальной точки зрения, чтобы ответить на ваш вопрос, во время оценки читатель ничего не делает. Он не участвует в оценке. Оценка выполняется функцией EVAL. Читатель вызывается вызовом READ. Поскольку EVAL использует структуры данных Lisp в качестве входных (а не s-выражений), читатель запускается до получения формы Lisp (например, путем интерпретации или путем компиляции и выполнения ее).