Repository: hylang/hy

Issue:
(comment ...) or #_ macro to comment a block as in Clojure
do hy has a **comment** or **#_** macro to comment block?
i can not find the comment block function in hy, 
 it's very convenient to comment one whole block rather text line.



Discussion:
If you text editor has Clojure support, you can probably just use whatever your editor has for commenting blocks. In Howl, you select the text and press ctrl+`/`.

Otherwise, you could always define a macro like this:

``` hy
(defmacro discard [&rest x] `nil)
```

Then you could do:

``` hy
(discard (print 123)) ; Does nothing.
(discard (. nil error)) ; Also does nothing.
```

Your `discard` macro becomes `nil` which is equivalent to Clojure's `comment` macro, but that's different than `#_`, which doesn't even become `nil`.

``` Clojure
user=> (println (do 1 2))
2
nil
user=> (println (do 1 (comment 2)))
nil
nil
user=> (println (do 1 #_2))
1
nil
```

You see the difference. Sometimes you need a `nil` placeholder and `comment` is what you want, but usually you want the entire form to not exist temporarily. I think it's worth adding `#_` to Hy proper.

I agree, `#_` would be great. It needs to be on a parser level, I suppose, since it needs to be ignored, and not even make it to the AST.


Resolving patch:
diff --git a/hy/lex/lexer.py b/hy/lex/lexer.py
--- a/hy/lex/lexer.py
+++ b/hy/lex/lexer.py
@@ -25,6 +25,7 @@
 lg.add('QUASIQUOTE', r'`%s' % end_quote)
 lg.add('UNQUOTESPLICE', r'~@%s' % end_quote)
 lg.add('UNQUOTE', r'~%s' % end_quote)
+lg.add('DISCARD', r'#_')
 lg.add('HASHSTARS', r'#\*+')
 lg.add('HASHOTHER', r'#%s' % identifier)
 
diff --git a/hy/lex/parser.py b/hy/lex/parser.py
--- a/hy/lex/parser.py
+++ b/hy/lex/parser.py
@@ -153,6 +153,17 @@ def list_contents_single(p):
     return [p[0]]
 
 
+@pg.production("list_contents : DISCARD term discarded_list_contents")
+def list_contents_empty(p):
+    return []
+
+
+@pg.production("discarded_list_contents : DISCARD term discarded_list_contents")
+@pg.production("discarded_list_contents :")
+def discarded_list_contents(p):
+    pass
+
+
 @pg.production("term : identifier")
 @pg.production("term : paren")
 @pg.production("term : dict")
@@ -163,6 +174,11 @@ def term(p):
     return p[0]
 
 
+@pg.production("term : DISCARD term term")
+def term_discard(p):
+    return p[2]
+
+
 @pg.production("term : QUOTE term")
 @set_quote_boundaries
 def term_quote(p):


Test patch:
diff --git a/tests/native_tests/core.hy b/tests/native_tests/core.hy
--- a/tests/native_tests/core.hy
+++ b/tests/native_tests/core.hy
@@ -635,3 +635,7 @@
                 [1 6 21])
   (assert-equal ((juxt identity) 42)
                 [42]))
+
+(defn test-comment []
+  (assert-none (comment <h1>This is merely a comment.</h1>
+                        <p> Move along. (Nothing to see here.)</p>)))
diff --git a/tests/test_lex.py b/tests/test_lex.py
--- a/tests/test_lex.py
+++ b/tests/test_lex.py
@@ -4,7 +4,7 @@
 
 from math import isnan
 from hy.models import (HyExpression, HyInteger, HyFloat, HyComplex, HySymbol,
-                       HyString, HyDict, HyList, HySet, HyCons)
+                       HyString, HyDict, HyList, HySet, HyCons, HyKeyword)
 from hy.lex import LexException, PrematureEndOfInput, tokenize
 import pytest
 
@@ -423,3 +423,72 @@ def test_cons_list():
     entry = tokenize("(a b . {})")[0]
     assert entry == HyDict([HySymbol("a"), HySymbol("b")])
     assert type(entry) == HyDict
+
+def test_discard():
+    """Check that discarded terms are removed properly."""
+    # empty
+    assert tokenize("") == []
+    # single
+    assert tokenize("#_1") == []
+    # multiple
+    assert tokenize("#_1 #_2") == []
+    assert tokenize("#_1 #_2 #_3") == []
+    # nested discard
+    assert tokenize("#_ #_1 2") == []
+    assert tokenize("#_ #_ #_1 2 3") == []
+    # trailing
+    assert tokenize("0") == [0]
+    assert tokenize("0 #_1") == [0]
+    assert tokenize("0 #_1 #_2") == [0]
+    # leading
+    assert tokenize("2") == [2]
+    assert tokenize("#_1 2") == [2]
+    assert tokenize("#_0 #_1 2") == [2]
+    assert tokenize("#_ #_0 1 2") == [2]
+    # both
+    assert tokenize("#_1 2 #_3") == [2]
+    assert tokenize("#_0 #_1 2 #_ #_3 4") == [2]
+    # inside
+    assert tokenize("0 #_1 2") == [0, 2]
+    assert tokenize("0 #_1 #_2 3") == [0, 3]
+    assert tokenize("0 #_ #_1 2 3") == [0, 3]
+    # in HyList
+    assert tokenize("[]") == [HyList([])]
+    assert tokenize("[#_1]") == [HyList([])]
+    assert tokenize("[#_1 #_2]") == [HyList([])]
+    assert tokenize("[#_ #_1 2]") == [HyList([])]
+    assert tokenize("[0]") == [HyList([HyInteger(0)])]
+    assert tokenize("[0 #_1]") == [HyList([HyInteger(0)])]
+    assert tokenize("[0 #_1 #_2]") == [HyList([HyInteger(0)])]
+    assert tokenize("[2]") == [HyList([HyInteger(2)])]
+    assert tokenize("[#_1 2]") == [HyList([HyInteger(2)])]
+    assert tokenize("[#_0 #_1 2]") == [HyList([HyInteger(2)])]
+    assert tokenize("[#_ #_0 1 2]") == [HyList([HyInteger(2)])]
+    # in HySet
+    assert tokenize("#{}") == [HySet()]
+    assert tokenize("#{#_1}") == [HySet()]
+    assert tokenize("#{0 #_1}") == [HySet([HyInteger(0)])]
+    assert tokenize("#{#_1 0}") == [HySet([HyInteger(0)])]
+    # in HyDict
+    assert tokenize("{}") == [HyDict()]
+    assert tokenize("{#_1}") == [HyDict()]
+    assert tokenize("{#_0 1 2}") == [HyDict([HyInteger(1), HyInteger(2)])]
+    assert tokenize("{1 #_0 2}") == [HyDict([HyInteger(1), HyInteger(2)])]
+    assert tokenize("{1 2 #_0}") == [HyDict([HyInteger(1), HyInteger(2)])]
+    # in HyExpression
+    assert tokenize("()") == [HyExpression()]
+    assert tokenize("(#_foo)") == [HyExpression()]
+    assert tokenize("(#_foo bar)") == [HyExpression([HySymbol("bar")])]
+    assert tokenize("(foo #_bar)") == [HyExpression([HySymbol("foo")])]
+    assert tokenize("(foo :bar 1)") == [HyExpression([HySymbol("foo"), HyKeyword(":bar"), HyInteger(1)])]
+    assert tokenize("(foo #_:bar 1)") == [HyExpression([HySymbol("foo"), HyInteger(1)])]
+    assert tokenize("(foo :bar #_1)") == [HyExpression([HySymbol("foo"), HyKeyword(":bar")])]
+    # discard term with nesting
+    assert tokenize("[1 2 #_[a b c [d e [f g] h]] 3 4]") == [
+        HyList([HyInteger(1), HyInteger(2), HyInteger(3), HyInteger(4)])
+    ]
+    # discard with other prefix syntax
+    assert tokenize("a #_'b c") == [HySymbol("a"), HySymbol("c")]
+    assert tokenize("a '#_b c") == [HySymbol("a"), HyExpression([HySymbol("quote"), HySymbol("c")])]
+    assert tokenize("a '#_b #_c d") == [HySymbol("a"), HyExpression([HySymbol("quote"), HySymbol("d")])]
+    assert tokenize("a '#_ #_b c d") == [HySymbol("a"), HyExpression([HySymbol("quote"), HySymbol("d")])]