resolve :: Ctx -> [TlExpr] -> DebugOr [TlExpr]
resolve ctx = traverse (resolveDef ctx)
  where
    resolveDef :: Ctx -> TlExpr -> DebugOr TlExpr
    resolveDef ctx tl = case tl of
      TlDef x ct e -> TlDef x ct <$> resolveExpr ctx e
      TlFuncDef x bs ct e -> TlFuncDef x bs ct <$> resolveExpr ctx e
    resolveExpr :: Ctx -> Expr -> DebugOr Expr
    resolveExpr ctx e = case e of
      ELitBool _ -> mkSuccess e
      ELitInt _ -> mkSuccess e
      EVar x (Unquantified ct) -> resolveVar ctx x ct
      EVar x Quantified {} -> fail "found a quantified variable while resolving"
      EAbs bs e' -> EAbs bs <$> resolveExpr ctx e'
      EApp e' es ->
        EApp <$> resolveExpr ctx e' <*> traverse (resolveExpr ctx) es
      EIf e1 e2 e3 ->
        EIf <$> resolveExpr ctx e1 <*> resolveExpr ctx e2 <*> resolveExpr ctx e3
      EUnBuiltin {} -> mkSuccess e
      EBinBuiltin {} -> mkSuccess e
    resolveVar :: Ctx -> ExprName -> CType -> DebugOr Expr
    resolveVar ctx x ct = case maybeGetInit ctx x ct of
      Just e@(EUnBuiltin {}) -> mkSuccess e
      Just e@(EBinBuiltin {}) -> mkSuccess e
      _ -> mkSuccess $ EVar x (Unquantified ct)
    maybeGetInit :: Ctx -> ExprName -> CType -> Maybe Expr
    maybeGetInit ctx x ct = fromDebugOr lkp varValue (const Nothing)
      where
        lkp = lookupSignature ctx x (Unquantified ct)
resolve :: Ctx -> [TlExpr] -> DebugOr [TlExpr]
resolve ctx = traverse (resolveDef ctx)
  where
    resolveDef :: Ctx -> TlExpr -> DebugOr TlExpr
    resolveDef ctx tl = case tl of
      TlDef x ct e -> TlDef x ct <$> resolveExpr ctx e
      TlFuncDef x bs ct e -> TlFuncDef x bs ct <$> resolveExpr ctx e
    resolveExpr :: Ctx -> Expr -> DebugOr Expr
    resolveExpr ctx e = case e of
      ELitBool _ -> mkSuccess e
      ELitInt _ -> mkSuccess e
      EVar x (Unquantified ct) -> resolveVar ctx x ct
      EVar x Quantified {} -> fail "found a quantified variable while resolving"
      EAbs bs e' -> EAbs bs <$> resolveExpr ctx e'
      EApp e' es ->
        EApp <$> resolveExpr ctx e' <*> traverse (resolveExpr ctx) es
      EIf e1 e2 e3 ->
        EIf <$> resolveExpr ctx e1 <*> resolveExpr ctx e2 <*> resolveExpr ctx e3
      EUnBuiltin {} -> mkSuccess e
      EBinBuiltin {} -> mkSuccess e
    resolveVar :: Ctx -> ExprName -> CType -> DebugOr Expr
    resolveVar ctx x ct = case maybeGetInit ctx x ct of
      Just e@(EUnBuiltin {}) -> mkSuccess e
      Just e@(EBinBuiltin {}) -> mkSuccess e
      _ -> mkSuccess $ EVar x (Unquantified ct)
    maybeGetInit :: Ctx -> ExprName -> CType -> Maybe Expr
    maybeGetInit ctx x ct = fromDebugOr lkp varValue (const Nothing)
      where
        lkp = lookupSignature ctx x (Unquantified ct)
resolve ctx = traverse (resolveDef ctx)
  where
    resolveDef :: Ctx -> TlExpr -> DebugOr TlExpr
    resolveDef ctx tl = case tl of
      TlDef x ct e -> TlDef x ct <$> resolveExpr ctx e
      TlFuncDef x bs ct e -> TlFuncDef x bs ct <$> resolveExpr ctx e
    resolveExpr :: Ctx -> Expr -> DebugOr Expr
    resolveExpr ctx e = case e of
      ELitBool _ -> mkSuccess e
      ELitInt _ -> mkSuccess e
      EVar x (Unquantified ct) -> resolveVar ctx x ct
      EVar x Quantified {} -> fail "found a quantified variable while resolving"
      EAbs bs e' -> EAbs bs <$> resolveExpr ctx e'
      EApp e' es ->
        EApp <$> resolveExpr ctx e' <*> traverse (resolveExpr ctx) es
      EIf e1 e2 e3 ->
        EIf <$> resolveExpr ctx e1 <*> resolveExpr ctx e2 <*> resolveExpr ctx e3
      EUnBuiltin {} -> mkSuccess e
      EBinBuiltin {} -> mkSuccess e
    resolveVar :: Ctx -> ExprName -> CType -> DebugOr Expr
    resolveVar ctx x ct = case maybeGetInit ctx x ct of
      Just e@(EUnBuiltin {}) -> mkSuccess e
      Just e@(EBinBuiltin {}) -> mkSuccess e
      _ -> mkSuccess $ EVar x (Unquantified ct)
    maybeGetInit :: Ctx -> ExprName -> CType -> Maybe Expr
    maybeGetInit ctx x ct = fromDebugOr lkp varValue (const Nothing)
      where
        lkp = lookupSignature ctx x (Unquantified ct)