forked from pool/python-synr
Accepting request 957922 from home:bnavigator:branches:devel:languages:python
required by update to tvm 0.8 OBS-URL: https://build.opensuse.org/request/show/957922 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-synr?expand=0&rev=1
This commit is contained in:
23
.gitattributes
vendored
Normal file
23
.gitattributes
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
## Default LFS
|
||||||
|
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bsp filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gem filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.jar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lzma filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.obscpio filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.oxt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.png filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.rpm filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tbz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.txz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.whl filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zst filter=lfs diff=lfs merge=lfs -text
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.osc
|
5
python-synr.changes
Normal file
5
python-synr.changes
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Mon Feb 28 00:28:12 UTC 2022 - Ben Greiner <code@bnavigator.de>
|
||||||
|
|
||||||
|
- Initial specfile for v0.6
|
||||||
|
- Required by tvm 0.8
|
62
python-synr.spec
Normal file
62
python-synr.spec
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#
|
||||||
|
# spec file for package python-synr
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022 SUSE LLC
|
||||||
|
#
|
||||||
|
# All modifications and additions to the file contributed by third parties
|
||||||
|
# remain the property of their copyright owners, unless otherwise agreed
|
||||||
|
# upon. The license for this file, and modifications and additions to the
|
||||||
|
# file, is the same license as for the pristine package itself (unless the
|
||||||
|
# license for the pristine package is not an Open Source License, in which
|
||||||
|
# case the license is the MIT License). An "Open Source License" is a
|
||||||
|
# license that conforms to the Open Source Definition (Version 1.9)
|
||||||
|
# published by the Open Source Initiative.
|
||||||
|
|
||||||
|
# Please submit bugfixes or comments via https://bugs.opensuse.org/
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
%{?!python_module:%define python_module() python3-%{**}}
|
||||||
|
%define skip_python2 1
|
||||||
|
Name: python-synr
|
||||||
|
Version: 0.6.0
|
||||||
|
Release: 0
|
||||||
|
Summary: A consistent AST for Python
|
||||||
|
License: Apache-2.0
|
||||||
|
URL: https://synr.readthedocs.io
|
||||||
|
Source0: https://files.pythonhosted.org/packages/source/s/synr/synr-%{version}.tar.gz
|
||||||
|
Source1: https://github.com/octoml/synr/raw/v%{version}/tests/test_synr.py
|
||||||
|
BuildRequires: python-rpm-macros
|
||||||
|
BuildRequires: %{python_module setuptools}
|
||||||
|
BuildRequires: %{python_module attrs}
|
||||||
|
BuildRequires: %{python_module pytest}
|
||||||
|
BuildRequires: fdupes
|
||||||
|
Requires: python-attrs
|
||||||
|
BuildArch: noarch
|
||||||
|
%python_subpackages
|
||||||
|
|
||||||
|
%description
|
||||||
|
A library for a stable Abstract Syntax Tree for Python.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%setup -q -n synr-%{version}
|
||||||
|
mkdir tests
|
||||||
|
cp %{SOURCE1} tests
|
||||||
|
|
||||||
|
%build
|
||||||
|
%python_build
|
||||||
|
|
||||||
|
%install
|
||||||
|
%python_install
|
||||||
|
%python_expand %fdupes %{buildroot}%{$python_sitelib}
|
||||||
|
|
||||||
|
%check
|
||||||
|
%pytest
|
||||||
|
|
||||||
|
%files %{python_files}
|
||||||
|
%doc README.md
|
||||||
|
%license LICENSE
|
||||||
|
%{python_sitelib}/synr
|
||||||
|
%{python_sitelib}/synr-%{version}*-info
|
||||||
|
|
||||||
|
%changelog
|
3
synr-0.6.0.tar.gz
Normal file
3
synr-0.6.0.tar.gz
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0b4e16b10c3988e1981e3372153a31956f74d86752eaaa55e8c4e7b7fe591e4e
|
||||||
|
size 17299
|
729
test_synr.py
Normal file
729
test_synr.py
Normal file
@@ -0,0 +1,729 @@
|
|||||||
|
import synr
|
||||||
|
from synr import __version__
|
||||||
|
from typing import Any
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def test_version():
|
||||||
|
assert __version__ == "0.6.0"
|
||||||
|
|
||||||
|
|
||||||
|
def to_ast(program: Any) -> Any:
|
||||||
|
diag_ctx = synr.PrinterDiagnosticContext()
|
||||||
|
transformer = None
|
||||||
|
res = synr.to_ast(program, diag_ctx, transformer)
|
||||||
|
if isinstance(res, str):
|
||||||
|
raise (RuntimeError(res))
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def assert_one_fn(module, name, no_params=None):
|
||||||
|
func = module.funcs.get(name)
|
||||||
|
assert func, "the function `%s` was not found" % name
|
||||||
|
if no_params:
|
||||||
|
assert len(func.params) == no_params, "the parameters do not match"
|
||||||
|
assert isinstance(func.body, synr.ast.Block)
|
||||||
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
def identity(x):
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def test_id_function():
|
||||||
|
module = to_ast(identity)
|
||||||
|
ast_fn = assert_one_fn(module, "identity", no_params=1)
|
||||||
|
return_var = ast_fn.body.stmts[-1].value
|
||||||
|
assert isinstance(return_var, synr.ast.Var)
|
||||||
|
assert return_var.id.name == "x"
|
||||||
|
|
||||||
|
|
||||||
|
class ExampleClass:
|
||||||
|
def func():
|
||||||
|
return 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_class():
|
||||||
|
module = to_ast(ExampleClass)
|
||||||
|
cls = module.funcs.get("ExampleClass")
|
||||||
|
assert cls, "ExampleClass not found"
|
||||||
|
assert isinstance(cls, synr.ast.Class), "ExampleClass was not parsed as a Class"
|
||||||
|
assert len(cls.funcs) == 1, "func not found"
|
||||||
|
fn = cls.funcs["func"]
|
||||||
|
assert isinstance(fn, synr.ast.Function), "func not found"
|
||||||
|
assert fn.name == "func", "func not found"
|
||||||
|
return_var = fn.body.stmts[-1].value
|
||||||
|
assert isinstance(return_var, synr.ast.Constant)
|
||||||
|
assert return_var.value == 3
|
||||||
|
|
||||||
|
|
||||||
|
def func_for():
|
||||||
|
for x in range(3):
|
||||||
|
return x
|
||||||
|
|
||||||
|
for x, y in grid(5, 6):
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def test_for():
|
||||||
|
module = to_ast(func_for)
|
||||||
|
fn = assert_one_fn(module, "func_for", no_params=0)
|
||||||
|
|
||||||
|
fr = fn.body.stmts[0]
|
||||||
|
assert isinstance(fr, synr.ast.For), "Did not find for loop"
|
||||||
|
assert fr.lhs[0].id.name == "x", "For lhs is incorrect"
|
||||||
|
assert isinstance(fr.rhs, synr.ast.Call)
|
||||||
|
assert fr.rhs.func_name.id.name == "range"
|
||||||
|
assert fr.rhs.params[0].value == 3
|
||||||
|
assert isinstance(fr.body.stmts[0], synr.ast.Return)
|
||||||
|
assert fr.body.stmts[0].value.id.name == "x"
|
||||||
|
|
||||||
|
fr = fn.body.stmts[1]
|
||||||
|
assert isinstance(fr, synr.ast.For), "Did not find for loop"
|
||||||
|
assert len(fr.lhs) == 2
|
||||||
|
assert fr.lhs[0].id.name == "x", "For lhs is incorrect"
|
||||||
|
assert fr.lhs[1].id.name == "y", "For lhs is incorrect"
|
||||||
|
assert isinstance(fr.rhs, synr.ast.Call)
|
||||||
|
assert fr.rhs.func_name.id.name == "grid"
|
||||||
|
assert fr.rhs.params[0].value == 5
|
||||||
|
assert fr.rhs.params[1].value == 6
|
||||||
|
assert isinstance(fr.body.stmts[0], synr.ast.Return)
|
||||||
|
assert fr.body.stmts[0].value.id.name == "x"
|
||||||
|
|
||||||
|
|
||||||
|
def func_while():
|
||||||
|
while x < 10:
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def test_while():
|
||||||
|
module = to_ast(func_while)
|
||||||
|
fn = assert_one_fn(module, "func_while", no_params=0)
|
||||||
|
|
||||||
|
while_stmt = fn.body.stmts[0]
|
||||||
|
assert isinstance(while_stmt, synr.ast.While)
|
||||||
|
assert isinstance(while_stmt.body.stmts[0], synr.ast.Return)
|
||||||
|
assert while_stmt.body.stmts[0].value.id.name == "x"
|
||||||
|
cond = while_stmt.condition
|
||||||
|
assert isinstance(cond, synr.ast.Call)
|
||||||
|
assert cond.func_name.name == synr.ast.BuiltinOp.LT
|
||||||
|
assert cond.params[0].id.name == "x"
|
||||||
|
assert cond.params[1].value == 10
|
||||||
|
|
||||||
|
|
||||||
|
def func_with():
|
||||||
|
with x as y:
|
||||||
|
return x
|
||||||
|
|
||||||
|
with block() as [x, y]:
|
||||||
|
return x
|
||||||
|
|
||||||
|
with block() as ():
|
||||||
|
return True
|
||||||
|
|
||||||
|
with block():
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def test_with():
|
||||||
|
module = to_ast(func_with)
|
||||||
|
fn = assert_one_fn(module, "func_with", no_params=0)
|
||||||
|
wth = fn.body.stmts[0]
|
||||||
|
assert isinstance(
|
||||||
|
wth, synr.ast.With
|
||||||
|
), "Did not find With statement, found %s" % type(wth)
|
||||||
|
assert wth.rhs.id.name == "x"
|
||||||
|
assert wth.lhs[0].id.name == "y"
|
||||||
|
assert isinstance(wth.body.stmts[0], synr.ast.Return)
|
||||||
|
assert wth.body.stmts[0].value.id.name == "x"
|
||||||
|
|
||||||
|
wth = fn.body.stmts[1]
|
||||||
|
assert isinstance(
|
||||||
|
wth, synr.ast.With
|
||||||
|
), "Did not find With statement, found %s" % type(wth)
|
||||||
|
assert isinstance(wth.rhs, synr.ast.Call)
|
||||||
|
assert wth.rhs.func_name.id.name == "block"
|
||||||
|
assert len(wth.lhs) == 2
|
||||||
|
assert wth.lhs[0].id.name == "x"
|
||||||
|
assert wth.lhs[1].id.name == "y"
|
||||||
|
assert isinstance(wth.body.stmts[0], synr.ast.Return)
|
||||||
|
assert wth.body.stmts[0].value.id.name == "x"
|
||||||
|
|
||||||
|
wth = fn.body.stmts[2]
|
||||||
|
assert isinstance(
|
||||||
|
wth, synr.ast.With
|
||||||
|
), "Did not find With statement, found %s" % type(wth)
|
||||||
|
assert isinstance(wth.rhs, synr.ast.Call)
|
||||||
|
assert wth.rhs.func_name.id.name == "block"
|
||||||
|
assert len(wth.lhs) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def func_block():
|
||||||
|
y = x
|
||||||
|
z = y
|
||||||
|
return z
|
||||||
|
|
||||||
|
|
||||||
|
def test_block():
|
||||||
|
module = to_ast(func_block)
|
||||||
|
fn = assert_one_fn(module, "func_block", no_params=0)
|
||||||
|
block = fn.body
|
||||||
|
assert isinstance(block, synr.ast.Block)
|
||||||
|
assert len(block.stmts) == 3
|
||||||
|
assert isinstance(block.stmts[0], synr.ast.Assign)
|
||||||
|
assert isinstance(block.stmts[1], synr.ast.Assign)
|
||||||
|
assert isinstance(block.stmts[2], synr.ast.Return)
|
||||||
|
|
||||||
|
|
||||||
|
def func_assign():
|
||||||
|
y = 2
|
||||||
|
x, y = 2, 2
|
||||||
|
(x, y) = 2, 2
|
||||||
|
[x, y] = 2, 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_assign():
|
||||||
|
module = to_ast(func_assign)
|
||||||
|
fn = assert_one_fn(module, "func_assign", no_params=0)
|
||||||
|
assign = fn.body.stmts[0]
|
||||||
|
assert isinstance(assign, synr.ast.Assign)
|
||||||
|
assert isinstance(assign.lhs[0], synr.ast.Var)
|
||||||
|
assert assign.lhs[0].id.name == "y"
|
||||||
|
assert isinstance(assign.rhs, synr.ast.Constant)
|
||||||
|
assert assign.rhs.value == 2
|
||||||
|
|
||||||
|
def _check_multi_assign(assign):
|
||||||
|
assert isinstance(assign, synr.ast.Assign)
|
||||||
|
|
||||||
|
assert len(assign.lhs) == 2
|
||||||
|
assert isinstance(assign.lhs[0], synr.ast.Var)
|
||||||
|
assert assign.lhs[0].id.name == "x"
|
||||||
|
assert isinstance(assign.lhs[1], synr.ast.Var)
|
||||||
|
assert assign.lhs[1].id.name == "y"
|
||||||
|
|
||||||
|
_check_multi_assign(fn.body.stmts[1])
|
||||||
|
_check_multi_assign(fn.body.stmts[2])
|
||||||
|
_check_multi_assign(fn.body.stmts[3])
|
||||||
|
|
||||||
|
|
||||||
|
def func_var():
|
||||||
|
return x.y.z
|
||||||
|
|
||||||
|
|
||||||
|
def test_var():
|
||||||
|
module = to_ast(func_var)
|
||||||
|
fn = assert_one_fn(module, "func_var", no_params=0)
|
||||||
|
ret = fn.body.stmts[0]
|
||||||
|
assert ret.value.field.name == "z"
|
||||||
|
assert ret.value.object.field.name == "y"
|
||||||
|
assert ret.value.object.object.id.name == "x"
|
||||||
|
|
||||||
|
|
||||||
|
def func_binop():
|
||||||
|
x = 1 + 2
|
||||||
|
x = 1 - 2
|
||||||
|
x = 1 * 2
|
||||||
|
x = 1 / 2
|
||||||
|
x = 1 // 2
|
||||||
|
x = 1 % 2
|
||||||
|
x = 1 == 2
|
||||||
|
x = 1 != 2
|
||||||
|
x = 1 >= 2
|
||||||
|
x = 1 <= 2
|
||||||
|
x = 1 < 2
|
||||||
|
x = 1 > 2
|
||||||
|
x = not True
|
||||||
|
x = True and False
|
||||||
|
x = True or False
|
||||||
|
x += 1
|
||||||
|
x -= 1
|
||||||
|
x /= 1
|
||||||
|
x *= 1
|
||||||
|
x //= 1
|
||||||
|
x %= 1
|
||||||
|
x = (1 + 3) / (4 % 2)
|
||||||
|
x = -1
|
||||||
|
x = +1
|
||||||
|
x = ~1
|
||||||
|
|
||||||
|
|
||||||
|
def test_binop():
|
||||||
|
module = to_ast(func_binop)
|
||||||
|
fn = assert_one_fn(module, "func_binop", no_params=0)
|
||||||
|
stmts = fn.body.stmts
|
||||||
|
|
||||||
|
def verify(stmt, op, vals):
|
||||||
|
assert isinstance(stmt, synr.ast.Call)
|
||||||
|
assert stmt.func_name.name == op, f"Expect {op.name}, got {stmt.func_name}"
|
||||||
|
assert len(vals) == len(stmt.params)
|
||||||
|
for i in range(len(vals)):
|
||||||
|
assert stmt.params[i].value == vals[i]
|
||||||
|
|
||||||
|
verify(stmts[0].rhs, synr.ast.BuiltinOp.Add, [1, 2])
|
||||||
|
verify(stmts[1].rhs, synr.ast.BuiltinOp.Sub, [1, 2])
|
||||||
|
verify(stmts[2].rhs, synr.ast.BuiltinOp.Mul, [1, 2])
|
||||||
|
verify(stmts[3].rhs, synr.ast.BuiltinOp.Div, [1, 2])
|
||||||
|
verify(stmts[4].rhs, synr.ast.BuiltinOp.FloorDiv, [1, 2])
|
||||||
|
verify(stmts[5].rhs, synr.ast.BuiltinOp.Mod, [1, 2])
|
||||||
|
verify(stmts[6].rhs, synr.ast.BuiltinOp.Eq, [1, 2])
|
||||||
|
verify(stmts[7].rhs, synr.ast.BuiltinOp.NotEq, [1, 2])
|
||||||
|
verify(stmts[8].rhs, synr.ast.BuiltinOp.GE, [1, 2])
|
||||||
|
verify(stmts[9].rhs, synr.ast.BuiltinOp.LE, [1, 2])
|
||||||
|
verify(stmts[10].rhs, synr.ast.BuiltinOp.LT, [1, 2])
|
||||||
|
verify(stmts[11].rhs, synr.ast.BuiltinOp.GT, [1, 2])
|
||||||
|
verify(stmts[12].rhs, synr.ast.BuiltinOp.Not, [True])
|
||||||
|
verify(stmts[13].rhs, synr.ast.BuiltinOp.And, [True, False])
|
||||||
|
verify(stmts[14].rhs, synr.ast.BuiltinOp.Or, [True, False])
|
||||||
|
|
||||||
|
def verify_assign(stmt, op, vals):
|
||||||
|
assert isinstance(stmt.rhs, synr.ast.Call)
|
||||||
|
assert stmt.rhs.func_name.name == op, f"Expect {op.name}, got {stmt.id.name}"
|
||||||
|
assert len(vals) + 1 == len(stmt.rhs.params)
|
||||||
|
assert stmt.lhs[0].id.name == stmt.rhs.params[0].id.name
|
||||||
|
for i in range(len(vals)):
|
||||||
|
assert stmt.rhs.params[i + 1].value == vals[i]
|
||||||
|
|
||||||
|
verify_assign(stmts[15], synr.ast.BuiltinOp.Add, [1])
|
||||||
|
verify_assign(stmts[16], synr.ast.BuiltinOp.Sub, [1])
|
||||||
|
verify_assign(stmts[17], synr.ast.BuiltinOp.Div, [1])
|
||||||
|
verify_assign(stmts[18], synr.ast.BuiltinOp.Mul, [1])
|
||||||
|
verify_assign(stmts[19], synr.ast.BuiltinOp.FloorDiv, [1])
|
||||||
|
verify_assign(stmts[20], synr.ast.BuiltinOp.Mod, [1])
|
||||||
|
verify(stmts[22].rhs, synr.ast.BuiltinOp.USub, [1])
|
||||||
|
verify(stmts[23].rhs, synr.ast.BuiltinOp.UAdd, [1])
|
||||||
|
verify(stmts[24].rhs, synr.ast.BuiltinOp.Invert, [1])
|
||||||
|
|
||||||
|
|
||||||
|
def func_if():
|
||||||
|
if 1 and 2 and 3 or 4:
|
||||||
|
return 1
|
||||||
|
elif 1:
|
||||||
|
return 2
|
||||||
|
else:
|
||||||
|
return 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_if():
|
||||||
|
module = to_ast(func_if)
|
||||||
|
fn = assert_one_fn(module, "func_if", no_params=0)
|
||||||
|
|
||||||
|
if_stmt = fn.body.stmts[0]
|
||||||
|
assert isinstance(if_stmt, synr.ast.If)
|
||||||
|
assert isinstance(if_stmt.true.stmts[0], synr.ast.Return)
|
||||||
|
assert if_stmt.true.stmts[0].value.value == 1
|
||||||
|
cond = if_stmt.condition
|
||||||
|
assert isinstance(cond, synr.ast.Call)
|
||||||
|
assert cond.func_name.name == synr.ast.BuiltinOp.Or
|
||||||
|
assert cond.params[1].value == 4
|
||||||
|
elif_stmt = if_stmt.false.stmts[0]
|
||||||
|
assert isinstance(elif_stmt.true.stmts[0], synr.ast.Return)
|
||||||
|
assert elif_stmt.true.stmts[0].value.value == 2
|
||||||
|
assert elif_stmt.condition.value == 1
|
||||||
|
assert isinstance(elif_stmt.false.stmts[0], synr.ast.Return)
|
||||||
|
assert elif_stmt.false.stmts[0].value.value == 3
|
||||||
|
|
||||||
|
|
||||||
|
def func_subscript():
|
||||||
|
z = x[1:2, y]
|
||||||
|
z = x[1.0:3.0:2]
|
||||||
|
x[1:2] = 3
|
||||||
|
z = x[y, z]
|
||||||
|
return x[:1]
|
||||||
|
|
||||||
|
|
||||||
|
def test_subscript():
|
||||||
|
module = to_ast(func_subscript)
|
||||||
|
fn = assert_one_fn(module, "func_subscript", no_params=0)
|
||||||
|
|
||||||
|
sub = fn.body.stmts[0].rhs
|
||||||
|
assert isinstance(sub, synr.ast.Call)
|
||||||
|
assert sub.func_name.name == synr.ast.BuiltinOp.Subscript
|
||||||
|
assert sub.params[0].id.name == "x"
|
||||||
|
assert sub.params[1].values[0].start.value == 1
|
||||||
|
assert sub.params[1].values[0].step.value == 1
|
||||||
|
assert sub.params[1].values[0].end.value == 2
|
||||||
|
assert sub.params[1].values[1].id.name == "y"
|
||||||
|
|
||||||
|
sub2 = fn.body.stmts[1].rhs
|
||||||
|
assert sub2.params[1].values[0].step.value == 2
|
||||||
|
|
||||||
|
sub3 = fn.body.stmts[2]
|
||||||
|
assert isinstance(sub3, synr.ast.UnassignedCall)
|
||||||
|
assert isinstance(sub3.call, synr.ast.Call)
|
||||||
|
assert sub3.call.func_name.name == synr.ast.BuiltinOp.SubscriptAssign
|
||||||
|
assert sub3.call.params[0].id.name == "x"
|
||||||
|
assert isinstance(sub3.call.params[1], synr.ast.Tuple)
|
||||||
|
assert isinstance(sub3.call.params[1].values[0], synr.ast.Slice)
|
||||||
|
assert sub3.call.params[1].values[0].start.value == 1
|
||||||
|
assert sub3.call.params[1].values[0].end.value == 2
|
||||||
|
assert sub3.call.params[2].value == 3
|
||||||
|
|
||||||
|
sub4 = fn.body.stmts[3].rhs
|
||||||
|
assert sub4.params[1].values[0].id.name == "y"
|
||||||
|
assert sub4.params[1].values[1].id.name == "z"
|
||||||
|
|
||||||
|
|
||||||
|
def func_literals():
|
||||||
|
x = 1
|
||||||
|
x = 2.0
|
||||||
|
x = (1, 2.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_literals():
|
||||||
|
module = to_ast(func_literals)
|
||||||
|
fn = assert_one_fn(module, "func_literals", no_params=0)
|
||||||
|
|
||||||
|
assert fn.body.stmts[0].rhs.value == 1
|
||||||
|
assert isinstance(fn.body.stmts[0].rhs.value, int)
|
||||||
|
|
||||||
|
assert fn.body.stmts[1].rhs.value == 2.0
|
||||||
|
assert isinstance(fn.body.stmts[1].rhs.value, float)
|
||||||
|
|
||||||
|
assert fn.body.stmts[2].rhs.values[0].value == 1
|
||||||
|
assert fn.body.stmts[2].rhs.values[1].value == 2.0
|
||||||
|
assert isinstance(fn.body.stmts[2].rhs, synr.ast.Tuple)
|
||||||
|
|
||||||
|
|
||||||
|
class X:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Y:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def func_type(x: X) -> Y:
|
||||||
|
x: test.X = 1
|
||||||
|
x: X[Y] = 1
|
||||||
|
x: X[X, Y] = 1
|
||||||
|
x: X[X:Y] = 1
|
||||||
|
x: X[1] = 1
|
||||||
|
x: test.X[Y] = 1
|
||||||
|
x: test.X(Y) = 1
|
||||||
|
x: X + Y = 1
|
||||||
|
x: test.X(Y_TYPE=Y) = 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_type():
|
||||||
|
module = to_ast(func_type)
|
||||||
|
fn = assert_one_fn(module, "func_type", no_params=0)
|
||||||
|
|
||||||
|
assert isinstance(fn.ret_type, synr.ast.TypeVar)
|
||||||
|
assert isinstance(fn.params[0].ty, synr.ast.TypeVar), fn.params[0].ty
|
||||||
|
assert fn.params[0].ty.id.name == "X"
|
||||||
|
|
||||||
|
stmts = fn.body.stmts
|
||||||
|
assert stmts[0].ty.object.id.name == "test"
|
||||||
|
assert stmts[0].ty.field.name == "X"
|
||||||
|
|
||||||
|
assert isinstance(stmts[1].ty, synr.ast.TypeApply)
|
||||||
|
assert stmts[1].ty.func_name.id.name == "X"
|
||||||
|
assert stmts[1].ty.params[0].id.name == "Y"
|
||||||
|
|
||||||
|
assert isinstance(stmts[2].ty, synr.ast.TypeApply)
|
||||||
|
assert stmts[2].ty.func_name.id.name == "X"
|
||||||
|
assert stmts[2].ty.params[0].id.name == "X"
|
||||||
|
assert stmts[2].ty.params[1].id.name == "Y"
|
||||||
|
|
||||||
|
assert isinstance(stmts[5].ty, synr.ast.TypeApply)
|
||||||
|
assert isinstance(stmts[5].ty.func_name, synr.ast.TypeAttr)
|
||||||
|
assert stmts[5].ty.func_name.object.id.name == "test"
|
||||||
|
assert stmts[5].ty.func_name.field.name == "X"
|
||||||
|
assert stmts[5].ty.params[0].id.name == "Y"
|
||||||
|
|
||||||
|
assert isinstance(stmts[6].ty, synr.ast.TypeCall)
|
||||||
|
assert isinstance(stmts[6].ty.func_name, synr.ast.TypeAttr)
|
||||||
|
assert stmts[6].ty.func_name.object.id.name == "test"
|
||||||
|
assert stmts[6].ty.func_name.field.name == "X"
|
||||||
|
assert stmts[6].ty.params[0].id.name == "Y"
|
||||||
|
|
||||||
|
assert isinstance(stmts[7].ty, synr.ast.TypeCall)
|
||||||
|
assert stmts[7].ty.func_name == synr.ast.BuiltinOp.Add
|
||||||
|
assert stmts[7].ty.params[0].id.name == "X"
|
||||||
|
assert stmts[7].ty.params[1].id.name == "Y"
|
||||||
|
|
||||||
|
# test TypeCall with kwargs
|
||||||
|
assert isinstance(stmts[8].ty, synr.ast.TypeCall)
|
||||||
|
assert isinstance(stmts[8].ty.func_name, synr.ast.TypeAttr)
|
||||||
|
assert stmts[8].ty.func_name.object.id.name == "test"
|
||||||
|
assert stmts[8].ty.func_name.field.name == "X"
|
||||||
|
for k, v in stmts[8].ty.keyword_params.items():
|
||||||
|
assert k.value == "Y_TYPE"
|
||||||
|
assert v.id.name == "Y"
|
||||||
|
|
||||||
|
|
||||||
|
def func_call():
|
||||||
|
test()
|
||||||
|
|
||||||
|
|
||||||
|
def test_call():
|
||||||
|
module = to_ast(func_call)
|
||||||
|
fn = assert_one_fn(module, "func_call", no_params=0)
|
||||||
|
|
||||||
|
assert isinstance(fn.body.stmts[0], synr.ast.UnassignedCall)
|
||||||
|
assert fn.body.stmts[0].call.func_name.id.name == "test"
|
||||||
|
|
||||||
|
|
||||||
|
def func_constants():
|
||||||
|
x = {"test": 1, "another": 3j}
|
||||||
|
y = ["an", "array", 2.0, None, True, False]
|
||||||
|
z = ("hi",)
|
||||||
|
|
||||||
|
|
||||||
|
def test_constants():
|
||||||
|
module = to_ast(func_constants)
|
||||||
|
fn = assert_one_fn(module, "func_constants", no_params=0)
|
||||||
|
|
||||||
|
d = fn.body.stmts[0].rhs
|
||||||
|
assert isinstance(d, synr.ast.DictLiteral)
|
||||||
|
k = [x.value for x in d.keys]
|
||||||
|
v = [x.value for x in d.values]
|
||||||
|
assert dict(zip(k, v)) == {"test": 1, "another": 3j}
|
||||||
|
|
||||||
|
ary = fn.body.stmts[1].rhs
|
||||||
|
assert isinstance(ary, synr.ast.ArrayLiteral)
|
||||||
|
assert [x.value for x in ary.values] == ["an", "array", 2.0, None, True, False]
|
||||||
|
|
||||||
|
t = fn.body.stmts[2].rhs
|
||||||
|
assert isinstance(t, synr.ast.Tuple)
|
||||||
|
assert [x.value for x in t.values] == ["hi"]
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorAccumulator:
|
||||||
|
def __init__(self):
|
||||||
|
self.errors = {}
|
||||||
|
self.sources = {}
|
||||||
|
|
||||||
|
def add_source(self, name, source):
|
||||||
|
self.sources[name] = source
|
||||||
|
|
||||||
|
def emit(self, level, message, span):
|
||||||
|
if span.start_line in self.errors:
|
||||||
|
self.errors[span.start_line].append((level, message, span))
|
||||||
|
else:
|
||||||
|
self.errors[span.start_line] = [(level, message, span)]
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
return self.errors
|
||||||
|
|
||||||
|
|
||||||
|
def to_ast_err(program: Any) -> Any:
|
||||||
|
diag_ctx = ErrorAccumulator()
|
||||||
|
transformer = None
|
||||||
|
return synr.to_ast(program, diag_ctx, transformer)
|
||||||
|
|
||||||
|
|
||||||
|
def func_err(x=2, *args, **kwargs):
|
||||||
|
x: X
|
||||||
|
|
||||||
|
|
||||||
|
def test_err_msg():
|
||||||
|
_, start = inspect.getsourcelines(func_err)
|
||||||
|
errs = to_ast_err(func_err)
|
||||||
|
def_errs = sorted(
|
||||||
|
[(x[1], x[2]) for x in errs[start]], key=lambda x: x[1].start_column
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_err(err, msg, filename, start_line, start_column):
|
||||||
|
assert (
|
||||||
|
err[0] == msg
|
||||||
|
), f"Error message `{err[0]}` does not match expected message `{msg}`"
|
||||||
|
span = err[1]
|
||||||
|
assert span.filename.endswith(
|
||||||
|
filename
|
||||||
|
), f"File name `{span.filename}` does not end with `{filename}`"
|
||||||
|
assert (
|
||||||
|
span.start_line == start_line
|
||||||
|
), f"Starting line of error does not match expected: {span.start_line} vs {start_line}"
|
||||||
|
assert (
|
||||||
|
span.start_column == start_column
|
||||||
|
), f"Starting column of error does not match expected: {span.start_column} vs {start_column}"
|
||||||
|
|
||||||
|
check_err(
|
||||||
|
def_errs[0],
|
||||||
|
"currently synr does not support defaults",
|
||||||
|
"test_synr.py",
|
||||||
|
start,
|
||||||
|
16,
|
||||||
|
)
|
||||||
|
check_err(
|
||||||
|
def_errs[1],
|
||||||
|
"currently synr does not support varargs",
|
||||||
|
"test_synr.py",
|
||||||
|
start,
|
||||||
|
20,
|
||||||
|
)
|
||||||
|
check_err(
|
||||||
|
def_errs[2],
|
||||||
|
"currently synr does not support kwarg",
|
||||||
|
"test_synr.py",
|
||||||
|
start,
|
||||||
|
28,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert errs[start + 1][0][1] == "Empty type assignment not supported"
|
||||||
|
|
||||||
|
|
||||||
|
def test_scoped_func():
|
||||||
|
global_var = 0
|
||||||
|
|
||||||
|
def func():
|
||||||
|
return global_var
|
||||||
|
|
||||||
|
module = to_ast(func)
|
||||||
|
fn = assert_one_fn(module, "func", no_params=0)
|
||||||
|
stmts = fn.body.stmts
|
||||||
|
assert isinstance(stmts[0], synr.ast.Return)
|
||||||
|
assert stmts[0].value.id.name == "global_var"
|
||||||
|
_, start_line = inspect.getsourcelines(func)
|
||||||
|
assert stmts[0].span.start_line == start_line + 1
|
||||||
|
assert stmts[0].span.start_column == 9
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_func():
|
||||||
|
def foo():
|
||||||
|
def bar():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return bar()
|
||||||
|
|
||||||
|
module = to_ast(foo)
|
||||||
|
fn = assert_one_fn(module, "foo")
|
||||||
|
stmts = fn.body.stmts
|
||||||
|
assert isinstance(stmts[0], synr.ast.Function)
|
||||||
|
assert stmts[0].name == "bar"
|
||||||
|
assert len(stmts[0].params) == 0
|
||||||
|
_, start_line = inspect.getsourcelines(foo)
|
||||||
|
assert stmts[0].span.start_line == start_line + 1
|
||||||
|
assert stmts[0].span.start_column == 9
|
||||||
|
|
||||||
|
|
||||||
|
def test_decorators():
|
||||||
|
def A(f):
|
||||||
|
return f
|
||||||
|
|
||||||
|
@A
|
||||||
|
def foo():
|
||||||
|
@B
|
||||||
|
@C
|
||||||
|
def bar():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return bar()
|
||||||
|
|
||||||
|
module = to_ast(foo)
|
||||||
|
fn = assert_one_fn(module, "foo")
|
||||||
|
_, start_line = inspect.getsourcelines(foo)
|
||||||
|
assert fn.span.start_line == start_line + 1
|
||||||
|
|
||||||
|
assert len(fn.decorators) == 1
|
||||||
|
assert isinstance(fn.decorators[0], synr.ast.Var)
|
||||||
|
assert fn.decorators[0].id.name == "A"
|
||||||
|
assert fn.decorators[0].span.start_line == start_line
|
||||||
|
|
||||||
|
# end_lineno was added in Python 3.8 so we check it here
|
||||||
|
if sys.version_info >= (3, 8):
|
||||||
|
assert fn.span.end_line == start_line + 7
|
||||||
|
|
||||||
|
bar = fn.body.stmts[0]
|
||||||
|
assert bar.span.start_line == start_line + 4
|
||||||
|
|
||||||
|
assert len(bar.decorators) == 2
|
||||||
|
|
||||||
|
assert isinstance(bar.decorators[0], synr.ast.Var)
|
||||||
|
assert bar.decorators[0].id.name == "B"
|
||||||
|
assert bar.decorators[0].span.start_line == start_line + 2
|
||||||
|
|
||||||
|
assert isinstance(bar.decorators[1], synr.ast.Var)
|
||||||
|
assert bar.decorators[1].id.name == "C"
|
||||||
|
assert bar.decorators[1].span.start_line == start_line + 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_nonlocal():
|
||||||
|
x, y = 1, 2
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
nonlocal x, y
|
||||||
|
return x + y
|
||||||
|
|
||||||
|
module = to_ast(foo)
|
||||||
|
fn = assert_one_fn(module, "foo")
|
||||||
|
nl = fn.body.stmts[0]
|
||||||
|
assert isinstance(nl, synr.ast.Nonlocal)
|
||||||
|
assert len(nl.vars) == 2
|
||||||
|
x, y = nl.vars
|
||||||
|
assert isinstance(x, synr.ast.Var) and x.id.name == "x"
|
||||||
|
assert isinstance(y, synr.ast.Var) and y.id.name == "y"
|
||||||
|
|
||||||
|
_, start_line = inspect.getsourcelines(foo)
|
||||||
|
assert nl.span.start_line == start_line + 1
|
||||||
|
# NOTE: variable spans are a bit hacky so we don't check them here
|
||||||
|
|
||||||
|
|
||||||
|
def test_global():
|
||||||
|
def foo():
|
||||||
|
global x, y
|
||||||
|
return x + y
|
||||||
|
|
||||||
|
module = to_ast(foo)
|
||||||
|
fn = assert_one_fn(module, "foo")
|
||||||
|
gl = fn.body.stmts[0]
|
||||||
|
assert isinstance(gl, synr.ast.Global)
|
||||||
|
assert len(gl.vars) == 2
|
||||||
|
x, y = gl.vars
|
||||||
|
assert isinstance(x, synr.ast.Var) and x.id.name == "x"
|
||||||
|
assert isinstance(y, synr.ast.Var) and y.id.name == "y"
|
||||||
|
|
||||||
|
_, start_line = inspect.getsourcelines(foo)
|
||||||
|
assert gl.span.start_line == start_line + 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_lambda():
|
||||||
|
def foo():
|
||||||
|
return lambda x, y: x + y
|
||||||
|
|
||||||
|
module = to_ast(foo)
|
||||||
|
fn = assert_one_fn(module, "foo")
|
||||||
|
|
||||||
|
assert isinstance(fn.body.stmts[0], synr.ast.Return)
|
||||||
|
assert isinstance(fn.body.stmts[0].value, synr.ast.Lambda)
|
||||||
|
node = fn.body.stmts[0].value
|
||||||
|
assert len(node.params) == 2
|
||||||
|
assert node.params[0].name == "x"
|
||||||
|
assert node.params[0].ty == None
|
||||||
|
assert node.params[1].name == "y"
|
||||||
|
assert node.params[1].ty == None
|
||||||
|
|
||||||
|
assert isinstance(node.body, synr.ast.Call)
|
||||||
|
assert node.body.func_name.name == synr.ast.BuiltinOp.Add
|
||||||
|
assert node.body.params[0].id.name == "x"
|
||||||
|
assert node.body.params[1].id.name == "y"
|
||||||
|
|
||||||
|
_, start_line = inspect.getsourcelines(foo)
|
||||||
|
assert node.span.start_line == start_line + 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_id_function()
|
||||||
|
test_class()
|
||||||
|
test_for()
|
||||||
|
test_while()
|
||||||
|
test_with()
|
||||||
|
test_block()
|
||||||
|
test_assign()
|
||||||
|
test_var()
|
||||||
|
test_binop()
|
||||||
|
test_if()
|
||||||
|
test_subscript()
|
||||||
|
test_literals()
|
||||||
|
test_type()
|
||||||
|
test_call()
|
||||||
|
test_constants()
|
||||||
|
test_err_msg()
|
||||||
|
test_scoped_func()
|
||||||
|
test_local_func()
|
||||||
|
test_decorators()
|
||||||
|
test_nonlocal()
|
||||||
|
test_global()
|
||||||
|
test_lambda()
|
Reference in New Issue
Block a user