diff --git a/docs/language/api.rst b/docs/language/api.rst index a31b6c8..f36cbd5 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -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]) at 0x7f76d0271158> - => #♣5 + => #♣ 5 [5, 5] => (setv x 0) => #♣(+= x 1) diff --git a/docs/tutorial.rst b/docs/tutorial.rst index f0231cd..030a136 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -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: diff --git a/hy/compiler.py b/hy/compiler.py old mode 100644 new mode 100755 index 157cdcb..d9c11b1 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -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") diff --git a/hy/lex/lexer.py b/hy/lex/lexer.py old mode 100644 new mode 100755 index 4039543..c6b5636 --- a/hy/lex/lexer.py +++ b/hy/lex/lexer.py @@ -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|$)') diff --git a/hy/lex/parser.py b/hy/lex/parser.py old mode 100644 new mode 100755 index 6e1aa09..e43d188 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -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]) diff --git a/hy/macros.py b/hy/macros.py index 84b192f..3da2bdb 100644 --- a/hy/macros.py +++ b/hy/macros.py @@ -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)