rokdocs
v0.1

1.7. 문자열 리터럴

문자열 리터럴은 문자의 나열을 표현하는 토큰입니다. 본 문서에서는 어휘 형식만을 다루며, 그 결과가 어떤 라이브러리 타입으로 평가되는지는 표준 라이브러리의 리터럴 핸들러가 결정합니다. 바이트 문자열 (1.7.5) 은 예외로, 그 타입은 언어가 정합니다.

1.7.1. 일반 문자열

string_lit  ::= '"' string_item* '"'
string_item ::= string_char | escape_seq
string_char ::= <any unicode codepoint except '"' or '\'>
escape_seq  ::= "\\" ("\\" | "\"" | "'" | "n" | "r" | "t" | "0")
              | "\\x" hex_digit hex_digit
              | "\\u" "{" hex_digit ("_"? hex_digit){0,5} "}"
              | "\\" line_terminator whitespace*

일반 문자열은 쌍따옴표 (") 로 둘러쌉니다.

"hello"
"hello, 세계"
""
"여러
줄에
걸친 문자열"

내부에는 임의의 유니코드 코드포인트를 포함할 수 있고, 줄바꿈 또한 그대로 포함할 수 있습니다. 다만 끝까지 닫히지 않으면 컴파일 에러가 발생하며, 닫는 " 는 반드시 같은 파일 안에 있어야 하므로 파일 끝까지 닫히지 않은 문자열은 에러로 처리됩니다.

이스케이프 시퀀스

다음과 같은 이스케이프 시퀀스를 인식합니다.

시퀀스의미
\\백슬래시 (U+005C)
\"쌍따옴표 (U+0022)
\'작은따옴표 (U+0027)
\n줄바꿈 (U+000A)
\r캐리지 리턴 (U+000D)
\t탭 (U+0009)
\0NULL (U+0000)
\xHH1바이트 (16진 두 자리, U+0000-U+007F 만 허용 - ASCII 범위)
\u{H...}임의 유니코드 코드포인트 (16진 1~6 자리, 최대 U+10FFFF)

\u{...} 안의 16진 자리는 _ 로 구분할 수 있습니다 (예: \u{1_F600}). 위에 나열된 것 외의 백슬래시 시퀀스는 모두 어휘 에러로 처리되며, 알려지지 않은 이스케이프를 암묵적으로 원본 문자 그대로 두는 일은 하지 않습니다.

줄바꿈 잇기

문자열 안의 백슬래시가 줄 종결자 바로 앞에 오면, 그 백슬래시와 줄 종결자, 그리고 다음 줄의 선행 공백이 모두 생략됩니다.

"한 줄로 \
 보이는 긴 문자열"
// 결과 내용: "한 줄로 보이는 긴 문자열"

이 규칙은 긴 문자열을 소스 코드 상에서는 여러 줄에 나누어 쓰면서도 결과 값에는 줄바꿈을 넣지 않기 위한 장치입니다.

1.7.2. 원시 문자열

원시 문자열은 r 접두에 쌍따옴표를 붙여 표현합니다.

r"raw\n"
r"C:\path\to\file"

원시 문자열 안에서는 어떤 이스케이프도 처리되지 않으므로, \n 은 두 글자 그대로 보존됩니다. 만약 원시 문자열의 내용에 닫는 " 가 필요한 경우에는, 양쪽에 같은 수의 # 을 둘러서 구분자를 변환할 수 있습니다.

r#"문자열 안에 "쌍따옴표"가 있어도 됨"#
r##"r#"이러한 형태"#도 가능"##

여는 r##" 은 닫는 "## 와 정확히 같은 수의 # 으로 끝나야 하며, 실제로 세는 글자는 여는 쪽의 # 개수입니다. 또한 원시 문자열은 줄바꿈 잇기 (\ + 줄 종결자) 를 지원하지 않습니다. 모든 글자를 그대로 보존하는 것이 원시 문자열의 정의이기 때문입니다.

1.7.3. 형식 문자열

형식 문자열은 f"..." 와 f"""...""" 두 형태입니다 (Python 3.12 / PEP 701 과 같은 규칙).

f"x = {x}"
f"sum = {a + b}, diff = {a - b}"
f"""
  name: "{name}"
  total: {items
      .len()}
"""
  • 텍스트의 {{ 와 }} 는 중괄호 하나입니다.
  • f"""...""" 의 텍스트에는 " 를 그대로 쓸 수 있고, 구멍의 코드가 여러 줄에 걸칠 수 있습니다. f"..." 의 구멍은 한 줄입니다. f 없는 """ 문자열은 없습니다.

구멍

구멍 { .. } 안은 일반 렉서가 읽는 일반 토큰입니다.

  • 바깥 따옴표를 다시 쓸 수 있습니다 (f"{s.split(" ")}", f"{d["k"]}"). f-string 은 몇 겹이든 중첩됩니다.
  • 구멍 안의 문자열 리터럴은 자기 escape 를 그대로 가집니다 (f"{"a\tb"}").
  • 문자열 리터럴 밖의 \ 는 에러입니다. 진단은 " 를 그대로 쓰라고 안내합니다.
  • 식은 괄호 깊이 0 의 } 나 : 에서 끝납니다. :: 는 경로 구분자입니다.
f"name = {user.name ?? "anon"}"     // OK
f"name = {user.name ?? \"anon\"}"   // ERROR - 구멍 안의 \
f"{\"ab\".replace("a", "b")}"  // ERROR - f"{"ab".replace("a", "b")}" 로 씀

