make sharp macros take arbitrary identifiers

Previously, only a single character was allowed.
This commit is contained in:
gilch 2017-05-09 19:54:32 -06:00 committed by gilch
parent 49fd49e7ee
commit 20c26a52e4
6 changed files with 23 additions and 24 deletions

View File

@ -785,19 +785,15 @@ defsharp
.. versionadded:: 0.13.0
``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
called without parentheses and with less whitespace. The name of a sharp macro
must be exactly one character long. It is called with the syntax ``#cFORM``,
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.
same semantics as an ordinary macro defined with ``defmacro``. It is called with
the syntax ``#tag FORM``, where ``tag`` is the name of the macro, and ``FORM``
is any form. The ``tag`` is often only one character, but it can be any symbol.
.. code-block:: clj
=> (defsharp ♣ [expr] `[~expr ~expr])
<function <lambda> at 0x7f76d0271158>
=> #♣5
=> #♣ 5
[5, 5]
=> (setv x 0)
=> #♣(+= x 1)

View File

@ -582,7 +582,7 @@ elements, so by the time program started executing, it actually reads:
(+ 1 2 3)
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
out of characters that soon:

13
hy/compiler.py Normal file → Executable file
View File

@ -2470,7 +2470,7 @@ class HyASTCompiler(object):
def compile_sharp_macro(self, expression):
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)
if not isinstance(name, HySymbol) and not isinstance(name, HyString):
raise HyTypeError(name,
@ -2490,14 +2490,15 @@ class HyASTCompiler(object):
@checkargs(exact=2)
def compile_dispatch_sharp_macro(self, expression):
expression.pop(0) # dispatch-sharp-macro
str_char = expression.pop(0)
if not type(str_char) == HyString:
tag = expression.pop(0)
if not type(tag) == HyString:
raise HyTypeError(
str_char,
tag,
"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)
@builds("eval_and_compile")

5
hy/lex/lexer.py Normal file → Executable file
View File

@ -12,6 +12,7 @@ lg = LexerGenerator()
# i.e. a space or a closing brace/paren/curly
end_quote = r'(?![\s\)\]\}])'
identifier = r'[^()\[\]{}\'"\s;]+'
lg.add('LPAREN', 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('UNQUOTE', r'~%s' % end_quote)
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
# multi-line strings in the interpreter
@ -44,7 +45,7 @@ partial_string = r'''(?x)
lg.add('STRING', r'%s"' % partial_string)
lg.add('PARTIAL_STRING', partial_string)
lg.add('IDENTIFIER', r'[^()\[\]{}\'"\s;]+')
lg.add('IDENTIFIER', identifier)
lg.ignore(r';.*(?=\r|\n|$)')

3
hy/lex/parser.py Normal file → Executable file
View File

@ -200,7 +200,8 @@ def term_unquote_splice(p):
@pg.production("term : HASHOTHER term")
@set_quote_boundaries
def hash_other(p):
st = p[0].getstr()[1]
# p == [(Token('HASHOTHER', '#foo'), bar)]
st = p[0].getstr()[1:]
str_object = HyString(st)
expr = p[1]
return HyExpression([HySymbol("dispatch_sharp_macro"), str_object, expr])

View File

@ -210,18 +210,18 @@ def macroexpand_1(tree, compiler):
return tree
def sharp_macroexpand(char, tree, compiler):
"""Expand the sharp macro "char" with argument `tree`."""
def sharp_macroexpand(tag, tree, compiler):
"""Expand the sharp macro "tag" with argument `tree`."""
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:
try:
sharp_macro = _hy_sharp[None][char]
sharp_macro = _hy_sharp[None][tag]
except KeyError:
raise HyTypeError(
char,
"`{0}' is not a defined sharp macro.".format(char)
tag,
"`{0}' is not a defined sharp macro.".format(tag)
)
expr = sharp_macro(tree)