parser just about taken care of

This commit is contained in:
William Ball 2024-11-30 20:34:09 -08:00
parent 57bffe00b5
commit b236bb1753
3 changed files with 125 additions and 129 deletions

44
lib/IR.hs Normal file
View file

@ -0,0 +1,44 @@
{-# LANGUAGE TemplateHaskell #-}
module IR where
import Control.Lens
type Param = (Text, IRExpr)
data IRExpr
= Var {_varName :: Text}
| Axiom
| Level {_level :: Integer}
| App
{ _appFunc :: IRExpr
, _appArg :: IRExpr
}
| Abs
{ _absParamName :: Text
, _absParamType :: IRExpr
, _absBody :: IRExpr
}
| Pi
{ _piParamName :: Text
, _piParamType :: IRExpr
, _piBody :: IRExpr
}
| Let
{ _letVarName :: Text
, _letAscription :: Maybe IRExpr
, _letValue :: IRExpr
, _letBody :: IRExpr
}
deriving (Show, Eq, Ord)
makeLenses ''IRExpr
data IRDef = Def
{ _defName :: Text
, _defParams :: [Param]
, _defAscription :: Maybe IRExpr
, _defBody :: IRExpr
}
type IRProgram = [IRDef]

View file

@ -1,37 +1,21 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TupleSections #-}
module Parser (parseDef, parseDefEmpty, parseExpr, parseProgram, handleFile) where module Parser where
import Check
import Control.Monad.Except import Control.Monad.Except
import Data.List (elemIndex, foldl, foldl1) import Data.List (foldl1)
import qualified Data.Map.Strict as M
import qualified Data.Text as T import qualified Data.Text as T
import Errors (Error (..)) import Errors (Error (..))
import Eval import IR
import Expr (Expr (..), incIndices)
import Preprocessor import Preprocessor
import Relude.Extra.Lens import Text.Megaparsec (Parsec, ShowErrorComponent (..), between, choice, chunk, errorBundlePretty, label, runParser, try)
import Text.Megaparsec (ParsecT, ShowErrorComponent (..), between, choice, chunk, customFailure, errorBundlePretty, label, runParserT, try)
import Text.Megaparsec.Char import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L import qualified Text.Megaparsec.Char.Lexer as L
data InnerState = IS {_binders :: [Text], _env :: Env}
bindsL :: Lens' InnerState [Text]
bindsL = lens _binders setter
where
setter (IS{_env}) new = IS{_env, _binders = new}
envL :: Lens' InnerState Env
envL = lens _env setter
where
setter (IS{_binders}) new = IS{_env = new, _binders}
newtype TypeError = TE Error newtype TypeError = TE Error
deriving (Eq, Ord) deriving (Eq, Ord)
type Parser = ParsecT TypeError Text (State InnerState) type Parser = Parsec TypeError Text
instance ShowErrorComponent TypeError where instance ShowErrorComponent TypeError where
showErrorComponent (TE e) = toString e showErrorComponent (TE e) = toString e
@ -62,142 +46,112 @@ pIdentifier = try $ label "identifier" $ lexeme $ do
"Reserved word: " ++ T.unpack ident "Reserved word: " ++ T.unpack ident
pure ident pure ident
pVar :: Parser Expr pVar :: Parser IRExpr
pVar = label "variable" $ lexeme $ do pVar = label "variable" $ lexeme $ Var <$> pIdentifier
name <- pIdentifier
binders <- view bindsL <$> get
pure $ Var name . fromIntegral <$> elemIndex name binders ?: Free name
defChoice :: NonEmpty Text -> Parser () defChoice :: NonEmpty Text -> Parser ()
defChoice options = lexeme $ label (T.unpack $ head options) $ void $ choice $ fmap chunk options defChoice options = lexeme $ label (T.unpack $ head options) $ void $ choice $ fmap chunk options
pParamGroup :: Parser [(Text, Expr)] pParamGroup :: Parser [Param]
pParamGroup = lexeme $ label "parameter group" $ between (char '(') (char ')') $ do pParamGroup = lexeme $ label "parameter group" $ between (char '(') (char ')') $ do
idents <- some pIdentifier idents <- some pIdentifier
eat ":" eat ":"
ty <- pExpr ty <- pIRExpr
modify $ over bindsL $ flip (foldl $ flip (:)) idents pure $ map (,ty) idents
pure $ zip idents (iterate incIndices ty)
pSomeParams :: Parser [(Text, Expr)] pSomeParams :: Parser [Param]
pSomeParams = lexeme $ concat <$> some pParamGroup pSomeParams = lexeme $ concat <$> some pParamGroup
pManyParams :: Parser [(Text, Expr)] pManyParams :: Parser [Param]
pManyParams = lexeme $ concat <$> many pParamGroup pManyParams = lexeme $ concat <$> many pParamGroup
withBinders :: Parser a -> Parser a pLAbs :: Parser IRExpr
withBinders parser = do pLAbs = lexeme $ label "λ-abstraction" $ do
oldBinders <- view bindsL <$> get
result <- parser
modify $ set bindsL oldBinders
pure result
pLAbs :: Parser Expr
pLAbs = lexeme $ label "λ-abstraction" $ withBinders $ do
_ <- defChoice $ "λ" :| ["fun"] _ <- defChoice $ "λ" :| ["fun"]
params <- pSomeParams params <- pSomeParams
_ <- defChoice $ "=>" :| [""] _ <- defChoice $ "=>" :| [""]
body <- pExpr body <- pIRExpr
pure $ foldr (uncurry Abs) body params pure $ foldr (uncurry Abs) body params
pPAbs :: Parser Expr pPAbs :: Parser IRExpr
pPAbs = lexeme $ label "Π-abstraction" $ withBinders $ do pPAbs = lexeme $ label "Π-abstraction" $ do
_ <- defChoice $ "" :| ["forall", ""] _ <- defChoice $ "" :| ["forall", ""]
params <- pSomeParams params <- pSomeParams
eat "," eat ","
body <- pExpr body <- pIRExpr
pure $ foldr (uncurry Pi) body params pure $ foldr (uncurry Pi) body params
pBinding :: Parser (Text, Expr) pBinding :: Parser (Text, Maybe IRExpr, IRExpr)
pBinding = lexeme $ label "binding" $ do pBinding = lexeme $ label "binding" $ do
env <- get
eat "(" eat "("
ident <- pIdentifier ident <- pIdentifier
params <- pManyParams params <- pManyParams
ascription <- pAscription
eat ":=" eat ":="
value <- pExpr value <- pIRExpr
eat ")" eat ")"
put env pure
modify $ over bindsL (ident :) ( ident
pure (ident, foldr (uncurry Abs) value params) , flip (foldr (uncurry Pi)) params <$> ascription
, foldr (uncurry Abs) value params
)
pLet :: Parser Expr pLet :: Parser IRExpr
pLet = lexeme $ label "let expression" $ withBinders $ do pLet = lexeme $ label "let expression" $ do
eat "let" eat "let"
bindings <- some pBinding bindings <- some pBinding
eat "in" eat "in"
body <- try pExpr body <- try pIRExpr
eat "end" eat "end"
pure $ foldr (uncurry Let) body bindings pure $ foldr letTuple body bindings
where
letTuple :: (Text, Maybe IRExpr, IRExpr) -> IRExpr -> IRExpr
letTuple (name, ascription, value) = Let name ascription value
pArrow :: Parser Expr pArrow :: Parser IRExpr
pArrow = lexeme $ label "->" $ do pArrow = lexeme $ label "->" $ do
a <- pAppTerm a <- pAppTerm
_ <- defChoice $ "->" :| [""] _ <- defChoice $ "->" :| [""]
Pi "" a . incIndices <$> pExpr Pi "" a <$> pIRExpr
pApp :: Parser Expr pApp :: Parser IRExpr
pApp = lexeme $ foldl1 App <$> some pTerm pApp = lexeme $ foldl1 App <$> some pTerm
pStar :: Parser Expr pStar :: Parser IRExpr
pStar = lexeme $ Level 0 <$ eat "*" pStar = lexeme $ Level 0 <$ eat "*"
pNumSort :: Parser Expr pNumSort :: Parser IRExpr
pNumSort = lexeme $ label "sort" $ eat "*" >> Level <$> L.decimal pNumSort = lexeme $ label "sort" $ eat "*" >> Level <$> L.decimal
pSort :: Parser Expr pSort :: Parser IRExpr
pSort = try pNumSort <|> pStar pSort = try pNumSort <|> pStar
checkAscription :: Text -> Expr -> Maybe Expr -> Parser () pAxiom :: Parser IRExpr
checkAscription ident value massert = do pAxiom = Axiom <$ eat "axiom"
env <- get
ty <- either (customFailure . TE) pure (checkType (env ^. envL) value)
case massert of
Nothing -> updateStateDefinition ident ty value
Just assert -> do
equiv <- either (customFailure . TE) pure (checkBeta (env ^. envL) ty assert)
unless equiv $ customFailure $ TE $ NotEquivalent ty assert value
updateStateDefinition ident assert value
updateStateDefinition :: Text -> Expr -> Expr -> Parser () pIRDef :: Parser IRDef
updateStateDefinition ident ty value = do pIRDef = lexeme $ label "definition" $ do
env <- get
when (M.member ident (env ^. envL)) (customFailure $ TE $ DuplicateDefinition ident)
modify $ over envL $ M.insert ident $ makeDef ty value
pAxiom :: Text -> Maybe Expr -> Parser ()
pAxiom ident Nothing = customFailure $ TE $ PNMissingType ident
pAxiom ident (Just ascription) = do
eat "axiom"
eat ";"
updateStateDefinition ident ascription (Axiom ident)
pBody :: [(Text, Expr)] -> Text -> Maybe Expr -> Parser ()
pBody params ident ascription = do
value <- flip (foldr (uncurry Abs)) params <$> pExpr
checkAscription ident value ascription
eat ";"
pDef :: Parser ()
pDef = lexeme $ label "definition" $ withBinders $ do
skipSpace skipSpace
ident <- pIdentifier ident <- pIdentifier
params <- pManyParams params <- pManyParams
ascription <- fmap (flip (foldr (uncurry Pi)) params) <$> pAscription ascription <- fmap (flip (foldr (uncurry Pi)) params) <$> pAscription
eat ":=" eat ":="
choice [pAxiom ident ascription, pBody params ident ascription] body <- pIRExpr
eat ";"
pure $ Def ident params ascription body
pTerm :: Parser Expr pTerm :: Parser IRExpr
pTerm = pTerm =
lexeme $ lexeme $
label "term" $ label "term" $
choice choice
[ between (char '(') (char ')') pExpr [ between (char '(') (char ')') pIRExpr
, pSort , pSort
, pAxiom
, pVar , pVar
] ]
pAppTerm :: Parser Expr pAppTerm :: Parser IRExpr
pAppTerm = pAppTerm =
lexeme $ lexeme $
choice choice
@ -207,33 +161,26 @@ pAppTerm =
, pApp , pApp
] ]
pExpr :: Parser Expr pIRExpr :: Parser IRExpr
pExpr = lexeme $ try pArrow <|> pAppTerm pIRExpr = lexeme $ try pArrow <|> pAppTerm
pAscription :: Parser (Maybe Expr) pAscription :: Parser (Maybe IRExpr)
pAscription = lexeme $ optional $ try $ defChoice (pure ":") >> label "type" pExpr pAscription = lexeme $ optional $ try $ eat ":" >> label "type" pIRExpr
pProgram :: Parser Env pIRProgram :: Parser IRProgram
pProgram = lexeme $ skipSpace >> many pDef >> _env <$> get pIRProgram = many pIRDef
emptyBinders :: Env -> InnerState parserWrapper :: Parser a -> String -> Text -> Either String a
emptyBinders env = IS{_binders = [], _env = env} parserWrapper p filename input = first errorBundlePretty $ runParser p filename input
parseDef :: Text -> State Env (Either String ()) parseProgram :: String -> Text -> Either String IRProgram
parseDef input = do parseProgram = parserWrapper pIRProgram
env <- get
let (output, IS{_env}) = runState (runParserT pDef "" input) (emptyBinders env)
put _env
pure $ first errorBundlePretty output
parseExpr :: Env -> Text -> Either String Expr parseDef :: String -> Text -> Either String IRDef
parseExpr env input = first errorBundlePretty $ evalState (runParserT pExpr "" input) $ emptyBinders env parseDef = parserWrapper pIRDef
parseDefEmpty :: Env -> Text -> (Either String (), Env) parseExpr :: String -> Text -> Either String IRExpr
parseDefEmpty env input = runState (parseDef input) env parseExpr = parserWrapper pIRExpr
parseProgram :: Env -> Text -> Either String Env handleFile :: String -> ExceptT String IO IRProgram
parseProgram initial input = first errorBundlePretty $ evalState (runParserT pProgram "" input) $ emptyBinders initial handleFile filename = (toString `withExceptT` runPreprocessor filename) >>= hoistEither . parseProgram filename
handleFile :: Env -> String -> ExceptT String IO Env
handleFile initial filename = (toString `withExceptT` runPreprocessor filename) >>= liftEither . parseProgram initial

