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.

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