이전 문서입니다. 현재 입력 문법은 소스 텍스트, 토큰, 리더에서 확인하세요.
리스펙스 구문 참조입니다. 이 문서는 입력 S-표현식 문법과 코어 AST로의 결정론적 정규화 규칙을 정의합니다. 실행 의미는 런타임에 정의되어 있으며, 실제 프로그램은 플레이그라운드에서 바로 실행해 볼 수 있습니다.
범위: 입력 문법 + 정규화. 사용자 매크로/리더 확장은 허용되지 않습니다.
0) 파일 헤더(선택)
;! lispex 1.41) 인코딩 / 공백 / 주석
- 인코딩: UTF‑8
- 공백: space, tab, CR, LF
- 주석: 한 줄
; ..., 블록#| ... |#(중첩 가능)
2) 토큰 & 리터럴
심벌(identifier)
대소문자 구분, 유니코드 허용. 개념적 정규식: [^()\[\]{}\s"';,]+`. 예약어는 바인딩 금지(아래 참조).
불리언
#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 ...)
인용 계열(리더 축약)
'x ⇒ (quote x)
`x ⇒ (quasiquote x)
,x ⇒ (unquote x)
,@xs ⇒ (unquote-splicing xs)3) 모듈 헤더(선택)
(module <name>
(export id ...)
(import <name> ...)
body ...)<name>은 심벌 또는 점-경로(util.string)입니다. 이 표면 헤더에는 rename/only/except가 없습니다. 모듈 없는 파일도 허용됩니다.
참고: 이 헤더는 선택 사항이며 가이드에서는 사용하지 않습니다. 리스펙스는 레퍼런스 인터프리터에서 직접 실행됩니다. 가이드는 예제를 단순하게 유지하기 위해 최소한의 최상위 폼만 사용합니다. 일부 도구는 다른 타깃을 준비할 때 이 헤더를 평탄화하거나 무시할 수 있습니다.
4) 예약어
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) 코어 폼
(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) 파생 폼
함수 정의 설탕
(define (f a b) body...) ⇒ (define f (lambda (a b) body...))let*
(let* ((x e1) (y e2) ...) body)
⇒ (let ((x e1)) (let* ((y e2) ...) body))cond
(cond (t1 b1...) ... (else e...))
⇒ (if t1 (begin b1...)
(if t2 (begin b2...)
...
(begin e...)))(=> 변형은 미지원)
case (eqv?, R7RS)
(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
(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)))