gluon_repl 0.7.0

REPL for gluon. A static, type inferred programming language for application embedding
let prelude = import! std.prelude
let io = import! std.io
let map @ { Map } = import! std.map
let { Bool } = import! std.bool
let { Option } = import! std.option
let { Result } = import! std.result
let string = import! std.string
let { ref, load, (<-) } = import! std.reference
let rustyline = import! rustyline
let repl_prim = import! repl.prim
let { wrap, (*>) } = prelude.make_Applicative io.applicative
let { flat_map, (>>=) } = prelude.make_Monad io.monad
let { append = (++) } = string.semigroup
let ord_map @ { singleton, find } = map.make string.ord
let traverse_with_key = ord_map.traverse_with_key io.applicative
let { (<>) } = prelude.make_Semigroup ord_map.semigroup
let { empty } = ord_map.monoid


let load_file filename : String -> IO String =
    let last_slash =
        match string.rfind filename "/" with
        | None -> 0
        | Some i -> i + 1
    let modulename = string.slice filename last_slash (string.len filename - 3)
<<<<<<< HEAD
    let read_result =
        let action =
            do x = io.read_file_to_string filename
            wrap (Ok x)
        io.catch action (\err -> wrap (Err err))
    do result = read_result
    match result with
    | Ok expr -> io.load_script modulename expr
    | Err msg -> wrap msg
=======
    let action =
        do expr = io.read_file_to_string filename
        do result = io.load_script modulename expr
        wrap result
    io.catch action (\err -> wrap err)
>>>>>>> 4117b2b3... fix(vm): Allow async functions to run within the io primitives

type Cmd = { info : String, action : String -> IO Bool }

let commands : Map String Cmd =
    let print_result result =
        match result with
        | Ok x -> io.println x
        | Err x -> io.println x

    let commands = ref empty
    let cmds =
        singleton "q" { info = "Quit the REPL", action = \_ -> wrap False } <> singleton "t" {
                    info = "Prints the type with an expression",
                    action = \arg -> repl_prim.type_of_expr arg >>= print_result *> wrap True,
                }
            <> singleton "i" {
                    info = "Prints information about the given name",
                    action = \arg -> repl_prim.find_info arg >>= print_result *> wrap True,
                }
            <> singleton "k" {
                    info = "Prints the kind with the given type",
                    action = \arg -> repl_prim.find_kind arg >>= print_result *> wrap True,
                }
            <> singleton "l" {
                    info = "Loads the file at \'folder/module.ext\' and stores it at \'module\'",
                    action = \arg -> load_file arg >>= io.println *> wrap True,
                }
            <> singleton "h" {
                    info = "Print this help",
                    action = \_ ->
                        let print_header = io.println "Available commands\n"
                        let print_cmd key cmd : String -> Cmd -> IO () =
                            io.println ("    :" ++ key ++ " " ++ cmd.info)

                        print_header *> traverse_with_key print_cmd (load commands)
                            *> wrap True,
                }
    commands <- cmds
    load commands

let do_command line : String -> IO Bool =
    if string.len line >= 2 then
        let cmd = string.slice line 1 2
        let arg =
            if string.len line >= 3
            then string.trim (string.slice line 3 (string.len line))
            else ""
        match find cmd commands with
        | Some command -> command.action arg
        | None -> io.println ("Unknown command \'" ++ cmd ++ "\'") *> wrap True
    else
        io.println "Expected a command such as `:h`"
            *> wrap True

let store line : String -> IO Bool =
    let line = string.trim line
    match string.find line " " with
    | Some bind_end ->
        let binding = string.slice line 0 bind_end
        let expr = string.slice line bind_end (string.len line)
        io.load_script binding expr *> wrap True
    | None -> io.println "Expected binding in definition" *> wrap True

let loop editor : Editor -> IO () =
    let run_line line =
        if string.is_empty (string.trim line)
        then wrap True
        else
            if string.starts_with line ":"
            then do_command line
            else io.catch (repl_prim.eval_line line) wrap >>= io.println *> wrap True

    do line_opt = rustyline.readline editor "> "
    match line_opt with
    | None -> wrap ()
    | Some line ->
        do continue = run_line line
        if continue then loop editor else wrap ()

let run x : () -> IO () =
    io.println "gluon (:h for help, :q to quit)"
        *> loop (rustyline.new_editor ())

run