git.m455.casa

fa

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


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

1 ;; This module contains all the special forms; all built in Fennel constructs
2 ;; which cannot be implemented as macros. It also contains some core compiler
3 ;; functionality which is kept in this module for circularity reasons.
4
5 (local utils (require "fennel.utils"))
6 (local parser (require "fennel.parser"))
7 (local compiler (require "fennel.compiler"))
8 (local unpack (or _G.unpack table.unpack))
9
10 (local SPECIALS compiler.scopes.global.specials)
11
12 (fn wrap-env [env]
13 "Convert a fennel environment table to a Lua environment table.
14 This means automatically unmangling globals when getting a value,
15 and mangling values when setting a value. This means the original env
16 will see its values updated as expected, regardless of mangling rules."
17 (setmetatable
18 [] {:__index (fn [_ key]
19 (if (= (type key) "string")
20 (. env (compiler.global-unmangling key))
21 (. env key)))
22 :__newindex (fn [_ key value]
23 (if (= (type key) "string")
24 (tset env (compiler.global-unmangling key) value)
25 (tset env key value)))
26 ;; checking the __pairs metamethod won't work automatically in Lua 5.1
27 ;; sadly, but it's important for 5.2+ and can be done manually in 5.1
28 :__pairs (fn []
29 (fn putenv [k v]
30 (values (if (= (type k) "string")
31 (compiler.global-unmangling k) k) v))
32 (values next (utils.kvmap env putenv) nil))}))
33
34 (fn current-global-names [env]
35 (utils.kvmap (or env _G) compiler.global-unmangling))
36
37 (fn load-code [code environment filename]
38 "Load Lua code with an environment in all recent Lua versions"
39 (let [environment (or (or environment _ENV) _G)]
40
41 (if (and _G.setfenv _G.loadstring)
42 (let [f (assert (_G.loadstring code filename))]
43 (_G.setfenv f environment)
44 f)
45 (assert (load code filename "t" environment)))))
46
47 (fn doc* [tgt name]
48 "Return a docstring for tgt."
49 (if (not tgt)
50 (.. name " not found")
51 (let [docstring (: (: (or (: compiler.metadata "get" tgt "fnl/docstring")
52 "#<undocumented>") :gsub "\n$" "")
53 :gsub "\n" "\n ")]
54 (if (= (type tgt) "function")
55 (let [arglist (table.concat (or (: compiler.metadata "get"
56 tgt "fnl/arglist")
57 ["#<unknown-arguments>"]) " ")]
58 (string.format "(%s%s%s)\n %s" name
59 (if (> (# arglist) 0) " " "") arglist docstring))
60 (string.format "%s\n %s" name docstring)))))
61
62 ;; TODO: replace this with using the special fn's own docstring
63 (fn doc-special [name arglist docstring]
64 "Add a docstring to a special form."
65 (tset compiler.metadata (. SPECIALS name) {:fnl/arglist arglist
66 :fnl/docstring docstring}))
67
68 (fn compile-do [ast scope parent start]
69 "Compile a list of forms for side effects."
70 (let [start (or start 2)
71 len (# ast)
72 sub-scope (compiler.make-scope scope)]
73 (for [i start len]
74 (compiler.compile1 (. ast i) sub-scope parent {:nval 0}))))
75
76 (fn SPECIALS.do [ast scope parent opts start chunk sub-scope pre-syms]
77 "Implements a do statement, starting at the 'start'-th element.
78 By default, start is 2."
79 (let [start (or start 2)
80 sub-scope (or sub-scope (compiler.make-scope scope))
81 chunk (or chunk [])
82 len (# ast)
83 retexprs {:returned true}]
84
85 (fn compile-body [outer-target outer-tail outer-retexprs]
86 (if (< len start)
87 ;; In the unlikely event we do a do with no arguments
88 (compiler.compile1 nil sub-scope chunk {:tail outer-tail
89 :target outer-target})
90 ;; There will be side-effects
91 (for [i start len]
92 (let [subopts {:nval (or (and (not= i len) 0) opts.nval)
93 :tail (or (and (= i len) outer-tail) nil)
94 :target (or (and (= i len) outer-target) nil)}
95 _ (utils.propagate-options opts subopts)
96 subexprs (compiler.compile1 (. ast i) sub-scope chunk subopts)]
97 (when (not= i len)
98 (compiler.keep-side-effects subexprs parent nil (. ast i))))))
99 (compiler.emit parent chunk ast)
100 (compiler.emit parent "end" ast)
101 (or outer-retexprs retexprs))
102
103 ;; See if we need special handling to get the return values of the do block
104 (if (or opts.target (= opts.nval 0) opts.tail)
105 (do (compiler.emit parent "do" ast)
106 (compile-body opts.target opts.tail))
107 opts.nval
108 ;; generate a local target
109 (let [syms []]
110 (for [i 1 opts.nval]
111 (let [s (or (and pre-syms (. pre-syms i)) (compiler.gensym scope))]
112 (tset syms i s)
113 (tset retexprs i (utils.expr s "sym"))))
114 (let [outer-target (table.concat syms ", ")]
115 (compiler.emit parent (string.format "local %s" outer-target) ast)
116 (compiler.emit parent "do" ast)
117 (compile-body outer-target opts.tail)))
118 ;; we will use an IIFE for the do
119 (let [fname (compiler.gensym scope)
120 fargs (if scope.vararg "..." "")]
121 (compiler.emit parent (string.format "local function %s(%s)"
122 fname fargs) ast)
123 (utils.hook :do ast sub-scope)
124 (compile-body nil true
125 (utils.expr (.. fname "(" fargs ")") :statement))))))
126
127 (doc-special "do" ["..."] "Evaluate multiple forms; return last value.")
128
129 (fn SPECIALS.values [ast scope parent]
130 "Unlike most expressions and specials, 'values' resolves with multiple
131 values, one for each argument, allowing multiple return values. The last
132 expression can return multiple arguments as well, allowing for more than
133 the number of expected arguments."
134 (let [len (# ast)
135 exprs []]
136 (for [i 2 len]
137 (let [subexprs (compiler.compile1 (. ast i) scope parent
138 {:nval (and (not= i len) 1)})]
139 (table.insert exprs (. subexprs 1))
140 (when (= i len)
141 (for [j 2 (# subexprs)]
142 (table.insert exprs (. subexprs j))))))
143 exprs))
144
145 (doc-special "values" ["..."]
146 "Return multiple values from a function. Must be in tail position.")
147
148 (fn set-fn-metadata [arg-list docstring parent fn-name]
149 (when utils.root.options.useMetadata
150 ;; TODO: show destructured args properly instead of replacing
151 (let [args (utils.map arg-list (fn [v] (if (utils.table? v)
152 "\"#<table>\""
153 (: "\"%s\"" :format
154 (tostring v)))))
155 meta-fields ["\"fnl/arglist\"" (.. "{" (table.concat args ", ") "}")]]
156 (when docstring
157 (table.insert meta-fields "\"fnl/docstring\"")
158 (table.insert meta-fields (.. "\"" (-> docstring
159 (: :gsub "%s+$" "")
160 (: :gsub "\\" "\\\\")
161 (: :gsub "\n" "\\n")
162 (: :gsub "\"" "\\\"")) "\"")))
163 (let [meta-str (: "require(\"%s\").metadata"
164 :format (or utils.root.options.moduleName "fennel"))]
165 (compiler.emit parent (: "pcall(function() %s:setall(%s, %s) end)"
166 :format meta-str fn-name
167 (table.concat meta-fields ", ")))))))
168
169 (fn get-fn-name [ast scope fn-name multi]
170 (if (and fn-name (not= (. fn-name 1) :nil))
171 (values (if (not multi)
172 (compiler.declare-local fn-name [] scope ast)
173 (. (compiler.symbol-to-expression fn-name scope) 1))
174 (not multi)
175 3)
176 (values (compiler.gensym scope) true 2)))
177
178 (fn SPECIALS.fn [ast scope parent]
179 (let [f-scope (doto (compiler.make-scope scope)
180 (tset :vararg false))
181 f-chunk []
182 fn-sym (utils.sym? (. ast 2))
183 multi (and fn-sym (utils.multi-sym? (. fn-sym 1)))
184 (fn-name local-fn? index) (get-fn-name ast scope fn-sym multi)
185 arg-list (compiler.assert (utils.table? (. ast index))
186 "expected parameters table" ast)]
187 (compiler.assert (or (not multi) (not multi.multi-sym-method-call))
188 (.. "unexpected multi symbol " (tostring fn-name)) fn-sym)
189
190 (fn get-arg-name [arg]
191 (if (utils.varg? arg)
192 (do (compiler.assert (= arg (. arg-list (# arg-list)))
193 "expected vararg as last parameter" ast)
194 (set f-scope.vararg true)
195 "...")
196 (and (utils.sym? arg)
197 (not= (utils.deref arg) "nil")
198 (not (utils.multi-sym? (utils.deref arg))))
199 (compiler.declare-local arg [] f-scope ast)
200 (utils.table? arg)
201 (let [raw (utils.sym (compiler.gensym scope))
202 declared (compiler.declare-local raw [] f-scope ast)]
203 (compiler.destructure arg raw ast f-scope f-chunk {:declaration true
204 :nomulti true})
205 declared)
206 (compiler.assert false
207 (: "expected symbol for function parameter: %s"
208 :format (tostring arg)) (. ast 2))))
209
210 (let [arg-name-list (utils.map arg-list get-arg-name)
211 (index docstring) (if (and (= (type (. ast (+ index 1))) :string)
212 (< (+ index 1) (# ast)))
213 (values (+ index 1) (. ast (+ index 1)))
214 (values index nil))]
215
216 (for [i (+ index 1) (# ast)]
217 (compiler.compile1 (. ast i) f-scope f-chunk
218 {:nval (or (and (not= i (# ast)) 0) nil)
219 :tail (= i (# ast))}))
220 (compiler.emit parent (string.format (if local-fn?
221 "local function %s(%s)"
222 "%s = function(%s)") fn-name
223 (table.concat arg-name-list ", ")) ast)
224 (compiler.emit parent f-chunk ast)
225 (compiler.emit parent "end" ast)
226 (set-fn-metadata arg-list docstring parent fn-name))
227 (utils.hook :fn ast f-scope)
228 (utils.expr fn-name "sym")))
229
230 (doc-special "fn" ["name?" "args" "docstring?" "..."]
231 (.. "Function syntax. May optionally include a name and docstring.
232 If a name is provided, the function will be bound in the current scope.
233 When called with the wrong number of args, excess args will be discarded
234 and lacking args will be nil, use lambda for arity-checked functions."))
235
236 ;; FORBIDDEN KNOWLEDGE:
237 ;; (lua "print('hello!')") -> prints hello, evaluates to nil
238 ;; (lua "print 'hello!'" "10") -> prints hello, evaluates to the number 10
239 ;; (lua nil "{1,2,3}") -> Evaluates to a table literal
240 (fn SPECIALS.lua [ast _ parent]
241 (compiler.assert (or (= (# ast) 2) (= (# ast) 3))
242 "expected 1 or 2 arguments" ast)
243 (when (not= (. ast 2) nil)
244 (table.insert parent {:ast ast :leaf (tostring (. ast 2))}))
245 (when (= (# ast) 3)
246 (tostring (. ast 3))))
247
248 (fn SPECIALS.doc [ast scope parent]
249 (assert utils.root.options.useMetadata
250 "can't look up doc with metadata disabled.")
251 (compiler.assert (= (# ast) 2) "expected one argument" ast)
252 (let [target (utils.deref (. ast 2))
253 special-or-macro (or (. scope.specials target) (. scope.macros target))]
254 (if special-or-macro
255 (: "print([[%s]])" :format (doc* special-or-macro target))
256 (let [value (tostring (. (compiler.compile1 (. ast 2)
257 scope parent {:nval 1}) 1))]
258 ;; need to require here since the metadata is stored in the module
259 ;; and we need to make sure we look it up in the same module it was
260 ;; declared from.
261 (: "print(require('%s').doc(%s, '%s'))" :format
262 (or utils.root.options.moduleName "fennel") value
263 (tostring (. ast 2)))))))
264
265 (doc-special
266 "doc" ["x"]
267 "Print the docstring and arglist for a function, macro, or special form.")
268
269 (fn dot [ast scope parent]
270 "Table lookup; equivalent to tbl[] in Lua."
271 (compiler.assert (< 1 (# ast)) "expected table argument" ast)
272 (let [len (# ast)
273 lhs (compiler.compile1 (. ast 2) scope parent {:nval 1})]
274 (if (= len 2)
275 (tostring (. lhs 1))
276 (let [indices []]
277 (for [i 3 len]
278 (let [index (. ast i)]
279 (if (and (= (type index) :string)
280 (utils.valid-lua-identifier? index))
281 (table.insert indices (.. "." index))
282 (let [[index] (compiler.compile1 index scope parent {:nval 1})]
283 (table.insert indices (.. "[" (tostring index) "]"))))))
284 ;; Extra parens are needed for table literals.
285 (if (utils.table? (. ast 2))
286 (.. "(" (tostring (. lhs 1)) ")" (table.concat indices))
287 (.. (tostring (. lhs 1)) (table.concat indices)))))))
288
289 (tset SPECIALS "." dot)
290
291 (doc-special
292 "." ["tbl" "key1" "..."]
293 "Look up key1 in tbl table. If more args are provided, do a nested lookup.")
294
295 (fn SPECIALS.global [ast scope parent]
296 (compiler.assert (= (# ast) 3) "expected name and value" ast)
297 (compiler.destructure (. ast 2) (. ast 3) ast scope parent {:forceglobal true
298 :nomulti true})
299 nil)
300
301 (doc-special "global" ["name" "val"] "Set name as a global with val.")
302
303 (fn SPECIALS.set [ast scope parent]
304 (compiler.assert (= (# ast) 3) "expected name and value" ast)
305 (compiler.destructure (. ast 2) (. ast 3) ast scope parent {:noundef true})
306 nil)
307
308 (doc-special
309 "set" ["name" "val"]
310 "Set a local variable to a new value. Only works on locals using var.")
311
312 (fn set-forcibly!* [ast scope parent]
313 (compiler.assert (= (# ast) 3) "expected name and value" ast)
314 (compiler.destructure (. ast 2) (. ast 3) ast scope parent {:forceset true})
315 nil)
316
317 (tset SPECIALS :set-forcibly! set-forcibly!*)
318
319 (fn local* [ast scope parent]
320 (compiler.assert (= (# ast) 3) "expected name and value" ast)
321 (compiler.destructure (. ast 2) (. ast 3) ast scope parent {:declaration true
322 :nomulti true})
323 nil)
324 (tset SPECIALS "local" local*)
325
326 (doc-special "local" ["name" "val"] "Introduce new top-level immutable local.")
327
328 (fn SPECIALS.var [ast scope parent]
329 (compiler.assert (= (# ast) 3) "expected name and value" ast)
330 (compiler.destructure (. ast 2) (. ast 3) ast scope parent {:declaration true
331 :isvar true
332 :nomulti true})
333 nil)
334
335 (doc-special "var" ["name" "val"] "Introduce new mutable local.")
336
337
338 (fn SPECIALS.let [ast scope parent opts]
339 (let [bindings (. ast 2)
340 pre-syms []]
341 (compiler.assert (or (utils.list? bindings) (utils.table? bindings))
342 "expected binding table" ast)
343 (compiler.assert (= (% (# bindings) 2) 0)
344 "expected even number of name/value bindings" (. ast 2))
345 (compiler.assert (>= (# ast) 3) "expected body expression" (. ast 1))
346 ;; we have to gensym the binding for the let body's return value before
347 ;; compiling the binding vector, otherwise there's a possibility to conflict
348 (for [_ 1 (or opts.nval 0)]
349 (table.insert pre-syms (compiler.gensym scope)))
350 (let [sub-scope (compiler.make-scope scope)
351 sub-chunk []]
352 (for [i 1 (# bindings) 2]
353 (compiler.destructure (. bindings i) (. bindings (+ i 1))
354 ast sub-scope sub-chunk {:declaration true
355 :nomulti true}))
356 (SPECIALS.do ast scope parent opts 3 sub-chunk sub-scope pre-syms))))
357
358 (doc-special
359 "let" ["[name1 val1 ... nameN valN]" "..."]
360 "Introduces a new scope in which a given set of local bindings are used.")
361
362 (fn SPECIALS.tset [ast scope parent]
363 "For setting items in a table."
364 (compiler.assert (> (# ast) 3) "expected table, key, and value arguments" ast)
365 (let [root (. (compiler.compile1 (. ast 2) scope parent {:nval 1}) 1)
366 keys []]
367 (for [i 3 (- (# ast) 1)]
368 (let [[key] (compiler.compile1 (. ast i) scope parent {:nval 1})]
369 (table.insert keys (tostring key))))
370 (let [value (. (compiler.compile1 (. ast (# ast)) scope parent {:nval 1}) 1)
371 rootstr (tostring root)
372 ;; Prefix 'do end ' so parens are not ambiguous (grouping or fn call?)
373 fmtstr (if (: rootstr :match "^{") "do end (%s)[%s] = %s" "%s[%s] = %s")]
374 (compiler.emit parent (: fmtstr :format (tostring root)
375 (table.concat keys "][") (tostring value)) ast))))
376
377 (doc-special
378 "tset" ["tbl" "key1" "..." "keyN" "val"]
379 "Set the value of a table field. Can take additional keys to set
380 nested values, but all parents must contain an existing table.")
381
382 (fn calculate-target [scope opts]
383 (if (not (or opts.tail opts.target opts.nval))
384 (values :iife true nil)
385 (and opts.nval (not= opts.nval 0) (not opts.target))
386 (let [accum []
387 target-exprs []]
388 ;; We need to create a target
389 (for [i 1 opts.nval]
390 (let [s (compiler.gensym scope)]
391 (tset accum i s)
392 (tset target-exprs i (utils.expr s :sym))))
393 (values :target opts.tail (table.concat accum ", ") target-exprs))
394 (values :none opts.tail opts.target)))
395
396 ;; TODO: refactor; too long!
397 (fn if* [ast scope parent opts]
398 (let [do-scope (compiler.make-scope scope)
399 branches []
400 (wrapper inner-tail inner-target target-exprs) (calculate-target scope opts)
401 body-opts {:nval opts.nval :tail inner-tail :target inner-target}]
402
403 (fn compile-body [i]
404 (let [chunk []
405 cscope (compiler.make-scope do-scope)]
406 (compiler.keep-side-effects (compiler.compile1 (. ast i) cscope chunk
407 body-opts) chunk nil
408 (. ast i))
409 {:chunk chunk :scope cscope}))
410
411 (for [i 2 (- (# ast) 1) 2]
412 (let [condchunk []
413 res (compiler.compile1 (. ast i) do-scope condchunk {:nval 1})
414 cond (. res 1)
415 branch (compile-body (+ i 1))]
416 (set branch.cond cond)
417 (set branch.condchunk condchunk)
418 (set branch.nested (and (not= i 2) (= (next condchunk nil) nil)))
419 (table.insert branches branch)))
420
421 ;; Emit code
422 (let [has-else? (and (> (# ast) 3) (= (% (# ast) 2) 0))
423 else-branch (and has-else? (compile-body (# ast)))
424 s (compiler.gensym scope)
425 buffer []]
426 (var last-buffer buffer)
427 (for [i 1 (# branches)]
428 (let [branch (. branches i)
429 fstr (if (not branch.nested) "if %s then" "elseif %s then")
430 cond (tostring branch.cond)
431 cond-line (if (and (= cond :true) branch.nested (= i (# branches)))
432 :else
433 (: fstr :format cond))]
434 (if branch.nested
435 (compiler.emit last-buffer branch.condchunk ast)
436 (each [_ v (ipairs branch.condchunk)]
437 (compiler.emit last-buffer v ast)))
438 (compiler.emit last-buffer cond-line ast)
439 (compiler.emit last-buffer branch.chunk ast)
440 (if (= i (# branches))
441 (do
442 (if has-else?
443 (do (compiler.emit last-buffer "else" ast)
444 (compiler.emit last-buffer else-branch.chunk ast))
445 ;; TODO: Consolidate use of cond-line ~= "else" with has-else
446 (and inner-target (not= cond-line :else))
447 (do (compiler.emit last-buffer "else" ast)
448 (compiler.emit last-buffer (: "%s = nil" :format
449 inner-target) ast)))
450 (compiler.emit last-buffer "end" ast))
451 (not (. (. branches (+ i 1)) "nested"))
452 (let [next-buffer []]
453 (compiler.emit last-buffer "else" ast)
454 (compiler.emit last-buffer next-buffer ast)
455 (compiler.emit last-buffer "end" ast)
456 (set last-buffer next-buffer)))))
457 ;; Emit if
458 (if (= wrapper :iife)
459 (let [iifeargs (or (and scope.vararg "...") "")]
460 (compiler.emit parent (: "local function %s(%s)" :format
461 (tostring s) iifeargs) ast)
462 (compiler.emit parent buffer ast)
463 (compiler.emit parent "end" ast)
464 (utils.expr (: "%s(%s)" :format (tostring s) iifeargs) :statement))
465 (= wrapper :none) ; Splice result right into code
466 (do (for [i 1 (# buffer)]
467 (compiler.emit parent (. buffer i) ast))
468 {:returned true})
469 ;; wrapper is target
470 (do (compiler.emit parent (: "local %s" :format inner-target) ast)
471 (for [i 1 (# buffer)]
472 (compiler.emit parent (. buffer i) ast))
473 target-exprs)))))
474
475 (tset SPECIALS "if" if*)
476
477 (doc-special
478 "if" ["cond1" "body1" "..." "condN" "bodyN"]
479 "Conditional form.
480 Takes any number of condition/body pairs and evaluates the first body where
481 the condition evaluates to truthy. Similar to cond in other lisps.")
482
483 (fn SPECIALS.each [ast scope parent]
484 (compiler.assert (>= (# ast) 3) "expected body expression" (. ast 1))
485 (let [binding (compiler.assert (utils.table? (. ast 2))
486 "expected binding table" ast)
487 iter (table.remove binding (# binding)) ; last item is iterator call
488 destructures []
489 new-manglings []
490 sub-scope (compiler.make-scope scope)]
491
492 (fn destructure-binding [v]
493 (if (utils.sym? v)
494 (compiler.declare-local v [] sub-scope ast new-manglings)
495 (let [raw (utils.sym (compiler.gensym sub-scope))]
496 (tset destructures raw v)
497 (compiler.declare-local raw [] sub-scope ast))))
498
499 (let [bind-vars (utils.map binding destructure-binding)
500 vals (compiler.compile1 iter sub-scope parent)
501 val-names (utils.map vals tostring)
502 chunk []]
503 (compiler.emit parent (: "for %s in %s do" :format
504 (table.concat bind-vars ", ")
505 (table.concat val-names ", ")) ast)
506 (each [raw args (utils.stablepairs destructures)]
507 (compiler.destructure args raw ast sub-scope chunk {:declaration true
508 :nomulti true}))
509 (compiler.apply-manglings sub-scope new-manglings ast)
510 (compile-do ast sub-scope chunk 3)
511 (compiler.emit parent chunk ast)
512 (compiler.emit parent "end" ast))))
513
514 (doc-special
515 "each" ["[key value (iterator)]" "..."]
516 "Runs the body once for each set of values provided by the given iterator.
517 Most commonly used with ipairs for sequential tables or pairs for undefined
518 order, but can be used with any iterator.")
519
520 (fn while* [ast scope parent]
521 (let [len1 (# parent)
522 condition (. (compiler.compile1 (. ast 2) scope parent {:nval 1}) 1)
523 len2 (# parent)
524 sub-chunk []]
525 (if (not= len1 len2)
526 ;; compound condition; move new compilation to subchunk
527 (do
528 (for [i (+ len1 1) len2]
529 (table.insert sub-chunk (. parent i))
530 (tset parent i nil))
531 (compiler.emit parent "while true do" ast)
532 (compiler.emit sub-chunk (: "if not %s then break end"
533 :format (. condition 1)) ast))
534 ;; simple condition
535 (compiler.emit parent (.. "while " (tostring condition) " do") ast))
536 (compile-do ast (compiler.make-scope scope) sub-chunk 3)
537 (compiler.emit parent sub-chunk ast)
538 (compiler.emit parent "end" ast)))
539
540 (tset SPECIALS "while" while*)
541
542 (doc-special
543 "while" ["condition" "..."]
544 "The classic while loop. Evaluates body until a condition is non-truthy.")
545
546 (fn for* [ast scope parent]
547 (let [ranges (compiler.assert (utils.table? (. ast 2))
548 "expected binding table" ast)
549 binding-sym (table.remove (. ast 2) 1)
550 sub-scope (compiler.make-scope scope)
551 range-args []
552 chunk []]
553 (compiler.assert (utils.sym? binding-sym)
554 (: "unable to bind %s %s" :format
555 (type binding-sym) (tostring binding-sym)) (. ast 2))
556 (compiler.assert (>= (# ast) 3)
557 "expected body expression" (. ast 1))
558 (for [i 1 (math.min (# ranges) 3)]
559 (tset range-args i (tostring (. (compiler.compile1 (. ranges i) sub-scope
560 parent {:nval 1}) 1))))
561 (compiler.emit parent (: "for %s = %s do" :format
562 (compiler.declare-local binding-sym [] sub-scope ast)
563 (table.concat range-args ", ")) ast)
564 (compile-do ast sub-scope chunk 3)
565 (compiler.emit parent chunk ast)
566 (compiler.emit parent "end" ast)))
567 (tset SPECIALS "for" for*)
568
569 (doc-special
570 "for" ["[index start stop step?]" "..."]
571 "Numeric loop construct.
572 Evaluates body once for each value between start and stop (inclusive).")
573
574 (fn native-method-call [ast _scope _parent target args]
575 "Prefer native Lua method calls when method name is a valid Lua identifier."
576 (let [[_ _ method-string] ast
577 call-string (if (or (= target.type :literal) (= target.type :expression))
578 "(%s):%s(%s)"
579 "%s:%s(%s)")]
580 (utils.expr (string.format call-string (tostring target) method-string
581 (table.concat args ", ")) "statement")))
582
583 (fn nonnative-method-call [ast scope parent target args]
584 "When we don't have to protect against double-evaluation, it's not so bad."
585 (let [method-string (tostring (. (compiler.compile1 (. ast 3) scope parent
586 {:nval 1}) 1))
587 args [(tostring target) (unpack args)]]
588 (utils.expr (string.format "%s[%s](%s)" (tostring target) method-string
589 (table.concat args ", ")) :statement)))
590
591 (fn double-eval-protected-method-call [ast scope parent target args]
592 "When double-evaluation is a concern, we have to wrap an IIFE."
593 (let [method-string (tostring (. (compiler.compile1 (. ast 3) scope parent
594 {:nval 1}) 1))
595 call "(function(tgt, m, ...) return tgt[m](tgt, ...) end)(%s, %s)"]
596 (table.insert args 1 method-string)
597 (utils.expr (string.format call (tostring target) (table.concat args ", "))
598 "statement")))
599
600 (fn method-call [ast scope parent]
601 (compiler.assert (< 2 (# ast)) "expected at least 2 arguments" ast)
602 (let [[target] (compiler.compile1 (. ast 2) scope parent {:nval 1})
603 args []]
604 (for [i 4 (# ast)]
605 (let [subexprs (compiler.compile1 (. ast i) scope parent
606 {:nval (if (not= i (# ast)) 1)})]
607 (utils.map subexprs tostring args)))
608 (if (and (= (type (. ast 3)) :string) (utils.valid-lua-identifier? (. ast 3)))
609 (native-method-call ast scope parent target args)
610 (= target.type :sym)
611 (nonnative-method-call ast scope parent target args)
612 ;; When the target is an expression, we can't use the naive
613 ;; nonnative-method-call approach, because it will cause the target
614 ;; to be evaluated twice. This is fine if it's a symbol but if it's
615 ;; the result of a function call, that function could have side-effects.
616 ;; See test-short-circuit in test/misc.fnl for an example of the problem.
617 (double-eval-protected-method-call ast scope parent target args))))
618
619 (tset SPECIALS ":" method-call)
620
621 (doc-special
622 ":" ["tbl" "method-name" "..."]
623 "Call the named method on tbl with the provided args.
624 Method name doesn't have to be known at compile-time; if it is, use
625 (tbl:method-name ...) instead.")
626
627 (fn SPECIALS.comment [ast _ parent]
628 (let [els []]
629 (for [i 2 (# ast)]
630 (table.insert els (pick-values 1 (: (tostring (. ast i)) :gsub "\n" " "))))
631 (compiler.emit parent (.. "-- " (table.concat els " ")) ast)))
632
633 (doc-special "comment" ["..."] "Comment which will be emitted in Lua output.")
634
635 (fn hashfn-max-used [f-scope i max]
636 (let [max (if (. f-scope.symmeta (.. "$" i) :used) i max)]
637 (if (< i 9)
638 (hashfn-max-used f-scope (+ i 1) max)
639 max)))
640
641 (fn SPECIALS.hashfn [ast scope parent]
642 (compiler.assert (= (# ast) 2) "expected one argument" ast)
643 (let [f-scope (doto (compiler.make-scope scope)
644 (tset :vararg false)
645 (tset :hashfn true))
646 f-chunk []
647 name (compiler.gensym scope)
648 symbol (utils.sym name)
649 args []]
650 (compiler.declare-local symbol [] scope ast)
651 (for [i 1 9]
652 (tset args i (compiler.declare-local (utils.sym (.. "$" i))
653 [] f-scope ast)))
654 ;; recursively walk the AST, transforming $... into ...
655 (fn walker [idx node parent-node]
656 (if (and (utils.sym? node) (= (utils.deref node) "$..."))
657 (do
658 (tset parent-node idx (utils.varg))
659 (set f-scope.vararg true))
660 (or (utils.list? node) (utils.table? node))))
661 (utils.walk-tree (. ast 2) walker)
662 ;; compile body
663 (compiler.compile1 (. ast 2) f-scope f-chunk {:tail true})
664 (let [max-used (hashfn-max-used f-scope 1 0)]
665 (when f-scope.vararg
666 (compiler.assert (= max-used 0)
667 "$ and $... in hashfn are mutually exclusive" ast))
668 (let [arg-str (if f-scope.vararg
669 (utils.deref (utils.varg))
670 (table.concat args ", " 1 max-used))]
671 (compiler.emit parent (string.format "local function %s(%s)"
672 name arg-str) ast)
673 (compiler.emit parent f-chunk ast)
674 (compiler.emit parent "end" ast)
675 (utils.expr name "sym")))))
676
677 (doc-special "hashfn" ["..."]
678 "Function literal shorthand; args are either $... OR $1, $2, etc.")
679
680 (fn define-arithmetic-special [name zero-arity unary-prefix lua-name]
681 (let [padded-op (.. " " (or lua-name name) " ")]
682 (tset SPECIALS name
683 (fn [ast scope parent]
684 (local len (# ast))
685 (if (= len 1)
686 (do
687 (compiler.assert (not= zero-arity nil)
688 "Expected more than 0 arguments" ast)
689 (utils.expr zero-arity "literal"))
690 (let [operands []]
691 (for [i 2 len]
692 (let [subexprs (compiler.compile1 (. ast i) scope parent
693 {:nval (if (= i 1) 1)})]
694 (utils.map subexprs tostring operands)))
695 (if (= (# operands) 1)
696 (if unary-prefix
697 (.. "(" unary-prefix padded-op (. operands 1) ")")
698 (. operands 1))
699 (.. "(" (table.concat operands padded-op) ")")))))))
700 (doc-special name ["a" "b" "..."]
701 "Arithmetic operator; works the same as Lua but accepts more arguments."))
702
703 (define-arithmetic-special "+" "0")
704 (define-arithmetic-special ".." "''")
705 (define-arithmetic-special "^")
706 (define-arithmetic-special "-" nil "")
707 (define-arithmetic-special "*" "1")
708 (define-arithmetic-special "%")
709 (define-arithmetic-special "/" nil "1")
710 (define-arithmetic-special "//" nil "1")
711 (define-arithmetic-special "lshift" nil "1" "<<")
712 (define-arithmetic-special "rshift" nil "1" ">>")
713 (define-arithmetic-special "band" "0" "0" "&")
714 (define-arithmetic-special "bor" "0" "0" "|")
715 (define-arithmetic-special "bxor" "0" "0" "~")
716
717 (doc-special "lshift" ["x" "n"]
718 "Bitwise logical left shift of x by n bits; only works in Lua 5.3+.")
719 (doc-special "rshift" ["x" "n"]
720 "Bitwise logical right shift of x by n bits; only works in Lua 5.3+.")
721 (doc-special "band" ["x1" "x2"]
722 "Bitwise AND of arguments; only works in Lua 5.3+.")
723 (doc-special "bor" ["x1" "x2"]
724 "Bitwise OR of arguments; only works in Lua 5.3+.")
725 (doc-special "bxor" ["x1" "x2"]
726 "Bitwise XOR of arguments; only works in Lua 5.3+.")
727
728 (define-arithmetic-special "or" "false")
729 (define-arithmetic-special "and" "true")
730
731 (doc-special "and" ["a" "b" "..."]
732 "Boolean operator; works the same as Lua but accepts more arguments.")
733 (doc-special "or" ["a" "b" "..."]
734 "Boolean operator; works the same as Lua but accepts more arguments.")
735 (doc-special ".." ["a" "b" "..."]
736 "String concatenation operator; works the same as Lua but accepts more arguments.")
737
738 (fn native-comparator [op [_ lhs-ast rhs-ast] scope parent]
739 "Naively compile a binary comparison to Lua."
740 (let [[lhs] (compiler.compile1 lhs-ast scope parent {:nval 1})
741 [rhs] (compiler.compile1 rhs-ast scope parent {:nval 1})]
742 (string.format "(%s %s %s)" (tostring lhs) op (tostring rhs))))
743
744 (fn double-eval-protected-comparator [op chain-op ast scope parent]
745 "Compile a multi-arity comparison to a binary Lua comparison."
746 (let [arglist [] comparisons [] vals []
747 chain (string.format " %s " (or chain-op "and"))]
748 (for [i 2 (# ast)]
749 (table.insert arglist (tostring (compiler.gensym scope)))
750 (table.insert vals (tostring (. (compiler.compile1 (. ast i) scope parent
751 {:nval 1}) 1))))
752 (for [i 1 (- (# arglist) 1)]
753 (table.insert comparisons (string.format "(%s %s %s)"
754 (. arglist i) op
755 (. arglist (+ i 1)))))
756 ;; The function call here introduces some overhead, but it is the only way
757 ;; to compile this safely while preventing both double-evaluation of
758 ;; side-effecting values and early evaluation of values which should never
759 ;; happen in the case of a short-circuited call. See test-short-circuit in
760 ;; test/misc.fnl for an example of the problem.
761 (string.format "(function(%s) return %s end)(%s)"
762 (table.concat arglist ",")
763 (table.concat comparisons chain)
764 (table.concat vals ","))))
765
766 (fn define-comparator-special [name lua-op chain-op]
767 (let [op (or lua-op name)]
768 (fn opfn [ast scope parent]
769 (compiler.assert (< 2 (# ast)) "expected at least two arguments" ast)
770 (if (= 3 (# ast))
771 (native-comparator op ast scope parent)
772 (double-eval-protected-comparator op chain-op ast scope parent)))
773 (tset SPECIALS name opfn))
774 (doc-special name ["a" "b" "..."]
775 "Comparison operator; works the same as Lua but accepts more arguments."))
776
777 (define-comparator-special ">")
778 (define-comparator-special "<")
779 (define-comparator-special ">=")
780 (define-comparator-special "<=")
781 (define-comparator-special "=" "==")
782 (define-comparator-special "not=" "~=" "or")
783 (tset SPECIALS "~=" (. SPECIALS "not=")) ; backwards-compatible alias
784
785 (fn define-unary-special [op realop]
786 (fn opfn [ast scope parent]
787 (compiler.assert (= (# ast) 2) "expected one argument" ast)
788 (let [tail (compiler.compile1 (. ast 2) scope parent {:nval 1})]
789 (.. (or realop op) (tostring (. tail 1)))))
790 (tset SPECIALS op opfn))
791
792 (define-unary-special "not" "not ")
793 (doc-special "not" ["x"] "Logical operator; works the same as Lua.")
794 (define-unary-special "bnot" "~")
795 (doc-special "bnot" ["x"] "Bitwise negation; only works in Lua 5.3+.")
796 (define-unary-special "length" "#")
797 (doc-special "length" ["x"] "Returns the length of a table or string.")
798
799 (tset SPECIALS "#" (. SPECIALS "length")) ; backwards-compatible alias
800
801 (fn SPECIALS.quote [ast scope parent]
802 (compiler.assert (= (# ast) 2) "expected one argument")
803 (var (runtime this-scope) (values true scope))
804 (while this-scope
805 (set this-scope this-scope.parent)
806 (when (= this-scope compiler.scopes.compiler)
807 (set runtime false)))
808 (compiler.do-quote (. ast 2) scope parent runtime))
809
810 (doc-special "quote" ["x"]
811 "Quasiquote the following form. Only works in macro/compiler scope.")
812
813 (local already-warned? {})
814
815 (local compile-env-warning
816 (.. "WARNING: Attempting to %s %s in compile"
817 " scope.\nIn future versions of Fennel this will not"
818 " be allowed without the\n--no-compiler-sandbox flag"
819 " or passing :compiler-env _G in options.\n"))
820
821 (fn compiler-env-warn [_ key]
822 "Warn once when allowing a global that the sandbox would normally block."
823 (let [v (. _G key)]
824 (when (and v io io.stderr (not (. already-warned? key)))
825 (tset already-warned? key true)
826 ;; Make this an error in a future release!
827 (io.stderr:write (compile-env-warning:format "use global" key)))
828 v))
829
830 ;; Note that this is not yet the safe compiler env! Enforcing a compiler sandbox
831 ;; is a breaking change, so we need to do it in a way that warns for several
832 ;; releases before enforcing the sandbox.
833 (local safe-compiler-env
834 (setmetatable {: table : math : string : pairs : ipairs : assert : error
835 : select : tostring : tonumber : pcall : xpcall : next
836 : print : type :bit _G.bit : setmetatable : getmetatable
837 : rawget : rawset : rawequal :rawlen _G.rawlen}
838 {:__index compiler-env-warn}))
839
840 (fn make-compiler-env [ast scope parent]
841 (setmetatable {:_AST ast ; state of compiler
842 :_CHUNK parent
843 :_IS_COMPILER true
844 :_SCOPE scope
845 :_SPECIALS compiler.scopes.global.specials
846 :_VARARG (utils.varg)
847
848 :unpack unpack ; compatibilty alias
849 :assert-compile compiler.assert
850
851 ;; AST functions
852 :list utils.list
853 :list? utils.list?
854 :multi-sym? utils.multi-sym?
855 :sequence utils.sequence
856 :sequence? utils.sequence?
857 :sym utils.sym
858 :sym? utils.sym?
859 :table? utils.table?
860 :varg? utils.varg?
861
862 ;; scoping functions
863 :gensym (fn [base]
864 (utils.sym (compiler.gensym
865 (or compiler.scopes.macro scope) base)))
866 :get-scope (fn [] compiler.scopes.macro)
867 :in-scope? (fn [symbol]
868 (compiler.assert compiler.scopes.macro
869 "must call from macro" ast)
870 (. compiler.scopes.macro.manglings
871 (tostring symbol)))
872 :macroexpand
873 (fn [form]
874 (compiler.assert compiler.scopes.macro
875 "must call from macro" ast)
876 (compiler.macroexpand form compiler.scopes.macro))}
877 {:__index (match utils.root.options
878 {: compiler-env} compiler-env
879 safe-compiler-env)}))
880
881 ;; have search-module use package.config to process package.path (windows compat)
882 (local cfg (string.gmatch package.config "([^\n]+)"))
883 (local (dirsep pathsep pathmark)
884 (values (or (cfg) "/") (or (cfg) ";") (or (cfg) "?")))
885 (local pkg-config {:dirsep dirsep
886 :pathmark pathmark
887 :pathsep pathsep})
888
889 (fn escapepat [str]
890 "Escape a string for safe use in a Lua pattern."
891 (string.gsub str "[^%w]" "%%%1"))
892
893 (fn search-module [modulename pathstring]
894 (let [pathsepesc (escapepat pkg-config.pathsep)
895 pattern (: "([^%s]*)%s" :format pathsepesc pathsepesc)
896 no-dot-module (: modulename :gsub "%." pkg-config.dirsep)
897 fullpath (.. (or pathstring utils.fennel-module.path) pkg-config.pathsep)]
898 (fn try-path [path]
899 (let [filename (: path :gsub (escapepat pkg-config.pathmark) no-dot-module)
900 filename2 (: path :gsub (escapepat pkg-config.pathmark) modulename)]
901 (match (or (io.open filename) (io.open filename2))
902 file (do (file:close) filename))))
903 (fn find-in-path [start]
904 (match (fullpath:match pattern start)
905 path (or (try-path path)
906 (find-in-path (+ start (# path) 1)))))
907 (find-in-path 1)))
908
909 (fn make-searcher [options]
910 "This will allow regular `require` to work with Fennel:
911 table.insert(package.loaders, fennel.searcher)"
912 (let [opts (utils.copy utils.root.options)]
913 (each [k v (pairs (or options {}))]
914 (tset opts k v))
915 (fn [module-name]
916 (let [filename (search-module module-name)]
917 (if filename
918 (fn [mod-name]
919 (utils.fennel-module.dofile filename opts mod-name)))))))
920
921 (fn macro-globals [env globals]
922 (let [allowed (current-global-names env)]
923 (each [_ k (pairs (or globals []))]
924 (table.insert allowed k))
925 allowed))
926
927 (fn compiler-env-domodule [modname env ?ast]
928 (let [filename (compiler.assert (search-module modname)
929 (.. modname " module not found.") ?ast)
930 globals (macro-globals env (current-global-names))]
931 (utils.fennel-module.dofile filename {:allowedGlobals globals
932 :env env
933 :useMetadata utils.root.options.useMetadata
934 :scope compiler.scopes.compiler})))
935
936 ;; This is the compile-env equivalent of package.loaded. It's used by
937 ;; require-macros and import-macros, but also by require when used from within
938 ;; default compiler scope.
939 (local macro-loaded {})
940
941 (fn metadata-only-fennel [modname]
942 "Let limited Fennel module thru just for purposes of compiling docstrings."
943 (if (or (= modname "fennel.macros")
944 (and package package.loaded
945 (= :table (type (. package.loaded modname)))
946 (= (. package.loaded modname :metadata) compiler.metadata)))
947 {:metadata compiler.metadata}))
948
949 (fn safe-compiler-env.require [modname]
950 "This is a replacement for require for use in macro contexts.
951 It ensures that compile-scoped modules are loaded differently from regular
952 modules in the compiler environment."
953 (or (. macro-loaded modname)
954 (metadata-only-fennel modname)
955 (let [mod (compiler-env-domodule modname safe-compiler-env)]
956 (tset macro-loaded modname mod)
957 mod)))
958
959 (fn add-macros [macros* ast scope]
960 (compiler.assert (utils.table? macros*) "expected macros to be table" ast)
961 (each [k v (pairs macros*)]
962 (compiler.assert (= (type v) "function")
963 "expected each macro to be function" ast)
964 (tset scope.macros k v)))
965
966 (fn SPECIALS.require-macros [ast scope parent]
967 (compiler.assert (= (# ast) 2) "Expected one module name argument" ast)
968 (let [modname (. ast 2)]
969 (when (not (. macro-loaded modname))
970 (let [env (make-compiler-env ast scope parent)]
971 (tset macro-loaded modname (compiler-env-domodule modname env ast))))
972 (add-macros (. macro-loaded modname) ast scope parent)))
973
974 (doc-special
975 "require-macros" ["macro-module-name"]
976 "Load given module and use its contents as macro definitions in current scope.
977 Macro module should return a table of macro functions with string keys.
978 Consider using import-macros instead as it is more flexible.")
979
980 (fn emit-fennel [src path opts sub-chunk]
981 "Emit Fennel code in src into sub-chunk."
982 (let [subscope (compiler.make-scope utils.root.scope.parent)
983 forms []]
984 (when utils.root.options.requireAsInclude
985 (set subscope.specials.require compiler.require-include))
986 ;; parse Fennel src into table of exprs to know which expr is the tail
987 (each [_ val (parser.parser (parser.string-stream src) path)]
988 (table.insert forms val))
989 ;; Compile the forms into sub-chunk; compiler.compile1 is necessary
990 ;; for all nested includes to be emitted in the same root chunk
991 ;; in the top-level module.
992 (for [i 1 (# forms)]
993 (let [subopts (if (= i (# forms)) {:nval 1 :tail true} {:nval 0})]
994 (utils.propagate-options opts subopts)
995 (compiler.compile1 (. forms i) subscope sub-chunk subopts)))))
996
997 (fn include-path [ast opts path mod fennel?]
998 "Helper function for include once we have determined the path to use."
999 (tset utils.root.scope.includes mod :fnl/loading)
1000 (let [src (with-open [f (assert (io.open path))]
1001 (: (f:read "*all") :gsub "[\r\n]*$" ""))
1002 ;; splice in source and memoize it in compiler AND package.preload
1003 ;; so we can include it again without duplication, even in runtime
1004 ret (utils.expr (.. "require(\"" mod "\")") "statement")
1005 target (: "package.preload[%q]" :format mod)
1006 preload-str (.. target " = " target " or function(...)")
1007 (temp-chunk sub-chunk) (values [] [])]
1008 (compiler.emit temp-chunk preload-str ast)
1009 (compiler.emit temp-chunk sub-chunk)
1010 (compiler.emit temp-chunk "end" ast)
1011 ;; Splice temp-chunk to begining of root chunk
1012 (each [i v (ipairs temp-chunk)]
1013 (table.insert utils.root.chunk i v))
1014
1015 ;; For fennel source, compile sub-chunk AFTER splicing into start of
1016 ;; root chunk.
1017 (if fennel?
1018 (emit-fennel src path opts sub-chunk)
1019 ;; For Lua source, simply emit src into the loaders's body
1020 (compiler.emit sub-chunk src ast))
1021
1022 ;; Put in cache and return
1023 (tset utils.root.scope.includes mod ret)
1024 ret))
1025
1026 (fn include-circular-fallback [mod modexpr fallback ast]
1027 "If a circular include is detected, fall back to require if possible."
1028 (when (= (. utils.root.scope.includes mod) :fnl/loading) ; circular include
1029 (compiler.assert fallback "circular include detected" ast)
1030 (fallback modexpr)))
1031
1032 (fn SPECIALS.include [ast scope parent opts]
1033 (compiler.assert (= (# ast) 2) "expected one argument" ast)
1034 (let [modexpr (. (compiler.compile1 (. ast 2) scope parent {:nval 1}) 1)]
1035 (if (or (not= modexpr.type "literal") (not= (: (. modexpr 1) :byte) 34))
1036 (if opts.fallback
1037 (opts.fallback modexpr)
1038 (compiler.assert false "module name must be string literal" ast))
1039 (let [mod ((load-code (.. "return " (. modexpr 1))))]
1040 (or (include-circular-fallback mod modexpr opts.fallback ast)
1041 (. utils.root.scope.includes mod) ; check cache
1042 ;; Find path to Fennel or Lua source
1043 (match (search-module mod) ; try fennel path first
1044 fennel-path (include-path ast opts fennel-path mod true)
1045 ;; then search for a lua module
1046 _ (let [lua-path (search-module mod package.path)]
1047 (if lua-path (include-path ast opts lua-path mod false)
1048 opts.fallback (opts.fallback modexpr)
1049 (compiler.assert false (.. "module not found " mod)
1050 ast)))))))))
1051
1052 (doc-special
1053 "include" ["module-name-literal"]
1054 "Like require but load the target module during compilation and embed it in the
1055 Lua output. The module must be a string literal and resolvable at compile time.")
1056
1057 (fn eval-compiler* [ast scope parent]
1058 (let [env (make-compiler-env ast scope parent)
1059 opts (utils.copy utils.root.options)]
1060 (set opts.scope (compiler.make-scope compiler.scopes.compiler))
1061 (set opts.allowedGlobals (macro-globals env (current-global-names)))
1062 ((load-code (compiler.compile ast opts) (wrap-env env)))))
1063
1064 (fn SPECIALS.macros [ast scope parent]
1065 (compiler.assert (= (# ast) 2) "Expected one table argument" ast)
1066 (add-macros (eval-compiler* (. ast 2) scope parent) ast scope parent))
1067
1068 (doc-special
1069 "macros" ["{:macro-name-1 (fn [...] ...) ... :macro-name-N macro-body-N}"]
1070 "Define all functions in the given table as macros local to the current scope.")
1071
1072 (fn SPECIALS.eval-compiler [ast scope parent]
1073 (let [old-first (. ast 1)]
1074 (tset ast 1 (utils.sym "do"))
1075 (let [val (eval-compiler* ast scope parent)]
1076 (tset ast 1 old-first)
1077 val)))
1078
1079 (doc-special
1080 "eval-compiler" ["..."]
1081 "Evaluate the body at compile-time. Use the macro system instead if possible.")
1082
1083 {:doc doc*
1084 : current-global-names
1085 : load-code
1086 : macro-loaded
1087 : make-compiler-env
1088 : search-module
1089 : make-searcher
1090 : wrap-env}
1091