Unreachable Неправильное выражение if является синтаксической ошибкой в ​​Scheme, но не в Common Lisp

Я пытаюсь лучше понять, как S-выражения оцениваются в разных lisps, и хотел увидеть, что они будут обрабатывать интересные неправильно сформированные выражения. Я понимаю, что Common Lisp и Scheme — совершенно разные языки, но есть ли какая-то особая разница в их семантике, объясняющая разницу в поведении. Например, Lisp-1 и Lisp-2 имеют заметные различия в своем поведении, как и гигиеничные макросистемы по сравнению с негигиеничными.

У меня есть программа, содержащая недостижимое некорректное выражение if в Scheme и Common Lisp.

;; foo.scm
(if #t 1 (if))

(display "12")

И версия Common Lisp

;; foo.lisp
(if t 1 (if))

(display "12")

chicken и guile вызывают синтаксическую ошибку.

Курица:

% chicken foo.scm

Syntax error: (foo.scm:1) in `if' - pair expected

    (if)

    Expansion history:

    <syntax>      (##core#begin (if #t 1 (if)))
    <syntax>      (if #t 1 (if))
    <syntax>      (##core#if #t 1 (if))
    <syntax>      (if)  <--

Коварство:

% guile foo.scm
...
.../foo.scm:1:9: source expression failed to match any pattern in form (if)

sbcl и clisp печатают 12 и не выдают предупреждений.

СБКЛ:

% sbcl --load foo.lisp
This is SBCL 1.3.11, an implementation of ANSI Common Lisp.
...
12
0]^D

КЛИСП

% clisp foo.lisp

"12"

person Gregory Nisbet    schedule 04.11.2016    source источник


Ответы (2)


Реализации Common Lisp: интерпретатор и компилятор

В Common Lisp тип выполнения кода зависит от того, что реализует система Lisp и как вы ее используете. Часто реализации Common Lisp имеют несколько способов выполнения кода: интерпретатор и один или несколько компиляторов. Это может быть даже в одной работающей реализации, и это позволит пользователю переключаться между ними.

  • Интерпретатор: выполнение непосредственно из структуры данных Лиспа. Это не интерпретатор кода виртуальной машины, как JVM. Нельзя ожидать, что интерпретатор проверяет полное дерево/граф кода во время выполнения. Интерпретатор обычно смотрит только на текущую верхнюю форму, которую он выполняет.

  • Компилятор: компилирует код Лиспа в C, некоторый байт-код или машинный код. Поскольку компилятор генерирует код перед его запуском, он выполняет проверку синтаксиса всего кода (возможное исключение, см. примечание внизу), которое он видит.

  • Оценка верхнего уровня: может использоваться интерпретатор, компилятор или их сочетание.

GNU CLISP имеет как интерпретатор, так и компилятор

Пример в GNU CLISP:

ЗАГРУЗКА текстового файла обычно использует интерпретатор:

[1]> (load "test.lisp")
;; Loading file test.lisp ...
;; Loaded file test.lisp
T

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

CLISP также имеет компилятор:

[2]> (compile-file "test.lisp")
;; Compiling file /tmp/test.lisp ...
** - Continuable Error
in #:|1 1 (IF T 1 ...)-1| in line 1 : Form too short, too few arguments: (IF)
If you continue (by typing 'continue'): Ignore the error and proceed
The following restarts are also available:
ABORT          :R1      Abort main loop

Как видите, компилятор CLISP обнаруживает синтаксическую ошибку и выдает четкое сообщение об ошибке.

SBCL использует компилятор, но также имеет интерпретатор

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

Если вы пишете простую форму IF на SBCL, оценщик не использует полную компиляцию и не перехватывает ошибку:

CL-USER> (if t 1 (if))
1

Если вы напишите тот же код внутри определения функции, ошибка будет обнаружена, потому что определения функций будут скомпилированы по умолчанию:

CL-USER> (defun foo () (if t 1 (if)))
; in: DEFUN FOO
;     (IF)
; 
; caught ERROR:
;   error while parsing arguments to special operator IF:
;     too few elements in
;       ()
;     to satisfy lambda list
;       (SB-C::TEST SB-C::THEN &OPTIONAL SB-C::ELSE):
;     between 2 and 3 expected, but got 0
; 
; compilation unit finished
;   caught 1 ERROR condition
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO

Если бы вы переключились на полный интерпретатор SBCL, ошибка не была бы обнаружена во время определения:

CL-USER> (setf *evaluator-mode* :interpret)
:INTERPRET
CL-USER> (defun foo () (if t 1 (if)))
WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
FOO

Оптимизация и проверка синтаксиса

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

Сводка

В большинстве реализаций Common Lisp вам нужно использовать компилятор для получения полных синтаксических предупреждений/ошибок.

person Rainer Joswig    schedule 04.11.2016

Похоже, Райнер прекрасно справляется с частью CL. Я просто хочу отметить, что R5RS не обязан действовать так, как ваши две реализации.

R5RS очень недоопределен, и для if он указывает только, что делать при правильном использовании, у нас есть этот отрывок об обработке ошибок:

Говоря об ошибке, в этом отчете используется фраза «сообщается об ошибке», чтобы указать, что реализации должны обнаруживать и сообщать об ошибке. Если такая формулировка не появляется при обсуждении ошибки, то реализации не обязаны обнаруживать или сообщать об ошибке, хотя это и рекомендуется. Ситуация с ошибкой, которую реализации не обязаны обнаруживать, обычно называется просто "ошибкой".

Таким образом, чтобы обернуть это в R5RS, строка "banana" так же правильна, как ответ Гайла и Цыпленка на оценку (if #t 1 (if)).

R6RS, с другой стороны, в целом утверждает, что когда реализация обнаруживает исключительную ситуацию, возникает исключение. Это означает, что if с менее чем двумя выражениями или более чем с 3 должно вызвать исключение, но это не говорит, что это должно произойти во время компиляции, поскольку язык может анализировать и интерпретировать по ходу дела.

person Sylwester    schedule 04.11.2016