Running a macro means compiling it and loading it into the compiler, and the step that reads as small in NEXT.md is not: OCaml has no dlopen for ELF, and lib/dune had no foreign_stubs. So the boundary is built first and the expander not at all. lib/dynload_stubs.c is the whole of it — dlopen, dlsym, a four-argument call into a macro thunk, and a peek/poke family, because OCaml cannot address the raw memory a Form image has to be laid out in. Nothing aggregate crosses to C. The unions lane verified a union's memory layout against clang, which is a different claim from LLVM's convention for an aggregate passed or returned by value in hand-written IR, so Emit.macro_thunk wraps every macro in void(ptr,i64,ptr,ptr): the slice is built and the result stored on the LLVM side, and the compiler's side is four pointers. Build.macro_module links the runtime in rather than declaring it external, so the module has no undefined symbols and the compiler's own link needs no -rdynamic. That is the difference from Build.shared, whose host is a running Flan program. defunion Form and the list-building surface quasiquote will desugar into are in the prelude. Form mirrors Form.value and not Form.t: no loc field, so the compiler stamps the call site's location onto everything a macro returns. The compiler builds. dune test was not run, and Form's layout is asserted nowhere — NEXT.md's new handoff section says what the three numbers are, what the next two commits should be, and the four decisions this made that the design did not settle.
31 lines
1.0 KiB
Plaintext
31 lines
1.0 KiB
Plaintext
(library
|
|
(name flan)
|
|
(libraries unix)
|
|
; Running a macro means dlopening it into the compiler, and OCaml has no
|
|
; dlopen for ELF -- Dynlink loads OCaml. These are the stubs for it, and the
|
|
; only C the compiler itself is built from. See lib/dynload_stubs.c.
|
|
(foreign_stubs
|
|
(language c)
|
|
(names dynload_stubs))
|
|
(c_library_flags (-ldl)))
|
|
|
|
; The host shim is Flan's, not the user's, so the compiler carries it rather
|
|
; than looking for it in an install directory. Generated from the real .c files
|
|
; so there is only ever one copy to edit. flan_dev.c goes into a dev build
|
|
; only — it is the run-time name lookup a REPL needs and a release build has
|
|
; no use for.
|
|
(rule
|
|
(target runtime_src.ml)
|
|
(deps
|
|
%{workspace_root}/runtime/flan_rt.c
|
|
%{workspace_root}/runtime/flan_dev.c)
|
|
(action
|
|
(with-stdout-to
|
|
runtime_src.ml
|
|
(progn
|
|
(echo "let source = {c|\n")
|
|
(cat %{workspace_root}/runtime/flan_rt.c)
|
|
(echo "|c}\n\nlet dev_source = {c|\n")
|
|
(cat %{workspace_root}/runtime/flan_dev.c)
|
|
(echo "|c}\n")))))
|