구문 객체 속성

구문 객체 속성 (Syntax Object Properties)

모든 구문 객체는 연관된 구문 속성(syntax property) 목록을 가지며, syntax-property로 조회하거나 확장할 수 있어요. 속성은 보존(preserved)되거나 그렇지 않게 설정돼요. 보존 속성은 바이트 문자열이나 .zo 파일로 마샬링되는 컴파일된 형태의 구문 객체에 대해 유지되며, 다른 속성은 마샬링할 때 버려져요.

출처: Racket Reference

본문

read-syntax에서 리더는 쌍 [] 또는 {}을 파싱해 만든 어떤 쌍(pair)이나 벡터 구문 객체에도 보존되는 'paren-shape 속성을 붙여요. 속성 값은 전자의 경우 #\[, 후자의 경우 #\{이에요. syntax 형식은 템플릿의 소스에서 대응하는 생성된 구문으로 'paren-shape 속성을 복사해요.

변환기로 들어가는 구문 입력과 변환기의 구문 결과는 둘 다 연관된 속성을 가질 수 있어요. 두 속성 집합은 구문 확장기가 병합해요: 원본에 있고 결과에 없는 각 속성은 결과로 복사되고, 둘 다에 있는 속성의 값은 cons로 결합돼요(결과 값이 먼저, 원본 값이 나중). 그리고 결합된 값은 두 값 중 하나라도 보존되었으면 보존돼요.

다만 병합을 수행하기 전에, 구문 확장기는 'origin 키를 사용해 원본 구문 객체에 속성을 자동으로 추가해요. 소스 구문에 'origin 속성이 없으면 빈 목록으로 설정돼요. 그런 다음, 여전히 병합 전에, 매크로 확장을 발동시킨 식별자(구문으로서)가 지금까지의 'origin 속성에 cons돼요. 따라서 'origin 속성은 확장된 표현식을 만들어낸 매크로 확장들의 순서를 (역순으로) 기록해요. 보통 'origin 값은 식별자들의 목록이지만, 변환기가 이미 확장된 구문을 반환할 수도 있는데, 그 경우 'origin 목록은 병합 후 다른 목록들을 포함할 수 있어요. syntax-track-origin 프로시저가 이 추적을 구현해요. 'origin 속성은 비보존으로 추가돼요.

일반적인 매크로 확장에 대한 'origin 추적 외에도, Racket은 확장된 구문에 속성을 추가해(종종 syntax-track-origin을 써서) 추가적인 확장 세부 사항을 기록해요.

  • 내부 정의(내부 정의 참고)를 가진 시퀀스에 begin 형식이 스플라이스될 때, begin 본문의 각 스플라이스된 요소에 syntax-track-origin이 적용돼요. syntax-track-origin의 두 번째 인자는 begin 형식이고, 세 번째 인자는 begin 키워드(스플라이스된 형식에서 추출)예요.
  • 내부 define-values 또는 define-syntaxes 형식이 letrec-syntaxes+values 형식으로 변환될 때(내부 정의 참고), 각 생성된 바인딩 절에 syntax-track-origin이 적용돼요. 두 번째 인자는 변환된 형식이고, 세 번째 인자는 define-values 또는 define-syntaxes 키워드 형식이에요.
  • letrec-syntaxes+values 표현식이 완전히 확장되면 구문 바인딩은 사라지고, 결과는 letrec-values 형식(확장되지 않은 형식이 비구문 바인딩을 포함했을 때)이거나 letrec-syntaxes+values 형식의 본문만(본문이 여러 표현식을 포함했다면 begin으로 감싸서)이 돼요. 사라진 구문 바인딩을 기록하기 위해 확장 결과에 속성이 추가돼요: 사라진 바인딩에서 온 식별자들의 불변 목록을 'disappeared-binding 속성으로.
  • 하위 타입을 만드는 struct 형식이 확장될 때, 기본 타입을 참조하는 데 쓰인 식별자는 확장에 나타나지 않아요. 그래서 struct 변환기는 그 식별자를 'disappeared-use 속성으로 확장 결과에 추가해요.
  • set! 대상 대체에 rename 변환기가 쓰일 때, 대상 식별자에 syntax-track-origin이 쓰여요(식별자가 표현식으로 쓰일 때와 동일하게).
  • 모듈에서 내보내지지 않았거나 보호된 식별자에 대한 참조가 발견되면, 'protected 속성이 #t 값과 함께 그 식별자에 추가돼요.
  • read-syntax가 구문 객체를 만들 때, 그 객체를 읽기에서 비롯된 것으로 표시하기 위해 (비공개 키로) 속성을 붙여요. syntax-original? 술어는 그 속성을 찾아 그런 구문 객체를 인식해요. (구문 객체 내용 참고. 이 속성은 매크로 변환기 입력에서 출력으로, 또는 syntax-track-origin에 의해 확장기가 전송하지 않아요.)

