From 370768105644559b314425694508b604cc9f933c Mon Sep 17 00:00:00 2001 From: gilch Date: Sun, 24 Sep 2017 11:54:54 -0600 Subject: [PATCH] make deftag/defmacro macros, not special forms --- hy/compiler.py | 59 ++------------------------------------------ hy/core/bootstrap.hy | 41 ++++++++++++++++++++++++++++++ hy/errors.py | 2 +- hy/macros.py | 2 ++ 4 files changed, 46 insertions(+), 58 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 5ce9073..2583cb2 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -831,6 +831,7 @@ class HyASTCompiler(object): @builds("try") @checkargs(min=2) def compile_try_expression(self, expr): + expr = copy.deepcopy(expr) expr.pop(0) # try # (try something…) @@ -1125,6 +1126,7 @@ class HyASTCompiler(object): @builds("import") def compile_import_expression(self, expr): + expr = copy.deepcopy(expr) def _compile_import(expr, module, names=None, importer=asty.Import): if not names: names = [ast.alias(name=ast_str(module), asname=None)] @@ -2054,63 +2056,6 @@ class HyASTCompiler(object): bases=bases_expr, body=body.stmts) - def _compile_time_hack(self, expression): - """Compile-time hack: we want to get our new macro now - We must provide __name__ in the namespace to make the Python - compiler set the __module__ attribute of the macro function.""" - - hy.importer.hy_eval(copy.deepcopy(expression), - compile_time_ns(self.module_name), - self.module_name) - - # We really want to have a `hy` import to get hy.macro in - ret = self.compile(expression) - ret.add_imports('hy', [None]) - return ret - - @builds("defmacro") - @checkargs(min=1) - def compile_macro(self, expression): - expression.pop(0) - name = expression.pop(0) - if not isinstance(name, HySymbol): - raise HyTypeError(name, ("received a `%s' instead of a symbol " - "for macro name" % type(name).__name__)) - name = HyString(name).replace(name) - for kw in ("&kwonly", "&kwargs", "&key"): - if kw in expression[0]: - raise HyTypeError(name, "macros cannot use %s" % kw) - expression[0].insert(0, HySymbol('&name')) - new_expression = HyExpression([ - HyExpression([HySymbol("hy.macros.macro"), name]), - HyExpression([HySymbol("fn")] + expression), - ]).replace(expression) - - ret = self._compile_time_hack(new_expression) - - return ret - - @builds("deftag") - @checkargs(min=2) - def compile_tag_macro(self, expression): - expression.pop(0) - name = expression.pop(0) - if name == ":" or name == "&": - raise NameError("%s can't be used as a tag macro name" % name) - if not isinstance(name, HySymbol) and not isinstance(name, HyString): - raise HyTypeError(name, - ("received a `%s' instead of a symbol " - "for tag macro name" % type(name).__name__)) - name = HyString(name).replace(name) - new_expression = HyExpression([ - HyExpression([HySymbol("hy.macros.tag"), name]), - HyExpression([HySymbol("fn")] + expression), - ]).replace(expression) - - ret = self._compile_time_hack(new_expression) - - return ret - @builds("dispatch_tag_macro") @checkargs(exact=2) def compile_dispatch_tag_macro(self, expression): diff --git a/hy/core/bootstrap.hy b/hy/core/bootstrap.hy index 4ecb243..d2a4920 100644 --- a/hy/core/bootstrap.hy +++ b/hy/core/bootstrap.hy @@ -6,6 +6,30 @@ ;;; 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 + macro-name + (% "received a `%s' instead of a symbol for macro name" + (. (type name) + __name__))))) + (for* [kw '[&kwonly &kwargs &key]] + (if* (in kw lambda-list) + (raise (hy.errors.HyTypeError macro-name + (% "macros cannot use %s" + kw))))) + ;; this looks familiar... + `(eval-and-compile + (import hy) + ((hy.macros.macro ~(str macro-name)) + (fn ~(+ `[&name] lambda-list) + ~@body)))))) + (defmacro if [&rest args] "if with elif" (setv n (len args)) @@ -16,6 +40,23 @@ ~(get args 1) (if ~@(cut args 2)))))) +(defmacro deftag [tag-name lambda-list &rest body] + (if (and (not (isinstance tag-name hy.models.HySymbol)) + (not (isinstance tag-name hy.models.HyString))) + (raise (hy.errors.HyTypeError + tag-name + (% "received a `%s' instead of a symbol for tag macro name" + (. (type tag-name) __name__))))) + (if (or (= tag-name ":") + (= tag-name "&")) + (raise (NameError (% "%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 [location reason] "error out properly within a macro" `(raise (hy.errors.HyMacroExpansionError ~location ~reason))) diff --git a/hy/errors.py b/hy/errors.py index 6fb00fd..257ea3c 100644 --- a/hy/errors.py +++ b/hy/errors.py @@ -81,7 +81,7 @@ class HyTypeError(TypeError): result += colored.yellow("%s: %s\n\n" % (self.__class__.__name__, - self.message.encode('utf-8'))) + self.message)) return result diff --git a/hy/macros.py b/hy/macros.py index 76b03de..82d88d6 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -34,6 +34,7 @@ def macro(name): """ def _(fn): + fn.__name__ = '({})'.format(name) try: argspec = getargspec(fn) fn._hy_macro_pass_compiler = argspec.keywords is not None @@ -63,6 +64,7 @@ def tag(name): """ def _(fn): + fn.__name__ = '#{}'.format(name) module_name = fn.__module__ if module_name.startswith("hy.core"): module_name = None