converted to Text
This commit is contained in:
parent
c73566d67f
commit
9ef9a8b6ba
6 changed files with 85 additions and 69 deletions
|
|
@ -3,16 +3,17 @@ module Main where
|
||||||
import Check
|
import Check
|
||||||
import Expr
|
import Expr
|
||||||
import Parser
|
import Parser
|
||||||
|
import qualified Data.Text.IO as T
|
||||||
import System.IO
|
import System.IO
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
_ <- putStr "> "
|
_ <- T.putStr "> "
|
||||||
_ <- hFlush stdout
|
_ <- hFlush stdout
|
||||||
input <- getLine
|
input <- T.getLine
|
||||||
case pAll input of
|
case pAll input of
|
||||||
Left err -> putStrLn err
|
Left err -> T.putStrLn err
|
||||||
Right expr -> case findType [] expr of
|
Right expr -> case findType [] expr of
|
||||||
Right ty -> putStrLn $ pretty expr ++ " : " ++ pretty ty
|
Right ty -> T.putStrLn $ pretty expr <> " : " <> pretty ty
|
||||||
Left err -> print err
|
Left err -> print err
|
||||||
main
|
main
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ library dependent-lambda-lib
|
||||||
, parser-combinators
|
, parser-combinators
|
||||||
, mtl
|
, mtl
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
default-extensions: OverloadedStrings
|
||||||
|
, GADTs
|
||||||
|
|
||||||
common warnings
|
common warnings
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
|
|
@ -49,8 +51,11 @@ executable dependent-lambda
|
||||||
|
|
||||||
build-depends: base ^>=4.19.1.0
|
build-depends: base ^>=4.19.1.0
|
||||||
, dependent-lambda-lib
|
, dependent-lambda-lib
|
||||||
|
, text
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
default-extensions: OverloadedStrings
|
||||||
|
, GADTs
|
||||||
|
|
||||||
test-suite tests
|
test-suite tests
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
|
|
@ -60,6 +65,9 @@ test-suite tests
|
||||||
, CheckTests
|
, CheckTests
|
||||||
build-depends: base ^>=4.19.1.0
|
build-depends: base ^>=4.19.1.0
|
||||||
, HUnit
|
, HUnit
|
||||||
|
, text
|
||||||
, dependent-lambda-lib
|
, dependent-lambda-lib
|
||||||
hs-source-dirs: tests
|
hs-source-dirs: tests
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
default-extensions: OverloadedStrings
|
||||||
|
, GADTs
|
||||||
|
|
|
||||||
14
lib/Check.hs
14
lib/Check.hs
|
|
@ -1,21 +1,23 @@
|
||||||
module Check (TypeCheckError (..), CheckResult (..), findType) where
|
module Check (TypeCheckError (..), CheckResult (..), findType) where
|
||||||
|
|
||||||
import Control.Monad.Except ( MonadError(throwError) )
|
import Control.Monad.Except (MonadError (throwError))
|
||||||
import Data.List (intercalate, (!?))
|
import Data.List (intercalate, (!?))
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
import Control.Monad (unless)
|
import Control.Monad (unless)
|
||||||
import Expr
|
import Expr
|
||||||
|
|
||||||
type Context = [Expr]
|
type Context = [Expr]
|
||||||
|
|
||||||
data TypeCheckError = SquareUntyped | UnboundVariable String | NotASort Expr Expr | ExpectedPiType Expr Expr | NotEquivalent Expr Expr Expr
|
data TypeCheckError = SquareUntyped | UnboundVariable Text | NotASort Expr Expr | ExpectedPiType Expr Expr | NotEquivalent Expr Expr Expr deriving (Eq)
|
||||||
|
|
||||||
instance Show TypeCheckError where
|
instance Show TypeCheckError where
|
||||||
show SquareUntyped = "□ does not have a type"
|
show SquareUntyped = "□ does not have a type"
|
||||||
show (UnboundVariable x) = "Unbound variable: " ++ x
|
show (UnboundVariable x) = "Unbound variable: " ++ T.unpack x
|
||||||
show (NotASort x t) = "Expected " ++ pretty x ++ " to have type * or □, instead found " ++ pretty t
|
show (NotASort x t) = "Expected " ++ prettyS x ++ " to have type * or □, instead found " ++ prettyS t
|
||||||
show (ExpectedPiType m a) = pretty m ++ " : " ++ pretty a ++ " is not a function"
|
show (ExpectedPiType m a) = prettyS m ++ " : " ++ prettyS a ++ " is not a function"
|
||||||
show (NotEquivalent a a' e) = "Cannot unify " ++ pretty a ++ " with " ++ pretty a' ++ " when evaluating " ++ pretty e
|
show (NotEquivalent a a' e) = "Cannot unify " ++ prettyS a ++ " with " ++ prettyS a' ++ " when evaluating " ++ prettyS e
|
||||||
|
|
||||||
type CheckResult = Either TypeCheckError
|
type CheckResult = Either TypeCheckError
|
||||||
|
|
||||||
|
|
|
||||||
41
lib/Expr.hs
41
lib/Expr.hs
|
|
@ -1,16 +1,16 @@
|
||||||
{-# LANGUAGE GADTs #-}
|
|
||||||
|
|
||||||
module Expr where
|
module Expr where
|
||||||
|
|
||||||
import Data.Function (on)
|
import Data.Function (on)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
data Expr where
|
data Expr where
|
||||||
Var :: Integer -> String -> Expr
|
Var :: Integer -> Text -> Expr
|
||||||
Star :: Expr
|
Star :: Expr
|
||||||
Square :: Expr
|
Square :: Expr
|
||||||
App :: Expr -> Expr -> Expr
|
App :: Expr -> Expr -> Expr
|
||||||
Abs :: String -> Expr -> Expr -> Expr
|
Abs :: Text -> Expr -> Expr -> Expr
|
||||||
Pi :: String -> Expr -> Expr -> Expr
|
Pi :: Text -> Expr -> Expr -> Expr
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
instance Eq Expr where
|
instance Eq Expr where
|
||||||
|
|
@ -32,58 +32,61 @@ occursFree n (Pi _ a b) = occursFree n a || occursFree (n + 1) b
|
||||||
|
|
||||||
{- --------------------- PRETTY PRINTING ----------------------------- -}
|
{- --------------------- PRETTY PRINTING ----------------------------- -}
|
||||||
|
|
||||||
parenthesize :: String -> String
|
parenthesize :: Text -> Text
|
||||||
parenthesize s = "(" ++ s ++ ")"
|
parenthesize s = T.concat ["(", s, ")"]
|
||||||
|
|
||||||
collectLambdas :: Expr -> ([(String, Expr)], Expr)
|
collectLambdas :: Expr -> ([(Text, Expr)], Expr)
|
||||||
collectLambdas (Abs x ty body) = ((x, ty) : params, final)
|
collectLambdas (Abs x ty body) = ((x, ty) : params, final)
|
||||||
where
|
where
|
||||||
(params, final) = collectLambdas body
|
(params, final) = collectLambdas body
|
||||||
collectLambdas e = ([], e)
|
collectLambdas e = ([], e)
|
||||||
|
|
||||||
collectPis :: Expr -> ([(String, Expr)], Expr)
|
collectPis :: Expr -> ([(Text, Expr)], Expr)
|
||||||
collectPis p@(Pi "" _ _) = ([], p)
|
collectPis p@(Pi "" _ _) = ([], p)
|
||||||
collectPis (Pi x ty body) = ((x, ty) : params, final)
|
collectPis (Pi x ty body) = ((x, ty) : params, final)
|
||||||
where
|
where
|
||||||
(params, final) = collectPis body
|
(params, final) = collectPis body
|
||||||
collectPis e = ([], e)
|
collectPis e = ([], e)
|
||||||
|
|
||||||
groupParams :: [(String, Expr)] -> [([String], Expr)]
|
groupParams :: [(Text, Expr)] -> [([Text], Expr)]
|
||||||
groupParams = foldr addParam []
|
groupParams = foldr addParam []
|
||||||
where
|
where
|
||||||
addParam :: (String, Expr) -> [([String], Expr)] -> [([String], Expr)]
|
addParam :: (Text, Expr) -> [([Text], Expr)] -> [([Text], Expr)]
|
||||||
addParam (x, t) [] = [([x], t)]
|
addParam (x, t) [] = [([x], t)]
|
||||||
addParam (x, t) l@((xs, s) : rest)
|
addParam (x, t) l@((xs, s) : rest)
|
||||||
| incIndices t == s = (x : xs, t) : rest
|
| incIndices t == s = (x : xs, t) : rest
|
||||||
| otherwise = ([x], t) : l
|
| otherwise = ([x], t) : l
|
||||||
|
|
||||||
showParamGroup :: ([String], Expr) -> String
|
showParamGroup :: ([Text], Expr) -> Text
|
||||||
showParamGroup (ids, ty) = parenthesize $ unwords ids ++ " : " ++ pretty ty
|
showParamGroup (ids, ty) = parenthesize $ T.unwords ids <> " : " <> pretty ty
|
||||||
|
|
||||||
helper :: Integer -> Expr -> String
|
helper :: Integer -> Expr -> Text
|
||||||
helper _ (Var _ s) = s
|
helper _ (Var _ s) = s
|
||||||
helper _ Star = "*"
|
helper _ Star = "*"
|
||||||
helper _ Square = "□"
|
helper _ Square = "□"
|
||||||
helper k (App e1 e2) = if k > 3 then parenthesize res else res
|
helper k (App e1 e2) = if k > 3 then parenthesize res else res
|
||||||
where
|
where
|
||||||
res = helper 3 e1 ++ " " ++ helper 4 e2
|
res = helper 3 e1 <> " " <> helper 4 e2
|
||||||
helper k (Pi "" t1 t2) = if k > 2 then parenthesize res else res
|
helper k (Pi "" t1 t2) = if k > 2 then parenthesize res else res
|
||||||
where
|
where
|
||||||
res = helper 3 t1 ++ " -> " ++ helper 2 t2
|
res = helper 3 t1 <> " -> " <> helper 2 t2
|
||||||
helper k e@(Pi{}) = if k > 2 then parenthesize res else res
|
helper k e@(Pi{}) = if k > 2 then parenthesize res else res
|
||||||
where
|
where
|
||||||
(params, body) = collectPis e
|
(params, body) = collectPis e
|
||||||
grouped = showParamGroup <$> groupParams params
|
grouped = showParamGroup <$> groupParams params
|
||||||
res = "∏ " ++ unwords grouped ++ " . " ++ pretty body
|
res = "∏ " <> T.unwords grouped <> " . " <> pretty body
|
||||||
helper k e@(Abs{}) = if k >= 1 then parenthesize res else res
|
helper k e@(Abs{}) = if k >= 1 then parenthesize res else res
|
||||||
where
|
where
|
||||||
(params, body) = collectLambdas e
|
(params, body) = collectLambdas e
|
||||||
grouped = showParamGroup <$> groupParams params
|
grouped = showParamGroup <$> groupParams params
|
||||||
res = "λ " ++ unwords grouped ++ " . " ++ pretty body
|
res = "λ " <> T.unwords grouped <> " . " <> pretty body
|
||||||
|
|
||||||
pretty :: Expr -> String
|
pretty :: Expr -> Text
|
||||||
pretty = helper 0
|
pretty = helper 0
|
||||||
|
|
||||||
|
prettyS :: Expr -> String
|
||||||
|
prettyS = T.unpack . pretty
|
||||||
|
|
||||||
{- --------------- ACTUAL MATH STUFF ---------------- -}
|
{- --------------- ACTUAL MATH STUFF ---------------- -}
|
||||||
|
|
||||||
isSort :: Expr -> Bool
|
isSort :: Expr -> Bool
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,22 @@ import Data.Functor.Identity
|
||||||
import Data.List (elemIndex)
|
import Data.List (elemIndex)
|
||||||
import Data.List.NonEmpty (NonEmpty ((:|)))
|
import Data.List.NonEmpty (NonEmpty ((:|)))
|
||||||
import qualified Data.List.NonEmpty as NE
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
import Expr (Expr (..), incIndices)
|
import Expr (Expr (..), incIndices)
|
||||||
import Text.Megaparsec hiding (State)
|
import Text.Megaparsec hiding (State)
|
||||||
import Text.Megaparsec.Char
|
import Text.Megaparsec.Char
|
||||||
import qualified Text.Megaparsec.Char.Lexer as L
|
import qualified Text.Megaparsec.Char.Lexer as L
|
||||||
|
|
||||||
type InnerState = [String]
|
type InnerState = [Text]
|
||||||
|
|
||||||
data CustomErrors = UnboundVariable String [String] deriving (Eq, Ord, Show)
|
data CustomErrors = UnboundVariable Text [Text] deriving (Eq, Ord, Show)
|
||||||
|
|
||||||
instance ShowErrorComponent CustomErrors where
|
instance ShowErrorComponent CustomErrors where
|
||||||
showErrorComponent (UnboundVariable var bound) =
|
showErrorComponent (UnboundVariable var bound) =
|
||||||
"Unbound variable: " ++ var ++ ". Did you mean one of: " ++ unwords bound ++ "?"
|
"Unbound variable: " ++ T.unpack var ++ ". Did you mean one of: " ++ T.unpack (T.unwords bound) ++ "?"
|
||||||
|
|
||||||
type Parser = ParsecT CustomErrors String (State InnerState)
|
type Parser = ParsecT CustomErrors Text (State InnerState)
|
||||||
|
|
||||||
skipSpace :: Parser ()
|
skipSpace :: Parser ()
|
||||||
skipSpace =
|
skipSpace =
|
||||||
|
|
@ -32,11 +34,11 @@ skipSpace =
|
||||||
lexeme :: Parser a -> Parser a
|
lexeme :: Parser a -> Parser a
|
||||||
lexeme = L.lexeme skipSpace
|
lexeme = L.lexeme skipSpace
|
||||||
|
|
||||||
pIdentifier :: Parser String
|
pIdentifier :: Parser Text
|
||||||
pIdentifier = label "identifier" $ lexeme $ do
|
pIdentifier = label "identifier" $ lexeme $ do
|
||||||
firstChar <- letterChar <|> char '_'
|
firstChar <- letterChar <|> char '_'
|
||||||
rest <- many $ alphaNumChar <|> char '_'
|
rest <- many $ alphaNumChar <|> char '_'
|
||||||
return $ firstChar : rest
|
return $ T.pack (firstChar : rest) -- Still need T.pack here as we're building from chars
|
||||||
|
|
||||||
pVar :: Parser Expr
|
pVar :: Parser Expr
|
||||||
pVar = label "variable" $ lexeme $ do
|
pVar = label "variable" $ lexeme $ do
|
||||||
|
|
@ -46,12 +48,10 @@ pVar = label "variable" $ lexeme $ do
|
||||||
Just i -> return $ Var (fromIntegral i) var
|
Just i -> return $ Var (fromIntegral i) var
|
||||||
Nothing -> customFailure $ UnboundVariable var binders
|
Nothing -> customFailure $ UnboundVariable var binders
|
||||||
|
|
||||||
defChoice :: NE.NonEmpty String -> Parser ()
|
defChoice :: NE.NonEmpty Text -> Parser ()
|
||||||
defChoice options = lexeme $ label labelText $ void $ choice $ NE.map string options
|
defChoice options = lexeme $ label (T.unpack $ NE.head options) $ void $ choice $ NE.map chunk options
|
||||||
where
|
|
||||||
labelText = NE.head options
|
|
||||||
|
|
||||||
pParamGroup :: Parser [(String, Expr)]
|
pParamGroup :: Parser [(Text, Expr)]
|
||||||
pParamGroup = lexeme $ label "parameter group" $ between (char '(') (char ')') $ do
|
pParamGroup = lexeme $ label "parameter group" $ between (char '(') (char ')') $ do
|
||||||
idents <- some pIdentifier
|
idents <- some pIdentifier
|
||||||
_ <- defChoice $ ":" :| []
|
_ <- defChoice $ ":" :| []
|
||||||
|
|
@ -59,7 +59,7 @@ pParamGroup = lexeme $ label "parameter group" $ between (char '(') (char ')') $
|
||||||
modify (flip (foldl $ flip (:)) idents)
|
modify (flip (foldl $ flip (:)) idents)
|
||||||
pure $ zip idents (iterate incIndices ty)
|
pure $ zip idents (iterate incIndices ty)
|
||||||
|
|
||||||
pParams :: Parser [(String, Expr)]
|
pParams :: Parser [(Text, Expr)]
|
||||||
pParams = concat <$> some pParamGroup
|
pParams = concat <$> some pParamGroup
|
||||||
|
|
||||||
pLAbs :: Parser Expr
|
pLAbs :: Parser Expr
|
||||||
|
|
@ -112,5 +112,5 @@ pAppTerm = lexeme $ pLAbs <|> pPAbs <|> pApp
|
||||||
pExpr :: Parser Expr
|
pExpr :: Parser Expr
|
||||||
pExpr = lexeme $ try pArrow <|> pAppTerm
|
pExpr = lexeme $ try pArrow <|> pAppTerm
|
||||||
|
|
||||||
pAll :: String -> Either String Expr
|
pAll :: Text -> Either Text Expr
|
||||||
pAll input = first errorBundlePretty $ fst $ runIdentity $ runStateT (runParserT pExpr "" input) []
|
pAll input = first (T.pack . errorBundlePretty) $ fst $ runIdentity $ runStateT (runParserT pExpr "" input) []
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
module ParserTests (tests) where
|
module ParserTests (tests) where
|
||||||
|
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
import Expr (Expr (..))
|
import Expr (Expr (..))
|
||||||
import Parser (pAll)
|
import Parser (pAll)
|
||||||
import Test.HUnit
|
import Test.HUnit
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue