Fix reader macros to actually be macros
This commit is contained in:
parent
d34c22eeac
commit
a35ecc41bd
@ -24,19 +24,28 @@ Syntax
|
|||||||
=> #^1+2+3+4+3+2
|
=> #^1+2+3+4+3+2
|
||||||
1+2+3+4+3+2
|
1+2+3+4+3+2
|
||||||
|
|
||||||
|
Hy got no literal for tuples. Lets say you dislike `(, ...)` and want something
|
||||||
|
else. This is a problem reader macros are able to solve in a neat way.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
=> (defreader t [expr] `(, ~@expr))
|
||||||
|
=> #t(1 2 3)
|
||||||
|
(1, 2, 3)
|
||||||
|
|
||||||
|
You could even do like clojure, and have a litteral for regular expressions!
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
=> (import re)
|
||||||
|
=> (defreader r [expr] `(re.compile ~expr))
|
||||||
|
=> #r".*"
|
||||||
|
<_sre.SRE_Pattern object at 0xcv7713ph15#>
|
||||||
|
|
||||||
|
|
||||||
Implementation
|
Implementation
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Hy uses ``defreader`` to define the reader symbol, and ``#`` as the dispatch
|
|
||||||
character. ``#`` expands into ``(dispatch_reader_macro ...)`` where the symbol
|
|
||||||
and expression is quoted, and then passed along to the correct function::
|
|
||||||
|
|
||||||
=> (defreader ^ ...)
|
|
||||||
=> #^()
|
|
||||||
;=> (dispatch_reader_macro '^ '())
|
|
||||||
|
|
||||||
|
|
||||||
``defreader`` takes a single character as symbol name for the reader macro,
|
``defreader`` takes a single character as symbol name for the reader macro,
|
||||||
anything longer will return an error. Implementation wise, ``defreader``
|
anything longer will return an error. Implementation wise, ``defreader``
|
||||||
expands into a lambda covered with a decorator, this decorater saves the
|
expands into a lambda covered with a decorator, this decorater saves the
|
||||||
@ -47,14 +56,17 @@ lambda in a dict with its module name and symbol.
|
|||||||
=> (defreader ^ [expr] (print expr))
|
=> (defreader ^ [expr] (print expr))
|
||||||
;=> (with_decorator (hy.macros.reader ^) (fn [expr] (print expr)))
|
;=> (with_decorator (hy.macros.reader ^) (fn [expr] (print expr)))
|
||||||
|
|
||||||
|
``#`` expands into ``(dispatch_reader_macro ...)`` where the symbol
|
||||||
Anything passed along is quoted, thus given to the function defined.
|
and expression is passed to the correct function.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
=> #^()
|
||||||
|
;=> (dispatch_reader_macro ^ ())
|
||||||
=> #^"Hello"
|
=> #^"Hello"
|
||||||
"Hello"
|
"Hello"
|
||||||
|
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
Because of a limitation in Hy's lexer and parser, reader macros can't
|
Because of a limitation in Hy's lexer and parser, reader macros can't
|
||||||
redefine defined syntax such as ``()[]{}``. This will most likely be
|
redefine defined syntax such as ``()[]{}``. This will most likely be
|
||||||
|
@ -38,8 +38,8 @@ from hy.models.dict import HyDict
|
|||||||
from hy.errors import HyCompileError, HyTypeError
|
from hy.errors import HyCompileError, HyTypeError
|
||||||
|
|
||||||
import hy.macros
|
import hy.macros
|
||||||
from hy.macros import require, macroexpand
|
|
||||||
from hy._compat import str_type, long_type, PY27, PY33, PY3, PY34
|
from hy._compat import str_type, long_type, PY27, PY33, PY3, PY34
|
||||||
|
from hy.macros import require, macroexpand, reader_macroexpand
|
||||||
import hy.importer
|
import hy.importer
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
@ -1980,7 +1980,7 @@ class HyASTCompiler(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@builds("defreader")
|
@builds("defreader")
|
||||||
@checkargs(min=2, max=3)
|
@checkargs(min=2)
|
||||||
def compile_reader(self, expression):
|
def compile_reader(self, expression):
|
||||||
expression.pop(0)
|
expression.pop(0)
|
||||||
name = expression.pop(0)
|
name = expression.pop(0)
|
||||||
@ -2002,6 +2002,23 @@ class HyASTCompiler(object):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@builds("dispatch_reader_macro")
|
||||||
|
@checkargs(exact=2)
|
||||||
|
def compile_dispatch_reader_macro(self, expression):
|
||||||
|
expression.pop(0) # dispatch-reader-macro
|
||||||
|
str_char = expression.pop(0)
|
||||||
|
if not type(str_char) == HyString:
|
||||||
|
raise HyTypeError(
|
||||||
|
str_char,
|
||||||
|
"Trying to expand a reader macro using `{0}' instead "
|
||||||
|
"of string".format(type(str_char).__name__),
|
||||||
|
)
|
||||||
|
|
||||||
|
module = self.module_name
|
||||||
|
expr = reader_macroexpand(str_char, expression.pop(0), module)
|
||||||
|
|
||||||
|
return self.compile(expr)
|
||||||
|
|
||||||
@builds("eval_and_compile")
|
@builds("eval_and_compile")
|
||||||
def compile_eval_and_compile(self, expression):
|
def compile_eval_and_compile(self, expression):
|
||||||
expression[0] = HySymbol("progn")
|
expression[0] = HySymbol("progn")
|
||||||
|
@ -181,13 +181,3 @@
|
|||||||
(setv -args (cdr (car -args))))
|
(setv -args (cdr (car -args))))
|
||||||
|
|
||||||
`(apply ~-fun [~@-args] (dict (sum ~-okwargs [])))))
|
`(apply ~-fun [~@-args] (dict (sum ~-okwargs [])))))
|
||||||
|
|
||||||
|
|
||||||
(defmacro dispatch-reader-macro [char &rest body]
|
|
||||||
"Dispatch a reader macro based on the character"
|
|
||||||
(import [hy.macros])
|
|
||||||
(setv str_char (get char 1))
|
|
||||||
(if (not (in str_char hy.macros._hy_reader_chars))
|
|
||||||
(raise (hy.compiler.HyTypeError char (.format "There is no reader macro with the character `{0}`" str_char))))
|
|
||||||
`(do (import [hy.macros [_hy_reader]])
|
|
||||||
((get (get _hy_reader --name--) ~char) ~(get body 0))))
|
|
||||||
|
@ -154,8 +154,8 @@ def term_unquote_splice(p):
|
|||||||
@set_quote_boundaries
|
@set_quote_boundaries
|
||||||
def hash_reader(p):
|
def hash_reader(p):
|
||||||
st = p[0].getstr()[1]
|
st = p[0].getstr()[1]
|
||||||
str_object = HyExpression([HySymbol("quote"), HyString(st)])
|
str_object = HyString(st)
|
||||||
expr = HyExpression([HySymbol("quote"), p[1]])
|
expr = p[1]
|
||||||
return HyExpression([HySymbol("dispatch_reader_macro"), str_object, expr])
|
return HyExpression([HySymbol("dispatch_reader_macro"), str_object, expr])
|
||||||
|
|
||||||
|
|
||||||
|
20
hy/macros.py
20
hy/macros.py
@ -43,7 +43,6 @@ EXTRA_MACROS = [
|
|||||||
|
|
||||||
_hy_macros = defaultdict(dict)
|
_hy_macros = defaultdict(dict)
|
||||||
_hy_reader = defaultdict(dict)
|
_hy_reader = defaultdict(dict)
|
||||||
_hy_reader_chars = set()
|
|
||||||
|
|
||||||
|
|
||||||
def macro(name):
|
def macro(name):
|
||||||
@ -85,8 +84,6 @@ def reader(name):
|
|||||||
module_name = None
|
module_name = None
|
||||||
_hy_reader[module_name][name] = fn
|
_hy_reader[module_name][name] = fn
|
||||||
|
|
||||||
# Ugly hack to get some error handling
|
|
||||||
_hy_reader_chars.add(name)
|
|
||||||
return fn
|
return fn
|
||||||
return _
|
return _
|
||||||
|
|
||||||
@ -209,3 +206,20 @@ def macroexpand_1(tree, module_name):
|
|||||||
|
|
||||||
return ntree
|
return ntree
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
|
|
||||||
|
def reader_macroexpand(char, tree, module_name):
|
||||||
|
"""Expand the reader macro "char" with argument `tree`."""
|
||||||
|
load_macros(module_name)
|
||||||
|
|
||||||
|
if not char in _hy_reader[module_name]:
|
||||||
|
raise HyTypeError(
|
||||||
|
char,
|
||||||
|
"`{0}' is not a reader macro in module '{1}'".format(
|
||||||
|
char,
|
||||||
|
module_name,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
expr = _hy_reader[module_name][char](tree)
|
||||||
|
return _wrap_value(expr).replace(tree)
|
||||||
|
@ -258,7 +258,7 @@ def test_reader_macro():
|
|||||||
"""Ensure reader macros are handles properly"""
|
"""Ensure reader macros are handles properly"""
|
||||||
entry = tokenize("#^()")
|
entry = tokenize("#^()")
|
||||||
assert entry[0][0] == HySymbol("dispatch_reader_macro")
|
assert entry[0][0] == HySymbol("dispatch_reader_macro")
|
||||||
assert entry[0][1] == HyExpression([HySymbol("quote"), HyString("^")])
|
assert entry[0][1] == HyString("^")
|
||||||
assert len(entry[0]) == 3
|
assert len(entry[0]) == 3
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,10 +23,14 @@
|
|||||||
(assert (= #+2 3)))
|
(assert (= #+2 3)))
|
||||||
|
|
||||||
|
|
||||||
(defn test-reader-macro-compile-docstring []
|
(defn test-reader-macros-macros []
|
||||||
"Test if we can compile with a docstring"
|
"Test if defreader is actually a macro"
|
||||||
(try
|
(defreader t [expr]
|
||||||
(defreader d []
|
`(, ~@expr))
|
||||||
"Compiles with docstrings")
|
|
||||||
(except [Exception]
|
(def a #t[1 2 3])
|
||||||
(assert False))))
|
|
||||||
|
(assert (= (type a) tuple))
|
||||||
|
(assert (= (, 1 2 3) a)))
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user