Split env-bind! from env-set!: fix lexical scoping and closures
Two fundamental environment bugs fixed: 1. env-set! was used for both binding creation (let, define, params) and mutation (set!). Binding creation must NOT walk the scope chain — it should set on the immediate env. Only set! should walk. Fix: introduce env-bind! for all binding creation. env-set! now exclusively means "mutate existing binding, walk scope chain". Changed across spec (eval.sx, cek.sx, render.sx) and all web adapters (dom, html, sx, async, boot, orchestration, forms). 2. makeLambda/makeComponent/makeMacro/makeIsland used merge(env) to flatten the closure into a plain object, destroying the prototype chain. This meant set! inside closures couldn't reach the original binding — it modified a snapshot copy instead. Fix: store env directly as closure (no merge). The prototype chain is preserved, so set! walks up to the original scope. Tests: 499/516 passing (96.7%), up from 485/516. Fixed: define self-reference, let scope isolation, set! through closures, counter-via-closure pattern, recursive functions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -498,10 +498,23 @@ def env_get(env, name):
|
||||
return env.get(name, NIL)
|
||||
|
||||
|
||||
def env_set(env, name, val):
|
||||
def env_bind(env, name, val):
|
||||
"""Create/overwrite binding on THIS env only (let, define, param binding)."""
|
||||
env[name] = val
|
||||
|
||||
|
||||
def env_set(env, name, val):
|
||||
"""Mutate existing binding, walking scope chain (set!)."""
|
||||
if hasattr(env, 'set'):
|
||||
try:
|
||||
env.set(name, val)
|
||||
except KeyError:
|
||||
# Not found anywhere — bind on immediate env
|
||||
env[name] = val
|
||||
else:
|
||||
env[name] = val
|
||||
|
||||
|
||||
def env_extend(env):
|
||||
return _ensure_env(env).extend()
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@
|
||||
"get-primitive" "get_primitive"
|
||||
"env-has?" "env_has"
|
||||
"env-get" "env_get"
|
||||
"env-bind!" "env_bind"
|
||||
"env-set!" "env_set"
|
||||
"env-extend" "env_extend"
|
||||
"env-merge" "env_merge"
|
||||
@@ -524,11 +525,16 @@
|
||||
", " (py-expr-with-cells (nth args 1) cell-vars)
|
||||
", " (py-expr-with-cells (nth args 2) cell-vars) ")")
|
||||
|
||||
(= op "env-set!")
|
||||
(= op "env-bind!")
|
||||
(str "_sx_dict_set(" (py-expr-with-cells (nth args 0) cell-vars)
|
||||
", " (py-expr-with-cells (nth args 1) cell-vars)
|
||||
", " (py-expr-with-cells (nth args 2) cell-vars) ")")
|
||||
|
||||
(= op "env-set!")
|
||||
(str "env_set(" (py-expr-with-cells (nth args 0) cell-vars)
|
||||
", " (py-expr-with-cells (nth args 1) cell-vars)
|
||||
", " (py-expr-with-cells (nth args 2) cell-vars) ")")
|
||||
|
||||
(= op "set-lambda-name!")
|
||||
(str "_sx_set_attr(" (py-expr-with-cells (nth args 0) cell-vars)
|
||||
", 'name', " (py-expr-with-cells (nth args 1) cell-vars) ")")
|
||||
@@ -901,10 +907,14 @@
|
||||
(= name "append!")
|
||||
(str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
||||
".append(" (py-expr-with-cells (nth expr 2) cell-vars) ")")
|
||||
(= name "env-set!")
|
||||
(= name "env-bind!")
|
||||
(str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
||||
"[" (py-expr-with-cells (nth expr 2) cell-vars)
|
||||
"] = " (py-expr-with-cells (nth expr 3) cell-vars))
|
||||
(= name "env-set!")
|
||||
(str pad "env_set(" (py-expr-with-cells (nth expr 1) cell-vars)
|
||||
", " (py-expr-with-cells (nth expr 2) cell-vars)
|
||||
", " (py-expr-with-cells (nth expr 3) cell-vars) ")")
|
||||
(= name "set-lambda-name!")
|
||||
(str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
||||
".name = " (py-expr-with-cells (nth expr 2) cell-vars))
|
||||
@@ -1098,10 +1108,14 @@
|
||||
(append! lines (str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
||||
"[" (py-expr-with-cells (nth expr 2) cell-vars)
|
||||
"] = " (py-expr-with-cells (nth expr 3) cell-vars)))
|
||||
(= name "env-set!")
|
||||
(= name "env-bind!")
|
||||
(append! lines (str pad (py-expr-with-cells (nth expr 1) cell-vars)
|
||||
"[" (py-expr-with-cells (nth expr 2) cell-vars)
|
||||
"] = " (py-expr-with-cells (nth expr 3) cell-vars)))
|
||||
(= name "env-set!")
|
||||
(append! lines (str pad "env_set(" (py-expr-with-cells (nth expr 1) cell-vars)
|
||||
", " (py-expr-with-cells (nth expr 2) cell-vars)
|
||||
", " (py-expr-with-cells (nth expr 3) cell-vars) ")"))
|
||||
:else
|
||||
(append! lines (py-statement-with-cells expr indent cell-vars)))))))))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user