'disappeared-use'disappeared-binding 속성의 한 사용처인 Check Syntax도 함께 보세요.

확장된 모듈 선언의 확장으로 생성된 속성에 대한 정보는 Information on Expanded Modules 문서를, 프로시저 컴파일 시 인식되는 속성에 대한 정보는 lambda와 Inferred Value Names 문서를, 속성과 바이트 코드에 대한 정보는 current-compile 문서를 참고하세요.

(syntax-property stx key v [preserved?]) → syntax?
  stx : syntax?
  key : (if preserved? (and/c symbol? symbol-interned?) any/c)
  v : any/c
  preserved? : any/c = (eq? key 'paren-shape)

(syntax-property stx key) → any
  stx : syntax?
  key : any/c

세 인자 또는 네 인자 형태는 key 키에 임의의 속성 값 v를 연관시켜 stx를 확장해요. 결과는 그 연관을 가진 새 구문 객체이고(stx 자체는 변하지 않음), 속성은 preserved?가 참이면 보존으로 추가돼요. 그 경우 key는 인터닝된 심볼이어야 하고, v는 아래에 설명된 것처럼 마샬링된 바이트코드에 저장할 수 있는 값이어야 해요.

두 인자 형태는 key 키에 stx에 연관된 임의의 속성 값을 반환하고, key에 대해 stx에 연관된 값이 없으면 #f를 반환해요. stx가 오염되어 있으면, 결과 값과 함께 있는 구문 객체들은 오염돼요.

바이트코드로의 마샬링을 지원하려면, 보존 구문 속성의 값은 순환하지 않는 값이어야 하며, 다음 중 하나여야 해요.

  • 허용되는 보존 속성 값을 담은 쌍(pair)
  • 허용되는 보존 속성 값을 담은 벡터(불변으로 언마샬링됨)
  • 허용되는 보존 속성 값을 담은 상자(box, 불변으로 언마샬링됨)
  • 허용되는 보존 속성 값을 담은 불변 prefab 구조체
  • 키와 값이 모두 허용되는 보존 속성 값인 불변 해시 테이블
  • 구문 객체
  • 빈 목록, 심볼, 숫자, 문자, 문자열, 바이트 문자열, 또는 정규식 값

보존 속성에 대한 그 외 다른 값이면, 소유 구문 객체를 바이트코드 형태로 마샬링하려 할 때 예외가 발생해요.

base 패키지 6.4.0.14 버전에서 preserved? 인자가 추가됐어요.

(syntax-property-remove stx key) → syntax?
  stx : syntax?
  key : any/c

stx와 같지만 key에 대한 속성(있으면)이 없는 구문 객체를 반환해요.

base 패키지 6.90.0.20 버전에서 추가.

(syntax-property-preserved? stx key) → boolean?
  stx : syntax?
  key : (and/c symbol? symbol-interned?)

stxkey에 대한 보존 속성 값을 가지면 #t, 아니면 #f를 반환해요.

base 패키지 6.4.0.14 버전에서 추가.

(syntax-property-symbol-keys stx) → list?
  stx : syntax?

stx에서 키로 연관된 속성을 가진 모든 심볼의 목록을 반환해요. 인터닝되지 않은 심볼(심볼 참고)은 결과 목록에 포함되지 않아요.

(syntax-track-origin new-stx orig-stx id-stx) → any
  new-stx : syntax?
  orig-stx : syntax?
  id-stx : identifier?

매크로 확장이 변환기 결과에 속성을 추가하는 것과 같은 방식으로 new-stx에 속성을 추가해요. 특히 orig-stx의 속성을 new-stx로 병합하며, 먼저 id-stx'origin 속성으로 추가하고 syntax-original?이 인식하는 속성을 제거하고, 속성이 확장된 구문 객체를 반환해요. 어떤 구문(키워드 id-stx를 가진 orig-stx에 해당)을 버리고 그 자리에 다른 구문(new-stx에 해당)을 남기는 매크로 변환기에서 syntax-track-origin 프로시저를 사용하세요.

예를 들어 표현식

(or x y)

(let ([or-part x]) (if or-part or-part (or y)))

로 확장되고, 다시

(let-values ([(or-part) x]) (if or-part or-part y))

로 확장돼요. 최종 표현식에 대한 구문 객체는 값이 (list (quote-syntax let) (quote-syntax or))'origin 속성을 가져요.

변경 사항: base 패키지 7.0 버전에서 syntax-original? 속성을 new-stx로 전송되는 속성들에 포함시켰어요. 8.2.0.7 버전에서 syntax-original? 속성을 new-stx로 전송되는 집합에서 제거하도록 다시 고쳤어요.

더 알아보기

  • Check Syntax: 'disappeared-use'disappeared-binding 속성의 사용처
  • Information on Expanded Modules, lambda/Inferred Value Names, current-compile
  • 구문 객체 내용(Syntax Object Content)