File size: 15,625 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
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}

{-|
Module      : Assertica.Core.Pattern
Description : Pattern matching compilation and exhaustiveness checking
Copyright   : (c) 2026 Ahmad Ali Parr
License     : BSL-1.0

This module implements pattern matching compilation: transforming surface pattern
syntax into core eliminators that preserve type information and exhaustiveness
guarantees.

DESIGN PRINCIPLES:
1. **Exhaustiveness checking**: All cases must be covered or default provided.
   No unmatched patterns reach the kernel.

2. **Type preservation**: Pattern compilation preserves types through pattern variables.

3. **Constructor awareness**: Pattern compiler knows constructor arities and argument types.

4. **Dependent pattern matching**: Support dependent patterns where types depend on matched values.

5. **Fail-closed**: Ill-formed patterns rejected with clear diagnostics.

ARCHITECTURE:
The pattern compiler sits between elaboration and type checking:

  Elaborator (Agent 3A)
      ↓ produces SurfacePattern
  Parser/Elaboration
      ↓
  Pattern Compiler (THIS MODULE)
      ↓ validates + compiles to Case
      ↓
  Type Checker (Agent 3B)
      ↓
  Kernel Verification

REQUIRED INTEGRATION POINTS:
- QueryConstructors: Look up constructor arities from type environment
- CompiledPattern: Pattern variables must have inferred types
- TypeChecker receives compiled Case expressions with type preservation
-}

module Assertica.Core.Pattern
  ( -- * Pattern AST (extended)
    Pattern(..)
  , PatternVar
  , PatternInfo(..)

    -- * Constructor information
  , Constructor(..)
  , ConstructorDB
  , emptyConstructorDB
  , addConstructor
  , queryConstructors
  , queryConstructorArity

    -- * Exhaustiveness checking
  , ExhaustiveResult(..)
  , checkExhaustive
  , findMissingCases
  , isRedundant

    -- * Pattern compilation
  , compilePattern
  , compilePatterns
  , CompiledPattern(..)
  , PatternBindings
  , extractPatternBindings

    -- * Type-aware pattern matching
  , PatternContext(..)
  , inferPatternType
  , checkPatternType

    -- * Error types and diagnostics
  , PatternError(..)
  , prettyPatternError
  , ExhaustiveError(..)
  , prettyExhaustiveError
  ) where

import qualified Data.Set as Set
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text as T
import Data.List (intercalate, nub)
import Data.Maybe (catMaybes, isJust, fromMaybe)
import GHC.Generics (Generic)
import Data.Typeable (Typeable)

import Assertica.Core.AST
  ( Var(..)
  , QName(..)
  , Type(..)
  , Term(..)
  , Pattern(..)
  , Clause(..)
  , Binder(..)
  , varName
  , prettyPattern
  , prettyType
  )

-- ============================================================================
-- PATTERN AST (EXTENDED)
-- ============================================================================

{-| Information about a pattern variable binding.
Tracks the variable name and its inferred or declared type.
-}
data PatternVar = PatternVar
  { pvVar  :: Var     -- ^ The bound variable
  , pvType :: Maybe Type  -- ^ Inferred or declared type
  }
  deriving (Eq, Show, Generic, Typeable)

{-| Extended pattern information for compilation.
-}
data PatternInfo = PatternInfo
  { piPattern :: Pattern     -- ^ The pattern itself
  , piBindings :: PatternBindings  -- ^ Variables bound by this pattern
  , piType :: Maybe Type     -- ^ Expected or inferred type
  }
  deriving (Eq, Show, Generic, Typeable)

-- ============================================================================
-- CONSTRUCTOR DATABASE
-- ============================================================================

{-| Information about a data constructor.
-}
data Constructor = Constructor
  { ctorName :: QName       -- ^ Qualified name of constructor
  , ctorArity :: Int        -- ^ Number of arguments
  , ctorArgTypes :: [Type]  -- ^ Types of arguments (may be dependent)
  , ctorReturnType :: Type  -- ^ Return type of constructor
  }
  deriving (Eq, Show, Generic, Typeable)

