git.m455.casa

fa

clone url: git://git.m455.casa/fa


esperbuild/espersrc/fennel-0.7.0/src/fennel/repl.fnl

1 ;; This module is the read/eval/print loop; for coding Fennel interactively.
2
3 ;; The most complex thing it does is locals-saving, which allows locals to be
4 ;; preserved in between "chunks"; by default Lua throws away all locals after
5 ;; evaluating each piece of input.
6
7 (local utils (require :fennel.utils))
8 (local parser (require :fennel.parser))
9 (local compiler (require :fennel.compiler))
10 (local specials (require :fennel.specials))
11
12 (fn default-read-chunk [parser-state]
13 (io.write (if (< 0 parser-state.stack-size) ".." ">> "))
14 (io.flush)
15 (let [input (io.read)]
16 (and input (.. input "\n"))))
17
18 (fn default-on-values [xs]
19 (io.write (table.concat xs "\t"))
20 (io.write "\n"))
21
22 (fn default-on-error [errtype err lua-source]
23 (io.write
24 (match errtype
25 "Lua Compile" (.. "Bad code generated - likely a bug with the compiler:\n"
26 "--- Generated Lua Start ---\n"
27 lua-source
28 "--- Generated Lua End ---\n")
29 "Runtime" (.. (compiler.traceback err 4) "\n")
30 _ (: "%s error: %s\n" :format errtype (tostring err)))))
31
32 (local save-source
33 (table.concat ["local ___i___ = 1"
34 "while true do"
35 " local name, value = debug.getlocal(1, ___i___)"
36 " if(name and name ~= \"___i___\") then"
37 " ___replLocals___[name] = value"
38 " ___i___ = ___i___ + 1"
39 " else break end end"] "\n"))
40
41 (fn splice-save-locals [env lua-source]
42 (set env.___replLocals___ (or env.___replLocals___ {}))
43 (let [spliced-source []
44 bind "local %s = ___replLocals___['%s']"]
45 (each [line (lua-source:gmatch "([^\n]+)\n?")]
46 (table.insert spliced-source line))
47 (each [name (pairs env.___replLocals___)]
48 (table.insert spliced-source 1 (bind:format name name)))
49 (when (and (< 1 (# spliced-source))
50 (: (. spliced-source (# spliced-source)) :match "^ *return .*$"))
51 (table.insert spliced-source (# spliced-source) save-source))
52 (table.concat spliced-source "\n")))
53
54 (local commands {})
55
56 (fn command? [input] (input:match "^%s*,"))
57
58 (fn commands.help [_ _ on-values]
59 (on-values ["Welcome to Fennel.
60 This is the REPL where you can enter code to be evaluated.
61 You can also run these repl commands:
62
63 ,help - show this message
64 ,reload module-name - reload the specified module
65 ,reset - erase all repl-local scope
66 ,exit - leave the repl
67
68 Use (doc something) to see descriptions for individual macros and special forms.
69
70 For more information about the language, see https://fennel-lang.org/reference"]))
71
72 (fn reload [module-name env on-values on-error]
73 ;; Sandbox the reload inside the limited environment, if present.
74 (match (pcall (specials.load-code "return require(...)" env) module-name)
75 (true old) (let [_ (tset package.loaded module-name nil)
76 (ok new) (pcall require module-name)
77 ;; keep the old module if reload failed
78 new (if (not ok) (do (on-values new) old) new)]
79 ;; if the module isn't a table then we can't make changes
80 ;; which affect already-loaded code, but if it is then we
81 ;; should splice new values into the existing table and
82 ;; remove values that are gone.
83 (when (and (= (type old) :table) (= (type new) :table))
84 (each [k v (pairs new)]
85 (tset old k v))
86 (each [k (pairs old)]
87 (when (= nil (. new k))
88 (tset old k nil)))
89 (tset package.loaded module-name old))
90 (on-values [:ok]))
91 (false msg) (on-error "Runtime" (pick-values 1 (msg:gsub "\n.*" "")))))
92
93 (fn commands.reload [read env on-values on-error]
94 (match (pcall read)
95 (true true module-sym) (reload (tostring module-sym) env on-values on-error)
96 (false ?parse-ok ?msg) (on-error "Parse" (or ?msg ?parse-ok))))
97
98 (fn commands.reset [_ env on-values]
99 (set env.___replLocals___ {})
100 (on-values [:ok]))
101
102 (fn run-command [input read loop env on-values on-error]
103 (let [command-name (input:match ",([^%s/]+)")]
104 (match (. commands command-name)
105 command (command read env on-values on-error)
106 _ (when (not= "exit" command-name)
107 (on-values ["Unknown command" command-name])))
108 (when (not= "exit" command-name)
109 (loop))))
110
111 (fn completer [env scope text]
112 (let [matches []
113 input-fragment (text:gsub ".*[%s)(]+" "")]
114 (fn add-partials [input tbl prefix] ; add partial key matches in tbl
115 (each [k (utils.allpairs tbl)]
116 (let [k (if (or (= tbl env) (= tbl env.___replLocals___))
117 (. scope.unmanglings k)
118 k)]
119 (when (and (< (# matches) 2000) ; stop explosion on too many items
120 (= (type k) "string")
121 (= input (k:sub 0 (# input))))
122 (table.insert matches (.. prefix k))))))
123 (fn add-matches [input tbl prefix] ; add matches, descending into tbl fields
124 (let [prefix (if prefix (.. prefix ".") "")]
125 (if (not (input:find "%.")) ; no more dots, so add matches
126 (add-partials input tbl prefix)
127 (let [(head tail) (input:match "^([^.]+)%.(.*)")
128 raw-head (if (or (= tbl env) (= tbl env.___replLocals___))
129 (. scope.manglings head)
130 head)]
131 (when (= (type (. tbl raw-head)) "table")
132 (add-matches tail (. tbl raw-head) (.. prefix head)))))))
133
134 (add-matches input-fragment (or scope.specials []))
135 (add-matches input-fragment (or scope.macros []))
136 (add-matches input-fragment (or env.___replLocals___ []))
137 (add-matches input-fragment env)
138 (add-matches input-fragment (or env._ENV env._G []))
139 matches))
140
141 (fn repl [options]
142 (let [old-root-options utils.root.options
143 env (if options.env
144 (specials.wrap-env options.env)
145 (setmetatable {} {:__index (or _G._ENV _G)}))
146 save-locals? (and (not= options.saveLocals false)
147 env.debug env.debug.getlocal)
148 opts {}
149 _ (each [k v (pairs options)] (tset opts k v))
150 read-chunk (or opts.readChunk default-read-chunk)
151 on-values (or opts.onValues default-on-values)
152 on-error (or opts.onError default-on-error)
153 pp (or opts.pp tostring)
154 ;; make parser
155 (byte-stream clear-stream) (parser.granulate read-chunk)
156 chars []
157 (read reset) (parser.parser (fn [parser-state]
158 (let [c (byte-stream parser-state)]
159 (table.insert chars c)
160 c)))
161 scope (compiler.make-scope)]
162
163 ;; use metadata unless we've specifically disabled it
164 (set opts.useMetadata (not= options.useMetadata false))
165 (when (= opts.allowedGlobals nil)
166 (set opts.allowedGlobals (specials.current-global-names opts.env)))
167
168 (when opts.registerCompleter
169 (opts.registerCompleter (partial completer env scope)))
170
171 (fn print-values [...]
172 (let [vals [...]
173 out []]
174 (set (env._ env.__) (values (. vals 1) vals))
175 ;; utils.map won't work here because of sparse tables
176 (for [i 1 (select :# ...)]
177 (table.insert out (pp (. vals i))))
178 (on-values out)))
179
180 (fn loop []
181 (each [k (pairs chars)] (tset chars k nil))
182 (let [(ok parse-ok? x) (pcall read)
183 src-string (string.char ((or _G.unpack table.unpack) chars))]
184 (set utils.root.options opts)
185 (if (not ok)
186 (do (on-error "Parse" parse-ok?)
187 (clear-stream)
188 (reset)
189 (loop))
190 (command? src-string) (run-command src-string read loop env
191 on-values on-error)
192 (when parse-ok? ; if this is false, we got eof
193 (match (pcall compiler.compile x {:correlate opts.correlate
194 :source src-string
195 :scope scope
196 :useMetadata opts.useMetadata
197 :moduleName opts.moduleName
198 :assert-compile opts.assert-compile
199 :parse-error opts.parse-error})
200 (false msg) (do (clear-stream)
201 (on-error "Compile" msg))
202 (true src) (let [src (if save-locals?
203 (splice-save-locals env src)
204 src)]
205 (match (pcall specials.load-code src env)
206 (false msg) (do (clear-stream)
207 (on-error "Lua Compile" msg src))
208 (_ chunk) (xpcall #(print-values (chunk))
209 (partial on-error "Runtime")))))
210 (set utils.root.options old-root-options)
211 (loop)))))
212 (loop)))