읽기

읽기 (Reading)

입력 포트에서 데이터를 읽어들이는 핵심 함수들과, 읽기 방식을 세밀하게 조정하는 수많은 파라미터들을 살펴볼게요. Racket의 파서(리더)를 다루는 입문 지점이에요.

출처: Racket Reference

본문

procedure

(read [in]) → any
  in : input-port? = (current-input-port)

in에서 단일 datum을 읽어 돌려줘요. inport-read-handler를 통해 연결된 핸들러를 가지면 그 핸들러가 호출돼요. 그렇지 않으면 기본 리더가 사용되는데, current-readtable 파라미터와 그 밖의 많은 파라미터로 매개변수화돼요.

기본 리더에 대한 정보는 The Reader를, read의 프로토콜은 Reading via an Extension을 참고하세요.

procedure

(read-syntax [source-name in]) → (or/c syntax? eof-object?)
  source-name : any/c = (object-name (current-input-port))
  in : input-port? = (current-input-port)

read와 같지만, 소스 위치 정보가 있는 구문 객체를 만들어 내요. source-name은 구문 객체의 source 필드로 쓰여요. 임의의 값일 수 있지만, 일반적으로 소스 파일의 경로여야 해요.

read-syntax 모드의 기본 리더에 대한 정보는 The Reader를, read-syntax의 프로토콜은 Reading via an Extension을 참고하세요.

보통 in에 대해 줄 세기(line counting)를 활성화해서 구문 객체의 소스 위치가 바이트가 아닌 문자로 표시되게 해야 해요. Counting Positions, Lines, and Columns도 참고하세요.

The Racket Guide의 Syntax Objects도 참고하세요.

procedure

