aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-19 01:48:45 +0100
committereunhhu <[email protected]>2026-10-19 01:48:45 +0100
commit9d3ed820105d97061aa5ce8d32b934cac85df180 (patch)
treeabb45c32f4270d57d7e437f2c7e96dc3ac4907aa
parent5ab44af129f0736be485afda6a2a73ff04660598 (diff)
downloadaup-9d3ed820105d97061aa5ce8d32b934cac85df180.tar.gz
aup-9d3ed820105d97061aa5ce8d32b934cac85df180.zip
Pure 027's 5c: {cents:.2d}, an i64 (or a number) in minor units written with its last N digits after a point (1234 at .2d is 12.34, -5 is -0.05), not Python's form, so the random formats test it against Decimal(v).scaleb(-N) written as .Nf (digits on two in five d columns); digits refused off f and d (the diagnostic names .2d), an i64's .2f pointing at .2d; 002's formats say so
-rw-r--r--compiler/compile.c8
-rw-r--r--docs/design/002-dsl-direction.md6
-rw-r--r--tests/check.py22
3 files changed, 24 insertions, 12 deletions
diff --git a/compiler/compile.c b/compiler/compile.c
index 9b11d92..7927acf 100644
--- a/compiler/compile.c
+++ b/compiler/compile.c
@@ -657,7 +657,8 @@ static Expr *postfix(Compiler *c, Expr *e) {
return e;
}
-/* A format after ':' in a string's braces, as Python's: [0][width][.digits][d x X b o f]; how NUM_TEXT writes it. */
+/* A format after ':' in a string's braces, as Python's: [0][width][.digits][d x X b o f]; how NUM_TEXT writes it.
+ * .2d is not Python's: an integer in minor units, its last two digits after a point (money's cents, Pure 027). */
static uint32_t format_of(Compiler *c, const char *text, size_t length, int line, int column) {
size_t at = 0;
uint32_t format = 0, width = 0, digits = 0, form = 0;
@@ -678,7 +679,8 @@ static uint32_t format_of(Compiler *c, const char *text, size_t length, int line
++at;
}
if (at != length) fail_at(c, line, column, "a format is [0][width][.digits][d x X b o f], as in 08x or .2f");
- if (digits && form != FORM_FIXED) fail_at(c, line, column, "digits after the point go with f, as in .2f");
+ if (digits && form != FORM_FIXED && form != FORM_DECIMAL)
+ fail_at(c, line, column, "digits after the point go with f or d, as in .2f or .2d (cents: 1234 is 12.34)");
return format | width << FORMAT_WIDTH | form << FORMAT_FORM | digits << FORMAT_DIGITS | 0x80000000u; /* written */
}
@@ -3321,7 +3323,7 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) {
fail_at(c, part->line, part->column, "a format writes a number, a bool or an i64, not %s", type_name(kind.type));
if (kind.type == BOOL && form) fail_at(c, part->line, part->column, "a bool's format is a width alone");
if (kind.type == I64 && form == FORM_FIXED)
- fail_at(c, part->line, part->column, "f writes a number; an i64 is written as one by number(i)");
+ fail_at(c, part->line, part->column, "f writes a number; an i64 in cents is written by .2d (1234 is 12.34)");
if (kind.type == NUM || kind.type == BOOL || kind.type == I64)
emit(c, part->line, part->column, NUM_TEXT, (kind.type == BOOL ? 1u : kind.type == I64 ? 2u : 0u) | format, 0);
else if (kind.type != TEXT)
diff --git a/docs/design/002-dsl-direction.md b/docs/design/002-dsl-direction.md
index 87b3803..8a70906 100644
--- a/docs/design/002-dsl-direction.md
+++ b/docs/design/002-dsl-direction.md
@@ -657,7 +657,7 @@ on check {
out = out + text(d[i .. i + 1]) # 덧붙이기(제자리)
i = i + 1
}
- shown = "{i:5}|{h:08x}|{price:.2f}" # 형식
+ shown = "{i:5}|{h:08x}|{price:.2f}|{cents:.2d}" # 형식
}
```
@@ -665,8 +665,10 @@ on check {
- `0`은 부호 뒤를 0으로 채운다. 폭은 255까지다.
- `d x X b o`는 i64나 정수인 수를 그 진법으로 쓴다. 정수가 아닌 수면 이벤트가 `limit`으로 실패한다.
- `f`는 수를 고정 자릿수로 쓴다(자릿수 0–30, 기본 6). −0은 0으로 쓴다.
+ - `.Nd`는 파이썬에 없다: 정수를 최소 단위로 보고 끝의 N자리를 소수점 뒤에 쓴다(돈의 센트: `{cents:.2d}`에서 1234는 `12.34`, −5는
+ `-0.05`. Pure 027의 5단계). 돈은 i64의 센트라 셈이 정확하고, 보일 때만 나눈다.
- 형식 없이 폭만 주면 지금의 글자를 왼쪽으로 채운다. bool은 폭만 받는다.
- - 파이썬의 `format()`과 같은 글자를 낸다(무작위 대조).
+ - 파이썬의 `format()`과 같은 글자를 낸다(무작위 대조, `.Nd`는 그 Decimal을 `.Nf`로 쓴 것과).
- **잇기** `a + b`는 text끼리, 바이트끼리 잇는다. 섞으면 진단이 나온다.
- **자르기** `b[a .. c]`는 a부터 c 앞까지의 바이트다.
- text는 바이트 자리로 자르고, 글자 가운데를 자르면 `encoding`으로 실패한다.
diff --git a/tests/check.py b/tests/check.py
index 17f0417..e66a384 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -1352,10 +1352,18 @@ def formats_and_texts(aup, pure_run, work, apps=6, per_app=24):
"""Pure 027's stage 2a. Random formats ({v:08x}, {x:.3f}, ...) of i64s
and numbers a message gives, against Python's format(): an integer's
form of a number that is not whole fails as limit, -0 is written as 0.
- Then an app that cuts a message's bytes at commas into a text joined by
- '|', counting with while and reading by position only where && decides
- (an empty message reads nothing), against Python."""
- import struct
+ .Nd (Pure 027's stage 5) writes an integer in minor units, its last N
+ digits after a point: the Decimal of v / 10^N in .Nf. Then an app that
+ cuts a message's bytes at commas into a text joined by '|', counting
+ with while and reading by position only where && decides (an empty
+ message reads nothing), against Python."""
+ import decimal
+
+ def shown(v, spec):
+ if '.' in spec and spec.endswith('d'):
+ return format(decimal.Decimal(v).scaleb(-int(spec.split('.')[1][:-1])), spec[:-1] + 'f')
+ return format(v, spec)
+
rng = random.Random(272)
checked = faults = 0
for app in range(apps):
@@ -1365,7 +1373,7 @@ def formats_and_texts(aup, pure_run, work, apps=6, per_app=24):
form = rng.choice('dxXbo' if integer else 'dxXbof')
width = rng.choice([0, 0, 1, 5, 8, 20])
zeros = width > 0 and rng.random() < 0.5
- digits = rng.randrange(0, 11) if form == 'f' else None
+ digits = rng.randrange(0, 11) if form == 'f' or (form == 'd' and rng.random() < 0.4) else None
spec = ('0' if zeros else '') + (str(width) if width else '') + (f'.{digits}' if digits is not None else '') + form
specs.append((integer, spec))
source = 'state t: text\n' + ''.join(f'input e{i} = message {{\n v: i64\n x: number\n}}\n' for i in range(per_app))
@@ -1380,11 +1388,11 @@ def formats_and_texts(aup, pure_run, work, apps=6, per_app=24):
x = rng.choice([rng.uniform(-1e6, 1e6), float(rng.randrange(-5000, 5000)), rng.uniform(-1, 1), -0.0, 2.5, 1e20])
given += f'message e{i} {struct.pack("<qd", v, x).hex()}\n'
if integer:
- wanted.append((format(v, spec), 'none'))
+ wanted.append((shown(v, spec), 'none'))
elif spec.endswith('f'):
wanted.append((format(x + 0.0 if x else 0.0, spec), 'none'))
elif x == int(x) and abs(x) <= 2 ** 53:
- wanted.append((format(int(x), spec), 'none'))
+ wanted.append((shown(int(x), spec), 'none'))
else:
wanted.append((None, 'limit'))
lines = run(pure_run, work / f'formats{app}.pmod', stdin=given, check=False).stdout.splitlines()