리스펙스 구문

토큰, 리터럴, S-표현식, 그리고 런타임에 닿기 전 소스를 정리하는 정규화 규칙을 설명합니다.

이전 문서입니다. 현재 입력 문법은 소스 텍스트, 토큰, 리더에서 확인하세요.

리스펙스 구문 참조입니다. 이 문서는 입력 S-표현식 문법과 코어 AST로의 결정론적 정규화 규칙을 정의합니다. 실행 의미는 런타임에 정의되어 있으며, 실제 프로그램은 플레이그라운드에서 바로 실행해 볼 수 있습니다.

범위: 입력 문법 + 정규화. 사용자 매크로/리더 확장은 허용되지 않습니다.

0) 파일 헤더(선택)

LISPEX
;! lispex 1.4

1) 인코딩 / 공백 / 주석

  • 인코딩: UTF‑8
  • 공백: space, tab, CR, LF
  • 주석: 한 줄 ; ..., 블록 #| ... |#(중첩 가능)

2) 토큰 & 리터럴

심벌(identifier)

대소문자 구분, 유니코드 허용. 개념적 정규식: [^()\[\]{}\s"';,]+`. 예약어는 바인딩 금지(아래 참조).

불리언

LISPEX
#t   #f

숫자(입력형)

  • 정수(10진): 0 | -?[1-9][0-9]*
  • 유리수: p/q (분모 0 금지)
  • 실수: -?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?

문자 / 문자열

  • 문자: #\a, #\space, #\x03B1
  • 문자열: "..." 이스케이프는 \" \\ \n \t \r \xHH;입니다.

리스트 / 점리스트 / 벡터 / 바이트벡터

  • 리스트: (a b c); 점리스트: (a b . c)
  • 벡터: #(a b c)
  • 바이트벡터: #u8(0 255 ...)

인용 계열(리더 축약)

LISPEX
'x      ⇒ (quote x)
`x      ⇒ (quasiquote x)
,x      ⇒ (unquote x)
,@xs    ⇒ (unquote-splicing xs)

3) 모듈 헤더(선택)

LISPEX
(module <name>
  (export id ...)
  (import <name> ...)
  body ...)

<name>은 심벌 또는 점-경로(util.string)입니다. 이 표면 헤더에는 rename/only/except가 없습니다. 모듈 없는 파일도 허용됩니다.

참고: 이 헤더는 선택 사항이며 가이드에서는 사용하지 않습니다. 리스펙스는 레퍼런스 인터프리터에서 직접 실행됩니다. 가이드는 예제를 단순하게 유지하기 위해 최소한의 최상위 폼만 사용합니다. 일부 도구는 다른 타깃을 준비할 때 이 헤더를 평탄화하거나 무시할 수 있습니다.

4) 예약어

LISPEX
quote quasiquote unquote unquote-splicing
lambda if begin set! define let let* letrec
cond case and or when unless do
values call-with-values
call/cc dynamic-wind
module export import

다음 사용자 매크로/리더 확장 사용 시 즉시 오류: define-syntax syntax-rules syntax-case let-syntax letrec-syntax #lang #;.

5) 코어 폼

LISPEX
(lambda (formals) body+)
(if test then else)
(begin e1 ... en)
(set! id expr)
(define id expr)
(define (f params) body+)      ; 정규화 시 define+lambda로 환원
(let ((id expr) ...) body+)
(letrec ((id expr) ...) body+)
(quote datum)
(quasiquote template)          ; §10 전개 규칙 후 코어로 수렴
(values expr*)
(call-with-values producer consumer)
(call/cc proc)
(dynamic-wind before thunk after)

call/cc는 위쪽으로 탈출하는 일회용 continuation을 만듭니다. 첫 호출은 unwind를 시작하기 전에 continuation을 소비합니다. 따라서 같은 unwind 중에 실행되는 dynamic-wind의 after thunk를 포함해 이후 다시 호출하면 E340 (escape continuation is no longer active) 오류가 발생합니다. 현재 프로필은 continuation 재진입을 지원하지 않습니다.

6) 파생 폼

함수 정의 설탕

LISPEX
(define (f a b) body...) ⇒ (define f (lambda (a b) body...))

let*

LISPEX
(let* ((x e1) (y e2) ...) body)
⇒ (let ((x e1)) (let* ((y e2) ...) body))

cond

LISPEX
(cond (t1 b1...) ... (else e...))
⇒ (if t1 (begin b1...)
      (if t2 (begin b2...)
          ...
          (begin e...)))

(=> 변형은 미지원)

case (eqv?, R7RS)

LISPEX
(case key
  ((k11 k12 ...) b1...)
  ...
  (else e...))
⇒ (let ((t key))
     (if (eqv? t (quote k11)) (begin b1...)
         (if (eqv? t (quote k12)) (begin b1...)
             ...
             (begin e...))))

case는 숨은 eqv? 인트린식으로 비교하므로 사용자가 eqv?를 재바인딩해도 영향이 없다.

and / or

LISPEX
(and)              ⇒ #t
(and e1)           ⇒ e1
(and e1 e2 ... en) ⇒ (if e1 (and e2 ... en) #f)

(or)               ⇒ #f
(or e1)            ⇒ e1
(or e1 e2 ... en)  ⇒ (let ((t e1)) (if t t (or e2 ... en)))