aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/compile.c15
-rw-r--r--docs/design/002-dsl-direction.md32
-rw-r--r--tests/check.py12
3 files changed, 52 insertions, 7 deletions
diff --git a/compiler/compile.c b/compiler/compile.c
index 5475afe..26e7172 100644
--- a/compiler/compile.c
+++ b/compiler/compile.c
@@ -70,7 +70,7 @@ typedef struct {
int line, column;
double number;
int64_t integer; /* a literal of digits alone (whole): its exact value, when not past an i64's range (wide) */
- bool whole, wide, hex; /* hex: written 0x..., an i64 wherever it stands */
+ bool whole, wide, hex; /* hex: written 0x..., an i64 unless a number is wanted where it stands */
char text[NAME + 1]; /* name, or punctuation of one or two characters */
size_t start, end; /* string contents in the source */
} Token;
@@ -2534,8 +2534,10 @@ static void expect_kind(Compiler *c, const Expr *e, Kind actual, Type type) {
if (actual.type != type) fail_at(c, e->line, e->column, "expected %s, not %s", type_name(type), type_name(actual.type));
}
+static Kind wanted(Compiler *c, const Expr *e, bool handler, int height, Type want);
+
static Kind number(Compiler *c, const Expr *e, bool handler, int height) {
- Kind kind = value(c, e, handler, height);
+ Kind kind = wanted(c, e, handler, height, NUM); /* 0x... is a number here */
expect_kind(c, e, kind, NUM);
return kind;
}
@@ -3464,6 +3466,12 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) {
if (height + 1 > STACK) fail_at(c, e->line, e->column, "expression needs more than 64 stack values");
switch (e->kind) {
case E_NUMBER:
+ if (e->hex && want == NUM) { /* 0x... where a number goes (140's UAX #14 friction): that number, exactly */
+ if (e->wide || e->integer > INT64_C(9007199254740992))
+ fail_at(c, e->line, e->column, "the number is past 2^53, where numbers stop being exact: write it as an i64");
+ emit(c, e->line, e->column, CONST, 0, (double)e->integer);
+ return (Kind){NUM, -1};
+ }
if (e->hex) { /* 0x...: an i64 */
emit(c, e->line, e->column, CONST, 2, 0);
made_i64(c, e, c->code_count - 1);
@@ -4960,7 +4968,8 @@ static void statements(Compiler *c, const Stmt *s) {
integer(c, s->value, c->handling, 0);
emit(c, s->line, s->column, STORE, (uint32_t)index, 0);
} else if (c->state_types[index] == STATE_NUM || c->state_types[index] == STATE_BOOL) {
- expect_kind(c, s->value, value(c, s->value, c->handling, 0), c->state_types[index] == STATE_BOOL ? BOOL : NUM);
+ Type type = c->state_types[index] == STATE_BOOL ? BOOL : NUM;
+ expect_kind(c, s->value, wanted(c, s->value, c->handling, 0, type), type);
emit(c, s->line, s->column, STORE, (uint32_t)index, 0);
} else if (c->state_types[index] == STATE_LIST && s->value->kind == E_LIST) {
statements(c, emptied(c, s->name, s->line, s->column)); /* xs = [...]: the list emptied, then its elements */
diff --git a/docs/design/002-dsl-direction.md b/docs/design/002-dsl-direction.md
index 4f7e855..8c85082 100644
--- a/docs/design/002-dsl-direction.md
+++ b/docs/design/002-dsl-direction.md
@@ -615,7 +615,7 @@ on hash {
}
```
-- **16진 리터럴** `0x…`는 어디서나 i64다.
+- **16진 리터럴** `0x…`는 i64다. 수가 오는 자리에서는 수다(아래 "140의 마찰을 걷어 냄").
- **비트 연산** `&`, `|`, `^`, `~`, `<<`, `>>`는 i64를 끝없는 2의 보수 위의 정수로 계산한다(파이썬의 정수와 같은 뜻).
- `x << n`은 x·2^n이고, i64를 넘으면 이벤트가 `overflow`로 실패한다.
- `x >> n`은 내림한 x / 2^n이다.
@@ -948,7 +948,7 @@ ys = [4, 5] # let의 리스트를 비우고 �
- **블록 안.** let의 리스트를 if, for, while, match의 블록에도 둔다. 뜻은 let을 지날 때마다 새 리스트다. 리스트는 블록이 끝나면
보이지 않는다. 같은 이름은 한 레코드의 리스트 하나이고, 핸들러나 fn의 이벤트 리스트 하나다(`let.이름`). 접기와 필터의 블록에는 둘 수
없다.
-- **`xs = [...]`.** let의 리스트를 비우고 원소를 차례로 붙인다. 저장되는 리스트에는 쓰지 않는다(진단).
+- **`xs = [...]`.** let의 리스트를 비우고 원소를 차례로 붙인다. 저장되는 리스트에도 쓴다(아래 "140의 마찰을 걷어 냄").
- **비용.** 비우기는 항목을 하나씩 지운다(`while xs.count > 0 { xs.remove(…) }`, 수의 리스트는 자리 `count - 1`, 다른 리스트는
`.last`). Pure에 한 번에 비우는 명령이 없어서다. 항목 수에 비례한다.
- **고친 것.** 식 안의 리스트 리터럴이 원소를 버리고 있었다(필드의 `[1, 2]`가 빈 리스트로 됐다). 이제 원소를 잇는다. 레코드 필드에는
@@ -978,3 +978,31 @@ view left = pending # 바이트 보기
- 검증: `bytes_state`가 0x00과 0xff가 든 무작위 바이트를 붙이고 자르며 300걸음을 저장소로 15번 다시 시작하며 파이썬과 견준다. 같은
이름의 text로 저장소에서 시작하고 실행 중에 교체해, UTF-8인 바이트는 남고 아닌 것은 새로 시작하며, 바이트로 돌아오면 text가 가졌던
것을 찾는다. 진단 셋(커서, insert, text에 바이트).
+
+### 140의 마찰을 걷어 냄 (Pure 140 뒤, 2026-10-19)
+
+```text
+let codes, classes: list of number # 리스트의 let 여럿을 한 줄에: 하나씩, 빈 채로
+boxes[k].x, boxes[k].y = at, top + offset # 대상에 필드와 항목이 섞여도: 값을 먼저 모두, 그다음 왼쪽부터
+trailers, j = fields(d, c + 2) # 리스트에 리스트 값을 통째로(fn의 것, let의 것, 다른 리스트)
+boxes = [] # 저장되는 리스트도 비우고 채운다
+rttvar = 0.75 * rttvar + 0.25 * abs(srtt - r) # abs: 수와 i64, 식은 한 번
+rto = max(srtt + max(tick.period, 4 * rttvar), 1000) # 타이머의 주기(밀리초, now처럼)
+if b == 0x2010 { ... } # 수를 만나는 16진은 수
+```
+
+- **리스트의 let 여럿.** `let a, b: list of R`은 이름마다 그 레코드의 리스트 하나다. 처음 원소는 따로 준다(진단).
+- **대상 여럿.** `a, xs[i].x, at.y = …`처럼 대상에 항목의 필드, 위치의 항목, 인덱스의 필드가 섞여도 된다. 값을 먼저 모두 숨은
+ let에 담고(리터럴은 바로), 대상마다 왼쪽부터 준다. 그래서 `i, xs[i] = 1, 7`의 `xs[i]`는 새 `i`를 읽는다(파이썬과 같다). 이름만이면
+ 예전과 같은 코드다.
+- **리스트에 리스트 값.** `xs = v`는 v(fn이 돌려준 리스트, let의 리스트, 다른 리스트)를 먼저 복사하고(xs가 그 안에 있을 수 있다), xs를
+ 비우고, 항목을 차례로 붙인다. 키 있는 리스트는 키로 더한다. `xs = ys`는 ys를 그 자리에서 걷고, `xs = xs`는 그대로다. 다른 레코드나
+ 종류의 값은 거절한다(`'xs' takes a list of F, not G`). `xs, j = f(x)`처럼 이름 묶음에 리스트가 끼어도 된다.
+- **비용.** 비우기와 채우기는 항목마다 지우고 붙인다. 손으로 쓴 고리와 같은 명령이다. 줄이 줄 뿐 빨라지지는 않는다.
+- **`abs(x)`.** 수와 i64를 받는다. x를 한 번 셈해 복제하고(PICK), 그 부호를 바꾼 것과 큰 쪽을 낸다(MAX). −0은 +0이 되고, i64의
+ 가장 작은 값은 `overflow`로 실패한다. Pure의 새 명령은 없다.
+- **`tick.period`.** `every` 입력의 주기를 밀리초로 준다(`now`가 세는 단위). 상수이므로 어디서나 읽는다.
+- **16진 리터럴.** 수가 오는 자리(수와의 비교와 셈, min과 max, 수의 상태·let·필드에 주기)에서는 그 수다. 2^53을 넘으면 정확하지 않으므로
+ 거절한다(진단). 따로 서거나 i64를 만나면 예전처럼 i64다. 비트 연산은 i64를 원하므로 그대로다.
+- 검증: `frictions`가 무작위 100 사건으로 위의 꼴을 파이썬과 견주고, i64의 가장 작은 값의 `abs`가 실패하는지 본다. 진단 여섯.
+ aup, Pure, Vega의 aup 소스 81개의 모듈은 바이트까지 같다(예전에 컴파일되던 꼴은 바뀌지 않는다).
diff --git a/tests/check.py b/tests/check.py
index a232e10..9368854 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -81,6 +81,8 @@ DIAGNOSTICS = [
'lists declared together begin empty: give each its elements apart'),
('state a = 0\non frame {\n let xs, xs: list of number\n}\n', '3:11', "'xs' is named twice"),
('state t: text\nview v = abs(t)\n', '2:14', 'expected a number, not text'),
+ ('state n = 0\nview v = n + 0x20000000000001\n', '2:14', 'the number is past 2^53, where numbers stop being exact: write it as an i64'),
+ ('state n = 0\nview v = n & 0xff\n', '2:10', 'bit operations take i64s, not numbers'),
('record F {\n v = 0\n}\nstate kept: list of F\nstate n = 0\non frame {\n kept = n\n}\n', '7:10',
"'kept' takes a list of F, not a number"),
('record F {\n v = 0\n}\nrecord G {\n w = 0\n}\nstate kept: list of F\nstate other: list of G\non frame {\n kept = other\n}\n',
@@ -671,6 +673,8 @@ state j = 0
state far = 0
state wide: i64
state per = 0
+state hexed = 0
+state bits: i64
input tick = every 250 ms
input go = message { k: u32 }
input edge = message { x: i64 }
@@ -689,6 +693,8 @@ on go {
far = abs(go.k - 4.5) + abs(-0.0)
wide = abs(i64(go.k) - 4)
per = tick.period
+ hexed = (if go.k == 0x2 { 1 } else { 0 }) + (if 0x3 == go.k { 10 } else { 0 }) + min(go.k, 0x5) * 100 + 0x10
+ bits = (i64(go.k) | 0xf0) & 0xff
let ts: list of P
ts.append(P { x: 1 })
ts, j = made(go.k)
@@ -1821,7 +1827,8 @@ def frictions(aup, pure_run, work, events=100):
each its own and empty as it begins, in inner blocks too; targets together with fields and items among them
(xs[i].x, xs[i].y = a, b; a, xs[i] = ...), the values first, then each target left to right as Python's; a list
given a list value whole (xs = f(x), xs = ys, xs = xs, xs = [] for a list state too, xs, j = f(x)), added by key
- when keyed; abs(x) of a number and an i64 (x once); a timer's period (tick.period). Each event against Python's."""
+ when keyed; abs(x) of a number and an i64 (x once); a timer's period (tick.period); 0x... a number where a number
+ goes (compared with one, in its arithmetic, given to one), an i64 elsewhere. Each event against Python's."""
import random
import struct
path = work / 'frictions.pmod'
@@ -1848,7 +1855,8 @@ def frictions(aup, pure_run, work, events=100):
ns[0], ns[1] = 3, k
b = b + ns[0] * 100 + ns[1]
want = {'total': sum(range(k)) * (1 + 200 + 10000), 'n': n, 'bs': bs, 'a': a, 'b': b, 'i': i, 'kept': kept,
- 'keyed': keyed, 'j': k + 100, 'far': abs(k - 4.5), 'wide': abs(k - 4), 'per': 250}
+ 'keyed': keyed, 'j': k + 100, 'far': abs(k - 4.5), 'wide': abs(k - 4), 'per': 250,
+ 'hexed': (k == 2) + 10 * (k == 3) + min(k, 5) * 100 + 16, 'bits': (k | 0xf0) & 0xff}
assert state['fault'] == 'none' and {key: state['states'][key] for key in want} == want, (k, state['states'])
edges = ''.join(f"message edge {struct.pack('<q', x).hex()}\n" for x in (-2 ** 63 + 1, -2 ** 63))
first, second = (json.loads(line) for line in run(pure_run, path, stdin=edges, check=False).stdout.splitlines())