Add index-of string primitive: spec, Python, JS, rebootstrap
(index-of s needle from?) returns first index of needle in s, or -1. Optional start offset. Specced in primitives.sx, implemented in both hand-written primitives.py and bootstrapper templates, rebootstrapped. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# WARNING: special-forms.sx declares forms not in eval.sx: reset, shift
|
||||
"""
|
||||
sx_ref.py -- Generated from reference SX evaluator specification.
|
||||
|
||||
@@ -725,6 +726,7 @@ PRIMITIVES["trim"] = lambda s: str(s).strip()
|
||||
PRIMITIVES["split"] = lambda s, sep=" ": str(s).split(sep)
|
||||
PRIMITIVES["join"] = lambda sep, coll: sep.join(coll)
|
||||
PRIMITIVES["replace"] = lambda s, old, new: s.replace(old, new)
|
||||
PRIMITIVES["index-of"] = lambda s, needle, start=0: str(s).find(needle, start)
|
||||
PRIMITIVES["starts-with?"] = lambda s, p: str(s).startswith(p)
|
||||
PRIMITIVES["ends-with?"] = lambda s, p: str(s).endswith(p)
|
||||
PRIMITIVES["slice"] = lambda c, a, b=None: c[a:b] if b is not None else c[a:]
|
||||
@@ -1169,64 +1171,6 @@ def _wrap_aser_outputs():
|
||||
aser_fragment = _aser_fragment_wrapped
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Extension: delimited continuations (shift/reset)
|
||||
# =========================================================================
|
||||
|
||||
_RESET_RESUME = [] # stack of resume values; empty = not resuming
|
||||
|
||||
_SPECIAL_FORM_NAMES = _SPECIAL_FORM_NAMES | frozenset(["reset", "shift"])
|
||||
|
||||
def sf_reset(args, env):
|
||||
"""(reset body) -- establish a continuation delimiter."""
|
||||
body = first(args)
|
||||
try:
|
||||
return trampoline(eval_expr(body, env))
|
||||
except _ShiftSignal as sig:
|
||||
def cont_fn(value=NIL):
|
||||
_RESET_RESUME.append(value)
|
||||
try:
|
||||
return trampoline(eval_expr(body, env))
|
||||
finally:
|
||||
_RESET_RESUME.pop()
|
||||
k = Continuation(cont_fn)
|
||||
sig_env = dict(sig.env)
|
||||
sig_env[sig.k_name] = k
|
||||
return trampoline(eval_expr(sig.body, sig_env))
|
||||
|
||||
def sf_shift(args, env):
|
||||
"""(shift k body) -- capture continuation to nearest reset."""
|
||||
if _RESET_RESUME:
|
||||
return _RESET_RESUME[-1]
|
||||
k_name = symbol_name(first(args))
|
||||
body = nth(args, 1)
|
||||
raise _ShiftSignal(k_name, body, env)
|
||||
|
||||
# Wrap eval_list to inject shift/reset dispatch
|
||||
_base_eval_list = eval_list
|
||||
def _eval_list_with_continuations(expr, env):
|
||||
head = first(expr)
|
||||
if type_of(head) == "symbol":
|
||||
name = symbol_name(head)
|
||||
args = rest(expr)
|
||||
if name == "reset":
|
||||
return sf_reset(args, env)
|
||||
if name == "shift":
|
||||
return sf_shift(args, env)
|
||||
return _base_eval_list(expr, env)
|
||||
eval_list = _eval_list_with_continuations
|
||||
|
||||
# Inject into aser_special
|
||||
_base_aser_special = aser_special
|
||||
def _aser_special_with_continuations(name, expr, env):
|
||||
if name == "reset":
|
||||
return sf_reset(expr[1:], env)
|
||||
if name == "shift":
|
||||
return sf_shift(expr[1:], env)
|
||||
return _base_aser_special(name, expr, env)
|
||||
aser_special = _aser_special_with_continuations
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Public API
|
||||
# =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user