(read/recursive [in start readtable graph?]) → any
  in : input-port? = (current-input-port)
  start : (or/c char? #f) = #f
  readtable : (or/c readtable? #f) = (current-readtable)
  graph? : any/c = #t

read를 호출하는 것과 비슷하지만, 보통 리더 확장 프로시저(Reader-Extension Procedures 참고) 안에서 read의 동적 범위 내에 쓰여요. read 대신 read/recursive를 사용하는 주된 효과는, 중첩된 읽기의 그래프 구조 주석(Reading Graph Structure 참고)이 graph? 인자가 참일 때 전체 읽기의 일부로 간주된다는 거예요. 결과는 placeholder에 감싸져 있으므로 직접 살펴볼 수는 없지만요.

start가 제공되고 #f가 아니면, in의 스트림 시작에 효과적으로 접두사처럼 붙어요. (여러 문자를 접두사로 붙이려면 input-port-append를 사용하세요.)

readtable 인자는 read 요청을 만족시키기 위한 최상위 파싱에 사용돼요. 여기에는 내장 최상위 폼의 다양한 구분자(해시 테이블을 읽기 위한 괄호와 . 등)도 포함돼요. 읽기 안의 재귀적 파싱(예: 리스트의 요소를 읽는 것)은 대신 current-readtable 파라미터가 결정하는 현재 리드테이블을 사용해요. 리더 매크로는 문자와 리드테이블로 read/recursive를 호출해 그 문자의 리드테이블 동작을 효과적으로 호출할 수 있어요. readtable#f면 최상위 파싱에 기본 리드테이블이 사용돼요.

graph?#f면 읽은 datum 안의 그래프 구조 주석은 그 datum에 국한돼요.

read의 동적 범위 내에서 호출되면, read/recursive 프로시저는 입력 스트림의 첫 비공백 내용이 주석으로 파싱될 때 특별 주석 값(Special Comments 참고)을 만들어 낼 수 있어요.

read/recursive를 사용하는 확장 예시는 Readtables를 참고하세요.

패키지 base의 버전 6.2에서 변경됨: 내장 폼의 구분자에 리드테이블을 더 일관되게 적용하도록 readtable의 사용을 조정했어요.

procedure

(read-syntax/recursive [source-name
                        in
                        start
                        readtable
                        graph?]) → any
  source-name : any/c = (object-name in)
  in : input-port? = (current-input-port)
  start : (or/c char? #f) = #f
  readtable : (or/c readtable? #f) = (current-readtable)
  graph? : any/c = #t

read/recursive를 호출하는 것과 유사하지만, 결과 값이 소스 위치 정보와 함께 S-표현식 구조를 캡슐화해요. read/recursive와 마찬가지로, read-syntax/recursiveread-syntax의 동적 범위 내에서 사용되면 read-syntax/recursive의 결과는 특별 주석 값, 파일 끝, 또는 불투명한 그래프 구조 placeholder(구문 객체가 아님) 중 하나예요. placeholder는 리더 매크로 등이 돌려주는 S-표현식이나 구문 객체에 내장될 수 있고, 가장 바깥쪽 read-syntax가 돌려주기 전에 실제 구문 객체로 대체돼요.

read-syntax의 동적 범위 내에서 read/recursive를 사용하면 읽기용 그래프 구조가 바깥쪽 read-syntax 파싱에 포함될 수 없어요. read의 동적 범위 내에서 read-syntax/recursive를 사용하는 것도 마찬가지예요. 그런 경우 read/recursiveread-syntax/recursivereadread-syntax 같은 결과를 만들어 내는데, 입력 스트림이 (공백 뒤) 주석으로 시작할 때 특별 주석 값이 돌려진다는 점만 달라요.

read-syntax/recursive를 사용하는 확장 예시는 Readtables를 참고하세요.

패키지 base의 버전 6.2에서 변경됨: read/recursive와 같은 방식으로 readtable의 사용을 조정했어요.

procedure

(read-language [in fail-thunk])
 → (or/c (any/c any/c . -> . any) #f)
  in : input-port? = (current-input-port)
  fail-thunk : (-> any) = (lambda () (error ...))

read와 같은 방식으로 in에서 읽지만, 리더 언어(또는 그것의 부재)가 결정되는 즉시 멈추고, 루트 네임스페이스 대신(다르다면) 현재 네임스페이스를 사용해 리더 모듈을 로드해요.

리더 언어는 입력 시작 부분(주석 폼 뒤일 수도 있음)의 #lang 또는 #!로 지정돼요(Reading via an Extension 참고). read-language는 (현재 리드테이블의 값 대신) 기본 리드테이블을 사용하고, #lang 또는 #! 앞에는 #reader 폼(주석을 만들 수 있는)이 허용되지 않아요.

The Racket Guide의 Source-Handling Configuration도 참고하세요.

#lang 또는 #! 지정을 찾으면, readread-syntax가 하듯 readread-syntax 함수로 디스패치하는 대신, read-language는 같은 모듈이 내보내는 get-info 함수(있으면)로 디스패치해요. get-info의 인자는 Reading via an Extension에 설명된 대로 read의 인자와 같아요. get-info 함수의 결과는, 그것이 두 인자 함수일 때, read-language의 결과예요. get-info가 다른 종류의 결과를 만들어 내면 exn:fail:contract 예외가 발생해요. get-info 함수가 내보내지지 않으면 read-language#f를 돌려줘요.

get-info가 만들어 내는 함수는 입력 스트림의 예상 구문에 대한 정보를 반영해요. 함수의 첫 인자는 그런 정보의 키로 쓰여요. 허용되는 키와 결과의 해석은 DrRacket 같은 외부 도구(DrRacket 문서 참고)에게 달려 있어요. 주어진 키에 대한 정보가 없으면, 결과는 두 번째 인자여야 해요.

예시를 볼게요:

> (define scribble-manual-info
    (read-language (open-input-string "#lang scribble/manual")))
> (scribble-manual-info 'color-lexer #f)
#<procedure:scribble-inside-lexer>
> (scribble-manual-info 'something-else #f)
#f

get-info 함수 자체는 다섯 인자로 적용돼요: 읽고 있는 입력 포트, get-info 함수가 추출된 모듈 경로, 그리고 #lang 또는 #! 폼 시작의 소스 줄(양의 정확한 정수 또는 #f), 열(음이 아닌 정확한 정수 또는 #f), 위치(양의 정확한 정수 또는 #f)예요. get-info 함수는 결과를 결정하기 위해 주어진 입력 포트에서 더 읽을 수 있지만, 필요한 것보다 더 읽으면 안 돼요. get-info 함수는 함수를 돌려준 뒤에는 포트에서 읽으면 안 돼요.

in이 리더 언어 지정으로 시작하지만 관련 모듈이 get-info를 내보내지 않으면(그러나 readread-syntax는 내보낼 수도 있을 때), read-language의 결과는 #f예요.

in#lang 또는 #! 지정을 가지지만, 그 지정의 파싱·해석이 예외를 발생시키면, 그 예외는 read-language가 전파해요. (주석과 공백 뒤) #l 또는 #!이 최소 하나 있으면 #lang 또는 #! 지정을 시작하는 것으로 간주돼요.

in#lang이나 #!로 리더 언어를 지정하지 않으면 fail-thunk가 호출돼요. 기본 fail-thunkexn:fail:read 또는 exn:fail:read:eof를 발생시켜요.

parameter

(read-case-sensitive) → boolean?
(read-case-sensitive on?) → void?
  on? : any/c

심볼의 파싱과 프린팅을 제어하는 파라미터예요. 이 파라미터 값이 #f면 리더가 심볼을 case-fold해요(예: 입력이 hi, Hi, HI, hI 중 무엇이든 'hi를 만듦). 이 파라미터는 대문자를 포함한 심볼을 write가 프린트하는 방식에도 영향을 줘요. 파라미터 값이 #f면 심볼이 대문자를 \ 또는 |로 인용해 인쇄해요. 파라미터 값은 \ 또는 | 세로 막대 인용과 #cs, #ci 접두사에 의해 무시돼요. 자세한 내용은 Reading Symbols를 참고하세요. 모듈이 로드되는 동안 이 파라미터는 #t로 설정돼요(current-load 참고).

parameter

(read-square-bracket-as-paren) → boolean?
(read-square-bracket-as-paren on?) → void?
  on? : any/c

[]를 괄호로 취급할지 여부를 제어하는 파라미터예요. 자세한 내용은 Reading Pairs and Lists를 참고하세요.

parameter

(read-curly-brace-as-paren) → boolean?
(read-curly-brace-as-paren on?) → void?
  on? : any/c

{}를 괄호로 취급할지 여부를 제어하는 파라미터예요. 자세한 내용은 Reading Pairs and Lists를 참고하세요.

parameter

(read-square-bracket-with-tag) → boolean?
(read-square-bracket-with-tag on?) → void?
  on? : any/c

[]를 괄호로 취급하되, 결과 리스트에 #%brackets 태그를 붙일지 여부를 제어하는 파라미터예요. 자세한 내용은 Reading Pairs and Lists를 참고하세요.

패키지 base의 버전 6.3.0.5에서 추가됨.

parameter

(read-curly-brace-with-tag) → boolean?
(read-curly-brace-with-tag on?) → void?
  on? : any/c

{}를 괄호로 취급하되, 결과 리스트에 #%braces 태그를 붙일지 여부를 제어하는 파라미터예요. 자세한 내용은 Reading Pairs and Lists를 참고하세요.

패키지 base의 버전 6.3.0.5에서 추가됨.

parameter

(read-accept-box) → boolean?
(read-accept-box on?) → void?
  on? : any/c

#& 입력의 파싱을 제어하는 파라미터예요. 자세한 내용은 Reading Boxes를 참고하세요.

parameter

(read-accept-compiled) → boolean?
(read-accept-compiled on?) → void?
  on? : any/c

#~ 컴파일된 입력의 파싱을 제어하는 파라미터예요. 자세한 내용은 The Reader와 current-compile을 참고하세요.

parameter

(read-accept-bar-quote) → boolean?
(read-accept-bar-quote on?) → void?
  on? : any/c

심볼 안 |의 파싱과 프린팅을 제어하는 파라미터예요. 자세한 내용은 Reading Symbols와 The Printer를 참고하세요.

parameter

(read-accept-graph) → boolean?
(read-accept-graph on?) → void?
  on? : any/c

read 모드에서 공유가 있는 입력의 파싱을 제어하는 파라미터 값이에요. 자세한 내용은 Reading Graph Structure를 참고하세요.

parameter

(read-syntax-accept-graph) → boolean?
(read-syntax-accept-graph on?) → void?
  on? : any/c

read-syntax 모드에서 공유가 있는 입력의 파싱을 제어하는 파라미터 값이에요. 자세한 내용은 Reading Graph Structure를 참고하세요.

패키지 base의 버전 8.4.0.8에서 추가됨.

parameter

(read-decimal-as-inexact) → boolean?
(read-decimal-as-inexact on?) → void?
  on? : any/c

소수점이나 지수(명시적인 정확도 태그는 없음)가 있는 입력 숫자의 파싱을 제어하는 파라미터예요. 자세한 내용은 Reading Numbers를 참고하세요.

parameter

(read-single-flonum) → boolean?
(read-single-flonum on?) → void?
  on? : any/c

f, F, s, S 정밀도 문자가 있는 입력 숫자의 파싱을 제어하는 파라미터예요. 자세한 내용은 Reading Numbers를 참고하세요.

패키지 base의 버전 7.3.0.5에서 추가됨.

parameter

(read-accept-dot) → boolean?
(read-accept-dot on?) → void?
  on? : any/c

보통 리터럴 cons 셀에 쓰이는, 점(dot)이 있는 입력의 파싱을 제어하는 파라미터예요. 자세한 내용은 Reading Pairs and Lists를 참고하세요.

parameter

(read-accept-infix-dot) → boolean?
(read-accept-infix-dot on?) → void?
  on? : any/c

두 개의 점으로 인픽스 변환을 촉발하는 입력의 파싱을 제어하는 파라미터예요. 자세한 내용은 Reading Pairs and Lists를 참고하세요.

parameter

(read-cdot) → boolean?
(read-cdot on?) → void?
  on? : any/c

C 구조체 접근자 스타일의, 점이 있는 입력의 파싱을 제어하는 파라미터예요. 자세한 내용은 Reading with C-style Infix-Dot Notation을 참고하세요.

패키지 base의 버전 6.3.0.5에서 추가됨.

parameter

(read-accept-quasiquote) → boolean?
(read-accept-quasiquote on?) → void?
  on? : any/c

보통 quasiquote, unquote, unquote-splicing 축약에 쓰이는 ` 또는 ,가 있는 입력의 파싱을 제어하는 파라미터예요. 자세한 내용은 Reading Quotes를 참고하세요.

parameter

(read-accept-reader) → boolean?
(read-accept-reader on?) → void?
  on? : any/c

파서 선택에 #reader, #lang, #!을 허용할지 여부를 제어하는 파라미터예요. 자세한 내용은 Reading via an Extension을 참고하세요.

parameter

(read-accept-lang) → boolean?
(read-accept-lang on?) → void?
  on? : any/c

(read-accept-reader와 함께) 파서 선택에 #lang#!을 허용할지 여부를 제어하는 파라미터예요. 자세한 내용은 Reading via an Extension을 참고하세요.

parameter

(current-readtable) → (or/c readtable? #f)
(current-readtable readtable) → void?
  readtable : (or/c readtable? #f)

S-표현식 입력의 파싱을 조정하는 리드테이블을 결정하는 값의 파라미터예요. #f는 기본 동작을 뜻해요. 자세한 내용은 Readtables를 참고하세요.

procedure

(call-with-default-reading-parameterization thunk) → any
  thunk : (-> any)

위의 모든 리더 파라미터를 기본값으로 설정하는 parameterize의 꼬리 위치에서 thunk를 호출해요.

기본 파라미터 값을 쓰면 일관성을 보장하고, 신뢰할 수 없는 소스에서 읽을 때 안전성을 제공해요. 기본값은 #lang이나 #reader를 통한 임의 코드 실행을 비활성화하기 때문이에요.

parameter

(current-reader-guard) → (any/c . -> . any)
(current-reader-guard proc) → void?
  proc : (any/c . -> . any)

#reader 다음에 오는 모듈 경로 datum을 변환하거나 (예외를 발생시켜) 거부하는 값의 파라미터예요. 자세한 내용은 Reading via an Extension을 참고하세요.

parameter

(read-on-demand-source)
 → (or/c #f #t (and/c path? complete-path?))
(read-on-demand-source mode) → void?
  mode : (or/c #f #t (and/c path? complete-path?))

컴파일된 코드의 지연 파싱(lazy parsing)을 활성화해, 클로저 본문과 구문 객체를 마셜된 컴파일 코드에서 온디맨드로 추출(및 검증)하는 파라미터예요. 보통 이 파라미터는 load-on-demand-enabled#t일 때 기본 load 핸들러가 설정해요.

read-on-demand-source#f 값은 컴파일 코드의 지연 파싱을 비활성화해요. #t 값은 지연 파싱을 활성화해요. 경로 값은, 시작 시 PLT_DELAY_FROM_ZO 환경 변수가 (어떤 값으로든) 설정되어 있을 때, 파싱되지 않은 컴파일 코드를 메모리에 유지하는 대신 디스크에서 지연 검색을 더 활성화해요.

지연 검색이 활성화되어 있을 때, 지연된 코드가 파싱되기 전에 mode의 경로인 파일이 바뀌면, 온디맨드 파싱이 아마도 쓰레기(garbage)를 만나 예외로 이어질 가능성이 커요.

procedure

(port-read-handler in) →
 (case->
  (input-port? . -> . any)
  (input-port?  any/c . -> . any))
  in : input-port?
(port-read-handler in proc) → void?
  in : input-port?
  proc :
 (case->
  (input-port? . -> . any)
  (input-port? any/c . -> . any))

in의 포트 read 핸들러를 가져오거나 설정해요. 내장 readread-syntax 프로시저가 포트에 적용될 때 포트에서 읽기 위해 호출되는 핸들러예요. (read/recursiveread-syntax/recursive에는 포트 read 핸들러가 사용되지 않아요.)

포트 read 핸들러는 한 인자 또는 두 인자로 적용돼요:

  • 포트가 read와 함께 사용되면 단일 인자가 공급돼요. 인자는 읽고 있는 포트예요. 반환 값은 포트에서 읽은 값(또는 파일 끝 end-of-file)이에요.
  • 포트가 read-syntax와 함께 사용되면 두 인자가 공급돼요. 첫 인자는 읽고 있는 포트이고, 두 번째 인자는 소스를 나타내는 값이에요. 반환 값은 포트에서 읽은 구문 객체(또는 파일 끝)예요.

기본 포트 read 핸들러는 Racket 내장 파서로 표준 Racket 표현식을 읽어요(The Reader 참고). 커스텀 입력 포트(make-input-port 참고)의 특별 결과를 단일 표현식으로 취급해 처리하는데, 특별 주석 값(Special Comments 참고)은 공백으로 취급돼요.

기본 포트 read 핸들러 자체는 리드테이블로 커스터마이즈할 수 있어요. 자세한 내용은 Readtables를 참고하세요.

더 알아보기