You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
스위프트의 어휘 구조(lexical structure)는 언어의 유효 낱말(token)을 형성하는 일련의 문자가 뭔지를 설명한다
이러한 유효 낱말이 언어의 가장 낮은-수준 구성 블록 단위을 형성하며 이를 써서 뒤이은 장의 언어 나머지(부분)을 설명한다
낱말은 식별자(identifier)나, 키워드(keyword), 글자값(literal), 또는 연산자(operator)로 구성된다
‘낱말 (token)’ 은 프로그래밍 언어에서 ‘의미를 가지는 최소 단위’를 뜻한다
여기서는 ‘token’을 ‘낱말’ 이라고 옮겼는데, 스위프트에서는 ‘token’ 을 ‘lexical token(lexeme, 형태소와 비슷한 개념)’의 의미로 사용하고 있다
굳이 옮기자면 ‘어휘소’ 나, ‘형태소’ 라고 할 수도 있겠으나, 프로그래밍을 하는데 이 정도까지 알아야 하는 것은 아니므로,
앞으로 ‘token’ 을 계속 ‘낱말’ 이라고 옮기겠습니다. ‘token’ 에 대한 더 자세한 개념은,
위키피디아의 Lexical analysis 항목 안의 Token 페이지를 참고하면 된다
대부분의 경우 낱말의 생성은 밑에서 알게될 문법의 제약 안에서,
스위프트 소스 파일 문자 중 입력 텍스트의 가능한 가장 긴 하위 문자열을 고려하게 된다
이런 동작을 longest match(가장 긴 일치) 또는 maximal munch(최대한 잘라먹기)라고 한다
공백은 소스 파일 안의 낱말 구분 / 연산자 챕터에서 보았듯이 접두사, 접미사 및 중위 연산자 구별이라는 두 개의 용도가 있지만,
그 외 경우엔 무시한다
다음의 문자들을 공백이라고 간주한다:
공간(space; U+0020), 줄 먹임(line feed; U+000A), 캐리지 반환(carriage return; U+000D),
가로 탭(horizontal tab; U+0009), 세로 탭(vertical tab; U+000B), 양식 먹임(form feed; U+000C) 및 널 문자(null; U+0000)
주석 (comments) 은 컴파일러가 공백처럼 취급한다
한 줄 주석은 // 로 시작해서 줄 먹임(U+000A)이나 캐리지 반환(U+000D)까지 계속 된다
여러 줄 주석은 /* 로 시작해서 */ 로 끝난다
여러 줄 주석의 중첩도 허용하지만, 주석 표시는 반드시 짝이 맞아야 한다
식별자(identifiers) 는 A에서 Z까지의 대소문자, 밑줄(_),
다국어 기본 평면(Basic Multilingual Plane)에 포함되는 의 비-조합형 영숫자 유니코드 문자,
또는 다국어 기본 평면 이외의 사용자 영역(Private Use Area)에 포함되지 않는 문자로 시작한다
첫 번째 문자 뒤엔, 숫자(digits) 및 조합형 유니코드 문자도 허용한다
‘다국어 기본 평면 (Basic Multilingual Plane)’: ‘유니코드 평면 (Unicode planes)’에서 ‘0번 평면(U+0000 ~ U+FFFF)’, 모든 근대 문자와 특수 문자를 포함
‘비-조합형 영숫자 유니코드 문자 (noncombining alphanumeric Unicode character)’: 비-조합형은 é 처럼 강세 기호 등의 문자와 조합하지 않은 문자, 영숫자 는 수학 기호에 사용되는 그리스-라틴 문자 (Mathematical Alphanumeric Symbols)
‘사용자 영역 (Private Use Area)’: ‘유니코드 평면 (Unicode planes)’에서 ‘15번 평면 (F0000 ~ FFFFF)’ 과 ‘16번 평면 (100000 ~ 10FFFF)’
밑줄로 시작한 식별자는 선언을 public 접근 지정자로 한 경우라도 internal로 취급한다
이러한 규약을 통해 프레임워크 작성자는 public으로 선언하더라도
클라이언트가 상호 작용하거나 의존해서는 안 되는 API의 일부라고 개발자가 명시할 수 있다
또한, 두 개의 밑줄로 시작하는 식별자는 Swift 컴파일러와 표준 라이브러리용 예약어로 사용되어져 있다
예약어를 식별자로 사용하려면, 그 앞뒤로 역따옴표(`)를 붙이면 된다
예를 들어, class 는 유효한 식별자가 아니지만, `class` 는 유효하게 된다
역따옴표 자체는 식별자로 고려하지 않으며 `x` 와 x 의 의미는 똑같다
클로저 안에 명시적으로 매개 변수 이름이 없으면, 매개 변수에 암시적으로 $0, $1, $2, 등등의 이름을 붙인다
해당 이름들은 클로저 영역 안에선 유효한 식별자로 쓰인다
프로퍼티 래퍼의 투영된 값을 가진 프로퍼티면 컴파일러가 달러 기호($)로 시작하는 식별자를 통합한다
해당 식별자와 상호 작용하는 코드를 만들 순 있지만, 해당 접두사의 식별자를 직접 선언할 순 없다
더 많은 정보는 Attributes 챕터의 propertyWrapper 페이지 부분을 보도록 하자
키워드 및 문장 부호 (Keywords and Punctuation)
다음 키워드들은 예약되어 있어서, 위의 식별자 부분에서 설명한 것처럼
역따옴표(`)로 벗어나지 (escaped)않는 한 식별자로 사용할 수 없다
inout과 var 및 let 외의 키워드는 역따옴표로 벗어나지 않고도
함수 선언이나 함수 호출 안에서 매개 변수 이름으로 사용할 수 있다
멤버 이름이 키워드와 똑같을 땐 해당 멤버 참조가 역따옴표로 벗어날 필요는 없지만,
멤버 참조와 키워드 사용이 모호할 땐 예외가 있다
ex. self 와 Type 및 Protocol은 명시적 멤버 표현식에선 특수한 의미가 있으므로,
해당 상황에선 반드시시 역따옴표로 벗어나게 해야 한다
선언에서 사용하는 키워드: associatedtype, class, deinit, enum, extension, fileprivate, func, import, init, inout, internal, let, open, operator, private, protocol, public, rethrows, static, struct, subscript, typealias, var
구문에서 사용하는 키워드: break, case, continue, default, defer, do, else, fallthrough, for, guard, if, in, repeat, return, switch, where, while
표현식과 타입에서 사용하는 키워드: as, Any, catch, false, is, nil, super, self, Self, throw, throws, true, try
패턴(pattern) 에서 사용하는 키워드 : _
번호 기호(#) 로 시작하는 키워드: #available, #colorLiteral, #column, #else, #elseif, #endif, #error, #file, #filePath, #fileLiteral, #function, #if, #imageLiteral, #line, #selector, #sourceLocation, #warning
특별한 상황을 위해 예약한 키워드: associativity, convenience, dynamic, didSet, final, get, infix, indirect, lazy, left, mutating, none, nonmutating, optional, override, postfix, precedence, prefix, Protocol, required, right, set, Type, unowned, weak, willSet 문법 안에서 나타나는 context 밖에선, 식별자로 사용할 수 있다
다음의 낱말은 문장 부호로 예약되어 있어서 사용자 정의 연산자로 사용할 수 없다:
(, ), {, }, [, ], ., ,, :, ;, =, @, #, (접두사 연산자로써의) &, ->, `, ?, (접미사 연산자로써의)!
글자값 (Literals)
글자값 (literal) 은, 수치 값이나 문자열 같이 타입의 값을 소스 코드에 나타낸 걸 의미한다
다음은 글자값의 예시:
글자값 그 자체엔 타입이 없다. 그 대신, 글자값 구문엔 무한한 정밀도가 있는 것처럼 해석하여
스위프트의 타입 추론 장치가 글자값의 타입 추론을 시도하게 된다
ex. let x: Int8 = 42 라는 선언에선,
스위프트가 명시적 타입 보조 설명(: Int8)을 사용하여
정수 글자값 42의 타입이 Int8 이라고 추론한다
사용할 적합한 타입 정보가 없으면,
글자값 타입은 스위프트 표준 라이브러리에 정의한 기본 글자값 타입 중 하나라고 스위프트가 추론하게 된다
정수 글자값이면 기본 타입이 Int고, 부동-소수점 글자값이면 Double, 문자열 글자값이면 String, 그리고 불리언 글자값이면 Bool 이게 된다
ex. let str = "Hello, world" 라는 선언에선,
문자열 글자값 "Hello, world"의 기본 추론 타입이 String으로 추론한다
글자값에 타입 보조 설명을 지정할 때의, 보조 설명 타입은 반드시 해당 글자값으로 인스턴스화 할 수 있는 타입이어야 한다
즉, 타입은 반드시 다음의 스위프트 표준 라이브러리 프로토콜 중 하나를 준수해야 한다
ex. Int8은 ExpressibleByIntegerLiteral 프로토콜을 준수하며,
따라서 let x: Int8 = 42 라는 선언에 있는 정수 글자값 42의 타입 보조 설명으로 사용할 수 있다
1) 정수 글자값 (Integer Literals )
정수 글자값(integer literals)은 특정한 정밀도가 없는 정수 값을 나타낸다
기본적으로, 정수 글자값은 10진수로 표현하며 대안으로 접두사를 사용하여 밑수(base)를 지정할 수 있다
2진 글자값은 0b,
8진(octal) 글자값은 0o,
16진(hexadecimal) 글자값은 0x 로 시작한다
10진 글자값은 0에서 9까지 숫자를 담고,
2진 글자값은 0과 1을 담고,
8진 글자값은 0에서 7까지를 담으며,
16진 글자값은 0에서 9뿐만 아니라 A에서 F까지의 대문자 또는 소문자를 담는다
음수 글자값을 표현하려면, -42 처럼, 정수 글자값 앞에 빼기 기호 (-) 를 두면 된다
가독성을 위해 숫자 사이에 밑줄 (_) 을 둘 수 있지만,
해당 (_)는 무시하므로 글자값에는 영향을 주지 않는다
정수 글자값은 0으로 시작할 수 있는데, 0도 마찬가지로 무시하므로 글자값의 밑수나 값엔 영향을 주지 않는다
따로 지정하지 않는 한, 정수 글자값의 기본 추론 타입은 스위프트 표준 라이브러리 타입 Int다
스위프트 표준 라이브러리는, Integers에서 설명한 것처럼,
다양한 크기의 부호 있는 정수와 부호 없는 정수도 정의한다
2) 부동-소수점 글자값 (Floating-Point Literals)
부동-소수점 글자값 (floating-point literals) 은 특정한 정밀도가 없는 부동-소수점 값을 나타낸다
기본적으로, 접두사가 없는 10진수로 부동-소수점 글자값을 표현하지만,
0x 접두사가 있는 16진수나 8진수로도 표현할 수 있다
10진 부동-소수점 글자값은 일렬로 나열된 10진 숫자들과
그 뒤의 10진 분수 부분(fraction) 혹은, 10진 지수 부분(exponent), 또는 그 둘 다로 구성된다
10진 분수 부분은 소수점 (.) 및 그 뒤의 일렬로 나열된 10진 숫자들로 구성된다
지수 부분은 대문자 또는 소문자 e 접두사 및 그 뒤의 일렬로 나열된 10진 숫자들로 구성되며
e 앞의 값을 10의 몇 제곱해야 하는지 지시한다
ex. 1.25e2 는 1.25 x 10^2 를 나타내며, 125.0 라고 평가한다
ex. 1.25e-2 는 1.25 x 10^(-2) 를 나타내며, 0.0125 라고 평가한다
16진 부동-소수점 글자값은 0x 접두사와,
그 뒤에 옵션으로 16진 분수 부분 및, 그 뒤의 16진 지수 부분으로 구성된다
16진 분수 부분은 소수점 및 그 뒤의 일렬로 나열된 16진 숫자들로 구성된다
지수 부분은 대문자 또는 소문자 p 접두사 및 그 뒤의 일렬로 나열된 10진 숫자들로 구성되며
p 앞의 값을 2의 몇 제곱해야 하는지 지시한다
ex. 0xFp2 는 15 x 2^2 를 나타내며, 60 이라고 평가한다
ex. 0xFp-2 는 15 x 2^(-2) 를 나타내며, 3.75 라고 평가한다
음수 부동-소수점 글자값을 표현하려면, -42.5 처럼, 부동-소수점 글자값 앞에 빼기 기호 (-) 를 두면 된다
정수와 마찬가지로 가독성을 위해 숫자 사이에 밑줄 (_) 을 둘 수 있지만, 무시하므로 글자값에는 영향을 주지 않는다
부동-소수점 글자값은 0 으로 시작할 수 있는데, 역시 무시하므로 글자값의 밑수나 값엔 영향을 주지 않는다
따로 지정하지 않는 한, 부동-소수점 글자값의 기본 추론 타입은
스위프트 표준 라이브리러는 Double 타입으로 정의하는데, 이는 64-비트 부동-소수점 수를 나타낸다
스위프트 표준 라이브러리는 Float 타입도 정의할 수 있는데, 이는 32-비트 부동-소수점 수를 나타낸다
3) 문자열 글자값 (String Literals)
문자열 글자값은 일렬로 나열된 문자들을 따옴표로 둘러싸서 표현한다
한 줄짜리 문자열 글자값은 큰 따옴표로 둘러싸며 형식은 다음과 같다
"characters"
문자열 글자값은 문자의 본래 의미로 사용하는 큰 따옴표(")나, 벗어나지 않는 역 빗금(backslash; \),
캐리지 반환(carriage return; \r), 또는 줄 먹임(line feed; \n)을 담을 수 없다
문자열 글자값이 따옴표를 직접 담을 수 없으며, 따옴표를 문자열 글자값에 사용하려면 \를 붙여야 한다
여러 줄짜리 문자열 글자값은 세 개의 큰 따옴표로 둘러싸며 형식은 다음과 같다
"""
characters
"""
한 줄짜리 문자열 글자값과 달리, 여러 줄짜리 문자열 글자값은
문자의 본래 의미로 사용하는 큰 따옴표("), 캐리지 반환(\r), 및 줄 바꿈(\n) 을 담을 수 있다
세 개의 벗어나지 않는 큰 따옴표를 서로 나란히 담을 순 없다 - """""" 처럼 큰 따옴표 여섯 개를 나란히 사용할 순 없다
여러 줄짜리 문자열 글자값을 시작하는 """ 뒤의 줄 바꿈(line break; \n)은 문자열의 일부가 아니다
글자값을 끝내는 """ 앞의 줄 바꿈도 문자열의 일부가 아니다
여러 줄짜리 문자열 글자값이 줄 바꿈으로 시작하거나 끝나려면, 자신의 첫 번째 또는 마지막 줄에 빈 줄을 작성한다
여러 줄짜리 문자열 글자값은
공백(spaces)과 탭(tabs)을 어떻게 조합하든 들여쓰기 할 수 있으며
들여쓰기는 문자열에 포함되지 않는다
글자값을 끝내는 """ 가 들여쓰기를 결정하는데
글자값 안의 모든 비어 있지 않은 줄은
반드시 닫는 """ 앞에 나타낸 것과 정확하게 똑같은 들여쓰기로 시작해야 하며
탭과 공백이 서로 변환되진 않는다
해당 들여쓰기 뒤에 추가로 공백과 탭을 포함할 수 있으며
이러한 공백과 탭은 문자열에 나타난다
여러 줄짜리 문자열 글자값 안에 있는 줄 바꿈은 줄 바꿈 문자를 사용하도록 정규화된다
소스 파일에 캐리지 반환과 줄 바꿈이 섞여 있는 경우라도, 문자열 안의 모든 줄 바꿈은 모두 동일하다
여러 줄짜리 문자열 글자값에서, 줄 끝에 역 빗금(\)을 쓰면
문자열에서 그 줄을 바꾸는 건 생략한다
역 빗금과 줄 바꿈 사이의 어떤 공백이든 역시 생략한다
이 구문을 사용하면 결과 문자열 값을 바꾸지 않고도,
소스 코드 안의 여러 줄짜리 문자열 글자값을 ‘직접 줄 바꿈(hard wrap)’ 할 수 있다
‘hard wrap’ 과 ‘sofe wrap’ 은 자동 줄 바꿈과 관련된 개념으로,
실제 문자열 글자값이 아니라, 편집기에서 문자열이 보여지는 것과 관련된 용어다
직접 줄 바꿈 (hard wrap) 할 수 있다는 건,
Xcode 에서 문자열 글자값은 그대로 유지하되, 편집기에선 코드를 줄 바꿈하여 알아보기 쉽게 한다는 의미이다
다음의 벗어난 확장열(escape sequences)을 사용하면 한 줄 및 여러 줄 형식의 문자열 글자값 둘 다 특수 문자를 포함할 수 있다
널 문자 (null character, \0)
역 빗금 (backslash, \)
가로 탭 (horizontal tab, \t)
줄 먹임 (line feed, \n)
캐리지 반환 (carrige return, \r)
큰 따옴표 (double quotation mark, \")
작은 따옴표 (single quotation mark, \')
유니코드 크기 값 (unicode scalar, \u{n}), 여기서 n은 한 개에서 여덟 개까지의 숫자로 된 16진수
표현식 값을 문자열 글자값에 집어 넣으려면 괄호 안에 표현식을 두고 앞에 역 빗금(\) 을 붙이면 된다
끼워 넣은 표현식은 문자열 글자값을 담을 수 있지만,
벗어나지 않는 역 빗금이나, 캐리지 반환, 또는 줄 바꿈을 담을 순 없다
확장 구분자로 생성한 여러 줄짜리 문자열 글자값의 들여쓰기는 표준적인 여러 줄짜리 문자열 글자값과 똑같다
문자열 글자값의 기본 추론 타입은 String이며 이에 대한 더 많은 정보는 Strings and Characters과 String 페이지에서 확인할 수 있다
+ 연산자로 이어붙인 문자열 글자값은 컴파일할 때 이어붙여진다
예를 들어, 아래 예제의 textA 와 textB 값은 완전히 똑같으며, 런타임 시기에 이어붙여지지 않는다
lettextA="Hello "+"world"lettextB="Hello world"
4) 정규 표현식 글자값 (Regular Expression Literals)
정규 표현식 글자값은 일렬로 나열된 문자들을 빗금(/)으로 둘러싼 것으로 형식은 다음과 같다
/regular expression/
정규 표현식 글자값은 반드시 벗어나지 않는 탭이나 공백으로 시작해선 안되며,
문자의 본래 의미로 사용하는 빗금(/)이나, 캐리지 반환, 또는 줄 바꿈 문자를 담을 수도 없다
정규 표현식 안에선 역 빗금이 해당 정규 표현식의 일부분이라고 이해하며
문자열 글자값 안에서와 같이 벗어난 문자라고 해석되지 않는다
이는 다음의 특수 문자를 글자 그대로 해석하거나,
다음의 특수하지 않은 문자를 특수한 방식으로 해석하라고 지시한다
ex. /(/ 는 단일 왼쪽 괄호와 일치하고 /\d/ 는 단일 숫자와 일치한다
확장 구분자로 구분한 정규 표현식 글자값은 빗금(/)으로 둘러싼 일렬로 나열된 문자들과 하나 이상의 번호 기호(#)로 된 균형 집합이다
확장 구분자로 구분한 정규 표현식 글자값의 형식은 다음과 같다
#/regular expression/#
#/
regular expression
/#
확장 구분자를 사용한 정규 표현식 글자 값은
벗어나지 않는 공백이나 탭으로 시작할 수도, 벗어나지 않는 빗금 (/) 을 담거나, 여러 줄에 걸쳐 있을 수도 있다
여러 줄짜리 정규 표현식 글자 값에선,
여는 구분자는 반드시 줄 끝에 있어야 하고, 닫는 구분자는 자신만의 줄 위에 있어야 한다
#/ 과 /# 이 각각 그 자체로 한 줄을 차지해야 한다
여러 줄짜리 정규 표현식 글자 값 안에선,
확장 정규 표현식 구문이 기본 사용 가능하다
특히, 공백은 무시하고 주석을 허용한다
확장 구분자로 구분한 정규 표현식 형식에 하나 이상의 번호 기호를 사용하려면, 번호 기호 사이에 공백을 두지 않는다
letregex1= ##/abc/## // OK
letregex2= # #/abc/# # // Error
빈 정규 표현식 글자 값을 만들 필요가 있으면, 반드시 확장 구분자 구문을 사용해야 한다
연산자
스위프트 표준 라이브러리에서 정의한 다수 연산자들의 사용법 중 많은 것들은 Basic Operators 페이지와 Advanced Operators 페이지에서 논의한다
현재 파트에선 사용자 연산자를 정의할 수 있는 문자가 어떤 것인지를 설명한다
사용자 정의 연산자는 / 나, =, -, +, !, *, %, <, >, &, |, ^, ? 또는 ~ 라는 아스키 (ASCII) 문자 중 하나,
혹은 아래 문법에서 정의한 유니코드 문자 중 하나로 시작할 수 있다
이는, 유니 코드 중에서 수학 연산자(Mathematical Operators), 잡다한 기호(Miscellaneous Symbols), 및 딩뱃(Dingbats) 유니코드 블럭 안의 문자를 포함한다
‘딩뱃(Dingbats)’은 조판 시에 사용하는 장식 문자나 공백을 말한다
첫 번째 문자 뒤엔, 조합형 유니코드 문자도 허용된다
점(.)으로 시작하는 사용자 연산자도 정의할 수 있다
이러한 연산자는 점을 추가로 담을 수도 있다
예를 들어 .+. 은 단일 연산자로 취급된다
연산자가 점으로 시작하지 않으면, 다른 어디서도 점을 담을 수 없다
예를 들어, +.+ 는 + 연산자 뒤에 .+ 연산자가 있는 걸로 취급된다
물음표(?)가 있는 사용자 연산자를 정의할 순 있지만, 단일 물음표 문자만으로 구성할 순 없다
추가적으로, 연산자에 느낌표(!)가 있을 순 있지만, 접미사 연산자를 물음표나 느낌표로 시작할 순 없다
스위프트에서 이미 ‘옵셔널 (optional)’ 를 의미하는 예약어로 잡혀있다
낱말 =, ->, //, /*, */, . 와
접두사 연산자 <, &, ?
중위 연산자 ?
접미사 연산자 >, !, ? 는
예약어로 이미 정해져 있다
해당 낱말들은 중복 정의할 수도 없고, 커스텀 연산자로 사용할 수도 없다
연산자 주위의 공백을 사용하여 연산자가 접두사 연산자나, 접미사 연산자, 또는 이항 연산자인지를 결정한다
해당 동작의 규칙은 다음과 같다
연산자 양쪽에 공백이 있거나 어느 쪽에도 없으면, 중위 연산자로 취급한다
ex. a+++b 와 a +++ b 안의 +++ 연산자는 중위 연산자로 취급한다
연산자 왼쪽에만 공백이 있으면, 단항 접두사 연산자로 취급한다
ex. a +++b 안의 +++ 연산자는 단항 접두사 연산자로 취급한다
연산자 오른쪽에만 공백이 있으면, 단항 접미사 연산자로 취급한다
ex. a+++ b 안의 +++ 연산자는 단항 접미사 연산자로 취급한다
연산자 왼쪽에 공백이 없지만 바로 뒤에 점(.)이 있으면, 단항 접미사 연산자로 취급한다
ex. a+++.b 안의 +++ 연산자는 단항 접미사 연산자 (a +++ .b 라기 보단 a+++ .b 라고) 취급한다
이러한 규칙용으로는 연산자 앞의 (, [, { 문자와, 연산자 뒤의 ), ], } 문자, 및 ,, ;, : 문자들도 공백으로 고려한다
위 규칙엔 한 가지 주의할 점이 있다
이미 정의된 ! 및 ? 연산자 왼쪽에 공백이 없으면,
오른쪽 공백과 상관없이, 접미사 연산자로 취급한다는 것이다
? 을 옵셔널-체인 연산자로 사용하려면, 반드시 왼쪽엔 공백이 없어야 한다
삼항 조건 (? :) 연산자로 사용하려면, 반드시 양쪽에 공백이 있어야 한다
특정 구조에서, 맨 앞이 < 또는 > 인 연산자는 두 개 이상의 낱말로 쪼개질 수 있다
나머지 부분도 똑같은 식으로 취급하여 또 다시 쪼깨질 수 있다
그 결과, Dictionary<String, Array> 같은 구조의 닫는 > 문자 사이에 공백을 추가해서 헷갈리지 않게 할 필요가 없다
해당 예제에선, 닫는 > 문자를 단일 낱말로 취급하여 비트 이동 >> 연산자로 잘못 해석되지 않는다
스위프트의 어휘 구조(lexical structure)는 언어의 유효 낱말(token)을 형성하는 일련의 문자가 뭔지를 설명한다
이러한 유효 낱말이 언어의 가장 낮은-수준 구성 블록 단위을 형성하며 이를 써서 뒤이은 장의 언어 나머지(부분)을 설명한다
낱말은 식별자(identifier)나, 키워드(keyword), 글자값(literal), 또는 연산자(operator)로 구성된다
대부분의 경우 낱말의 생성은 밑에서 알게될 문법의 제약 안에서,
스위프트 소스 파일 문자 중 입력 텍스트의 가능한 가장 긴 하위 문자열을 고려하게 된다
이런 동작을 longest match(가장 긴 일치) 또는 maximal munch(최대한 잘라먹기)라고 한다
공백과 주석 (Whitespace and Comments)
공백은 소스 파일 안의 낱말 구분 / 연산자 챕터에서 보았듯이 접두사, 접미사 및 중위 연산자 구별이라는 두 개의 용도가 있지만,
그 외 경우엔 무시한다
다음의 문자들을 공백이라고 간주한다:
공간(space; U+0020), 줄 먹임(line feed; U+000A), 캐리지 반환(carriage return; U+000D),
가로 탭(horizontal tab; U+0009), 세로 탭(vertical tab; U+000B), 양식 먹임(form feed; U+000C) 및 널 문자(null; U+0000)
주석 (comments) 은 컴파일러가 공백처럼 취급한다
한 줄 주석은 // 로 시작해서 줄 먹임(U+000A)이나 캐리지 반환(U+000D)까지 계속 된다
여러 줄 주석은 /* 로 시작해서 */ 로 끝난다
여러 줄 주석의 중첩도 허용하지만, 주석 표시는 반드시 짝이 맞아야 한다
Markup Formatting Reference에서 설명한 것처럼, 주석에 추가 양식 및 마크업(markup)을 담을 수도 있다
식별자 (Identifiers)
식별자(identifiers) 는 A에서 Z까지의 대소문자, 밑줄(_),
다국어 기본 평면(Basic Multilingual Plane)에 포함되는 의 비-조합형 영숫자 유니코드 문자,
또는 다국어 기본 평면 이외의 사용자 영역(Private Use Area)에 포함되지 않는 문자로 시작한다
첫 번째 문자 뒤엔, 숫자(digits) 및 조합형 유니코드 문자도 허용한다
추가적인 정보는 위키피디아의 유니코드 평면 페이지를 참고하자
밑줄로 시작한 식별자는 선언을 public 접근 지정자로 한 경우라도 internal로 취급한다
이러한 규약을 통해 프레임워크 작성자는 public으로 선언하더라도
클라이언트가 상호 작용하거나 의존해서는 안 되는 API의 일부라고 개발자가 명시할 수 있다
또한, 두 개의 밑줄로 시작하는 식별자는 Swift 컴파일러와 표준 라이브러리용 예약어로 사용되어져 있다
예약어를 식별자로 사용하려면, 그 앞뒤로 역따옴표(`)를 붙이면 된다
예를 들어, class 는 유효한 식별자가 아니지만, `class` 는 유효하게 된다
역따옴표 자체는 식별자로 고려하지 않으며 `x` 와 x 의 의미는 똑같다
클로저 안에 명시적으로 매개 변수 이름이 없으면, 매개 변수에 암시적으로 $0, $1, $2, 등등의 이름을 붙인다
해당 이름들은 클로저 영역 안에선 유효한 식별자로 쓰인다
프로퍼티 래퍼의 투영된 값을 가진 프로퍼티면 컴파일러가 달러 기호($)로 시작하는 식별자를 통합한다
해당 식별자와 상호 작용하는 코드를 만들 순 있지만, 해당 접두사의 식별자를 직접 선언할 순 없다
더 많은 정보는 Attributes 챕터의 propertyWrapper 페이지 부분을 보도록 하자
키워드 및 문장 부호 (Keywords and Punctuation)
다음 키워드들은 예약되어 있어서, 위의 식별자 부분에서 설명한 것처럼
역따옴표(`)로 벗어나지 (escaped)않는 한 식별자로 사용할 수 없다
inout과 var 및 let 외의 키워드는 역따옴표로 벗어나지 않고도
함수 선언이나 함수 호출 안에서 매개 변수 이름으로 사용할 수 있다
멤버 이름이 키워드와 똑같을 땐 해당 멤버 참조가 역따옴표로 벗어날 필요는 없지만,
멤버 참조와 키워드 사용이 모호할 땐 예외가 있다
ex. self 와 Type 및 Protocol은 명시적 멤버 표현식에선 특수한 의미가 있으므로,
해당 상황에선 반드시시 역따옴표로 벗어나게 해야 한다
다음의 낱말은 문장 부호로 예약되어 있어서 사용자 정의 연산자로 사용할 수 없다:
(, ), {, }, [, ], ., ,, :, ;, =, @, #, (접두사 연산자로써의) &, ->, `, ?, (접미사 연산자로써의)!
글자값 (Literals)
글자값 (literal) 은, 수치 값이나 문자열 같이 타입의 값을 소스 코드에 나타낸 걸 의미한다
다음은 글자값의 예시:
글자값 그 자체엔 타입이 없다. 그 대신, 글자값 구문엔 무한한 정밀도가 있는 것처럼 해석하여
스위프트의 타입 추론 장치가 글자값의 타입 추론을 시도하게 된다
ex. let x: Int8 = 42 라는 선언에선,
스위프트가 명시적 타입 보조 설명(: Int8)을 사용하여
정수 글자값 42의 타입이 Int8 이라고 추론한다
사용할 적합한 타입 정보가 없으면,
글자값 타입은 스위프트 표준 라이브러리에 정의한 기본 글자값 타입 중 하나라고 스위프트가 추론하게 된다
정수 글자값이면 기본 타입이 Int고, 부동-소수점 글자값이면 Double, 문자열 글자값이면 String, 그리고 불리언 글자값이면 Bool 이게 된다
ex. let str = "Hello, world" 라는 선언에선,
문자열 글자값 "Hello, world"의 기본 추론 타입이 String으로 추론한다
글자값에 타입 보조 설명을 지정할 때의, 보조 설명 타입은 반드시 해당 글자값으로 인스턴스화 할 수 있는 타입이어야 한다
즉, 타입은 반드시 다음의 스위프트 표준 라이브러리 프로토콜 중 하나를 준수해야 한다
ex. Int8은 ExpressibleByIntegerLiteral 프로토콜을 준수하며,
따라서 let x: Int8 = 42 라는 선언에 있는 정수 글자값 42의 타입 보조 설명으로 사용할 수 있다
1) 정수 글자값 (Integer Literals )
정수 글자값(integer literals)은 특정한 정밀도가 없는 정수 값을 나타낸다
기본적으로, 정수 글자값은 10진수로 표현하며 대안으로 접두사를 사용하여 밑수(base)를 지정할 수 있다
2진 글자값은 0b,
8진(octal) 글자값은 0o,
16진(hexadecimal) 글자값은 0x 로 시작한다
10진 글자값은 0에서 9까지 숫자를 담고,
2진 글자값은 0과 1을 담고,
8진 글자값은 0에서 7까지를 담으며,
16진 글자값은 0에서 9뿐만 아니라 A에서 F까지의 대문자 또는 소문자를 담는다
음수 글자값을 표현하려면, -42 처럼, 정수 글자값 앞에 빼기 기호 (-) 를 두면 된다
가독성을 위해 숫자 사이에 밑줄 (_) 을 둘 수 있지만,
해당 (_)는 무시하므로 글자값에는 영향을 주지 않는다
정수 글자값은 0으로 시작할 수 있는데, 0도 마찬가지로 무시하므로 글자값의 밑수나 값엔 영향을 주지 않는다
따로 지정하지 않는 한, 정수 글자값의 기본 추론 타입은 스위프트 표준 라이브러리 타입 Int다
스위프트 표준 라이브러리는, Integers에서 설명한 것처럼,
다양한 크기의 부호 있는 정수와 부호 없는 정수도 정의한다
2) 부동-소수점 글자값 (Floating-Point Literals)
부동-소수점 글자값 (floating-point literals) 은 특정한 정밀도가 없는 부동-소수점 값을 나타낸다
기본적으로, 접두사가 없는 10진수로 부동-소수점 글자값을 표현하지만,
0x 접두사가 있는 16진수나 8진수로도 표현할 수 있다
10진 부동-소수점 글자값은 일렬로 나열된 10진 숫자들과
그 뒤의 10진 분수 부분(fraction) 혹은, 10진 지수 부분(exponent), 또는 그 둘 다로 구성된다
10진 분수 부분은 소수점 (.) 및 그 뒤의 일렬로 나열된 10진 숫자들로 구성된다
지수 부분은 대문자 또는 소문자 e 접두사 및 그 뒤의 일렬로 나열된 10진 숫자들로 구성되며
e 앞의 값을 10의 몇 제곱해야 하는지 지시한다
ex. 1.25e2 는 1.25 x 10^2 를 나타내며, 125.0 라고 평가한다
ex. 1.25e-2 는 1.25 x 10^(-2) 를 나타내며, 0.0125 라고 평가한다
16진 부동-소수점 글자값은 0x 접두사와,
그 뒤에 옵션으로 16진 분수 부분 및, 그 뒤의 16진 지수 부분으로 구성된다
16진 분수 부분은 소수점 및 그 뒤의 일렬로 나열된 16진 숫자들로 구성된다
지수 부분은 대문자 또는 소문자 p 접두사 및 그 뒤의 일렬로 나열된 10진 숫자들로 구성되며
p 앞의 값을 2의 몇 제곱해야 하는지 지시한다
ex. 0xFp2 는 15 x 2^2 를 나타내며, 60 이라고 평가한다
ex. 0xFp-2 는 15 x 2^(-2) 를 나타내며, 3.75 라고 평가한다
음수 부동-소수점 글자값을 표현하려면, -42.5 처럼, 부동-소수점 글자값 앞에 빼기 기호 (-) 를 두면 된다
정수와 마찬가지로 가독성을 위해 숫자 사이에 밑줄 (_) 을 둘 수 있지만, 무시하므로 글자값에는 영향을 주지 않는다
부동-소수점 글자값은 0 으로 시작할 수 있는데, 역시 무시하므로 글자값의 밑수나 값엔 영향을 주지 않는다
따로 지정하지 않는 한, 부동-소수점 글자값의 기본 추론 타입은
스위프트 표준 라이브리러는 Double 타입으로 정의하는데, 이는 64-비트 부동-소수점 수를 나타낸다
스위프트 표준 라이브러리는 Float 타입도 정의할 수 있는데, 이는 32-비트 부동-소수점 수를 나타낸다
3) 문자열 글자값 (String Literals)
문자열 글자값은 일렬로 나열된 문자들을 따옴표로 둘러싸서 표현한다
한 줄짜리 문자열 글자값은 큰 따옴표로 둘러싸며 형식은 다음과 같다
문자열 글자값은 문자의 본래 의미로 사용하는 큰 따옴표(")나, 벗어나지 않는 역 빗금(backslash; \),
캐리지 반환(carriage return; \r), 또는 줄 먹임(line feed; \n)을 담을 수 없다
문자열 글자값이 따옴표를 직접 담을 수 없으며, 따옴표를 문자열 글자값에 사용하려면 \를 붙여야 한다
여러 줄짜리 문자열 글자값은 세 개의 큰 따옴표로 둘러싸며 형식은 다음과 같다
한 줄짜리 문자열 글자값과 달리, 여러 줄짜리 문자열 글자값은
문자의 본래 의미로 사용하는 큰 따옴표("), 캐리지 반환(\r), 및 줄 바꿈(\n) 을 담을 수 있다
세 개의 벗어나지 않는 큰 따옴표를 서로 나란히 담을 순 없다 - """""" 처럼 큰 따옴표 여섯 개를 나란히 사용할 순 없다
여러 줄짜리 문자열 글자값을 시작하는 """ 뒤의 줄 바꿈(line break; \n)은 문자열의 일부가 아니다
글자값을 끝내는 """ 앞의 줄 바꿈도 문자열의 일부가 아니다
여러 줄짜리 문자열 글자값이 줄 바꿈으로 시작하거나 끝나려면, 자신의 첫 번째 또는 마지막 줄에 빈 줄을 작성한다
여러 줄짜리 문자열 글자값은
공백(spaces)과 탭(tabs)을 어떻게 조합하든 들여쓰기 할 수 있으며
들여쓰기는 문자열에 포함되지 않는다
글자값을 끝내는 """ 가 들여쓰기를 결정하는데
글자값 안의 모든 비어 있지 않은 줄은
반드시 닫는 """ 앞에 나타낸 것과 정확하게 똑같은 들여쓰기로 시작해야 하며
탭과 공백이 서로 변환되진 않는다
해당 들여쓰기 뒤에 추가로 공백과 탭을 포함할 수 있으며
이러한 공백과 탭은 문자열에 나타난다
여러 줄짜리 문자열 글자값 안에 있는 줄 바꿈은 줄 바꿈 문자를 사용하도록 정규화된다
소스 파일에 캐리지 반환과 줄 바꿈이 섞여 있는 경우라도, 문자열 안의 모든 줄 바꿈은 모두 동일하다
여러 줄짜리 문자열 글자값에서, 줄 끝에 역 빗금(\)을 쓰면
문자열에서 그 줄을 바꾸는 건 생략한다
역 빗금과 줄 바꿈 사이의 어떤 공백이든 역시 생략한다
이 구문을 사용하면 결과 문자열 값을 바꾸지 않고도,
소스 코드 안의 여러 줄짜리 문자열 글자값을 ‘직접 줄 바꿈(hard wrap)’ 할 수 있다
다음의 벗어난 확장열(escape sequences)을 사용하면 한 줄 및 여러 줄 형식의 문자열 글자값 둘 다 특수 문자를 포함할 수 있다
표현식 값을 문자열 글자값에 집어 넣으려면 괄호 안에 표현식을 두고 앞에 역 빗금(\) 을 붙이면 된다
끼워 넣은 표현식은 문자열 글자값을 담을 수 있지만,
벗어나지 않는 역 빗금이나, 캐리지 반환, 또는 줄 바꿈을 담을 순 없다
예를 들어, 다음의 문자열 글자값은 모두 똑같은 값을 가진다
확장 구분자로 구분한 문자열은 따옴표로 둘러싼 일렬로 나열된 문자들과 하나 이상의 번호 기호(#)로 된 균형 집합이다
확장 구분자로 구분한 문자열 형식은 다음과 같다
확장 구분자로 구분한 문자열 안의 특수 문자는
결과 문자열 안에서 특수 문자라기 보단 일반 문자처럼 표시된다
확장 구분자를 사용하면 문자열 끼워 넣기를 생성하거나, 벗어난 문자열을 시작하는, 또는 문자열 종결과 같은
일반적으로 특수 효과가 있는 문자열을 생성할 수 있다
다음 예제는 문자열 글자값과 확장 구분자로 구분한 문자열이 서로 같은 문자열 값을 생성하는 걸 보여준다
하나 이상의 번호 기호를 써서 확장 구분자로 구분한 문자열을 만든다면, 번호 기호 사이에 공백을 두면 안된다
확장 구분자로 생성한 여러 줄짜리 문자열 글자값의 들여쓰기는 표준적인 여러 줄짜리 문자열 글자값과 똑같다
문자열 글자값의 기본 추론 타입은 String이며 이에 대한 더 많은 정보는 Strings and Characters과 String 페이지에서 확인할 수 있다
+ 연산자로 이어붙인 문자열 글자값은 컴파일할 때 이어붙여진다
예를 들어, 아래 예제의 textA 와 textB 값은 완전히 똑같으며, 런타임 시기에 이어붙여지지 않는다
4) 정규 표현식 글자값 (Regular Expression Literals)
정규 표현식 글자값은 일렬로 나열된 문자들을 빗금(/)으로 둘러싼 것으로 형식은 다음과 같다
정규 표현식 글자값은 반드시 벗어나지 않는 탭이나 공백으로 시작해선 안되며,
문자의 본래 의미로 사용하는 빗금(/)이나, 캐리지 반환, 또는 줄 바꿈 문자를 담을 수도 없다
정규 표현식 안에선 역 빗금이 해당 정규 표현식의 일부분이라고 이해하며
문자열 글자값 안에서와 같이 벗어난 문자라고 해석되지 않는다
이는 다음의 특수 문자를 글자 그대로 해석하거나,
다음의 특수하지 않은 문자를 특수한 방식으로 해석하라고 지시한다
ex. /(/ 는 단일 왼쪽 괄호와 일치하고 /\d/ 는 단일 숫자와 일치한다
확장 구분자로 구분한 정규 표현식 글자값은 빗금(/)으로 둘러싼 일렬로 나열된 문자들과 하나 이상의 번호 기호(#)로 된 균형 집합이다
확장 구분자로 구분한 정규 표현식 글자값의 형식은 다음과 같다
확장 구분자를 사용한 정규 표현식 글자 값은
벗어나지 않는 공백이나 탭으로 시작할 수도, 벗어나지 않는 빗금 (/) 을 담거나, 여러 줄에 걸쳐 있을 수도 있다
여러 줄짜리 정규 표현식 글자 값에선,
여는 구분자는 반드시 줄 끝에 있어야 하고, 닫는 구분자는 자신만의 줄 위에 있어야 한다
#/ 과 /# 이 각각 그 자체로 한 줄을 차지해야 한다
여러 줄짜리 정규 표현식 글자 값 안에선,
확장 정규 표현식 구문이 기본 사용 가능하다
특히, 공백은 무시하고 주석을 허용한다
확장 구분자로 구분한 정규 표현식 형식에 하나 이상의 번호 기호를 사용하려면, 번호 기호 사이에 공백을 두지 않는다
빈 정규 표현식 글자 값을 만들 필요가 있으면, 반드시 확장 구분자 구문을 사용해야 한다
연산자
스위프트 표준 라이브러리에서 정의한 다수 연산자들의 사용법 중 많은 것들은
Basic Operators 페이지와 Advanced Operators 페이지에서 논의한다
현재 파트에선 사용자 연산자를 정의할 수 있는 문자가 어떤 것인지를 설명한다
사용자 정의 연산자는 / 나, =, -, +, !, *, %, <, >, &, |, ^, ? 또는 ~ 라는 아스키 (ASCII) 문자 중 하나,
혹은 아래 문법에서 정의한 유니코드 문자 중 하나로 시작할 수 있다
이는, 유니 코드 중에서 수학 연산자(Mathematical Operators), 잡다한 기호(Miscellaneous Symbols), 및 딩뱃(Dingbats) 유니코드 블럭 안의 문자를 포함한다
‘딩뱃(Dingbats)’은 조판 시에 사용하는 장식 문자나 공백을 말한다
첫 번째 문자 뒤엔, 조합형 유니코드 문자도 허용된다
점(.)으로 시작하는 사용자 연산자도 정의할 수 있다
이러한 연산자는 점을 추가로 담을 수도 있다
예를 들어 .+. 은 단일 연산자로 취급된다
연산자가 점으로 시작하지 않으면, 다른 어디서도 점을 담을 수 없다
예를 들어, +.+ 는 + 연산자 뒤에 .+ 연산자가 있는 걸로 취급된다
물음표(?)가 있는 사용자 연산자를 정의할 순 있지만, 단일 물음표 문자만으로 구성할 순 없다
추가적으로, 연산자에 느낌표(!)가 있을 순 있지만, 접미사 연산자를 물음표나 느낌표로 시작할 순 없다
스위프트에서 이미 ‘옵셔널 (optional)’ 를 의미하는 예약어로 잡혀있다
연산자 주위의 공백을 사용하여 연산자가 접두사 연산자나, 접미사 연산자, 또는 이항 연산자인지를 결정한다
해당 동작의 규칙은 다음과 같다
ex. a+++b 와 a +++ b 안의 +++ 연산자는 중위 연산자로 취급한다
ex. a +++b 안의 +++ 연산자는 단항 접두사 연산자로 취급한다
ex. a+++ b 안의 +++ 연산자는 단항 접미사 연산자로 취급한다
ex. a+++.b 안의 +++ 연산자는 단항 접미사 연산자 (a +++ .b 라기 보단 a+++ .b 라고) 취급한다
이러한 규칙용으로는 연산자 앞의 (, [, { 문자와, 연산자 뒤의 ), ], } 문자, 및 ,, ;, : 문자들도 공백으로 고려한다
위 규칙엔 한 가지 주의할 점이 있다
이미 정의된 ! 및 ? 연산자 왼쪽에 공백이 없으면,
오른쪽 공백과 상관없이, 접미사 연산자로 취급한다는 것이다
? 을 옵셔널-체인 연산자로 사용하려면, 반드시 왼쪽엔 공백이 없어야 한다
삼항 조건 (? :) 연산자로 사용하려면, 반드시 양쪽에 공백이 있어야 한다
특정 구조에서, 맨 앞이 < 또는 > 인 연산자는 두 개 이상의 낱말로 쪼개질 수 있다
나머지 부분도 똑같은 식으로 취급하여 또 다시 쪼깨질 수 있다
그 결과, Dictionary<String, Array> 같은 구조의 닫는 > 문자 사이에 공백을 추가해서 헷갈리지 않게 할 필요가 없다
해당 예제에선, 닫는 > 문자를 단일 낱말로 취급하여 비트 이동 >> 연산자로 잘못 해석되지 않는다
새로운, 커스텀 연산자를 정의하는 방법을 배우려면,
Custom Operators 페이지와 Operator Declaration 페이지 부분을 보도록 하자
기존 연산자를 중복 정의하는 방법을 배우려면, Operator Methods 페이지 부분을 보도록 하자
The text was updated successfully, but these errors were encountered: