Skip to content

Commit

Permalink
New null rule for simplifying maths binary operations
Browse files Browse the repository at this point in the history
If either side is `null` then the expression will always evaluate to a 
`null`.
  • Loading branch information
jonathanhogg committed Dec 20, 2024
1 parent 12b7672 commit fccb9d9
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
30 changes: 30 additions & 0 deletions src/flitter/language/tree.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,8 @@ cdef class Add(MathsBinaryOperation):
program.add()

cdef Expression constant_left(self, Vector left, Expression right):
if left.eq(null_):
return NoOp
if left.eq(false_):
return Positive(right)

Expand All @@ -653,6 +655,8 @@ cdef class Subtract(MathsBinaryOperation):
program.sub()

cdef Expression constant_left(self, Vector left, Expression right):
if left.eq(null_):
return NoOp
if left.eq(false_):
return Negative(right)

Expand All @@ -674,6 +678,8 @@ cdef class Multiply(MathsBinaryOperation):
program.mul()

cdef Expression constant_left(self, Vector left, Expression right):
if left.eq(null_):
return NoOp
if left.eq(true_):
return Positive(right)
if left.eq(minusone_):
Expand Down Expand Up @@ -707,7 +713,13 @@ cdef class Divide(MathsBinaryOperation):
cdef void _compile_op(self, Program program):
program.truediv()

cdef Expression constant_left(self, Vector left, Expression right):
if left.eq(null_):
return NoOp

cdef Expression constant_right(self, Expression left, Vector right):
if right.eq(null_):
return NoOp
if right.eq(true_):
return Positive(left)
return Multiply(Literal(true_.truediv(right)), left)
Expand All @@ -720,7 +732,13 @@ cdef class FloorDivide(MathsBinaryOperation):
cdef void _compile_op(self, Program program):
program.floordiv()

cdef Expression constant_left(self, Vector left, Expression right):
if left.eq(null_):
return NoOp

cdef Expression constant_right(self, Expression left, Vector right):
if right.eq(null_):
return NoOp
if right.eq(true_):
return Floor(left)

Expand All @@ -732,7 +750,13 @@ cdef class Modulo(MathsBinaryOperation):
cdef void _compile_op(self, Program program):
program.mod()

cdef Expression constant_left(self, Vector left, Expression right):
if left.eq(null_):
return NoOp

cdef Expression constant_right(self, Expression left, Vector right):
if right.eq(null_):
return NoOp
if right.eq(true_):
return Fract(left)

Expand All @@ -750,7 +774,13 @@ cdef class Power(MathsBinaryOperation):
else:
program.pow()

cdef Expression constant_left(self, Vector left, Expression right):
if left.eq(null_):
return NoOp

cdef Expression constant_right(self, Expression left, Vector right):
if right.eq(null_):
return NoOp
if right.eq(true_):
return Positive(left)

Expand Down
35 changes: 35 additions & 0 deletions tests/test_simplifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ def test_literal(self):
"""Literal left and right is evaluated"""
self.assertSimplifiesTo(Add(Literal(5), Literal(10)), Literal(15))

def test_null(self):
"""Literal null on either side simplifies to null"""
self.assertSimplifiesTo(Add(Name('x'), Literal(null)), Literal(null), dynamic={'x'})
self.assertSimplifiesTo(Add(Literal(null), Name('x')), Literal(null), dynamic={'x'})

def test_zero(self):
"""Adding literal zero becomes Positive"""
self.assertSimplifiesTo(Add(Literal(0), Name('x')), Positive(Name('x')), dynamic={'x'})
Expand Down Expand Up @@ -312,6 +317,11 @@ def test_literal(self):
"""Literal left and right is evaluated"""
self.assertSimplifiesTo(Subtract(Literal(5), Literal(10)), Literal(-5))

def test_null(self):
"""Literal null on either side simplifies to null"""
self.assertSimplifiesTo(Subtract(Name('x'), Literal(null)), Literal(null), dynamic={'x'})
self.assertSimplifiesTo(Subtract(Literal(null), Name('x')), Literal(null), dynamic={'x'})

