1.2. 어휘 구조
이 장에서는 문자 흐름이 토큰 흐름으로 바뀌는 큰 그림을 살펴봅니다. 각 토큰 종류의 세부 형식은 후속 문서에서 정의하므로, 여기서는 전체 윤곽만 잡고 가겠습니다.
token ::= identifier
| keyword
| numeric_lit
| string_lit
| byte_string_lit
| char_lit
| sentinel_lit // true | false | none
| punctuation
whitespace ::= space_char+
| line_comment
| block_comment
space_char ::= U+0009 | U+000A | U+000B | U+000C | U+000D | U+0020
line_comment ::= "//" <any char except line_terminator>* line_terminator
block_comment ::= "/*" <any char>* "*/" // recursive
shebang_line ::= "#!" <any char except line_terminator>* // first line only, see 1.2.3
line_terminator ::= U+000A | U+000D U+000A? | U+000D
1.2.1. 토큰
컴파일러의 어휘 분석기 (이하 렉서) 는 소스 파일을 읽어 토큰 흐름을 만듭니다. 이렇게 만들어진 토큰은 다음과 같은 종류로 나뉘어 있습니다.
| 종류 | 예 |
|---|---|
| 식별자 (identifier) | count, α, Δt |
| 키워드 (keyword) | let, if, fn |
| 숫자 리터럴 | 42, 3.14, 0xFF, 1_000 |
| 문자열 리터럴 | "hello", r"raw\n", f"x = {x}", b"bytes" |
| 문자 리터럴 | 'A', c'A' |
| 진리값 / 센티넬 리터럴 | true, false, none |
| 연산자 / 구두점 | +, ==, .., [, ; |
각 종류의 정확한 형식 규칙은 이어지는 문서들에서 차례로 정의하겠습니다.
1.2.2. 공백
공백 문자는 토큰 사이의 구분자로만 의미를 가지며, 그 자체로는 토큰을 만들지 않습니다.
다음에 나오는 코드포인트들을 공백으로 인정합니다.
| 코드포인트 | 이름 |
|---|---|
U+0009 | 탭 (TAB) |
U+000A | 줄바꿈 (LF) |
U+000B | 수직 탭 (VT) |
U+000C | 폼 피드 (FF) |
U+000D | 캐리지 리턴 (CR) |
U+0020 | 스페이스 |
위 표에 포함되지 않은 유니코드 공백류 (U+00A0 NO-BREAK SPACE, U+200B ZERO WIDTH SPACE 등) 는 공백으로 인정하지 않습니다. 이들은 식별자나 문자열 안에서 일반 코드포인트로 등장할 따름입니다.
또한 들여쓰기는 의미를 갖지 않습니다. 따라서 Python 식의 의미 있는 들여쓰기는 도입하지 않습니다.
1.2.3. 주석
rok 은 한 줄 주석, 블록 주석, 문서 주석의 세 종류를 지원합니다. 파일 첫 줄의 shebang 줄은 주석처럼 무시합니다.
Shebang 줄
파일의 첫 줄 (BOM 뒤) 이 #! 로 시작하면 그 줄은 shebang 줄이며 주석처럼 무시합니다. 그래서 #!/usr/bin/env rok 을 담은 스크립트는 ./script.rok 으로 실행됩니다 (9.10.1). 예외는 #! 뒤의 첫 토큰이, 공백과 문서 주석이 아닌 주석을 지나, [ 인 경우입니다: 그러면 #! 는 내부 속성 (#![allow(unused)]) 을 시작합니다. 그 뒤의 줄에 있는 #! 는 shebang 줄이 아닙니다.
한 줄 주석
// 이 줄 끝까지 주석
// 부터 그 줄의 줄 종결자까지가 주석으로 인식됩니다. 다만 줄바꿈 자체는 주석에 포함되지 않습니다.
블록 주석
/* 여러 줄
걸쳐서 */
/* 와 */ 사이가 주석이며, 중첩이 허용됩니다. 안쪽의 /*/*/ 쌍이 정확히 닫혀야 바깥쪽이 닫히는데 (Rust 와 같은 규칙), 이러한 규칙 덕분에 큰 코드 영역을 잠시 비활성화할 때 유용하게 쓸 수 있습니다.
/* 바깥
/* 안쪽 */
여전히 주석 */
문서 주석
문서 주석은 Rust 처럼 네 형태가 있습니다.
/// 외부 문서 주석 (뒤따르는 항목)
//! 내부 문서 주석 (둘러싸는 파일, 모듈, 함수)
/** 외부 블록 문서 주석 */
/*! 내부 블록 문서 주석 */
- 한 줄 문서 주석은 줄 종결자까지입니다.
/// text는#[doc = "text"](표시 뒤의 공백 하나는 버림),//! text는#![doc = "text"]입니다 (6.2.8).////로 시작하는 줄은 일반 주석입니다. - 블록 문서 주석은 짝이 맞는
*/까지이며 블록 주석처럼 중첩됩니다./**/,/***/, 그리고/***로 시작하는 주석은 일반 주석입니다. 그 텍스트는#[doc]/#![doc]속성 하나입니다. 빈 첫 줄과 빈 마지막 줄은 버립니다. 주석이 여러 줄에 걸치고 첫 줄 뒤의 모든 줄이 선택적 공백과*로 시작하면 그 접두어를 지웁니다. 그다음 비어 있지 않은 줄들에 공통인 들여쓰기와*/앞의 공백을 지웁니다. 줄 바꿈은 LF 입니다. - 내부 문서 주석은 파일, 모듈 본체, 함수 본체의 시작에만 옵니다. 그 밖의 자리에서는 에러입니다.
- 외부 문서 주석은 뒤따르는 항목을 설명합니다. 아무것도 설명하지 않는 것 (
},),]앞이나 파일 끝) 은 에러이며, 함수 매개변수에 붙은 것도 에러입니다. 문, match arm, 구조체 리터럴 필드, tuple 필드, 식에 붙은 것은 어떤 도구도 간직하지 않으며unused_doc_commentslint (6.2.9) 를 일으킵니다. - 미리 빌드한 패키지의 ROKHIR 인터페이스 텍스트 (9.4) 는 문서 주석을 보존합니다.
1.2.4. 컴파일러 출력
렉서의 출력은 토큰 흐름과 위치 정보로 구성됩니다. 이때 위치 정보는 소스 파일 의 진단 단위 규칙을 따릅니다.
이렇게 만들어진 출력이 다음 단계인 구문 분석기의 유일한 입력이 되며, 구문 분석기는 원본 소스를 다시 보지 않습니다.