목표
-
표준에서 반복되는 보일러플레이트를 제거합니다.
-
표준 간의 관례, 용어, 데이터 구조를 일치시킵니다.
-
여러 표준에서 사용되지만 적절한 위치가 없는 개념들의 집합소가 됩니다.
-
모호할 수 있는 개념을 명확히 하여, 명확하고 읽기 쉬운 알고리즘적 산문 작성에 도움을 줍니다.
더 많은 목표에 대한 제안도 환영합니다.
1. 사용법
X라는 제목의 문서에서 이 표준을 사용하려면 다음과 같이 합니다:
X는 인프라에 의존합니다. [Infra]
더불어, 모든 용어를 교차 참조하여 모호성을 피하는 것이 강력히 권장됩니다.
2. 관례
2.1. 적합성
모든 단언문, 다이어그램, 예시, 주석은 비규범적이며, 명시적으로 비규범적임이 표시된 모든 섹션 역시 비규범적입니다. 그 외의 모든 내용은 규범적입니다.
"MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", "OPTIONAL" 키워드는 RFC 2119에서 설명한 대로 해석해야 합니다. [RFC2119]
이러한 키워드는 소문자로 작성되어도 동일한 의미를 가지며, 비규범적 내용에서는 등장할 수 없습니다.
이는 RFC 8174를 의도적으로 위반하는 것으로, 가독성과 IETF 이외에서 출판된 RFC 8174 이전 문서들에서의 오랜 관행을 유지하기 위한 것입니다. [RFC8174]
위의 모든 내용은 이 표준 및 이 표준을 사용하는 모든 문서에 적용됩니다. 이 표준을 사용하는 문서는 "must", "must not", "should", "may"만을, 그리고 이를 소문자로 사용하는 것이 더 읽기 쉽기 때문에 권장합니다.
비규범적 내용에는 "strongly encouraged", "strongly discouraged", "encouraged", "discouraged", "can", "cannot", "could", "could not", "might", "might not" 등을 사용할 수 있습니다.
2.2. 다른 명세와의 준수
일반적으로, 명세는 다양한 다른 명세들과 상호작용하고 이에 의존합니다. 때로는 불행히도 상충되는 요구로 인해 어떤 명세가 다른 명세의 요구사항을 위반해야 하는 경우가 있습니다. 이런 경우, Infra 표준을 사용하는 문서에서는 그러한 위반을 의도적 위반으로 표기하고, 그 이유를 명시해야 합니다.
이전 섹션 § 2.1 적합성은 의도적 위반의 예시로, 인프라가 RFC 8174를 위반했음을 문서화합니다.
2.3. 용어
둘 다 포함하는 "or"(예: "width 또는 height가 0인 경우")와 둘 중 하나만을 의미하는 "or"가 모두 가능한 상황에서는, 명시적으로 "but not both"로 독점적임을 나타내지 않는 한, "or"는 포괄적 "또는"(즉, "둘 다 가능")로 해석합니다.
user agent는 사용자를 대신하여 동작하는 모든 소프트웨어 엔터티를 의미합니다. 예를 들어 웹 콘텐츠를 검색·렌더링하고 사용자 상호작용을 지원합니다. Infra 표준을 사용하는 명세에서, user agent는 일반적으로 해당 명세를 구현하는 클라이언트 소프트웨어의 인스턴스입니다. 클라이언트 소프트웨어 자체는 구현체로 불립니다. 한 사람은 여러 user agent를 사용할 수 있으며, 하나의 구현체를 여러 user agent로 동작하게 할 수도 있습니다. 예를 들어 여러 프로필 혹은 구현체의 프라이빗 브라우징 모드를 사용하는 경우입니다.
구현체 정의로 언급된 경우, 그 세부사항은 구현체에 달려 있습니다. 별도의 언급이 없다면, 그 반대로 구현체는 이 표준을 사용하는 문서에 정의된 규칙을 따라야 합니다.
input에 U+000A (LF) 코드를, 결과적으로 각 줄이 width 코드 포인트를 넘지 않도록 구현체 정의 방식으로 삽입합니다. 여기서 줄 구분은 input의 시작, 끝, 그리고 U+000A (LF)로 판단합니다.
2.4. 프라이버시 문제
Infra 표준을 사용하는 문서에 정의된 일부 기능은 사용자 편의성과 맞바꾸어 일정 수준의 프라이버시를 희생할 수 있습니다.
일반적으로 인터넷의 구조상, 사용자는 자신의 IP 주소로 식별될 수 있습니다. 하지만 IP 주소는 완벽하게 사용자를 식별하지 않습니다. 사용자가 장치나 네트워크를 옮기면 IP도 바뀌고, NAT 라우팅, 프록시 서버, 공용 컴퓨터 등으로 인해 하나의 IP에서 여러 사용자가 나올 수 있습니다. 또한 onion 라우팅 같은 기술로 요청을 더 익명화할 수 있습니다. [RFC791]
그러나 사용자의 요청을 서로 연결하는 데 IP 주소만이 유일한 수단은 아닙니다. 예를 들어 쿠키는 바로 이 목적을 위해 설계된 것이며, 대부분의 웹 세션 기능의 근간입니다. 더 일반적으로는, HSTS, HTTP 캐시, 연결 그룹화, Storage API 등 모든 종류의 캐시 혹은 공유 상태가 오용될 수 있습니다. [COOKIES] [RFC6797] [STORAGE]
더 미묘한 방법도 있습니다. 사용자의 시스템 특성 일부가 여러 사용자 그룹을 구분하는 데 쓰일 수 있습니다. 이런 정보가 충분히 모이면, 개별 사용자의 브라우저의 "디지털 지문"을 산출할 수 있으며, 이는 IP 주소보다도 더 확실하게 동일 사용자의 요청임을 판별할 수 있습니다.
이렇게 요청을 그룹화하는 것은 특히 여러 사이트에 걸쳐 악의적으로 사용될 수 있습니다. 예를 들어, 정부가 어떤 사람이 길찾기 서비스를 통해 알아본 주소(실제 거주지)와 포럼 사이트 활동(정치 성향)을 결합해 선거권을 제한하는 것 등이 있습니다.
악의적 목적이 매우 심각할 수 있으므로, user agent 구현자와 명세 작성자는 지문 채취나 추적에 쓰일 수 있는 정보 누출을 최소화하도록 적극 권장합니다.
하지만, 이 섹션 첫 문단에서 알 수 있듯, 때로는 지문 채취·추적에 악용될 수 있는 API를 열어주는 것이 상당한 이득이 되기도 하기에, 모든 누수를 막는 것은 불가능합니다. 예를 들어, 특정 계정으로 로그인하려면 요청이 모두 동일 사용자임을 식별할 수 있어야 합니다. 더 미묘하게는, 텍스트 너비 측정처럼 캔버스에 텍스트를 그리는 효과에 필요한 정보도 사용자의 폰트 설치 정보 등 지문 채취에 쓰일 수 있습니다.
Infra 표준을 사용하는 문서에서 정의된 기능 중 추적 벡터로 사용될 수 있는 항목에는 이 단락처럼 표시됩니다.
플랫폼의 다른 기능도 다음과 같이 동일한 목적으로 악용될 수 있습니다:
- user agent가 지원하는 정확한 기능 목록
- 스크립트 재귀의 최대 허용 스택 깊이
- 사용자의 환경을 설명하는 기능
- 사용자의 시간대
- HTTP 요청 헤더
3. 알고리즘
3.1. 적합성
알고리즘과 알고리즘의 일부로 명령형으로 작성된 요구사항(예: "앞의 공백을 제거한다" 또는 "false를 반환한다")은 알고리즘이나 단계의 도입에 사용된 키워드(예: "must")의 의미대로 해석해야 합니다. 만약 그런 키워드가 없다면, must가 암묵적으로 적용됩니다.
예를 들어, 명세에 다음과 같이 쓰여 있다면:
오렌지를 먹는다는 것은 사용자가 다음을 해야 한다:
- 오렌지 껍질을 벗긴다.
- 오렌지 조각들을 분리한다.
- 오렌지 조각을 먹는다.
이는 다음과 동일합니다:
오렌지를 먹는다는 것은:
- 사용자는 오렌지 껍질을 벗겨야 한다.
- 사용자는 오렌지 조각을 분리해야 한다.
- 사용자는 오렌지 조각을 먹어야 한다.
여기서 핵심 키워드는 "must"입니다.
위 예시에서, 알고리즘이 단순히 "오렌지를 먹는다:"로만 도입되어도 동일하게 "must"가 암묵적으로 적용됩니다.
알고리즘 또는 구체적 단계로 작성된 적합성 요구사항은 결과가 동일하다면 어떤 방식으로든 구현할 수 있습니다. (특히, 알고리즘은 따라가기 쉽게 설계되었으며 성능을 목적으로 하지 않습니다.)
성능은 사용자 인식, 컴퓨터 아키텍처, 그리고 시간이 지남에 따라 흔해지는 입력 유형 등에 의해 영향을 받으므로 맞추기 어렵습니다. 예를 들어, JavaScript 엔진은 표준화된 단일 알고리즘에 대해 여러 코드 경로를 가질 수 있으며, 이는 속도나 메모리 최적화를 위해서입니다. 이러한 모든 코드 경로를 표준화하는 것은 현실적으로 불가능하며, 단일 알고리즘만큼 시간의 시험을 견디기도 어렵습니다. 따라서 성능은 경쟁의 영역으로 남겨두는 것이 가장 좋습니다.
3.2. 알고리즘 입력 제한 피하기
Infra 표준을 사용하는 문서는 일반적으로 알고리즘 입력의 크기, 자원 사용량 등과 관련한 특정 제한을 강제하지 않아야 합니다. 이는 user agent 간 경쟁을 가능하게 하고 미래의 컴퓨팅 요구를 제한하지 않기 위함입니다.
그럼에도
불구하고, user agent는 제한되지 않은 입력에 대해 구현체 정의 제한을 둘 수 있습니다. 예를 들어, 서비스 거부 공격 방지, 메모리 부족 방지, 플랫폼 고유 제약
회피 등이 있습니다.
전역 자원 제한은 자원 고갈 공격의 변종을 통해 부채널로 악용될 수 있으며, 공격자가 피해 애플리케이션이 전역 제한에 도달했는지 관찰할 수 있습니다. 제한은 user agent의 고유성을 높이는 경우에만 지문 채취에도 쓰일 수 있습니다. 예를 들어, 하드웨어에 특화된 경우입니다.
메모리 내
비트맵을 생성할 수 있게 하는 API는 모든 크기를 허용하거나, JavaScript의 Number.MAX_SAFE_INTEGER
와 같이 매우 큰 제한까지 허용할 수
있습니다. 하지만, 구현체는 실제로 구현체 정의(즉, 명세에 없는) 제한을 둘 수 있으며, 무한한 메모리 할당을 시도하지 않을 수 있습니다.
프로그래밍 언어가 최대 호출 스택 크기를 명세하지 않을 수도 있습니다. 하지만 구현체는 실질적 이유로 제한을 둘 수 있습니다.
특정 제한에 코드가 의존하게 될 수 있으므로, 상호운용성을 위해 제한을 정의하는 것이 유용할 수 있습니다. 때로는 이런 방식을 받아들이는 것이 미래에도 문제가 되지 않으며, 더 많은 user agent에서 코드를 실행할 수 있게 해줍니다.
또한 구현체 정의 제한에 하한을 두는 것도 유용할 수 있습니다. 즉, 모든 구현체가 일정 최소 크기의 입력을 처리할 수 있도록 보장하는 것입니다.
3.3. 선언
알고리즘 이름은 보통 동사구로 작성되지만, 때로는 독립적 존재를 강조하는 이름을 부여해 표준과 독자가 더 관용적으로 알고리즘을 참조할 수 있게 합니다.
후자의 예시로는 "attribute change steps", "internal module script graph fetching procedure", "overload resolution algorithm" 등이 있습니다.
알고리즘은 이름, 매개변수, 반환 타입을 아래와 같은 형식으로 선언합니다:
[알고리즘 이름]을 위해, [type1] [parameter1], [type2] [parameter2], …를 받아 다음 단계를 수행한다. 반환값은 [return type]이다.
(동사구가 아닌 알고리즘 이름에는 "To perform the [algorithm name]…"과 같이 씁니다. 더 복잡한 매개변수 선언은 § 3.4 매개변수 참고.)
멋진 포맷을 파싱한다 알고리즘은 byte sequence bytes를 받아 다음 단계를 수행한다. 반환값은 string 또는 null이다.
값을 반환하지 않는 알고리즘은 더 짧은 형식을 사용합니다. 반환 타입이 단계에서 명확히 유추된다면 반환값이 있는 경우에도 이 짧은 형식을 사용할 수 있습니다:
[알고리즘 이름]을 위해, [type1] [parameter1], [type2] [parameter2], …를 받는다:
멋진 포맷을 파싱한다 알고리즘은 byte sequence bytes를 받는다:
매우 짧은 알고리즘은 한 문장으로 선언 및 명세할 수 있습니다:
멋진 포맷을 파싱한다 알고리즘은 byte sequence bytes를 받아, bytes의 isomorphic decoding의 ASCII 대문자화 결과를 반환한다.
타입은 알고리즘 선언에서 포함하는 것이 좋으나, 매개변수명이 충분히 명확하거나 맥락상 명확하다면 생략할 수 있습니다. (예: 알고리즘이 다른 알고리즘의 단순 래퍼일 경우.)
클래식 스크립트를 로드한다 알고리즘은
url을 받아, 내부 스크립트 로딩 알고리즘에 url과 "classic
"을 넘겨 그 결과를 반환한다.
3.4. 매개변수
알고리즘 매개변수는 일반적으로 § 3.3 선언에 기술된 방식처럼 순차적으로 나열됩니다. 다만, 좀 더 복잡한 경우도 있습니다.
알고리즘 매개변수는 선택적일 수 있으며, 이 경우 알고리즘 선언에서 명시적으로 선택적임을 표시하고, 필수가 아닌 매개변수 뒤에 나열해야 합니다. 기본값을 줄 수도 있고, 알고리즘 본문에서 인자가 제공됐는지 확인할 수도 있습니다. 구체적으로 다음과 같이 작성합니다:
… an optional [type] [parameter] …
… an optional [type] [parameter] (default [default value]) …
선택적 boolean 매개변수에는 반드시 기본값이 지정되어야 하며, 그 값은 false여야 합니다.
탐색한다 알고리즘은 리소스 resource에 대해, 선택적 문자열 navigationType과 선택적 불리언 exceptionsEnabled (기본값 false)를 받는다:
- …
- navigationType이 제공된 경우, navigationType으로 무언가를 수행한다.
- …
이런 선택적 위치 매개변수를 가지는 알고리즘을 호출할 때, 선택적 인자는 뒤에서부터 생략할 수 있습니다.
이전 예시 알고리즘의 호출부는 다음과 같을 수 있습니다:
하지만, 두 번째(navigationType) 인자는 생략한 채 세 번째(exceptionsEnabled) 인자에만 값을 제공하는 것은 불가능합니다. 또한 마지막 호출은 "true"가 "예외 허용"임을 이해하려면 알고리즘 선언까지 돌아가서 매개변수 순서를 세어야 하므로 가독성이 떨어집니다. 이 문제를 해결하는 방법은 아래에서 설명합니다!
위치 매개변수 대신 명명된 선택적 매개변수를 사용하면 호출부의 명확성과 유연성을 높일 수 있습니다. 이런 매개변수는 변수이자 정의로 마크업되며, 호출부에서 링크로 연결됩니다.
탐색한다 알고리즘은 리소스 resource, 선택적 문자열 navigationType과 선택적 불리언 exceptionsEnabled (기본값 false)를 받는다:
- …
- navigationType이 제공된 경우, navigationType으로 무언가를 수행한다.
- …
호출부는 다음과 같이 쓸 수 있습니다:
- 탐색 resource.
- 탐색 resource와
navigationType을
"
form-submission
"으로 설정. - 탐색 resource와 exceptionsEnabled을 true로 설정.
- 탐색 resource와
navigationType을
"
form-submission
"으로, exceptionsEnabled을 true로 설정.
알고리즘 단계 내에서는 인자값이 매개변수 선언과 링크되지 않고 단순 변수 참조로 남는다는 점에 유의하세요. 호출부에서만 매개변수 선언에 링크합니다.
필수 명명 매개변수도 위와 같은 방식(변수이자 정의로 마크업, 호출부에서 링크)으로 사용할 수 있습니다. 이는 호출부의 명확성을 높여줍니다.
불리언 매개변수는 선택적 여부와 상관없이 위치 인자로 둘 때보다 이름을 명확히 하는 것이 훨씬 읽기 쉽습니다. 이에 대한 논의는 Boolean Trap의 함정을 참고하세요.
명확성 향상을 위한 또 다른 보완 기법으로는, 관련 값을 구조체로 묶어 매개변수로 넘기는 방법이 있습니다. 이는 동일한 값 집합이 여러 알고리즘의 입력으로 쓰일 때 특히 유용합니다.
3.5. 변수
변수는 "let"으로 선언하고 "set"으로 변경합니다.
list를 새로운 list로 둔다.
activationTarget를 isActivationEvent가 true이고 target이 activation behavior를 가지면 target, 아니면 null로 둔다.
변수는 선언 전에 사용할 수 없습니다. 변수는 블록 범위를 가집니다. 변수는 하나의 알고리즘에서 한 번만 선언할 수 있습니다.
여러 변수에 tuple의 항목을 할당할 때 괄호로 변수명을 감싸고 쉼표로 구분해 사용할 수 있습니다. 할당할 변수의 개수와 tuple의 항목 개수는 같아야 합니다.
-
statusInstance를 status (200, `
OK
`)로 둔다. -
(status, statusMessage)를 statusInstance로 둔다.
status와 statusMessage에 할당하는 것은 인덱스 또는 이름을 이용해 tuple의 항목에 접근하는 두 단계로 쓸 수도 있습니다.
3.6. 제어 흐름
알고리즘의 제어 흐름에서 "return"이나 "throw"가 요구되면 해당 문장이 속한 알고리즘을 종료합니다. "Return"은 값을 호출자에게 넘기고, "Throw"는 호출자에게 값을 자동으로 다시 던져 호출자 알고리즘을 종료합니다. 산문으로 예외를 "catch"하여 다른 동작을 수행할 수도 있습니다.
3.7. 조건부 중단
특정 조건이 참이 되면 일련의 단계를 중단하는 것이 유용할 때가 있습니다.
이럴 때는, 주어진 일련의 단계가 특정 condition에 도달하면 중단된다고 명시합니다. 이는 각 단계 전마다 condition을 검사하는 단계를 추가하고, 참이면 남은 단계를 건너뛰는 것으로 해석해야 함을 의미합니다.
이런 알고리즘에서는 다음 단계를 중단 시로 표시할 수 있으며, 그러한 경우 앞선 단계가 중단 조건으로 건너뛰어졌다면 반드시 실행해야 합니다.
이 구문을 사용할 때는, 구현체가 지정된 단계 중간에 condition을 평가해도 결과가 구분되지 않는 한 허용됩니다. 예를 들어, 위 예시에서 result가 계산 중에 변경되지 않는다면, user agent는 계산을 중단할 수 있습니다.
3.8. 조건문
조건문이 있는 알고리즘은 "if", "then", "otherwise" 키워드를 사용해야 합니다.
-
value를 null로 둔다.
-
input이 string이면, value를 input으로 설정한다.
-
value를 반환한다.
"otherwise" 키워드가 사용되면, "then" 키워드는 생략합니다.
-
value를 null로 둔다.
-
input이 string이면, value를 input으로 설정한다.
-
그 외에는 value를 failure로 설정한다.
-
value를 반환한다.
3.9. 반복
조건이 충족될 때까지 일련의 단계를 반복하는 다양한 방법이 있습니다.
Infra 표준은 이에 대해 아직 완전하지 않습니다. 필요하다면 이슈를 등록해 주세요.
반복의 흐름은 continue 또는 break 요구로 제어할 수 있습니다. Continue는 반복 내 남은 단계를 건너뛰고 다음 항목으로 진행합니다. 더 이상 항목이 없으면 반복이 종료됩니다. Break는 반복 내 남은 단계와 항목을 모두 건너뛰어 반복을 종료합니다.
example을 list « 1, 2, 3, 4 »로 둔다. 다음 산문은 operation을 1, 2, 3, 4에 각각 수행한다:
-
각 item에 대해 example에서:
- item에 operation을 수행한다.
다음 산문은 1, 2, 4에 operation을 수행하며, 3은 건너뜁니다.
다음 산문은 1, 2에 operation을 수행하며, 3과 4는 건너뜁니다.
3.10. 단언문
가독성을 높이기 위해 알고리즘에 불변식을 명시하는 단언문을 추가할 수 있습니다. 이를 위해 "Assert:" 뒤에 반드시 참이어야 하는 문장을 작성합니다. 해당 문장이 거짓이 된다면, 이는 Infra 표준을 사용하는 문서에 문제가 있음을 의미하므로 보고 및 수정되어야 합니다.
이 문장은 항상 참이어야 하므로, 구현체에는 아무런 영향이 없습니다.
-
x를 "
Aperture Science
"로 둔다. -
Assert: x는 "
Aperture Science
"이다.
4. 원시 데이터 타입
4.1. Null값
null 값은 값이 없음을 나타내는 데 사용됩니다. JavaScript의 null 값과 상호 교환하여 사용할 수 있습니다. [ECMA-262]
4.2. 불리언
불리언은 true 또는 false입니다.
4.3. 숫자
숫자는 복잡합니다. 이슈 #87을 참고하세요. 향후 타입 및 수학 연산에 대한 안내를 보강할 예정입니다. 기여 환영!
8비트 부호 없는 정수는 0에서 255(0부터 28 − 1)까지의 정수입니다.
16비트 부호 없는 정수는 0에서 65535(0부터 216 − 1)까지의 정수입니다.
32비트 부호 없는 정수는 0에서 4294967295(0부터 232 − 1)까지의 정수입니다.
64비트 부호 없는 정수는 0에서 18446744073709551615(0부터 264 − 1)까지의 정수입니다.
128비트 부호 없는 정수는 0에서 340282366920938463463374607431768211455(0부터 2128 − 1)까지의 정수입니다.
IPv6 주소는 128비트 부호 없는 정수입니다.
8비트 부호 있는 정수는 −128에서 127(−27에서 27 − 1)까지의 정수입니다.
16비트 부호 있는 정수는 −32768에서 32767(−215에서 215 − 1)까지의 정수입니다.
32비트 부호 있는 정수는 −2147483648에서 2147483647(−231에서 231 − 1)까지의 정수입니다.
64비트 부호 있는 정수는 −9223372036854775808에서 9223372036854775807(−263에서 263 − 1)까지의 정수입니다.
4.4. 바이트
바이트는 8비트의 시퀀스로, "0x
" 뒤에
두 자리 ASCII 대문자
16진수가 붙은 형식(0x00~0xFF)입니다. 바이트의 값은 실제
숫자입니다.
ASCII 바이트는 0x00(NUL)에서 0x7F(DEL)까지의 바이트입니다. 0x28, 0x29를 제외한 ASCII 바이트는 ASCII format for Network Interchange의 Standard Code 섹션에 설명된 표기(괄호로 둘러싼 형식)가 뒤따를 수 있습니다. [RFC20]
0x28 뒤에는 "(left parenthesis)", 0x29 뒤에는 "(right parenthesis)"가 올 수 있습니다.
0x49 (I)는 UTF-8로 디코딩 시 코드 포인트 U+0049 (I)로 변환됩니다.
4.5. 바이트 시퀀스
바이트 시퀀스는 바이트들의 시퀀스로, 바이트들을 공백으로 구분해 나타냅니다. 바이트가 0x20(SP)에서 0x7E(~) 범위에 있을 경우, 문자열처럼 백틱(`)으로 감싸서 표현할 수도 있습니다. 이는 실제 문자열과 혼동을 방지하기 위한 것입니다.
바이트 시퀀스를 문자열로 변환하려면 Encoding의 UTF-8 인코드 사용이 권장됩니다. 아주 드물게 isomorphic encode가 필요할 수도 있습니다. [ENCODING]
바이트 시퀀스를 byte-lowercase한다는 것은, 포함된 바이트 중 0x41(A)~0x5A(Z) 범위의 값에 0x20을 더하는 것입니다.
바이트 시퀀스를 byte-uppercase한다는 것은, 포함된 바이트 중 0x61(a)~0x7A(z) 범위의 값에서 0x20을 뺍니다.
바이트 시퀀스 A가 byte-case-insensitive하게 바이트 시퀀스 B와 일치한다는 것은 A를 byte-lowercase한 결과와 B를 byte-lowercase한 결과가 같음을 의미합니다.
바이트 시퀀스 potentialPrefix가 prefix로 바이트 시퀀스 input에 해당하려면 다음 단계를 따릅니다:
-
i를 0으로 둔다.
-
While true:
"input starts with potentialPrefix"는 "potentialPrefix가 prefix로 input에 해당"의 동의어로 사용할 수 있습니다.
바이트 시퀀스 a가 byte less than 바이트 시퀀스 b인 경우는 다음과 같습니다:
-
b가 prefix로 a에 해당하면 false를 반환한다.
-
a가 prefix로 b에 해당하면 true를 반환한다.
-
n을 a의 n번째 바이트와 b의 n번째 바이트가 다를 때의 최소 인덱스로 둔다. (둘 중 하나가 다른 쪽의 prefix가 아니므로 반드시 이런 인덱스가 존재함.)
-
a의 n번째 바이트가 b의 n번째 바이트보다 작으면 true를 반환한다.
-
false를 반환한다.
isomorphic decode 알고리즘은 바이트 시퀀스 input을 받아, input의 길이와 같은 문자열을 반환하고, 그 코드 포인트 길이와 코드 포인트 값들이 input의 바이트 값과 동일해야 하며, 순서도 같아야 합니다.
4.6. 코드 포인트
코드 포인트는 유니코드 코드 포인트이며, "U+" 뒤에 네 자리에서 여섯 자리 ASCII 대문자 16진수로 표기합니다. 범위는 U+0000에서 U+10FFFF까지 포함됩니다. 코드 포인트의 값은 그 내부 숫자 값입니다.
코드 포인트 뒤에는 이름이나, U+0028 또는 U+0029가 아닐 때 괄호 안에 렌더링된 형태, 또는 둘 다를 표기할 수 있습니다. Infra 표준을 사용하는 문서는 렌더링이 불가능하거나 U+0028, U+0029인 경우 이름으로, 그렇지 않으면 괄호 안에 렌더링된 형태로 표기하는 것이 가독성에 좋으므로 이를 권장합니다.
코드 포인트의 이름은 Unicode에서 정의되며, ASCII 대문자로 표기합니다. [UNICODE]
🤔로 렌더링되는 코드 포인트는 U+1F914로 표기됩니다.
이 코드 포인트를 언급할 때 "U+1F914 (🤔)"처럼 추가 정보를 제공할 수 있습니다. 문서에서는 "U+1F914 THINKING FACE (🤔)"와 같이 사용할 수도 있지만, 이는 다소 장황합니다.
렌더링이 어려운 코드 포인트(예: U+000A)는 "U+000A LF"로 표기할 수 있습니다. U+0029는 괄호 불일치 방지를 위해 "U+0029 RIGHT PARENTHESIS"로 쓸 수 있습니다.
코드 포인트는 때로 문자로 불리거나, 상황에 따라 "U+" 대신 "0x"로 접두사를 붙이기도 합니다.
선행 서러게이트는 U+D800에서 U+DBFF 범위의 코드 포인트입니다.
후행 서러게이트는 U+DC00에서 U+DFFF 범위의 코드 포인트입니다.
서러게이트는 선행 서러게이트 또는 후행 서러게이트입니다.
비문자는 U+FDD0~U+FDEF, U+FFFE, U+FFFF, U+1FFFE, U+1FFFF, U+2FFFE, U+2FFFF, U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE, U+6FFFF, U+7FFFE, U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF, U+DFFFE, U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, U+10FFFF에 해당하는 코드 포인트입니다.
ASCII 코드 포인트는 U+0000(NULL)~U+007F(DELETE) 범위의 코드 포인트입니다.
ASCII 탭 또는 줄바꿈은 U+0009 TAB, U+000A LF, U+000D CR입니다.
ASCII 공백은 U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, U+0020 SPACE입니다.
"Whitespace"는 물질명사입니다.
XML, JSON, HTTP 일부 명세에서는 U+000C FF를 공백 정의에서 제외합니다:
새 기능에는 Infra의 ASCII 공백 정의 사용을 권장합니다. 단, 명세가 XML/JSON/HTTP만을 다룬다면 예외입니다.
C0 제어문자는 U+0000(NULL)~U+001F(INFORMATION SEPARATOR ONE) 범위의 코드 포인트입니다.
C0 제어문자 또는 공백은 C0 제어문자 또는 U+0020 SPACE입니다.
제어문자는 C0 제어문자 또는 U+007F(DELETE)~U+009F(APPLICATION PROGRAM COMMAND) 범위의 코드 포인트입니다.
ASCII 숫자는 U+0030(0)~U+0039(9) 범위의 코드 포인트입니다.
ASCII 대문자 16진수는 ASCII 숫자 또는 U+0041(A)~U+0046(F) 범위의 코드 포인트입니다.
ASCII 소문자 16진수는 ASCII 숫자 또는 U+0061(a)~U+0066(f) 범위의 코드 포인트입니다.
ASCII 16진수는 ASCII 대문자 16진수 또는 ASCII 소문자 16진수입니다.
ASCII 대문자 알파벳은 U+0041(A)~U+005A(Z) 범위의 코드 포인트입니다.
ASCII 소문자 알파벳은 U+0061(a)~U+007A(z) 범위의 코드 포인트입니다.
ASCII 알파벳은 ASCII 대문자 알파벳 또는 ASCII 소문자 알파벳입니다.
ASCII 영숫자는 ASCII 숫자 또는 ASCII 알파벳입니다.
4.7. 문자열
문자열은 16비트 부호 없는 정수들의 시퀀스이며, 코드 유닛이라고도 합니다. 문자열은 JavaScript 문자열이라고도 불립니다. 문자열은 큰따옴표와 고정폭 글꼴로 표기됩니다.
이는 Unicode에서 "code unit"을 정의하는 방식과 다릅니다. 특히 이는 Unicode가 유니코드 16비트 문자열에 대해 정의한 방식에만 해당합니다. [UNICODE]
문자열은 코드 포인트를 포함하는 것으로도 해석할 수 있으며, 이 변환은 JavaScript 명세의 The String Type 섹션에 정의되어 있습니다. [ECMA-262]
이 변환 과정에서 서러게이트 쌍은 해당 스칼라 값으로 변환되며, 남은 서러게이트는 해당 코드 포인트로 변환되어 사실상 원래대로 남습니다.
문자열이 0xD83D, 0xDCA9, 0xD800 코드 유닛으로 구성되어 있다면, 이를 코드 포인트로 해석하면 U+1F4A9와 U+D800 두 코드 포인트가 됩니다.
문자열의 코드 포인트 길이는 포함된 코드 포인트의 수입니다.
문자열이 포함할 수 있는 코드 포인트에 추가 제한이 있을 때, 이 명세는 ASCII 문자열, isomorphic 문자열, 스칼라 값 문자열을 정의합니다. 이를 사용하면 명세의 명확성이 높아집니다.
ASCII 문자열은 문자열의 코드 포인트가 모두 ASCII 코드 포인트인 경우입니다.
isomorphic 문자열은 문자열의 코드 포인트가 모두 U+0000(NULL)에서 U+00FF(ÿ) 범위에 있는 경우입니다.
스칼라 값 문자열은 문자열의 코드 포인트가 모두 스칼라 값인 경우입니다.
스칼라 값 문자열은 UTF-8 인코드가 필요한 모든 입출력이나 기타 연산에 유용합니다.
문자열을 스칼라 값 문자열로 변환하려면 모든 서러게이트를 U+FFFD(�)로 대체합니다.
대체되는 서러게이트는 결코 서러게이트 쌍의 일부가 아닙니다. 문자열을 코드 포인트로 해석하는 과정에서 서러게이트 쌍은 이미 스칼라 값으로 변환됩니다.
스칼라 값 문자열은 항상 문자열로 자동 변환될 수 있습니다. 모든 스칼라 값 문자열은 문자열이기 때문입니다. 반대로, 문자열이 스칼라 값 문자열로 자동 변환될 수 있으려면 서러게이트가 없어야 하며, 그렇지 않으면 변환이 필요합니다.
구현체가 문자열과 스칼라 값 문자열을 실제로 어떻게 표현하느냐에 따라 명시적 변환이 필요할 수 있습니다. 성능 및 메모리상의 이유로 문자열 자체만도 여러 구현이 존재하는 것이 일반적입니다.
문자열 a가 같다 또는 동일하다고 할 때는 두 문자열이 동일한 코드 유닛 시퀀스일 때입니다.
별도의 명시가 없는 한, 모든 문자열 비교에는 같음을 사용합니다.
이런 문자열 비교는 과거 HTML에서는 "대소문자 구분" 비교로 불렸습니다. 동일하게 비교되는 문자열은 대소문자뿐 아니라 정규화 방식, 결합 문자 순서 등 기타 인코딩에도 민감합니다. Unicode에서 시각적으로나 규범적으로 동일한 두 문자열도 동일하지 않을 수 있습니다. [HTML] [UNICODE]
문자열 potentialPrefix가 코드 유닛 접두사로 문자열 input에 해당하려면 다음 단계를 따릅니다:
-
i를 0으로 둔다.
-
While true:
컨텍스트상 코드 유닛이 사용되는 것이 명확하다면(예: 문자열이 U+0020 SPACE~U+007E(~)만 포함하는 리터럴), "input 시작됨 potentialPrefix"는 "potentialPrefix가 코드 유닛 접두사로 input에 해당"의 동의어로 사용할 수 있습니다.
값이
미지수일 때는 명시적으로: targetString이 코드 유닛 접두사로 userInput에 해당. 리터럴에는 평범한 언어로:
userInput 시작됨 "!
".
문자열 potentialSuffix가 코드 유닛 접미사로 문자열 input에 해당하려면 다음 단계를 따릅니다:
-
i를 1로 둔다.
-
While true:
-
potentialSuffixIndex를 potentialSuffix의 길이 − i로 둔다.
-
inputIndex를 input의 길이 − i로 둔다.
-
potentialSuffixIndex가 0보다 작으면 true를 반환한다.
-
inputIndex가 0보다 작으면 false를 반환한다.
-
potentialSuffixCodeUnit을 potentialSuffixIndex번째 코드 유닛으로 둔다.
-
inputCodeUnit을 inputIndex번째 코드 유닛으로 둔다.
-
potentialSuffixCodeUnit이 inputCodeUnit과 다르면 false를 반환한다.
-
i를 i + 1로 설정한다.
-
컨텍스트상 코드 유닛이 사용되는 것이 명확하다면(예: 문자열이 U+0020 SPACE~U+007E(~)만 포함하는 리터럴), "input 끝남 potentialSuffix"는 "potentialSuffix가 코드 유닛 접미사로 input에 해당"의 동의어로 사용할 수 있습니다.
값이
미지수일 때는 명시적으로: targetString이 코드 유닛 접미사로 domain에 해당. 리터럴에는 평범한 언어로: domain 끝남 ".
".
문자열 a가 코드 유닛 기준 작다면, 문자열 b에 대해 다음 단계가 true를 반환하는 경우입니다:
-
b가 코드 유닛 접두사로 a에 해당하면 false를 반환한다.
-
a가 코드 유닛 접두사로 b에 해당하면 true를 반환한다.
-
n을 a의 n번째 코드 유닛이 b의 n번째 코드 유닛과 다를 때의 최소 인덱스로 둔다. (둘 중 하나가 다른 쪽의 접두사가 아니므로 반드시 이런 인덱스가 존재함.)
-
a의 n번째 코드 유닛이 b의 n번째 코드 유닛보다 작으면 true를 반환한다.
-
false를 반환한다.
이 비교는 JavaScript의 <
연산자, 그리고 문자열 배열의 sort()
메서드에서 사용되는 순서와 일치합니다. 이 순서는 각 문자열의 16비트 코드 유닛을 비교하여 매우 효율적이고 일관적이며 결정적인 정렬 순서를 만듭니다. 결과 순서는 어떤 알파벳이나 사전적
순서와는 다를 수 있으며, 특히 서러게이트 쌍으로 표현되는 코드
포인트의 경우 더욱 그렇습니다. [ECMA-262]
예를 들어, U+FF5E(전각 물결표)는 U+1F600(😀)보다 코드 포인트 값이 작지만, 물결표는 하나의 코드 유닛(0xFF5E)으로 이루어져 있고, 스마일리는 두 개의 코드 유닛(0xD83D, 0xDE00)으로 이루어져 있으므로, 스마일리가 코드 유닛 기준 작다가 됩니다.
코드 유닛 부분 문자열은 문자열 string에서 start 위치에서 length 길이만큼 추출한 부분 문자열로, 다음과 같이 결정합니다:
-
Assert: start와 length는 음수가 아니다.
-
result를 빈 문자열로 둔다.
-
각 i에 대해 start에서 start + length까지(끝 미포함) 반복: string의 i번째 코드 유닛을 result에 추가한다.
-
result를 반환한다.
코드 유닛 부분 문자열은 문자열 string에서 start 위치부터 end까지의 부분 문자열로, start에서 end − start 길이만큼의 코드 유닛 부분 문자열입니다.
코드 유닛 부분 문자열은 문자열 string에서 start 위치부터 끝까지의 부분 문자열로, code unit substring from start to string의 길이에 해당합니다.
"Hello world
"에서 1 위치에서 길이 3의 코드 유닛 부분 문자열은
"ell
"입니다. 이는 1에서 4까지의 코드 유닛 부분 문자열로도 표현할 수 있습니다.
이 알고리즘에서 숫자는 코드 유닛 자체의 인덱스가 아니라 코드 유닛 사이의 위치로 생각하는 것이 가장 이해하기 쉽습니다. 반환되는 부분 문자열은 이 위치 사이의 코드 유닛으로 구성됩니다. 예를 들면, 빈 문자열에서 0에서 0까지의 코드 유닛 부분 문자열은 빈 문자열입니다. 빈 문자열의 0번 인덱스에 코드 유닛이 없지만 반환 결과는 빈 문자열입니다.
코드 포인트 부분 문자열은 문자열 string에서 start 위치에서 length 길이만큼의 부분 문자열로, 다음과 같이 결정합니다:
-
Assert: start와 length는 음수가 아니다.
-
result를 빈 문자열로 둔다.
-
각 i에 대해 start에서 start + length까지(끝 미포함) 반복: string의 i번째 코드 포인트를 result에 추가한다.
-
result를 반환한다.
코드 포인트 부분 문자열은 문자열 string에서 start 위치부터 end까지의 부분 문자열로, start에서 end − start 길이만큼의 코드 포인트 부분 문자열입니다.
코드 포인트 부분 문자열은 문자열 string에서 start 위치부터 끝까지의 부분 문자열로, code point substring from start to string의 코드 포인트 길이에 해당합니다.
일반적으로 코드 유닛 부분
문자열은 개발자가 제공한 위치나 길이에 대해 사용됩니다. 이는 자바스크립트에서 문자열 인덱싱이 이런 방식으로 동작하기 때문입니다. 예를 들어 CharacterData
클래스의 메서드 등이 있습니다. [DOM]
그 외의 경우에는 코드 포인트
부분 문자열이 더 적합할 수 있습니다. 예를 들어, "👽
"에서 0 위치에서 길이 1의 코드 포인트 부분 문자열은
"👽
"이지만, 동일 문자열에서 0 위치에서 길이 1의 코드 유닛 부분 문자열은 U+D83B 서러게이트 하나만 포함합니다.
isomorphic encode는 isomorphic 문자열 input을 받아, input의 코드 포인트 길이와 같은 바이트 시퀀스를 반환하며, 그 바이트 값은 input의 코드 포인트 값과 동일한 순서로 같아야 합니다.
ASCII 소문자화는 문자열의 모든 ASCII 대문자 알파벳을 해당 코드 포인트에 해당하는 ASCII 소문자 알파벳으로 바꿉니다.
ASCII 대문자화는 문자열의 모든 ASCII 소문자 알파벳을 해당 코드 포인트에 해당하는 ASCII 대문자 알파벳으로 바꿉니다.
문자열 A가 ASCII 대소문자 구분 없음으로 문자열 B와 일치한다는 것은 A를 ASCII 소문자화한 결과와 B를 ASCII 소문자화한 결과가 같음을 의미합니다.
ASCII encode는 ASCII 문자열 input을 받아, input의 isomorphic encoding을 반환합니다.
Isomorphic encode와 UTF-8 encode는 input에 대해 동일한 바이트 시퀀스를 반환합니다.
ASCII decode는 바이트 시퀀스 input을 받아 다음 단계를 실행합니다:
-
Assert: input의 모든 바이트는 ASCII 바이트여야 한다.
이 전제조건은 isomorphic decode와 UTF-8 decode가 이 입력에 대해 동일한 문자열을 반환함을 보장합니다.
-
isomorphic decode 결과를 반환한다.
줄바꿈 제거는 문자열에서 모든 U+000A LF와 U+000D CR 코드 포인트를 제거합니다.
줄바꿈 정규화는 문자열에서 모든 U+000D CR U+000A LF 코드 포인트 쌍을 단일 U+000A LF 코드 포인트로 바꾸고, 남은 모든 U+000D CR 코드 포인트를 U+000A LF 코드 포인트로 바꿉니다.
선행 및 후행 ASCII 공백 제거는 문자열에서 시작이나 끝에 있는 ASCII 공백을 모두 제거합니다.
ASCII 공백 제거 및 병합은 문자열 내 연속된 한 개 이상의 코드 포인트가 ASCII 공백일 때, 이를 단일 U+0020 SPACE 코드 포인트로 바꾸고, 이어서 선행 및 후행 ASCII 공백을 제거합니다.
조건 condition을 만족하는 코드 포인트 시퀀스 수집은 문자열 input에서, 위치 변수 position을 활용하여 호출 알고리즘이 input 내에서 위치를 추적하는 상황에서 실행함:
-
result를 빈 문자열로 둔다.
-
position이 input의 끝을 지나지 않았고, input 내 position의 코드 포인트가 condition을 만족하는 동안:
-
그 코드 포인트를 result 끝에 추가한다.
-
position을 1만큼 증가시킨다.
-
-
result를 반환한다.
이 알고리즘은 수집한 코드 포인트를 반환할 뿐 아니라, 호출 알고리즘의 위치 변수도 갱신합니다.
ASCII 공백 건너뛰기는 문자열 input과 위치 변수 position이 주어졌을 때, position을 기준으로 ASCII 공백인 코드 포인트 시퀀스 수집을 실행한다. 수집된 코드 포인트는 사용하지 않지만, position은 갱신된다.
문자열 엄격 분할은 문자열 input을 특정 구분자 코드 포인트 delimiter로 분할하는 알고리즘입니다:
-
position을 위치 변수로 input의 시작을 가리키게 한다.
-
token을 조건을 만족하는 코드 포인트 시퀀스 수집 (여기서는 delimiter와 같지 않은)를 input과 position에 대해 실행한 결과로 둔다.
-
Append로 token을 tokens에 추가한다.
-
position이 input의 끝을 지나지 않을 때 반복:
-
position을 1만큼 앞으로 이동한다.
-
token을 조건을 만족하는 코드 포인트 시퀀스 수집 (여기서는 delimiter와 같지 않은)를 input과 position에 대해 실행한 결과로 둔다.
-
Append로 token을 tokens에 추가한다.
-
tokens를 반환한다.
이 알고리즘은 "엄격(strict)" 분할로, 아래의 ASCII 공백 또는 쉼표 기준 분할처럼 여러 가지 유연한 처리(공백 포함 등)를 하지 않는다.
문자열 input을 ASCII 공백 기준으로 분할하는 방법:
-
position을 위치 변수로 input의 시작을 가리키게 한다.
-
ASCII 공백 건너뛰기를 input과 position에 대해 실행한다.
-
position이 input의 끝을 지나지 않을 때 반복:
-
token을 조건을 만족하는 코드 포인트 시퀀스 수집 (여기서는 ASCII 공백이 아닌)를 input과 position에 대해 실행한 결과로 둔다.
-
Append로 token을 tokens에 추가한다.
-
ASCII 공백 건너뛰기를 input과 position에 대해 실행한다.
-
-
tokens를 반환한다.
문자열 input을 쉼표로 분할하는 방법:
-
position을 위치 변수로 input의 시작을 가리키게 한다.
-
position이 input의 끝을 지나지 않을 때 반복:
-
token을 조건을 만족하는 코드 포인트 시퀀스 수집 (여기서는 U+002C(,)가 아닌)를 input과 position에 대해 실행한 결과로 둔다.
token은 빈 문자열일 수도 있다.
- 선행 및 후행 ASCII 공백 제거를 token에 실행한다.
-
Append로 token을 tokens에 추가한다.
-
position이 input의 끝을 지나지 않으면:
-
-
tokens를 반환한다.
리스트 list의 문자열들을(선택적으로 구분자 문자열 separator 사용) 연결(concatenate)하는 방법:
-
list가 비어 있으면 빈 문자열을 반환한다.
-
separator가 주어지지 않았으면 separator를 빈 문자열로 설정한다.
집합 set을 직렬화할 때, concatenate를 U+0020 SPACE를 구분자로 사용해 반환한다.
4.8. 시간
시간은 moment 및 duration 명세 타입을 사용하여 표현합니다. JavaScript와 이 값을 교환하거나 생성할 때에는 High Resolution Time § 3 명세 작성자를 위한 도구의 권고를 따르세요. [HR-TIME]
5. 데이터 구조
관례적으로, 명세들은 의미에 대한 공통된 이해를 바탕으로 다양한 모호한 명세 수준의 데이터 구조를 사용해 왔습니다. 이는 대체로 잘 작동하지만, 반복 순서나 이미 ordered set에 item을 append할 때 등 경계 사례에서 모호함이 발생할 수 있습니다. 또한, map과 같은 더 복잡한 데이터 구조에서 다양한 표기법과 표현이 파생되기도 했습니다.
이 표준은 공통 기반을 마련하기 위해, 몇 가지 일반적인 데이터 구조와 그것을 다루기 위한 표기법 및 표현을 제공합니다.
5.1. 리스트
리스트는 유한한 순서의 항목 시퀀스로 이루어진 명세 타입입니다.
표기상의 편의를 위해, 리스트는 « »로 감싸고 각 항목을 쉼표로 구분하는 리터럴 문법을 사용할 수 있습니다. 리스트의 인덱싱은 대괄호[] 안에 0부터 시작하는 인덱스를 넣어 사용할 수 있습니다. 단, exists와 함께 쓰는 경우를 제외하고 인덱스가 범위를 벗어나면 안 됩니다.
example을 리스트 «
"a
", "b
", "c
", "a
" »로 둔다면, example[1]은 문자열 "b
"입니다.
표기상의 편의를 위해, 여러 변수에 리스트의 항목을 한 번에 할당할 수 있습니다. 이때 할당할 변수들은 « »로 감싸고, 각 변수명을 쉼표로 구분합니다. 리스트의 크기는 할당할 변수 개수와 같아야 하며, 각 변수는 리스트의 해당 인덱스 항목 값으로 설정됩니다.
리스트의 내용이 완전히 제어되지 않을 때(예: 사용자 입력에서 가져온 리스트), 리스트 다중 할당 문법을 사용하기 전에 크기가 예상과 일치하는지 반드시 확인해야 합니다.
-
list의 크기가
3
이 아니면 failure를 반환한다. -
« a, b, c »를 list로 둔다.
append는 ordered set이 아닌 리스트의 끝에 항목을 추가하는 것입니다.
확장(extend)은 리스트가 ordered set이 아닐 때, A에 리스트 B를 확장하는 작업이다. 각 item에 대하여 B에서, append로 item을 A에 추가한다.
prepend는 ordered set이 아닌 리스트의 맨 앞에 항목을 추가하는 것입니다.
replace는 ordered set이 아닌 리스트에서 주어진 조건을 만족하는 모든 항목을 주어진 항목으로 교체하는 것이며, 해당 항목이 없다면 아무 것도 하지 않습니다.
위 정의들은 리스트가 ordered set일 때는 수정됩니다. 아래의 ordered set append, prepend, replace 참고.
insert는 리스트에, 주어진 인덱스 바로 앞(인덱스−1과 인덱스 사이)에 항목을 추가하는 것입니다. 인덱스가 0이면 prepend로 추가합니다.
remove는 리스트에서 주어진 조건을 만족하는 모든 항목을 제거하며, 해당 항목이 없다면 아무 것도 하지 않습니다.
x를 리스트 « x, y, z, x »에서 x를 제거하면, x와 동일한 모든 항목이 삭제됩니다. 리스트는 이제 « y, z »와 같습니다.
"a"로 시작하는 모든 항목을 리스트 «
"a
", "b
", "ab
", "ba
" »에서 제거하면, "a
"와
"ab
"가 삭제됩니다. 남은 리스트는 « "b
", "ba
" »입니다.
empty는 리스트의 모든 항목을 remove하는 것입니다.
리스트가 항목을 포함한다는 것은 해당 항목이 리스트에 나타나는 경우입니다. 또한, 리스트 list와 인덱스 index에 대해 "list[index] exists"라고도 표기할 수 있습니다.
인덱스 구하기는 리스트의 0부터 크기까지(끝 미포함)의 범위를 반환하는 것입니다.
반복은 리스트의 각 항목에 대해 차례로 일련의 단계를 수행하는 것으로, "각 item에 대해 list에서"와 같이 표현하고, 이후 산문에서 item에 대해 연산합니다.
clone은 리스트 list와 동일한 designation의 새로운 리스트 clone을 만든 후, 각 item에 대해 append하여 clone이 list와 같은 순서의 항목을 가지도록 합니다.
이는 "얕은 복제(shallow clone)"로, 항목 자체는 복제되지 않습니다.
original을 ordered set « "a
", "b
", "c
" »로 둔 뒤,
clone하면 새로운 ordered set clone이 생성되어, clone에서
"a
"를 "foo
"로 교체하면 « "foo
", "b
", "c
" »가 되지만,
original[0]은 여전히 문자열
"a
"입니다.
오름차순 정렬은 리스트 list와 less than 비교 알고리즘 lessThanAlgo에 대해, list와 동일한 항목을 포함하되 lessThanAlgo에 따라 각 항목이 다음 항목보다 작도록 정렬한 새 리스트 sorted를 만드는 것입니다. 동일하게 정렬되는 항목(즉, lessThanAlgo로 양방향 비교 시 모두 false인 경우)의 상대적인 순서는 list 내 원래 순서를 보장해야 합니다.
내림차순 정렬은 리스트 list와 less than 비교 알고리즘 lessThanAlgo에 대해, list와 동일한 항목을 포함하되 lessThanAlgo에 따라 각 항목이 이전 항목보다 작도록 정렬한 새 리스트 sorted를 만드는 것입니다. 동일하게 정렬되는 항목의 상대적인 순서는 list 내 원래 순서를 보장해야 합니다.
original을 리스트 « (200, "OK
"), (404, "Not Found
"), (null,
"OK
") »로 두고, a의 두 번째 항목이 b의 두 번째 항목보다 코드 유닛 기준 작다면 a가 b보다 작다고 보는 오름차순 정렬을 하면, 결과는
« (404, "Not Found
"), (200, "OK
"), (null, "OK
") »가 됩니다.
리스트 타입은 JavaScript 명세(여기서는 대문자로 List로 표기됨)에서 기원했으며, 이 명세에서 일부 정의 요소를 반복하여 참고하기 쉽도록 하고, 리스트를 다루는 확장된 어휘를 제공합니다. JavaScript가 List를 기대하는 경우, 여기 정의된 리스트를 사용할 수 있습니다. 동일한 타입입니다. [ECMA-262]
5.1.1. 스택
일부 리스트는 스택으로 지정됩니다. 스택은 리스트이지만, 관례적으로 append, prepend, remove 대신 아래의 연산을 사용합니다.
pop은 스택이 비어 있지 않으면 마지막 항목을 remove해서 반환하고, 비어 있으면 아무것도 반환하지 않습니다.
peek은 스택이 비어 있지 않으면 마지막 항목을 반환하고, 비어 있으면 아무것도 반환하지 않습니다.
스택은 리스트이지만, for each를 사용하면 안 되고, 대신 while과 pop을 조합해 사용하는 것이 적절합니다.
5.1.2. 큐
일부 리스트는 큐로 지정됩니다. 큐는 리스트이지만, 관례적으로 append, prepend, remove 대신 아래의 연산을 사용합니다.
dequeue는 큐가 비어 있지 않으면 첫 번째 항목을 remove해서 반환하고, 비어 있으면 아무것도 반환하지 않습니다.
큐는 리스트이지만, for each를 사용하면 안 되고, 대신 while과 dequeue를 조합해 사용하는 것이 적합합니다.
5.1.3. 셋
일부 리스트는 ordered set으로 지정됩니다. ordered set은 동일한 항목이 두 번 이상 포함될 수 없는 리스트입니다.
웹 플랫폼의 거의 모든 경우는 unordered set 대신 ordered set이 필요합니다. 일관된 동작을 위해서 브라우저 간에 셋의 열거 순서가 일치해야 하기 때문입니다. 순서가 중요하지 않은 경우에도 ordered set을 사용하며, 구현체는 순서가 관찰 불가함을 최적화에 활용할 수 있습니다.
create 알고리즘은 리스트 input을 받아:
append는 ordered set이 이미 해당 항목을 포함하고 있으면 아무 것도 하지 않고, 아니면 일반 리스트의 append 연산을 수행합니다.
확장(extend)은 ordered set A에 리스트 B를 확장하는 작업이다. 각 item에 대하여 B에서, append로 item을 A에 추가한다.
prepend는 ordered set이 해당 항목을 포함하고 있으면 아무 것도 하지 않고, 아니면 일반 리스트의 prepend 연산을 수행합니다.
replace는 ordered set set에서 item과 replacement가 존재하면, 둘 중 첫 번째를 replacement로 대체하고 나머지는 모두 remove합니다.
"a"를 "c"로 replace하면, ordered set « "a", "b", "c" »는 « "c", "b" »가 됩니다. « "c", "b", "a" »에서도 결과는 « "c", "b" »입니다.
ordered set set이 다른 ordered set superset의 부분집합이면(그리고 반대로 superset이 상위집합이면), 각 item에 대해 set에서 superset이 포함하고 있어야 합니다.
이는 ordered set이 자기 자신에 대해 부분집합 및 상위집합임을 내포합니다.
set A가 동일하다는 것은 set B에 대해 A가 부분집합이고 A가 상위집합인 경우입니다.
intersection(교집합)은 ordered set A와 B에 대해, 새 ordered set set을 만들고, 각 item에 대해 A에서 B가 포함하면, append로 item을 set에 추가합니다.
union(합집합)은 ordered set A와 B에 대해, A를 clone하여 set으로 만들고, 각 item에 대해 B에서 append로 item을 set에 추가합니다.
difference(차집합)은 ordered set A와 B에 대해, 새 ordered set set을 만들고, 각 item에 대해 A에서 B가 포함하지 않으면 append로 item을 set에 추가합니다.
범위 n에서 m까지(포함)는 n 이상 m 이하의 모든 정수를 오름차순으로 담은 새 ordered set을 생성합니다. 단, m이 n 이상이어야 합니다.
범위 n에서 m까지(미포함)는 n 이상 m 미만의 모든 정수를 오름차순으로 담은 새 ordered set을 생성합니다. m = n이면 빈 ordered set을 생성합니다.
5.2. 맵
ordered map(또는 단순히 "map")은, 각 항목이 tuple로 이루어진 유한한 순서의 시퀀스이며, 각 튜플은 키와 값을 가지며, 동일한 키가 두 번 이상 등장하지 않습니다. 각 튜플은 엔트리(entry)라고 합니다.
ordered set과 마찬가지로, 기본적으로 구현체 간 상호 운용성을 위해 맵도 순서를 보장해야 합니다.
리터럴 표기법으로 ordered map을 «[ ]»로 감싸고, 각 엔트리는 key → value로, 엔트리끼리는 쉼표로 구분하여 표현할 수 있습니다.
example을 ordered map «[
"a
" → `x
`, "b
" → `y
` ]»로 두면,
example["a
"]는 byte sequence `x
`가 됩니다.
엔트리 값 가져오기는 ordered map map에서 key key와 선택적 default에 대해 다음과 같습니다:
-
map이 key를 포함하지 않고, default가 주어졌다면 default를 반환합니다.
-
Assert: map이 key를 포함해야 합니다.
엔트리 값 가져오기는 대괄호 인덱스 표기법으로도 표현할 수 있습니다. map 뒤에 대괄호 안에 키를 넣어서 사용합니다. 기본값을 주려면 with default 문구 뒤에 기본값을 추가합니다.
map["test
"]
exists이면
map["test
"]를 반환합니다.
example을 ordered map
«[ "a
" → "x
", "b
" → "y
" ]»로 두면,
example["a
"]는 example["a
"] with default
"z
"와 같으며, 결과는 "x
"입니다. example["c
"]는 assert에 걸립니다.
example["c
"] with default "z
"는 "z
"를 반환합니다.
엔트리 값 설정은 ordered map에 대해, 해당 key가 이미 존재하면 값을 갱신하고, 없으면 새 엔트리를 맵 끝에 추가합니다. 이때 map, key, value에 대해 "set map[key] to value"라고도 표현할 수 있습니다.
엔트리 제거는 ordered map에서 주어진 조건을 만족하는 모든 엔트리를 삭제하고, 해당하는 것이 없으면 아무 것도 하지 않습니다. 조건이 특정 key일 경우, map, key에 대해 "remove map[key]"로도 표현할 수 있습니다.
clear는 ordered map의 모든 엔트리를 삭제하는 것입니다.
ordered map이 주어진 key를 가진 엔트리를 포함한다는 것은, 해당 key를 가진 엔트리가 존재함을 의미합니다. map, key에 대해 "map[key] exists"로도 표기합니다.
키 얻기는 ordered map의 각 엔트리의 키로 이루어진 새로운 ordered set을 반환합니다.
값 얻기는 ordered map의 각 엔트리의 값으로 이루어진 새로운 리스트를 반환합니다.
ordered map의 크기는 키 얻기 결과의 크기입니다.
ordered map이 비어 있다는 것은 크기가 0일 때입니다.
반복은 ordered map의 각 엔트리에 대해 "각 key → value of map"와 같은 방식으로 사용할 수 있으며, 이후 산문에서 key와 value로 연산합니다.
clone은 ordered map map에 대해, 새로운 ordered map clone을 만든 후, 각 key → value에 대해 set clone[key] to value로 복제합니다.
이는 "얕은 복제(shallow clone)"로, 키나 값 자체는 복제하지 않습니다.
original을 ordered map «[
"a
" → «1, 2, 3», "b
" → «» ]»로 두고 clone하면 새로운 ordered map clone이 생성됩니다. set clone["a
"] to «-1, -2, -3»을 하면 결과는
«[ "a
" → «-1, -2, -3», "b
" → «» ]»가 되고 original은 변하지 않습니다. 하지만 append 4 to
clone["b
"]를 하면, clone과 original의 해당 값이 모두 변경됩니다(같은 리스트를 참조하기 때문).
오름차순 정렬은 map map과 less than 알고리즘 lessThanAlgo에 대해, map과 동일한 엔트리를 포함하되 lessThanAlgo에 따라 각 엔트리가 다음 엔트리보다 작도록 정렬한 새 map sorted를 만드는 것입니다. 동일하게 정렬되는 엔트리(즉, lessThanAlgo로 쌍방 비교 모두 false인 경우)의 상대적 순서는 map 내 원래 순서를 보장해야 합니다.
내림차순 정렬은 map map과 less than 알고리즘 lessThanAlgo에 대해, map과 동일한 엔트리를 포함하되 lessThanAlgo에 따라 각 엔트리가 이전 엔트리보다 작도록 정렬한 새 map sorted를 만드는 것입니다. 동일하게 정렬되는 엔트리의 상대적 순서는 map 내 원래 순서를 보장해야 합니다.
5.3. 구조체
구조체는 유한한 개수의 항목으로 구성된 명세 타입이며, 각 항목은 고유하고 불변의 이름을 가집니다. 항목은 정의된 타입의 값을 담습니다.
email은 구조체 예시로, local part(문자열)와 host(host)로 구성됩니다.
임의의 알고리즘에서 이 정의를 다음과 같이 활용할 수 있습니다:
- email을 local part가 "
hostmaster
"이고 host가infra.example
인 email로 둔다. - …
5.3.1. 튜플
튜플은 구조체의 일종으로, 항목이 순서를 가집니다. 표기상의 편의를 위해, 소괄호로 감싸고 각 항목을 쉼표로 구분하여 튜플을 표현할 수 있습니다. 이 표기를 사용할 경우 이름은 문맥상 명확해야 합니다. 튜플 이름을 먼저 명시함으로써 가능합니다. 대괄호를 사용해 0부터 시작하는 인덱스로 튜플을 인덱싱할 수 있으며, 인덱스는 범위를 벗어나면 안 됩니다.
status는 튜플의 예시로, code(숫자)와 text(byte sequence)로 구성됩니다.
튜플 사용을 보여주는 임의의 알고리즘은 다음과 같습니다:
- statusInstance를 status (200, `
OK
`)로 둔다. - statusInstance를 (301, `
FOO BAR
`)로 설정한다. - statusInstance의 code가 404이면 …
마지막 단계는 "만약 statusInstance[0]이 404이면 …"처럼 쓸 수도 있습니다. 튜플의 이름이 명시적으로 정의되어 있지 않을 때는 이 방식이 더 나을 수 있습니다.
모든 구조체가 튜플이 아닌 것은 의도된 설계입니다. Infra 표준을 사용하는 문서는, 종속 명세가 사용하는 리터럴 문법을 깨뜨리지 않고 구조체에 새로운 이름을 추가할 유연성이 필요할 수 있습니다. 이럴 때 튜플은 적합하지 않습니다.
6. JSON
이 절의 알고리즘에서 사용하는 관례는 JavaScript 명세의 관례를 따릅니다. [ECMA-262]
JSON 문자열을 JavaScript 값으로 파싱하려면, 문자열 string이 주어졌을 때:
-
? Call(%JSON.parse%, undefined, « string »)을 반환한다.
JSON 바이트를 JavaScript 값으로 파싱하려면, 바이트 시퀀스 bytes가 주어졌을 때:
-
string을 UTF-8 디코드로 bytes를 변환한 결과로 둔다. [ENCODING]
-
JSON 문자열을 JavaScript 값으로 파싱을 string에 대해 실행한 결과를 반환한다.
JavaScript 값을 JSON 문자열로 직렬화하려면, JavaScript 값 value가 주어졌을 때:
-
result를 ? Call(%JSON.stringify%, undefined, « value »)로 둔다.
%JSON.stringify%에 추가 인자를 전달하지 않으므로, 결과 문자열에는 공백이 삽입되지 않습니다.
-
result가 undefined이면
TypeError
를 throw한다.value가 undefined이거나 함수 등 JSON 표현이 불가능한 값이면 이런 일이 발생할 수 있습니다.
-
result를 반환한다.
JavaScript 값을 JSON 바이트로 직렬화하려면, JavaScript 값 value가 주어졌을 때:
-
string을 JavaScript 값을 JSON 문자열로 직렬화한 결과로 둔다(value에 대해).
-
UTF-8 인코드로 string을 변환한 결과를 반환한다. [ENCODING]
위 연산들은 JavaScript 값 자체에 직접 동작합니다. 특히 이는 관련 객체나 배열이 특정 JavaScript realm에 묶여 있음을 의미합니다. 표준에서는 JSON과 realm에 독립적인 map, list, 문자열, 불리언, 숫자, null 등의 변환이 더 편리한 경우가 많습니다.
JSON 문자열을 Infra 값으로 파싱하려면, 문자열 string이 주어졌을 때:
-
jsValue를 ? Call(%JSON.parse%, undefined, « string »)로 둔다.
-
JSON 파생 JavaScript 값을 Infra 값으로 변환을 jsValue에 대해 실행한 결과를 반환한다.
JSON 바이트를 Infra 값으로 파싱하려면, 바이트 시퀀스 bytes가 주어졌을 때:
-
string을 UTF-8 디코드로 bytes를 변환한 결과로 둔다. [ENCODING]
-
JSON 문자열을 Infra 값으로 파싱을 string에 대해 실행한 결과를 반환한다.
JSON 파생 JavaScript 값을 Infra 값으로 변환하려면, JavaScript 값 jsValue가 주어졌을 때:
-
jsValue가
null 이거나, Boolean이거나, String이거나, Number이면, jsValue를 반환한다. -
IsArray(jsValue)가 true이면:
-
result를 빈 list로 둔다.
-
각 index에 대해 범위 0에서 length − 1까지(포함) 반복:
-
indexName을 ! ToString(index)로 둔다.
-
jsValueAtIndex를 ! Get(jsValue, indexName)로 둔다.
-
infraValueAtIndex를 JSON 파생 JavaScript 값을 Infra 값으로 변환에 jsValueAtIndex를 넘겨 얻은 결과로 둔다.
-
infraValueAtIndex를 result에 append한다.
-
-
result를 반환한다.
-
-
result를 빈 ordered map으로 둔다.
-
각 key에 대해 ! jsValue.[[OwnPropertyKeys]]() 반복:
-
jsValueAtKey를 ! Get(jsValue, key)로 둔다.
-
infraValueAtKey를 JSON 파생 JavaScript 값을 Infra 값으로 변환에 jsValueAtKey를 넘겨 얻은 결과로 둔다.
-
result[key]에 infraValueAtKey를 set한다.
-
-
result를 반환한다.
Infra 값을 JSON 문자열로 직렬화하려면, 문자열, 불리언, 숫자, null, 리스트, 문자열 키를 가진 map value가 주어졌을 때:
-
jsValue를 Infra 값을 JSON 호환 JavaScript 값으로 변환에 value를 넘겨 얻은 결과로 둔다.
-
! Call(%JSON.stringify%, undefined, « jsValue ») 결과를 반환한다.
%JSON.stringify%에 추가 인자를 전달하지 않으므로, 결과 문자열에는 공백이 삽입되지 않습니다.
Infra 값을 JSON 바이트로 직렬화하려면, 문자열, 불리언, 숫자, null, 리스트, 문자열 키를 가진 map value가 주어졌을 때:
-
string을 Infra 값을 JSON 문자열로 직렬화에 value를 넘긴 결과로 둔다.
-
UTF-8 인코드로 string을 변환한 결과를 반환한다. [ENCODING]
Infra 값을 JSON 호환 JavaScript 값으로 변환하려면, value가 주어졌을 때:
-
value가 리스트이면:
-
jsValue를 ! ArrayCreate(0)로 둔다.
-
i를 0으로 둔다.
-
각 listItem에 대해 value에서 반복:
-
listItemJSValue를 Infra 값을 JSON 호환 JavaScript 값으로 변환에 listItem을 넘긴 결과로 둔다.
-
! CreateDataPropertyOrThrow(jsValue, ! ToString(i), listItemJSValue)를 수행한다.
-
i를 i + 1로 설정한다.
-
-
jsValue를 반환한다.
-
-
Assert: value는 map이어야 한다.
-
jsValue를 ! OrdinaryObjectCreate(null)로 둔다.
-
각 mapKey → mapValue에 대해 value에서 반복:
-
Assert: mapKey는 문자열이어야 한다.
-
mapValueJSValue를 Infra 값을 JSON 호환 JavaScript 값으로 변환에 mapValue를 넘긴 결과로 둔다.
-
! CreateDataPropertyOrThrow(jsValue, mapKey, mapValueJSValue)를 수행한다.
-
-
jsValue를 반환한다.
명세에서 JavaScript 값을 직접 조작하는 것은 거의 적합하지 않으므로, 이 알고리즘 대신 Infra 값을 JSON 문자열로 직렬화나 Infra 값을 JSON 바이트로 직렬화 사용을 권장합니다. 만약 Infra 값을 JSON 호환 JavaScript 값으로 변환이 꼭 필요하다면, 이슈를 등록하여 논의해 주세요.
7. 관대한 base64
forgiving-base64 인코딩은 byte sequence data가 주어졌을 때, RFC 4648의 section 4에 정의된 base64 알고리즘을 data에 적용하여 결과를 반환한다. [RFC4648]
이 명칭은 forgiving-base64 encode와 forgiving-base64 decode 간의 대칭성을 위해 붙여졌으며, forgiving-base64 decode는 RFC와 달리 특정 입력에 대한 오류 처리를 정의한다는 점에서 다르다.
forgiving-base64 디코딩은 string data가 주어졌을 때 다음 절차를 따른다:
-
data에서 모든 ASCII 공백을 제거한다.
-
data의 코드 포인트 길이를 4로 나눈 나머지가 0일 경우:
-
data가 하나 또는 두 개의 U+003D(=) 코드 포인트로 끝나면, 이들을 data에서 제거한다.
-
-
data의 코드 포인트 길이를 4로 나눈 나머지가 1이면, 실패를 반환한다.
-
data에 아래 중 하나가 아닌 코드 포인트가 있으면
- U+002B (+)
- U+002F (/)
- ASCII 영숫자
실패를 반환한다.
-
output을 빈 byte sequence로 둔다.
-
buffer를 비트 추가가 가능한 빈 버퍼로 둔다.
-
position을 위치 변수로 data의 시작을 가리키게 둔다.
-
position이 data의 끝을 지나지 않을 때 반복:
-
buffer가 비어 있지 않다면, 12 또는 18비트가 들어있다. 12비트라면 마지막 4비트를 버리고 남은 8비트를 8비트 big-endian 숫자로 해석한다. 18비트라면 마지막 2비트를 버리고 남은 16비트를 두 개의 8비트 big-endian 숫자로 해석한다. 이 숫자(들)에 해당하는 바이트 1~2개를 output에 같은 순서로 추가한다.
버려진 비트 때문에, 예를 들어 "
YQ
"와 "YR
" 모두 `a
`를 반환하게 된다. -
output을 반환한다.
8. 네임스페이스
HTML 네임스페이스는
"http://www.w3.org/1999/xhtml
"이다.
MathML 네임스페이스는
"http://www.w3.org/1998/Math/MathML
"이다.
SVG 네임스페이스는
"http://www.w3.org/2000/svg
"이다.
XLink 네임스페이스는
"http://www.w3.org/1999/xlink
"이다.
XML 네임스페이스는
"http://www.w3.org/XML/1998/namespace
"이다.
XMLNS 네임스페이스는
"http://www.w3.org/2000/xmlns/
"이다.
감사의 글
진심으로 감사드립니다: Addison Phillips, Andreu Botella, Aryeh Gregor, Ben Kelly, Chris Rebert, Daniel Ehrenberg, Dominic Farolino, Gabriel Pivovarov, Ian Hickson, Jakob Ackermann, Jake Archibald, Jeff Hodges, Jeffrey Yasskin, Jungkee Song, Leonid Vasilyev, Maciej Stachowiak, Malika Aubakirova, Martin Thomson, Michael™ Smith, Mike West, Mike Taylor, Ms2ger, Pavel "Al Arz" Kurochkin, Philip Jägenstedt, Rashaun "Snuggs" Stovall, Sergey Shekyan, Simon Pieters, Tab Atkins, Tobie Langel, triple-underscore, Wolf Lammen, 그리고 Xue Fuqiao 여러분 모두 멋진 분들입니다!
이 표준은 Anne van Kesteren (Apple, annevk@annevk.nl)와 Domenic Denicola (Google, d@domenic.me)가 작성했습니다.
지적 재산권
Copyright © WHATWG (Apple, Google, Mozilla, Microsoft). 이 저작물은 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스에 따라 라이선스가 부여됩니다. 이 중 일부가 소스 코드에 포함되는 경우, 해당 부분은 BSD 3-Clause License로 라이선스됩니다.
이 문서는 현행 표준입니다. 특허 검토 버전이 필요하신 분은 현행 표준 검토초안을 참고해 주세요.