def test_subtract_zero(self):
"""Subtracting literal zero becomes Positive"""
self.assertSimplifiesTo(Subtract(Name('x'), Literal(0)), Positive(Name('x')), dynamic={'x'})
Expand Down Expand Up @@ -339,6 +349,11 @@ def test_literal(self):
"""Literal left and right is evaluated"""
self.assertSimplifiesTo(Multiply(Literal(5), Literal(10)), Literal(50))

def test_null(self):
"""Literal null on either side simplifies to null"""
self.assertSimplifiesTo(Multiply(Name('x'), Literal(null)), Literal(null), dynamic={'x'})
self.assertSimplifiesTo(Multiply(Literal(null), Name('x')), Literal(null), dynamic={'x'})

def test_multiply_one(self):
"""Multiplying by literal 1 becomes Positive"""
self.assertSimplifiesTo(Multiply(Name('x'), Literal(1)), Positive(Name('x')), dynamic={'x'})
Expand Down Expand Up @@ -394,6 +409,11 @@ def test_literal(self):
"""Literal left and right is evaluated"""
self.assertSimplifiesTo(Divide(Literal(5), Literal(10)), Literal(0.5))

def test_null(self):
"""Literal null on either side simplifies to null"""
self.assertSimplifiesTo(Divide(Name('x'), Literal(null)), Literal(null), dynamic={'x'})
self.assertSimplifiesTo(Divide(Literal(null), Name('x')), Literal(null), dynamic={'x'})

def test_divide_by_one(self):
"""Dividing by literal 1 becomes Positive"""
self.assertSimplifiesTo(Divide(Name('x'), Literal(1)), Positive(Name('x')), dynamic={'x'})
Expand All @@ -417,6 +437,11 @@ def test_literal(self):
"""Literal left and right is evaluated"""
self.assertSimplifiesTo(FloorDivide(Literal(5), Literal(10)), Literal(0))

def test_null(self):
"""Literal null on either side simplifies to null"""
self.assertSimplifiesTo(FloorDivide(Name('x'), Literal(null)), Literal(null), dynamic={'x'})
self.assertSimplifiesTo(FloorDivide(Literal(null), Name('x')), Literal(null), dynamic={'x'})

def test_divide_by_one(self):
"""Dividing by literal 1 becomes Floor"""
self.assertSimplifiesTo(FloorDivide(Name('x'), Literal(1)), Floor(Name('x')), dynamic={'x'})
Expand All @@ -436,6 +461,11 @@ def test_literal(self):
"""Literal left and right is evaluated"""
self.assertSimplifiesTo(Modulo(Literal(5), Literal(10)), Literal(5))

def test_null(self):
"""Literal null on either side simplifies to null"""
self.assertSimplifiesTo(Modulo(Name('x'), Literal(null)), Literal(null), dynamic={'x'})
self.assertSimplifiesTo(Modulo(Literal(null), Name('x')), Literal(null), dynamic={'x'})

def test_modulo_one(self):
"""Modulo literal 1 becomes Fract"""
self.assertSimplifiesTo(Modulo(Name('x'), Literal(1)), Fract(Name('x')), dynamic={'x'})
Expand All @@ -455,6 +485,11 @@ def test_literal(self):
"""Literal left and right is evaluated"""
self.assertSimplifiesTo(Power(Literal(5), Literal(2)), Literal(25))

def test_null(self):
"""Literal null on either side simplifies to null"""
self.assertSimplifiesTo(Power(Name('x'), Literal(null)), Literal(null), dynamic={'x'})
self.assertSimplifiesTo(Power(Literal(null), Name('x')), Literal(null), dynamic={'x'})

def test_raise_to_power_of_one(self):
"""Power to literal 1 becomes Positive"""
self.assertSimplifiesTo(Power(Name('x'), Literal(1)), Positive(Name('x')), dynamic={'x'})
Expand Down

0 comments on commit fccb9d9

Please sign in to comment.