S-выражения и отслеживание местоположения источника - PullRequest
3 голосов
/ 10 апреля 2019

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

Некоторые диалекты Схемы решили проблему, представив код не с обычными cons-ячейками, а с синтаксическими объектами , которые изоморфны cons-ячейкам, но также могут нести дополнительную информацию, такую ​​как местоположение источника.

Решала ли какая-либо реализация Common Lisp эту проблему? Если да, то как?

Ответы [ 2 ]

3 голосов
/ 10 апреля 2019

Стандарт Common Lisp очень мало говорит об этих вещах. Например, упоминается, что функция ed может принять имя функции , а затем открыть редактор с соответствующим исходным кодом. Но механизм не указан, и эта возможность полностью предоставляется средой разработки, возможно, в сочетании с системой Lisp.

Типичным способом решения этой проблемы является компиляция файла, и компилятор записывает исходное местоположение определенного объекта (функция, переменная, класс и т. Д.). Исходное местоположение может быть, например, помещено в список свойств символа (название определенной вещи) или записано в каком-либо другом месте. Также фактический исходный код в виде структуры списка может быть связан с символом Lisp. Смотрите функцию FUNCTION-LAMBDA-EXPRESSION.

Некоторые реализации делают более сложную запись местоположения источника. Например, LispWorks может найти определенную часть функции, которая в данный момент выполняется. Он также отмечает, когда определение приходит от редактора или слушателя. См. Dspecs: Инструменты для обработки определений . Затем отладчик может, например, определить, где находится код определенного фрейма стека в источнике.

SBCL также имеет функцию найти исходный код .

Обратите внимание также, что фактический «исходный код» в Common Lisp - это не всегда текстовый файл, а читаемое s-выражение. eval и compile - две стандартные функции - не принимают строки или имена файлов в качестве аргументов. Они используют фактические выражения:

CL-USER 26 > (compile 'foo (lambda (x) (1+ x)))
FOO
NIL
NIL

CL-USER 27 > (foo 41)
42

S-выражения в виде кода не привязаны к какому-либо конкретному текстовому форматированию. Их можно переформатировать с помощью симпатичной функции принтера pprint, и это может учитывать доступную ширину для создания макета.

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

2 голосов
/ 10 апреля 2019

Насколько я понимаю, все, что Схема данных хранит в AST, является данными, которые могут быть связаны с выражениями в среде CL.

Схема

(defun my-simple-scheme-reader (stream)
  (let ((char (read-char stream)))
    (or (position char "0123456789")
        (and (member char '(#\newline #\space #\tab)) :space)
        (case char
          (#\) :closing-paren)
          (#\( (loop
                  with beg = (file-position stream)
                  for x = (my-simple-scheme-reader stream)
                  until (eq x :closing-paren)
                  unless (eq x :space)
                    collect x into items
                  finally (return (list :beg beg
                                        :end (file-position stream)
                                        :items items))))))))

Например:

(with-input-from-string (in "(0(1 2 3) 4 5 (6 7))")
  (my-simple-scheme-reader in))

возвращается:

(:BEG 1 :END 20 :ITEMS
 (0 (:BEG 3 :END 9 :ITEMS (1 2 3)) 4 5 (:BEG 15 :END 19 :ITEMS (6 7))))

Обогащенное дерево представляет объекты синтаксиса.

Common-Lisp

(defun make-environment ()
  (make-hash-table :test #'eq))

(defun my-simple-lisp-reader (stream environment)
  (let ((char (read-char stream)))
    (or (position char "0123456789")
        (and (member char '(#\newline #\space #\tab)) :space)
        (case char
          (#\) :closing-paren)
          (#\( (loop
                  with beg = (file-position stream)
                  for x = (my-simple-lisp-reader stream environment)
                  until (eq x :closing-paren)
                  unless (eq x :space)
                    collect x into items
                  finally
                    (setf (gethash items environment)
                          (list :beg beg :end (file-position stream)))
                    (return items)))))))

Тест:

(let ((env (make-environment)))
  (with-input-from-string (in "(0(1 2 3) 4 5 (6 7))")
    (values
     (my-simple-lisp-reader in env)
     env)))

Возвращает два значения:

(0 (1 2 3) 4 5 (6 7))
#<HASH-TABLE :TEST EQL :COUNT 3 {1010524CD3}>

Учитывая минус ячейку, вы можете отследить ее исходное положение. Вы можете добавить более точную информацию, если хотите. Например, когда вы оцениваете defun, информация об источнике может быть присоединена к объекту функции или как свойство символа, что означает, что информация является мусором при переопределениях.

Примечание

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

...