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

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveAnyClass #-}
module Main where
import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Exception
import qualified Data.ByteString.Lazy as Bsl
import qualified Data.Text.Encoding as T
import qualified Data.Map.Strict as Map
import qualified Data.Text.IO as T
import qualified Data.Set as Set
import qualified Data.Text as T
import Data.Sequence (Seq)
import Data.Text ( Text )
import Data.Foldable
import Data.Maybe
import Data.IORef
import Debug.Trace
import Elab.Monad hiding (switch)
import Elab.WiredIn
import Elab.Eval
import Elab
import Options.Applicative
import Presyntax.Presyntax (Posn(Posn))
import Presyntax.Parser
import Presyntax.Tokens
import Presyntax.Lexer
import Prettyprinter
import Syntax.Pretty
import Syntax
import System.Console.Haskeline
import System.Exit
main :: IO ()
main = do
opts <- execParser parser
case opts of
Load files exp -> do
env <- checkFiles files
case exp of
[] -> enterReplIn env
xs -> traverse_ (evalArgExpr env) xs
Check files verbose -> do
env <- checkFiles files
when verbose $ dumpEnv env
Repl -> enterReplIn =<< checkFiles ["./test.tt"]
evalArgExpr :: ElabEnv -> String -> IO ()
evalArgExpr env str =
case runAlex (Bsl.fromStrict (T.encodeUtf8 inp)) parseExp of
Right e ->
flip runElab env (do
(e, _) <- infer e
liftIO . putStrLn . show . prettyTm . quote . zonk =<< Elab.Eval.eval e)
`catch` \e -> do
displayExceptions' inp (e :: SomeException)
Left e -> liftIO $ print e
where
inp = T.pack str
enterReplIn :: ElabEnv -> IO ()
enterReplIn env = runInputT defaultSettings (loop env') where
env' = env { commHook = T.putStrLn . render . prettyTm . quote . zonk }
loop :: ElabEnv -> InputT IO ()
loop env = do
inp <- fmap T.pack <$> getInputLine "% "
case inp of
Nothing -> pure ()
Just inp ->
case runAlex (Bsl.fromStrict (T.encodeUtf8 inp)) parseRepl of
Left e -> do
liftIO $ print e
loop env
Right st -> do
env <- liftIO $
runElab (checkStatement st ask) env
`catch` \e -> do
displayExceptions' inp (e :: SomeException)
pure env
loop env
checkFiles :: [String] -> IO ElabEnv
checkFiles files = runElab (go files ask) =<< emptyEnv where
go [] k = do
env <- ask
for_ (Map.toList (nameMap env)) \case
(_, v@Defined{})
| Set.member v (definedNames env) -> pure ()
| otherwise ->
let
pos = fromJust (Map.lookup v (whereBound env))
in withSpan (fst pos) (snd pos) $ throwElab $ Elab.DeclaredUndefined v
_ -> pure ()
metas <- liftIO $ readIORef (unsolvedMetas env)
unless (Map.null metas) $ do
liftIO $ reportUnsolved metas
k
go (x:xs) k = do
code <- liftIO $ Bsl.readFile x
case runAlex (code <> Bsl.singleton 10) parseProg of
Left e -> liftIO $ print e *> error (show e)
Right prog ->
local (\e -> e { currentFile = Just (T.pack x) }) (checkProgram prog (go xs k))
`catchElab` \e -> liftIO $ displayAndDie (T.decodeUtf8 (Bsl.toStrict code)) (e :: SomeException)
dumpEnv :: ElabEnv -> IO ()
dumpEnv env = for_ (Map.toList (nameMap env)) $ \(_, name) ->
let nft = fst $ getEnv env Map.! name in
T.putStrLn $ render (pretty name <+> align (nest (negate 1) (colon <+> prettyTm (quote (zonk nft)))))
parser :: ParserInfo Opts
parser = info (subparser (load <> check) <|> pure Repl <**> helper) (header "cubical - a cubical programming language")
where
load = command "load" $
info ((Load <$> (some (argument str (metavar "file...")))
<*> (many (strOption (long "eval" <> short 'e' <> help "Also evaluate this expression"))))
<**> helper) (progDesc "Check and load a list of files in the REPL")
check = command "check" $
info ((Check <$> some (argument str (metavar "file..."))
<*> switch ( long "verbose"
<> short 'v'
<> help "Print the types of all declared/defined values"))
<**> helper)
(progDesc "Check a list of files")
data Opts
= Load { files :: [String], eval :: [String] }
| Check { files :: [String], verbose :: Bool }
| Repl
deriving (Eq, Show, Ord)
displayAndDie :: Exception e => Text -> e -> IO a
displayAndDie lines e = do
() <- throwIO e `catches` displayExceptions lines
exitFailure
displayExceptions :: Text -> [Handler ()]
displayExceptions lines =
[ Handler \(WhileChecking a b e) -> do
T.putStrLn $ squiggleUnder a b lines
displayExceptions' lines e
, Handler \(SeeAlso a b e) -> do
displayExceptions' lines e
T.putStrLn $ squiggleUnder a b lines
, Handler \(AttachedNote n e) -> do
displayExceptions' lines e
T.putStrLn $ "\x1b[1;32mnote\x1b[0m: " <> render n
, Handler \(WhenCheckingEndpoint le ri ep e) -> do
displayExceptions' lines e
let
endp = case ep of
VI0 -> T.pack "left"
VI1 -> T.pack "right"
_ -> T.pack $ show (prettyTm (quote ep))
T.putStrLn . T.unlines $
[ "\x1b[1;32mnote\x1b[0m: This path was expected to fill the diagram"
, "\t " <> render (prettyTm (quote le)) <> " " <> T.replicate 7 (T.singleton '\x2500') <> " " <> render (prettyTm (quote ri))
, "\x1b[1;32mnote\x1b[0m: the " <> endp <> " endpoint is incorrect"
]
, Handler \(NotEqual ta tb) -> do
putStrLn . unlines $
[ "\x1b[1;31merror\x1b[0m: Mismatch between actual and expected types:"
, " * \x1b[1mActual\x1b[0m: " ++ showValue (zonk ta)
, " * \x1b[1mExpected\x1b[0m: " ++ showValue (zonk tb)
]
, Handler \(NoSuchPrimitive x) -> do
putStrLn $ "Unknown primitive: " ++ T.unpack x
, Handler \(NotInScope x) -> do
putStrLn $ "Variable not in scope: " ++ show (pretty x)
, Handler \(Elab.DeclaredUndefined n) -> do
putStrLn $ "Name declared but not defined: " ++ show (pretty n)
]
reportUnsolved :: Foldable t => Map.Map MV (t (Seq Projection, Value)) -> IO ()
reportUnsolved metas = do
for_ (Map.toList metas) \(m, p) -> do
case mvSpan m of
Just (f, s, e) -> T.putStrLn . squiggleUnder s e =<< T.readFile (T.unpack f)
Nothing -> pure ()
T.putStrLn . render $
"Unsolved metavariable" <+> prettyTm (Meta m) <+> pretty ':' <+> prettyTm (quote (mvType m)) <+> "should satisfy:"
for_ p \p ->
T.putStrLn . render $ indent 2 $ prettyTm (quote (VNe (HMeta m) (fst p))) <+> pretty '≡' <+> prettyTm (quote (snd p))
displayExceptions' :: Exception e => Text -> e -> IO ()
displayExceptions' lines e = displayAndDie lines e `catch` \(_ :: ExitCode) -> pure ()
squiggleUnder :: Posn -> Posn -> Text -> Text
squiggleUnder (Posn l c) (Posn l' c') file
| l == l' =
let
line = T.pack (show l) <> " | " <> T.lines file !! (l - 1)
padding = T.replicate (length (show l)) (T.singleton ' ') <> " |"
squiggle = T.replicate c " " <> T.pack "\x1b[1;31m" <> T.replicate (c' - c) "~" <> T.pack "\x1b[0m"
in T.unlines [ padding, line, padding <> squiggle ]
| otherwise = T.unlines (take (l' - l) (drop l (T.lines file)))
dumpTokens :: Alex ()
dumpTokens = do
t <- alexMonadScan
case tokenClass t of
TokEof -> pure ()
_ -> do
traceM (show t)
dumpTokens