Compiler: CALL_PRIM only for real primitives, GLOBAL_GET+CALL for runtime fns

compile-quasiquote, compile-defcomp, compile-defmacro were hardcoding
CALL_PRIM for runtime functions (qq-expand-runtime, eval-defcomp,
eval-defmacro) that aren't in the primitives table. Changed to
GLOBAL_GET + CALL so the VM resolves them from env.bindings at runtime.

The compile-call function already checks (primitive? name) before
emitting CALL_PRIM — only the three special-case compilers were wrong.

Also: register scope-push!/pop! as primitives, add scope-peek/emit!
to OCaml transpiler name mapping, fix sx_runtime.ml scope wrappers
to route through prim_call "scope-push!" etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 10:02:17 +00:00
parent 09feb51762
commit e12b2eab6b
2 changed files with 25 additions and 12 deletions

View File

@@ -435,6 +435,17 @@ let make_server_env () =
| [], _ -> Nil)
| _ -> Nil);
(* qq-expand-runtime — quasiquote expansion at runtime.
The bytecode compiler emits CALL_PRIM "qq-expand-runtime" for
backtick expressions. Expands the template with the calling env. *)
bind "qq-expand-runtime" (fun args ->
match args with
| [template] ->
(* qq_expand needs an env — use the kernel env *)
Sx_ref.qq_expand template (Env env)
| [template; Env e] -> Sx_ref.qq_expand template (Env e)
| _ -> Nil);
(* Evaluator bridge — aser calls these spec functions.
Route to the OCaml CEK machine. *)
bind "eval-expr" (fun args ->

View File

@@ -584,28 +584,30 @@
(define compile-defcomp
(fn (em args scope)
"Compile defcomp/defisland — delegates to runtime primitive."
;; For now, quote the entire defcomp form and call the primitive
(emit-const em (concat (list (make-symbol "defcomp")) args))
"Compile defcomp/defisland — delegates to runtime via GLOBAL_GET + CALL."
(let ((name-idx (pool-add (get em "pool") "eval-defcomp")))
(emit-op em 52) (emit-u16 em name-idx) (emit-byte em 1)) ;; CALL_PRIM
))
(emit-op em 20) (emit-u16 em name-idx)) ;; GLOBAL_GET fn
(emit-const em (concat (list (make-symbol "defcomp")) args))
(emit-op em 48) (emit-byte em 1))) ;; CALL 1
(define compile-defmacro
(fn (em args scope)
"Compile defmacro — delegates to runtime."
(emit-const em (concat (list (make-symbol "defmacro")) args))
"Compile defmacro — delegates to runtime via GLOBAL_GET + CALL."
(let ((name-idx (pool-add (get em "pool") "eval-defmacro")))
(emit-op em 52) (emit-u16 em name-idx) (emit-byte em 1))))
(emit-op em 20) (emit-u16 em name-idx)) ;; GLOBAL_GET fn
(emit-const em (concat (list (make-symbol "defmacro")) args))
(emit-op em 48) (emit-byte em 1)))
(define compile-quasiquote
(fn (em expr scope)
"Compile quasiquote — for now, quote the template and call qq-expand at runtime."
(emit-const em expr) ;; push template
;; qq-expand is a runtime function — call it
"Compile quasiquote — look up qq-expand-runtime in globals and call it.
Uses GLOBAL_GET + CALL (not CALL_PRIM) since it's a runtime function."
;; Stack: push fn first, then arg, then CALL 1
(let ((name-idx (pool-add (get em "pool") "qq-expand-runtime")))
(emit-op em 52) (emit-u16 em name-idx) (emit-byte em 1))))
(emit-op em 20) (emit-u16 em name-idx)) ;; GLOBAL_GET fn
(emit-const em expr) ;; push template
(emit-op em 48) (emit-byte em 1)))
;; --------------------------------------------------------------------------