def testSubscript(self): self.assertQueryMatches("d['foo']", ast.Select(ast.Var("d"), ast.Literal("foo"))) self.assertQueryMatches( "d['foo'] + 10", ast.Sum(ast.Select(ast.Var("d"), ast.Literal("foo")), ast.Literal(10))) self.assertQueryMatches( "obj.props[0]", ast.Select(ast.Resolve(ast.Var("obj"), ast.Literal("props")), ast.Literal(0))) self.assertQueryMatches( "obj.props[0].foo", ast.Resolve( ast.Select(ast.Resolve(ast.Var("obj"), ast.Literal("props")), ast.Literal(0)), ast.Literal("foo"))) self.assertQueryMatches( "obj.props[10 + 10].foo", ast.Resolve( ast.Select(ast.Resolve(ast.Var("obj"), ast.Literal("props")), ast.Sum(ast.Literal(10), ast.Literal(10))), ast.Literal("foo"))) self.assertQueryMatches( "w['x'][y[5] + 5] * 10", ast.Product( ast.Select( ast.Select(ast.Var("w"), ast.Literal("x")), ast.Sum(ast.Select(ast.Var("y"), ast.Literal(5)), ast.Literal(5))), ast.Literal(10)))
def testLet(self): self.assertEqual( solve.solve( ast.Let(ast.Bind(ast.Pair(ast.Literal("x"), ast.Literal(5))), ast.Sum(ast.Var("x"), ast.Var("x"))), {}).value, [10]) # Previous binding should be made available to subsequent bindings. self.assertEqual( solve.solve( ast.Let( ast.Bind( ast.Pair(ast.Literal("x"), ast.Literal(5)), ast.Pair(ast.Literal("y"), ast.Sum(ast.Var("x"), ast.Literal(5)))), ast.Var("y")), {}).value, [10])
def testMixfix(self): self.assertQueryParses("'foo'[0 ]", ast.Select(ast.Literal("foo"), ast.Literal(0))) self.assertQueryParses( # I refer you to my previous statement about making sense. " (5 +5) [ 'foo']", ast.Select(ast.Sum(ast.Literal(5), ast.Literal(5)), ast.Literal("foo"))) self.assertQueryParses( "5 + 5['foo' + 10]", ast.Sum( ast.Literal(5), ast.Select(ast.Literal(5), ast.Sum(ast.Literal("foo"), ast.Literal(10)))))
def testPrefix(self): self.assertQueryParses( "-5 + 5 eq - (10)", ast.Equivalence( ast.Sum(ast.Product(ast.Literal(-1), ast.Literal(5)), ast.Literal(5)), ast.Product(ast.Literal(-1), ast.Literal(10))))
def testParens(self): # Base case. self.assertQueryMatches( "x + y * z", ast.Sum(ast.Var("x"), ast.Product(ast.Var("y"), ast.Var("z")))) # With parens. self.assertQueryMatches( "(x + y) * z", ast.Product(ast.Sum(ast.Var("x"), ast.Var("y")), ast.Var("z"))) # Missing rparen. self.assertQueryRaises("(x + y") # Empty expressions make no sense. self.assertQueryRaises("()")
def testInfix(self): self.assertQueryMatches("x + y", ast.Sum(ast.Var("x"), ast.Var("y"))) self.assertQueryMatches( "w.x.y.z", ast.Resolve( ast.Resolve(ast.Resolve(ast.Var("w"), ast.Literal("x")), ast.Literal("y")), ast.Literal("z"))) # Operator precedence should work correctly. self.assertQueryMatches( "x + y * z", ast.Sum(ast.Var("x"), ast.Product(ast.Var("y"), ast.Var("z")))) self.assertQueryMatches( "x * y + z", ast.Sum(ast.Product(ast.Var("x"), ast.Var("y")), ast.Var("z")))
def testCircumfix(self): self.assertQueryParses( "[1, 2, 3]", ast.Tuple(ast.Literal(1), ast.Literal(2), ast.Literal(3))) self.assertQueryParses( # Lists and selection are non-ambiguous. "10 + ['foo', 'bar'][1]", ast.Sum( ast.Literal(10), ast.Select(ast.Tuple(ast.Literal("foo"), ast.Literal("bar")), ast.Literal(1))))
def testLet(self): self.assertQueryMatches( "let x = 5, y = 10 x + y", ast.Let( ast.Bind(ast.Pair(ast.Literal("x"), ast.Literal(5)), ast.Pair(ast.Literal("y"), ast.Literal(10))), ast.Sum(ast.Var("x"), ast.Var("y")))) self.assertQueryMatches( "let( (x = 5 - 3,y=(10+(10)) ) )x + y", ast.Let( ast.Bind( ast.Pair(ast.Literal("x"), ast.Difference(ast.Literal(5), ast.Literal(3))), ast.Pair(ast.Literal("y"), ast.Sum(ast.Literal(10), ast.Literal(10)))), ast.Sum(ast.Var("x"), ast.Var("y")))) self.assertQueryRaises("let x = 5) x + 5") self.assertQueryRaises("let ((x = 5) x + 5") self.assertQueryRaises("let (x = 5)) x + 5") self.assertQueryRaises("let (x = 5 x + 5") self.assertQueryRaises("let (x = 5)")
def testOperatorPrecedence(self): # Prefix operator, like the unary minus sign, should respect operator # precedence order. self.assertQueryMatches( "-x + y", ast.Sum(ast.Product(ast.Literal(-1), ast.Var("x")), ast.Var("y"))) self.assertQueryMatches( "not x and y", ast.Intersection(ast.Complement(ast.Var("x")), ast.Var("y"))) self.assertQueryMatches( "x / -f(y) or not z(a, b)", ast.Union( ast.Quotient( ast.Var("x"), ast.Product(ast.Literal(-1), ast.Apply(ast.Var("f"), ast.Var("y")))), ast.Complement( ast.Apply(ast.Var("z"), ast.Var("a"), ast.Var("b")))))
def testIfElse(self): self.assertQueryMatches( "if true then 'foo'", ast.IfElse(ast.Literal(True), ast.Literal("foo"), ast.Literal(None))) self.assertQueryMatches( "if true then 'foo' else 'bar'", ast.IfElse(ast.Literal(True), ast.Literal("foo"), ast.Literal("bar"))) self.assertQueryMatches( "if true then 'foo' else if 5 + 5 then 'bar' else 'baz'", ast.IfElse(ast.Literal(True), ast.Literal("foo"), ast.Sum(ast.Literal(5), ast.Literal(5)), ast.Literal("bar"), ast.Literal("baz"))) # Missing then blows up: self.assertQueryRaises("if (true) bar") # Colon blows up: self.assertQueryRaises("if true: bar")
def testParens(self): self.assertQueryParses( "5 + (5 eq 10)", # It doesn't have to make sense. ast.Sum(ast.Literal(5), ast.Equivalence(ast.Literal(5), ast.Literal(10))))
def testInfix(self): self.assertQueryParses( "5 + 5 eq 10", ast.Equivalence(ast.Sum(ast.Literal(5), ast.Literal(5)), ast.Literal(10)))