2017-09-08 20:22:31 +02:00
|
|
|
# -*- encoding: utf-8 -*-
|
2019-02-07 14:57:35 +01:00
|
|
|
# Copyright 2019 the authors.
|
2017-04-27 23:16:57 +02:00
|
|
|
# This file is part of Hy, which is free software licensed under the Expat
|
|
|
|
# license. See the LICENSE.
|
2013-03-03 05:47:16 +01:00
|
|
|
|
2013-04-12 20:58:20 +02:00
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
from hy import HyString
|
2018-11-10 19:53:28 +01:00
|
|
|
from hy.compiler import hy_compile, hy_eval
|
2018-10-13 06:25:43 +02:00
|
|
|
from hy.errors import HyCompileError, HyLanguageError, HyError
|
2018-11-10 19:53:28 +01:00
|
|
|
from hy.lex import hy_parse
|
2018-10-29 02:43:17 +01:00
|
|
|
from hy.lex.exceptions import LexException, PrematureEndOfInput
|
2019-05-20 20:34:00 +02:00
|
|
|
from hy._compat import PY36
|
2013-04-02 01:51:21 +02:00
|
|
|
|
2013-03-03 17:18:13 +01:00
|
|
|
import ast
|
2017-12-30 20:17:26 +01:00
|
|
|
import pytest
|
2013-03-03 05:47:16 +01:00
|
|
|
|
2018-08-20 06:29:29 +02:00
|
|
|
|
2013-03-03 19:10:50 +01:00
|
|
|
def _ast_spotcheck(arg, root, secondary):
|
|
|
|
if "." in arg:
|
|
|
|
local, full = arg.split(".", 1)
|
|
|
|
return _ast_spotcheck(full,
|
|
|
|
getattr(root, local),
|
|
|
|
getattr(secondary, local))
|
|
|
|
assert getattr(root, arg) == getattr(secondary, arg)
|
|
|
|
|
|
|
|
|
2013-05-16 15:30:44 +02:00
|
|
|
def can_compile(expr):
|
2018-10-13 06:25:43 +02:00
|
|
|
return hy_compile(hy_parse(expr), __name__)
|
2013-05-16 15:30:44 +02:00
|
|
|
|
|
|
|
|
2018-02-08 21:08:09 +01:00
|
|
|
def can_eval(expr):
|
2018-08-20 06:29:29 +02:00
|
|
|
return hy_eval(hy_parse(expr))
|
2018-02-08 21:08:09 +01:00
|
|
|
|
|
|
|
|
2013-04-06 10:37:21 +02:00
|
|
|
def cant_compile(expr):
|
2018-10-13 06:25:43 +02:00
|
|
|
with pytest.raises(HyError) as excinfo:
|
|
|
|
hy_compile(hy_parse(expr), __name__)
|
|
|
|
|
|
|
|
if issubclass(excinfo.type, HyLanguageError):
|
|
|
|
assert excinfo.value.msg
|
|
|
|
return excinfo.value
|
|
|
|
elif issubclass(excinfo.type, HyCompileError):
|
2013-05-13 18:09:05 +02:00
|
|
|
# Anything that can't be compiled should raise a user friendly
|
|
|
|
# error, otherwise it's a compiler bug.
|
2018-10-13 06:25:43 +02:00
|
|
|
return excinfo.value
|
2013-04-06 10:37:21 +02:00
|
|
|
|
|
|
|
|
2017-09-08 20:22:31 +02:00
|
|
|
def s(x):
|
2018-03-13 21:29:41 +01:00
|
|
|
return can_compile('"module docstring" ' + x).body[-1].value.s
|
2017-09-08 20:22:31 +02:00
|
|
|
|
|
|
|
|
2013-03-03 19:12:23 +01:00
|
|
|
def test_ast_bad_type():
|
2013-03-06 04:15:45 +01:00
|
|
|
"Make sure AST breakage can happen"
|
2017-06-27 23:09:31 +02:00
|
|
|
class C:
|
|
|
|
pass
|
2018-10-13 06:25:43 +02:00
|
|
|
|
|
|
|
with pytest.raises(TypeError):
|
|
|
|
hy_compile(C(), __name__, filename='<string>', source='')
|
2013-03-03 19:12:23 +01:00
|
|
|
|
|
|
|
|
2018-07-24 17:59:52 +02:00
|
|
|
def test_empty_expr():
|
|
|
|
"Empty expressions should be illegal at the top level."
|
|
|
|
cant_compile("(print ())")
|
|
|
|
can_compile("(print '())")
|
|
|
|
|
|
|
|
|
2018-07-24 18:45:00 +02:00
|
|
|
def test_dot_unpacking():
|
|
|
|
|
|
|
|
can_compile("(.meth obj #* args az)")
|
|
|
|
cant_compile("(.meth #* args az)")
|
|
|
|
cant_compile("(. foo #* bar baz)")
|
|
|
|
|
|
|
|
can_compile("(.meth obj #** args az)")
|
|
|
|
can_compile("(.meth #** args obj)")
|
|
|
|
cant_compile("(. foo #** bar baz)")
|
|
|
|
|
|
|
|
|
2013-04-06 21:28:12 +02:00
|
|
|
def test_ast_bad_if():
|
2015-10-14 03:38:15 +02:00
|
|
|
"Make sure AST can't compile invalid if*"
|
|
|
|
cant_compile("(if*)")
|
|
|
|
cant_compile("(if* foobar)")
|
|
|
|
cant_compile("(if* 1 2 3 4 5)")
|
2013-04-05 17:37:55 +02:00
|
|
|
|
|
|
|
|
2013-04-03 11:25:17 +02:00
|
|
|
def test_ast_valid_if():
|
2015-10-14 03:38:15 +02:00
|
|
|
"Make sure AST can compile valid if*"
|
|
|
|
can_compile("(if* foo bar)")
|
2013-04-03 11:25:17 +02:00
|
|
|
|
|
|
|
|
2013-04-06 10:37:21 +02:00
|
|
|
def test_ast_valid_unary_op():
|
|
|
|
"Make sure AST can compile valid unary operator"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(not 2)")
|
|
|
|
can_compile("(~ 1)")
|
2013-04-06 10:37:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_invalid_unary_op():
|
|
|
|
"Make sure AST can't compile invalid unary operator"
|
|
|
|
cant_compile("(not 2 3 4)")
|
|
|
|
cant_compile("(not)")
|
|
|
|
cant_compile("(not 2 3 4)")
|
|
|
|
cant_compile("(~ 2 2 3 4)")
|
|
|
|
cant_compile("(~)")
|
|
|
|
|
|
|
|
|
2013-04-06 21:28:12 +02:00
|
|
|
def test_ast_bad_while():
|
2013-04-03 19:55:09 +02:00
|
|
|
"Make sure AST can't compile invalid while"
|
2013-04-06 21:28:12 +02:00
|
|
|
cant_compile("(while)")
|
2013-04-03 19:55:09 +02:00
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def test_ast_good_do():
|
|
|
|
"Make sure AST can compile valid do"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(do)")
|
|
|
|
can_compile("(do 1)")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
2013-04-07 18:24:01 +02:00
|
|
|
def test_ast_good_raise():
|
|
|
|
"Make sure AST can compile valid raise"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(raise)")
|
2014-05-01 22:31:45 +02:00
|
|
|
can_compile("(raise Exception)")
|
|
|
|
can_compile("(raise e)")
|
|
|
|
|
|
|
|
|
2019-05-20 20:34:00 +02:00
|
|
|
def test_ast_raise_from():
|
|
|
|
can_compile("(raise Exception :from NameError)")
|
2013-04-07 18:24:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_raise():
|
|
|
|
"Make sure AST can't compile invalid raise"
|
2014-05-01 22:31:45 +02:00
|
|
|
cant_compile("(raise Exception Exception)")
|
2013-04-07 18:24:01 +02:00
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def test_ast_good_try():
|
|
|
|
"Make sure AST can compile valid try"
|
2018-04-17 04:34:50 +02:00
|
|
|
can_compile("(try 1 (except []) (else 1))")
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(try 1 (finally 1))")
|
2018-04-17 04:34:50 +02:00
|
|
|
can_compile("(try 1 (except []) (finally 1))")
|
2017-05-14 00:52:59 +02:00
|
|
|
can_compile("(try 1 (except [x]) (except [y]) (finally 1))")
|
2018-04-17 04:34:50 +02:00
|
|
|
can_compile("(try 1 (except []) (else 1) (finally 1))")
|
2017-05-14 00:52:59 +02:00
|
|
|
can_compile("(try 1 (except [x]) (except [y]) (else 1) (finally 1))")
|
2013-04-08 15:58:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_try():
|
|
|
|
"Make sure AST can't compile invalid try"
|
2017-05-26 03:43:31 +02:00
|
|
|
cant_compile("(try)")
|
|
|
|
cant_compile("(try 1)")
|
2013-04-08 15:58:43 +02:00
|
|
|
cant_compile("(try 1 bla)")
|
|
|
|
cant_compile("(try 1 bla bla)")
|
2017-05-26 03:43:31 +02:00
|
|
|
cant_compile("(try (do bla bla))")
|
2013-04-08 15:58:43 +02:00
|
|
|
cant_compile("(try (do) (else 1) (else 2))")
|
|
|
|
cant_compile("(try 1 (else 1))")
|
2018-04-17 04:34:50 +02:00
|
|
|
cant_compile("(try 1 (else 1) (except []))")
|
|
|
|
cant_compile("(try 1 (finally 1) (except []))")
|
|
|
|
cant_compile("(try 1 (except []) (finally 1) (else 1))")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
2013-04-07 18:24:01 +02:00
|
|
|
def test_ast_good_except():
|
|
|
|
"Make sure AST can compile valid except"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(try 1 (except []))")
|
|
|
|
can_compile("(try 1 (except [Foobar]))")
|
|
|
|
can_compile("(try 1 (except [[]]))")
|
|
|
|
can_compile("(try 1 (except [x FooBar]))")
|
|
|
|
can_compile("(try 1 (except [x [FooBar BarFoo]]))")
|
|
|
|
can_compile("(try 1 (except [x [FooBar BarFoo]]))")
|
2013-04-07 18:24:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_except():
|
|
|
|
"Make sure AST can't compile invalid except"
|
2013-05-09 02:00:09 +02:00
|
|
|
cant_compile("(except 1)")
|
2018-04-17 04:34:50 +02:00
|
|
|
cant_compile("(try 1 (except))")
|
2013-05-09 01:58:36 +02:00
|
|
|
cant_compile("(try 1 (except 1))")
|
|
|
|
cant_compile("(try 1 (except [1 3]))")
|
2018-04-17 04:34:50 +02:00
|
|
|
cant_compile("(try 1 (except [(f) [IOError ValueError]]))")
|
2013-05-09 01:58:36 +02:00
|
|
|
cant_compile("(try 1 (except [x [FooBar] BarBar]))")
|
2013-04-07 18:24:01 +02:00
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def test_ast_good_assert():
|
2015-02-22 17:34:19 +01:00
|
|
|
"""Make sure AST can compile valid asserts. Asserts may or may not
|
|
|
|
include a label."""
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(assert 1)")
|
2015-02-22 17:34:19 +01:00
|
|
|
can_compile("(assert 1 \"Assert label\")")
|
|
|
|
can_compile("(assert 1 (+ \"spam \" \"eggs\"))")
|
|
|
|
can_compile("(assert 1 12345)")
|
2016-11-24 03:35:17 +01:00
|
|
|
can_compile("(assert 1 None)")
|
2015-02-22 17:34:19 +01:00
|
|
|
can_compile("(assert 1 (+ 2 \"incoming eggsception\"))")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_assert():
|
|
|
|
"Make sure AST can't compile invalid assert"
|
|
|
|
cant_compile("(assert)")
|
2015-02-22 17:34:19 +01:00
|
|
|
cant_compile("(assert 1 2 3)")
|
|
|
|
cant_compile("(assert 1 [1 2] 3)")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
2013-04-24 01:25:02 +02:00
|
|
|
|
2013-04-19 08:40:03 +02:00
|
|
|
def test_ast_good_global():
|
|
|
|
"Make sure AST can compile valid global"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(global a)")
|
2014-12-14 21:13:44 +01:00
|
|
|
can_compile("(global foo bar)")
|
2013-04-19 08:40:03 +02:00
|
|
|
|
2013-04-24 01:25:02 +02:00
|
|
|
|
2013-04-19 08:40:03 +02:00
|
|
|
def test_ast_bad_global():
|
|
|
|
"Make sure AST can't compile invalid global"
|
|
|
|
cant_compile("(global)")
|
2014-12-14 21:13:44 +01:00
|
|
|
cant_compile("(global (foo))")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
2013-04-24 01:25:02 +02:00
|
|
|
|
2019-05-20 20:34:00 +02:00
|
|
|
def test_ast_good_nonlocal():
|
|
|
|
"Make sure AST can compile valid nonlocal"
|
|
|
|
can_compile("(nonlocal a)")
|
|
|
|
can_compile("(nonlocal foo bar)")
|
2014-12-14 21:14:19 +01:00
|
|
|
|
2019-05-20 20:34:00 +02:00
|
|
|
|
|
|
|
def test_ast_bad_nonlocal():
|
|
|
|
"Make sure AST can't compile invalid nonlocal"
|
|
|
|
cant_compile("(nonlocal)")
|
|
|
|
cant_compile("(nonlocal (foo))")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
2013-04-24 01:25:02 +02:00
|
|
|
|
2013-04-24 22:18:05 +02:00
|
|
|
def test_ast_good_defclass():
|
|
|
|
"Make sure AST can compile valid defclass"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(defclass a)")
|
|
|
|
can_compile("(defclass a [])")
|
2018-03-15 03:21:14 +01:00
|
|
|
can_compile("(defclass a [] None 42)")
|
|
|
|
can_compile("(defclass a [] None \"test\")")
|
|
|
|
can_compile("(defclass a [] None (print \"foo\"))")
|
2013-04-24 22:18:05 +02:00
|
|
|
|
|
|
|
|
2018-02-12 00:26:29 +01:00
|
|
|
def test_ast_good_defclass_with_metaclass():
|
|
|
|
"Make sure AST can compile valid defclass with keywords"
|
|
|
|
can_compile("(defclass a [:metaclass b])")
|
|
|
|
can_compile("(defclass a [:b c])")
|
|
|
|
|
|
|
|
|
2013-04-24 22:18:05 +02:00
|
|
|
def test_ast_bad_defclass():
|
|
|
|
"Make sure AST can't compile invalid defclass"
|
|
|
|
cant_compile("(defclass)")
|
2015-08-12 00:22:13 +02:00
|
|
|
cant_compile("(defclass a None)")
|
|
|
|
cant_compile("(defclass a None None)")
|
2013-04-24 22:18:05 +02:00
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def test_ast_good_lambda():
|
|
|
|
"Make sure AST can compile valid lambda"
|
2017-02-23 00:36:52 +01:00
|
|
|
can_compile("(fn [])")
|
|
|
|
can_compile("(fn [] 1)")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_lambda():
|
|
|
|
"Make sure AST can't compile invalid lambda"
|
2017-02-23 00:36:52 +01:00
|
|
|
cant_compile("(fn)")
|
2018-04-15 05:58:52 +02:00
|
|
|
cant_compile("(fn ())")
|
|
|
|
cant_compile("(fn () 1)")
|
|
|
|
cant_compile("(fn (x) 1)")
|
2018-04-21 21:49:38 +02:00
|
|
|
cant_compile('(fn "foo")')
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_good_yield():
|
|
|
|
"Make sure AST can compile valid yield"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(yield 1)")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_yield():
|
|
|
|
"Make sure AST can't compile invalid yield"
|
|
|
|
cant_compile("(yield 1 2)")
|
|
|
|
|
|
|
|
|
2018-05-20 23:55:23 +02:00
|
|
|
def test_ast_import_mangle_dotted():
|
|
|
|
"""Mangling a module name with a period shouldn't create a spurious
|
|
|
|
`asname`."""
|
|
|
|
code = can_compile("(import a-b.c)")
|
|
|
|
assert code.body[0].names[0].name == "a_b.c"
|
|
|
|
assert code.body[0].names[0].asname is None
|
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def test_ast_good_import_from():
|
2013-04-20 16:06:32 +02:00
|
|
|
"Make sure AST can compile valid selective import"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(import [x [y]])")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
Give `require` the same features as `import` (#1142)
Give `require` the same features as `import`
You can now do (require foo), (require [foo [a b c]]), (require [foo [*]]), and (require [foo :as bar]). The first and last forms get you macros named foo.a, foo.b, etc. or bar.a, bar.b, etc., respectively. The second form only gets the macros in the list.
Implements #1118 and perhaps partly addresses #277.
N.B. The new meaning of (require foo) will cause all existing code that uses macros to break. Simply replace these forms with (require [foo [*]]) to get your code working again.
There's a bit of a hack involved in the forms (require foo) or (require [foo :as bar]). When you call (foo.a ...) or (bar.a ...), Hy doesn't actually look inside modules. Instead, these (require ...) forms give the macros names that have periods in them, which happens to work fine with the way Hy finds and interprets macro calls.
* Make `require` syntax stricter and add tests
* Update documentation for `require`
* Documentation wording improvements
* Allow :as in `require` name lists
2016-11-03 08:35:58 +01:00
|
|
|
def test_ast_require():
|
|
|
|
"Make sure AST respects (require) syntax"
|
|
|
|
can_compile("(require tests.resources.tlib)")
|
|
|
|
can_compile("(require [tests.resources.tlib [qplah parald]])")
|
|
|
|
can_compile("(require [tests.resources.tlib [*]])")
|
|
|
|
can_compile("(require [tests.resources.tlib :as foobar])")
|
|
|
|
can_compile("(require [tests.resources.tlib [qplah :as quiz]])")
|
|
|
|
can_compile("(require [tests.resources.tlib [qplah :as quiz parald]])")
|
|
|
|
cant_compile("(require [tests.resources.tlib])")
|
|
|
|
cant_compile("(require [tests.resources.tlib [* qplah]])")
|
|
|
|
cant_compile("(require [tests.resources.tlib [qplah *]])")
|
|
|
|
cant_compile("(require [tests.resources.tlib [* *]])")
|
|
|
|
|
|
|
|
|
2018-06-13 18:04:52 +02:00
|
|
|
def test_ast_import_require_dotted():
|
2018-07-24 18:37:19 +02:00
|
|
|
"""As in Python, it should be a compile-time error to attempt to
|
2018-06-13 18:04:52 +02:00
|
|
|
import a dotted name."""
|
|
|
|
cant_compile("(import [spam [foo.bar]])")
|
|
|
|
cant_compile("(require [spam [foo.bar]])")
|
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def test_ast_good_get():
|
|
|
|
"Make sure AST can compile valid get"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(get x y)")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_get():
|
|
|
|
"Make sure AST can't compile invalid get"
|
|
|
|
cant_compile("(get)")
|
|
|
|
cant_compile("(get 1)")
|
|
|
|
|
|
|
|
|
2014-09-05 05:29:57 +02:00
|
|
|
def test_ast_good_cut():
|
|
|
|
"Make sure AST can compile valid cut"
|
|
|
|
can_compile("(cut x)")
|
|
|
|
can_compile("(cut x y)")
|
|
|
|
can_compile("(cut x y z)")
|
|
|
|
can_compile("(cut x y z t)")
|
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_cut():
|
|
|
|
"Make sure AST can't compile invalid cut"
|
|
|
|
cant_compile("(cut)")
|
|
|
|
cant_compile("(cut 1 2 3 4 5)")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
2013-04-21 22:41:20 +02:00
|
|
|
def test_ast_good_take():
|
|
|
|
"Make sure AST can compile valid 'take'"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(take 1 [2 3])")
|
2013-04-21 22:41:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_good_drop():
|
|
|
|
"Make sure AST can compile valid 'drop'"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(drop 1 [2 3])")
|
2013-04-21 22:41:20 +02:00
|
|
|
|
|
|
|
|
2013-04-06 16:33:06 +02:00
|
|
|
def test_ast_good_assoc():
|
|
|
|
"Make sure AST can compile valid assoc"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(assoc x y z)")
|
2013-04-06 16:33:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_assoc():
|
|
|
|
"Make sure AST can't compile invalid assoc"
|
|
|
|
cant_compile("(assoc)")
|
|
|
|
cant_compile("(assoc 1)")
|
|
|
|
cant_compile("(assoc 1 2)")
|
|
|
|
cant_compile("(assoc 1 2 3 4)")
|
|
|
|
|
|
|
|
|
2013-04-08 00:35:36 +02:00
|
|
|
def test_ast_bad_with():
|
|
|
|
"Make sure AST can't compile invalid with"
|
2013-11-10 19:00:01 +01:00
|
|
|
cant_compile("(with*)")
|
|
|
|
cant_compile("(with* [])")
|
|
|
|
cant_compile("(with* [] (pass))")
|
2013-04-08 00:35:36 +02:00
|
|
|
|
|
|
|
|
2013-04-03 19:55:09 +02:00
|
|
|
def test_ast_valid_while():
|
|
|
|
"Make sure AST can't compile invalid while"
|
2013-05-16 15:30:44 +02:00
|
|
|
can_compile("(while foo bar)")
|
2017-09-13 23:31:28 +02:00
|
|
|
can_compile("(while foo bar (else baz))")
|
2013-04-03 19:55:09 +02:00
|
|
|
|
|
|
|
|
2013-11-10 19:00:01 +01:00
|
|
|
def test_ast_valid_for():
|
|
|
|
"Make sure AST can compile valid for"
|
2013-12-31 19:35:31 +01:00
|
|
|
can_compile("(for [a 2] (print a))")
|
2013-04-14 20:22:38 +02:00
|
|
|
|
|
|
|
|
2017-08-26 20:37:15 +02:00
|
|
|
def test_nullary_break_continue():
|
|
|
|
can_compile("(while 1 (break))")
|
|
|
|
cant_compile("(while 1 (break 1))")
|
|
|
|
can_compile("(while 1 (continue))")
|
|
|
|
cant_compile("(while 1 (continue 1))")
|
|
|
|
|
|
|
|
|
2013-03-03 06:00:55 +01:00
|
|
|
def test_ast_expression_basics():
|
2013-03-03 17:18:13 +01:00
|
|
|
""" Ensure basic AST expression conversion works. """
|
2013-05-16 15:30:44 +02:00
|
|
|
code = can_compile("(foo bar)").body[0]
|
2013-03-05 01:12:57 +01:00
|
|
|
tree = ast.Expr(value=ast.Call(
|
2013-04-06 21:22:35 +02:00
|
|
|
func=ast.Name(
|
|
|
|
id="foo",
|
|
|
|
ctx=ast.Load(),
|
|
|
|
),
|
|
|
|
args=[
|
|
|
|
ast.Name(id="bar", ctx=ast.Load())
|
|
|
|
],
|
|
|
|
keywords=[],
|
|
|
|
starargs=None,
|
|
|
|
kwargs=None,
|
|
|
|
))
|
2013-03-05 01:12:57 +01:00
|
|
|
|
|
|
|
_ast_spotcheck("value.func.id", code, tree)
|
2013-03-05 15:08:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_anon_fns_basics():
|
|
|
|
""" Ensure anon fns work. """
|
2018-04-15 05:58:52 +02:00
|
|
|
code = can_compile("(fn [x] (* x x))").body[0].value
|
2017-02-23 00:36:52 +01:00
|
|
|
assert type(code) == ast.Lambda
|
2018-04-15 05:58:52 +02:00
|
|
|
code = can_compile("(fn [x] (print \"multiform\") (* x x))").body[0]
|
2013-03-05 15:08:13 +01:00
|
|
|
assert type(code) == ast.FunctionDef
|
2018-04-15 05:58:52 +02:00
|
|
|
can_compile("(fn [x])")
|
2013-04-20 03:31:32 +02:00
|
|
|
cant_compile("(fn)")
|
2013-04-02 01:38:58 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_non_decoratable():
|
|
|
|
""" Ensure decorating garbage breaks """
|
2013-04-28 17:14:22 +02:00
|
|
|
cant_compile("(with-decorator (foo) (* x x))")
|
2013-04-02 02:00:37 +02:00
|
|
|
|
|
|
|
|
2013-05-08 21:10:30 +02:00
|
|
|
def test_ast_lambda_lists():
|
|
|
|
"""Ensure the compiler chokes on invalid lambda-lists"""
|
2013-05-08 20:56:16 +02:00
|
|
|
cant_compile('(fn [&optional [a b c]] a)')
|
2016-04-11 17:38:13 +02:00
|
|
|
cant_compile('(fn [&optional [1 2]] (list 1 2))')
|
2013-05-08 21:10:30 +02:00
|
|
|
|
|
|
|
|
2013-04-02 01:51:21 +02:00
|
|
|
def test_ast_print():
|
2013-05-16 15:30:44 +02:00
|
|
|
code = can_compile("(print \"foo\")").body[0]
|
2013-04-02 04:07:05 +02:00
|
|
|
|
2013-09-22 15:31:15 +02:00
|
|
|
assert type(code.value) == ast.Call
|
2013-04-02 01:53:04 +02:00
|
|
|
|
|
|
|
|
2013-04-02 04:07:05 +02:00
|
|
|
def test_ast_tuple():
|
|
|
|
""" Ensure tuples work. """
|
2013-05-16 15:30:44 +02:00
|
|
|
code = can_compile("(, 1 2 3)").body[0].value
|
2013-04-02 01:53:04 +02:00
|
|
|
assert type(code) == ast.Tuple
|
2013-04-12 20:58:20 +02:00
|
|
|
|
|
|
|
|
2013-04-19 04:27:38 +02:00
|
|
|
def test_lambda_list_keywords_rest():
|
2013-04-11 18:00:27 +02:00
|
|
|
""" Ensure we can compile functions with lambda list keywords."""
|
2018-04-15 05:58:52 +02:00
|
|
|
can_compile("(fn [x &rest xs] (print xs))")
|
|
|
|
cant_compile("(fn [x &rest xs &rest ys] (print xs))")
|
|
|
|
can_compile("(fn [&optional a &rest xs] (print xs))")
|
2013-04-18 21:17:30 +02:00
|
|
|
|
2013-04-21 18:29:09 +02:00
|
|
|
|
2013-04-19 04:27:38 +02:00
|
|
|
def test_lambda_list_keywords_kwargs():
|
|
|
|
""" Ensure we can compile functions with &kwargs."""
|
2018-04-15 05:58:52 +02:00
|
|
|
can_compile("(fn [x &kwargs kw] (list x kw))")
|
|
|
|
cant_compile("(fn [x &kwargs xs &kwargs ys] (list x xs ys))")
|
|
|
|
can_compile("(fn [&optional x &kwargs kw] (list x kw))")
|
2013-04-19 04:27:38 +02:00
|
|
|
|
2013-04-21 18:29:09 +02:00
|
|
|
|
2015-03-21 21:43:28 +01:00
|
|
|
def test_lambda_list_keywords_kwonly():
|
2017-02-23 00:36:52 +01:00
|
|
|
kwonly_demo = "(fn [&kwonly a [b 2]] (print 1) (print a b))"
|
2019-05-20 20:34:00 +02:00
|
|
|
code = can_compile(kwonly_demo)
|
|
|
|
for i, kwonlyarg_name in enumerate(('a', 'b')):
|
|
|
|
assert kwonlyarg_name == code.body[0].args.kwonlyargs[i].arg
|
|
|
|
assert code.body[0].args.kw_defaults[0] is None
|
|
|
|
assert code.body[0].args.kw_defaults[1].n == 2
|
2015-03-21 21:43:28 +01:00
|
|
|
|
|
|
|
|
2013-04-19 04:27:38 +02:00
|
|
|
def test_lambda_list_keywords_mixed():
|
|
|
|
""" Ensure we can mix them up."""
|
2018-04-15 05:58:52 +02:00
|
|
|
can_compile("(fn [x &rest xs &kwargs kw] (list x xs kw))")
|
|
|
|
cant_compile("(fn [x &rest xs &fasfkey {bar \"baz\"}])")
|
2019-05-20 20:34:00 +02:00
|
|
|
can_compile("(fn [x &rest xs &kwonly kwoxs &kwargs kwxs]"
|
|
|
|
" (list x xs kwxs kwoxs))")
|
2013-04-21 18:29:09 +02:00
|
|
|
|
2013-04-18 23:47:08 +02:00
|
|
|
|
2015-06-22 20:18:04 +02:00
|
|
|
def test_missing_keyword_argument_value():
|
|
|
|
"""Ensure the compiler chokes on missing keyword argument values."""
|
2018-10-13 06:25:43 +02:00
|
|
|
with pytest.raises(HyLanguageError) as excinfo:
|
2015-06-22 20:18:04 +02:00
|
|
|
can_compile("((fn [x] x) :x)")
|
2018-10-13 06:25:43 +02:00
|
|
|
assert excinfo.value.msg == "Keyword argument :x needs a value."
|
2015-06-22 20:18:04 +02:00
|
|
|
|
|
|
|
|
2013-04-12 20:58:20 +02:00
|
|
|
def test_ast_unicode_strings():
|
|
|
|
"""Ensure we handle unicode strings correctly"""
|
|
|
|
|
|
|
|
def _compile_string(s):
|
|
|
|
hy_s = HyString(s)
|
|
|
|
|
2018-10-13 06:25:43 +02:00
|
|
|
code = hy_compile([hy_s], __name__, filename='<string>', source=s)
|
2018-03-13 21:29:41 +01:00
|
|
|
# We put hy_s in a list so it isn't interpreted as a docstring.
|
2013-04-12 20:58:20 +02:00
|
|
|
|
2018-03-13 21:29:41 +01:00
|
|
|
# code == ast.Module(body=[ast.Expr(value=ast.List(elts=[ast.Str(s=xxx)]))])
|
|
|
|
return code.body[0].value.elts[0].s
|
2013-04-12 20:58:20 +02:00
|
|
|
|
|
|
|
assert _compile_string("test") == "test"
|
|
|
|
assert _compile_string("\u03b1\u03b2") == "\u03b1\u03b2"
|
|
|
|
assert _compile_string("\xc3\xa9") == "\xc3\xa9"
|
2013-04-24 22:34:14 +02:00
|
|
|
|
|
|
|
|
2017-02-19 01:15:58 +01:00
|
|
|
def test_ast_unicode_vs_bytes():
|
2019-05-20 20:34:00 +02:00
|
|
|
assert s('"hello"') == "hello"
|
|
|
|
assert type(s('"hello"')) is str
|
|
|
|
assert s('b"hello"') == b"hello"
|
|
|
|
assert type(s('b"hello"')) is bytes
|
|
|
|
assert s('b"\\xa0"') == bytes([160])
|
2017-09-08 20:22:31 +02:00
|
|
|
|
|
|
|
|
2019-02-26 20:04:24 +01:00
|
|
|
@pytest.mark.skipif(not PY36, reason='f-strings require Python 3.6+')
|
|
|
|
def test_format_string():
|
|
|
|
assert can_compile('f"hello world"')
|
|
|
|
assert can_compile('f"hello {(+ 1 1)} world"')
|
|
|
|
assert can_compile('f"hello world {(+ 1 1)}"')
|
|
|
|
assert cant_compile('f"hello {(+ 1 1) world"')
|
|
|
|
assert cant_compile('f"hello (+ 1 1)} world"')
|
|
|
|
assert cant_compile('f"hello {(+ 1 1} world"')
|
|
|
|
assert can_compile(r'f"hello {\"n\"} world"')
|
|
|
|
assert can_compile(r'f"hello {\"\\n\"} world"')
|
|
|
|
|
|
|
|
|
2017-09-08 20:22:31 +02:00
|
|
|
def test_ast_bracket_string():
|
|
|
|
assert s(r'#[[empty delims]]') == 'empty delims'
|
|
|
|
assert s(r'#[my delim[fizzle]my delim]') == 'fizzle'
|
|
|
|
assert s(r'#[[]]') == ''
|
|
|
|
assert s(r'#[my delim[]my delim]') == ''
|
2019-05-20 20:34:00 +02:00
|
|
|
assert type(s('#[X[hello]X]')) is str
|
2017-09-08 20:22:31 +02:00
|
|
|
assert s(r'#[X[raw\nstring]X]') == 'raw\\nstring'
|
|
|
|
assert s(r'#[foozle[aa foozli bb ]foozle]') == 'aa foozli bb '
|
|
|
|
assert s(r'#[([unbalanced](]') == 'unbalanced'
|
|
|
|
assert s(r'#[(1💯@)} {a![hello world](1💯@)} {a!]') == 'hello world'
|
|
|
|
assert (s(r'''#[X[
|
|
|
|
Remove the leading newline, please.
|
|
|
|
]X]''') == 'Remove the leading newline, please.\n')
|
|
|
|
assert (s(r'''#[X[
|
|
|
|
|
|
|
|
|
|
|
|
Only one leading newline should be removed.
|
|
|
|
]X]''') == '\n\nOnly one leading newline should be removed.\n')
|
2017-02-19 01:15:58 +01:00
|
|
|
|
|
|
|
|
2013-04-24 22:34:14 +02:00
|
|
|
def test_compile_error():
|
|
|
|
"""Ensure we get compile error in tricky cases"""
|
2018-10-13 06:25:43 +02:00
|
|
|
with pytest.raises(HyLanguageError) as excinfo:
|
2015-10-03 11:01:48 +02:00
|
|
|
can_compile("(fn [] (in [1 2 3]))")
|
2013-12-26 17:36:45 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_for_compile_error():
|
|
|
|
"""Ensure we get compile error in tricky 'for' cases"""
|
2018-10-29 02:43:17 +01:00
|
|
|
with pytest.raises(PrematureEndOfInput) as excinfo:
|
2013-12-26 17:36:45 +01:00
|
|
|
can_compile("(fn [] (for)")
|
2018-10-13 06:25:43 +02:00
|
|
|
assert excinfo.value.msg == "Premature end of input"
|
2013-12-26 17:36:45 +01:00
|
|
|
|
2018-03-12 06:44:57 +01:00
|
|
|
with pytest.raises(LexException) as excinfo:
|
2013-12-26 17:36:45 +01:00
|
|
|
can_compile("(fn [] (for)))")
|
2018-10-13 06:25:43 +02:00
|
|
|
assert excinfo.value.msg == "Ran into a RPAREN where it wasn't expected."
|
2013-12-26 17:36:45 +01:00
|
|
|
|
2018-06-12 19:54:08 +02:00
|
|
|
cant_compile("(fn [] (for [x] x))")
|
2013-12-26 17:36:45 +01:00
|
|
|
|
2014-01-09 03:34:29 +01:00
|
|
|
|
|
|
|
def test_attribute_access():
|
|
|
|
"""Ensure attribute access compiles correctly"""
|
|
|
|
can_compile("(. foo bar baz)")
|
|
|
|
can_compile("(. foo [bar] baz)")
|
|
|
|
can_compile("(. foo bar [baz] [0] quux [frob])")
|
|
|
|
can_compile("(. foo bar [(+ 1 2 3 4)] quux [frob])")
|
|
|
|
cant_compile("(. foo bar :baz [0] quux [frob])")
|
|
|
|
cant_compile("(. foo bar baz (0) quux [frob])")
|
|
|
|
cant_compile("(. foo bar baz [0] quux {frob})")
|
2013-05-16 18:59:20 +02:00
|
|
|
|
|
|
|
|
2016-11-30 06:00:48 +01:00
|
|
|
def test_attribute_empty():
|
|
|
|
"""Ensure using dot notation with a non-expression is an error"""
|
|
|
|
cant_compile(".")
|
|
|
|
cant_compile("foo.")
|
|
|
|
cant_compile(".foo")
|
|
|
|
cant_compile('"bar".foo')
|
|
|
|
cant_compile('[2].foo')
|
|
|
|
|
|
|
|
|
2015-08-10 14:19:19 +02:00
|
|
|
def test_bad_setv():
|
|
|
|
"""Ensure setv handles error cases"""
|
|
|
|
cant_compile("(setv (a b) [1 2])")
|
2015-08-12 05:55:27 +02:00
|
|
|
|
|
|
|
|
2015-08-10 13:44:11 +02:00
|
|
|
def test_defn():
|
|
|
|
"""Ensure that defn works correctly in various corner cases"""
|
|
|
|
cant_compile("(defn \"hy\" [] 1)")
|
|
|
|
cant_compile("(defn :hy [] 1)")
|
|
|
|
can_compile("(defn &hy [] 1)")
|
2018-04-21 21:49:38 +02:00
|
|
|
cant_compile('(defn hy "foo")')
|
2015-08-11 13:55:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_setv_builtins():
|
2015-08-11 14:03:09 +02:00
|
|
|
"""Ensure that assigning to a builtin fails, unless in a class"""
|
2016-11-24 03:35:17 +01:00
|
|
|
cant_compile("(setv None 42)")
|
2015-08-11 14:03:09 +02:00
|
|
|
can_compile("(defclass A [] (defn get [self] 42))")
|
|
|
|
can_compile("""
|
|
|
|
(defclass A []
|
|
|
|
(defn get [self] 42)
|
|
|
|
(defclass B []
|
|
|
|
(defn get [self] 42))
|
2015-10-14 03:38:15 +02:00
|
|
|
(defn if* [self] 0))
|
2015-08-11 14:03:09 +02:00
|
|
|
""")
|
2017-07-12 23:07:42 +02:00
|
|
|
|
|
|
|
|
2018-04-08 03:58:59 +02:00
|
|
|
def test_top_level_unquote():
|
2018-10-13 06:25:43 +02:00
|
|
|
with pytest.raises(HyLanguageError) as excinfo:
|
2018-04-08 03:58:59 +02:00
|
|
|
can_compile("(unquote)")
|
2018-10-13 06:25:43 +02:00
|
|
|
assert excinfo.value.msg == "The special form 'unquote' is not allowed here"
|
2018-04-08 03:58:59 +02:00
|
|
|
|
2018-10-13 06:25:43 +02:00
|
|
|
with pytest.raises(HyLanguageError) as excinfo:
|
2018-04-08 03:58:59 +02:00
|
|
|
can_compile("(unquote-splice)")
|
2018-10-13 06:25:43 +02:00
|
|
|
assert excinfo.value.msg == "The special form 'unquote-splice' is not allowed here"
|
2018-04-08 03:58:59 +02:00
|
|
|
|
|
|
|
|
2017-07-12 23:07:42 +02:00
|
|
|
def test_lots_of_comment_lines():
|
|
|
|
# https://github.com/hylang/hy/issues/1313
|
|
|
|
can_compile(1000 * ";\n")
|
2017-07-25 19:28:42 +02:00
|
|
|
|
|
|
|
|
2017-08-07 22:43:52 +02:00
|
|
|
def test_compiler_macro_tag_try():
|
|
|
|
"""Check that try forms within defmacro/deftag are compiled correctly"""
|
|
|
|
# https://github.com/hylang/hy/issues/1350
|
|
|
|
can_compile("(defmacro foo [] (try None (except [] None)) `())")
|
|
|
|
can_compile("(deftag foo [] (try None (except [] None)) `())")
|
2017-12-30 20:17:26 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_ast_good_yield_from():
|
|
|
|
"Make sure AST can compile valid yield-from"
|
|
|
|
can_compile("(yield-from [1 2])")
|
|
|
|
|
|
|
|
|
|
|
|
def test_ast_bad_yield_from():
|
|
|
|
"Make sure AST can't compile invalid yield-from"
|
|
|
|
cant_compile("(yield-from)")
|
2018-02-08 21:08:09 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_eval_generator_with_return():
|
|
|
|
"""Ensure generators with a return statement works."""
|
|
|
|
can_eval("(fn [] (yield 1) (yield 2) (return))")
|
2018-08-28 01:30:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_futures_imports():
|
|
|
|
"""Make sure __future__ imports go first, especially when builtins are
|
|
|
|
automatically added (e.g. via use of a builtin name like `name`)."""
|
|
|
|
hy_ast = can_compile((
|
|
|
|
'(import [__future__ [print_function]])\n'
|
|
|
|
'(import sys)\n'
|
|
|
|
'(setv name [1 2])'
|
|
|
|
'(print (first name))'))
|
|
|
|
|
|
|
|
assert hy_ast.body[0].module == '__future__'
|
|
|
|
assert hy_ast.body[1].module == 'hy.core.language'
|
|
|
|
|
|
|
|
hy_ast = can_compile((
|
|
|
|
'(import sys)\n'
|
|
|
|
'(import [__future__ [print_function]])\n'
|
|
|
|
'(setv name [1 2])'
|
|
|
|
'(print (first name))'))
|
|
|
|
|
|
|
|
assert hy_ast.body[0].module == '__future__'
|
|
|
|
assert hy_ast.body[1].module == 'hy.core.language'
|