Current section
Files
Jump to
Current section
Files
examples/core-macros.lfe
;; Copyright (c) 2008-2020 Robert Virding
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; File : core-macros.lfe
;; Author : Robert Virding
;; Purpose : Some of LFE core macrosas they could have been implemented in LFE.
;; This file contains some of the core LFE macros as they could have
;; been implemented in LFE. They are implemented in lfe_macro.erl
;; today as there is yet no way to compile macros, and to ensure that
;; they are always available. These are shown mostly without comments.
;;
;; For some macros we give two versions, an all-in-one version and a
;; recursive more syntax pattern based expansion. This to show
;; different styles of doing the same thing.
;; Here is some example usage:
;;
;; $ ./bin/lfe
;;
;; lfe> (include-file "examples/core-macros.lfe")
;; ()
;; lfe> (:: init get_status)
;; #(started started)
(defmacro caar (x) `(car (car ,x)))
(defmacro cadr (x) `(car (cdr ,x)))
(defmacro cdar (x) `(cdr (car ,x)))
(defmacro cddr (x) `(cdr (cdr ,x)))
(defmacro ++
;; Cases with quoted lists.
((cons (list 'quote (cons a as)) es) `(cons ',a (++ ',as . ,es)))
((cons (list 'quote ()) es) `(++ . ,es))
;; Cases with explicit cons/list/list*.
((cons (list 'list* a) es) `(++ ,a . ,es))
((cons (cons 'list* (cons a as)) es) `(cons ,a (++ (list* . ,as) . ,es)))
((cons (cons 'list (cons a as)) es) `(cons ,a (++ (list . ,as) . ,es)))
((cons (list 'list) es) `(++ . ,es))
((cons (list 'cons h t) es) `(cons ,h (++ ,t . ,es)))
((cons () es) `(++ . ,es))
;; Default cases with unquoted arg.
((list e) e)
((cons e es) `(call 'erlang '++ ,e (++ . ,es)))
(() ()))
(defmacro :
((list* m f as) `(call ',m ',f . ,as)))
(defmacro ?
((list to default)
`(receive (omega omega) (after ,to ,default)))
((list to) `(? ,to (exit 'timeout)))
(() `(receive (omega omega))))
(defmacro list*
((list e) e)
((cons e es) `(cons ,e (list* . ,es)))
(() ()))
;; (defmacro let*
;; ((cons vbs b)
;; (: lists foldr
;; (lambda (vb rest) `(let (,vb) ,rest)) `(progn . ,b) vbs)))
(defmacro let*
((cons (cons vb vbs) b) `(let (,vb) (let* ,vbs . ,b)))
((cons () b) `(progn . ,b))
((cons vb b) `(let ,vb . b))) ;Pass error to let
(defmacro flet*
((cons (cons fb fbs) b) `(flet (,fb) (flet* ,fbs . ,b)))
((cons () b) `(progn . ,b))
((cons fb b) `(flet ,fb . b))) ;Pass error to flet
(defmacro cond
((list (cons 'else b)) `(progn . ,b))
((cons (cons (list '?= p e) b) cond)
`(case ,e (,p . ,b) (_ (cond . ,cond))))
((cons (cons (list '?= p (= (cons 'when _) g) e) b) cond)
`(case ,e (,p ,g . ,b) (_ (cond . ,cond))))
((cons (cons test b) cond) `(if ,test (progn . ,b) (cond . ,cond)))
(() `'false))
(defmacro andalso
((list e) e)
((cons e es) `(if ,e (andalso . ,es) 'false))
(() `'true))
(defmacro orelse args ;Using single argument
(case args
((list e) e)
((cons e es) `(if ,e 'true (orelse . ,es)))
(() `'false)))
;; This version of backquote is almost an exact copy of a quasiquote
;; expander for Scheme by André van Tonder. It is very compact and
;; with some cons/append optimisations we have added produces quite
;; reasonable code.
(defmacro backquote (e) (bq-expand e 0))
(eval-when-compile
(defun bq-expand (exp n)
;; Note that we cannot *use* backquote or any macros using
;; backquote in here! It will cause us to loop.
(fletrec ((bq-app ;Optimise append
([(list '++ l) r] (bq-app l r)) ;Catch single comma-at
([() r] r)
([l ()] l)
([(list 'list l) (cons 'list r)] (cons 'list (cons l r)))
([(list 'list l) r] (list 'cons l r))
([l r] (list '++ l r)))
(bq-cons ;Optimise cons
([(list 'quote l) (list 'quote r)] (list 'quote (cons l r)))
([l (cons 'list r)] (cons 'list (cons l r)))
([l ()] (list 'list l))
([l r] (list 'cons l r))))
(case exp
((list 'backquote x)
;; `(list 'backquote ,(bq-expand x (+ n 1)))
(list 'list (list 'quote 'backquote) (bq-expand x (+ n 1))))
((list 'comma x) (when (> n 0))
(bq-cons (list 'quote 'comma) (bq-expand x (- n 1))))
((list 'comma x) (when (=:= n 0)) x)
((cons 'comma-at x) (when (> n 0))
(bq-cons (list 'quote 'comma-at) (bq-expand x (- n 1))))
;; The next two cases handle splicing into a list.
((cons (cons 'comma x) y) (when (=:= n 0))
(bq-app (cons 'list x) (bq-expand y 0)))
((cons (cons 'comma-at x) y) (when (=:= n 0))
(bq-app (cons '++ x) (bq-expand y 0)))
((cons x y) ;The general list case
(bq-cons (bq-expand x n) (bq-expand y n)))
(x (when (is_tuple x))
;; Tuples need some smartness for efficient code to handle
;; when no splicing so as to avoid list_to_tuple.
(case (bq-expand (tuple_to_list x) n)
((cons 'list es) (cons 'tuple es))
((= (cons 'cons _) e) (list 'list_to_tuple e))))
(x (when (is_atom x)) (list 'quote x))
(x x)) ;Self quoting
)))