From 1327d5888227ce0bb8a691454d9e482ab8e1bfe1 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 13:44:11 +0200 Subject: [PATCH 1/7] Add a few tests for various defn corner cases Closes #302. Signed-off-by: Gergely Nagy --- tests/compilers/test_ast.py | 8 ++++++++ tests/native_tests/language.hy | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4491f83..3b23b61 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -519,3 +519,11 @@ def test_attribute_access(): def test_cons_correct(): """Ensure cons gets compiled correctly""" can_compile("(cons a b)") + + +def test_defn(): + """Ensure that defn works correctly in various corner cases""" + cant_compile("(defn if [] 1)") + cant_compile("(defn \"hy\" [] 1)") + cant_compile("(defn :hy [] 1)") + can_compile("(defn &hy [] 1)") diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d468533..ec771e6 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -673,6 +673,12 @@ (assert (= 43 (my-fun 42)))) +(defn test-defn-lambdakey [] + "NATIVE: test defn with a &symbol function name" + (defn &hy [] 1) + (assert (= (&hy) 1))) + + (defn test-defn-do [] "NATIVE: test defn evaluation order with do" (setv acc []) From d3520e56407d037de0f2725c793c7a24dd209a2b Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 14:19:19 +0200 Subject: [PATCH 2/7] Trying to setv a callable should raise a nice error When trying to setv a callable, raise an error instead of showing the user an incredibly ugly backtrace. Closes #532. Signed-off-by: Gergely Nagy --- hy/compiler.py | 4 ++++ tests/compilers/test_ast.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/hy/compiler.py b/hy/compiler.py index 8e9893c..fac4cfa 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2004,6 +2004,10 @@ class HyASTCompiler(object): result = self.compile(result) ld_name = self.compile(name) + if isinstance(ld_name.expr, ast.Call): + raise HyTypeError(name, + "Can't assign to a callable: `%s'" % str_name) + if result.temp_variables \ and isinstance(name, HyString) \ and '.' not in name: diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4491f83..c68c609 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -519,3 +519,9 @@ def test_attribute_access(): def test_cons_correct(): """Ensure cons gets compiled correctly""" can_compile("(cons a b)") + + +def test_bad_setv(): + """Ensure setv handles error cases""" + cant_compile("(setv if 1)") + cant_compile("(setv (a b) [1 2])") From 73f8a47f659680dc3c77f6819909a09f9e4aec73 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 10 Aug 2015 14:37:17 +0200 Subject: [PATCH 3/7] Mangle trailing bangs on symbols Postfixing functions with a bang, like set!, get!, etc are relatively common. However, those names are not valid in python, so in the name of python interoperability, lets mangle them to set_bang and get_bang, respectively. Closes #536. Signed-off-by: Gergely Nagy --- hy/lex/parser.py | 3 +++ tests/lex/test_lex.py | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/hy/lex/parser.py b/hy/lex/parser.py index a63be3e..22aaf26 100644 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -308,6 +308,9 @@ def t_identifier(p): if p.endswith("?") and p != "?": p = "is_%s" % (p[:-1]) + if p.endswith("!") and p != "!": + p = "%s_bang" % (p[:-1]) + return p obj = ".".join([mangle(part) for part in obj.split(".")]) diff --git a/tests/lex/test_lex.py b/tests/lex/test_lex.py index cc95675..56ed52a 100644 --- a/tests/lex/test_lex.py +++ b/tests/lex/test_lex.py @@ -326,6 +326,24 @@ def test_lex_mangling_qmark(): assert entry == [HySymbol(".is_foo.bar.is_baz")] +def test_lex_mangling_bang(): + """Ensure that identifiers ending with a bang get mangled ok""" + entry = tokenize("foo!") + assert entry == [HySymbol("foo_bang")] + entry = tokenize("!") + assert entry == [HySymbol("!")] + entry = tokenize("im!foo") + assert entry == [HySymbol("im!foo")] + entry = tokenize(".foo!") + assert entry == [HySymbol(".foo_bang")] + entry = tokenize("foo.bar!") + assert entry == [HySymbol("foo.bar_bang")] + entry = tokenize("foo!.bar") + assert entry == [HySymbol("foo_bang.bar")] + entry = tokenize(".foo!.bar.baz!") + assert entry == [HySymbol(".foo_bang.bar.baz_bang")] + + def test_simple_cons(): """Check that cons gets tokenized correctly""" entry = tokenize("(a . b)")[0] From 4138c660cc040aac1177086e3a236c7585e5c91f Mon Sep 17 00:00:00 2001 From: Ewald Grusk Date: Mon, 15 Jun 2015 22:42:02 +0200 Subject: [PATCH 4/7] Raise more appropriate error on missing kwarg value --- hy/compiler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 8e9893c..05a2fd5 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -459,8 +459,9 @@ class HyASTCompiler(object): try: value = next(exprs_iter) except StopIteration: - msg = "Keyword argument {kw} needs a value" - raise HyCompileError(msg.format(kw=str(expr))) + raise HyTypeError(expr, + "Keyword argument {kw} needs " + "a value.".format(kw=str(expr))) compiled_value = self.compile(value) ret += compiled_value From e407b0a605f42c7d266d9334dcc59e3558313d16 Mon Sep 17 00:00:00 2001 From: Ewald Grusk Date: Mon, 22 Jun 2015 20:18:04 +0200 Subject: [PATCH 5/7] New test: Ensure the compiler chokes on missing keyword argument values --- tests/compilers/test_ast.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4491f83..633c3bb 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -439,6 +439,16 @@ def test_lambda_list_keywords_mixed(): " (list x xs kwxs kwoxs))") +def test_missing_keyword_argument_value(): + """Ensure the compiler chokes on missing keyword argument values.""" + try: + can_compile("((fn [x] x) :x)") + except HyTypeError as e: + assert(e.message == "Keyword argument \ufdd0:x needs a value.") + else: + assert(False) + + def test_ast_unicode_strings(): """Ensure we handle unicode strings correctly""" From b36294336572d6c4238613bf5be3288192d40fd3 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 11 Aug 2015 10:43:00 +0200 Subject: [PATCH 6/7] compiler: Fix the kw argument needs value exception Strip the \ufdd0 prefix from the keyword argument before turning it into a string: the same representation the user entered looks better, and is printable too, thus Python2 doesn't choke on it. Signed-off-by: Gergely Nagy --- hy/compiler.py | 2 +- tests/compilers/test_ast.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 05a2fd5..41119f1 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -461,7 +461,7 @@ class HyASTCompiler(object): except StopIteration: raise HyTypeError(expr, "Keyword argument {kw} needs " - "a value.".format(kw=str(expr))) + "a value.".format(kw=str(expr[1:]))) compiled_value = self.compile(value) ret += compiled_value diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 633c3bb..dd6ae59 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -444,7 +444,7 @@ def test_missing_keyword_argument_value(): try: can_compile("((fn [x] x) :x)") except HyTypeError as e: - assert(e.message == "Keyword argument \ufdd0:x needs a value.") + assert(e.message == "Keyword argument :x needs a value.") else: assert(False) From a22e2ca4cceea247435c59fbb21fa40d01a8fe7f Mon Sep 17 00:00:00 2001 From: gilch Date: Tue, 11 Aug 2015 16:22:13 -0600 Subject: [PATCH 7/7] purged null from Hy we already have `nil`, and `null` was barely ever used. --- eg/debian/parse-rfc822.hy | 2 +- hy/compiler.py | 2 +- hy/lex/parser.py | 1 - tests/compilers/test_ast.py | 4 ++-- tests/native_tests/language.hy | 4 +--- tests/native_tests/unless.hy | 4 ++-- tests/native_tests/when.hy | 4 ++-- 7 files changed, 9 insertions(+), 12 deletions(-) diff --git a/eg/debian/parse-rfc822.hy b/eg/debian/parse-rfc822.hy index fe76870..3ba7de9 100644 --- a/eg/debian/parse-rfc822.hy +++ b/eg/debian/parse-rfc822.hy @@ -16,7 +16,7 @@ (defn parse-rfc822-stream [fd] "Parse an RFC822 stream" (setv bits {}) - (setv key null) + (setv key None) (for [line fd] (if (in ":" line) (do (setv line (.split line ":" 1)) diff --git a/hy/compiler.py b/hy/compiler.py index 41119f1..38eef63 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -81,7 +81,7 @@ def load_stdlib(): # keywords in Python 3.* def _is_hy_builtin(name, module_name): extras = ['True', 'False', 'None', - 'true', 'false', 'nil', 'null'] + 'true', 'false', 'nil'] if name in extras or keyword.iskeyword(name): return True # for non-Hy modules, check for pre-existing name in diff --git a/hy/lex/parser.py b/hy/lex/parser.py index 22aaf26..abce1d7 100644 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -289,7 +289,6 @@ def t_identifier(p): "true": "True", "false": "False", "nil": "None", - "null": "None", } if obj in table: diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index dd6ae59..47c868a 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -219,8 +219,8 @@ def test_ast_good_defclass(): def test_ast_bad_defclass(): "Make sure AST can't compile invalid defclass" cant_compile("(defclass)") - cant_compile("(defclass a null)") - cant_compile("(defclass a null null)") + cant_compile("(defclass a None)") + cant_compile("(defclass a None None)") def test_ast_good_lambda(): diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d468533..30130f4 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -54,8 +54,6 @@ (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(setv nil 1)) (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) - (try (eval '(setv null 1)) - (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(defn defclass [] (print "hello"))) (except [e [TypeError]] (assert (in "Can't assign to a builtin" (str e))))) (try (eval '(defn get [] (print "hello"))) @@ -228,7 +226,7 @@ "NATIVE: test if cond sorta works." (cond [(= 1 2) (assert (is true false))] - [(is null null) (setv x true) (assert x)])) + [(is None None) (setv x true) (assert x)])) (defn test-index [] diff --git a/tests/native_tests/unless.hy b/tests/native_tests/unless.hy index 2b957fe..a55e052 100644 --- a/tests/native_tests/unless.hy +++ b/tests/native_tests/unless.hy @@ -3,8 +3,8 @@ (assert (= (unless false 1) 1)) (assert (= (unless false 1 2) 2)) (assert (= (unless false 1 3) 3)) - (assert (= (unless true 2) null)) + (assert (= (unless true 2) None)) (assert (= (unless true 2) nil)) - (assert (= (unless (!= 1 2) 42) null)) + (assert (= (unless (!= 1 2) 42) None)) (assert (= (unless (!= 1 2) 42) nil)) (assert (= (unless (!= 2 2) 42) 42))) diff --git a/tests/native_tests/when.hy b/tests/native_tests/when.hy index c281439..9c21c9f 100644 --- a/tests/native_tests/when.hy +++ b/tests/native_tests/when.hy @@ -3,8 +3,8 @@ (assert (= (when true 1) 1)) (assert (= (when true 1 2) 2)) (assert (= (when true 1 3) 3)) - (assert (= (when false 2) null)) - (assert (= (when (= 1 2) 42) null)) + (assert (= (when false 2) None)) + (assert (= (when (= 1 2) 42) None)) (assert (= (when false 2) nil)) (assert (= (when (= 1 2) 42) nil)) (assert (= (when (= 2 2) 42) 42)))