1;; This module contains all the built-in Fennel macros. Unlike all the other
2;; modules that are loaded by the old bootstrap compiler, this runs in the
3;; compiler scope of the version of the compiler being defined.
4
5;; The code for these macros is somewhat idiosyncratic because it cannot use any
6;; macros which have not yet been defined.
7
8;; TODO: some of these macros modify their arguments; we should stop doing that,
9;; but in a way that preserves file/line metadata.
10
11(fn ->* [val ...]
12  "Thread-first macro.
13Take the first value and splice it into the second form as its first argument.
14The value of the second form is spliced into the first arg of the third, etc."
15  (var x val)
16  (each [_ e (ipairs [...])]
17    (let [elt (if (list? e) e (list e))]
18      (table.insert elt 2 x)
19      (set x elt)))
20  x)
21
22(fn ->>* [val ...]
23  "Thread-last macro.
24Same as ->, except splices the value into the last position of each form
25rather than the first."
26  (var x val)
27  (each [_ e (ipairs [...])]
28    (let [elt (if (list? e) e (list e))]
29      (table.insert elt x)
30      (set x elt)))
31  x)
32
33(fn -?>* [val ...]
34  "Nil-safe thread-first macro.
35Same as -> except will short-circuit with nil when it encounters a nil value."
36  (if (= 0 (select "#" ...))
37      val
38      (let [els [...]
39            e (table.remove els 1)
40            el (if (list? e) e (list e))
41            tmp (gensym)]
42        (table.insert el 2 tmp)
43        `(let [,tmp ,val]
44           (if (not= nil ,tmp)
45               (-?> ,el ,(unpack els))
46               ,tmp)))))
47
48(fn -?>>* [val ...]
49  "Nil-safe thread-last macro.
50Same as ->> except will short-circuit with nil when it encounters a nil value."
51  (if (= 0 (select "#" ...))
52      val
53      (let [els [...]
54            e (table.remove els 1)
55            el (if (list? e) e (list e))
56            tmp (gensym)]
57        (table.insert el tmp)
58        `(let [,tmp ,val]
59           (if (not= ,tmp nil)
60               (-?>> ,el ,(unpack els))
61               ,tmp)))))
62
63(fn ?dot [tbl ...]
64  "Nil-safe table look up.
65Same as . (dot), except will short-circuit with nil when it encounters
66a nil value in any of subsequent keys."
67  (let [head (gensym :t)
68        lookups `(do (var ,head ,tbl) ,head)]
69    (each [_ k (ipairs [...])]
70      ;; Kinda gnarly to reassign in place like this, but it emits the best lua.
71      ;; With this impl, it emits a flat, concise, and readable set of if blocks.
72      (table.insert lookups (# lookups) `(if (not= nil ,head)
73                                           (set ,head (. ,head ,k)))))
74    lookups))
75
76(fn doto* [val ...]
77  "Evaluates val and splices it into the first argument of subsequent forms."
78  (let [name (gensym)
79        form `(let [,name ,val])]
80    (each [_ elt (ipairs [...])]
81      (let [elt (if (list? elt) elt (list elt))]
82        (table.insert elt 2 name)
83        (table.insert form elt)))
84    (table.insert form name)
85    form))
86
87(fn when* [condition body1 ...]
88  "Evaluate body for side-effects only when condition is truthy."
89  (assert body1 "expected body")
90  `(if ,condition
91       (do
92         ,body1
93         ,...)))
94
95(fn with-open* [closable-bindings ...]
96  "Like `let`, but invokes (v:close) on each binding after evaluating the body.
97The body is evaluated inside `xpcall` so that bound values will be closed upon
98encountering an error before propagating it."
99  (let [bodyfn `(fn []
100                  ,...)
101        closer `(fn close-handlers# [ok# ...]
102                  (if ok# ... (error ... 0)))
103        traceback `(. (or package.loaded.fennel debug) :traceback)]
104    (for [i 1 (length closable-bindings) 2]
105      (assert (sym? (. closable-bindings i))
106              "with-open only allows symbols in bindings")
107      (table.insert closer 4 `(: ,(. closable-bindings i) :close)))
108    `(let ,closable-bindings
109       ,closer
110       (close-handlers# (_G.xpcall ,bodyfn ,traceback)))))
111
112(fn into-val [iter-tbl]
113  (var into nil)
114  (for [i (length iter-tbl) 2 -1]
115    (if (= :into (. iter-tbl i))
116        (do (assert (not into) "expected only one :into clause")
117            (set into (table.remove iter-tbl (+ i 1)))
118            (table.remove iter-tbl i))))
119  (assert (or (not into)
120              (sym? into)
121              (table? into)
122              (list? into))
123          "expected table, function call, or symbol in :into clause")
124  (or into []))
125
126(fn collect* [iter-tbl key-expr value-expr ...]
127  "Returns a table made by running an iterator and evaluating an expression that
128returns key-value pairs to be inserted sequentially into the table.  This can
129be thought of as a table comprehension. The body should provide two
130expressions (used as key and value) or nil, which causes it to be omitted from
131the resulting table.
132
133For example,
134  (collect [k v (pairs {:apple \"red\" :orange \"orange\"})]
135    v k)
136returns
137  {:red \"apple\" :orange \"orange\"}
138
139Supports an :into clause after the iterator to put results in an existing table.
140Supports early termination with an :until clause."
141  (assert (and (sequence? iter-tbl) (>= (length iter-tbl) 2))
142          "expected iterator binding table")
143  (assert (not= nil key-expr) "expected key and value expression")
144  (assert (= nil ...)
145          "expected 1 or 2 body expressions; wrap multiple expressions with do")
146  (let [kv-expr (if (= nil value-expr) key-expr `(values ,key-expr ,value-expr))]
147    `(let [tbl# ,(into-val iter-tbl)]
148       (each ,iter-tbl
149         (match ,kv-expr
150           (k# v#) (tset tbl# k# v#)))
151       tbl#)))
152
153(fn icollect* [iter-tbl value-expr ...]
154  "Returns a sequential table made by running an iterator and evaluating an
155expression that returns values to be inserted sequentially into the table.
156This can be thought of as a \"list comprehension\". If the body returns nil
157that element is omitted from the resulting table.
158
159For example,
160  (icollect [_ v (ipairs [1 2 3 4 5])] (when (not= v 3) (* v v)))
161returns
162  [1 4 16 25]
163
164Supports an :into clause after the iterator to put results in an existing table.
165Supports early termination with an :until clause."
166  (assert (and (sequence? iter-tbl) (>= (length iter-tbl) 2))
167          "expected iterator binding table")
168  (assert (not= nil value-expr) "expected table value expression")
169  (assert (= nil ...)
170          "expected exactly one body expression. Wrap multiple expressions with do")
171  `(let [tbl# ,(into-val iter-tbl)]
172     ;; believe it or not, using a var here has a pretty good performance boost:
173     ;; https://p.hagelb.org/icollect-performance.html
174     (var i# (length tbl#))
175     (each ,iter-tbl
176       (let [val# ,value-expr]
177         (when (not= nil val#)
178           (set i# (+ i# 1))
179           (tset tbl# i# val#))))
180     tbl#))
181
182(fn accumulate* [iter-tbl accum-expr ...]
183  "Accumulation macro.
184It takes a binding table and an expression as its arguments.
185In the binding table, the first symbol is bound to the second value, being an
186initial accumulator variable. The rest are an iterator binding table in the
187format `each` takes.
188It runs through the iterator in each step of which the given expression is
189evaluated, and its returned value updates the accumulator variable.
190It eventually returns the final value of the accumulator variable.
191
192For example,
193  (accumulate [total 0
194               _ n (pairs {:apple 2 :orange 3})]
195    (+ total n))
196returns
197  5"
198  (assert (and (sequence? iter-tbl) (>= (length iter-tbl) 4))
199          "expected initial value and iterator binding table")
200  (assert (not= nil accum-expr) "expected accumulating expression")
201  (assert (= nil ...)
202          "expected exactly one body expression. Wrap multiple expressions with do")
203  (let [accum-var (table.remove iter-tbl 1)
204        accum-init (table.remove iter-tbl 1)]
205    `(do (var ,accum-var ,accum-init)
206         (each ,iter-tbl
207           (set ,accum-var ,accum-expr))
208         ,accum-var)))
209
210(fn partial* [f ...]
211  "Returns a function with all arguments partially applied to f."
212  (assert f "expected a function to partially apply")
213  (let [bindings []
214        args []]
215    (each [_ arg (ipairs [...])]
216      (if (or (= :number (type arg))
217              (= :string (type arg))
218              (= :boolean (type arg))
219              (= `nil arg))
220        (table.insert args arg)
221        (let [name (gensym)]
222          (table.insert bindings name)
223          (table.insert bindings arg)
224          (table.insert args name))))
225    (let [body (list f (unpack args))]
226      (table.insert body _VARARG)
227      `(let ,bindings
228         (fn [,_VARARG]
229           ,body)))))
230
231(fn pick-args* [n f]
232  "Creates a function of arity n that applies its arguments to f.
233
234For example,
235  (pick-args 2 func)
236expands to
237  (fn [_0_ _1_] (func _0_ _1_))"
238  (if (and _G.io _G.io.stderr)
239      (_G.io.stderr:write
240       "-- WARNING: pick-args is deprecated and will be removed in the future.\n"))
241  (assert (and (= (type n) :number) (= n (math.floor n)) (>= n 0))
242          (.. "Expected n to be an integer literal >= 0, got " (tostring n)))
243  (let [bindings []]
244    (for [i 1 n]
245      (tset bindings i (gensym)))
246    `(fn ,bindings
247       (,f ,(unpack bindings)))))
248
249(fn pick-values* [n ...]
250  "Like the `values` special, but emits exactly n values.
251
252For example,
253  (pick-values 2 ...)
254expands to
255  (let [(_0_ _1_) ...]
256    (values _0_ _1_))"
257  (assert (and (= :number (type n)) (>= n 0) (= n (math.floor n)))
258          (.. "Expected n to be an integer >= 0, got " (tostring n)))
259  (let [let-syms (list)
260        let-values (if (= 1 (select "#" ...)) ... `(values ,...))]
261    (for [i 1 n]
262      (table.insert let-syms (gensym)))
263    (if (= n 0) `(values)
264        `(let [,let-syms ,let-values]
265           (values ,(unpack let-syms))))))
266
267(fn lambda* [...]
268  "Function literal with nil-checked arguments.
269Like `fn`, but will throw an exception if a declared argument is passed in as
270nil, unless that argument's name begins with a question mark."
271  (let [args [...]
272        has-internal-name? (sym? (. args 1))
273        arglist (if has-internal-name? (. args 2) (. args 1))
274        docstring-position (if has-internal-name? 3 2)
275        has-docstring? (and (> (length args) docstring-position)
276                            (= :string (type (. args docstring-position))))
277        arity-check-position (- 4 (if has-internal-name? 0 1)
278                                (if has-docstring? 0 1))
279        empty-body? (< (length args) arity-check-position)]
280    (fn check! [a]
281      (if (table? a)
282          (each [_ a (pairs a)]
283            (check! a))
284          (let [as (tostring a)]
285            (and (not (as:match "^?")) (not= as "&") (not= as "_")
286                 (not= as "...") (not= as "&as")))
287          (table.insert args arity-check-position
288                        `(_G.assert (not= nil ,a)
289                                    ,(: "Missing argument %s on %s:%s" :format
290                                        (tostring a)
291                                        (or a.filename :unknown)
292                                        (or a.line "?"))))))
293
294    (assert (= :table (type arglist)) "expected arg list")
295    (each [_ a (ipairs arglist)]
296      (check! a))
297    (if empty-body?
298        (table.insert args (sym :nil)))
299    `(fn ,(unpack args))))
300
301(fn macro* [name ...]
302  "Define a single macro."
303  (assert (sym? name) "expected symbol for macro name")
304  (local args [...])
305  `(macros {,(tostring name) (fn ,(unpack args))}))
306
307(fn macrodebug* [form return?]
308  "Print the resulting form after performing macroexpansion.
309With a second argument, returns expanded form as a string instead of printing."
310  (let [handle (if return? `do `print)]
311    `(,handle ,(view (macroexpand form _SCOPE)))))
312
313(fn import-macros* [binding1 module-name1 ...]
314  "Binds a table of macros from each macro module according to a binding form.
315Each binding form can be either a symbol or a k/v destructuring table.
316Example:
317  (import-macros mymacros                 :my-macros    ; bind to symbol
318                 {:macro1 alias : macro2} :proj.macros) ; import by name"
319  (assert (and binding1 module-name1 (= 0 (% (select "#" ...) 2)))
320          "expected even number of binding/modulename pairs")
321  (for [i 1 (select "#" binding1 module-name1 ...) 2]
322    ;; delegate the actual loading of the macros to the require-macros
323    ;; special which already knows how to set up the compiler env and stuff.
324    ;; this is weird because require-macros is deprecated but it works.
325    (let [(binding modname) (select i binding1 module-name1 ...)
326          scope (get-scope)
327          macros* (_SPECIALS.require-macros `(import-macros ,modname)
328                                            scope {} binding1)]
329      (if (sym? binding)
330          ;; bind whole table of macros to table bound to symbol
331          (tset scope.macros (. binding 1) macros*)
332          ;; 1-level table destructuring for importing individual macros
333          (table? binding)
334          (each [macro-name [import-key] (pairs binding)]
335            (assert (= :function (type (. macros* macro-name)))
336                    (.. "macro " macro-name " not found in module "
337                        (tostring modname)))
338            (tset scope.macros import-key (. macros* macro-name))))))
339  nil)
340
341;;; Pattern matching
342
343(fn match-values [vals pattern unifications match-pattern]
344  (let [condition `(and)
345        bindings []]
346    (each [i pat (ipairs pattern)]
347      (let [(subcondition subbindings) (match-pattern [(. vals i)] pat
348                                                      unifications)]
349        (table.insert condition subcondition)
350        (each [_ b (ipairs subbindings)]
351          (table.insert bindings b))))
352    (values condition bindings)))
353
354(fn match-table [val pattern unifications match-pattern]
355  (let [condition `(and (= (_G.type ,val) :table))
356        bindings []]
357    (each [k pat (pairs pattern)]
358      (if (= pat `&)
359          (let [rest-pat (. pattern (+ k 1))
360                rest-val `(select ,k ((or table.unpack _G.unpack) ,val))
361                subcondition (match-table `(pick-values 1 ,rest-val)
362                                          rest-pat unifications match-pattern)]
363            (if (not (sym? rest-pat))
364                (table.insert condition subcondition))
365            (assert (= nil (. pattern (+ k 2)))
366                    "expected & rest argument before last parameter")
367            (table.insert bindings rest-pat)
368            (table.insert bindings [rest-val]))
369          (= k `&as)
370          (do
371            (table.insert bindings pat)
372            (table.insert bindings val))
373          (and (= :number (type k)) (= `&as pat))
374          (do
375            (assert (= nil (. pattern (+ k 2)))
376                    "expected &as argument before last parameter")
377            (table.insert bindings (. pattern (+ k 1)))
378            (table.insert bindings val))
379          ;; don't process the pattern right after &/&as; already got it
380          (or (not= :number (type k)) (and (not= `&as (. pattern (- k 1)))
381                                           (not= `& (. pattern (- k 1)))))
382          (let [subval `(. ,val ,k)
383                (subcondition subbindings) (match-pattern [subval] pat
384                                                          unifications)]
385            (table.insert condition subcondition)
386            (each [_ b (ipairs subbindings)]
387              (table.insert bindings b)))))
388    (values condition bindings)))
389
390(fn match-pattern [vals pattern unifications]
391  "Takes the AST of values and a single pattern and returns a condition
392to determine if it matches as well as a list of bindings to
393introduce for the duration of the body if it does match."
394  ;; we have to assume we're matching against multiple values here until we
395  ;; know we're either in a multi-valued clause (in which case we know the #
396  ;; of vals) or we're not, in which case we only care about the first one.
397  (let [[val] vals]
398    (if (or (and (sym? pattern) ; unification with outer locals (or nil)
399                 (not= "_" (tostring pattern)) ; never unify _
400                 (or (in-scope? pattern) (= :nil (tostring pattern))))
401            (and (multi-sym? pattern) (in-scope? (. (multi-sym? pattern) 1))))
402        (values `(= ,val ,pattern) [])
403        ;; unify a local we've seen already
404        (and (sym? pattern) (. unifications (tostring pattern)))
405        (values `(= ,(. unifications (tostring pattern)) ,val) [])
406        ;; bind a fresh local
407        (sym? pattern)
408        (let [wildcard? (: (tostring pattern) :find "^_")]
409          (if (not wildcard?) (tset unifications (tostring pattern) val))
410          (values (if (or wildcard? (string.find (tostring pattern) "^?")) true
411                      `(not= ,(sym :nil) ,val)) [pattern val]))
412        ;; guard clause
413        (and (list? pattern) (= (. pattern 2) `?))
414        (let [(pcondition bindings) (match-pattern vals (. pattern 1)
415                                                   unifications)
416              condition `(and ,(unpack pattern 3))]
417          (values `(and ,pcondition
418                        (let ,bindings
419                          ,condition)) bindings))
420        ;; multi-valued patterns (represented as lists)
421        (list? pattern)
422        (match-values vals pattern unifications match-pattern)
423        ;; table patterns
424        (= (type pattern) :table)
425        (match-table val pattern unifications match-pattern)
426        ;; literal value
427        (values `(= ,val ,pattern) []))))
428
429(fn match-condition [vals clauses]
430  "Construct the actual `if` AST for the given match values and clauses."
431  (if (not= 0 (% (length clauses) 2)) ; treat odd final clause as default
432      (table.insert clauses (length clauses) (sym "_")))
433  (let [out `(if)]
434    (for [i 1 (length clauses) 2]
435      (let [pattern (. clauses i)
436            body (. clauses (+ i 1))
437            (condition bindings) (match-pattern vals pattern {})]
438        (table.insert out condition)
439        (table.insert out `(let ,bindings
440                             ,body))))
441    out))
442
443(fn match-val-syms [clauses]
444  "How many multi-valued clauses are there? return a list of that many gensyms."
445  (let [syms (list (gensym))]
446    (for [i 1 (length clauses) 2]
447      (let [clause (if (and (list? (. clauses i)) (= `? (. clauses i 2)))
448                       (. clauses i 1)
449                       (. clauses i))]
450        (if (list? clause)
451            (each [valnum (ipairs clause)]
452              (if (not (. syms valnum))
453                  (tset syms valnum (gensym)))))))
454    syms))
455
456(fn match* [val ...]
457  ;; Old implementation of match macro, which doesn't directly support
458  ;; `where' and `or'. New syntax is implemented in `match-where',
459  ;; which simply generates old syntax and feeds it to `match*'.
460  (let [clauses [...]
461        vals (match-val-syms clauses)]
462    (assert (= 0 (math.fmod (length clauses) 2))
463            "expected even number of pattern/body pairs")
464    ;; protect against multiple evaluation of the value, bind against as
465    ;; many values as we ever match against in the clauses.
466    (list `let [vals val] (match-condition vals clauses))))
467
468;; Construction of old match syntax from new syntax
469
470(fn partition-2 [seq]
471  ;; Partition `seq` by 2.
472  ;; If `seq` has odd amount of elements, the last one is dropped.
473  ;;
474  ;; Input: [1 2 3 4 5]
475  ;; Output: [[1 2] [3 4]]
476  (let [firsts []
477        seconds []
478        res []]
479    (for [i 1 (length seq) 2]
480      (let [first (. seq i)
481            second (. seq (+ i 1))]
482        (table.insert firsts (if (not= nil first) first `nil))
483        (table.insert seconds (if (not= nil second) second `nil))))
484    (each [i v1 (ipairs firsts)]
485      (let [v2 (. seconds i)]
486        (if (not= nil v2)
487            (table.insert res [v1 v2]))))
488    res))
489
490(fn transform-or [[_ & pats] guards]
491  ;; Transforms `(or pat pats*)` lists into match `guard` patterns.
492  ;;
493  ;; (or pat1 pat2), guard => [(pat1 ? guard) (pat2 ? guard)]
494  (let [res []]
495    (each [_ pat (ipairs pats)]
496      (table.insert res (list pat `? (unpack guards))))
497    res))
498
499(fn transform-cond [cond]
500  ;; Transforms `where` cond into sequence of `match` guards.
501  ;;
502  ;; pat => [pat]
503  ;; (where pat guard) => [(pat ? guard)]
504  ;; (where (or pat1 pat2) guard) => [(pat1 ? guard) (pat2 ? guard)]
505  (if (and (list? cond) (= (. cond 1) `where))
506      (let [second (. cond 2)]
507        (if (and (list? second) (= (. second 1) `or))
508            (transform-or second [(unpack cond 3)])
509            :else
510            [(list second `? (unpack cond 3))]))
511      :else
512      [cond]))
513
514(fn match-where [val ...]
515  "Perform pattern matching on val. See reference for details.
516
517Syntax:
518
519(match data-expression
520  pattern body
521  (where pattern guard guards*) body
522  (where (or pattern patterns*) guard guards*) body)"
523  (let [conds-bodies (partition-2 [...])
524        else-branch (if (not= 0 (% (select "#" ...) 2))
525                        (select (select "#" ...) ...))
526        match-body []]
527    (each [_ [cond body] (ipairs conds-bodies)]
528      (each [_ cond (ipairs (transform-cond cond))]
529        (table.insert match-body cond)
530        (table.insert match-body body)))
531    (if else-branch
532        (table.insert match-body else-branch))
533    (match* val (unpack match-body))))
534
535{:-> ->*
536 :->> ->>*
537 :-?> -?>*
538 :-?>> -?>>*
539 :?. ?dot
540 :doto doto*
541 :when when*
542 :with-open with-open*
543 :collect collect*
544 :icollect icollect*
545 :accumulate accumulate*
546 :partial partial*
547 :lambda lambda*
548 :pick-args pick-args*
549 :pick-values pick-values*
550 :macro macro*
551 :macrodebug macrodebug*
552 :import-macros import-macros*
553 :match match-where}
554