perga/app/Parser.hs

102 lines
2.6 KiB
Haskell
Raw Normal View History

2024-10-05 13:31:09 -07:00
module Parser where
2024-10-05 16:04:13 -07:00
import Control.Monad
import Control.Monad.State.Strict
import Data.Functor.Identity
import Data.List (elemIndex)
import Data.List.NonEmpty (NonEmpty((:|)))
import qualified Data.List.NonEmpty as NE
2024-10-05 13:31:09 -07:00
import Expr (Expr (..))
2024-10-05 16:04:13 -07:00
import Text.Megaparsec hiding (State)
2024-10-05 13:31:09 -07:00
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L
2024-10-05 16:04:13 -07:00
import Data.Bifunctor (first)
type InnerState = [String]
data CustomErrors = UnboundVariable String [String] deriving (Eq, Ord, Show)
2024-10-05 13:31:09 -07:00
2024-10-05 16:04:13 -07:00
instance ShowErrorComponent CustomErrors where
showErrorComponent (UnboundVariable var bound) =
"Unbound variable: " ++ var ++ ". Did you mean one of: " ++ unwords bound ++ "?"
type Parser = ParsecT CustomErrors String (State InnerState)
2024-10-05 13:31:09 -07:00
skipSpace :: Parser ()
skipSpace =
L.space
space1
(L.skipLineComment "--")
(L.skipBlockCommentNested "(*" "*)")
lexeme :: Parser a -> Parser a
lexeme = L.lexeme skipSpace
2024-10-05 16:04:13 -07:00
pIdentifier :: Parser String
pIdentifier = label "identifier" $ lexeme $ do
firstChar <- letterChar <|> char '_'
rest <- many $ alphaNumChar <|> char '_'
return $ firstChar : rest
2024-10-05 13:31:09 -07:00
2024-10-05 16:04:13 -07:00
pVar :: Parser Expr
pVar = label "variable" $ lexeme $ do
var <- pIdentifier
binders <- get
case elemIndex var binders of
Just i -> return $ Var (fromIntegral i) var
2024-10-05 16:04:13 -07:00
Nothing -> customFailure $ UnboundVariable var binders
2024-10-05 13:31:09 -07:00
2024-10-05 16:04:13 -07:00
defChoice :: NE.NonEmpty String -> Parser ()
defChoice options = lexeme $ label labelText $ void $ choice $ NE.map string options
where labelText = NE.head options
2024-10-05 13:31:09 -07:00
pLAbs :: Parser Expr
pLAbs = lexeme $ label "λ-abstraction" $ do
2024-10-05 16:04:13 -07:00
_ <- defChoice $ "λ" :| ["lambda"]
ident <- pIdentifier
_ <- defChoice $ ":" :| []
ty <- pExpr
_ <- defChoice $ "." :| []
modify (ident :)
2024-11-11 13:37:44 -08:00
body <- pExpr
modify $ drop 1
pure $ Abs ident ty body
2024-10-05 13:31:09 -07:00
pPAbs :: Parser Expr
pPAbs = lexeme $ label "Π-abstraction" $ do
2024-10-05 16:04:13 -07:00
_ <- defChoice $ "" :| ["Pi"]
ident <- pIdentifier
_ <- defChoice $ ":" :| []
ty <- pExpr
_ <- defChoice $ "." :| []
modify (ident :)
2024-11-11 13:37:44 -08:00
body <- pExpr
modify $ drop 1
pure $ Pi ident ty body
2024-10-05 13:31:09 -07:00
pApp :: Parser Expr
pApp = foldl1 App <$> some pTerm
pStar :: Parser Expr
2024-10-05 16:04:13 -07:00
pStar = Star <$ defChoice ("*" :| [])
2024-10-05 13:31:09 -07:00
pSquare :: Parser Expr
2024-10-05 16:04:13 -07:00
pSquare = Square <$ defChoice ("" :| ["[]"])
2024-10-05 13:31:09 -07:00
pTerm :: Parser Expr
pTerm =
lexeme $
label "term" $
choice
[ between (char '(') (char ')') pExpr,
2024-10-05 16:04:13 -07:00
pVar,
2024-10-05 13:31:09 -07:00
pStar,
pSquare
]
pExpr :: Parser Expr
2024-10-05 16:04:13 -07:00
pExpr = lexeme $ pLAbs <|> pPAbs <|> pApp
2024-10-05 13:31:09 -07:00
pAll :: String -> Either String Expr
2024-10-05 16:04:13 -07:00
pAll input = first errorBundlePretty $ fst $ runIdentity $ runStateT (runParserT pExpr "" input) []