Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge ast and parser from forward #3

Merged
merged 68 commits into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
53844ed
Update poetry configuration
Legotier May 6, 2022
e6a9c16
Add testreport to gitignore
Legotier May 6, 2022
0e4ffaa
Merge new stuff from forward into ast.py
Legotier May 6, 2022
7fbc8bd
Minimal changes to make the tests pass
Legotier May 6, 2022
4fc36c1
Rename float types to real types
Legotier May 6, 2022
c296998
Update docs to include new classes
Legotier May 6, 2022
9d167e3
Use autodata instead of autoclass for union types
Legotier May 6, 2022
7adbe0f
changed workflow to python version 3.9
LKlinke May 10, 2022
7021d76
Enable type aliases for union types in the docs
Legotier May 11, 2022
816dc31
Merge branch 'brand-new' of https://github.com/Philipp15b/probably in…
Legotier May 11, 2022
697e1d1
Remove forward type references in ast.py
Legotier May 11, 2022
b2ede13
resolved dep issues
May 11, 2022
1d5af7a
Disallow edges to None in ControlFlowGraph.to_networkx()
Legotier May 13, 2022
6eb0df9
Merge parser from forward
Legotier May 13, 2022
b8f7cf3
A helpful comment so people don't have to spend hours figuring out wh…
Legotier May 13, 2022
15f59e7
Update test_uniform_checks
Legotier May 13, 2022
9012370
Fix precedence of likelyhood operator
Legotier May 18, 2022
077a5eb
Add test for basic operator precedence
Legotier May 18, 2022
f8b3e08
Revert "Add test for basic operator precedence"
Legotier May 18, 2022
9cc3a75
Add test for basic operator precedence
Legotier May 18, 2022
8833b7f
Test for correct parsing of distributions
Legotier May 18, 2022
3835e4d
Test for using minus with likely
Legotier May 18, 2022
2afe578
Only allow natural numbers and parameters in distribution expressions
Legotier May 18, 2022
7912125
Revert "Only allow natural numbers and parameters in distribution exp…
Legotier May 19, 2022
ce569fb
Minor fixes
Legotier May 20, 2022
c179a25
Revert to older version (2.5) of networkx
Legotier May 20, 2022
5de84e6
Remove test for parser check that is no longer done
Legotier May 20, 2022
a3a3246
Clear parameter dict for every new program that is parsed
Legotier May 20, 2022
fe6feab
Fix almost all pylint problems
Legotier May 20, 2022
017ad52
Split ast module into several files
Legotier May 25, 2022
1bfd89c
I forgot to add the new files to git
Legotier May 25, 2022
9fd0257
Move documentation of ast module to __init__.py
Legotier May 25, 2022
8557c91
Fix pylint cyclic import warnings
Legotier May 25, 2022
e300fd3
Fix mypy errors
Legotier May 25, 2022
b22519b
Update mypy to newer version
Legotier May 25, 2022
9189730
Move Bounds class to types file
Legotier May 25, 2022
6ec6e8a
Move unused import to doctest
Legotier May 27, 2022
5bdf111
Merge typechecker from forward
Legotier May 27, 2022
3e25603
Add tests for some of the typechecking
Legotier May 27, 2022
965da72
Revert "Remove test for parser check that is no longer done"
Legotier May 27, 2022
8404efa
Fix bug in typechecking test
Legotier May 27, 2022
02dc6dd
Remove 'parameters' parameter from Program.from_parse
Legotier May 27, 2022
9943cb9
Remove VarExpr.is_parameter
Legotier May 27, 2022
a1f93d9
Fix unsafe return type of get_type for iid expressions
Legotier Jun 1, 2022
8cc2a56
Prettify linear checks
Legotier Jun 1, 2022
2aa93a3
Fix unsafe default value warning
Legotier Jun 1, 2022
f96efa4
Add some old checks from the parser to the typechecker
Legotier Jun 1, 2022
ecd08e9
Fix precedence of modulo operator
Legotier Jun 2, 2022
476940a
Fix (suppressed) cyclic dependency warnings
Legotier Jun 2, 2022
16e31c4
Fix typechecking for IidSampleExpr
Legotier Jun 2, 2022
5ab560c
Give some binary operators clearer names
Legotier Jun 2, 2022
0da4e94
Replace not_a_variable and add a warning to the typechecker
Legotier Jun 2, 2022
6158b37
Make it more explicit that iid type checking is unsafe
Legotier Jun 2, 2022
7e8c180
Logger warning for typechecking of iid exprs
Legotier Jun 3, 2022
6247646
Update parameter in syntax module
Legotier Jun 3, 2022
bb071e1
Fix warnings
Legotier Jun 3, 2022
579fdf6
Disallow constants in programs in the syntax module
Legotier Jun 3, 2022
0a598c2
Make some variable names (true, false) illegal
Legotier Jun 3, 2022
12167a5
Eliminate true and false from pgcl grammar
Legotier Jun 3, 2022
e606806
Revert "Eliminate true and false from pgcl grammar"
Legotier Jun 8, 2022
4d4c322
Fix error message in check_is_linear_expr
Legotier Jun 8, 2022
21ae331
Add assertion for illegal keyword to constructor of VarExpr
Legotier Jun 8, 2022
f1d923a
Better error message for usage of true or false in plot instructions
Legotier Jun 8, 2022
79991b9
Allow variables in distribution parameters
Legotier Jun 9, 2022
1777e88
Remove program config
Legotier Jun 9, 2022
3af0bf5
Add typecheck for TickInstr and TickExpr
Legotier Jun 9, 2022
2dd1aa3
Auto format
Legotier Jun 9, 2022
a2adf9f
Add format target to makefile
Legotier Jun 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion probably/pgcl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
... {c := unif(1,10)} [0.8] {f:=true}
... }'''
>>> compile_pgcl(code)
Program(variables={'f': BoolType(), 'c': NatType(bounds=None)}, constants={}, parameters={}, instructions=[SkipInstr(), WhileInstr(cond=BinopExpr(operator=Binop.AND, lhs=BinopExpr(operator=Binop.LE, lhs=VarExpr('c'), rhs=NatLitExpr(10)), rhs=VarExpr('f')), body=[ChoiceInstr(prob=RealLitExpr("0.8"), lhs=[AsgnInstr(lhs='c', rhs=DUniformExpr(start=NatLitExpr(1), end=NatLitExpr(10)))], rhs=[AsgnInstr(lhs='f', rhs=BoolLitExpr(True))])])])
Program(variables={'f': BoolType(), 'c': NatType(bounds=None)}, constants={}, parameters={}, instructions=[SkipInstr(), WhileInstr(cond=BinopExpr(operator=Binop.AND, lhs=BinopExpr(operator=Binop.LT, lhs=VarExpr('c'), rhs=NatLitExpr(10)), rhs=VarExpr('f')), body=[ChoiceInstr(prob=RealLitExpr("0.8"), lhs=[AsgnInstr(lhs='c', rhs=DUniformExpr(start=NatLitExpr(1), end=NatLitExpr(10)))], rhs=[AsgnInstr(lhs='f', rhs=BoolLitExpr(True))])])])

For more details on what syntax is accepted for pGCL programs, you can view the :ref:`formal grammar used for the pGCL parser <pgcl_grammar>`.

Expand Down
19 changes: 17 additions & 2 deletions probably/pgcl/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
ExpectationInstr, PrintInstr, OptimizationQuery, PlotInstr,
LoopInstr, CUniformExpr, GeometricExpr, BernoulliExpr,PoissonExpr,
LogDistExpr, BinomialExpr, IidSampleExpr, DistrExpr)
from .ast.walk import Walk, walk_expr
from .ast.walk import Walk, mut_expr_children, walk_expr

_T = TypeVar('_T')

Expand Down Expand Up @@ -198,8 +198,23 @@ def get_type(program: Program,


def _check_distribution_arguments(prog: Program, expected_type: Type, *parameters: Tuple[Type, Expr]) -> Union[Type, CheckFail]:
# this function is copied from the syntax module, which is not pretty, but prevents cyclic imports
# also, it works only if constant substitution has been performed
# pylint: disable=duplicate-code
def _has_variable(expr: Expr) -> bool:
Legotier marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(expr, UnopExpr) and expr.operator == Unop.IVERSON:
Legotier marked this conversation as resolved.
Show resolved Hide resolved
return False
if isinstance(expr, VarExpr) and expr.var not in prog.parameters:
return True
for child_ref in mut_expr_children(Mut.alloc(expr)):
if _has_variable(child_ref.val):
return True
return False

for expected_param_type, param_expr in parameters:
# Check for variables in parameters here
if _has_variable(param_expr):
return CheckFail(param_expr, "In distribution parameter expressions, no variables are allowed. - Forgot parameter declaration?")

param_type = get_type(prog, param_expr)
if isinstance(param_type, CheckFail):
return param_type
Expand Down
4 changes: 2 additions & 2 deletions probably/pgcl/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ def compile_pgcl(code: str,
substitute_constants: Whether constant substitution is done on the program, defaults to `True`.
"""
program = parse_pgcl(code)
if substitute_constants:
substitute.substitute_constants(program)
check_result = check_program(program)
if check_result is not None:
return check_result
if substitute_constants:
substitute.substitute_constants(program)
return program


Expand Down
2 changes: 1 addition & 1 deletion probably/pgcl/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def parse_expr(code: str) -> Expr:
.. doctest::

>>> parse_expr("x < y & z")
BinopExpr(operator=Binop.AND, lhs=BinopExpr(operator=Binop.LE, lhs=VarExpr('x'), rhs=VarExpr('y')), rhs=VarExpr('z'))
BinopExpr(operator=Binop.AND, lhs=BinopExpr(operator=Binop.LT, lhs=VarExpr('x'), rhs=VarExpr('y')), rhs=VarExpr('z'))

>>> parse_expr("[x]")
Traceback (most recent call last):
Expand Down
25 changes: 12 additions & 13 deletions probably/pgcl/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,11 @@
"""

from typing import Optional, Sequence
from collections.abc import Container

from probably.util.ref import Mut

from .ast import (AsgnInstr, Binop, BinopExpr, Expr, Instr, Program, Unop,
UnopExpr, Var, VarExpr, WhileInstr)
UnopExpr, VarExpr, WhileInstr)
from .check import CheckFail
from .ast.walk import Walk, instr_exprs, mut_expr_children, walk_expr, walk_instrs

Expand All @@ -120,42 +119,42 @@ def check_is_linear_program(program: Program) -> Optional[CheckFail]:
"""
for instr_ref in walk_instrs(Walk.DOWN, program.instructions):
for expr in instr_exprs(instr_ref.val):
res = check_is_linear_expr(expr, {*program.parameters.keys()})
res = check_is_linear_expr(expr, program)
if isinstance(res, CheckFail):
return res
return None


def check_is_linear_expr(expr: Expr, not_a_variable: Container[Var] = frozenset()) -> Optional[CheckFail]:
def check_is_linear_expr(expr: Expr, context: Program) -> Optional[CheckFail]:
Legotier marked this conversation as resolved.
Show resolved Hide resolved
"""
Linear expressions do not multiply variables with each other.
However, they may contain multiplication with constants or Iverson brackets.
Division is also not allowed in linear expressions.

:param expr:
:param not_a_variable: A collection of literals that are not to be considered variables, for example the
parameters of a program
:param not_a_variable: The context in which the expression is to be evaluated. Literals that are
Legotier marked this conversation as resolved.
Show resolved Hide resolved
parameters according to this context not considered variables.

.. doctest::

>>> from .ast import *
>>> from .parser import parse_expectation
>>> nat = NatLitExpr(10)
>>> check_is_linear_expr(BinopExpr(Binop.TIMES, nat, nat))
>>> check_is_linear_expr(BinopExpr(Binop.TIMES, nat, VarExpr('x')))
>>> check_is_linear_expr(BinopExpr(Binop.TIMES, VarExpr('x'), VarExpr('x')))
>>> check_is_linear_expr(BinopExpr(Binop.TIMES, nat, nat), None)
>>> check_is_linear_expr(BinopExpr(Binop.TIMES, nat, VarExpr('x')), None)
>>> check_is_linear_expr(BinopExpr(Binop.TIMES, VarExpr('x'), VarExpr('x')), None)
CheckFail(location=..., message='Is not a linear expression')
>>> check_is_linear_expr(parse_expectation("[x < 6] * x"))
>>> check_is_linear_expr(parse_expectation("[x * x]"))
>>> check_is_linear_expr(parse_expectation("[x < 6] * x"), None)
>>> check_is_linear_expr(parse_expectation("[x * x]"), None)
CheckFail(location=..., message='Is not a linear expression')
>>> check_is_linear_expr(parse_expectation("x/x"))
>>> check_is_linear_expr(parse_expectation("x/x"), None)
CheckFail(location=..., message='General division is not linear (division of constants is)')
"""

def _has_variable(expr: Expr) -> bool:
if isinstance(expr, UnopExpr) and expr.operator == Unop.IVERSON:
return False
if isinstance(expr, VarExpr) and not expr.var in not_a_variable:
if isinstance(expr, VarExpr) and (context is None or expr.var not in context.parameters):
Legotier marked this conversation as resolved.
Show resolved Hide resolved
return True
for child_ref in mut_expr_children(Mut.alloc(expr)):
if _has_variable(child_ref.val):
Expand Down
10 changes: 10 additions & 0 deletions tests/test_typechecking.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,13 @@ def test_for_instr_errors():
x := 5
""")
assert isinstance(check_program(program), CheckFail)

program = parse_pgcl("""
nat x
nat y
x := 5
y := unif(10, x * 5 + 3)
""")
res = check_program(program)
assert isinstance(res, CheckFail)
assert "In distribution parameter expressions, no variables are allowed. - Forgot parameter declaration?" in res.message