File size: 9,207 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 | {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-|
Module : Assertica.Backend.CodeGen
Description : Main code generation engine for Assertica → Haskell compilation
Copyright : (c) 2026 Ahmad Ali Parr
License : BSL-1.0
This module is the primary interface for translating verified Assertica Core AST
to executable Haskell code. It orchestrates the compilation pipeline:
1. **TermCompiler**: Translates computational terms to Haskell expressions
2. **TypeCompiler**: Maps type universe to Haskell type signatures
3. **ProofCompiler**: Converts proof terms to Haskell evidence
4. **Emit**: Renders the result as syntactically valid Haskell source
DESIGN PRINCIPLES:
1. **Deterministic Generation**: Same AST input always produces identical output
2. **Type Safety**: Generated code must typecheck under GHC 9.2+
3. **No Unsafe Code**: Forbidden to use unsafeCoerce or similar
4. **Fail-Closed**: Malformed input generates an error, not silent code
5. **Module-Aware**: Qualified names preserve module structure
INTERFACE CONTRACT:
- compileModule: Module → Either String HaskellModule
Takes a Module (from Core.Module), returns Haskell source or error
- compileDefinition: Definition → Either String HaskellDefinition
Compiles a single definition (term, type, assertion, etc.)
- compileExpression: Term → Either String HaskellExpr
Compiles a single term expression to Haskell
ASSUMPTIONS:
- All input has been verified by Agent 3B (TypeChecker)
- All proofs are valid (checked by Agent 2B)
- All module dependencies are resolved
- No circular references in type definitions
-}
module Assertica.Backend.CodeGen
( -- * Main compilation interface
compileModule
, compileDefinition
, compileExpression
, compileType
, compileProof
-- * Compiled output types
, HaskellModule(..)
, HaskellDefinition(..)
, HaskellExpr(..)
-- * Error handling
, CodeGenError(..)
, formatCodeGenError
-- * Configuration
, CodeGenConfig(..)
, defaultConfig
) where
import Data.Text (Text)
import qualified Data.Text as T
import Data.List (intercalate)
import Data.Maybe (mapMaybe, catMaybes)
import qualified Data.Set as Set
import qualified Data.Map.Strict as Map
import GHC.Generics (Generic)
import Assertica.Core.AST
import Assertica.Core.Module (Module(..), Definition(..), ModuleMetadata(..))
import qualified Assertica.Backend.TermCompiler as TC
import qualified Assertica.Backend.TypeCompiler as TyC
import qualified Assertica.Backend.ProofCompiler as PC
import qualified Assertica.Backend.Emit as Emit
-- ============================================================================
-- TYPES
-- ============================================================================
{-| Configuration for code generation -}
data CodeGenConfig = CodeGenConfig
{ cgcAddComments :: Bool -- ^ Include source location comments
, cgcAddTypeSignatures :: Bool -- ^ Include type signatures for all definitions
, cgcModulePrefix :: [Text] -- ^ Module path prefix for qualification
, cgcTargetVersion :: (Int, Int) -- ^ GHC version target (e.g., (9, 2))
}
deriving (Show, Generic)
{-| Default code generation configuration -}
defaultConfig :: CodeGenConfig
defaultConfig = CodeGenConfig
{ cgcAddComments = True
, cgcAddTypeSignatures = True
, cgcModulePrefix = []
, cgcTargetVersion = (9, 2)
}
{-| A compiled Haskell module (abstract representation) -}
data HaskellModule = HaskellModule
{ hmName :: [Text] -- ^ Module name path
, hmImports :: [Text] -- ^ Import statements (one per line)
, hmDefinitions :: [HaskellDefinition] -- ^ All definitions in module
, hmComments :: [Text] -- ^ Header comments
}
deriving (Show, Generic)
{-| A single compiled Haskell definition -}
data HaskellDefinition = HaskellDefinition
{ hdName :: Text -- ^ Definition name
, hdTypeSignature :: Maybe Text -- ^ Type signature (e.g., "x :: Int")
, hdBody :: Text -- ^ Definition body
, hdSourceLocation :: Maybe Text -- ^ Source location comment
}
deriving (Show, Generic)
{-| A compiled Haskell expression -}
data HaskellExpr = HaskellExpr
{ heExpr :: Text -- ^ The Haskell expression
, heType :: Maybe Text -- ^ Inferred type annotation
}
deriving (Show, Generic)
{-| Code generation errors -}
data CodeGenError
= UnhandledConstruct String
| InvalidQName QName
| TypeCompilationFailed String
| ProofCompilationFailed String
| TermCompilationFailed String
| ModuleCompilationFailed String
| UnsupportedFeature String
deriving (Show, Generic)
{-| Human-readable error formatting -}
formatCodeGenError :: CodeGenError -> String
formatCodeGenError = \case
UnhandledConstruct desc ->
"Code generation: unhandled construct: " ++ desc
InvalidQName (QName mod name) ->
"Code generation: invalid qualified name: " ++ intercalate "." (map T.unpack mod) ++ "." ++ T.unpack name
TypeCompilationFailed desc ->
"Code generation: type compilation failed: " ++ desc
ProofCompilationFailed desc ->
"Code generation: proof compilation failed: " ++ desc
TermCompilationFailed desc ->
"Code generation: term compilation failed: " ++ desc
ModuleCompilationFailed desc ->
"Code generation: module compilation failed: " ++ desc
UnsupportedFeature desc ->
"Code generation: unsupported feature: " ++ desc
-- ============================================================================
-- MAIN COMPILATION INTERFACE
-- ============================================================================
{-| Compile an entire Assertica module to Haskell code.
The module must be fully elaborated and type-checked before reaching this stage.
This is the primary entry point for the backend.
-}
compileModule :: CodeGenConfig -> Module -> Either String HaskellModule
compileModule config (Module _modQName _imports _exports defMap _metadata) = do
-- Compile all definitions from the definition map
compiledDefs <- mapM (compileDefinitionValue config) (Map.elems defMap)
-- Generate module header
let imports = generateImports config
let comments = ["-- Generated by Assertica Backend", "-- Do not edit manually"]
return $ HaskellModule
{ hmName = [] -- Could extract from modQName if needed
, hmImports = imports
, hmDefinitions = compiledDefs
, hmComments = comments
}
{-| Compile a single definition from Module's Definition type -}
compileDefinitionValue :: CodeGenConfig -> Definition -> Either String HaskellDefinition
compileDefinitionValue config defn = case defn of
DefTerm term -> do
compiled <- compileExpression config term
return $ HaskellDefinition
{ hdName = "value" -- Anonymous term
, hdTypeSignature = Nothing
, hdBody = heExpr compiled
, hdSourceLocation = if cgcAddComments config then Just "term" else Nothing
}
DefType ty -> do
compiled <- compileType config ty
return $ HaskellDefinition
{ hdName = "type_def" -- Anonymous type
, hdTypeSignature = Just "Type"
, hdBody = heExpr compiled
, hdSourceLocation = if cgcAddComments config then Just "type" else Nothing
}
DefAssertion _assertion -> do
-- For now, compile assertion as a unit
return $ HaskellDefinition
{ hdName = "assertion"
, hdTypeSignature = Nothing
, hdBody = "()"
, hdSourceLocation = if cgcAddComments config then Just "assertion" else Nothing
}
DefOpaque desc ->
return $ HaskellDefinition
{ hdName = "opaque"
, hdTypeSignature = Nothing
, hdBody = T.pack desc
, hdSourceLocation = if cgcAddComments config then Just "opaque" else Nothing
}
{-| Compile a single definition (can be a term, type, assertion, etc.) -}
compileDefinition :: CodeGenConfig -> Definition -> Either String HaskellDefinition
compileDefinition = compileDefinitionValue
{-| Compile a single term expression to Haskell -}
compileExpression :: CodeGenConfig -> Term -> Either String HaskellExpr
compileExpression config term = do
expr <- TC.compileTerm config term
return $ HaskellExpr
{ heExpr = expr
, heType = Nothing
}
{-| Compile a type to Haskell -}
compileType :: CodeGenConfig -> Type -> Either String HaskellExpr
compileType config ty = do
expr <- TyC.compileType config ty
return $ HaskellExpr
{ heExpr = expr
, heType = Just "Type" -- All types have kind Type
}
{-| Compile a proof to Haskell -}
compileProof :: CodeGenConfig -> Proof -> Either String HaskellExpr
compileProof config proof = do
expr <- PC.compileProof config proof
return $ HaskellExpr
{ heExpr = expr
, heType = Nothing
}
-- ============================================================================
-- HELPER FUNCTIONS
-- ============================================================================
{-| Generate standard Haskell imports for a module -}
generateImports :: CodeGenConfig -> [Text]
generateImports _config =
[ "import Prelude"
, "import qualified Data.Typeable as Typeable"
, "import GHC.Generics (Generic)"
]
|