View file

@ -26,25 +26,30 @@ common warnings
library perga-lib library perga-lib
import: warnings import: warnings
exposed-modules: Check exposed-modules: Check
Parser Elaborator
Expr
Eval
Errors Errors
Eval
Expr
IR
Parser
Preprocessor Preprocessor
hs-source-dirs: lib hs-source-dirs: lib
build-depends: base ^>=4.19.1.0 build-depends: base ^>=4.19.1.0
, relude , relude
, mtl
, megaparsec
, parser-combinators
, filepath , filepath
, lens
, megaparsec
, mtl
, parser-combinators
mixins: base hiding (Prelude) mixins: base hiding (Prelude)
, relude (Relude as Prelude) , relude (Relude as Prelude)
, relude , relude
default-language: Haskell2010 default-language: Haskell2010
default-extensions: OverloadedStrings default-extensions: OverloadedStrings
, GADTs , GADTs
, DuplicateRecordFields
, OverloadedRecordDot
executable perga executable perga
import: warnings import: warnings
@ -53,11 +58,11 @@ executable perga
build-depends: base ^>=4.19.1.0 build-depends: base ^>=4.19.1.0
, relude , relude
, perga-lib
, haskeline
, mtl
, directory , directory
, filepath , filepath
, haskeline
, mtl
, perga-lib
mixins: base hiding (Prelude) mixins: base hiding (Prelude)
, relude (Relude as Prelude) , relude (Relude as Prelude)
, relude , relude