From 7340ba9af757b6f135dff9c51a193ba527f061b3 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Sun, 4 Oct 2026 13:58:19 +0100 Subject: Count with for i in a .. b, break, nest fors and let an event's own lists (Pure 018) --- tests/check.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/check.py b/tests/check.py index aa18217..eb37c35 100644 --- a/tests/check.py +++ b/tests/check.py @@ -249,8 +249,9 @@ DIAGNOSTICS = [ ('view v = floor(1, 2)\n', '1:10', 'floor expects 1 argument'), ('state now = 1\n', '1:7', "'now' is reserved"), # A for walks a list once; a message's rows are a list its handler reads. - (ROWS + 'on o {\n for a in xs {\n for b in xs {\n n = n + 1\n }\n }\n}\n', '12:5', - 'a for cannot hold another for'), + # Fors nest (Pure 018): an inner for changes no list an outer one walks. + (ROWS + 'on o {\n for a in xs {\n for b in o.rows {\n xs.append(L { item: 1 })\n }\n }\n}\n', '13:7', + 'a for does not add to the list it walks; add to another list, or after the for'), (ROWS + 'on o {\n for a in xs {\n xs.append(L { item: 1 })\n }\n}\n', '12:5', 'a for does not add to the list it walks; add to another list, or after the for'), (ROWS + 'on o {\n for a in o.rows {\n reply "x"\n }\n}\n', '12:5', 'a message replies once: reply after the for'), @@ -296,6 +297,26 @@ DIAGNOSTICS = [ (LETS + 'on k.down {\n let i = xs.first\n i = ys.first\n}\n', '11:7', "'i' holds an index of its list"), (LETS + 'on k.down {\n if n > 0 {\n let x = 1\n }\n n = x\n}\n', '13:7', "unknown state 'x'"), (LETS + 'on k.down {\n for r in xs {\n let r = 1\n }\n}\n', '11:9', "'r' is the item this for walks"), + # Counting fors, break and a let's list (Pure 018). + (LETS + 'on k.down {\n break\n}\n', '10:3', "break ends a for; this one is outside any"), + (LETS + 'on k.down {\n if n > 0 {\n let q: list of T\n }\n}\n', '11:9', + "a let's list goes in the handler's own block, not in an if or a for"), + (LETS + 'on k.down {\n for i in 0 .. 3 {\n i = 1\n }\n}\n', '11:5', "'i' is what this for counts; it is not assigned"), + (LETS + 'on k.down {\n for a in xs {\n for b in ys {\n xs.remove(a)\n }\n }\n}\n', '12:7', + 'a for removes from the list it walks, not from one an outer for walks'), + (LETS + 'on k.down {\n for a in xs {\n for i in 0 .. 2 {\n xs.append(T { a: 1 })\n }\n }\n}\n', '12:7', + 'a for does not add to the list it walks; add to another list, or after the for'), + (LETS + 'record U {\n b = 0\n}\ninput j = key KeyJ\non k.down {\n let q: list of T\n}\non j.down {\n' + ' let q: list of U\n}\n', '17:7', "'q' is a let's list of another record or key in another handler"), + (LETS + 'on k.down {\n let q: list of T\n}\non k.down {\n let q: list of T\n}\n', '13:7', + "another handler of this event has a list 'q'; name this one apart"), + (LETS + 'on k.down {\n' + ''.join(' ' * (d + 1) + f'for i{d} in 0 .. 2 {{\n' for d in range(9)) + '}\n' * 10, '18:19', 'fors nest at most 8 deep'), + (LETS + 'on k.down {\n let x: text\n}\n', '10:10', "a let holds a list as let x: list of a record; others take '='"), + (LETS + 'on k.down {\n let q: list of Nope\n}\n', '10:7', "a list holds a record; 'Nope' is not one"), + (LETS + 'on k.down {\n let ' + 'q' * 28 + ': list of T\n}\n', '10:7', "a let's list has a name of 27 characters at most"), + (LETS + 'on k.down {\n for i in t .. 3 {\n }\n}\n', '10:12', 'expected a number'), + ('state break = 0\n', '1:7', "'break' is reserved"), + (LETS + 'on k.down {\n let q: list of T\n for i in 0 .. 3 {\n let q = 1\n }\n}\n', '12:9', "'q' is already a let here"), (LETS + 'view v = x\non k.down {\n let x = 1\n}\n', '9:10', "unknown state 'x'"), (LETS + 'on k.down {\n let let = 1\n}\n', '10:7', "'let' is reserved"), # A slice takes a list keyed by a number. @@ -1093,6 +1114,99 @@ def transients(aup, pure_run, work): return {'store_files_compared': len(compared.common_files), 'type_words_differ': len(differ)} +def computing(aup, pure_run, work, graphs=20): + """Computing in one event (Pure 018): examples/routes.aup's searches + over random graphs (breadth first, depth first, nearest by weight) and + examples/numbers.aup's square roots (bit for bit as Python works them out + in the same order) and knapsacks, each against a model. The searches' + lists are the event's own: none is left after it.""" + import heapq + import random + import struct + routes, numbers = work / 'routes.pmod', work / 'numbers.pmod' + compile_module(aup, ROOT / 'examples' / 'routes.aup', routes) + compile_module(aup, ROOT / 'examples' / 'numbers.aup', numbers) + + def message(name, layout, *values): + return f'message {name} {struct.pack(layout, *values).hex()}\n' + + searched = 0 + for seed in range(graphs): + rng = random.Random(seed) + n = rng.randint(5, 60) + edges = {} + for _ in range(rng.randint(n, 4 * n)): + a, b = rng.randrange(n), rng.randrange(n) + if a != b: + edges[(a, b)] = rng.randint(1, 9) + out = {v: sorted(b for (a, b) in edges if a == v) for v in range(n)} + commands = ''.join(message('link', ' 1 else 1.0), 0 + for _ in range(200): + following = (r + x / r) / 2 + if following >= r: + break + r, steps = following, steps + 1 + states = json.loads(line)['states'] + assert struct.pack('