2017-09-08 20:22:31 +02:00
|
|
|
# -*- encoding: utf-8 -*-
|
2018-01-01 16:38:33 +01:00
|
|
|
# Copyright 2018 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
|
Much better version of new error messages.
This version is much simpler.
At the point that the exception is raised, we don't have access to
the actual source, just the current expression. but as the
exception percolates up, we can intercept it, add the source and
the re-raise it.
Then at the final point, in the cmdline handler, we can choose to
let the entire traceback print, or just the simpler, direct error
message.
And even with the full traceback, the last bit is nicely formatted
just like the shorter, simpler message.
The error message is colored if clint is installed, but to avoid
yet another dependency, you get monochrome without clint.
I'm sure there is a better way to do the markup, the current method
is kludgy but works.
I wish there was more shared code between HyTypeError and LexException
but they are kind of different in some fundamental ways.
This doesn't work (yet) with runtime errors generated from Python,
like NameError, but I have a method that can catch NameError and turn it
into a more pleasing output.
Finally, there is no obvious way to raise HyTypeError from pure Hy code,
so methods in core/language.hy throw ugly TypeError/ValueError.
2013-12-22 20:56:03 +01:00
|
|
|
from hy.models import HyObject
|
2018-11-10 19:53:28 +01:00
|
|
|
from hy.compiler import hy_compile, hy_eval
|
2013-12-26 17:36:45 +01:00
|
|
|
from hy.errors import HyCompileError, HyTypeError
|
2018-11-10 19:53:28 +01:00
|
|
|
from hy.lex import hy_parse
|
2013-12-26 17:36:45 +01:00
|
|
|
from hy.lex.exceptions import LexException
|
2014-05-01 22:31:45 +02:00
|
|
|
from hy._compat import PY3
|
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-08-20 06:29:29 +02:00
|
|
|
return hy_compile(hy_parse(expr), "__main__")
|
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):
|
|
|
|
try:
|
2018-08-20 06:29:29 +02:00
|
|
|
hy_compile(hy_parse(expr), "__main__")
|
2013-04-06 10:37:21 +02:00
|
|
|
assert False
|
Much better version of new error messages.
This version is much simpler.
At the point that the exception is raised, we don't have access to
the actual source, just the current expression. but as the
exception percolates up, we can intercept it, add the source and
the re-raise it.
Then at the final point, in the cmdline handler, we can choose to
let the entire traceback print, or just the simpler, direct error
message.
And even with the full traceback, the last bit is nicely formatted
just like the shorter, simpler message.
The error message is colored if clint is installed, but to avoid
yet another dependency, you get monochrome without clint.
I'm sure there is a better way to do the markup, the current method
is kludgy but works.
I wish there was more shared code between HyTypeError and LexException
but they are kind of different in some fundamental ways.
This doesn't work (yet) with runtime errors generated from Python,
like NameError, but I have a method that can catch NameError and turn it
into a more pleasing output.
Finally, there is no obvious way to raise HyTypeError from pure Hy code,
so methods in core/language.hy throw ugly TypeError/ValueError.
2013-12-22 20:56:03 +01:00
|
|
|
except HyTypeError as e:
|
|
|
|
# Anything that can't be compiled should raise a user friendly
|
|
|
|
# error, otherwise it's a compiler bug.
|
|
|
|
assert isinstance(e.expression, HyObject)
|
|
|
|
assert e.message
|
2015-03-21 21:43:28 +01:00
|
|
|
return e
|
2013-05-13 18:09:05 +02:00
|
|
|
except HyCompileError as e:
|
|
|
|
# Anything that can't be compiled should raise a user friendly
|
|
|
|
# error, otherwise it's a compiler bug.
|
|
|
|
assert isinstance(e.exception, HyTypeError)
|
|
|
|
assert e.traceback
|
2015-03-21 21:43:28 +01:00
|
|
|
return e
|
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
|
2013-03-03 19:12:23 +01:00
|
|
|
try:
|
2017-06-27 23:09:31 +02:00
|
|
|
hy_compile(C(), "__main__")
|
2013-04-02 02:00:37 +02:00
|
|
|
assert True is False
|
2018-05-21 20:37:46 +02:00
|
|
|
except TypeError:
|
2013-03-03 19:12:23 +01:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
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)")
|
|
|
|
|
|
|
|
|
|
|
|
if PY3:
|
|
|
|
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
|
|
|
|
2014-12-14 21:14:19 +01:00
|
|
|
if PY3:
|
|
|
|
def test_ast_good_nonlocal():
|
|
|
|
"Make sure AST can compile valid nonlocal"
|
|
|
|
can_compile("(nonlocal a)")
|
|
|
|
can_compile("(nonlocal foo bar)")
|
|
|
|
|
|
|
|
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
|
|
|
@pytest.mark.skipif(not PY3, reason="Python 3 supports class keywords")
|
|
|
|
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]])")
|
|
|
|
|
|
|
|
|
2017-02-17 05:14:06 +01:00
|
|
|
def test_ast_no_pointless_imports():
|
|
|
|
def contains_import_from(code):
|
|
|
|
return any([isinstance(node, ast.ImportFrom)
|
2017-06-23 01:32:29 +02:00
|
|
|
for node in can_compile(code).body])
|
2017-02-17 05:14:06 +01:00
|
|
|
# `reduce` is a builtin in Python 2, but not Python 3.
|
|
|
|
# The version of `map` that returns an iterator is a builtin in
|
|
|
|
# Python 3, but not Python 2.
|
|
|
|
if PY3:
|
|
|
|
assert contains_import_from("reduce")
|
|
|
|
assert not contains_import_from("map")
|
|
|
|
else:
|
|
|
|
assert not contains_import_from("reduce")
|
|
|
|
assert contains_import_from("map")
|
|
|
|
|
|
|
|
|
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():
|
|
|
|
"""Ensure we can compile functions with &kwonly if we're on Python
|
|
|
|
3, or fail with an informative message on Python 2."""
|
2017-02-23 00:36:52 +01:00
|
|
|
kwonly_demo = "(fn [&kwonly a [b 2]] (print 1) (print a b))"
|
2015-03-21 21:43:28 +01:00
|
|
|
if PY3:
|
|
|
|
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
|
|
|
|
else:
|
|
|
|
exception = cant_compile(kwonly_demo)
|
|
|
|
assert isinstance(exception, HyTypeError)
|
|
|
|
message, = exception.args
|
2018-04-21 21:49:38 +02:00
|
|
|
assert message == "&kwonly parameters require Python 3"
|
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\"}])")
|
2015-03-21 21:43:28 +01:00
|
|
|
if PY3:
|
2018-04-21 21:49:38 +02:00
|
|
|
can_compile("(fn [x &rest xs &kwonly kwoxs &kwargs kwxs]"
|
2015-03-21 21:43:28 +01:00
|
|
|
" (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-03-12 06:44:57 +01:00
|
|
|
with pytest.raises(HyTypeError) as excinfo:
|
2015-06-22 20:18:04 +02:00
|
|
|
can_compile("((fn [x] x) :x)")
|
2018-03-12 06:44:57 +01:00
|
|
|
assert excinfo.value.message == "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-03-13 21:29:41 +01:00
|
|
|
code = hy_compile([hy_s], "__main__")
|
|
|
|
# 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():
|
2017-09-08 20:22:31 +02:00
|
|
|
assert s('"hello"') == u"hello"
|
|
|
|
assert type(s('"hello"')) is (str if PY3 else unicode) # noqa
|
|
|
|
assert s('b"hello"') == (eval('b"hello"') if PY3 else "hello")
|
|
|
|
assert type(s('b"hello"')) is (bytes if PY3 else str)
|
|
|
|
assert s('b"\\xa0"') == (bytes([160]) if PY3 else chr(160))
|
|
|
|
|
|
|
|
|
|
|
|
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]') == ''
|
|
|
|
assert type(s('#[X[hello]X]')) is (str if PY3 else unicode) # noqa
|
|
|
|
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-03-12 06:44:57 +01:00
|
|
|
with pytest.raises(HyTypeError) 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-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-03-12 06:44:57 +01:00
|
|
|
assert excinfo.value.message == "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-03-12 06:44:57 +01:00
|
|
|
assert excinfo.value.message == "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():
|
|
|
|
with pytest.raises(HyTypeError) as excinfo:
|
|
|
|
can_compile("(unquote)")
|
2018-04-21 22:39:49 +02:00
|
|
|
assert excinfo.value.message == "The special form 'unquote' is not allowed here"
|
2018-04-08 03:58:59 +02:00
|
|
|
|
|
|
|
with pytest.raises(HyTypeError) as excinfo:
|
|
|
|
can_compile("(unquote-splice)")
|
2018-04-21 22:39:49 +02:00
|
|
|
assert excinfo.value.message == "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
|
|
|
|
|
|
|
|
|
|
|
def test_exec_star():
|
|
|
|
|
|
|
|
code = can_compile('(exec* "print(5)")').body[0]
|
|
|
|
assert type(code) == (ast.Expr if PY3 else ast.Exec)
|
|
|
|
if not PY3:
|
|
|
|
assert code.body.s == "print(5)"
|
|
|
|
assert code.globals is None
|
|
|
|
assert code.locals is None
|
|
|
|
|
|
|
|
code = can_compile('(exec* "print(a)" {"a" 3})').body[0]
|
|
|
|
assert type(code) == (ast.Expr if PY3 else ast.Exec)
|
|
|
|
if not PY3:
|
|
|
|
assert code.body.s == "print(a)"
|
|
|
|
assert code.globals.keys[0].s == "a"
|
|
|
|
assert code.locals is None
|
|
|
|
|
|
|
|
code = can_compile('(exec* "print(a + b)" {"a" "x"} {"b" "y"})').body[0]
|
|
|
|
assert type(code) == (ast.Expr if PY3 else ast.Exec)
|
|
|
|
if not PY3:
|
|
|
|
assert code.body.s == "print(a + b)"
|
|
|
|
assert code.globals.keys[0].s == "a"
|
|
|
|
assert code.locals.keys[0].s == "b"
|
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
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(not PY3, reason="Python 3 required")
|
|
|
|
def test_ast_good_yield_from():
|
|
|
|
"Make sure AST can compile valid yield-from"
|
|
|
|
can_compile("(yield-from [1 2])")
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(not PY3, reason="Python 3 required")
|
|
|
|
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'
|