make sharp macros take arbitrary identifiers
Previously, only a single character was allowed.
This commit is contained in:
parent
49fd49e7ee
commit
20c26a52e4
@ -785,19 +785,15 @@ defsharp
|
|||||||
.. versionadded:: 0.13.0
|
.. versionadded:: 0.13.0
|
||||||
|
|
||||||
``defsharp`` defines a sharp macro. A sharp macro is a unary macro that has the
|
``defsharp`` defines a sharp macro. A sharp macro is a unary macro that has the
|
||||||
same semantics as an ordinary macro defined with ``defmacro``, but can be
|
same semantics as an ordinary macro defined with ``defmacro``. It is called with
|
||||||
called without parentheses and with less whitespace. The name of a sharp macro
|
the syntax ``#tag FORM``, where ``tag`` is the name of the macro, and ``FORM``
|
||||||
must be exactly one character long. It is called with the syntax ``#cFORM``,
|
is any form. The ``tag`` is often only one character, but it can be any symbol.
|
||||||
where ``#`` is a literal sharp sign (hence the term "sharp macro"), ``c`` is
|
|
||||||
the name of the macro, and ``FORM`` is any form. Whitspace is forbidden between
|
|
||||||
``#`` and ``c``. Whitespace is allowed between ``c`` and ``FORM``, but not
|
|
||||||
required.
|
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: clj
|
||||||
|
|
||||||
=> (defsharp ♣ [expr] `[~expr ~expr])
|
=> (defsharp ♣ [expr] `[~expr ~expr])
|
||||||
<function <lambda> at 0x7f76d0271158>
|
<function <lambda> at 0x7f76d0271158>
|
||||||
=> #♣5
|
=> #♣ 5
|
||||||
[5, 5]
|
[5, 5]
|
||||||
=> (setv x 0)
|
=> (setv x 0)
|
||||||
=> #♣(+= x 1)
|
=> #♣(+= x 1)
|
||||||
|
@ -582,7 +582,7 @@ elements, so by the time program started executing, it actually reads:
|
|||||||
(+ 1 2 3)
|
(+ 1 2 3)
|
||||||
|
|
||||||
Sometimes it's nice to be able to call a one-parameter macro without
|
Sometimes it's nice to be able to call a one-parameter macro without
|
||||||
parentheses. Sharp macros allow this. The name of a sharp macro must be only
|
parentheses. Sharp macros allow this. The name of a sharp macro is typically
|
||||||
one character long, but since Hy operates well with Unicode, we aren't running
|
one character long, but since Hy operates well with Unicode, we aren't running
|
||||||
out of characters that soon:
|
out of characters that soon:
|
||||||
|
|
||||||
|
13
hy/compiler.py
Normal file → Executable file
13
hy/compiler.py
Normal file → Executable file
@ -2470,7 +2470,7 @@ class HyASTCompiler(object):
|
|||||||
def compile_sharp_macro(self, expression):
|
def compile_sharp_macro(self, expression):
|
||||||
expression.pop(0)
|
expression.pop(0)
|
||||||
name = expression.pop(0)
|
name = expression.pop(0)
|
||||||
if name == ":" or name == "&" or len(name) > 1:
|
if name == ":" or name == "&":
|
||||||
raise NameError("%s can't be used as a sharp macro name" % name)
|
raise NameError("%s can't be used as a sharp macro name" % name)
|
||||||
if not isinstance(name, HySymbol) and not isinstance(name, HyString):
|
if not isinstance(name, HySymbol) and not isinstance(name, HyString):
|
||||||
raise HyTypeError(name,
|
raise HyTypeError(name,
|
||||||
@ -2490,14 +2490,15 @@ class HyASTCompiler(object):
|
|||||||
@checkargs(exact=2)
|
@checkargs(exact=2)
|
||||||
def compile_dispatch_sharp_macro(self, expression):
|
def compile_dispatch_sharp_macro(self, expression):
|
||||||
expression.pop(0) # dispatch-sharp-macro
|
expression.pop(0) # dispatch-sharp-macro
|
||||||
str_char = expression.pop(0)
|
tag = expression.pop(0)
|
||||||
if not type(str_char) == HyString:
|
if not type(tag) == HyString:
|
||||||
raise HyTypeError(
|
raise HyTypeError(
|
||||||
str_char,
|
tag,
|
||||||
"Trying to expand a sharp macro using `{0}' instead "
|
"Trying to expand a sharp macro using `{0}' instead "
|
||||||
"of string".format(type(str_char).__name__),
|
"of string".format(type(tag).__name__),
|
||||||
)
|
)
|
||||||
expr = sharp_macroexpand(str_char, expression.pop(0), self)
|
tag = HyString(hy_symbol_mangle(str(tag))).replace(tag)
|
||||||
|
expr = sharp_macroexpand(tag, expression.pop(0), self)
|
||||||
return self.compile(expr)
|
return self.compile(expr)
|
||||||
|
|
||||||
@builds("eval_and_compile")
|
@builds("eval_and_compile")
|
||||||
|
5
hy/lex/lexer.py
Normal file → Executable file
5
hy/lex/lexer.py
Normal file → Executable file
@ -12,6 +12,7 @@ lg = LexerGenerator()
|
|||||||
# i.e. a space or a closing brace/paren/curly
|
# i.e. a space or a closing brace/paren/curly
|
||||||
end_quote = r'(?![\s\)\]\}])'
|
end_quote = r'(?![\s\)\]\}])'
|
||||||
|
|
||||||
|
identifier = r'[^()\[\]{}\'"\s;]+'
|
||||||
|
|
||||||
lg.add('LPAREN', r'\(')
|
lg.add('LPAREN', r'\(')
|
||||||
lg.add('RPAREN', r'\)')
|
lg.add('RPAREN', r'\)')
|
||||||
@ -25,7 +26,7 @@ lg.add('QUASIQUOTE', r'`%s' % end_quote)
|
|||||||
lg.add('UNQUOTESPLICE', r'~@%s' % end_quote)
|
lg.add('UNQUOTESPLICE', r'~@%s' % end_quote)
|
||||||
lg.add('UNQUOTE', r'~%s' % end_quote)
|
lg.add('UNQUOTE', r'~%s' % end_quote)
|
||||||
lg.add('HASHBANG', r'#!.*[^\r\n]')
|
lg.add('HASHBANG', r'#!.*[^\r\n]')
|
||||||
lg.add('HASHOTHER', r'#[^{]')
|
lg.add('HASHOTHER', r'#%s' % identifier)
|
||||||
|
|
||||||
# A regexp which matches incomplete strings, used to support
|
# A regexp which matches incomplete strings, used to support
|
||||||
# multi-line strings in the interpreter
|
# multi-line strings in the interpreter
|
||||||
@ -44,7 +45,7 @@ partial_string = r'''(?x)
|
|||||||
lg.add('STRING', r'%s"' % partial_string)
|
lg.add('STRING', r'%s"' % partial_string)
|
||||||
lg.add('PARTIAL_STRING', partial_string)
|
lg.add('PARTIAL_STRING', partial_string)
|
||||||
|
|
||||||
lg.add('IDENTIFIER', r'[^()\[\]{}\'"\s;]+')
|
lg.add('IDENTIFIER', identifier)
|
||||||
|
|
||||||
|
|
||||||
lg.ignore(r';.*(?=\r|\n|$)')
|
lg.ignore(r';.*(?=\r|\n|$)')
|
||||||
|
3
hy/lex/parser.py
Normal file → Executable file
3
hy/lex/parser.py
Normal file → Executable file
@ -200,7 +200,8 @@ def term_unquote_splice(p):
|
|||||||
@pg.production("term : HASHOTHER term")
|
@pg.production("term : HASHOTHER term")
|
||||||
@set_quote_boundaries
|
@set_quote_boundaries
|
||||||
def hash_other(p):
|
def hash_other(p):
|
||||||
st = p[0].getstr()[1]
|
# p == [(Token('HASHOTHER', '#foo'), bar)]
|
||||||
|
st = p[0].getstr()[1:]
|
||||||
str_object = HyString(st)
|
str_object = HyString(st)
|
||||||
expr = p[1]
|
expr = p[1]
|
||||||
return HyExpression([HySymbol("dispatch_sharp_macro"), str_object, expr])
|
return HyExpression([HySymbol("dispatch_sharp_macro"), str_object, expr])
|
||||||
|
12
hy/macros.py
12
hy/macros.py
@ -210,18 +210,18 @@ def macroexpand_1(tree, compiler):
|
|||||||
return tree
|
return tree
|
||||||
|
|
||||||
|
|
||||||
def sharp_macroexpand(char, tree, compiler):
|
def sharp_macroexpand(tag, tree, compiler):
|
||||||
"""Expand the sharp macro "char" with argument `tree`."""
|
"""Expand the sharp macro "tag" with argument `tree`."""
|
||||||
load_macros(compiler.module_name)
|
load_macros(compiler.module_name)
|
||||||
|
|
||||||
sharp_macro = _hy_sharp[compiler.module_name].get(char)
|
sharp_macro = _hy_sharp[compiler.module_name].get(tag)
|
||||||
if sharp_macro is None:
|
if sharp_macro is None:
|
||||||
try:
|
try:
|
||||||
sharp_macro = _hy_sharp[None][char]
|
sharp_macro = _hy_sharp[None][tag]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise HyTypeError(
|
raise HyTypeError(
|
||||||
char,
|
tag,
|
||||||
"`{0}' is not a defined sharp macro.".format(char)
|
"`{0}' is not a defined sharp macro.".format(tag)
|
||||||
)
|
)
|
||||||
|
|
||||||
expr = sharp_macro(tree)
|
expr = sharp_macro(tree)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user