CEK-native higher-order forms: map, filter, reduce, some, every?, for-each
Some checks are pending
Build and Deploy / build-and-deploy (push) Has started running

Higher-order forms now step element-by-element through the CEK machine
using dedicated frames instead of delegating to tree-walk ho-map etc.
Each callback invocation goes through continue-with-call, so deref-as-shift
works inside map/filter/reduce callbacks in reactive island contexts.

- cek.sx: rewrite step-ho-* to use CEK frames, add frame handlers in
  step-continue for map, filter, reduce, for-each, some, every
- frames.sx: add SomeFrame, EveryFrame, MapIndexedFrame
- test-cek-reactive.sx: add 10 tests for CEK-native HO forms

89 tests pass (20 signal + 43 CEK + 26 CEK reactive).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 10:45:36 +00:00
parent d0a5ce1070
commit 2211655060
5 changed files with 451 additions and 27 deletions

View File

@@ -3256,7 +3256,11 @@ def make_thread_frame(remaining, env):
# make-map-frame
def make_map_frame(f, remaining, results, env):
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env}
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env, 'indexed': False}
# make-map-indexed-frame
def make_map_indexed_frame(f, remaining, results, env):
return {'type': 'map', 'f': f, 'remaining': remaining, 'results': results, 'env': env, 'indexed': True}
# make-filter-frame
def make_filter_frame(f, remaining, results, current_item, env):
@@ -3270,6 +3274,14 @@ def make_reduce_frame(f, remaining, env):
def make_for_each_frame(f, remaining, env):
return {'type': 'for-each', 'f': f, 'remaining': remaining, 'env': env}
# make-some-frame
def make_some_frame(f, remaining, env):
return {'type': 'some', 'f': f, 'remaining': remaining, 'env': env}
# make-every-frame
def make_every_frame(f, remaining, env):
return {'type': 'every', 'f': f, 'remaining': remaining, 'env': env}
# make-scope-frame
def make_scope_frame(name, remaining, env):
return {'type': 'scope', 'name': name, 'remaining': remaining, 'env': env}
@@ -3994,15 +4006,15 @@ def step_eval_list(expr, env, kont):
elif sx_truthy((name == 'map')):
return step_ho_map(args, env, kont)
elif sx_truthy((name == 'map-indexed')):
return make_cek_value(ho_map_indexed(args, env), env, kont)
return step_ho_map_indexed(args, env, kont)
elif sx_truthy((name == 'filter')):
return step_ho_filter(args, env, kont)
elif sx_truthy((name == 'reduce')):
return step_ho_reduce(args, env, kont)
elif sx_truthy((name == 'some')):
return make_cek_value(ho_some(args, env), env, kont)
return step_ho_some(args, env, kont)
elif sx_truthy((name == 'every?')):
return make_cek_value(ho_every(args, env), env, kont)
return step_ho_every(args, env, kont)
elif sx_truthy((name == 'for-each')):
return step_ho_for_each(args, env, kont)
elif sx_truthy((env_has(env, name) if not sx_truthy(env_has(env, name)) else is_macro(env_get(env, name)))):
@@ -4178,19 +4190,67 @@ def step_eval_call(head, args, env, kont):
# step-ho-map
def step_ho_map(args, env, kont):
return make_cek_value(ho_map(args, env), env, kont)
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value([], env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_map_frame(f, rest(coll), [], env), kont))
# step-ho-map-indexed
def step_ho_map_indexed(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value([], env, kont)
else:
return continue_with_call(f, [0, first(coll)], env, [], kont_push(make_map_indexed_frame(f, rest(coll), [], env), kont))
# step-ho-filter
def step_ho_filter(args, env, kont):
return make_cek_value(ho_filter(args, env), env, kont)
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value([], env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_filter_frame(f, rest(coll), [], first(coll), env), kont))
# step-ho-reduce
def step_ho_reduce(args, env, kont):
return make_cek_value(ho_reduce(args, env), env, kont)
f = trampoline(eval_expr(first(args), env))
init = trampoline(eval_expr(nth(args, 1), env))
coll = trampoline(eval_expr(nth(args, 2), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(init, env, kont)
else:
return continue_with_call(f, [init, first(coll)], env, [], kont_push(make_reduce_frame(f, rest(coll), env), kont))
# step-ho-some
def step_ho_some(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(False, env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_some_frame(f, rest(coll), env), kont))
# step-ho-every
def step_ho_every(args, env, kont):
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(True, env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_every_frame(f, rest(coll), env), kont))
# step-ho-for-each
def step_ho_for_each(args, env, kont):
return make_cek_value(ho_for_each(args, env), env, kont)
f = trampoline(eval_expr(first(args), env))
coll = trampoline(eval_expr(nth(args, 1), env))
if sx_truthy(empty_p(coll)):
return make_cek_value(NIL, env, kont)
else:
return continue_with_call(f, [first(coll)], env, [], kont_push(make_for_each_frame(f, rest(coll), env), kont))
# step-continue
def step_continue(state):
@@ -4400,6 +4460,68 @@ def step_continue(state):
return make_cek_value(value, fenv, rest_k)
else:
return make_cek_state(first(remaining), fenv, kont_push(make_scope_frame(name, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'map')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
results = get(frame, 'results')
indexed = get(frame, 'indexed')
fenv = get(frame, 'env')
new_results = append(results, [value])
if sx_truthy(empty_p(remaining)):
return make_cek_value(new_results, fenv, rest_k)
else:
call_args = ([len(new_results), first(remaining)] if sx_truthy(indexed) else [first(remaining)])
next_frame = (make_map_indexed_frame(f, rest(remaining), new_results, fenv) if sx_truthy(indexed) else make_map_frame(f, rest(remaining), new_results, fenv))
return continue_with_call(f, call_args, fenv, [], kont_push(next_frame, rest_k))
elif sx_truthy((ft == 'filter')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
results = get(frame, 'results')
current_item = get(frame, 'current-item')
fenv = get(frame, 'env')
new_results = (append(results, [current_item]) if sx_truthy(value) else results)
if sx_truthy(empty_p(remaining)):
return make_cek_value(new_results, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_filter_frame(f, rest(remaining), new_results, first(remaining), fenv), rest_k))
elif sx_truthy((ft == 'reduce')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(empty_p(remaining)):
return make_cek_value(value, fenv, rest_k)
else:
return continue_with_call(f, [value, first(remaining)], fenv, [], kont_push(make_reduce_frame(f, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'for-each')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(empty_p(remaining)):
return make_cek_value(NIL, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_for_each_frame(f, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'some')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy(value):
return make_cek_value(value, fenv, rest_k)
else:
if sx_truthy(empty_p(remaining)):
return make_cek_value(False, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_some_frame(f, rest(remaining), fenv), rest_k))
elif sx_truthy((ft == 'every')):
f = get(frame, 'f')
remaining = get(frame, 'remaining')
fenv = get(frame, 'env')
if sx_truthy((not sx_truthy(value))):
return make_cek_value(False, fenv, rest_k)
else:
if sx_truthy(empty_p(remaining)):
return make_cek_value(True, fenv, rest_k)
else:
return continue_with_call(f, [first(remaining)], fenv, [], kont_push(make_every_frame(f, rest(remaining), fenv), rest_k))
else:
return error(sx_str('Unknown frame type: ', ft))