Skip to content

Commit

Permalink
make deftag/defmacro macros, not special forms
Browse files Browse the repository at this point in the history
  • Loading branch information
gilch committed Sep 24, 2017
1 parent 1d2e4b5 commit 94f30c3
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 58 deletions.
59 changes: 2 additions & 57 deletions hy/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,7 @@ def compile_raise_expression(self, expr):
@builds("try")
@checkargs(min=2)
def compile_try_expression(self, expr):
expr = copy.deepcopy(expr)
expr.pop(0) # try

# (try something…)
Expand Down Expand Up @@ -1125,6 +1126,7 @@ def compile_yield_expression(self, expr):

@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)]
Expand Down Expand Up @@ -2054,63 +2056,6 @@ def rewire_init(expr):
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):
Expand Down
41 changes: 41 additions & 0 deletions hy/core/bootstrap.hy
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)))
Expand Down
2 changes: 1 addition & 1 deletion hy/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def __str__(self):

result += colored.yellow("%s: %s\n\n" %
(self.__class__.__name__,
self.message.encode('utf-8')))
self.message))

return result

Expand Down
2 changes: 2 additions & 0 deletions hy/macros.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -63,6 +64,7 @@ def tag(name):
"""
def _(fn):
fn.__name__ = '#'+name
module_name = fn.__module__
if module_name.startswith("hy.core"):
module_name = None
Expand Down

0 comments on commit 94f30c3

Please sign in to comment.