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,24 +81,56 @@ 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."
let rec analyze tbl current = function (* We need to do some more sophisticated analysis to detect cases where
| Core_ast.Literal s -> Ok (Literal s) a symbol is accessed before it is defined.
| Var sym -> resolve_symbol tbl current sym If a symbol is accessed in a lambda body, that is fine, since that computation
| Set (sym, expr) -> is delayed, but for top-level forms that are directly executed we must be strict.
let* inner = analyze tbl current expr in
resolve_set tbl current sym inner The analyze function is strict by default, until it encounters a lambda, at which
| Lambda (s, body) -> point it switches to resolving against all symbols.
let* body = (analyze tbl (s :: current) body) in global_tbl is a table that contains ALL defined symbols,
Ok (Lambda body) tbl is a table that contains symbols defined only until this point.
| Apply (f, e) ->
let* f = analyze tbl current f in NOTE: because we currently convert all let expressions into lambdas, things like
let* e = analyze tbl current e in this won't immediately be rejected by the compiler:
Ok (Apply (f, e))
| If (test, pos, neg) -> (let ((a 5))
let* test = analyze tbl current test in b)
let* pos = analyze tbl current pos in (define b 5)
let* neg = analyze tbl current neg in
Ok (If (test, pos, neg)) I may consider adding special support for let forms, as this is pretty annoying.
| Begin el -> *)
let* body = traverse (analyze tbl current) el in let convert program =
Ok (Begin body) 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
| Core_ast.Literal s -> Ok (Literal s)
| Var sym -> resolve_symbol tbl current sym
| Set (sym, expr) ->
let* inner = analyze tbl current expr in
resolve_set tbl current sym inner
| Lambda (s, body) ->
let* body = (analyze global_tbl (s :: current) body) in
Ok (Lambda body)
| Apply (f, e) ->
let* f = analyze tbl current f in
let* e = analyze tbl current e in
Ok (Apply (f, e))
| If (test, pos, neg) ->
let* test = analyze tbl current test in
let* pos = analyze tbl current pos in
let* neg = analyze tbl current neg in
Ok (If (test, pos, neg))
| Begin el ->
let* body = traverse (analyze tbl current) el in
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