Fix VM correctness: get nil-safe, scope/context/collect! as primitives
- get primitive returns nil for type mismatches (list+string) instead of raising — matches JS/Python behavior, fixes find-nav-match errors - scope-peek, collect!, collected, clear-collected! registered as real primitives in sx_primitives table (not just env bindings) so the CEK step-sf-context can find them via get-primitive - step-sf-context checks scope-peek hashtable BEFORE walking CEK continuation — bridges aser's scope-push!/pop! with CEK's context - context, emit!, emitted added to SPECIAL_FORM_NAMES and handled in aser-special (scope operations in aser rendering mode) - sx-context NativeFn for VM-compiled code paths - VM execution errors no longer mark functions as permanently failed — bytecode is correct, errors are from runtime data - kbd, samp, var added to HTML_TAGS + sx-browser.js rebuilt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -500,7 +500,9 @@ let () =
|
||||
| [Dict d; Keyword k] -> dict_get d k
|
||||
| [List l; Number n] | [ListRef { contents = l }; Number n] ->
|
||||
(try List.nth l (int_of_float n) with _ -> Nil)
|
||||
| _ -> raise (Eval_error "get: dict+key or list+index"));
|
||||
| [Nil; _] -> Nil (* nil.anything → nil *)
|
||||
| [_; _] -> Nil (* type mismatch → nil (matches JS/Python behavior) *)
|
||||
| _ -> Nil);
|
||||
register "has-key?" (fun args ->
|
||||
match args with
|
||||
| [Dict d; String k] -> Bool (dict_has d k)
|
||||
|
||||
@@ -403,7 +403,7 @@ and step_sf_provide args env kont =
|
||||
|
||||
(* step-sf-context *)
|
||||
and step_sf_context args env kont =
|
||||
(let name = (trampoline ((eval_expr ((first (args))) (env)))) in let default_val = (if sx_truthy ((prim_call ">=" [(len (args)); (Number 2.0)])) then (trampoline ((eval_expr ((nth (args) ((Number 1.0)))) (env)))) else Nil) in let frame = (kont_find_provide (kont) (name)) in (if sx_truthy (frame) then (make_cek_value ((get (frame) ((String "value")))) (env) (kont)) else (if sx_truthy ((prim_call ">=" [(len (args)); (Number 2.0)])) then (make_cek_value (default_val) (env) (kont)) else (raise (Eval_error (value_to_str (String (sx_str [(String "No provider for: "); name]))))))))
|
||||
(let name = (trampoline ((eval_expr ((first (args))) (env)))) in let default_val = (if sx_truthy ((prim_call ">=" [(len (args)); (Number 2.0)])) then (trampoline ((eval_expr ((nth (args) ((Number 1.0)))) (env)))) else Nil) in (let stack_val = (if sx_truthy ((is_primitive ((String "scope-peek")))) then (cek_call ((get_primitive ((String "scope-peek")))) (List [name])) else Nil) in (if sx_truthy ((Bool (not (sx_truthy ((is_nil (stack_val))))))) then (make_cek_value (stack_val) (env) (kont)) else (let frame = (kont_find_provide (kont) (name)) in (if sx_truthy (frame) then (make_cek_value ((get (frame) ((String "value")))) (env) (kont)) else (if sx_truthy ((prim_call ">=" [(len (args)); (Number 2.0)])) then (make_cek_value (default_val) (env) (kont)) else (raise (Eval_error (value_to_str (String (sx_str [(String "No provider for: "); name])))))))))))
|
||||
|
||||
(* step-sf-emit *)
|
||||
and step_sf_emit args env kont =
|
||||
|
||||
@@ -313,11 +313,11 @@ and vm_call vm f args =
|
||||
(* Try JIT-compiled path first *)
|
||||
(match l.l_compiled with
|
||||
| Some cl when not (is_jit_failed cl) ->
|
||||
(* Execute cached bytecode; fall back to CEK on VM error *)
|
||||
(* Execute cached bytecode; fall back to CEK on VM error.
|
||||
Don't mark as failed — the bytecode is correct, the error
|
||||
is from runtime data (e.g. type mismatch in get). *)
|
||||
(try push vm (call_closure cl args vm.globals)
|
||||
with _ ->
|
||||
l.l_compiled <- Some jit_failed_sentinel;
|
||||
push vm (Sx_ref.cek_call f (List args)))
|
||||
with _ -> push vm (Sx_ref.cek_call f (List args)))
|
||||
| Some _ ->
|
||||
(* Previously failed or skipped — use CEK *)
|
||||
push vm (Sx_ref.cek_call f (List args))
|
||||
|
||||
Reference in New Issue
Block a user