aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design')
-rw-r--r--docs/design/002-dsl-direction.md70
1 files changed, 66 insertions, 4 deletions
diff --git a/docs/design/002-dsl-direction.md b/docs/design/002-dsl-direction.md
index 3b02291..49e6ecd 100644
--- a/docs/design/002-dsl-direction.md
+++ b/docs/design/002-dsl-direction.md
@@ -11,8 +11,8 @@ aup는 범용 언어가 아니라 **무엇이 무엇에 연결되는지를 선�
## 원칙
-1. 모든 구문은 Pure가 로드 전에 검증할 수 있는 것으로 컴파일된다. 무제한 반복·재귀는 없다.
- 길이에 비례하는 연산은 이벤트 작업 예산을 쓰고, 넘으면 이벤트가 실패한다.
+1. 모든 구문은 Pure가 로드 전에 검증할 수 있는 것으로 컴파일된다. 끝을 모르는 반복(`while`)과 재귀도 있다(Pure 027).
+ 끝남은 문법이 아니라 예산이 보증한다. 걸음은 이벤트 작업 예산을, 부름의 깊이는 메모리 몫을 쓰고, 넘으면 이벤트가 실패한다.
2. 이벤트가 트랜잭션이다. 효과는 이벤트 경계에서만 일어나고, 실패하면 아무것도 바뀌지 않는다.
3. 형태는 한 번만 선언한다. 같은 `record`가 저장·메시지·전송·보기에 쓰이고 표현은 런타임이 고른다.
4. 표면은 가독성을 우선한다. 줄바꿈으로 끝나는 문장, `{}` 블록, `state / input / on / view`.
@@ -234,8 +234,8 @@ view summary = words.counted(tasks.count, "task", "tasks")
매개변수에 타입을 적지 않는다. 부르는 곳마다 인라인하므로 수·text·커서·인덱스 무엇이든 받는다. 본문의
타입 오류는 본문 위치에 `(in fn 'f' called at 줄:열)`을 붙여 알린다.
- **런타임 호출이 없다.** 인자는 부를 때 한 번 계산해 스택에 두고, 본문은 Pure의 PICK으로 읽고 SLIDE로 치운다.
- 인자를 여러 번 써도 다시 계산하지 않으며, 쓰지 않는 인자도 계산한다(값 전달). 재귀는 컴파일 오류다.
- 그래서 실행량 상한과 로드 전 검증이 그대로 유지된다.
+ 인자를 여러 번 써도 다시 계산하지 않으며, 쓰지 않는 인자도 계산한다(값 전달). 재귀하는 fn과 블록 본문의 fn은 Pure의 함수로
+ 돈다(아래 "스스로 부르는 fn", Pure 027의 2c).
- **use는 라이브러리를 가져온다.** `use words`는 소스 옆의 `words.aup`, 없으면 `AUP_PATH`(`:`로 구분)의 디렉터리에서
찾는다. 라이브러리는 `fn`, `record`(아래), `use`만 선언하고 앱의 상태를 볼 수 없다(매개변수만 읽는다). 부를 때는
`words.counted(...)`처럼 라이브러리 이름을 붙인다. 순환 use는 오류이고, 라이브러리의 오류는 그 파일 경로로 알린다.
@@ -752,3 +752,65 @@ on check {
- 받아들이는 꼴 하나.
- 중첩 값의 앱이 기대값을 낸다. 같은 이벤트를 두 번 해도 같은 몫이 늘고, 실패한 이벤트는 아무것도 남기지 않는다.
- `examples/json_tree.aup`는 Pure의 검사가 JSONTestSuite와 무작위 문서 100개로 파이썬의 읽기와 대조한다.
+
+### 스스로 부르는 fn, 블록, 여러 값, 값으로서의 레코드, 글자 (Pure 027의 2c)
+
+`examples/json_tree.aup`가 이것으로 재귀 하강 파서와 깊이 우선 걷기로 다시 쓰였다.
+
+```text
+fn value(d, i) -> Json, number { # 블록 본문의 fn. 스스로 부르면 결과의 타입을 적는다
+ let items: list of Json # let의 리스트는 부를 때마다 제 것이다
+ let j = skip(d, i)
+ if d[j] == 0x5b {
+ let v, k = value(d, j + 1) # 여러 값을 한 번에 받는다. v는 Json 값이다
+ items.append(v)
+ return Json.arr(items), k + 1 # 레코드나 갈래의 값을 돌려준다
+ }
+ ...
+}
+fn walk(x) { # 값을 주지 않는 fn: 부름은 문장이다
+ match x { # 이름에 담긴 갈래의 값을 match한다
+ arr(items) => { for y in items { walk(y) } } # 항목은 값으로 넘어간다
+ _ => {}
+ }
+}
+on check {
+ let root, end = value(bytes(text(check.data)), 0)
+ walk(root)
+ for ch in check.name { n = n + number(ch) } # text의 글자(코드 포인트, i64)
+}
+```
+
+- **fn이 Pure의 함수가 되는 때.** 블록 본문을 가졌거나, 부름의 고리가 자신을 지나면 Pure의 함수(CALL)로 돈다. 식 하나의 fn은
+ 지금처럼 인라인한다. 인자의 타입마다 한 벌을 만든다(단형화). 레코드 값을 받는 인라인 fn도 함수로 돈다.
+- **결과.**
+ - `return a, b`의 값들이 결과다. 값 없는 `return`과, 끝에 이르는 블록은 아무것도 주지 않는다.
+ - 결과의 타입은 처음 컴파일된 `return`이 정한다. 그보다 먼저 자신을 부르면 `-> 타입, …`을 적어야 한다(진단).
+ - 모든 `return`은 같은 타입을 준다. 결과가 있는 fn은 모든 길이 `return`으로 끝나야 한다.
+ - 적은 결과의 타입은 `return`의 값이 바란다. 그래서 `-> i64`인 fn의 `return 1`은 i64다.
+- **부름.** 식 안에서 쓰거나, 값을 주지 않으면 문장으로 쓴다(`walk(root)`). 여러 값은 `let a, b = f(x)`나 `a, b = f(x)`(let과 수,
+ bool, i64 상태)로 받고, `return f(x)`로 그대로 넘긴다.
+- **보기에서는 부르지 않는다.** 보기와 count·sum·max·min의 블록은 함수를 부르지 않는다(Pure가 그렇다). 핸들러와 함수가 부른다.
+- **레코드의 값.**
+ - `R { … }`, `V.case(x)`, `V.case`, 레코드 값을 주는 부름, 이름에 담긴 레코드 값은 그 필드들로 스택에 놓인다.
+ - `let p = …`는 이름 하나에 레코드를 담는다. 필드는 `p.x`로 읽힌다(이름 `p.필드`의 let들).
+ - 항목(`for y in ys`의 y, `m.value` 같은 레코드 필드의 경로)은 함수에 값으로 넘어간다. 함수는 제 리스트를 가지므로 항목의 자리가
+ 아니라 값을 받는다.
+ - `match`는 이름에 담긴 갈래의 값도 받는다. 경우의 이름은 읽기 전용이다.
+ - 리스트 값은 지역 칸에 담기고(Pure의 지역 칸 타입 4), `for y in items`가 레지스터에 묶어 걷는다.
+- **도달하지 않는 코드.** `return`이나 `break` 뒤의 줄은 진단이 나온다. 모든 경우가 이름 붙은 `match`의 마지막 갈래는 묻지 않고
+ 탄다. `if`의 then이 돌아가거나 멈추면 else를 건너는 점프를 두지 않는다.
+- **글자.**
+ - `for ch in t`는 text의 글자를 코드 포인트(i64)로 돈다. t는 let, 매개변수, 상태, 레코드 값이나 메시지의 text 필드다. 돌기 전에
+ 제 이름에 담는다.
+ - `char(n)`은 코드 포인트 하나의 text다. 서로게이트면 `encoding`, 0–0x10FFFF 밖이면 `limit`이다(Pure의 CHAR).
+
+검증:
+- 진단 13건: 결과를 모른 채 자신을 부름, 보기의 부름, 값 없이 끝날 수 있는 fn, 서로 다른 결과, 도달하지 않는 줄, 값을 주는 부름의
+ 문장, 여러 값을 이름 하나에, 이름과 값의 수, 모르는 타입, 커서 인자, 경우 이름에 대입, 갈래가 아닌 match, 접기 안의 부름.
+- 받아들이는 꼴 둘.
+- 함수의 앱이 파이썬과 같다. 무작위 트리 40개를 재귀로 짓고 합하고, 깊이와 잎 수를 여러 값으로 받는다. 무작위 text 60개의 글자 수,
+ 코드 포인트의 합, 넓은 글자 수, 그리고 `char`로 다시 지은 text다. 끝없는 재귀는 `memory`로 실패하고 아무것도 바꾸지 않는다.
+- 모듈은 2b의 컴파일러와 바이트까지 같다(aup와 Pure의 aup 소스 49개). 다른 것은 `match`를 쓴 `json_tree.aup`뿐이다.
+- 바뀐 진단 문구 셋: 레코드가 이제 값이라 `n.insert(at, T { … })`는 "expected text, not a record"다. let의 타입 목록에 리스트와
+ 레코드가 들었다. 자신을 부르는 식 fn은 이제 보기에서만 거절된다.