tests: add predicates and constraints test
This commit is contained in:
@@ -39,9 +39,6 @@ class Generator(p.Stmt.Visitor[ast.stmt], p.Expr.Visitor[ast.expr]):
|
|||||||
def __init__(self, workdir: Path, types: TypesRegistry) -> None:
|
def __init__(self, workdir: Path, types: TypesRegistry) -> None:
|
||||||
self.workdir: Path = workdir.resolve()
|
self.workdir: Path = workdir.resolve()
|
||||||
self.build_dir: Path = self.workdir / "build" / "midas"
|
self.build_dir: Path = self.workdir / "build" / "midas"
|
||||||
if self.build_dir.exists():
|
|
||||||
shutil.rmtree(self.build_dir)
|
|
||||||
self.build_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
self.rel_src_path: Path = Path()
|
self.rel_src_path: Path = Path()
|
||||||
|
|
||||||
self._typed_ast: TypedAST = TypedAST(
|
self._typed_ast: TypedAST = TypedAST(
|
||||||
@@ -56,7 +53,7 @@ class Generator(p.Stmt.Visitor[ast.stmt], p.Expr.Visitor[ast.expr]):
|
|||||||
self._constraints: list[tuple[m.Expr, ast.expr]] = []
|
self._constraints: list[tuple[m.Expr, ast.expr]] = []
|
||||||
|
|
||||||
def generate_ast(self, typed_ast: TypedAST, src_path: Path) -> ast.AST:
|
def generate_ast(self, typed_ast: TypedAST, src_path: Path) -> ast.AST:
|
||||||
self.rel_src_path = src_path.relative_to(self.workdir)
|
self.rel_src_path = src_path.resolve().relative_to(self.workdir)
|
||||||
self._typed_ast = typed_ast
|
self._typed_ast = typed_ast
|
||||||
body: list[ast.stmt] = self._visit_body(typed_ast.stmts)
|
body: list[ast.stmt] = self._visit_body(typed_ast.stmts)
|
||||||
predicates: list[ast.stmt] = self._constraint_generator.get_definitions()
|
predicates: list[ast.stmt] = self._constraint_generator.get_definitions()
|
||||||
@@ -70,6 +67,9 @@ class Generator(p.Stmt.Visitor[ast.stmt], p.Expr.Visitor[ast.expr]):
|
|||||||
module: ast.AST = self.generate_ast(typed_ast, src_path)
|
module: ast.AST = self.generate_ast(typed_ast, src_path)
|
||||||
compiled: str = ast.unparse(module)
|
compiled: str = ast.unparse(module)
|
||||||
if out_path is None:
|
if out_path is None:
|
||||||
|
if self.build_dir.exists():
|
||||||
|
shutil.rmtree(self.build_dir)
|
||||||
|
self.build_dir.mkdir(parents=True, exist_ok=True)
|
||||||
out_path = (self.build_dir / self.rel_src_path).resolve()
|
out_path = (self.build_dir / self.rel_src_path).resolve()
|
||||||
try:
|
try:
|
||||||
_ = out_path.relative_to(self.build_dir)
|
_ = out_path.relative_to(self.build_dir)
|
||||||
|
|||||||
14
tests/cases/generator/02_constraints.midas
Normal file
14
tests/cases/generator/02_constraints.midas
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// Inline
|
||||||
|
type T1 = float where _ > 0
|
||||||
|
|
||||||
|
// Named
|
||||||
|
predicate is_positive(v: float) = v > 0
|
||||||
|
type T2 = float where is_positive(_)
|
||||||
|
|
||||||
|
// Curried
|
||||||
|
predicate in_range(mn: float, mx: float)(v: float) = v >= mn & v < mx
|
||||||
|
type T3 = float where in_range(100, 200)(_)
|
||||||
|
|
||||||
|
// Alias
|
||||||
|
predicate minor = in_range(0, 18)
|
||||||
|
type T4 = float where minor(_)
|
||||||
8
tests/cases/generator/02_constraints.py
Normal file
8
tests/cases/generator/02_constraints.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from midas import T1, T2, T3, T4, cast
|
||||||
|
|
||||||
|
t: float = 12.5
|
||||||
|
|
||||||
|
t1: T1 = cast(T1, t)
|
||||||
|
t2: T2 = cast(T2, t)
|
||||||
|
t3: T3 = cast(T3, t)
|
||||||
|
t4: T4 = cast(T4, t)
|
||||||
333
tests/cases/generator/02_constraints.py.ref.txt
Normal file
333
tests/cases/generator/02_constraints.py.ref.txt
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
Module(
|
||||||
|
body=[
|
||||||
|
FunctionDef(
|
||||||
|
name='__midas_p0__',
|
||||||
|
args=arguments(
|
||||||
|
posonlyargs=[],
|
||||||
|
args=[
|
||||||
|
arg(
|
||||||
|
arg='_',
|
||||||
|
annotation=Constant(value='Any'))],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[]),
|
||||||
|
body=[
|
||||||
|
Return(
|
||||||
|
value=Compare(
|
||||||
|
left=Name(id='_'),
|
||||||
|
ops=[
|
||||||
|
Gt()],
|
||||||
|
comparators=[
|
||||||
|
Constant(value=0.0)]))],
|
||||||
|
decorator_list=[],
|
||||||
|
returns=Constant(value='bool')),
|
||||||
|
FunctionDef(
|
||||||
|
name='__midas_is_positive__',
|
||||||
|
args=arguments(
|
||||||
|
posonlyargs=[],
|
||||||
|
args=[
|
||||||
|
arg(
|
||||||
|
arg='v',
|
||||||
|
annotation=Constant(value='float'))],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[]),
|
||||||
|
body=[
|
||||||
|
Return(
|
||||||
|
value=Compare(
|
||||||
|
left=Name(id='v'),
|
||||||
|
ops=[
|
||||||
|
Gt()],
|
||||||
|
comparators=[
|
||||||
|
Constant(value=0.0)]))],
|
||||||
|
decorator_list=[],
|
||||||
|
returns=Constant(value='bool')),
|
||||||
|
FunctionDef(
|
||||||
|
name='__midas_p1__',
|
||||||
|
args=arguments(
|
||||||
|
posonlyargs=[],
|
||||||
|
args=[
|
||||||
|
arg(
|
||||||
|
arg='_',
|
||||||
|
annotation=Constant(value='Any'))],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[]),
|
||||||
|
body=[
|
||||||
|
Return(
|
||||||
|
value=Call(
|
||||||
|
func=Name(id='__midas_is_positive__'),
|
||||||
|
args=[
|
||||||
|
Name(id='_')],
|
||||||
|
keywords=[]))],
|
||||||
|
decorator_list=[],
|
||||||
|
returns=Constant(value='bool')),
|
||||||
|
FunctionDef(
|
||||||
|
name='__midas_in_range__',
|
||||||
|
args=arguments(
|
||||||
|
posonlyargs=[],
|
||||||
|
args=[
|
||||||
|
arg(
|
||||||
|
arg='mn',
|
||||||
|
annotation=Constant(value='float')),
|
||||||
|
arg(
|
||||||
|
arg='mx',
|
||||||
|
annotation=Constant(value='float'))],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[]),
|
||||||
|
body=[
|
||||||
|
FunctionDef(
|
||||||
|
name='inner0',
|
||||||
|
args=arguments(
|
||||||
|
posonlyargs=[],
|
||||||
|
args=[
|
||||||
|
arg(
|
||||||
|
arg='v',
|
||||||
|
annotation=Constant(value='float'))],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[]),
|
||||||
|
body=[
|
||||||
|
Return(
|
||||||
|
value=BoolOp(
|
||||||
|
op=And(),
|
||||||
|
values=[
|
||||||
|
Compare(
|
||||||
|
left=Name(id='v'),
|
||||||
|
ops=[
|
||||||
|
GtE()],
|
||||||
|
comparators=[
|
||||||
|
Name(id='mn')]),
|
||||||
|
Compare(
|
||||||
|
left=Name(id='v'),
|
||||||
|
ops=[
|
||||||
|
Lt()],
|
||||||
|
comparators=[
|
||||||
|
Name(id='mx')])]))],
|
||||||
|
decorator_list=[],
|
||||||
|
returns=Constant(value='bool')),
|
||||||
|
Return(
|
||||||
|
value=Name(id='inner0'))],
|
||||||
|
decorator_list=[],
|
||||||
|
returns=Constant(value='Callable[[float], bool]')),
|
||||||
|
FunctionDef(
|
||||||
|
name='__midas_p2__',
|
||||||
|
args=arguments(
|
||||||
|
posonlyargs=[],
|
||||||
|
args=[
|
||||||
|
arg(
|
||||||
|
arg='_',
|
||||||
|
annotation=Constant(value='Any'))],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[]),
|
||||||
|
body=[
|
||||||
|
Return(
|
||||||
|
value=Call(
|
||||||
|
func=Call(
|
||||||
|
func=Name(id='__midas_in_range__'),
|
||||||
|
args=[
|
||||||
|
Constant(value=100.0),
|
||||||
|
Constant(value=200.0)],
|
||||||
|
keywords=[]),
|
||||||
|
args=[
|
||||||
|
Name(id='_')],
|
||||||
|
keywords=[]))],
|
||||||
|
decorator_list=[],
|
||||||
|
returns=Constant(value='bool')),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_minor__')],
|
||||||
|
value=Call(
|
||||||
|
func=Name(id='__midas_in_range__'),
|
||||||
|
args=[
|
||||||
|
Constant(value=0.0),
|
||||||
|
Constant(value=18.0)],
|
||||||
|
keywords=[])),
|
||||||
|
FunctionDef(
|
||||||
|
name='__midas_p3__',
|
||||||
|
args=arguments(
|
||||||
|
posonlyargs=[],
|
||||||
|
args=[
|
||||||
|
arg(
|
||||||
|
arg='_',
|
||||||
|
annotation=Constant(value='Any'))],
|
||||||
|
kwonlyargs=[],
|
||||||
|
kw_defaults=[],
|
||||||
|
defaults=[]),
|
||||||
|
body=[
|
||||||
|
Return(
|
||||||
|
value=Call(
|
||||||
|
func=Name(id='__midas_minor__'),
|
||||||
|
args=[
|
||||||
|
Name(id='_')],
|
||||||
|
keywords=[]))],
|
||||||
|
decorator_list=[],
|
||||||
|
returns=Constant(value='bool')),
|
||||||
|
ImportFrom(
|
||||||
|
module='midas',
|
||||||
|
names=[
|
||||||
|
alias(name='T1'),
|
||||||
|
alias(name='T2'),
|
||||||
|
alias(name='T3'),
|
||||||
|
alias(name='T4'),
|
||||||
|
alias(name='cast')],
|
||||||
|
level=0),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='t')],
|
||||||
|
value=Constant(value=12.5)),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a0__')],
|
||||||
|
value=Name(id='t')),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='isinstance'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a0__'),
|
||||||
|
Name(id='float')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=JoinedStr(
|
||||||
|
values=[
|
||||||
|
Constant(value='02_constraints.py:L5:10: CastError: Cannot cast '),
|
||||||
|
FormattedValue(
|
||||||
|
value=Attribute(
|
||||||
|
value=Call(
|
||||||
|
func=Name(id='type'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a0__')],
|
||||||
|
keywords=[]),
|
||||||
|
attr='__name__'),
|
||||||
|
conversion=-1),
|
||||||
|
Constant(value=' to float')])),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='__midas_p0__'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a0__')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=Constant(value="02_constraints.py:L5:10: ConstraintError: Value does not fit constraint '_ > 0.0'")),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='t1')],
|
||||||
|
value=Name(id='__midas_a0__')),
|
||||||
|
Delete(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a0__')]),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a1__')],
|
||||||
|
value=Name(id='t')),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='isinstance'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a1__'),
|
||||||
|
Name(id='float')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=JoinedStr(
|
||||||
|
values=[
|
||||||
|
Constant(value='02_constraints.py:L6:10: CastError: Cannot cast '),
|
||||||
|
FormattedValue(
|
||||||
|
value=Attribute(
|
||||||
|
value=Call(
|
||||||
|
func=Name(id='type'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a1__')],
|
||||||
|
keywords=[]),
|
||||||
|
attr='__name__'),
|
||||||
|
conversion=-1),
|
||||||
|
Constant(value=' to float')])),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='__midas_p1__'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a1__')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=Constant(value="02_constraints.py:L6:10: ConstraintError: Value does not fit constraint 'is_positive(_)'")),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='t2')],
|
||||||
|
value=Name(id='__midas_a1__')),
|
||||||
|
Delete(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a1__')]),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a2__')],
|
||||||
|
value=Name(id='t')),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='isinstance'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a2__'),
|
||||||
|
Name(id='float')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=JoinedStr(
|
||||||
|
values=[
|
||||||
|
Constant(value='02_constraints.py:L7:10: CastError: Cannot cast '),
|
||||||
|
FormattedValue(
|
||||||
|
value=Attribute(
|
||||||
|
value=Call(
|
||||||
|
func=Name(id='type'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a2__')],
|
||||||
|
keywords=[]),
|
||||||
|
attr='__name__'),
|
||||||
|
conversion=-1),
|
||||||
|
Constant(value=' to float')])),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='__midas_p2__'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a2__')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=Constant(value="02_constraints.py:L7:10: ConstraintError: Value does not fit constraint 'in_range(100.0, 200.0)(_)'")),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='t3')],
|
||||||
|
value=Name(id='__midas_a2__')),
|
||||||
|
Delete(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a2__')]),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a3__')],
|
||||||
|
value=Name(id='t')),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='isinstance'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a3__'),
|
||||||
|
Name(id='float')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=JoinedStr(
|
||||||
|
values=[
|
||||||
|
Constant(value='02_constraints.py:L8:10: CastError: Cannot cast '),
|
||||||
|
FormattedValue(
|
||||||
|
value=Attribute(
|
||||||
|
value=Call(
|
||||||
|
func=Name(id='type'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a3__')],
|
||||||
|
keywords=[]),
|
||||||
|
attr='__name__'),
|
||||||
|
conversion=-1),
|
||||||
|
Constant(value=' to float')])),
|
||||||
|
Assert(
|
||||||
|
test=Call(
|
||||||
|
func=Name(id='__midas_p3__'),
|
||||||
|
args=[
|
||||||
|
Name(id='__midas_a3__')],
|
||||||
|
keywords=[]),
|
||||||
|
msg=Constant(value="02_constraints.py:L8:10: ConstraintError: Value does not fit constraint 'minor(_)'")),
|
||||||
|
Assign(
|
||||||
|
targets=[
|
||||||
|
Name(id='t4')],
|
||||||
|
value=Name(id='__midas_a3__')),
|
||||||
|
Delete(
|
||||||
|
targets=[
|
||||||
|
Name(id='__midas_a3__')])],
|
||||||
|
type_ignores=[])
|
||||||
Reference in New Issue
Block a user