{-| Database of known constructors.
Maps type names to their constructors.
-}
type ConstructorDB = Map.Map QName [Constructor]

{-| Create an empty constructor database.
-}
emptyConstructorDB :: ConstructorDB
emptyConstructorDB = Map.empty

{-| Add a constructor to the database.
-}
addConstructor :: ConstructorDB -> QName -> Constructor -> ConstructorDB
addConstructor db tyName ctor =
  Map.insertWith (++) tyName [ctor] db

{-| Query all constructors for a given type.
-}
queryConstructors :: ConstructorDB -> QName -> Either PatternError [Constructor]
queryConstructors db tyName =
  case Map.lookup tyName db of
    Just ctors -> Right ctors
    Nothing -> Left (UnknownConstructor tyName)

{-| Query the arity of a specific constructor.
-}
queryConstructorArity :: ConstructorDB -> QName -> Either PatternError Int
queryConstructorArity db ctorName = do
  -- Search through all constructors in all types
  let allCtors = concat (Map.elems db)
  case filter (\c -> ctorName == ctorName) allCtors of
    (ctor:_) -> Right (ctorArity ctor)
    [] -> Left (UnknownConstructor ctorName)

-- ============================================================================
-- EXHAUSTIVENESS CHECKING
-- ============================================================================

{-| Result of exhaustiveness checking.
-}
data ExhaustiveResult
  = Exhaustive          -- ^ All cases covered
  | MissingCases [Pattern]  -- ^ Missing constructor patterns
  | HasDefault          -- ^ Has a default clause (always safe)
  deriving (Eq, Show, Generic, Typeable)

{-| Error type for exhaustiveness checking failures.
-}
data ExhaustiveError
  = IncompleteMatch [Pattern]  -- ^ Patterns that are not covered
  | DuplicatePattern Pattern   -- ^ Duplicate or redundant pattern
  | UnreachablePattern Pattern -- ^ Pattern can never match
  deriving (Eq, Show, Generic, Typeable)

{-| Pretty-print an exhaustive checking error.
-}
prettyExhaustiveError :: ExhaustiveError -> String
prettyExhaustiveError = \case
  IncompleteMatch pats ->
    "Incomplete pattern match. Missing cases:\n  " ++
    intercalate "\n  " (map prettyPattern pats)
  DuplicatePattern pat ->
    "Duplicate or redundant pattern: " ++ prettyPattern pat
  UnreachablePattern pat ->
    "Unreachable pattern: " ++ prettyPattern pat

{-| Check if a set of patterns exhaustively covers a type.

Returns:
  - Right () if patterns are exhaustive or have a default
  - Left (IncompleteMatch missing) if patterns don't cover all cases
  - Left (DuplicatePattern pat) if there are redundant patterns

For this implementation, we need the constructor database to determine
which constructors must be matched.
-}
checkExhaustive :: ConstructorDB -> Type -> [Pattern] -> Maybe Pattern
                -> Either ExhaustiveError ()
checkExhaustive _db _ty _patterns (Just _) =
  -- If there's a default clause, patterns are always exhaustive
  Right ()

checkExhaustive db ty patterns Nothing = do
  -- Check for duplicates
  let patStrs = map prettyPattern patterns
  if length (nub patStrs) /= length patStrs
    then Left (DuplicatePattern (head patterns))
    else Right ()

  -- Find missing cases
  missing <- findMissingCases db ty patterns
  if null missing
    then Right ()
    else Left (IncompleteMatch missing)

{-| Find pattern cases that are not covered by the given patterns.
-}
findMissingCases :: ConstructorDB -> Type -> [Pattern]
                 -> Either ExhaustiveError [Pattern]
