|
{-# LANGUAGE LambdaCase #-}
|
|
module Main where
|
|
|
|
import Control.Monad.IO.Class
|
|
import Control.Monad.Catch
|
|
|
|
import qualified Data.Map.Strict as Map
|
|
import Data.List
|
|
|
|
import Elab
|
|
|
|
import Eval (eval, UnifyError (..))
|
|
|
|
import Presyntax.Parser
|
|
import Presyntax
|
|
|
|
import Syntax
|
|
|
|
import System.Console.Haskeline (runInputT, defaultSettings, getInputLine)
|
|
import System.Exit
|
|
|
|
import Systems (formulaOfFace, Face)
|
|
import System.Environment
|
|
|
|
showTypeError :: Maybe [String] -> Elab.TypeError -> String
|
|
showTypeError lines (NotInScope l_c) = "Variable not in scope: " ++ l_c
|
|
|
|
showTypeError lines (UnifyError (NotPiType vl)) = "Not a function type: " ++ show vl
|
|
showTypeError lines (UnifyError (NotSigmaType vl)) = "Not a sigma type: " ++ show vl
|
|
showTypeError lines (UnifyError (Mismatch a b)) =
|
|
unlines [ "Types are not equal: "
|
|
, " " ++ show (quote a)
|
|
, " vs "
|
|
, " " ++ show (quote b)
|
|
]
|
|
|
|
showTypeError lines (WrongFaces _ faces) = unlines (map face faces) where
|
|
face :: ([Value], Value, Elab.TypeError) -> String
|
|
face (ixs, rhs, err) =
|
|
"When checking face described by " ++ show ixs ++ ":\n" ++ showTypeError Nothing err
|
|
|
|
showTypeError lines (InSpan start end err)
|
|
| Just lines <- lines, fst start == fst end
|
|
= makeRange (lines !! fst start) start end ++ '\n':showTypeError Nothing err
|
|
| otherwise = showTypeError Nothing err
|
|
|
|
showTypeError lines (IncompleteSystem formula extent) =
|
|
unlines $
|
|
[ "Incomplete system: "
|
|
, "it is defined by " ++ show formula
|
|
, "but the context mandates extent " ++ show extent ]
|
|
|
|
showTypeError lines (IncompatibleFaces (fa, ta) (fb, tb) err) =
|
|
unlines [ showTypeError lines err
|
|
, "while checking that these overlapping faces are compatible:"
|
|
, "* " ++ show fa ++ " -> " ++ show ta
|
|
, "* " ++ show fb ++ " -> " ++ show tb
|
|
]
|
|
|
|
showTypeError _ x = show x
|
|
|
|
makeRange :: String -> (Int, Int) -> (Int, Int) -> String
|
|
makeRange line (_, sc) (_, ec) = line ++ "\n" ++ replicate (sc + 1) ' ' ++ replicate (ec - sc) '~'
|
|
|
|
main :: IO ()
|
|
main = do
|
|
t <- getArgs
|
|
case t of
|
|
[file] -> Main.check file
|
|
_ -> repl
|
|
|
|
check :: FilePath -> IO ()
|
|
check file = do
|
|
code <- readFile file
|
|
let
|
|
ste e = do
|
|
putStrLn $ showTypeError (Just (lines code)) e
|
|
exitFailure
|
|
case parseString body code of
|
|
Left e -> print e
|
|
Right x -> do
|
|
(tm, _) <- infer (Env mempty) x `catch` ste `catch` (ste . UnifyError)
|
|
print (eval (Env mempty) tm)
|
|
|
|
repl :: IO ()
|
|
repl = runInputT defaultSettings (go (Env mempty)) where
|
|
go env = getInputLine "> " >>= \case
|
|
Just i | ":sq " `isPrefixOf` i -> do
|
|
case parseString body (replicate 4 ' ' ++ drop 4 i) of
|
|
Right exp ->
|
|
(do
|
|
(tm, ty) <- liftIO $ infer env exp
|
|
liftIO $ drawExtent ty (eval env tm)
|
|
`catch` \e -> liftIO $ putStrLn (showTypeError (Just [i]) e))
|
|
`catch` \e -> liftIO $ print (e :: SomeException)
|
|
Left e -> liftIO (print e)
|
|
go env
|
|
Just i ->
|
|
case parseString statement i of
|
|
Left e -> liftIO (print e) *> go env
|
|
Right (Assume vs) ->
|
|
let
|
|
loop env ((v, t):vs) = do
|
|
tm <- liftIO $ Elab.check env t VTypeω
|
|
let ty = eval env tm
|
|
loop env{ names = Map.insert v (ty, VVar v) (names env) } vs
|
|
loop env [] = go env
|
|
in (loop env vs
|
|
`catch` \e -> (liftIO $ putStrLn (showTypeError (Just [i]) e)) *> go env)
|
|
`catch` \e -> (liftIO $ print (e :: SomeException)) *> go env
|
|
Right (Eval v) -> do
|
|
liftIO $
|
|
(do
|
|
(tm, ty) <- infer env v
|
|
let v_nf = eval env tm
|
|
putStrLn $ show v_nf ++ " : " ++ show ty
|
|
`catch` \e -> putStrLn (showTypeError (Just [i]) e))
|
|
`catch` \e -> print (e :: SomeException)
|
|
go env
|
|
Right (Declare n t e) -> do
|
|
(do
|
|
t <- liftIO $ Elab.check env t VTypeω
|
|
let t' = eval env t
|
|
b <- liftIO $ Elab.check env e t'
|
|
let b' = eval env b
|
|
go env{ names = Map.insert n (t', b') (names env) })
|
|
`catch` \e -> (liftIO $ putStrLn (showTypeError (Just [i]) e)) *> go env
|
|
`catch` \e -> (liftIO $ print (e :: SomeException)) *> go env
|
|
Nothing -> pure ()
|
|
|
|
drawExtent :: Value -> Value -> IO ()
|
|
drawExtent ty vl = nicely $ getDirections ty vl where
|
|
getDirections :: Value -> Value -> [([(String, Bool)], Value, Value)]
|
|
getDirections (VPi _ VI r) (VLam s VI k) =
|
|
let trues = getDirections (r VI1) (k VI1)
|
|
falses = getDirections (r VI0) (k VI0)
|
|
in map (\(p, t, v) -> ((s, True):p, t, v)) trues
|
|
++ map (\(p, t, v) -> ((s, False):p, t, v)) falses
|
|
getDirections ty t = [([], ty, t)]
|
|
|
|
nicely :: [([(String, Bool)], Value, Value)] -> IO ()
|
|
nicely [] = pure ()
|
|
nicely ((bs, ty, el):fcs) = do
|
|
putStr . unwords $ theFace bs
|
|
putStrLn $ " => " ++ show el ++ " : " ++ show ty
|
|
nicely fcs
|
|
|
|
theFace = map (\(i, b) ->
|
|
if b
|
|
then "(" ++ i ++ "1)"
|
|
else "(" ++ i ++ "0)")
|