diff options
| author | eunhhu <[email protected]> | 2026-10-19 01:48:45 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-19 01:48:45 +0100 |
| commit | 9d3ed820105d97061aa5ce8d32b934cac85df180 (patch) | |
| tree | abb45c32f4270d57d7e437f2c7e96dc3ac4907aa | |
| parent | 5ab44af129f0736be485afda6a2a73ff04660598 (diff) | |
| download | aup-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.c | 8 | ||||
| -rw-r--r-- | docs/design/002-dsl-direction.md | 6 | ||||
| -rw-r--r-- | tests/check.py | 22 |
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() |
