Cleanup the hy.macros module

Add comments to the functions, reorder, make the file clearer
This commit is contained in:
Nicolas Dandrimont 2013-09-29 17:55:15 +02:00
parent 9ea153fd7e
commit d5bf328aa7
3 changed files with 69 additions and 25 deletions

View File

@ -36,7 +36,7 @@ from hy.models.float import HyFloat
from hy.models.list import HyList from hy.models.list import HyList
from hy.models.dict import HyDict from hy.models.dict import HyDict
from hy.macros import require, process from hy.macros import require, macroexpand
from hy._compat import str_type from hy._compat import str_type
import hy.importer import hy.importer
@ -419,7 +419,7 @@ class HyASTCompiler(object):
def compile(self, tree): def compile(self, tree):
try: try:
tree = process(tree, self.module_name) tree = macroexpand(tree, self.module_name)
_type = type(tree) _type = type(tree)
ret = self.compile_atom(_type, tree) ret = self.compile_atom(_type, tree)
if ret: if ret:

View File

@ -43,6 +43,17 @@ _hy_macros = defaultdict(dict)
def macro(name): def macro(name):
"""Decorator to define a macro called `name`.
This stores the macro `name` in the namespace for the module where it is
defined.
If the module where it is defined is in `hy.core`, then the macro is stored
in the default `None` namespace.
This function is called from the `defmacro` special form in the compiler.
"""
def _(fn): def _(fn):
module_name = fn.__module__ module_name = fn.__module__
if module_name.startswith("hy.core"): if module_name.startswith("hy.core"):
@ -52,20 +63,20 @@ def macro(name):
return _ return _
def require(source_module_name, target_module_name): def require(source_module, target_module):
macros = _hy_macros[source_module_name] """Load the macros from `source_module` in the namespace of
refs = _hy_macros[target_module_name] `target_module`.
This function is called from the `require` special form in the compiler.
"""
macros = _hy_macros[source_module]
refs = _hy_macros[target_module]
for name, macro in macros.items(): for name, macro in macros.items():
refs[name] = macro refs[name] = macro
def _wrap_value(x): # type -> wrapping function mapping for _wrap_value
wrapper = _wrappers.get(type(x))
if wrapper is None:
return x
else:
return wrapper(x)
_wrappers = { _wrappers = {
int: HyInteger, int: HyInteger,
bool: lambda x: HySymbol("True") if x else HySymbol("False"), bool: lambda x: HySymbol("True") if x else HySymbol("False"),
@ -77,27 +88,60 @@ _wrappers = {
} }
def process(tree, module_name): def _wrap_value(x):
load_macros(module_name) """Wrap `x` into the corresponding Hy type.
old = None
while old != tree: This allows a macro to return an unquoted expression transparently.
old = tree
tree = macroexpand(tree, module_name) """
return tree wrapper = _wrappers.get(type(x))
if wrapper is None:
return x
else:
return wrapper(x)
def load_macros(module_name): def load_macros(module_name):
"""Load the hy builtin macros for module `module_name`.
Modules from `hy.core` can only use the macros from CORE_MACROS.
Other modules get the macros from CORE_MACROS and EXTRA_MACROS.
"""
def _import(module, module_name=module_name):
"__import__ a module, avoiding recursions"
if module != module_name:
__import__(module)
for module in CORE_MACROS: for module in CORE_MACROS:
__import__(module) _import(module)
if module_name.startswith("hy.core"): if module_name.startswith("hy.core"):
return return
for module in EXTRA_MACROS: for module in EXTRA_MACROS:
__import__(module) _import(module)
def macroexpand(tree, module_name): def macroexpand(tree, module_name):
"""Expand the toplevel macros for the `tree`.
Load the macros from the given `module_name`, then expand the (top-level)
macros in `tree` until it stops changing.
"""
load_macros(module_name)
old = None
while old != tree:
old = tree
tree = macroexpand_1(tree, module_name)
return tree
def macroexpand_1(tree, module_name):
"""Expand the toplevel macro from `tree` once, in the context of
`module_name`."""
if isinstance(tree, HyExpression): if isinstance(tree, HyExpression):
if tree == []: if tree == []:
return tree return tree

View File

@ -1,5 +1,5 @@
from hy.macros import macro, process from hy.macros import macro, macroexpand
from hy.lex import tokenize from hy.lex import tokenize
from hy.models.string import HyString from hy.models.string import HyString
@ -16,14 +16,14 @@ def tmac(*tree):
def test_preprocessor_simple(): def test_preprocessor_simple():
""" Test basic macro expansion """ """ Test basic macro expansion """
obj = process(tokenize('(test "one" "two")')[0], __name__) obj = macroexpand(tokenize('(test "one" "two")')[0], __name__)
assert obj == HyList(["one", "two"]) assert obj == HyList(["one", "two"])
assert type(obj) == HyList assert type(obj) == HyList
def test_preprocessor_expression(): def test_preprocessor_expression():
""" Test that macro expansion doesn't recurse""" """ Test that macro expansion doesn't recurse"""
obj = process(tokenize('(test (test "one" "two"))')[0], __name__) obj = macroexpand(tokenize('(test (test "one" "two"))')[0], __name__)
assert type(obj) == HyList assert type(obj) == HyList
assert type(obj[0]) == HyExpression assert type(obj[0]) == HyExpression
@ -34,4 +34,4 @@ def test_preprocessor_expression():
obj = HyList([HyString("one"), HyString("two")]) obj = HyList([HyString("one"), HyString("two")])
obj = tokenize('(shill ["one" "two"])')[0][1] obj = tokenize('(shill ["one" "two"])')[0][1]
assert obj == process(obj, '') assert obj == macroexpand(obj, '')