From 705c95ee286bf7071e1692c63665a17d93aa0b6d Mon Sep 17 00:00:00 2001 From: Simon Forman Date: Mon, 25 Jun 2018 12:43:05 -0700 Subject: [PATCH] Fix a couple of bug in Kleene Stars. ...and some minor cleanup. --- joy/utils/polytypes.py | 9 ++- joy/utils/types.py | 2 +- test/test_type_inference.py | 157 ++++++++++++++++++++++++------------ 3 files changed, 112 insertions(+), 56 deletions(-) diff --git a/joy/utils/polytypes.py b/joy/utils/polytypes.py index 272b512..335f38b 100644 --- a/joy/utils/polytypes.py +++ b/joy/utils/polytypes.py @@ -56,6 +56,7 @@ class KleeneStar(object): kind = AnyJoyType def __init__(self, number): + assert number self.number = number self.count = 0 self.prefix = repr(self) @@ -129,8 +130,10 @@ def unify(u, v, s=None): a, b = v if isinstance(a, KleeneStar): - if isinstance(b, KleeneStar): - return _lil_uni(a, b, s) + c, d = u + if isinstance(c, KleeneStar): + s = _lil_uni(a, c, s) # Attempt to unify the two K-stars. + return unify(d, b, s[0]) # Two universes, in one the Kleene star disappears and unification # continues without it... @@ -247,7 +250,7 @@ s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 = S f0, f1, f2, f3, f4, f5, f6, f7, f8, f9 = F = map(FloatJoyType, _R) i0, i1, i2, i3, i4, i5, i6, i7, i8, i9 = I = map(IntJoyType, _R) - +_R = range(1, 11) As = map(AnyStarJoyType, _R) Ns = map(NumberStarJoyType, _R) Ss = map(StackStarJoyType, _R) diff --git a/joy/utils/types.py b/joy/utils/types.py index 477155b..1bcd73b 100644 --- a/joy/utils/types.py +++ b/joy/utils/types.py @@ -78,7 +78,7 @@ def delabel(f, seen=None, c=None): if not isinstance(f, tuple): try: - seen[f] = f.__class__(c[f.prefix]) + seen[f] = f.__class__(c[f.prefix] + 1) except TypeError: # FunctionJoyTypes break this. seen[f] = f else: diff --git a/test/test_type_inference.py b/test/test_type_inference.py index 87b61fa..a60fa54 100644 --- a/test/test_type_inference.py +++ b/test/test_type_inference.py @@ -11,53 +11,37 @@ infr = lambda e: infer(__(e)) globals().update(FUNCTIONS) -class TestKleeneStar(unittest.TestCase): +class TestMixin(object): + + def assertEqualTypeStructure(self, a, b): + # Check shape and types match. + self.assert_(a >= b) + self.assert_(b >= a) + # Check type variables match expected pattern. + self._compare_structures(a, b) + + def _compare_structures(self, a, b, seen=None): + # Sometimes we change ONLY the "number" attr of our type vars. + # We need to make sure the patterns still match our expected, uh, + # patterns. + if seen is None: + seen = {} + self.assertEqual(type(a), type(b)) + if isinstance(a, (tuple, list)): + self.assertEqual(len(a), len(b)) + for aa, bb in zip(a, b): + self._compare_structures(aa, bb, seen) + else: + if a in seen: + self.assertIs(b, seen[a]) + seen[a] = b + + +class TestCombinators(TestMixin, unittest.TestCase): # def setUp(self): # def tearDown(self): - def test_infra(self): - expression = [ - __((n1, n2, n3), s1), # Three numbers in a stack. - (mul, s2), - infra - ] - f = (s0, ((n0, (n1, s1)), s2)) - # (-- [n0 n1 ...1]) Two numbers in a stack. - self.assertEqual(infr(expression), [f]) - - def test_sum(self): - expression = [ - __((n1, n2, n3), s1), # Three numbers in a stack. - sum, # builtin shadowed by SymbolJoyType - ] - # A function that puts a single number on the stack. - f = s0, (n0, s0) - self.assertEqual(infr(expression), [f]) - - def test_Yin(self): - expression = pop, swap, rolldown, rest, rest, cons, cons - # ([a3 a4 ...0] a2 a1 a0 -- [a1 a2 ...0]) - f = (a0, (a1, (a2, ((a3, (a4, s0)), s1)))), ((a1, (a2, s0)), s1) - self.assertEqual(infr(expression), [f]) - - def test_cons_dip(self): - expression = a1, (cons, s0), dip # a1 [cons] dip - # (a0 [...0] -- [a0 ...0] a1) - f = ((s0, (a0, s1)), (a1, ((a0, s0), s1))) - self.assertEqual(infr(expression), [f]) - - def test_cons_dipd(self): - expression = a1, a3, (cons, s0), dipd - f = ((s0, (a0, s1)), (a1, (a2, ((a0, s0), s1)))) - # (a0 [...0] -- [a0 ...0] a2 a1) - self.assertEqual(infr(expression), [f]) - - def test_i(self): - # [cons] i == cons - expression = (cons, s0), i - self.assertEqual(infr(expression), infr([cons])) - def test_branch(self): ''' a1 [dup] [cons] branch @@ -67,25 +51,94 @@ class TestKleeneStar(unittest.TestCase): ((s0, (a0, s1)), ((a0, s0), s1)), # (a0 [...0] -- [a0 ...0]) ((a0, s0), (a0, (a0, s0))), # (a0 -- a0 a0) ] - self.assertEqual(infr(expression), f) + self.assertEqualTypeStructure(infr(expression), f) - def test_swaack(self): - expression = a0, (a1, s0), swaack - f = (s0, ((a0, s0), (a1, s1))) # (-- a1 [a0 ...0]) - self.assertEqual(infr(expression), [f]) + def test_cons_dip(self): + expression = a1, (cons, s0), dip # a1 [cons] dip + # (a0 [...0] -- [a0 ...0] a1) + f = ((s0, (a0, s1)), (a1, ((a0, s0), s1))) + self.assertEqualTypeStructure(infr(expression), [f]) + + def test_cons_dipd(self): + expression = a1, a3, (cons, s0), dipd + f = ((s0, (a0, s1)), (a1, (a2, ((a0, s0), s1)))) + # (a0 [...0] -- [a0 ...0] a2 a1) + self.assertEqualTypeStructure(infr(expression), [f]) + + def test_i(self): + # [cons] i == cons + expression = (cons, s0), i + self.assertEqualTypeStructure(infr(expression), infr([cons])) + + def test_infra(self): + expression = [ + __((n1, n2, n3), s1), # Three numbers in a stack. + (mul, s2), + infra + ] + f = (s0, ((n0, (n1, s1)), s2)) + # (-- [n0 n1 ...1]) Two numbers in a stack. + self.assertEqualTypeStructure(infr(expression), [f]) def test_x(self): expression = (a1, (swap, ((dup, s2), (dip, s0)))), x f = (s0, ((a0, (swap, ((dup, s1), (dip, s2)))), (a1, (a1, s0)))) # (-- a1 a1 [a0 swap [dup ...1] dip ...2]) - self.assertEqual(infr(expression), [f]) + self.assertEqualTypeStructure(infr(expression), [f]) + +class TestKleeneStar(TestMixin, unittest.TestCase): + + def test_Astar(self): + expression = a1, As[2], a2, cons + f = [ # Vanish in one, spawn a new variable in the other... + (s1, ((a1, s2), s1)), # (-- [a1 ...2]) + (s1, ((a1, s2), (As[1], (a2, s1)))), # (-- a2 a1* [a1 ...2]) + ] + self.assertEqualTypeStructure(infr(expression), f) + + def test_sum(self): + expression = [ + __((n1, n2, n3), s1), # Three numbers in a stack. + sum, # builtin shadowed by SymbolJoyType + ] + # A function that puts a single number on the stack. + f = s0, (n0, s0) + self.assertEqualTypeStructure(infr(expression), [f]) + + def test_no_infinite_loop(self): + expression = [ + (Ns[2], s1), # A stack of numbers. + sum, # builtin shadowed by SymbolJoyType. + ] + # A function that puts a single number on the stack. + f = s0, (n0, s0) + self.assertEqualTypeStructure(infr(expression), [f]) + + +class TestYin(TestMixin, unittest.TestCase): + + def test_MiscYin(self): + expression = pop, swap, rolldown, rest, rest, cons, cons + # ([a3 a4 ...0] a2 a1 a0 -- [a1 a2 ...0]) + f = (a0, (a1, (a2, ((a3, (a4, s0)), s1)))), ((a1, (a2, s0)), s1) + self.assertEqualTypeStructure(infr(expression), [f]) + + def test_swaack(self): + expression = a0, (a1, s0), swaack + f = (s0, ((a0, s0), (a1, s1))) # (-- a1 [a0 ...0]) + self.assertEqualTypeStructure(infr(expression), [f]) + + +## def test_(self): +## expression = pop, swap, rolldown, rest, rest, cons, cons +## f = ## for sec in infr(expression): ## print sec, doc_from_stack_effect(*sec) +## self.assertEqualTypeStructure(infr(expression), [f]) - -##for g in MC(dup, mul): -## print doc_from_stack_effect(*g) +## for g in MC(dup, mul): +## print doc_from_stack_effect(*g) if __name__ == '__main__':