fb6feaf082
Compiler and command-line error messages now reflect their Python counterparts. E.g. where Python emits a `SyntaxError`, so does Hy; same for `TypeError`s. Multiple tests have been added that check the format and type of raised exceptions over varying command-line invocations (e.g. interactive and not). A new exception type for `require` errors was added so that they can be treated like normal run-time errors and not compiler errors. The Hy REPL has been further refactored to better match the class-structured API. Now, different error types are handled separately and leverage more base class-provided functionality. Closes hylang/hy#1486.
87 lines
3.2 KiB
Hy
87 lines
3.2 KiB
Hy
;;; Hy bootstrap macros
|
|
;; Copyright 2019 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 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 takes a name as first argument"))
|
|
(if (not (isinstance lambda-list hy.HyList))
|
|
(macro-error name "defn takes a parameter list as second argument"))
|
|
`(setv ~name (fn* ~lambda-list ~@body)))
|
|
|
|
(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)))
|
|
|
|
(defmacro if-python2 [python2-form python3-form]
|
|
"If running on python2, execute python2-form, else, execute python3-form"
|
|
(import sys)
|
|
(if (< (get sys.version_info 0) 3)
|
|
python2-form
|
|
python3-form))
|