hy/hy/core/bootstrap.hy

78 lines
2.9 KiB
Hy

;;; Hy bootstrap macros
;; Copyright 2020 the authors.
;; This file is part of Hy, which is free software licensed under the Expat
;; license. See the LICENSE.
;;; These macros are the essential hy macros.
;;; They are automatically required everywhere, even inside hy.core modules.
(eval-and-compile
(import hy)
((hy.macros.macro "defmacro")
(fn [&name macro-name lambda-list &rest body]
"the defmacro macro"
(if* (not (isinstance macro-name hy.models.HySymbol))
(raise
(hy.errors.HyTypeError
(% "received a `%s' instead of a symbol for macro name"
(. (type name) __name__))
None --file-- None)))
(for [kw '[&kwonly &kwargs]]
(if* (in kw lambda-list)
(raise (hy.errors.HyTypeError (% "macros cannot use %s"
kw)
macro-name --file-- None))))
;; this looks familiar...
`(eval-and-compile
(import hy)
((hy.macros.macro ~(str macro-name))
(fn ~(+ `[&name] lambda-list)
~@body))))))
(defmacro if [&rest args]
"Conditionally evaluate alternating test and then expressions."
(setv n (len args))
(if* n
(if* (= n 1)
(get args 0)
`(if* ~(get args 0)
~(get args 1)
(if ~@(cut args 2))))))
(defmacro deftag [tag-name lambda-list &rest body]
(import hy.models)
(if (and (not (isinstance tag-name hy.models.HySymbol))
(not (isinstance tag-name hy.models.HyString)))
(raise (hy.errors.HyTypeError
(% "received a `%s' instead of a symbol for tag macro name"
(. (type tag-name) --name--))
tag-name --file-- None)))
(if (or (= tag-name ":")
(= tag-name "&"))
(raise (hy.errors.HyNameError (% "%s can't be used as a tag macro name" tag-name))))
(setv tag-name (.replace (hy.models.HyString tag-name)
tag-name))
`(eval-and-compile
(import hy)
((hy.macros.tag ~tag-name)
(fn ~lambda-list ~@body))))
(defmacro macro-error [expression reason &optional [filename '--name--]]
`(raise (hy.errors.HyMacroExpansionError ~reason ~filename ~expression None)))
(defmacro defn [name &rest args]
"Define `name` as a function with `args` as the signature, annotations, and body."
(import hy)
(if (not (= (type name) hy.HySymbol))
(macro-error name "defn takes a name as first argument"))
`(setv ~name (fn* ~@args)))
(defmacro defn/a [name lambda-list &rest body]
"Define `name` as a function with `lambda-list` signature and body `body`."
(import hy)
(if (not (= (type name) hy.HySymbol))
(macro-error name "defn/a takes a name as first argument"))
(if (not (isinstance lambda-list hy.HyList))
(macro-error name "defn/a takes a parameter list as second argument"))
`(setv ~name (fn/a ~lambda-list ~@body)))