File size: 11,667 Bytes
a10b189 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | {-# LANGUAGE OverloadedStrings #-}
module Test.Backend.TermCompiler
( tests
) where
import Test.Tasty
import Test.Tasty.HUnit
import qualified Data.Text as T
import Data.Text (Text)
import qualified Data.Set as Set
import Assertica.Core.AST
import Assertica.Backend.CodeGen (defaultConfig)
import Assertica.Backend.TermCompiler
-- ============================================================================
-- TEST SUITE
-- ============================================================================
tests :: TestTree
tests = testGroup "Assertica.Backend.TermCompiler"
[ testGroup "Variable Compilation"
[ testCase "Compile simple variable" testSimpleVariable
, testCase "Variable name preserved" testVariableNamePreserved
]
, testGroup "Constant Compilation"
[ testCase "Compile integer constant" testIntConstant
, testCase "Compile boolean constant" testBoolConstant
, testCase "Compile unit constant" testUnitConst
]
, testGroup "Lambda Abstraction"
[ testCase "Compile simple lambda" testSimpleLambda
, testCase "Lambda contains backslash" testLambdaBackslash
, testCase "Nested lambda" testNestedLambda
]
, testGroup "Function Application"
[ testCase "Simple application" testSimpleApplication
, testCase "Application with multiple args" testMultipleApplication
]
, testGroup "Data Constructors"
[ testCase "Simple constructor" testSimpleConstructor
, testCase "Constructor with arguments" testConstructorWithArgs
]
, testGroup "Case Expressions"
[ testCase "Simple case" testSimpleCase
, testCase "Case with default" testCaseWithDefault
]
, testGroup "Let-Bindings"
[ testCase "Simple let binding" testSimpleLet
, testCase "Let with variable in body" testLetWithVar
]
, testGroup "Type Annotation"
[ testCase "Annotated term" testAnnotatedTerm
]
, testGroup "Pattern Matching"
[ testCase "Variable pattern" testPatternVar
, testCase "Constructor pattern" testPatternConstructor
, testCase "Wildcard pattern" testPatternWildcard
]
, testGroup "Determinism"
[ testCase "Compilation is deterministic" testDeterminism
]
]
-- ============================================================================
-- UNIT TESTS: VARIABLES
-- ============================================================================
testSimpleVariable :: Assertion
testSimpleVariable = do
let var = Var (Var "x" 0)
result <- case compileTerm defaultConfig var of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be x" "x" result
testVariableNamePreserved :: Assertion
testVariableNamePreserved = do
let var = Var (Var "myVar" 42) -- ID should be ignored in output
result <- case compileTerm defaultConfig var of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be myVar" "myVar" result
-- ============================================================================
-- UNIT TESTS: CONSTANTS
-- ============================================================================
testIntConstant :: Assertion
testIntConstant = do
let term = Const (IntLit 123)
result <- case compileTerm defaultConfig term of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be 123" "123" result
testBoolConstant :: Assertion
testBoolConstant = do
let term = Const (BoolLit True)
result <- case compileTerm defaultConfig term of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be True" "True" result
testUnitConst :: Assertion
testUnitConst = do
let term = Const UnitConst
result <- case compileTerm defaultConfig term of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be ()" "()" result
-- ============================================================================
-- UNIT TESTS: LAMBDA
-- ============================================================================
testSimpleLambda :: Assertion
testSimpleLambda = do
let lambda = Lam (Binder (Var "x" 0) Nothing) (Var (Var "x" 0))
result <- case compileTerm defaultConfig lambda of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain backslash" ("\\" `T.isInfixOf` result)
assertBool "Should contain x" ("x" `T.isInfixOf` result)
testLambdaBackslash :: Assertion
testLambdaBackslash = do
let lambda = Lam (Binder (Var "f" 0) Nothing) (Var (Var "f" 0))
result <- case compileTerm defaultConfig lambda of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should start with lambda" ("(\\" `T.isInfixOf` result)
testNestedLambda :: Assertion
testNestedLambda = do
let inner = Lam (Binder (Var "y" 0) Nothing) (Var (Var "y" 0))
let outer = Lam (Binder (Var "x" 0) Nothing) inner
result <- case compileTerm defaultConfig outer of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain x" ("x" `T.isInfixOf` result)
assertBool "Should contain y" ("y" `T.isInfixOf` result)
-- ============================================================================
-- UNIT TESTS: APPLICATION
-- ============================================================================
testSimpleApplication :: Assertion
testSimpleApplication = do
let f = Var (Var "f" 0)
let x = Var (Var "x" 0)
let app = App f x
result <- case compileTerm defaultConfig app of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain f" ("f" `T.isInfixOf` result)
assertBool "Should contain x" ("x" `T.isInfixOf` result)
testMultipleApplication :: Assertion
testMultipleApplication = do
let f = Var (Var "f" 0)
let x = Var (Var "x" 0)
let y = Var (Var "y" 0)
let app1 = App f x
let app2 = App app1 y
result <- case compileTerm defaultConfig app2 of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
-- Result should contain all three variables
let hasAll = "f" `T.isInfixOf` result &&
"x" `T.isInfixOf` result &&
"y" `T.isInfixOf` result
assertBool "Should contain all variables" hasAll
-- ============================================================================
-- UNIT TESTS: CONSTRUCTORS
-- ============================================================================
testSimpleConstructor :: Assertion
testSimpleConstructor = do
let ctor = Constr (QName [] "True") []
result <- case compileTerm defaultConfig ctor of
Right x -> return x
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be True" "True" result
testConstructorWithArgs :: Assertion
testConstructorWithArgs = do
let x = Var (Var "x" 0)
let y = Var (Var "y" 0)
let pair = Constr (QName ["Data", "Tuple"] "Pair") [x, y]
result <- case compileTerm defaultConfig pair of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain Pair" ("Pair" `T.isInfixOf` result)
assertBool "Should contain x" ("x" `T.isInfixOf` result)
assertBool "Should contain y" ("y" `T.isInfixOf` result)
-- ============================================================================
-- UNIT TESTS: CASE EXPRESSIONS
-- ============================================================================
testSimpleCase :: Assertion
testSimpleCase = do
let scrutinee = Var (Var "x" 0)
let trueClause = Clause (PatConstr (QName [] "True") []) (Const (IntLit 1))
let falseClause = Clause (PatConstr (QName [] "False") []) (Const (IntLit 0))
let caseExpr = Case scrutinee [trueClause, falseClause] Nothing
result <- case compileTerm defaultConfig caseExpr of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain case" ("case" `T.isInfixOf` result)
assertBool "Should contain scrutinee" ("x" `T.isInfixOf` result)
testCaseWithDefault :: Assertion
testCaseWithDefault = do
let scrutinee = Var (Var "x" 0)
let defaultBody = Const (IntLit 42)
let caseExpr = Case scrutinee [] (Just defaultBody)
result <- case compileTerm defaultConfig caseExpr of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should be valid case" (not (T.null result))
-- ============================================================================
-- UNIT TESTS: LET-BINDINGS
-- ============================================================================
testSimpleLet :: Assertion
testSimpleLet = do
let bindValue = Const (IntLit 5)
let bodyExpr = Var (Var "x" 0)
let letExpr = Let (Binder (Var "x" 0) Nothing) bindValue bodyExpr
result <- case compileTerm defaultConfig letExpr of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain let" ("let" `T.isInfixOf` result)
assertBool "Should contain 5" ("5" `T.isInfixOf` result)
testLetWithVar :: Assertion
testLetWithVar = do
let bindValue = Const (IntLit 10)
let bodyExpr = Var (Var "x" 0)
let letExpr = Let (Binder (Var "x" 0) Nothing) bindValue bodyExpr
result <- case compileTerm defaultConfig letExpr of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain in" ("in" `T.isInfixOf` result)
-- ============================================================================
-- UNIT TESTS: ANNOTATIONS
-- ============================================================================
testAnnotatedTerm :: Assertion
testAnnotatedTerm = do
let term = Var (Var "x" 0)
let ty = TyConst (QName [] "Int") []
let annTerm = Ann term ty
result <- case compileTerm defaultConfig annTerm of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain variable" ("x" `T.isInfixOf` result)
-- ============================================================================
-- UNIT TESTS: PATTERNS
-- ============================================================================
testPatternVar :: Assertion
testPatternVar = do
let pat = PatVar (Var "x" 0)
result <- case compilePattern pat of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be x" "x" result
testPatternConstructor :: Assertion
testPatternConstructor = do
let pat = PatConstr (QName [] "Cons")
[ PatVar (Var "x" 0)
, PatVar (Var "xs" 0)
]
result <- case compilePattern pat of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertBool "Should contain Cons" ("Cons" `T.isInfixOf` result)
assertBool "Should contain x" ("x" `T.isInfixOf` result)
testPatternWildcard :: Assertion
testPatternWildcard = do
let pat = PatWildcard
result <- case compilePattern pat of
Right r -> return r
Left err -> assertFailure $ "Unexpected error: " ++ err
assertEqual "Should be wildcard" "_" result
-- ============================================================================
-- PROPERTY TESTS
-- ============================================================================
testDeterminism :: Assertion
testDeterminism = do
let term = App (Var (Var "f" 0)) (Const (IntLit 42))
let result1 = compileTerm defaultConfig term
let result2 = compileTerm defaultConfig term
assertEqual "Compilation should be deterministic" result1 result2
|