findMissingCases db ty patterns = do
  -- Get the type name
  let tyName = case ty of
        TyConst name _ -> name
        _ -> QName [] "Unknown"

  -- Query available constructors
  ctors <- queryConstructors db tyName

  -- Extract constructor names from patterns
  let coveredCtors = Set.fromList
        [name | PatConstr name _ <- patterns]

  -- Find uncovered constructors
  let uncovered = [ctor | ctor <- ctors, not (Set.member (ctorName ctor) coveredCtors)]

  -- Create missing patterns (wildcards for now)
  let missingPats = [PatConstr (ctorName c) (replicate (ctorArity c) PatWildcard) | c <- uncovered]

  Right missingPats

{-| Check if a pattern is redundant (unreachable).
A pattern is redundant if it cannot match any value that wasn't already
matched by earlier patterns.

This is a simplified check: we mark a pattern as potentially redundant if
it matches a constructor that was already matched.
-}
isRedundant :: [Pattern] -> Pattern -> Bool
isRedundant earlier pat =
  case pat of
    PatConstr name _ ->
      any (\case PatConstr n _ -> n == name; _ -> False) earlier
    PatWildcard -> any (== PatWildcard) earlier
    PatVar _ -> False  -- Variables always bind, never redundant
    PatLit _ -> False  -- Literal patterns handled separately (not yet implemented)

-- ============================================================================
-- PATTERN COMPILATION
-- ============================================================================

{-| Type for pattern bindings (variable -> type mappings).
-}
type PatternBindings = Map.Map Var Type

{-| Compiled pattern information.
-}
data CompiledPattern = CompiledPattern
  { cpOriginal :: Pattern     -- ^ Original surface pattern
  , cpBindings :: PatternBindings  -- ^ Variables bound by this pattern
  , cpType :: Maybe Type      -- ^ Inferred type for this pattern
  }
  deriving (Eq, Show, Generic, Typeable)

{-| Extract all pattern variable bindings from a pattern.

This walks the pattern tree and collects all bound variables with their types.
-}
extractPatternBindings :: Pattern -> Type -> PatternBindings
extractPatternBindings pat ty = go pat ty
  where
    go :: Pattern -> Type -> PatternBindings
    go p t = case p of
      PatVar v -> Map.singleton v t

      PatConstr _ subPats ->
        -- For constructor patterns, extract bindings from subpatterns
        -- This is simplified: in full implementation, we'd use constructor arg types
        let subTypes = replicate (length subPats) t
        in Map.unions [go subpat subty | (subpat, subty) <- zip subPats subTypes]

      PatWildcard -> Map.empty

      PatLit _ -> Map.empty

{-| Compile a single pattern to a core pattern.

This validates the pattern structure and preserves type information.
-}
compilePattern :: Pattern -> Either PatternError CompiledPattern
compilePattern pat = do
  -- For now, minimal compilation: just validate structure
  case pat of
    PatVar v ->
      Right (CompiledPattern pat (Map.singleton v (TyVar v)) Nothing)

    PatConstr name subPats -> do
      -- Validate that we can compile subpatterns
      compiledSubs <- mapM compilePattern subPats
      let allBindings = Map.unions [cpBindings cp | cp <- compiledSubs]
      Right (CompiledPattern pat allBindings Nothing)

    PatWildcard ->
      Right (CompiledPattern pat Map.empty Nothing)

    PatLit _ ->
      Right (CompiledPattern pat Map.empty Nothing)

{-| Compile a list of patterns to core patterns.

Returns a list of compiled patterns with bindings and type information.
-}
compilePatterns :: [Pattern] -> Either PatternError [CompiledPattern]
compilePatterns = mapM compilePattern

-- ============================================================================
-- PATTERN TYPE INFERENCE
-- ============================================================================

{-| Context for type-aware pattern matching.
-}
data PatternContext = PatternContext
  { pcType :: Type        -- ^ Expected type for patterns
  , pcConstructors :: ConstructorDB  -- ^ Available constructors
  }
  deriving (Eq, Show, Generic, Typeable)

