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,13 +785,9 @@ 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

View File

@ -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
View 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
View 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
View 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])

View File

@ -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)