Files
newt/src/Lib/Parser.idr

274 lines
5.9 KiB
Idris

module Lib.Parser
import Lib.TT
-- NEXT - need to sort out parsing implicits
--
-- app: foo {a} a b
-- lam: λ {A} {b : A} (c : Blah) d e f. something
-- pi: (A : Set) -> {b : A} -> (c : Foo b) -> c -> bar d
import Lib.Token
import Lib.Parser.Impl
import Syntax
import Data.List
-- There is the whole core vs surface thing here.
-- might be best to do core first/ Technically don't
-- _need_ a parser, but would be useful for testing.
-- look to pi-forall / ezoo, but I think we start with a
-- TTImpl level grammar, then add a more sugared layer above
-- so holes and all that
-- After the parser runs, see below, take a break and finish pi-forall
-- exercises. There is some fill in the parser stuff that may show
-- the future.
-- kovacs desugars the (a b c : Foo) during parsing (foldr)
-- We need to allow A -> B -> C in functions
-- We need to handle A -> (A -> B) -> C
ident = token Ident
parens : Parser a -> Parser a
parens pa = do
sym "("
t <- pa
sym ")"
pure t
braces : Parser a -> Parser a
braces pa = do
sym "{"
t <- pa
sym "}"
pure t
optional : Parser a -> Parser (Maybe a)
optional pa = Just <$> pa <|> pure Nothing
lit : Parser Raw
lit = do
t <- token Number
pure $ RLit (LInt (cast t))
-- typeExpr is term with arrows.
export typeExpr : Parser Raw
export term : (Parser Raw)
withPos : Parser Raw -> Parser Raw
withPos p = RSrcPos <$> getPos <*> p
-- the inside of Raw
atom : Parser Raw
atom = withPos (RU <$ keyword "U"
<|> RVar <$> ident
<|> lit
<|> RHole <$ keyword "_")
<|> parens typeExpr
-- Argument to a Spine
pArg : Parser (Icit,Raw)
pArg = (Explicit,) <$> atom <|> (Implicit,) <$> braces term
--
-- atom is lit or ident
data Fixity = InfixL | InfixR | Infix
-- starter pack, but we'll move some to prelude
operators : List (String, Int, Fixity)
operators = [
("=",2,Infix),
("+",4,InfixL),
("-",4,InfixL),
("*",5,InfixL),
("/",5,InfixL)
]
parseApp : Parser Raw
parseApp = do
hd <- atom
rest <- many pArg
pure $ foldl (\a, (c,b) => RApp a b c) hd rest
parseOp : Lazy (Parser Raw)
parseOp = parseApp >>= go 0
where
go : Int -> Raw -> Parser Raw
go prec left =
do
op <- token Oper
let Just (p,fix) = lookup op operators
| Nothing => fail "expected operator"
if p >= prec then pure () else fail ""
let pr = case fix of InfixR => p; _ => p + 1
right <- go pr !(parseApp)
go prec (RApp (RApp (RVar op) left Explicit) right Explicit)
<|> pure left
export
letExpr : Parser Raw
letExpr = do
keyword "let"
commit
alts <- startBlock $ someSame $ letAssign
keyword' "in"
scope <- typeExpr
pure $ foldl (\ acc, (n,v) => RLet n RHole v acc) scope alts
where
letAssign : Parser (Name,Raw)
letAssign = do
name <- ident
-- TODO type assertion
keyword "="
t <- typeExpr
pure (name,t)
pLetArg : Parser (Icit, String, Maybe Raw)
pLetArg = (Implicit,,) <$> braces ident <*> optional (sym ":" >> typeExpr)
<|> (Explicit,,) <$> parens ident <*> optional (sym ":" >> typeExpr)
<|> (Explicit,,Nothing) <$> ident
<|> (Explicit,"_",Nothing) <$ keyword "_"
-- lam: λ {A} {b : A} (c : Blah) d e f. something
export
lamExpr : Parser Raw
lamExpr = do
keyword "\\"
commit
(icit, name, ty) <- pLetArg
keyword "=>"
scope <- typeExpr
-- TODO optional type
pure $ RLam name icit scope
pPattern : Parser Pattern
pPattern
= PatWild <$ keyword "_"
<|> PatVar <$> ident
caseAlt : Parser CaseAlt
caseAlt = do
pat <- pPattern -- term and sort it out later?
keyword "=>"
commit
t <- term
pure $ MkAlt pat t
export
caseExpr : Parser Raw
caseExpr = do
keyword "case"
commit
sc <- term
keyword "of"
alts <- startBlock $ someSame $ caseAlt
pure $ RCase sc alts
term = caseExpr
<|> letExpr
<|> lamExpr
<|> parseOp
expBinder : Parser Raw
expBinder = do
sym "("
name <- ident
sym ":"
ty <- typeExpr
sym ")"
sym "->"
scope <- typeExpr
pure $ RPi (Just name) Explicit ty scope
impBinder : Parser Raw
impBinder = do
sym "{"
name <- ident
sym ":"
ty <- typeExpr
sym "}"
sym "->"
scope <- typeExpr
pure $ RPi (Just name) Implicit ty scope
-- something binder looking
-- todo sepby space or whatever
export
binder : Parser Raw
binder = expBinder <|> impBinder
typeExpr = binder
<|> do
exp <- term
scope <- optional (sym "->" *> mustWork typeExpr)
case scope of
Nothing => pure exp
-- consider Maybe String to represent missing
(Just scope) => pure $ RPi Nothing Explicit exp scope
-- And top level stuff
export
parseSig : Parser Decl
parseSig = TypeSig <$> ident <* keyword ":" <*> mustWork typeExpr
parseImport : Parser Decl
parseImport = DImport <$ keyword "import" <* commit <*> ident
-- Do we do pattern stuff now? or just name = lambda?
export
parseDef : Parser Decl
parseDef = Def <$> ident <* keyword "=" <*> mustWork typeExpr
export
parseData : Parser Decl
parseData = do
keyword "data"
name <- ident
keyword ":"
ty <- typeExpr
keyword "where"
decls <- startBlock $ someSame $ parseSig
-- TODO - turn decls into something more useful
pure $ Data name ty decls
export
parseDecl : Parser Decl
parseDecl = parseImport <|> parseSig <|> parseDef <|> parseData
export
parseMod : Parser Module
parseMod = do
sameLevel $ keyword "module"
name <- ident
-- probably should be manySame, and we want to start with col -1
-- if we enforce blocks indent more than parent
decls <- startBlock $ someSame $ parseDecl
pure $ MkModule name [] decls
public export
data ReplCmd =
Def Decl
| Norm Raw -- or just name?
| Check Raw
-- Eventually I'd like immediate actions in the file, like lean, but I
-- also want to REPL to work and we can do that first.
export parseRepl : Parser ReplCmd
parseRepl = Def <$> parseDecl <|> Norm <$ keyword "#nf" <*> typeExpr
<|> Check <$ keyword "#check" <*> typeExpr