{-| Infer the type of a pattern variable.

Given a pattern and an expected type, infer the type of any bound variables.
-}
inferPatternType :: PatternContext -> Pattern -> Either PatternError Type
inferPatternType ctx = \case
  PatVar _ -> Right (pcType ctx)

  PatConstr _ _ -> Right (pcType ctx)

  PatWildcard -> Right (pcType ctx)

  PatLit _ -> Right (pcType ctx)

{-| Check that a pattern matches an expected type.

Returns the refined type for the pattern (in case of dependent pattern matching).
-}
checkPatternType :: PatternContext -> Pattern -> Type
                 -> Either PatternError Type
checkPatternType _ctx _pat expectedTy =
  -- For now, just accept the expected type
  Right expectedTy

-- ============================================================================
-- ERROR TYPES
-- ============================================================================

{-| Pattern compilation and checking errors.
-}
data PatternError
  = UnknownConstructor QName
  | ConstructorArityMismatch QName Int Int  -- ^ (constructor, expected, actual)
  | DuplicateConstructor QName
  | InvalidPattern String
  | PatternTypeError String
  | UncoveredConstructor QName
  deriving (Eq, Show, Generic, Typeable)

{-| Pretty-print a pattern error.
-}
prettyPatternError :: PatternError -> String
prettyPatternError = \case
  UnknownConstructor (QName mod name) ->
    "Unknown constructor: " ++ qualName mod name
  ConstructorArityMismatch (QName mod name) expected actual ->
    qualName mod name ++ " expects " ++ show expected ++ " arguments, got " ++ show actual
  DuplicateConstructor (QName mod name) ->
    "Duplicate constructor: " ++ qualName mod name
  InvalidPattern msg ->
    "Invalid pattern: " ++ msg
  PatternTypeError msg ->
    "Pattern type error: " ++ msg
  UncoveredConstructor (QName mod name) ->
    "Pattern match is not exhaustive for constructor: " ++ qualName mod name
  where
    qualName mod name =
      if null mod
        then T.unpack name
        else intercalate "." (map T.unpack mod) ++ "." ++ T.unpack name

-- ============================================================================
-- INTEGRATION: PATTERN COMPILATION TO CORE CASE
-- ============================================================================

{-| Compile patterns and clauses to a core Case expression.

This is the main entry point for the pattern compiler. It takes a list of
surface patterns and produces a typed core Case expression.

Preconditions:
  - Patterns are exhaustive (or a default is provided)
  - Constructors are known in the database
  - Type information is available

Postconditions:
  - Compiled patterns preserve type information
  - Pattern variables are properly bound
  - Case expression is ready for type checking
-}
compilePatternCases :: ConstructorDB -> Term -> [Clause]
                     -> Either PatternError Term
compilePatternCases _db scrutinee clauses = do
  -- Compile each clause
  _compiledClauses <- mapM compileSingleClause clauses

  -- For now, return the case expression as-is
  -- The actual transformation would happen here
  Right (Case scrutinee clauses Nothing)
  where
    compileSingleClause (Clause pat body) = do
      cp <- compilePattern pat
      let bindings = cpBindings cp
      -- The body should have all variables from bindings in scope
      return (Clause pat body, bindings)

-- ============================================================================
-- UTILITIES
-- ============================================================================

{-| Check if a pattern is wildcard-like (matches anything).
-}
isWildcardPattern :: Pattern -> Bool
isWildcardPattern = \case
  PatWildcard -> True
  PatVar _ -> True
  _ -> False

{-| Extract all constructors mentioned in a pattern.
-}
patternConstructors :: Pattern -> Set.Set QName
patternConstructors = \case
  PatConstr name subPats ->
    Set.insert name (Set.unions (map patternConstructors subPats))
  PatVar _ -> Set.empty
  PatWildcard -> Set.empty
  PatLit _ -> Set.empty