aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/compile.c19
-rw-r--r--tests/check.py18
2 files changed, 34 insertions, 3 deletions
diff --git a/compiler/compile.c b/compiler/compile.c
index b11acec..5475afe 100644
--- a/compiler/compile.c
+++ b/compiler/compile.c
@@ -40,6 +40,7 @@ enum { BIT_AND = 0x100, BIT_OR, BIT_XOR, BIT_LEFT, BIT_RIGHT, BIT_NOT };
enum { LOCALS = 64 }; /* an event's locals (Pure's) */
enum { NESTING = 8, LOOP_INTEGER = 256 }; /* loops (for) nest 8 deep; an i64's count (Pure 018) */
enum { FOLD_COUNT, FOLD_SUM, FOLD_MAX, FOLD_MIN, FOLD_FIRST, FOLD_DISTINCT, FOLD_INTEGER = 0x40 };
+enum { ABS = BIT_NOT + 1 }; /* abs(x), the compiler's alone: x, a PICK of it, its negation and MAX (no instruction of Pure's) */
/* An input as the app writes it; in Pure's module (027's 4d) each of them but an output and a view's arguments is a
* message, its binding what makes it: a key's press (KIND_KEY) and its release (KIND_RELEASE, an `on k.up`'s, k.up), a
* text's (one field, text), a timer's tick, a frame (one field, dt: frame, an `on frame`'s) or a pointing at a table's
@@ -916,7 +917,8 @@ static Expr *expression(Compiler *c, int minimum, bool multiline) {
} else if (token.kind == T_NAME) {
static const struct { const char *name; unsigned count; int op; } calls[] =
{{"min", 2, MIN}, {"max", 2, MAX}, {"clamp", 3, CLAMP}, {"text", 1, TEXT_FROM}, {"bytes", 1, BYTES_FROM},
- {"floor", 1, FLOOR}, {"i64", 1, I64_FROM}, {"number", 1, NUM_FROM}, {"byte", 1, BYTE}, {"char", 1, CHAR}};
+ {"floor", 1, FLOOR}, {"i64", 1, I64_FROM}, {"number", 1, NUM_FROM}, {"byte", 1, BYTE}, {"char", 1, CHAR},
+ {"abs", 1, ABS}};
int call = -1, how = -1;
for (int i = 0; i < (int)(sizeof(calls) / sizeof(*calls)); ++i) if (!strcmp(token.text, calls[i].name)) call = i;
static const char *const folds[] = {"count", "sum", "max", "min", "first", "distinct"};
@@ -3604,6 +3606,12 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) {
const Expr *base = e->args[0];
int item_list;
char item[2 * NAME + 2];
+ int timer = base->kind == E_NAME && !strcmp(e->member, "period") && !c->module && parameter(c, base->name) < 0 &&
+ local_named(c, base->name) < 0 ? input_index(c, base->name) : -1;
+ if (timer >= 0 && c->inputs[timer].kind == KIND_TIMER) { /* tick.period: a timer's, in milliseconds, as now is */
+ emit(c, e->line, e->column, CONST, 0, (double)c->inputs[timer].period);
+ return (Kind){NUM, -1};
+ }
int binding = binding_path(c, e, item);
if (binding >= 0) { /* down a record's value held by name (027's 2c) */
Kind kind = binding_value(c, e, binding, item);
@@ -4097,6 +4105,15 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) {
emit(c, e->line, e->column, (uint8_t)e->op, 0, 0);
return (Kind){BOOL, -1};
}
+ if (e->op == ABS) { /* abs(x): x once, then the greater of it and its negation (-0's is +0, as IEEE's maximum) */
+ Kind operand = wanted(c, e->args[0], handler, height, want);
+ if (operand.type != I64) expect_kind(c, e->args[0], operand, NUM);
+ if (height + 2 > STACK) fail_at(c, e->line, e->column, "expression needs more than 64 stack values");
+ emit(c, e->line, e->column, PICK, 0, 0);
+ emit(c, e->line, e->column, operand.type == I64 ? I64_NEG : NEG, 0, 0);
+ emit(c, e->line, e->column, operand.type == I64 ? I64_MAX : MAX, 0, 0);
+ return (Kind){operand.type, -1};
+ }
if (e->op == BYTE) { /* byte(n): one byte, n an i64 0-255 */
integer(c, e->args[0], handler, height);
emit(c, e->line, e->column, BYTE, 0, 0);
diff --git a/tests/check.py b/tests/check.py
index 9f1242d..a232e10 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -80,6 +80,7 @@ DIAGNOSTICS = [
('state a = 0\non frame {\n let xs, ys: list of number = [1]\n}\n', '3:30',
'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'),
('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',
@@ -667,7 +668,13 @@ state i = 0
state kept: list of P
state keyed: list of P by x
state j = 0
+state far = 0
+state wide: i64
+state per = 0
+input tick = every 250 ms
input go = message { k: u32 }
+input edge = message { x: i64 }
+on edge { wide = abs(edge.x) }
fn made(n) -> list of P, number {
let out: list of P
for m in 0 .. n { out.append(P { x: m * 10 }) }
@@ -679,6 +686,9 @@ fn only(n) -> list of P {
return out
}
on go {
+ far = abs(go.k - 4.5) + abs(-0.0)
+ wide = abs(i64(go.k) - 4)
+ per = tick.period
let ts: list of P
ts.append(P { x: 1 })
ts, j = made(go.k)
@@ -1811,7 +1821,7 @@ 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. Each event against Python's."""
+ when keyed; abs(x) of a number and an i64 (x once); a timer's period (tick.period). Each event against Python's."""
import random
import struct
path = work / 'frictions.pmod'
@@ -1838,8 +1848,12 @@ 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}
+ 'keyed': keyed, 'j': k + 100, 'far': abs(k - 4.5), 'wide': abs(k - 4), 'per': 250}
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())
+ assert first['fault'] == 'none' and first['states']['wide'] == 2 ** 63 - 1, first # abs of i64's least overflows
+ assert second['fault'] == 'overflow' and second['states']['wide'] == 2 ** 63 - 1, second
return events