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.

154 lines
5.3 KiB

  1. {-# LANGUAGE BlockArguments #-}
  2. {-# LANGUAGE OverloadedStrings #-}
  3. {-# LANGUAGE ScopedTypeVariables #-}
  4. module Main where
  5. import Control.Exception
  6. import qualified Data.ByteString.Lazy as Bsl
  7. import qualified Data.Text.Encoding as T
  8. import qualified Data.Map.Strict as Map
  9. import qualified Data.Text.IO as T
  10. import qualified Data.Text as T
  11. import Data.Text ( Text )
  12. import Data.Foldable
  13. import Elab.Monad hiding (switch)
  14. import Elab.Eval
  15. import Elab
  16. import Presyntax.Presyntax (Posn(Posn))
  17. import Presyntax.Parser
  18. import Presyntax.Lexer
  19. import System.Exit
  20. import Syntax.Pretty
  21. import Elab.WiredIn
  22. import Options.Applicative
  23. import Control.Monad
  24. import Syntax
  25. import Prettyprinter
  26. import Control.Monad.IO.Class
  27. import Control.Monad.Reader
  28. import System.Console.Haskeline
  29. main :: IO ()
  30. main = do
  31. opts <- execParser parser
  32. case opts of
  33. Load files -> do
  34. env <- checkFiles files
  35. enterReplIn env
  36. Check files verbose -> do
  37. env <- checkFiles files
  38. when verbose $ dumpEnv (getEnv env)
  39. Repl -> enterReplIn emptyEnv
  40. enterReplIn :: ElabEnv -> IO ()
  41. enterReplIn env = runInputT defaultSettings (loop env') where
  42. env' = env { commHook = T.putStrLn . render . prettyTm . quote . zonk }
  43. loop :: ElabEnv -> InputT IO ()
  44. loop env = do
  45. inp <- fmap T.pack <$> getInputLine "% "
  46. case inp of
  47. Nothing -> pure ()
  48. Just inp ->
  49. case runAlex (Bsl.fromStrict (T.encodeUtf8 inp)) parseRepl of
  50. Left e -> do
  51. liftIO $ print e
  52. loop env
  53. Right st -> do
  54. env <- liftIO $
  55. runElab (checkStatement st ask) env
  56. `catch` \e -> do
  57. displayExceptions' inp (e :: SomeException)
  58. pure env
  59. loop env
  60. checkFiles :: [String] -> IO ElabEnv
  61. checkFiles files = runElab (go files ask) emptyEnv where
  62. go [] k = k
  63. go (x:xs) k = do
  64. code <- liftIO $ Bsl.readFile x
  65. case runAlex code parseProg of
  66. Left e -> liftIO $ print e *> error (show e)
  67. Right prog -> do
  68. env <- ask
  69. liftIO $ runElab (checkProgram prog (go xs k)) env
  70. `catch` \e -> displayAndDie (T.decodeUtf8 (Bsl.toStrict code)) (e :: SomeException)
  71. dumpEnv :: Map.Map Name (NFType, Value) -> IO ()
  72. dumpEnv env = for_ (Map.toList env) $ \(name, (nft, _)) ->
  73. T.putStrLn $ render (pretty name <+> colon <+> prettyTm (quote (zonk nft)))
  74. parser :: ParserInfo Opts
  75. parser = info (subparser (load <> check) <|> pure Repl <**> helper) (header "cubical - a cubical programming language")
  76. where
  77. load = command "load" $ info (fmap Load (some (argument str (metavar "file..."))) <**> helper) (progDesc "Check and load a list of files in the REPL")
  78. check = command "check" $
  79. info ((Check <$> some (argument str (metavar "file..."))
  80. <*> switch ( long "verbose"
  81. <> short 'v'
  82. <> help "Print the types of all declared/defined values"))
  83. <**> helper)
  84. (progDesc "Check a list of files")
  85. data Opts
  86. = Load { files :: [String] }
  87. | Check { files :: [String], verbose :: Bool }
  88. | Repl
  89. deriving (Eq, Show, Ord)
  90. displayAndDie :: Exception e => Text -> e -> IO a
  91. displayAndDie lines e = do
  92. () <- throwIO e `catches` displayExceptions lines
  93. exitFailure
  94. displayExceptions :: Text -> [Handler ()]
  95. displayExceptions lines =
  96. [ Handler \(WhileChecking a b e) -> do
  97. T.putStrLn $ squiggleUnder a b lines
  98. displayExceptions' lines e
  99. , Handler \(SeeAlso a b e) -> do
  100. displayExceptions' lines e
  101. T.putStrLn $ squiggleUnder a b lines
  102. , Handler \(AttachedNote n e) -> do
  103. displayExceptions' lines e
  104. T.putStrLn $ "\x1b[1;32mnote\x1b[0m: " <> render n
  105. , Handler \(WhenCheckingEndpoint le ri ep e) -> do
  106. displayExceptions' lines e
  107. let
  108. endp = case ep of
  109. VI0 -> T.pack "left"
  110. VI1 -> T.pack "right"
  111. _ -> T.pack $ show (prettyTm (quote ep))
  112. T.putStrLn . T.unlines $
  113. [ "\x1b[1;32mnote\x1b[0m: This path was expected to fill the diagram"
  114. , "\t " <> render (prettyTm (quote le)) <> " " <> T.replicate 7 (T.singleton '\x2500') <> " " <> render (prettyTm (quote ri))
  115. , "\x1b[1;32mnote\x1b[0m: the " <> endp <> " endpoint is incorrect"
  116. ]
  117. , Handler \(NotEqual ta tb) -> do
  118. putStrLn . unlines $
  119. [ "\x1b[1;31merror\x1b[0m: Mismatch between actual and expected types:"
  120. , " * \x1b[1mActual\x1b[0m: " ++ showValue (zonk ta)
  121. , " * \x1b[1mExpected\x1b[0m: " ++ showValue (zonk tb)
  122. ]
  123. , Handler \(NoSuchPrimitive x) -> do
  124. putStrLn $ "Unknown primitive: " ++ T.unpack x
  125. , Handler \(NotInScope x) -> do
  126. putStrLn $ "Variable not in scope: " ++ show x
  127. ]
  128. displayExceptions' :: Exception e => Text -> e -> IO ()
  129. displayExceptions' lines e = displayAndDie lines e `catch` \(_ :: ExitCode) -> pure ()
  130. squiggleUnder :: Posn -> Posn -> Text -> Text
  131. squiggleUnder (Posn l c) (Posn l' c') file
  132. | l == l' =
  133. let
  134. line = T.pack (show l) <> " | " <> T.lines file !! (l - 1)
  135. padding = T.replicate (length (show l)) (T.singleton ' ') <> " |"
  136. squiggle = T.replicate c " " <> T.pack "\x1b[1;31m" <> T.replicate (c' - c) "~" <> T.pack "\x1b[0m"
  137. in T.unlines [ padding, line, padding <> squiggle ]
  138. | otherwise = T.pack (show (Posn l c, Posn l' c'))