diff options
| -rw-r--r-- | README.md | 20 | ||||
| -rw-r--r-- | docs/design/001-language-v0.md | 110 |
2 files changed, 130 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..ded9b11 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# aup + +aup는 [Pure](/srv/git/pure.git) 런타임을 위한 **언어와 툴체인**이다. Java와 javac가 JVM에 +대해 하는 역할과 같다. aup 소스를 검사해 Pure가 검증한 뒤 실행하는 모듈(`.pmod`)로 만든다. + +| 층 | 역할 | +|---|---| +| Pure | 런타임. 모듈 형식과 검증기·실행기, 실행·메모리·영속 상태·통신 | +| **aup** | 언어와 툴체인. 소스 → 진단 → Pure 모듈 | +| [Vega](/srv/git/vega.git) | aup와 Pure 위의 인터페이스 구현자(TUI·CLI·GUI·NUI) | + +상태: v0 설계 중. [언어 v0](docs/design/001-language-v0.md). + +## 원칙 + +- **앱은 상태·입력·반응·보기를 선언하고, 저장·트랜잭션·화면 모델은 런타임이 소유한다.** +- 컴파일 결과는 Pure가 로드 전에 검증할 수 있는 형식이어야 한다. 소스나 AST를 런타임에 두지 않는다. +- 툴체인은 C17로 작성하고 외부 의존성을 두지 않는다. 검증 기기는 로컬 Raspberry Pi 5다. +- 기존 Pure `.pure` 두 방언(워크벤치 scalar, 문서 연결)을 aup 하나로 대체한다. + 대체의 근거는 기존 실행기와의 비트 단위 일치다. diff --git a/docs/design/001-language-v0.md b/docs/design/001-language-v0.md new file mode 100644 index 0000000..e690a89 --- /dev/null +++ b/docs/design/001-language-v0.md @@ -0,0 +1,110 @@ +# aup v0 — 한 언어, 한 모듈 형식 + +2026-09-25. 상태: v0a(scalar) 확정, v0b(바이트·영속 문서) 초안. + +착수 기준: + +- 바꿀 결정: Pure 앱을 두 `.pure` 방언과 두 실행기(워크벤치 Zig scalar VM, 문서 `PureProgram` 해석기)로 + 계속 만들지, aup 한 언어 → Pure 모듈 한 형식 → Pure 실행기 하나로 합칠지. +- 남은 질문: 작은 언어 하나와 형식 하나로 두 앱의 관찰 가능한 동작을 특수 규칙 없이 표현할 수 있는가? + 런타임은 소스를 몰라도 로드 전에 모듈을 검증할 수 있는가? +- 최소 확인: v0a는 워크벤치 예제를 aup로 옮겨, 같은 이벤트열에서 Pure 실행기의 상태가 워크벤치 + Zig 엔진과 비트 단위로 같아야 한다. v0b는 문서 편집 앱을 옮겨 기존 CLI와 저장 결과가 같아야 한다. +- 중단 기준: 한쪽 앱에 그 앱만을 위한 규칙이 필요하거나, Pure 실행기가 기존 두 실행기의 합보다 + 커지면 설계를 다시 본다. + +## 의심한 관례와 aup의 선택 + +| 관례 | aup | +|---|---| +| 앱이 저장·불러오기·직렬화 코드를 쓴다 | 상태를 선언하면 런타임이 소유한다(영속화는 v0b) | +| 트랜잭션과 롤백을 앱이 관리한다 | 모든 이벤트가 트랜잭션이다. 성공하면 반영하고, 실패하면 아무것도 바뀌지 않는다 | +| 요청/응답 핸들러와 라우터 | 입력은 이름 있는 이벤트다. 키·CLI·HTTP는 호스트 어댑터가 입력으로 바꾼다 | +| 화면용 모델을 따로 만든다 | `view`는 상태의 순수 함수다. 호스트와 나중의 Vega가 읽어 표시한다 | +| 런타임이 소스를 해석한다 | 툴체인이 모듈로 컴파일하고, 런타임은 검증한 뒤 실행한다(JVM의 class 파일처럼) | +| 이벤트 처리 시간에 상한이 없다 | 반복이 없고 분기는 앞으로만 한다. 이벤트당 실행량의 상한이 모듈에서 정해진다 | + +## v0a 문법 + +```text +# 주석은 줄 끝까지 +state count = 0 # f64 상태. 초기값은 부호 있는 숫자 상수 +input tap = key Space # 키 입력. tap.down, tap.up 이벤트 +view label = count # 상태의 순수 함수 +view width = 40 + min(count, 20) * 8 + +on tap.down { + count = count + 1 +} + +on frame { # 1/60초 진행. 핸들러 안에서 dt를 쓸 수 있다 + if count > 20 { + count = 0 + } else { + count = count + dt + } +} +``` + +- 선언과 문장은 줄바꿈이나 `;`로 끝난다. 이름은 ASCII `[A-Za-z_][A-Za-z_0-9]*`, 31바이트 이하다. +- 예약어: `state input view on if else key frame dt min max clamp`. +- 키: `Space Enter ArrowLeft ArrowRight ArrowUp ArrowDown KeyA..KeyZ Digit0..Digit9`. 입력 이름과 + 키는 각각 한 번만 쓴다. 상태·입력·보기의 이름공간은 서로 겹칠 수 없다. +- 식: 숫자, 상태 이름, `dt`(핸들러 안에서만), 괄호, 단항 `- !`, 이항 `* /`, `+ -`, + `< <= > >=`, `== !=`, `&&`, `||`(뒤로 갈수록 우선순위가 낮다), `min(a,b) max(a,b) clamp(x,lo,hi)`. +- 문장: `이름 = 식`, `if 식 { … }`, `if 식 { … } else { … }`. +- 같은 이벤트의 핸들러가 여럿이면 소스 순서로 하나의 이벤트 안에서 실행한다. + +## 의미 (워크벤치 엔진과 같음) + +- 값은 IEEE binary64다. 연산 결과나 읽은 값이 유한하지 않으면, 0으로 나누면, `clamp`의 `lo > hi`이면 + 이벤트 전체가 실패하고 상태는 이벤트 전과 같다. +- 비교와 논리 연산은 0 또는 1을 낸다. `&& ||`는 양쪽을 모두 계산한다. 0이 아니면 참이다. +- `if`의 조건은 한 번 계산한다. 문장은 순서대로 실행한다. +- `frame`의 `dt`는 1/60이고 키 이벤트의 `dt`는 0이다. +- 한도: 상태 32, 입력 8, 보기 24, 핸들러 64, 명령 512, 스택 64, 소스 32 KiB. +- 모듈을 교체하면 같은 이름의 상태 값은 유지하고, 새 상태는 초기값을 쓰고, 없어진 상태는 버린다. + 교체에 실패하면 이전 모듈과 상태가 그대로다. + +워크벤치와 다른 점은 셋이다. 입력 종류를 `key`로 적고, 도형 대신 `view`로 값을 내보내고, `else`가 있다. +도형과 인터페이스 표현은 Vega가 맡는다. 포인터 입력, 값 입력, 레코드·규칙은 v0a에 없다. + +## 모듈 + +모듈 형식은 Pure가 소유한다([Pure 설계 008](/srv/git/pure.git): `docs/design/008-module-format.md`). +aup는 그 형식의 생산자일 뿐이다. 모듈에는 이름표·초기값·입력 키·보기·핸들러·명령이 들어가고, +Pure는 로드할 때 범위·이름·분기·스택 높이를 모두 정적으로 검증한다. 검증을 통과한 모듈은 실행 중에 +스택 넘침이나 잘못된 참조를 만들 수 없다. + +## 툴체인 + +- `aup check FILE`: 진단만 한다. 오류는 `파일:줄:열: 메시지` 한 줄이다. +- `aup build FILE -o OUT.pmod`: 모듈을 만든다. 같은 소스는 바이트 단위로 같은 모듈을 만든다. +- C17, 외부 의존성 없음. 컴파일러는 Pure를 링크하지 않는다. 시험에서는 Pure의 검증기로 결과를 확인한다. + +## v0b 초안: 바이트와 영속 문서 + +```text +layout Edit { + start: u32le + remove: u32le + insert: bytes +} + +state documents: bytes by key # 키마다 하나의 바이트 값. 런타임이 버전을 붙여 영속화한다 +input edit = message Edit by key # 키와 기준 버전을 가진 메시지 +input show = message by key + +on edit { + documents[key].splice(edit.start, edit.remove, edit.insert) + reply edit.insert +} + +on show { + reply documents[key] +} +``` + +- 이벤트 트랜잭션이 곧 Pure draft다. 핸들러가 성공하면 draft를 커밋하고, 기준 버전이 다르면 충돌이다. +- 바이트 값은 익스텐트 열이다. 연속 버퍼를 언어에서 드러내지 않는다. +- `get/put` 핸들러, `store.read/splice` 호출, `request.revision`은 언어에서 사라진다. HTTP·CLI 호스트가 입력과 응답으로 바꾼다. |
