구현
14. 코어 변경 범위
14.1 레이아웃 파일 안에서 끝나는 것
선언 셀 스캔, 행 키 인식, 경계 판정, 메모 컬럼, 경로 파싱([] · [n] · .), 접힌 타입
표현의 분해(기존 타입 칸 + 디테일 칸으로), 괄호 메타의 Field.Constraints 대입, 멀티
로우의 레코드 분할과 원소 구성, 예약 컬럼의 인식과 거부 — 전부 [TabbitLayout] 파서 파일
하나입니다. 그 파일을 지우면 빌드도 코어도 흔적이 없어야 합니다.
enum
alias는 여기가 아니었습니다. 이 목록은 처음에 「alias의 라벨 환원」도 레이아웃 안의 일로 적었는데, 구현해 보니 모델에 별칭 의 자리가 없었습니다 — 라벨이 가진 것은Name(Pascal)과RawName(선언 표기)뿐이고, 데이터 셀이 통하는 표기는 그 둘과 숫자 셋이었습니다. 그래서 레이아웃이 할 수 있는 유일한 환원은 별칭을RawName에 밀어 넣는 것인데, 그 필드는 이름 규약 보고가 시트를 판정하는 표기라 (ModelCooker.Naming의NameKind.Label) 규약 검사가 라벨의 실제 표기가 아니라 별칭을 판정하게 됩니다.Enum.Label.Alias를 두고FindLabelByName이 실제 이름 둘 뒤에서 찾도록 했습니다 — 별칭이 어느 라벨의 제 이름이 답할 셀을 가져가지 못하는 순서입니다. 이 자리가 DSL의(alias=)가 「담지 않음」에서 벗어나는 자리이기도 합니다.
14.2 코어에 필요한 변경 — 0건
수식 오류 보고의 지연은 이미 되어 있습니다. 이 문서를 쓸 때는 남은 일로 적었는데,
그 사이에 수식 오류가 별도 스펙으로 구현되었습니다 — 임포터는
RawCell.FormulaError에 기록만 하고(XlsxImporter), 정책은 레이아웃이 필드로 읽는
셀에서 ReadCell이 적용합니다. 메모 컬럼을 자유 공간으로 선언한 이 설계의 전제 조건이
선행 작업 없이 이미 성립합니다.
남은 코어 변경은 2건입니다. 필드 변형의 recipe 항목(Variants, §3.6) — 어느
레이아웃인지 몰라도 찾을 수 있어야 하는 설정이므로 정식 프로퍼티의 자리입니다 — 과
Enum.Label.Alias(§14.1의 주석)입니다. 둘 다 레이아웃 이름이 코어에 들어가지 않고,
둘 다 다른 레이아웃과 DSL에도 그대로 유효합니다.
코어에 걸리지만 이 레이아웃의 일이 아닌 것 1건. 셀 병합의 경고는 병합 사각형이 로우 모델까지 와야 성립하고, 그것은 임포터 3종의 작업입니다(§3.2). 이 레이아웃은 그것 없이 완성되고, 병합 사고는 그때까지 증상으로 걸립니다.
15. 게이트
| # | 무엇 | 기준 |
|---|---|---|
| 1 | 표기 동치 — 같은 데이터를 기존 tabbit 레이아웃과 이 레이아웃으로 적은 픽스처 2벌 | 모델이 동일하고 골든 산출물이 한 바이트도 다르지 않아야 합니다. 행 키 레이아웃 §10의 방식이고, 파서 결함이 곧 산출물 차이로 드러나므로 픽스처 신설보다 강한 검증입니다. 교체 단계(§16의 6)에서 이 게이트가 픽스처 전체로 확장됩니다 — 이관한 모든 시트에서 골든 무변경입니다 |
| 2 | 멀티 로우 동치 — 같은 배열 데이터를 [n] 고정 칸(+TrimTrailingArrayElements)과 [] 멀티 로우로 적은 픽스처 2벌 | .tcb 바이트 동일. 합성 값 타입과 STRUCT DSL이 세운 게이트 방식 그대로입니다 |
| 3 | 기존 무변경 — 기존 픽스처 전체 | 변환 골든 무변경(33초 게이트). 이 작업은 생성기 · 템플릿 · 와이어에 닿지 않으므로 골든이 움직이면 그 자체가 결함입니다 |
| 4 | §11의 취약 사례 | 점검표의 각 행이 오류 픽스처 하나 — 지목하는 셀과 메시지까지 검사합니다 |
16. 구현 순서
| 순서 | 무엇 | 게이트 |
|---|---|---|
| 0 | 이 스펙의 확정 — 됨. 레이아웃 id는 tabbit(기존 레이아웃 폐기 · 승계, 6번), role 메타 키는 통일(등재는 괄호 메타와 같은 3번), 셀 병합은 경고(§3.2) | — |
| 1 | 파서 뼈대 — 선언 셀 · 행 키 · 경계 · 메모 컬럼 · 경로([n] · .) · 접힌 타입 표현 · enum · const. 새 파서는 임시 id primary로 등록합니다(메시지 접두어와 카탈로그 파일 이름도 같이) — tabbit 승계는 6번입니다 | 게이트 3 · 파서 단위 테스트. 게이트 1은 워크북 픽스처가 필요하므로 그 다음 |
| 1′ | 표기 동치 픽스처 — 같은 데이터를 두 표기로 적은 워크북 짝. FixtureGen이 생성합니다 | 게이트 1 |
| 2 | 멀티 로우 — 됨. [] 경로, 레코드 분할, §6의 규칙 전부. §6.3의 거부 셋(중첩 멀티 로우 · 번호와 []의 혼용 · 행마다 셀 배열)은 이름을 대고 거부합니다 | 게이트 2 통과 — 같은 배열을 [n]과 []로 적은 .tcb가 바이트 동일 |
| 3 | 괄호 메타 — 됨. 첫 (부터 메타, 키 사전과 검증은 DSL의 것을 그대로 씁니다(SchemaMetadata). role 키 3종을 등재하면서 text가 값을 담게 하고 namespace를 신설했습니다 | 게이트 3 무변경 + 양쪽 표기의 단위 테스트 |
| 3′ | 필드 변형 — 됨. :variant 행, recipe Variants, CLI --variant. 헤더는 기본 변형 컬럼에 한 번 적고 다른 변형 컬럼은 비웁니다 — 다르게 적으면 거부합니다. 키 컬럼과 그룹 컬럼의 변형은 거부합니다 | 게이트 3 무변경 + 같은 시트를 변형별로 읽은 단위 테스트 |
| 4 | 예약 컬럼의 거부 자리 — 됨. $type · $key · $value를 :field에서 인식하고, 각자 무엇을 기다리는지와 함께 거부합니다 | 오류 테스트 3건 |
| 4′ | key 메타 — 됨. 단일 지정이 기본 인덱스를 첫 컬럼에서 옮깁니다. 컬럼은 그대로이므로 와이어도 그대로이고, 멀티 로우의 레코드 경계도 키와 함께 움직입니다. 복합 키는 조회 표면이 바뀌므로 8번입니다 | 골든 무변경 |
| 5 | 수식 오류 지연 — 이미 됨(수식 오류). 이 순서에 남길 것이 없습니 다(§14.2) | — |
| 6 | 교체와 이관 — 됨. 기존 tabbit 파서 · 메시지 · 카탈로그를 지우고 새 파서가 그 id를 승계했습니다. 픽스처는 FixtureGen의 이미터를 새 표기로 바꿔 한 번에 옮겼고, FoldSerialFields는 존재 이유가 사라져 제거했습니다(§16.7) | 전체 스위트 1,683개 통과. 골든은 승인된 범주만 움직였습니다 — _array 접미 제거 · rawName의 표기 · 0-기반 원소 번호 · 셀 위치 |
| 7 | 작성 문서 — 됨. 시트 작성을 다시 썼습니다. 표기를 서술하던 절 전부와 옛 표기의 스크린샷 11개를 대체하고, 예시는 spec 쪽과 같은 생성기로 만든 엑셀 격자 SVG입니다 | — |
| 8 | 복합 키 — 문법 · 모델 · 유일성 — 됨. key="a,b; c,d"가 읽히고, 성분마다 인덱스의 기존 규칙이 적용되며, 조합의 유일성을 검사합니다. 타깃은 SupportsCompositeKeys로 옵트인하고 그때까지 이름을 대고 거부합니다 | 골든 무변경 — 와이어도 컬럼 순서도 움직이지 않습니다 |
| 8′ | 복합 키 조회 — 됨. FindByStageAndSlot(a, b)를 모든 언어에. 키마다 조회 하나이고, 단일 키는 지금 내던 것을 그대로 냅니다. 스펙이 정해 두고 구현이 없던 두 거부 — 복합 기본 키인 테이블은 foreign의 대상이 될 수 없고 멀티 로우도 될 수 없다 — 도 여기서 닫았습니다 | 기존 골든 24개 무변경, 새 시나리오 composite-key가 17개 타깃 전부. 언어 게이트 14개 — 컴파일 9개 · 되읽기 5개(§16.8) |
1~7은 형식도 생성 코드도 건드리지 않습니다 — 골든이 판정 기준인 변경들입니다. 8′은 모든 언어의 조회 표면이 움직이는 별도 등급이고, 와이어는 그때도 무변경입니다.
병존 기간에 기존 쪽을 건드리지 않는 이유. 레지스트리가 id 중복을
거부하므로(LayoutRegistry.Discover) 한쪽은 임시 id여야 하고, 그 임시 이름을 새
파서가 답니다. 기존 tabbit은 SheetSourceRecipe.Layout의 기본값이라 레이아웃을
명시하지 않은 픽스처 전부가 그것으로 읽힙니다 — 기존 쪽의 id를 바꾸면 그 기본값과 메시지
카탈로그까지 함께 움직이고, 스위트 전체가 개발 기간 내내 그 위에 놓입니다. 새 픽스처만
primary를 명시하면 기존 픽스처는 한 줄도 바뀌지 않습니다. 임시 id에 하이픈은 쓸 수
없습니다 — 메시지 id의 첫 마디가 [a-z0-9]+여야 한다는 카탈로그 게이트가 있고, 그
게이트가 맞습니다.
16.1 1단계에서 드러난 것
| # | 무엇 |
|---|---|
| 1 | 뒤 원소의 빈 타입 칸은 「허용」이 아니라 「물려받음」입니다. §4.3은 원소 0에만 타입을 적고 나머지를 비운다고 정하는데, 모델은 ValidateRecordGroup에서 원소마다 같은 타입을 요구합니다 — 파일이 멤버를 컬럼 하나로 싣고 타입을 하나 적기 때문입니다. 그래서 파서가 원소 0의 타입을 뒤 원소에 실제로 복사해야 하고, 비워 두면 「멤버가 두 타입」으로 거부됩니다. 복사하지 않는 유일한 자리는 (나) DSL struct 그룹의 2번째 이후 멤버입니다 — 그쪽은 물려받을 원소 0이 없고, 선언이 답을 주므로 기다리는 것이 맞습니다 |
| 2 | key 메타는 별도 단계입니다(4′). 모델은 기본 인덱스를 Fields[0]으로 표현하는데, §3.5는 「와이어는 움직이지 않는다」고 정합니다. 첫 컬럼이 아닌 컬럼을 기본 인덱스로 만들면서 컬럼 순서를 그대로 두는 표현이 모델에 아직 없으므로, 1단계는 이름을 대고 거부합니다 — 조용히 무시하면 작성자가 고르지 않은 컬럼으로 인덱스된 테이블이 나옵니다 |
| 3 | 골든에는 원소 번호가 나오지 않습니다. 생성 코드가 내는 것은 그룹 이름과 멤버 이름(skill · step · pos · x)이므로, 게이트 1의 픽스처 짝은 그룹·멤버 이름만 맞추면 되고 번호의 기준(옛 표기의 1-기반, 새 표기의 0-기반)은 산출물에 영향이 없습니다 |
| 4 | enum alias에 모델의 자리가 필요했습니다. §14.1의 주석에 적었습니다 — 레이아웃 안에서 끝나지 않는 유일한 항목이었고, Enum.Label.Alias가 그 자리입니다 |
| 5 | 옛 표기와 갈리는 두 가지가 게이트 1의 픽스처에서 드러났습니다. 옛 표기는 테이블의 사각형에 최소 폭 3컬럼을 요구하므로 2컬럼 테이블이 빈 이름 칸을 읽고, 뒤 원소의 타입 칸도 채워야 합니다. 새 표기는 둘 다 없습니다 — 엔티티의 폭은 마커 열이 정하고, 타입은 원소 0에만 적습니다(§4.3). 픽스처가 두 표기를 각각 적는 자리가 그 둘입니다 |
16.2 2단계에서 드러난 것
| # | 무엇 |
|---|---|
| 1 | 멀티 로우 테이블은 절단을 강제합니다. 가변 길이가 와이어에 나오는 경로는 「뒤에서 절단」 하나이고(Table.ElementCountIn), 그 스위치는 테이블 단위입니다(Table.TrimTrailingArrayElements). 그래서 []가 하나라도 있으면 파서가 그것을 켭니다 — named-range 가 자기 시트 규칙으로 항상 자르는 것과 같은 자리입니다. 귀결이 하나 있습니다: 같은 테이블의 [n] 고정 칸 그룹도 함께 잘립니다(§8.6.1이 섞어 쓰는 것을 허용하므로 실재하는 조합입니다). recipe가 요청하지 않은 동작이지만, 그룹 단위 스위치가 모델에 없고 멀티 로우는 그것 없이 성립하지 않습니다 |
| 2 | 시트의 컬럼 하나가 모델의 원소 컬럼 여러 개가 됩니다. 시트는 멤버당 컬럼 하나인데 모델의 배열 길이는 컬럼 수이므로, 파서가 가장 긴 레코드만큼 원소 컬럼을 합성합니다. 순서는 원소 우선(reward[0].id · reward[0].count · reward[1].id · …)이고 그룹은 첫 멤버 컬럼의 자리에서 펼쳐집니다 — 같은 데이터를 [n]로 적은 시트가 내는 순서와 같아야 와이어가 같기 때문입니다. 원소를 하나도 채우지 않은 그룹도 컬럼 하나는 갖습니다. 0개면 멤버가 모델에서 사라지고, 「어느 행도 없다」는 빈 배열이지 없는 필드가 아닙니다 |
| 3 | 게이트 2의 대조군은 옵셔널 멤버여야 합니다. [n] 쪽에서 레코드가 닿지 않는 원소는 「값 없음」으로 적어야 절단이 세어 주고, 값 없음은 ? 컬럼의 -입니다. 그래서 짝의 양쪽 모두 멤버가 int?입니다 — 한쪽만 옵셔널이면 presence 비트맵이 갈려 바이트가 달라집니다. 기존 record-trim 픽스처가 같은 이유로 같은 선택을 해 두었습니다 |
| 4 | 연장 행의 #와 첫 행의 #를 가르려면 # 행이 남아 있어야 합니다(규칙 8). 1단계는 # 행을 읽는 목록에서 아예 뺐는데, 그러면 레코드의 첫 행이 빠진 뒤 그 연장 행들이 위 레코드에 붙습니다. 표시만 하고 목록에 남깁니다 |
16.3 6단계 이관의 규모 — 실측
규칙으로 옮길 수 없는 자리는 「이름의 숫자」입니다. 새 레이아웃은 FoldSerialFields를
읽지 않으므로(§5) Text1 · Text2는 text[0] · text[1]로 적어야 배열이 됩니다. 그런데
접기가 켜져 있었는지는 recipe가 아는 것이고 픽스처 생성기는 모릅니다. 무조건 옮기면
접기가 꺼져 있던 테이블의 필드 2개가 배열 1개가 되고, 그것이 이 도구가 없애려는 조용한 형태
변경입니다.
판단이 필요한 범위는 좁습니다. 접기를 켠 레시피는 94개 중 33개인데, 그 33개가 읽는 워크북은 6개뿐입니다.
| 워크북 | 접기에 걸리는 이름 |
|---|---|
core | TextEn1 · TextEn2 · TextKo1 · TextKo2 · Slot1 · Slot2 |
nested | Tag1 · Tag2 |
nullable-elements | Tag1 · Tag2 · Tag3 |
record-trim | Tag1 · Tag2 · Tag3 |
serial-ref | Slot1 · Slot2 · Tier1 · Tier2 |
member-array | 없음 — 숫자가 점 뒤에 있어 경로 규칙으로 옮겨집니다 |
점이 든 이름(Slot1.Id)은 FoldSerialFields와 무관하게 경로로 접히므로 slot[0].id로
기계적으로 옮겨집니다. 판단이 필요한 것은 점 없는 숫자 이름뿐입니다.
그런데 같은 워크북을 접기 없이 읽는 레시피가 있습니다.
| 레시피 | 워크북 | 숫자 이름이 걸리는가 |
|---|---|---|
workbook-excluded | core | 아니오 — 그 워크북을 제외하므로 읽는 것이 없습니다 |
data-file-case · html-row-cap | core | 예. 이 둘의 관심사는 파일 이름과 HTML 쪽 수이지만, 골든에는 필드 2개의 형태가 실려 있습니다 |
nullable-elements-binary | nullable-elements | 예 |
core 하나가 25개 레시피에서는 배열로, 2개에서는 필드 둘로 읽히고 있습니다. 새 표기에는
그 스위치가 없습니다 — 배열인지는 표기가 말하므로, 워크북 하나가 두 읽기를 겸할 수 없습니다.
그래서 6단계는 이 두 자리에 답해야 합니다: 워크북을 갈라 두 벌로 두는가, 그 레시피들의 골든을
새 형태로 재기록하는가.
기존 픽스처의 잠재 불일치 하나가 여기서 드러났습니다.
nullable-elements-binary의 주석은 「같은 워크북을 파일로 쓴 것」이라고 적고 있는데,nullable-elements는 접기를 켜고 그것은 켜지 않습니다. 지금 두 레시피는 같은 워크북의Tag1·Tag2·Tag3을 배열 하나와 필드 셋으로 각각 읽고 있습니다. 새 표기로 옮기면 그 차이가 표기의 차이가 되므로 선택이 드러납니다 — 옮기는 값 중 하나입니다.
게이트는 전체 골든이고 검증 한 바퀴가 15분 53초입니다.
16.4 key 메타와 복합 키 조회는 한 작업입니다 — 실측
4′와 8은 따로 적혀 있지만 같은 모델 변경을 기다립니다. 둘 다 「기본 인덱스는 첫 컬럼이다」를 풀어야 하고, 그것이 풀리기 전에는 어느 쪽도 반쯤 할 수 없습니다.
모델이 그 전제를 이미 한곳에 모아 두었습니다. Table.PrimaryIndexField의 주석이 그
사정을 적고 있습니다 — 「첫 번째 것. 모든 레이아웃이 첫 컬럼을 기본 인덱스로 표시하고,
키가 필요한 자리들이 각자 Fields[0]에 이유를 주석으로 달고 있었다」. 그래서 옮길 자리가
어디인지가 분명합니다.
| 무엇 | 얼마 |
|---|---|
PrimaryIndexField로 옮겨야 하는 Fields[0] 직접 참조 | 14곳 — 검증(참조 해석 3곳) · HTML 생성기 · 히스토리 지문 · SheetSchema · 검증 파이프라인의 셀 위치 추적 2곳 · SchemaView 등 |
| 조회 표면을 내는 템플릿 | 17개 — 언어마다 하나씩과 Unreal 둘 |
| 골든 | 움직입니다. 조회가 다인자가 되는 테이블의 생성 코드가 바뀝니다 |
순서가 정해집니다. ① Fields[0]을 PrimaryIndexField로 모으고(동작 무변경, 골든
무변경), ② 그 프로퍼티가 첫 컬럼이 아닌 컬럼을 가리킬 수 있게 하고(key= 단일 지정이
여기서 성립합니다 — 와이어는 그대로이고 컬럼 순서도 그대로입니다), ③ 키를 목록으로
확장하며 17개 템플릿의 조회를 다인자로 엽니다.
①과 ②는 됐습니다. 레이아웃 밖의 Fields[0] 8곳이 PrimaryIndexField로 모였고 —
골든이 한 바이트도 움직이지 않았습니다 — 모델이 PrimaryIndexName으로 키의 이름을 들게
되어 key= 단일 지정이 성립합니다. 남은 것은 ③이고, 그것이 8번입니다.
①은 골든이 판정 기준인 리팩터링이고, ③이 적합성 코퍼스를 전 언어로 다시 돌려야 하는 단계입니다. 그래서 4′는 ②까지이고 8은 ③입니다 — 지금 순서표의 두 항목이 실제로는 한 작업의 두 단계입니다.
16.5 6단계를 막는 것 둘 — 착수해 보고 되돌린 기록
교체 자체는 깨끗합니다. 기존 파서 · 메시지 · 카탈로그 5개를 지우고 새 파서에 tabbit을
물려주는 것까지 경고 0 오류 0으로 컴파일됩니다 — 기존 파서를 참조하는 자리가 다른
레이아웃 메시지의 주석 2줄뿐이기 때문입니다. 막히는 것은 픽스처 쪽이고, 둘은 노력이 아니라
설계의 공백입니다.
| # | 막는 것 |
|---|---|
| 1 | 새 표기에 「배열의 배열」의 자리가 없었습니다 — 정했습니다. grid[0][1]이고, 대괄호가 이어지면 레벨이 하나 더 생기며 안쪽은 이름이 없습니다(§5). 이름 없는 레벨은 대괄호로만 생기므로 grid.[1]은 오류입니다 — 한 형태를 적는 방법이 둘이 되지 않게 하기 위해서입니다. 옛 Grid1.2가 같은 것이었습니다 |
| 2 | member-array.xlsx는 생성기가 만들지 않습 니다. 최초 커밋부터 있는 손으로 만든 워크북입니다. 다른 픽스처는 FixtureGen을 고치면 함께 옮겨지는데 이것은 Excel로 고치거나 생성기에 새로 넣어야 합니다 |
그 밖에 옮기면서 마주칠 자리로 확인해 둔 것 2가지입니다.
- 나란히 놓은 엔티티의 간격.
row-sets픽스처가column + spec.Fields.Count + 1로 엔티티를 옆에 붙이는데, 새 표기는 마커 열이 하나 더 있으므로 그 계산이+2가 됩니다. - 점이 든 숫자 이름은 기계적으로 옮겨집니다 —
Slot1.Id는slot[0].id이고, 번호가 1-기반에서 0-기반으로 바뀌는 것뿐입니다. 판단이 필요한 것은 §16.3이 적은 점 없는 숫자 이름입니다.
착수 순서. ① §5에 배열의 배열의 표기를 정하고 — 됐습니다 — ② member-array를
생성기로 옮기고, ③ 그다음에 교체와 이관을 한 번에 합니다. 남은 것은 ②와 ③입니다.
16.6 이관이 생성 API의 이름을 바꾸는 자리 — 실측
FoldSerialFields는 접은 배열의 이름에 _array를 붙입니다. Table.cs의 접기 코드가
output.Name = output.NamePart + "_array"로 정하므로, Tag1 · Tag2는 생성 코드에서
tagArray · TagArray가 됩니다.
새 표기에는 그 접미가 없습니다. tag[0] · tag[1]은 그룹 이름이 Tag이므로 생성
코드가 tag · Tag를 냅니다. 접미는 옛 표기가 「이 이름의 숫자가 배열이다」를 말할 방법이
없어 만들어 붙인 것이고, 새 표기는 대괄호로 말하므로 접미가 할 일이 없습니다.
그래서 이관은 생성 API의 이름을 바꿉니다. 실측한 범위입니다.
| 무엇 | 얼마 |
|---|---|
| 접미가 실린 골든 트리 | 8개 — core · core-client · core-server · member-array · nested · nullable-elements · record-trim · serial-ref |
| 언어 | 전부 — 모든 언어와 json · Unreal |
| 바뀌는 이름 | tagArray → tag · slotArray → slot · textEnArray → textEn · tierArray → tier 계열 |
이것은 소비자에게 보이는 변경입니다. 옛 표기로 적은 시트를 새 표기로 옮기는 프로젝트는 생성된 접근자의 필드 이름이 바뀌는 것을 함께 받습니다 — 시트를 옮기는 것과 같은 순간이므로 한 번에 끝나지만, 옮기기 전까지는 기존 레이아웃이 그대로 유효하다는 것이 그 완충입니다.
판정이 필요합니다. 접미를 버리는 것(생성 API가 표기를 따라가고, 골든 8개를 재기록)과 새 표기에서도 접미를 붙이는 것(골든 무변경, 대신 대괄호가 이미 말한 것을 이름이 다시 말함) 중 하나입니다.
16.7 FoldSerialFields가 없어진 이유
그 옵션은 옛 표기의 공백을 메우던 것입니다. 옛 표기에는 「이 이름의 숫자가 배열이다」를
말할 자리가 없었고, 이름만으로는 Text1 · Text2가 배열 하나인지 필드 둘인지 알 수
없었습니다 — 어느 실제 워크북의 Condition_1 · Condition_2 · Condition_3은 서로 다른
enum 셋입니다. 그래서 recipe가 그것을 정했습니다.
새 표기는 시트가 정합니다. text[0] · text[1]이 배열이고 Text1 · Text2는 필드
둘입니다. 그러면 그 옵션이 정할 것이 남지 않으므로, 남겨 두는 것은 정하는 자리가 둘이 되는
것입니다.
없어진 것은 옵션 하나가 아닙니다.
| 무엇 | 어디 |
|---|---|
recipe의 FoldSerialFields | SheetSourceRecipe · SheetLayout · SheetImportSettings |
| 접기 자체 | Table.BuildSerialFieldsFromPlainFields와 그것을 고르던 분기 |
_array 접미 | 접기가 접은 배열의 이름에 붙이던 것입니다. 대괄호가 이미 말하므로 이름이 다시 말할 것이 없습니다(§16.6) |
| 옵션을 켠 레시피 33개의 그 줄 | 픽스처 · 샘플 전부 |
읽던 것은 기존 tabbit 파서 하나였습니다. 다른 레이아웃들과 이 레이아웃은 모두
false로 두고 있었으므로, 기존 파서가 지워지는 순간 아무도 읽지 않는 설정이 되었습니다.
옮긴 자리의 판정은 픽스처가 들고 있습니다. FixtureGen의 FieldSpec.Numbered가 그
자리입니다 — 「이 숫자는 배열이었다」를 픽스처가 적고, 점이 든 이름(Slot1.Id)은 규칙으로
옮겨집니다. 그 구분이 §16.3이 적은 「판단이 필요한 것과 기계적인 것」의 경계입니다.
16.8 8′단계에서 정한 것 — 맵의 키는 무엇인가
공개 표면은 FindByStageAndSlot(stageKey, slotKey)이고, 맵이 무엇으로 키가 되는지는
생성 코드의 사정입니다. 이 구분이 이 단계 설계의 전부입니다.
언어마다 각자의 자연스러운 튜플 키를 쓰게 하면 일곱 군데에 손으로 쓴 해시가
생깁니다. C · Lua · PHP · TypeScript는 쓸 수 있는 튜플 키가 아예 없고, C++의
std::tuple은 std::hash가 없으며, Swift의 튜플은 Hashable이 아니고, 언리얼의 TMap은
GetTypeHash를 요구합니다. 그 일곱은 「가끔 조회가 빗나간다」로 드러나는 결함의 자리이고,
그런 결함은 컴파일도 골든도 잡지 못합니다.
그래서 모든 언어가 성분마다 길이를 앞에 붙여 이은 문자열을 씁니다. 구분자만 쓰면
("a b", "c")와 ("a", "b c")가 같은 키가 되어 두 행 중 하나가 사라지고, 그 짝이
composite-key 픽스처의 Route에 들어 있는 이유입니다 — 생성된 글자는 어느 쪽이든
같으므로, 돌려 봐야만 갈립니다.
비용은 조회마다 문자열 하나이고, 그것을 감수한 이유는 맵이 공개되지 않는다는 것입니다.
C#의 RecordsBy…처럼 사전을 내주는 자리는 단일 키에만 있습니다. 튜플 키가 자연스러운
언어가 나중에 그쪽으로 옮겨도 조회의 모습은 그대로이므로, 이것은 되돌릴 수 있는
선택입니다.
Lua의 int64는 따로 셉니다. LuaJIT에서 64비트 값은 FFI cdata이고 그 tostring은 평범한
숫자를 넘긴 호출자가 만드는 십진수가 아닙니다 — 그래서 성분의 「모양」에 int64가 하나 더
있고, 그것을 이름으로 부르지 않는 언어는 number의 표기를 받습니다.
기록해 둘 결함 하나. PHP는 enum이 객체이므로 (int) 캐스트가 경고와 0을 냅니다 —
Loadout의 네 행이 전부 같은 키를 갖고 셋이 사라졌습니다. key-types가 PHP에서 찾은 것과
같은 종류의 잘못이 폭만 달라진 것이고, 잡은 것은 되읽기 게이트입니다.