형식 지정자

표현식 뒤에 콜론 (:) 을 두고 형식 지정자를 적을 수 있습니다.

f"{x:.2f}"        // 소수점 둘째 자리까지
f"{n:0>5d}"       // 5자리 0 채움 우측 정렬
f"{val:?}"        // 디버그 형식

형식 지정자의 문법은 다음과 같습니다.

spec  := [fill][align][sign]['#'][0][width]['.'precision][type]
align := '<' | '>' | '^' | '='
sign  := '+' | '-' | ' '
type  := 'd' | 'g' | 's' | 'f' | 'b' | 'o' | 'x' | 'X' | 'e' | 'E' | '?'

width 가 있고 align 이 없으면, 수 (정수와 부동소수) 는 오른쪽 정렬, 그 밖의 타입은 왼쪽 정렬입니다. < > ^ 를 적으면 (fill 문자와 함께) 어떤 타입이든 그 정렬을 따릅니다. f-string 은 구멍의 타입을 아는 단계에서 형식화 코드로 바뀌므로, 정렬 기본값과 spec 검사가 타입에 따라 정해집니다. 구멍들은 Formatter 하나의 buffer 에 차례로 쓰며, 구멍마다 중간 문자열을 만들지 않습니다 (stdlib 2.10).

부분의미
fill채울 문자 (기본 공백). 바로 뒤에 align 이 있어야 함
align< 왼쪽, > 오른쪽, ^ 가운데, = 부호와 숫자 사이를 채움 (수만)
sign+ 양수에도 +, 공백은 양수에 공백, - 는 기본 (음수만 -)
#b / o / x / X 에 0b / 0o / 0x 접두
0수를 0 으로 채움 (= 정렬, fill 0 과 같음)
.precision부동소수는 소수점 아래 자릿수 (변환 없이), 문자열 등은 최대 길이로 자름, 정수에는 무시
type대상의미
(없음), d, g, s모든 타입Display
f수고정 소수점 (precision 자릿수)
b o x X정수2 / 8 / 16 진수. 그 정수 타입의 폭으로 ({-1i8:x} 는 ff)
e E정수, 부동소수지수 표기
?모든 타입Debug. width / align 과 함께 쓸 수 있음

정적 spec 은 컴파일 타임에 구멍의 타입과 맞춰 검사하며, 맞지 않으면 그 구멍을 가리키는 에러입니다 ({s:x} 에서 s: str 등). 각 type 글자가 부르는 트레잇은 표준 라이브러리 형식화 사양 (stdlib 2.10) 이 정의합니다.

동적 형식 지정자

형식 지정자 안에 식을 끼울 수 있습니다. {expr} (Python 식) 과 ${expr} 둘 다 됩니다.

f"{x:.{prec}f}"           // 동적 정밀도
f"{x:>${width}.${prec}f}" // 동적 너비 + 정밀도
f"{val:{full_spec}}"      // 전체 지정자 동적
  • 끼운 식은 평가 뒤 문자열로 바뀌어 지정자 텍스트의 일부가 됩니다. 펼친 결과는 spec 문법과 구멍의 타입을 만족해야 하며, 아니면 런타임 panic 입니다.
  • 중첩 구멍은 자기 지정자를 갖지 않습니다.
  • 지정자 안의 $$ 는 $ 하나입니다. ${ 가 아닌 단독 $ 는 그대로입니다.

어휘 처리

렉서는 형식 문자열을 다음 토큰으로 나눕니다.

FStrStart                     f" 또는 f"""
  FStrText    "x = "          ({{ / }} 는 중괄호로 접힘)
  HoleStart
    (일반 토큰 - 식)
    HoleSpec                  (선택) :
      SpecText / 중첩 구멍
  HoleEnd
  FStrText    ", y = "
FStrEnd

구멍 안의 식은 일반 식 파서가 처리합니다.

1.7.4. 인접 문자열의 자동 결합

C 와 Python 식의 인접 문자열 자동 결합 ("abc" "def" → "abcdef") 은 도입하지 않습니다. 연결이 필요한 경우에는 명시적인 연산자나 메서드를 사용해야 합니다.

1.7.5. 바이트 문자열

byte_string_lit  ::= 'b"' byte_string_item* '"'
byte_string_item ::= <any ASCII byte except '"' or '\'> | byte_escape

바이트 문자열은 b 접두어 뒤에 큰따옴표가 오는 형태입니다. 그 바이트들을 담은 고정 배열 [u8; N] 으로 평가되며, N 은 바이트의 개수입니다.

let magic = b"\x7FELF";          // [u8; 4]
let req: Slice<u8> = b"GET /\r\n";

직접 적는 문자는 ASCII 여야 하며, 그 밖의 바이트는 escape 로 적습니다. escape 는 c'A' (1.8.1) 처럼 1.7.1 의 표를 따릅니다: \xHH 는 \x00-\xFF 의 어느 값이든 되며, 모든 escape 의 값은 U+0000-U+00FF 안에 있어야 합니다. 직접 적은 비 ASCII 문자나 U+00FF 를 넘는 escape 는 어휘 에러입니다. 줄 이음 (\ + 줄 끝) 은 지원하지 않습니다.

기대 타입이 Slice<u8> 이거나, Slice<u8> 멤버가 있고 배열 멤버가 없는 합 타입이면, 리터럴은 대신 그 배열의 Slice<u8> view 가 됩니다. b"..."[:] 라고 적은 것과 같습니다.

GitHub에서 이 페이지 고치기