aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design/008-module-format.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/008-module-format.md')
-rw-r--r--docs/design/008-module-format.md34
1 files changed, 22 insertions, 12 deletions
diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md
index 8ccd6af..a78c5f2 100644
--- a/docs/design/008-module-format.md
+++ b/docs/design/008-module-format.md
@@ -1,4 +1,4 @@
-# Pure 모듈 형식 v4
+# Pure 모듈 형식 v5
2026-09-25. 구현 `prototypes/connections/module.{h,c}`, `module-text.c`, `module-list.c`, 트리 저장소 연결 `module-store.c`.
@@ -9,7 +9,8 @@ Pure가 실행하는 앱 정의의 형식이다. JVM이 class 파일 형식을 �
v1(스칼라)은 워크벤치 앱과 같은 의미였다: f64 상태, 키 입력, 원자적 이벤트 핸들러, 순수한 보기.
v2는 타입 있는 바이트 값, 필드가 있는 **메시지 입력**, 키별 **영속 컬렉션**, 응답을 더했다.
v3은 **text 상태와 커서**, text 상수, text 입력, text 보기를 더했다.
-v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스** 상태를 더한다. 이전 판은 받지 않는다(생산자는 aup 하나다).
+v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스** 상태를 더했다.
+v5는 값을 가진 채 합류하는 분기(조건식)와, 리스트 항목마다 행을 내는 **표 보기**를 더한다. 이전 판은 받지 않는다(생산자는 aup 하나다).
## 바이트 배치
@@ -17,7 +18,7 @@ v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스
| 위치 | 내용 |
|---|---|
-| 0 | `PUREMOD4` (8 B) |
+| 0 | `PUREMOD5` (8 B) |
| 8 | u16 상태, u16 입력, u16 보기, u16 핸들러 |
| 16 | u32 명령 수, u32 문자열 바이트 수 |
| 24 | u16 필드, u16 컬렉션, u16 상수, u16 레코드 |
@@ -29,7 +30,7 @@ v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스
| | 상수 × 8 B: u32 블롭 오프셋, u32 길이 (UTF-8) |
| | 레코드 × 8 B: u32 이름, u16 첫 레코드 필드, u16 필드 수(1–16) |
| | 레코드 필드 × 16 B: u32 이름, u32 타입(0 수, 1 text), 8 B 기본값: 수는 f64, text는 u32 상수 |
-| | 보기 × 16 B: u32 이름, u32 타입(0 수, 1 text), u32 시작 명령, u32 명령 수 |
+| | 보기 × 16 B: u32 이름, u8 타입(0 수, 1 text, 2 표), u8 0, u16 표(열이면 그 표 보기 + 1, 아니면 0), u32 시작 명령, u32 명령 수. 표는 시작 명령 자리에 리스트 상태, 명령 수 0 |
| | 핸들러 × 12 B: u32 이벤트, u32 시작 명령, u32 명령 수 |
| | 명령 × 16 B: u8 opcode, 0 3바이트, u32 인자, f64 값 |
| | 문자열: `[u8 길이][바이트]`의 연속 |
@@ -53,8 +54,8 @@ v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스
| 17–18 | MIN MAX | n n → n | IEEE 754-2019: −0 < +0 |
| 19 | CLAMP | n n n → n | lo > hi이면 실패 |
| 20 | STORE | n → | 상태 `인자`. 핸들러 전용 |
-| 21 | JZ | n → | 0이면 `인자`로. 핸들러 전용 |
-| 22 | JMP | | `인자`로. 핸들러 전용 |
+| 21 | JZ | n → | 0이면 `인자`로 |
+| 22 | JMP | | `인자`로 |
| 23 | FIELD | → n 또는 b | 메시지 필드 `인자`. 메시지 핸들러 전용 |
| 24 | DOC | → b | 컬렉션 `인자`의 메시지 키 문서. 없으면 `not_found`. 키별 메시지 핸들러 전용 |
| 25 | DOC_SIZE | → n | 그 문서의 바이트 수(없으면 0) |
@@ -90,13 +91,14 @@ v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스
| 58 | INDEX_MOVE | i n → i | n만큼. 정수가 아니면 `limit`. 처음과 마지막 항목에서 멈춘다 |
| 59–60 | INDEX_FIRST INDEX_LAST | → i | 리스트 상태 `인자`의 첫·마지막 항목(빈 리스트면 0) |
| 61 | INDEX_NUM | i → n | 0부터 센 위치 |
+| 62 | ROW | → i | 열이 계산하는 행. `인자`는 그 표의 리스트 상태. 열 전용 |
`f`는 레코드 필드의 타입(수 또는 text)이다. 수 연산 결과가 유한하지 않으면 실패한다. 실패하면 그 이벤트의 모든
핸들러가 한 일이 취소된다.
## 로드할 때 검증하는 것
-- 머리말, 예약 바이트, 명령 패딩이 0인지. 한도(상태 32, 입력 32, 보기 24, 핸들러 64, 명령 512,
+- 머리말, 예약 바이트, 명령 패딩이 0인지. 한도(상태 32, 입력 32, 보기 64(열 포함), 핸들러 64, 명령 512,
필드 32, 컬렉션 1, 문자열 32 KiB)와 정확한 파일 크기.
- 이름은 ASCII 식별자 1–31바이트이고, 상태·레코드·컬렉션·입력·보기가 한 이름공간을 쓴다. 필드 이름은 메시지나
레코드 안에서 유일하다. 레코드(최대 8)의 필드 범위는 차례로 이어지고 모두 합쳐 64개 이하다.
@@ -108,16 +110,19 @@ v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스
- 각 블록에서 명령마다 피연산자 타입(수, 바이트, text, 커서, 인덱스)과 스택 높이(0–64)를 확인한다. 커서 타입은 어느
text의 커서인지, 인덱스 타입은 어느 리스트의 인덱스인지 담는다. 다른 text의 커서나 다른 리스트의 인덱스를 쓰는
모듈은 거절한다. 레코드 명령은 레코드 필드 수만큼 필드 타입대로 꺼낸다. 명령이 놓일 수 있는 문맥(보기,
- 핸들러, 메시지 핸들러, 키별 메시지 핸들러, text 입력 핸들러)도 확인한다. 분기는 블록 안에서 앞으로만 가고, 분기와
- 합류는 스택이 빈 곳에서만 일어난다. 그래서 타입이 합쳐지는 경로가 없다. 모든 명령에 도달할 수 있어야 하고,
- 핸들러는 값을 남기지 않으며 보기는 수 하나를 남긴다.
+ 열, 핸들러, 메시지 핸들러, 키별 메시지 핸들러, text 입력 핸들러)도 확인한다. 분기는 블록 안에서 앞으로만 간다.
+ 합류 지점에는 모든 경로가 같은 스택(높이, 타입, 커서·인덱스의 대상)을 가져와야 한다. 그래서 조건식처럼 값이
+ 합류를 건너가도 타입이 섞이지 않는다. 앞에 남은, 값을 가진 합류는 한 번에 8개까지다. 모든 명령에 도달할 수
+ 있어야 하고, 핸들러는 값을 남기지 않으며 보기와 열은 자기 타입의 값 하나를 남긴다.
+- 표는 리스트 상태를 가리키고 열은 앞에 나온 표를 가리킨다. 표 밖의 이름은 한 이름공간이고, 열 이름은 표
+ 안에서만 유일하다.
검증된 모듈은 실행 중 스택·타입 검사가 필요 없고, 이벤트 하나의 명령 수는 핸들러 길이 이하다.
## 실행
`PureApp`은 모듈 참조, 상태 값, text 상태 draft, 리스트, 열린 이벤트 하나를 가진다(1,016 B, `PureModule`은 상수
-블롭을 포함해 26,704 B). text 상태는 저장소 파일이 없는 draft라 1 MiB 텍스트도 조각 트리로 편집한다. 커서는 바이트
+블롭을 포함해 28,600 B). text 상태는 저장소 파일이 없는 draft라 1 MiB 텍스트도 조각 트리로 편집한다. 커서는 바이트
위치이며 항상 글자 경계에 있다. 넣기는 그 위치 이후의 커서를 뒤로(삽입 위치의 커서 포함) 밀고, 지우기는 범위
안의 커서를 범위 처음으로 모은다. 모든 이벤트에서 text draft를 잡아 두고, 실패하면 이벤트 전 버전으로 돌린다.
@@ -141,9 +146,14 @@ text·리스트가 남을 때만 위치를 유지한다.
응답은 `pure_app_reply`로 순서대로 빌려준다. 페이로드와 문서의 익스텐트를 복사하지 않는다. 문서를 고친 이벤트의
문서 응답과 text 상태의 응답은 확정 전에 복사한다. text 보기는 `pure_app_view_text`로 빌려준다.
+
+표 보기는 리스트 항목마다 행 하나를 가진다(`pure_app_rows`). 호스트는 보여 줄 칸만
+`pure_app_cell`·`pure_app_cell_text`로 행 번호와 함께 계산한다. 화면 모델을 따로 만들거나 목록을 복사하지 않으므로,
+10만 항목 목록에서 20줄을 그리는 표면은 20행만 계산한다(가상 스크롤이 런타임의 성질이 된다). 보기가 실패해도
+앱의 마지막 이벤트 실패 정보는 바뀌지 않는다.
데이터 호스트는 `PureData` 인터페이스이며, `pure_tree_data`가 트리 저장소의 draft로 구현한다.
`pure-edit-module`은 기존 문서 CLI(`pure-edit`)와 같은 명령·출력·종료 코드를 모듈로 제공한다.
아직 런타임 코어(`runtime.c`)의 작업·예산 모델과 연결하지 않았다. 호스트가 직접 호출한다. 실행기 코드는
-`module.c` 28.3 KB, `module-text.c` 3.6 KB, `module-list.c` 3.2 KB(`-O2` text)로 v1(7.4 KB)보다 크다. 줄이는 것이
+`module.c` 31.1 KB, `module-text.c` 3.6 KB, `module-list.c` 3.2 KB(`-O2` text)로 v1(7.4 KB)보다 크다. 줄이는 것이
남은 일이다. 항목 text를 읽을 때마다 복사하는 비용도 남아 있다.