| module Language.Elaborator (elaborate, ElabError(..)) where | |
| import Language.AST | |
| import qualified IR.Boolean as B | |
| import qualified Data.Map.Strict as Map | |
| data ElabError | |
| = UnboundVariable String | |
| | UndefinedFunction String | |
| | ArityMismatch String Int Int | |
| deriving (Show) | |
| type Env = Map.Map String ([String], Expr) | |
| elaborate :: Module -> Either ElabError [B.Circuit] | |
| elaborate (Module _ stmts) = do | |
| let env = buildEnv stmts | |
| mapM (elabStmt env) (filter isVerifiable stmts) | |
| buildEnv :: [Stmt] -> Env | |
| buildEnv = foldl addDef Map.empty | |
| where | |
| addDef env (SDef (Ident name) params body) = | |
| Map.insert name (map identName params, body) env | |
| addDef env _ = env | |
| isVerifiable :: Stmt -> Bool | |
| isVerifiable (SAssert _) = True | |
| isVerifiable (SProve _ _) = True | |
| isVerifiable _ = False | |
| elabStmt :: Env -> Stmt -> Either ElabError B.Circuit | |
| elabStmt env (SAssert expr) = do | |
| bexpr <- elabExpr env Map.empty expr | |
| Right (B.Circuit "assert" [] bexpr) | |
| elabStmt env (SProve (Ident name) expr) = do | |
| bexpr <- elabExpr env Map.empty expr | |
| Right (B.Circuit name [] bexpr) | |
| elabStmt _ _ = Right (B.Circuit "_skip" [] B.BTrue) | |
| elabExpr :: Env -> Map.Map String B.BExpr -> Expr -> Either ElabError B.BExpr | |
| elabExpr _ locals (EVar (Ident v)) = | |
| case Map.lookup v locals of | |
| Just bv -> Right bv | |
| Nothing -> Left (UnboundVariable v) | |
| elabExpr _ _ (ELit True) = Right B.BTrue | |
| elabExpr _ _ (ELit False) = Right B.BFalse | |
| elabExpr env locals (ENand l r) = do | |
| bl <- elabExpr env locals l | |
| br <- elabExpr env locals r | |
| Right (B.BNand bl br) | |
| elabExpr env locals (EApp (Ident fname) args) = | |
| case Map.lookup fname env of | |
| Nothing -> Left (UndefinedFunction fname) | |
| Just (params, body) -> do | |
| if length params /= length args | |
| then Left (ArityMismatch fname (length params) (length args)) | |
| else do | |
| bargs <- mapM (elabExpr env locals) args | |
| let newLocals = Map.union (Map.fromList (zip params bargs)) locals | |
| elabExpr env newLocals body | |