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
|
||||
|
||||
``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)
|
||||
|
@ -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
13
hy/compiler.py
Normal file → Executable 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
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
|
||||
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
3
hy/lex/parser.py
Normal file → Executable 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])
|
||||
|
12
hy/macros.py
12
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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user