A structured-data shell in Gleam, inspired by Nushell
0

Configure Feed

Select the types of activity you want to include in your feed.

gleshell / src / gleshell.gleam
3.2 kB 149 lines
1//// gleshell — a structured-data shell in Gleam, inspired by Nushell. 2 3import argv 4import gleam/io 5import gleam/string 6import gleshell/display 7import gleshell/env 8import gleshell/eval 9import gleshell/sys 10import gleshell/value.{Nothing} 11 12pub fn main() -> Nil { 13 case argv.load().arguments { 14 ["-h"] | ["--help"] | ["help"] -> { 15 print_usage() 16 Nil 17 } 18 ["-c", code] -> run_once(code) 19 ["-c"] -> { 20 io.println_error("gleshell: -c requires a command string") 21 halt(2) 22 } 23 [] -> repl(env.new()) 24 args -> run_once(string.join(args, " ")) 25 } 26} 27 28fn print_usage() -> Nil { 29 io.println(string.join( 30 [ 31 "gleshell — Gleam shell inspired by Nushell", 32 "", 33 "Usage:", 34 " gleshell Interactive REPL", 35 " gleshell -c <code> Evaluate a one-liner", 36 " gleshell <code>… Evaluate remaining args as code", 37 "", 38 "Examples:", 39 " gleshell -c 'ls | where type == file | first 5'", 40 " gleshell -c 'range 10 | reverse | first 3'", 41 " gleshell -c 'echo {name: \"gleshell\", cool: true}'", 42 "", 43 "In the REPL, type `help` for built-in commands.", 44 ], 45 "\n", 46 )) 47} 48 49fn run_once(code: String) -> Nil { 50 let env = env.new() 51 case eval.eval_source(env, code) { 52 eval.Quit(code) -> halt(code) 53 eval.Continue(_env, value) -> { 54 print_value(value) 55 case value { 56 value.Fail(_) -> halt(1) 57 _ -> Nil 58 } 59 } 60 } 61} 62 63fn repl(env: env.Env) -> Nil { 64 io.println( 65 "gleshell 0.1 — structured data shell (type `help`, `exit` to quit)", 66 ) 67 repl_loop(env) 68} 69 70fn repl_loop(env: env.Env) -> Nil { 71 let prompt = prompt_for(env) 72 case sys.get_line(prompt) { 73 Error("eof") -> { 74 io.println("") 75 Nil 76 } 77 Error(e) -> { 78 io.println_error("read error: " <> e) 79 Nil 80 } 81 Ok(line) -> { 82 let line = string.trim(line) 83 case line { 84 "" -> repl_loop(env) 85 _ -> 86 case eval.eval_source(env, line) { 87 eval.Quit(code) -> { 88 case code { 89 0 -> Nil 90 _ -> halt(code) 91 } 92 } 93 eval.Continue(env2, value) -> { 94 print_value(value) 95 repl_loop(env2) 96 } 97 } 98 } 99 } 100 } 101} 102 103fn prompt_for(env: env.Env) -> String { 104 let base = basename(env.cwd) 105 "gleshell:" <> base <> "> " 106} 107 108fn basename(path: String) -> String { 109 case string.split(path, "/") { 110 [] -> path 111 parts -> 112 case list_last(parts) { 113 "" -> 114 // path ended with / 115 case parts { 116 [only] -> only 117 _ -> { 118 // take second last non-empty if possible 119 path 120 } 121 } 122 name -> name 123 } 124 } 125} 126 127fn list_last(items: List(String)) -> String { 128 case items { 129 [] -> "" 130 [x] -> x 131 [_, ..rest] -> list_last(rest) 132 } 133} 134 135fn print_value(value: value.Value) -> Nil { 136 case value { 137 Nothing -> Nil 138 _ -> { 139 let text = display.render(value) 140 case text { 141 "" -> Nil 142 t -> io.println(t) 143 } 144 } 145 } 146} 147 148@external(erlang, "erlang", "halt") 149fn halt(status: Int) -> Nil