구문 개체는 기본 Racket 컴파일러의 어휘 컨텍스트 저장소입니다. 구체적으로 다음과 같은 프로그램을 입력 할 때 :
#lang racket/base
(* 3 4)
컴파일러는 해당 프로그램의 전체 내용을 나타내는 구문 객체를 수신합니다. 프로그램의 *
이 thingy
내에서 구문 개체로 컴파일 시간 표현을 가지고
#lang racket/base
(define example-program
(open-input-string
"
#lang racket/base
(* 3 4)
"))
(read-accept-reader #t)
(define thingy (read-syntax 'the-test-program example-program))
(print thingy) (newline)
(syntax? thingy)
참고 : 여기에 우리가 그 구문 객체가 어떻게 생겼는지 볼 수 있도록 예입니다. 그리고 현재 thingy
에있는 *
은 그것이 어디에서 왔는지 알지 못합니다. 아직 바인딩 정보가 없습니다. 컴파일 중 확장 중에의 컴파일러가 *
을 *
#lang racket/base
에 대한 참조로 연결한다는 것입니다.
컴파일 할 때 상호 작용하면 더 쉽게 알 수 있습니다. (참고 : 나는 컴파일 타임과 런타임 중 일어나는 일에 대한 토론을 피하고 싶기 때문에 고의적으로 eval
에 대한 이야기를 피하고있다.
#lang racket/base
(require (for-syntax racket/base))
;; This macro is only meant to let us see what the compiler is dealing with
;; at compile time.
(define-syntax (at-compile-time stx)
(syntax-case stx()
[(_ expr)
(let()
(define the-expr #'expr)
(printf "I see the expression is: ~s\n" the-expr)
;; Ultimately, as a macro, we must return back a rewrite of
;; the input. Let's just return the expr:
the-expr)]))
(at-compile-time (* 3 4))
우리는 우리가 컴파일하는 동안 사물의 상태를 검사 할 수 있도록, 매크로, 여기 at-compile-time
사용합니다 :) 여기
은 우리가 이러한 구문 객체가하는 일의 이상을 검사 할 수있는 예입니다. DrRacket에서이 프로그램을 실행하면 DrRacket이 먼저 프로그램을 컴파일 한 다음 실행한다는 것을 알 수 있습니다. 프로그램을 컴파일 할 때
at-compile-time
의 사용을 볼 때 컴파일러가 매크로를 호출합니다. 의 프로그램을 조금 수정하자
I see the expression is: #<syntax:20:17 (* 3 4)>
, 우리는 식별자의 identifier-binding
검사 할 수 있는지 :
그래서 컴파일시에, 우리는 같은 것을 볼 수 있습니다
#lang racket/base
(require (for-syntax racket/base))
(define-syntax (at-compile-time stx)
(syntax-case stx()
[(_ expr)
(let()
(define the-expr #'expr)
(printf "I see the expression is: ~s\n" the-expr)
(when (identifier? the-expr)
(printf "The identifier binding is: ~s\n" (identifier-binding the-expr)))
the-expr)]))
((at-compile-time *) 3 4)
(let ([* +])
((at-compile-time *) 3 4))
을 DrRacket에서이 프로그램을 실행하면 다음 출력이 표시됩니다.
I see the expression is: #<syntax:21:18 *>
The identifier binding is: (#<module-path-index> * #<module-path-index> * 0 0 0)
I see the expression is: #<syntax:24:20 *>
The identifier binding is: lexical
12
7
(덧붙여서 : at-compile-time
의 출력을 왜 앞에서 볼 수 있습니까? 컴파일은 런타임 전에 완전히 완료 되었기 때문에! 우리가 경우 프로그램을 미리 컴파일하고 raco make를 사용하여 바이트 코드를 저장, 우리가 컴파일러는 우리가 프로그램을 실행할 때 호출되는 볼 것입니다.) 컴파일러가 at-compile-time
의 사용에 도달하는 시간으로
, 그것은에 알고 적절한 어휘 바인딩 정보를 식별자에 연결하십시오. 첫 번째 경우에 identifier-binding
을 검사하면 컴파일러는 해당 모듈이 특정 모듈 (이 경우 #lang racket/base
)과 연결되어 있다는 것을 알고 있습니다. 이는 module-path-index
사업에 관한 것입니다. 그러나 두 번째 경우에는 어휘가 바인딩임을 알 수 있습니다. 컴파일러는 이미 (let ([* +]) ...)
을 통과 했으므로 *
의 사용이 let
으로 설정된 바인딩을 다시 참조합니다.
라켓 컴파일러는 구문 개체를 사용하여 이러한 종류의 바인딩 정보를 클라이언트와 같은 매크로에 전달합니다. 구문 객체의 바인딩 정보는 시간에 우리는 구문 객체를 평가하기 때문에, 자신의 바인딩이 일을 참조 수도 관련되지 않을 수 있습니다
물건의 종류의 문제에 고민입니다 검사
eval
를 사용하려고 존재하지 않는! 그게 근본적으로 당신이 실험에서 실수를 본 이유입니다. 이 예 신중 문법 객체의 내용만을 위해 모듈 결합 된 것을 참조하도록 구성된다
#lang racket/base
(module mod1 racket/base
(provide x)
(define x #'(* 3 4)))
(module mod2 racket/base
(define * +) ;; Override!
(provide x)
(define x #'(* 3 4)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require (prefix-in m1: (submod "." mod1))
(prefix-in m2: (submod "." mod2)))
(displayln m1:x)
(displayln (syntax->datum m1:x))
(eval m1:x)
(displayln m2:x)
(displayln (syntax->datum m2:x))
(eval m2:x)
:
여전히 여기에 S-식과 구문 개체 사이의 차이를 도시 한 예이다 우리는 eval
을 사용할 당시 존재할 것입니다. 우리는 약간의 예제를 변경한다면
(module broken-mod2 racket/base
(provide x)
(define x
(let ([* +])
#'(* 3 4))))
우리가
eval
broken-mod2
나오는
x
하려고 할 때 구문 객체에 의해 존재하지 않는 어휘 바인딩을 참조하기 때문에
는 일들이 끔찍하게 휴식 우리는 eval
시간. eval
은 어려운 짐승입니다.
보셨습니까? http://docs.racket-lang.org/reference/syntax-model.html#%28part._stxobj-model%29 yet? – dyoo
@dyoo 나는 여러 번 읽어 봤는데, 내가 위의 테스트를 시도하기 전까지는 그 점을 이해했다고 생각했다. 그 페이지에서'(eval (shadow-stx))'가'6 '을 반환해야한다고 생각했을 것입니다. – Matt
구문 개체에 대해 물어볼 때 분명히 알 수있는 것은 정말 독특한 라켓 (Racket)의 것입니다. 일반적으로 스키마와 관련이 없습니다. 구문 객체는 라켓의 언어 인프라의 중심에있는 데이터 유형입니다. – dyoo