aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/compile.c5
-rw-r--r--tests/check.py58
2 files changed, 63 insertions, 0 deletions
diff --git a/compiler/compile.c b/compiler/compile.c
index 0fcaa1b..5c88b97 100644
--- a/compiler/compile.c
+++ b/compiler/compile.c
@@ -3738,6 +3738,11 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) {
return field_value(r, field);
}
case E_INDEX: {
+ int given = e->count == 2 ? rows_list(c, e->args[0]) : -1;
+ if (given >= 0) { /* a message's rows' item at a position: read where they are, not from a copy of them */
+ item_of(c, e->args[1], handler, height, given);
+ return item_value(c, e, given);
+ }
Kind base = value(c, e->args[0], handler, height);
if (base.type == LIST && e->count == 2) { /* a list value's item at a position */
int reg = bind_list(c, e, base);
diff --git a/tests/check.py b/tests/check.py
index fc08708..e299911 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -622,6 +622,29 @@ on m {
}
'''
+ROWS_AT = '''record R {
+ a = 0
+ b = 0
+}
+state first = 0
+state last = 0
+state total = 0
+state through = 0
+state picked = 0
+input m = message { at: u32, rows: list of R }
+fn both(r) = r.a * 1000 + r.b
+on m {
+ first = m.rows[0].a
+ last = m.rows[m.rows.count - 1].b
+ let t = 0
+ for k in 0 .. m.rows.count { t = t + m.rows[k].a * (k + 1) }
+ total = t
+ let d = m.rows[m.at]
+ through = d.a + d.b
+ picked = both(m.rows[m.at])
+}
+'''
+
INNER_LISTS = '''record Seen {
node = 0
depth = 0
@@ -1675,6 +1698,40 @@ def inner_lists(aup, pure_run, work, events=100):
return events
+def rows_at(aup, pure_run, work, events=200):
+ """A message's rows read by position (after 140's box layout: read where they are, not from a copy of them, which
+ made each read cost the rows' count): at a literal, a computed and a looped position, through a let and as a fn's
+ argument; a position past the last fails the event (none is kept). Each event against Python's."""
+ import random
+ import struct
+ path = work / 'rows_at.pmod'
+ (work / 'rows_at.aup').write_text(ROWS_AT)
+ compile_module(aup, work / 'rows_at.aup', path)
+ rng = random.Random(140)
+ asked = []
+ for _ in range(events):
+ rows = [(rng.randrange(100), rng.randrange(100)) for _ in range(rng.randrange(1, 7))]
+ asked.append((rng.randrange(len(rows) + 1), rows))
+ commands = ''.join(f"message m {(struct.pack('<I', at) + b''.join(struct.pack('<2d', *r) for r in rows)).hex()}\n"
+ for at, rows in asked)
+ lines = run(pure_run, path, stdin=commands, check=False).stdout.splitlines()
+ assert len(lines) == events
+ failed, kept = 0, {'first': 0, 'last': 0, 'total': 0, 'through': 0, 'picked': 0}
+ for (at, rows), line in zip(asked, lines):
+ state = json.loads(line)
+ state['states'].pop('m.rows')
+ if at == len(rows):
+ assert state['fault'] != 'none' and state['states'] == kept, (at, rows, state)
+ failed += 1
+ continue
+ a, b = rows[at]
+ kept = {'first': rows[0][0], 'last': rows[-1][1], 'total': sum(r[0] * (k + 1) for k, r in enumerate(rows)),
+ 'through': a + b, 'picked': a * 1000 + b}
+ assert state['fault'] == 'none' and state['states'] == kept, (at, rows, state)
+ assert failed > 0
+ return events
+
+
def bytes_compared(aup, pure_run, work, events=400):
"""Bytes compared (Pure's TEXT_COMPARE with PURE_COMPARE_BYTES, after 140's DNS and HTTP): random bytes, 0x00 and
0xff among them, in the six relations, against literals on either side and a string with a format (a string where
@@ -2869,6 +2926,7 @@ def main():
report['bare_lists'] = bare_lists(aup, pure_run, work)
report['bytes_compared'] = bytes_compared(aup, pure_run, work)
report['inner_lists'] = inner_lists(aup, pure_run, work)
+ report['rows_at'] = rows_at(aup, pure_run, work)
report['bytes_state'] = bytes_state(aup, pure_run, work)
report['stored_nested'] = stored_nested(aup, pure_run, work, range(6))
report['documents'] = documents_model(aup, pure_run, work, range(4))