scope_analysis: added support for deferred computation
All checks were successful
ci/woodpecker/push/debian Pipeline was successful
ci/woodpecker/push/fedora Pipeline was successful
ci/woodpecker/push/nix Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful

This commit is contained in:
2026-02-15 15:09:59 +03:00
parent 7685ae2e45
commit 24db34db62

View File

@@ -81,6 +81,30 @@ let resolve_set tbl env sym expr =
| Global i -> Ok (SetGlobal (i, expr)) | Global i -> Ok (SetGlobal (i, expr))
| _ -> Error "resolve_set: symbol resolution returned something invalid." | _ -> Error "resolve_set: symbol resolution returned something invalid."
(* We need to do some more sophisticated analysis to detect cases where
a symbol is accessed before it is defined.
If a symbol is accessed in a lambda body, that is fine, since that computation
is delayed, but for top-level forms that are directly executed we must be strict.
The analyze function is strict by default, until it encounters a lambda, at which
point it switches to resolving against all symbols.
global_tbl is a table that contains ALL defined symbols,
tbl is a table that contains symbols defined only until this point.
NOTE: because we currently convert all let expressions into lambdas, things like
this won't immediately be rejected by the compiler:
(let ((a 5))
b)
(define b 5)
I may consider adding special support for let forms, as this is pretty annoying.
*)
let convert program =
let global_tbl = extract_globals program in
let id_counter = (ref (-1)) in
let id () =
id_counter := !id_counter + 1; !id_counter in
let rec analyze tbl current = function let rec analyze tbl current = function
| Core_ast.Literal s -> Ok (Literal s) | Core_ast.Literal s -> Ok (Literal s)
| Var sym -> resolve_symbol tbl current sym | Var sym -> resolve_symbol tbl current sym
@@ -88,7 +112,7 @@ let rec analyze tbl current = function
let* inner = analyze tbl current expr in let* inner = analyze tbl current expr in
resolve_set tbl current sym inner resolve_set tbl current sym inner
| Lambda (s, body) -> | Lambda (s, body) ->
let* body = (analyze tbl (s :: current) body) in let* body = (analyze global_tbl (s :: current) body) in
Ok (Lambda body) Ok (Lambda body)
| Apply (f, e) -> | Apply (f, e) ->
let* f = analyze tbl current f in let* f = analyze tbl current f in
@@ -102,3 +126,11 @@ let rec analyze tbl current = function
| Begin el -> | Begin el ->
let* body = traverse (analyze tbl current) el in let* body = traverse (analyze tbl current) el in
Ok (Begin body) Ok (Begin body)
in
let[@tail_mod_cons] rec aux tbl = function
| [] -> []
| (Core_ast.Expr e) :: rest -> (analyze tbl [] e) :: (aux tbl rest)
| (Define (s, e)) :: rest ->
let tbl = SymbolTable.add s (id ()) tbl in
(analyze tbl [] e) :: (aux tbl rest)
in aux SymbolTable.empty program