def test_user_operators(op): assume(not op.startswith("--")) assert ( parse(f"a {op} b") == parse(f"({op}) a b") == Application(Application(Identifier(op), Identifier("a")), Identifier("b")) )
def test_wfe_float_literals(n): code = f"{n!r:.60}" assert parse(code) == Literal(n, NumberType) assert parse("1_000_.500") == Literal(1000.5, NumberType) assert parse("_1e+10") == Application( Application(Identifier("+"), Identifier("_1e")), Literal(10, NumberType) ) with pytest.raises(LarkError): tokenize("_0.1")
def test_list_cons_operator(): assert ( parse("a:b:xs") == parse("a:(b:xs)") == Application( Application(Identifier(":"), Identifier("a")), Application( Application(Identifier(":"), Identifier("b")), Identifier("xs") ), ) )
def test_annotated_numbers(): assert parse("1e+10@km") == Literal(1e10, NumberType, Identifier("km")) assert parse('1e+10@"km"') == Literal(1e10, NumberType, Literal("km", StringType)) assert parse("1e+10@'m'") == Literal(1e10, NumberType, Literal("m", CharType)) assert parse("1e+10 @ km") == Application( Application(Identifier("@"), Literal(1e10, NumberType)), Identifier("km") ) assert parse("a @ b") == Application( Application(Identifier("@"), Identifier("a")), Identifier("b") )
def test_pattern_matching_let(): code = """let if True t f = t if False t f = f in g . if""" result = parse(code) expected = ConcreteLet( [ Equation("if", [ConsPattern("True"), "t", "f"], Identifier("t")), Equation("if", [ConsPattern("False"), "t", "f"], Identifier("f")), ], Application(Application(Identifier("."), Identifier("g")), Identifier("if")), ) assert result == expected
def test_wfe_composition(): assert ( parse("a . b") == parse("(.) a b") == Application(Application(Identifier("."), Identifier("a")), Identifier("b")) ) assert parse("a . b . c") == parse("a . (b . c)") # yay! partial application assert parse("((.) a) b") == parse("a . b") assert parse("(.)a") == Application(Identifier("."), Identifier("a")) # Application is stronger than composition assert parse("a . b c") == parse("a . (b c)")
def test_datetime_literals_application(d): code = f"f <{d!s}>" res = parse(code) assert res == Application(Identifier("f"), Literal(d, DateTimeType)) code = f"f <{d!s}> x" res = parse(code) assert res == Application( Application(Identifier("f"), Literal(d, DateTimeType)), Identifier("x") ) # Semantically incorrect but parser must accept it code = f"<{d!s}> x" res = parse(code) assert res == Application(Literal(d, DateTimeType), Identifier("x"))
def compile(self) -> AST: """Return the compiled form of the function definition. """ from xotl.fl.ast.expressions import build_lambda, build_application # This is similar to the function `match` in 5.2 of [PeytonJones1987]; # but I want to avoid *enlarging* simple functions needlessly. if self.arity: vars = list(namesupply(f".{self.name}_arg", limit=self.arity)) body: AST = NO_MATCH_ERROR for eq in self.equations: dfn = eq.body patterns: Iterable[Tuple[str, Pattern]] = zip(vars, eq.patterns) for var, pattern in patterns: if isinstance(pattern, str): # Our algorithm is trivial but comes with a cost: # ``id x = x`` is must be translated to # ``id = \.id_arg0 -> (\x -> x) .id_arg0``. dfn = Application(Lambda(pattern, dfn), Identifier(var)) elif isinstance(pattern, Literal): # ``fib 0 = 1``; is transformed to # ``fib = \.fib_arg0 -> <MatchLiteral 0> .fib_arg0 1`` dfn = build_application( Identifier(MatchLiteral(pattern)), # type: ignore Identifier(var), dfn, ) elif isinstance(pattern, ConsPattern): if not pattern.params: # This is just a Match; similar to MatchLiteral dfn = build_application( Identifier(Match( pattern.cons)), # type: ignore Identifier(var), dfn, ) else: for i, param in reversed( list(enumerate(pattern.params, 1))): if isinstance(param, str): dfn = build_application( Identifier(Extract(pattern.cons, i)), # type: ignore Identifier(var), Lambda(param, dfn), ) else: raise NotImplementedError( f"Nested patterns {param}") else: assert False body = build_lambda( vars, build_application(MATCH_OPERATOR, dfn, body)) return body else: # This should be a simple value, so we return the body of the # first equation. return self.equations[0].body # type: ignore
def test_comma_as_an_operator(): assert ( parse("(a, b)") == parse("(,) a b") == Application(Application(Identifier(","), Identifier("a")), Identifier("b")) )
def test_wfe_application(): assert parse("a b") == Application(Identifier("a"), Identifier("b")) assert parse("a b c") == parse("(a b) c") assert parse("(a)(b)") == parse("(a)b") == parse("a(b)") == parse("a b") assert parse("a b") == parse("(a b)")