less prototype, less bad code implementation of CCHM type theory
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

222 lines
7.4 KiB

  1. {-# LANGUAGE LambdaCase #-}
  2. {-# LANGUAGE BlockArguments #-}
  3. {-# LANGUAGE OverloadedStrings #-}
  4. {-# LANGUAGE ScopedTypeVariables #-}
  5. {-# LANGUAGE DeriveAnyClass #-}
  6. module Main where
  7. import Control.Monad.IO.Class
  8. import Control.Monad.Reader
  9. import Control.Exception
  10. import qualified Data.ByteString.Lazy as Bsl
  11. import qualified Data.Text.Encoding as T
  12. import qualified Data.Map.Strict as Map
  13. import qualified Data.Text.IO as T
  14. import qualified Data.Set as Set
  15. import qualified Data.Text as T
  16. import Data.Sequence (Seq)
  17. import Data.Text ( Text )
  18. import Data.Foldable
  19. import Data.Maybe
  20. import Data.IORef
  21. import Debug.Trace
  22. import Elab.Monad hiding (switch)
  23. import Elab.WiredIn
  24. import Elab.Eval
  25. import Elab
  26. import Options.Applicative
  27. import Presyntax.Presyntax (Posn(Posn))
  28. import Presyntax.Parser
  29. import Presyntax.Tokens
  30. import Presyntax.Lexer
  31. import Prettyprinter
  32. import Syntax.Pretty
  33. import Syntax
  34. import System.Console.Haskeline
  35. import System.Exit
  36. main :: IO ()
  37. main = do
  38. opts <- execParser parser
  39. case opts of
  40. Load files exp -> do
  41. env <- checkFiles files
  42. case exp of
  43. [] -> enterReplIn env
  44. xs -> traverse_ (evalArgExpr env) xs
  45. Check files verbose -> do
  46. env <- checkFiles files
  47. when verbose $ dumpEnv env
  48. Repl -> enterReplIn =<< checkFiles ["./test.tt"]
  49. evalArgExpr :: ElabEnv -> String -> IO ()
  50. evalArgExpr env str =
  51. case runAlex (Bsl.fromStrict (T.encodeUtf8 inp)) parseExp of
  52. Right e ->
  53. flip runElab env (do
  54. (e, _) <- infer e
  55. liftIO . putStrLn . show . prettyTm . quote . zonk =<< Elab.Eval.eval e)
  56. `catch` \e -> do
  57. displayExceptions' inp (e :: SomeException)
  58. Left e -> liftIO $ print e
  59. where
  60. inp = T.pack str
  61. enterReplIn :: ElabEnv -> IO ()
  62. enterReplIn env = runInputT defaultSettings (loop env') where
  63. env' = env { commHook = T.putStrLn . render . prettyTm . quote . zonk }
  64. loop :: ElabEnv -> InputT IO ()
  65. loop env = do
  66. inp <- fmap T.pack <$> getInputLine "% "
  67. case inp of
  68. Nothing -> pure ()
  69. Just inp ->
  70. case runAlex (Bsl.fromStrict (T.encodeUtf8 inp)) parseRepl of
  71. Left e -> do
  72. liftIO $ print e
  73. loop env
  74. Right st -> do
  75. env <- liftIO $
  76. runElab (checkStatement st ask) env
  77. `catch` \e -> do
  78. displayExceptions' inp (e :: SomeException)
  79. pure env
  80. loop env
  81. checkFiles :: [String] -> IO ElabEnv
  82. checkFiles files = runElab (go files ask) =<< emptyEnv where
  83. go [] k = do
  84. env <- ask
  85. for_ (Map.toList (nameMap env)) \case
  86. (_, v@Defined{})
  87. | Set.member v (definedNames env) -> pure ()
  88. | otherwise ->
  89. let
  90. pos = fromJust (Map.lookup v (whereBound env))
  91. in withSpan (fst pos) (snd pos) $ throwElab $ Elab.DeclaredUndefined v
  92. _ -> pure ()
  93. metas <- liftIO $ readIORef (unsolvedMetas env)
  94. unless (Map.null metas) $ do
  95. liftIO $ reportUnsolved metas
  96. k
  97. go (x:xs) k = do
  98. code <- liftIO $ Bsl.readFile x
  99. case runAlex (code <> Bsl.singleton 10) parseProg of
  100. Left e -> liftIO $ print e *> error (show e)
  101. Right prog ->
  102. local (\e -> e { currentFile = Just (T.pack x) }) (checkProgram prog (go xs k))
  103. `catchElab` \e -> liftIO $ displayAndDie (T.decodeUtf8 (Bsl.toStrict code)) (e :: SomeException)
  104. dumpEnv :: ElabEnv -> IO ()
  105. dumpEnv env = for_ (Map.toList (nameMap env)) $ \(_, name) ->
  106. let nft = fst $ getEnv env Map.! name in
  107. T.putStrLn $ render (pretty name <+> align (nest (negate 1) (colon <+> prettyTm (quote (zonk nft)))))
  108. parser :: ParserInfo Opts
  109. parser = info (subparser (load <> check) <|> pure Repl <**> helper) (header "cubical - a cubical programming language")
  110. where
  111. load = command "load" $
  112. info ((Load <$> (some (argument str (metavar "file...")))
  113. <*> (many (strOption (long "eval" <> short 'e' <> help "Also evaluate this expression"))))
  114. <**> helper) (progDesc "Check and load a list of files in the REPL")
  115. check = command "check" $
  116. info ((Check <$> some (argument str (metavar "file..."))
  117. <*> switch ( long "verbose"
  118. <> short 'v'
  119. <> help "Print the types of all declared/defined values"))
  120. <**> helper)
  121. (progDesc "Check a list of files")
  122. data Opts
  123. = Load { files :: [String], eval :: [String] }
  124. | Check { files :: [String], verbose :: Bool }
  125. | Repl
  126. deriving (Eq, Show, Ord)
  127. displayAndDie :: Exception e => Text -> e -> IO a
  128. displayAndDie lines e = do
  129. () <- throwIO e `catches` displayExceptions lines
  130. exitFailure
  131. displayExceptions :: Text -> [Handler ()]
  132. displayExceptions lines =
  133. [ Handler \(WhileChecking a b e) -> do
  134. T.putStrLn $ squiggleUnder a b lines
  135. displayExceptions' lines e
  136. , Handler \(SeeAlso a b e) -> do
  137. displayExceptions' lines e
  138. T.putStrLn $ squiggleUnder a b lines
  139. , Handler \(AttachedNote n e) -> do
  140. displayExceptions' lines e
  141. T.putStrLn $ "\x1b[1;32mnote\x1b[0m: " <> render n
  142. , Handler \(WhenCheckingEndpoint le ri ep e) -> do
  143. displayExceptions' lines e
  144. let
  145. endp = case ep of
  146. VI0 -> T.pack "left"
  147. VI1 -> T.pack "right"
  148. _ -> T.pack $ show (prettyTm (quote ep))
  149. T.putStrLn . T.unlines $
  150. [ "\x1b[1;32mnote\x1b[0m: This path was expected to fill the diagram"
  151. , "\t " <> render (prettyTm (quote le)) <> " " <> T.replicate 7 (T.singleton '\x2500') <> " " <> render (prettyTm (quote ri))
  152. , "\x1b[1;32mnote\x1b[0m: the " <> endp <> " endpoint is incorrect"
  153. ]
  154. , Handler \(NotEqual ta tb) -> do
  155. putStrLn . unlines $
  156. [ "\x1b[1;31merror\x1b[0m: Mismatch between actual and expected types:"
  157. , " * \x1b[1mActual\x1b[0m: " ++ showValue (zonk ta)
  158. , " * \x1b[1mExpected\x1b[0m: " ++ showValue (zonk tb)
  159. ]
  160. , Handler \(NoSuchPrimitive x) -> do
  161. putStrLn $ "Unknown primitive: " ++ T.unpack x
  162. , Handler \(NotInScope x) -> do
  163. putStrLn $ "Variable not in scope: " ++ show (pretty x)
  164. , Handler \(Elab.DeclaredUndefined n) -> do
  165. putStrLn $ "Name declared but not defined: " ++ show (pretty n)
  166. ]
  167. reportUnsolved :: Foldable t => Map.Map MV (t (Seq Projection, Value)) -> IO ()
  168. reportUnsolved metas = do
  169. for_ (Map.toList metas) \(m, p) -> do
  170. case mvSpan m of
  171. Just (f, s, e) -> T.putStrLn . squiggleUnder s e =<< T.readFile (T.unpack f)
  172. Nothing -> pure ()
  173. T.putStrLn . render $
  174. "Unsolved metavariable" <+> prettyTm (Meta m) <+> pretty ':' <+> prettyTm (quote (mvType m)) <+> "should satisfy:"
  175. for_ p \p ->
  176. T.putStrLn . render $ indent 2 $ prettyTm (quote (VNe (HMeta m) (fst p))) <+> pretty '≡' <+> prettyTm (quote (snd p))
  177. displayExceptions' :: Exception e => Text -> e -> IO ()
  178. displayExceptions' lines e = displayAndDie lines e `catch` \(_ :: ExitCode) -> pure ()
  179. squiggleUnder :: Posn -> Posn -> Text -> Text
  180. squiggleUnder (Posn l c) (Posn l' c') file
  181. | l == l' =
  182. let
  183. line = T.pack (show l) <> " | " <> T.lines file !! (l - 1)
  184. padding = T.replicate (length (show l)) (T.singleton ' ') <> " |"
  185. squiggle = T.replicate c " " <> T.pack "\x1b[1;31m" <> T.replicate (c' - c) "~" <> T.pack "\x1b[0m"
  186. in T.unlines [ padding, line, padding <> squiggle ]
  187. | otherwise = T.unlines (take (l' - l) (drop l (T.lines file)))
  188. dumpTokens :: Alex ()
  189. dumpTokens = do
  190. t <- alexMonadScan
  191. case tokenClass t of
  192. TokEof -> pure ()
  193. _ -> do
  194. traceM (show t)
  195. dumpTokens