{
    Let analyze. analyze
}

Where

Let analyze program.
    Let {_ program}.
        (LIST.fold program {1 []}
            Func package {i packages}.
                Let {i init functions}.
                    (lift_functions i $ analyze_variables package.tree)
                In
                Let package.
                    {
                        Let path. package.path
                        Let imports. package.imports
                        Let init. init
                        Let functions. functions
                    }
                In
                {i (package::packages)})
    In
    program

Where

Let analyze_variables expr.
    Let SET. (SEARCH.SET STRING.compare)
    Let MAP.
        (SEARCH.MAP
            STRING.compare
            Func var. var.name)
    In
    Let reduce_binder reduce_expr env depth free binders binder.
        Match binder
        | `let.{pat expr}
            Let {expr free}. (reduce_expr env depth free expr)
            In
            {(`let.{pat expr}::binders) free}
        | `do.expr
            Let {expr free}. (reduce_expr env depth free expr)
            In
            {(`do.expr::binders) free}
        ;
    In
    Define reduce_expr env depth free expr.
        Let trivial. {expr free}
        In
        Match expr
        | `true trivial
        | `false trivial
        | `num._ trivial
        | `str._ trivial
        | `package._ trivial
        | `prim._ trivial
        | `var.name
            Match (MAP.search env name)
            | `just.var
                Let free.
                    If (var.depth < depth)
                        (SET.insert free name)
                        free
                In
                {expr free}
            | `nothing
                (die (STRING.concat ["Variable \"" name "\" is not bound."]))
            ;
        | `chain.{expr chain}
            Let {expr free}. (reduce_expr env depth free expr)
            In
            {`chain.{expr chain} free}
        | `tuple.exprs
            Let {exprs free}.
                (LIST.fold exprs {[] free}
                    Func expr {exprs free}.
                        Let {expr free}. (reduce_expr env depth free expr)
                        In
                        {(expr::exprs) free})
            In
            {`tuple.exprs free}
        | `module.binders
            Let {binders free}.
                (LIST.fold binders {[] free}
                    Func binder {binders free}.
                        (reduce_binder reduce_expr env depth free
                            binders binder))
            In
            {`module.binders free}
        | `block.{binder_groups expr}
            Let {env binder_groups free}.
                (LIST.fold binder_groups {env [] free}
                    Func binder_group {env binder_groups free}.
                        Let {vars binder_group free}.
                            (LIST.fold binder_group {[] [] free}
                                Func binder {vars binders free}.
                                    Let {binders free}.
                                        (reduce_binder reduce_expr
                                            env depth free binders binder)
                                    Let vars.
                                        (LIST.append (binder_variables binder)
                                            vars)
                                    In
                                    {vars binders free})
                        In
                        Let env.
                            (LIST.reduce vars env
                                Func env var.
                                    (MAP.insert env
                                        {Let name. var Let depth. depth}))
                        In
                        {env (binder_group::binder_groups) free})
            In
            Let {expr free}. (reduce_expr env depth free expr)
            In
            {`block.{binder_groups expr} free}
        | `app.{func args}
            Let {func free}. (reduce_expr env depth free func)
            In
            Let {args free}.
                (LIST.fold args {[] free}
                    Func arg {args free}.
                        Let {arg free}. (reduce_expr env depth free arg)
                        In
                        {(arg::args) free})
            In
            {`app.{func args} free}
        | `func.{self param_pats expr}
            Let {expr func_free}.
                Let env.
                    Let depth. (depth + 1)
                    Let vars.
                        (LIST.fold param_pats
                            Match self | `nothing [] | `just.var [var] ;
                            Func pat vars.
                                (LIST.append (pattern_variables pat) vars))
                    In
                    (LIST.reduce vars env
                        Func env var.
                            (MAP.insert env {Let name. var Let depth. depth}))
                In
                (reduce_expr env (depth + 1) SET.empty expr)
            In
            Let free.
                (LIST.reduce (SET.list func_free) free
                    Func free name.
                        Match (MAP.search env name)
                        | `just.var
                            If (var.depth < depth)
                                (SET.insert free name)
                                free
                        ;)
            In
            {`func.{self (SET.list func_free) param_pats expr} free}
        | `goto.expr
            Let {expr free}. (reduce_expr env depth free expr)
            In
            {`goto.expr free}
        | `switch.{expr clauses}
            Let {expr free}. (reduce_expr env depth free expr)
            In
            Let {clauses free}.
                (LIST.fold clauses {[] free}
                    Func {pat body} {clauses free}.
                        Let {body free}.
                            Match pat
                            | `default.pat
                                Match pat
                                | `just.name
                                    Let env.
                                        (MAP.insert env
                                            {Let name. name Let depth. depth})
                                    In
                                    (reduce_expr env depth free body)
                                | `nothing
                                    (reduce_expr env depth free body)
                                ;
                            | `value._
                                (reduce_expr env depth free body)
                            ;
                        In
                        {({pat body}::clauses) free})
            In
            {`switch.{expr clauses} free}
        | `cond.clauses
            Let {clauses free}.
                (LIST.fold clauses {[] free}
                    Func {test body} {clauses free}.
                        Let {test free}. (reduce_expr env depth free test)
                        In
                        Let {body free}. (reduce_expr env depth free body)
                        In
                        {({test body}::clauses) free})
            In
            {`cond.clauses free}
        | `if.{test_expr then_expr else_expr}
            Let {test_expr free}. (reduce_expr env depth free test_expr)
            In
            Let {then_expr free}. (reduce_expr env depth free then_expr)
            In
            Let {else_expr free}. (reduce_expr env depth free else_expr)
            In
            {`if.{test_expr then_expr else_expr} free}
        | `not.expr
            Let {expr free}. (reduce_expr env depth free expr)
            In
            {`not.expr free}
        | `and.{test_expr then_expr}
            Let {test_expr free}. (reduce_expr env depth free test_expr)
            In
            Let {then_expr free}. (reduce_expr env depth free then_expr)
            In
            {`and.{test_expr then_expr} free}
        | `or.{test_expr else_expr}
            Let {test_expr free}. (reduce_expr env depth free test_expr)
            In
            Let {else_expr free}. (reduce_expr env depth free else_expr)
            In
            {`or.{test_expr else_expr} free}
        | `list.exprs
            Let {exprs free}.
                (LIST.fold exprs {[] free}
                    Func expr {exprs free}.
                        Let {expr free}. (reduce_expr env depth free expr)
                        In
                        {(expr::exprs) free})
            In
            {`list.exprs free}
        | `labeled.{label expr}
            Let {expr free}. (reduce_expr env depth free expr)
            In
            {`labeled.{label expr} free}
        | `match.{expr clauses}
            Let {expr free}. (reduce_expr env depth free expr)
            In
            Let {clauses free}.
                (LIST.fold clauses {[] free}
                    Func {pat body} {clauses free}.
                        Let env.
                            Match pat
                            | `default env
                            | `labeled.{label vars}
                                Let names.
                                    Match vars
                                    | `default []
                                    | `var.name [name]
                                    | `tuple.names names
                                    ;
                                In
                                (LIST.reduce names env
                                    Func env name.
                                        (MAP.insert env
                                            {Let name. name Let depth. depth}))
                            ;
                        In
                        Let {body free}. (reduce_expr env depth free body)
                        In
                        {({pat body}::clauses) free})
            In
            {`match.{expr clauses} free}
        ;
    In
    Let {expr _}.
        Let env. MAP.empty
        Let depth. 0
        Let free. SET.empty
        In
        (reduce_expr env depth free expr)
    In
    expr

Let lift_functions i expr.
    Let reduce_list items i funcs reduce.
        (LIST.fold items {i [] funcs}
            Func item {i items funcs}.
                Let {i item funcs}. (reduce i funcs item)
                In
                {i (item::items) funcs})
    Let reduce_binder reduce_expr i funcs binder.
        Match binder
        | `let.{pat expr}
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            {i `let.{pat expr} funcs}
        | `do.expr
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            {i `do.expr funcs}
        ;
    In
    Define reduce_expr i funcs expr.
        Let reduce_binder i funcs binder.
            (reduce_binder reduce_expr i funcs binder)
        Let trivial. {i expr funcs}
        In
        Match expr
        | `true trivial
        | `false trivial
        | `num._ trivial
        | `str._ trivial
        | `package._ trivial
        | `prim._ trivial
        | `var._ trivial
        | `chain.{expr chain}
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            {i `chain.{expr chain} funcs}
        | `tuple.exprs
            Let {i exprs funcs}. (reduce_list exprs i funcs reduce_expr)
            In
            {i `tuple.exprs funcs}
        | `module.binders
            Let {i binders funcs}. (reduce_list binders i funcs reduce_binder)
            In
            {i `module.binders funcs}
        | `block.{binder_groups expr}
            Let {i binder_groups funcs}.
                (reduce_list binder_groups i funcs
                    Func i funcs binder_group.
                        (reduce_list binder_group i funcs reduce_binder))
            In
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            {i `block.{binder_groups expr} funcs}
        | `app.{func args}
            Let {i func funcs}. (reduce_expr i funcs func)
            In
            Let {i args funcs}. (reduce_list args i funcs reduce_expr)
            In
            {i `app.{func args} funcs}
        | `func.{self free param_pats expr}
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            Let closure. `closure.{i free (LIST.length param_pats)}
            In
            Let func. `func.{i self free param_pats expr}
            In
            {(i + 1) closure (func::funcs)}
        | `goto.expr
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            {i `goto.expr funcs}
        | `switch.{expr clauses}
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            Let {i clauses funcs}.
                (LIST.fold clauses {i [] funcs}
                    Func {pat body} {i clauses funcs}.
                        Let {i body funcs}. (reduce_expr i funcs body)
                        In
                        {i ({pat body}::clauses) funcs})
            In
            {i `switch.{expr clauses} funcs}
        | `cond.clauses
            Let {i clauses funcs}.
                (LIST.fold clauses {i [] funcs}
                    Func {test body} {i clauses funcs}.
                        Let {i test funcs}. (reduce_expr i funcs test)
                        In
                        Let {i body funcs}. (reduce_expr i funcs body)
                        In
                        {i ({test body}::clauses) funcs})
            In
            {i `cond.clauses funcs}
        | `if.{test_expr then_expr else_expr}
            Let {i test_expr funcs}. (reduce_expr i funcs test_expr)
            In
            Let {i then_expr funcs}. (reduce_expr i funcs then_expr)
            In
            Let {i else_expr funcs}. (reduce_expr i funcs else_expr)
            In
            {i `if.{test_expr then_expr else_expr} funcs}
        | `not.expr
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            {i `not.expr funcs}
        | `and.{test_expr then_expr}
            Let {i test_expr funcs}. (reduce_expr i funcs test_expr)
            In
            Let {i then_expr funcs}. (reduce_expr i funcs then_expr)
            In
            {i `and.{test_expr then_expr} funcs}
        | `or.{test_expr else_expr}
            Let {i test_expr funcs}. (reduce_expr i funcs test_expr)
            In
            Let {i else_expr funcs}. (reduce_expr i funcs else_expr)
            In
            {i `or.{test_expr else_expr} funcs}
        | `list.exprs
            Let {i exprs funcs}.
                (LIST.fold exprs {i [] funcs}
                    Func expr {i exprs funcs}.
                        Let {i expr funcs}. (reduce_expr i funcs expr)
                        In
                        {i (expr::exprs) funcs})
            In
            {i `list.exprs funcs}
        | `labeled.{label expr}
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            {i `labeled.{label expr} funcs}
        | `match.{expr clauses}
            Let {i expr funcs}. (reduce_expr i funcs expr)
            In
            Let {i clauses funcs}.
                (LIST.fold clauses {i [] funcs}
                    Func {pat body} {i clauses funcs}.
                        Let {i body funcs}. (reduce_expr i funcs body)
                        In
                        {i ({pat body}::clauses) funcs})
            In
            {i `match.{expr clauses} funcs}
        ;
    In
    (reduce_expr i [] expr)

Where

Let pattern_variables pat.
    Match pat
    | `tuple.vars vars
    | `var.var [var]
    | `ignore []
    ;

Let binder_variables binder.
    Match binder
    | `let.{pat _}
        Match pat
        | `tuple.vars vars
        | `var.var [var]
        ;
    | `do._ []
    ;

Where

Let die. Prim die

Where

Let LIST. Package "list"
Let SEARCH. Package "search"
Let STDIO. Package "stdio"
Let STRING. Package "string"