{-# LANGUAGE OverloadedStrings #-} module Jikka.RestrictedPython.Convert.AlphaSpec ( spec, ) where import Jikka.Common.Alpha import Jikka.Common.Error import Jikka.RestrictedPython.Convert.Alpha (run) import Jikka.RestrictedPython.Language.Expr import Jikka.RestrictedPython.Language.WithoutLoc import Test.Hspec run' :: Program -> Either Error Program run' = flip evalAlphaT 0 . run spec :: Spec spec = describe "run" $ do it "works" $ do let parsed = [ ToplevelFunctionDef "f" [("n", IntTy)] IntTy [ If (eqExp (VarTy "t") (name "n") (constIntExp 0)) [ Return (constIntExp 1) ] [ Return (multExp (name "n") (call (name "f") [subExp (name "n") (constIntExp 1)])) ] ], ToplevelFunctionDef "solve" [("n", IntTy)] IntTy [ Return (binOp (call (name "f") [name "n"]) FloorMod (constIntExp 1000000007)) ] ] let expected = [ ToplevelFunctionDef "f" [("n$0", IntTy)] IntTy [ If (eqExp (VarTy "t") (name "n$0") (constIntExp 0)) [ Return (constIntExp 1) ] [ Return (multExp (name "n$0") (call (name "f") [subExp (name "n$0") (constIntExp 1)])) ] ], ToplevelFunctionDef "solve" [("n$1", IntTy)] IntTy [ Return (binOp (call (name "f") [name "n$1"]) FloorMod (constIntExp 1000000007)) ] ] run' parsed `shouldBe` Right expected it "fails with undefined variables" $ do let parsed = [ ToplevelFunctionDef "solve" [] IntTy [ Return (name "y") ] ] let expected = WithWrapped "Jikka.RestrictedPython.Convert.Alpha" (WithGroup SymbolError (Error "undefined identifier: y")) run' parsed `shouldBe` Left expected it "doesn't rename builtin functions " $ do let parsed = [ ToplevelFunctionDef "solve" [] IntTy [ Return (name "range") ] ] let expected = [ ToplevelFunctionDef "solve" [] IntTy [ Return (name "range") ] ] run' parsed `shouldBe` Right expected it "distinguishes local variables in two diffrent functions" $ do let parsed = [ ToplevelFunctionDef "foo" [("x", IntTy)] IntTy [ AnnAssign (nameTrg "y") IntTy (name "x") ], ToplevelFunctionDef "bar" [("x", IntTy)] IntTy [ AnnAssign (nameTrg "y") IntTy (name "x") ] ] let expected = [ ToplevelFunctionDef "foo" [("x$0", IntTy)] IntTy [ AnnAssign (nameTrg "y$1") IntTy (name "x$0") ], ToplevelFunctionDef "bar" [("x$2", IntTy)] IntTy [ AnnAssign (nameTrg "y$3") IntTy (name "x$2") ] ] run' parsed `shouldBe` Right expected it "distinguishes variables in two diffrent for-loops" $ do let parsed = [ ToplevelFunctionDef "solve" [] IntTy [ For (nameTrg "i") (list IntTy []) [ AnnAssign (nameTrg "x") IntTy (name "i") ], For (nameTrg "i") (list IntTy []) [ AnnAssign (nameTrg "x") IntTy (name "i") ] ] ] let expected = [ ToplevelFunctionDef "solve" [] IntTy [ For (nameTrg "i$0") (list IntTy []) [ AnnAssign (nameTrg "x$1") IntTy (name "i$0") ], For (nameTrg "i$2") (list IntTy []) [ AnnAssign (nameTrg "x$3") IntTy (name "i$2") ] ] ] run' parsed `shouldBe` Right expected it "removes underscoes" $ do let parsed = [ ToplevelAnnAssign "a" (ListTy IntTy) ( listComp (constIntExp 0) ( Comprehension (nameTrg "_") (call (name "range") [constIntExp 10]) Nothing ) ) ] let expected = [ ToplevelAnnAssign "a" (ListTy IntTy) ( listComp (constIntExp 0) ( Comprehension (nameTrg "$0") (call (name "range") [constIntExp 10]) Nothing ) ) ] run' parsed `shouldBe` Right expected it "works on recursive functions" $ do let parsed = [ ToplevelFunctionDef "f" [("x", IntTy)] IntTy [ Return (call (name "f") [name "x"]) ] ] let expected = [ ToplevelFunctionDef "f" [("x$0", IntTy)] IntTy [ Return (call (name "f") [name "x$0"]) ] ] run' parsed `shouldBe` Right expected it "doesn't rename type variables" $ do let parsed = [ ToplevelFunctionDef "f" [("x", VarTy "x")] (VarTy "f") [ Return (call (name "f") [name "x"]) ] ] let expected = [ ToplevelFunctionDef "f" [("x$0", VarTy "x")] (VarTy "f") [ Return (call (name "f") [name "x$0"]) ] ] run' parsed `shouldBe` Right expected it "makes repeated assignments for the same variable to single-assignments for different variables" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x") ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x$0") IntTy (constIntExp 0), AnnAssign (nameTrg "x$1") IntTy (name "x$0"), AnnAssign (nameTrg "x$2") IntTy (constIntExp 0), AnnAssign (nameTrg "x$3") IntTy (name "x$2"), AnnAssign (nameTrg "x$4") IntTy (constIntExp 0), AnnAssign (nameTrg "x$5") IntTy (name "x$4") ] ] run' parsed `shouldBe` Right expected it "doesn't rename for augumented assignments" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), AugAssign (nameTrg "x") Add (constIntExp 0), AugAssign (nameTrg "x") Add (name "x"), AugAssign (nameTrg "x") Add (constIntExp 0), AugAssign (nameTrg "x") Add (name "x") ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x$0") IntTy (constIntExp 0), AnnAssign (nameTrg "x$1") IntTy (name "x$0"), AugAssign (nameTrg "x$1") Add (constIntExp 0), AugAssign (nameTrg "x$1") Add (name "x$1"), AugAssign (nameTrg "x$1") Add (constIntExp 0), AugAssign (nameTrg "x$1") Add (name "x$1") ] ] run' parsed `shouldBe` Right expected it "blames leaks of loop counters of for-statements" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ For (nameTrg "i") (list IntTy []) [ Return (name "i") ], Return (name "i") ] ] let expected = WithWrapped "Jikka.RestrictedPython.Convert.Alpha" (WithGroup SymbolError (Error "undefined identifier: i")) run' parsed `shouldBe` Left expected it "blames leaks of loop counters of for-statements even if variables with the same names are defined" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "i") IntTy (constIntExp 0), For (nameTrg "i") (list IntTy []) [ Return (name "i") ], Return (name "i") ] ] let expected = WithWrapped "Jikka.RestrictedPython.Convert.Alpha" (WithGroup SemanticError (Error "cannot redefine variable: i")) run' parsed `shouldBe` Left expected it "blames undefined variables which will be defined in the rest of the same loop" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ For (nameTrg "i") (list IntTy []) [ Return (name "a"), AnnAssign (nameTrg "a") IntTy (constIntExp 0) ] ] ] let expected = WithWrapped "Jikka.RestrictedPython.Convert.Alpha" (WithGroup SymbolError (Error "undefined identifier: a")) run' parsed `shouldBe` Left expected it "blames using variables which are defined in only one branch of if-statement" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ If (constBoolExp True) [ AnnAssign (nameTrg "a") IntTy (constIntExp 0) ] [], Return (name "a") ] ] let expected = WithWrapped "Jikka.RestrictedPython.Convert.Alpha" (WithGroup SymbolError (Error "undefined identifier: a")) run' parsed `shouldBe` Left expected it "works with variables which are defined in both branches of if-statement" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ If (constBoolExp True) [ AnnAssign (nameTrg "a") IntTy (constIntExp 0) ] [ AnnAssign (nameTrg "a") IntTy (constIntExp 1) ], Return (name "a") ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ If (constBoolExp True) [ AnnAssign (nameTrg "a$0") IntTy (constIntExp 0) ] [ AnnAssign (nameTrg "a$0") IntTy (constIntExp 1) ], Return (name "a$0") ] ] run' parsed `shouldBe` Right expected it "doesn't leak loop counters of for-exprs" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "i") IntTy (constIntExp 0), AnnAssign (nameTrg "a") (ListTy IntTy) (listComp (constIntExp 0) (Comprehension (nameTrg "i") (list IntTy []) Nothing)), Return (name "i") ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "i$0") IntTy (constIntExp 0), AnnAssign (nameTrg "a$2") (ListTy IntTy) (listComp (constIntExp 0) (Comprehension (nameTrg "i$1") (list IntTy []) Nothing)), Return (name "i$0") ] ] run' parsed `shouldBe` Right expected it "works with if-statements without else-clause" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), If (name "x") [ AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "y") IntTy (constIntExp 0), AnnAssign (nameTrg "y") IntTy (name "y"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x") ] [], AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x") ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x$0") IntTy (constIntExp 0), AnnAssign (nameTrg "x$1") IntTy (name "x$0"), AnnAssign (nameTrg "x$2") IntTy (constIntExp 0), AnnAssign (nameTrg "x$3") IntTy (name "x$2"), If (name "x$3") [ AnnAssign (nameTrg "x$3") IntTy (constIntExp 0), AnnAssign (nameTrg "x$3") IntTy (name "x$3"), AnnAssign (nameTrg "y$4") IntTy (constIntExp 0), AnnAssign (nameTrg "y$5") IntTy (name "y$4"), AnnAssign (nameTrg "x$3") IntTy (constIntExp 0), AnnAssign (nameTrg "x$3") IntTy (name "x$3") ] [], AnnAssign (nameTrg "x$6") IntTy (name "x$3"), AnnAssign (nameTrg "x$7") IntTy (constIntExp 0), AnnAssign (nameTrg "x$8") IntTy (name "x$7") ] ] run' parsed `shouldBe` Right expected it "works with if-statements with else-clause" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), If (name "x") [ AugAssign (nameTrg "x") Add (name "x") ] [ AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (name "x") ], AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x") ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x$0") IntTy (constIntExp 0), AnnAssign (nameTrg "x$1") IntTy (name "x$0"), If (name "x$1") [ AugAssign (nameTrg "x$1") Add (name "x$1") ] [ AnnAssign (nameTrg "x$1") IntTy (name "x$1"), AnnAssign (nameTrg "x$1") IntTy (name "x$1") ], AnnAssign (nameTrg "x$2") IntTy (name "x$1"), AnnAssign (nameTrg "x$3") IntTy (constIntExp 0), AnnAssign (nameTrg "x$4") IntTy (name "x$3") ] ] run' parsed `shouldBe` Right expected it "isn't confused by conflicts between variables in if-statements and variables in toplevel" $ do let parsed = [ ToplevelFunctionDef "x" [] IntTy [ Return (constIntExp 0) ], ToplevelFunctionDef "main" [] IntTy [ If (constBoolExp True) [ AnnAssign (nameTrg "x") IntTy (constIntExp 0) ] [ AnnAssign (nameTrg "x") IntTy (constIntExp 1) ], AnnAssign (nameTrg "x") IntTy (name "x") ] ] let expected = [ ToplevelFunctionDef "x" [] IntTy [ Return (constIntExp 0) ], ToplevelFunctionDef "main" [] IntTy [ If (constBoolExp True) [ AnnAssign (nameTrg "x$0") IntTy (constIntExp 0) ] [ AnnAssign (nameTrg "x$0") IntTy (constIntExp 1) ], AnnAssign (nameTrg "x$1") IntTy (name "x$0") ] ] run' parsed `shouldBe` Right expected it "works with for-loops" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), For (nameTrg "i") (list IntTy []) [ AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "y") IntTy (constIntExp 0), AnnAssign (nameTrg "y") IntTy (name "y"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x") ], AnnAssign (nameTrg "x") IntTy (name "x"), AnnAssign (nameTrg "x") IntTy (constIntExp 0), AnnAssign (nameTrg "x") IntTy (name "x") ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "x$0") IntTy (constIntExp 0), AnnAssign (nameTrg "x$1") IntTy (name "x$0"), AnnAssign (nameTrg "x$2") IntTy (constIntExp 0), AnnAssign (nameTrg "x$3") IntTy (name "x$2"), For (nameTrg "i$4") (list IntTy []) [ AnnAssign (nameTrg "x$3") IntTy (constIntExp 0), AnnAssign (nameTrg "x$3") IntTy (name "x$3"), AnnAssign (nameTrg "y$5") IntTy (constIntExp 0), AnnAssign (nameTrg "y$6") IntTy (name "y$5"), AnnAssign (nameTrg "x$3") IntTy (constIntExp 0), AnnAssign (nameTrg "x$3") IntTy (name "x$3") ], AnnAssign (nameTrg "x$7") IntTy (name "x$3"), AnnAssign (nameTrg "x$8") IntTy (constIntExp 0), AnnAssign (nameTrg "x$9") IntTy (name "x$8") ] ] run' parsed `shouldBe` Right expected it "doesn't rename subscripted assignments" $ do let parsed = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "a") (ListTy IntTy) (list IntTy []), AnnAssign (subscriptTrg (nameTrg "a") (constIntExp 0)) IntTy (subscript (name "a") (constIntExp 0)), AnnAssign (subscriptTrg (nameTrg "a") (constIntExp 0)) IntTy (subscript (name "a") (constIntExp 0)), AnnAssign (subscriptTrg (nameTrg "a") (constIntExp 0)) IntTy (subscript (name "a") (constIntExp 0)), AnnAssign (subscriptTrg (nameTrg "a") (constIntExp 0)) IntTy (subscript (name "a") (constIntExp 0)) ] ] let expected = [ ToplevelFunctionDef "main" [] IntTy [ AnnAssign (nameTrg "a$0") (ListTy IntTy) (list IntTy []), AnnAssign (subscriptTrg (nameTrg "a$0") (constIntExp 0)) IntTy (subscript (name "a$0") (constIntExp 0)), AnnAssign (subscriptTrg (nameTrg "a$0") (constIntExp 0)) IntTy (subscript (name "a$0") (constIntExp 0)), AnnAssign (subscriptTrg (nameTrg "a$0") (constIntExp 0)) IntTy (subscript (name "a$0") (constIntExp 0)), AnnAssign (subscriptTrg (nameTrg "a$0") (constIntExp 0)) IntTy (subscript (name "a$0") (constIntExp 0)) ] ] run' parsed `shouldBe` Right expected