main

Where

Let (main args)
    Let root_path (parse_command_line args)
    In
    (MAIN.run
        (MAIN.bind1 (parse_packages root_path)
            Func packages.
                Let paths (LIST.map packages (Func p. p.path))
                Let program (compile packages)
                In
                (MAIN.sequence2
                    (write_dependencies_file root_path paths)
                    (emit root_path program))))

Where

Let (parse_command_line args)
    Let usage "usage: 84 path"
    In
    Let (pop_arg args)
        Match args
        | `nil (OS.die usage)
        | `cons.pair pair
        ;
    In
    Let {command_name args} (pop_arg args)
    In
    Let {root_path args} (pop_arg args)
    In
    Match args
    | `nil root_path
    | `cons._ (OS.die usage)
    ;

Let (parse_packages root_path)
    Let STRING_SET (SEARCH.SET STRING.compare)
    In
    Let (parse_file path)
        Let file_name (STRING.append path ".84")
        In
        (MAIN.bind1 (MAIN.read_file file_name)
            Func text.
                Match (PARSE.file text)
                | `succeed.expr (MAIN.pure expr)
                | `fail.{message i}
                    Do (SYNTAX_ERROR.show file_name text message i)
                    In
                    (MAIN.die "Compilation failed.")
                ;)
    Let (push_paths stack filter paths)
        (LIST.reduce paths {stack filter}
            Func {stack filter} path.
                Match (STRING_SET.search filter path)
                | `just._ {stack filter}
                | `nothing {(path :: stack) (STRING_SET.insert filter path)}
                ;)
    In
    Define (parsing packages stack filter)
        Match stack
        | `nil (MAIN.pure packages)
        | `cons.{path stack}
            (MAIN.bind1 (parse_file path)
                Func expr.
                    Let imports (PACKAGE.gather_imports expr)
                    In
                    Let package {Record path imports expr}
                    Let {stack filter} (push_paths stack filter imports)
                    In
                    (parsing (package :: packages) stack filter))
        ;
    In
    (MAIN.lift1 (parsing [] [root_path] (STRING_SET.new [root_path]))
        PACKAGE.sort_by_dependence)

Let (write_dependencies_file root_path paths)
    Let file_name (STRING.append root_path ".c.d")
    In
    (MAIN.bind1 (MAIN.file_create file_name)
        Func file.
            Define (writing paths)
                Match paths
                | `nil
                    (MAIN.sequence2
                        (MAIN.file_write file "\n")
                        (MAIN.file_close file))
                | `cons.{path paths}
                    (MAIN.sequence2
                        (MAIN.file_write file (STRING.concat [" " path ".84"]))
                        (writing paths))
                ;
            In
            (MAIN.sequence2
                (MAIN.file_write file (STRING.append root_path ".c:"))
                (writing paths)))

Let (compile program)
    Let {_ program}
        (LIST.fold program {1 []}
            Func package {i packages}.
                Let {init i functions}
                    (COMPILE.lift_functions i
                        (COMPILE.collect_free_variables package.expr))
                In
                Let package
                    {Record
                        path:package.path
                        imports:package.imports
                        init
                        functions}
                In
                {i (package :: packages)})
    In
    (COMPILE.collect_constants program)

Let (emit root_path program)
    (MAIN.lift1 (MAIN.file_create (STRING.append root_path ".c"))
        Func file.
            Let (write string)
                (OS.file_write file string)
            In
            Do (C.emit (C.elaborate program) write)
            Do (OS.file_close file)
            In
            {})

Where

Let C Package "c"
Let COMPILE Package "compile"
Let LIST Package "list"
Let MAIN Package "main"
Let OS Package "os"
Let PACKAGE Package "package"
Let PARSE Package "parse"
Let SEARCH Package "search"
Let STRING Package "string"
Let SYNTAX_ERROR Package "syntax_error"