보간 표현식 시퀀스
보간 표현식 시퀀스 (Interpolation Expression Sequence)
D 언어의 보간 표현식 시퀀스(Interpolation Expression Sequence, 줄여서 IES) 는 문자열을 쓰는 자리에 그대로 값을 끼워 넣을 수 있게 해 주는 문법이에요. 일반 문자열 리터럴처럼 보이지만, 안에 들어 있는 값은 문자열로 굳어지지 않고 표현식의 나열(Sequence) 로 변환되어서, 오버로딩이나 템플릿 함수가 직접 받아서 처리할 수 있어요. 이번 장에서는 IES 리터럴을 어떻게 쓰는지, 컴파일러가 이를 어떤 표현식들로 번역하는지, 그리고 라이브러리 쪽에서 이를 어떻게 받아서 처리하는지를 차근차근 살펴볼게요.
본문
보간 표현식 시퀀스(IES)는 문자열 리터럴 데이터와 값을 서로 섞어 넣는 표현식이에요. 보간 표현식은 문자열처럼 작성하지만, 그 값을 직접 받아낼 수 있는 함수로 전달되는 값을 안에 담을 수 있어요. 이 값은 오버로딩되거나 템플릿 함수로 처리될 수 있는 표현식들의 *시퀀스(Sequence)*로 변환됩니다.
IES 리터럴
InterpolationExpressionSequence:
InterpolatedDoubleQuotedLiteral
InterpolatedWysiwygLiteral
InterpolatedTokenLiteral
보간 표현식 시퀀스는 위즈이위그(wysiwyg) 따옴표, 이중 따옴표(double quote), 또는 토큰 리터럴 중 하나가 될 수 있어요. 이 중에서 이스케이프(escape)를 가질 수 있는 것은 이중 따옴표 리터럴뿐이에요.
일반 문자열 리터럴과 달리, IES 리터럴은 문자열 표현식의 문자 타입 너비를 정하는 접미사(suffix)를 가질 수 없어요.
이중 따옴표 IES 리터럴 (Double Quoted IES Literals)
InterpolatedDoubleQuotedLiteral:
i" InterpolatedDoubleQuotedCharactersopt "
InterpolatedDoubleQuotedCharacters:
InterpolatedDoubleQuotedCharacter
InterpolatedDoubleQuotedCharacter InterpolatedDoubleQuotedCharacters
InterpolatedDoubleQuotedCharacter:
DoubleQuotedCharacter
InterpolationEscapeSequence
InterpolationExpression
InterpolationEscapeSequence:
EscapeSequence
\$
InterpolationExpression:
$( AssignExpression )
DoubleQuotedString 처럼, 이중 따옴표 IES 리터럴도 이스케이프 문자를 가질 수 있어요. 여기에는 일반적인 이스케이프에 더해, 리터럴 $ 를 이스케이프할 수 있는 기능이 추가돼 있어요.
$ 뒤에 왼쪽 괄호가 아닌 다른 문자가 오면, 그것은 표현식 안에서 리터럴 $ 로 취급되므로 따로 이스케이프할 필요가 없어요.
InterpolationExpression 안에 들어가는 표현식은 완전한 D 표현식이에요. 따라서 그 부분 안에서는 이스케이프가 필요하지 않아요.
위즈이위그 IES 리터럴 (Wysiwyg IES Literals)
InterpolatedWysiwygLiteral:
i` InterpolatedWysiwygCharactersopt `
InterpolatedWysiwygCharacters:
InterpolatedWysiwygCharacter
InterpolatedWysiwygCharacter InterpolatedWysiwygCharacters
InterpolatedWysiwygCharacter:
WysiwygCharacter
InterpolationExpression
위즈이위그("what you see is what you get" — 보이는 그대로) IES 리터럴은 WysiwygString 문자열처럼 정의되지만, 백쿼트(backquote) 문법만 지원해요. 이 리터럴 안에서는 어떤 이스케이프도 인식되지 않아요.
토큰 IES 리터럴 (Token IES Literals)
InterpolatedTokenLiteral:
iq{ InterpolatedTokenStringTokensopt }
InterpolatedTokenStringTokens:
InterpolatedTokenStringToken
InterpolatedTokenStringToken InterpolatedTokenStringTokens
InterpolatedTokenStringToken:
InterpolatedTokenNoBraces
{ InterpolatedTokenStringTokensopt }
InterpolatedTokenNoBraces:
TokenNoBraces
InterpolationExpression
TokenString 처럼, IES 토큰 리터럴은 InterpolationExpression 을 제외하고는 오직 유효한 D 토큰만 담아야 해요. 이스케이프는 인식되지 않아요.
표현식 번역 (Expression Translation)
렉서(lexer)가 보간 표현식 시퀀스를 만나면, 그 토큰은 단일 토큰을 대체하는 표현식들의 시퀀스로 번역돼요. 시퀀스는 항상 core.interpolation.InterpolationHeader() 표현식으로 시작해서, 항상 core.interpolation.InterpolationFooter() 로 끝나요.
토큰에서 리터럴 문자열 데이터인 부분 str 은 core.interpolation.InterpolatedLiteral!(str) 표현식으로 번역돼요.
InterpolationExpression 인 토큰의 각 부분 $(expr) 은 core.interpolation.InterpolatedExpression!(expr), expr 시퀀스로 번역돼요.
// simple version of std.typecons.Tuple
struct Tuple(T...) { T value; }
Tuple!T tuple(T...)(T value) { return Tuple!T(value); }
import core.interpolation;
string name = "John Doe";
auto items = tuple(i"Hello, $(name), how are you?");
assert(items == tuple(
InterpolationHeader(), // denotes the start of an IES
InterpolatedLiteral!("Hello, ")(), // literal string data
InterpolatedExpression!("name")(), // expression literal data
name, // expression passed directly
InterpolatedLiteral!(", how are you?")(), // literal string data
InterpolationFooter())); // denotes the end of an IES
core.interpolation 타입 (core.interpolation Types)
core.interpolation 에 정의된 타입들은 IES를 쓰기 위해 임포트할 필요가 없어요. IES를 사용하면 이것들이 자동으로 임포트돼요. 이 타입들은 컴파일 타임에 IES를 처리하기 쉽게 들여다볼(introspect) 수 있도록 설계됐어요.
InterpolationHeader 그리고 InterpolationFooter
InterpolationHeader 와 InterpolationFooter 타입은 빈 구조체(empty struct)로, IES를 처리하는 함수를 쉽게 오버로딩할 수 있게 해 줘요. 또한 이들을 사용하면 표현식 목록 중 어떤 부분이 IES를 통해 전달됐는지 이해할 수 있어요.
이 타입들에는 빈 문자열을 반환하는 toString 정의가 있어서, std.stdio.writeln 이나 std.conv.text 처럼 IES를 텍스트로 변환하려는 함수가 처리할 수 있게 해 줘요.
InterpolatedLiteral
InterpolatedLiteral 타입은 IES의 문자열 리터럴 부분에 컴파일 타임 접근을 제공하는 빈 구조체예요. 이 타입은 또한 이 값이 대체한 시퀀스 부분을 반환하는 toString 멤버 함수를 제공해요.
InterpolatedExpression
InterpolatedExpression 타입은 뒤따르는 표현식을 형성하는 데 쓰인 리터럴에 컴파일 타임 접근을 제공하는 빈 구조체예요. 빈 문자열을 반환하는 toString 멤버 함수를 제공해요. 또한 타입을 구성하는 데 쓰인 템플릿 파라미터와 같은 값을 갖는 enum expression 멤버도 있어요.
string name = "John Doe";
auto ies = i"Hello, $(name)";
static assert(is(typeof(ies[0]) == InterpolationHeader));
static assert(ies[1].toString() == "Hello, ");
static assert(ies[2].expression == "name");
assert(ies[3] == name);
static assert(is(typeof(ies[4]) == InterpolationFooter));
IES 받아들이고 처리하기 (Accepting and Processing IES)
IES를 받아들이는 권장 방법은, 명시적인 InterpolationHeader 와 InterpolationFooter 파라미터로 감싸진 가변 인자 템플릿(variadic template) 함수를 제공해서 시퀀스 안의 각종 파라미터와 매칭시키는 거예요.
void processIES(Sequence...)(InterpolationHeader, Sequence data, InterpolationFooter)
{
// process `data` here
}
IES는 보간 표현식으로 타입도 담을 수 있어요. 이는 가변 인자 템플릿 파라미터로 전달해서 사용할 수 있어요.
template processIESAtCompileTime(InterpolationHeader header, Sequence...)
{
static assert(Sequence[$-1] == InterpolationFooter());
}
alias result = processIES!i"Here is a type: $(int)";
문자열로 변환하기 (Converting to a String)
많은 경우 IES를 string 으로 변환하고 싶을 거예요. Phobos의 std.conv.text 함수는 IES를 string 으로 변환해서, 문자열 변수에 할당하거나 문자열을 받는 함수를 호출하는 등 문자열이 필요한 어떤 상황에서도 쓸 수 있게 해 줘요.
import std.conv : text;
string name = "John Doe";
string badgreeting = i"Hello, $(name)"; // Error
string greeting = i"Hello, $(name)".text; // OK
assert(greeting == "Hello, John Doe");
IES를 받아들이고 싶은 라이브러리 작성자라면 std.conv 에 기대기보다는 IES를 받는 오버로드를 제공하는 것을 강력히 권장해요. 왜냐하면 std.conv 에 기대면 불필요한 메모리 할당이 발생하기 때문이에요. 이는 특히 악의적인 사용자 제공 데이터로부터 특정 종류의 주입 공격(injection attack)이 가능한 경우에 중요해요.
더 알아보기 (Learn more)
- D Language Reference — Lexical(spec/lex.html): 이스케이프 시퀀스,
DoubleQuotedString,WysiwygString,TokenString등 IES가 기반으로 하는 문자열 리터럴 문법. - D Language Reference — Grammar(spec/grammar.html): 전체 문법 개요.
- Phobos —
std.conv.text: IES를 문자열로 변환하는 데 쓰는 함수. - Phobos —
std.stdio.writeln: IES를 바로 출력할 수 있는 함수. core.interpolation:InterpolationHeader,InterpolationFooter,InterpolatedLiteral,InterpolatedExpression타입이 정의된 D 런타임 모듈.