module Lib.Parser -- NOW Still working on this. import Prelude import Lib.Common import Data.SortedMap import Data.String import Lib.Parser.Impl import Lib.Syntax import Lib.Token import Lib.Types lazy : ∀ a. (Unit → Parser a) → Parser a lazy pa = pa MkUnit ident : Parser String ident = token Ident <|> token MixFix uident : Parser String uident = token UIdent parenWrap : ∀ a. Parser a -> Parser a parenWrap pa = do symbol "(" t <- pa symbol ")" pure t braces : ∀ a. Parser a -> Parser a braces pa = do symbol "{" t <- pa symbol "}" pure t dbraces : ∀ a. Parser a -> Parser a dbraces pa = do symbol "{{" t <- pa symbol "}}" pure t optional : ∀ a. Parser a -> Parser (Maybe a) optional pa = Just <$> pa <|> pure Nothing stringLit : Parser Raw stringLit = do fc <- getPos t <- token StringKind pure $ RLit fc (LString t) -- typeExpr is term with arrows. typeExpr : Parser Raw term : (Parser Raw) interp : Parser Raw interp = do token StartInterp tm <- term token EndInterp pure tm interpString : Parser Raw interpString = do -- fc <- getPos ignore $ token StartQuote part <- term parts <- many (stringLit <|> interp) ignore $ token EndQuote pure $ foldl append part parts where append : Raw -> Raw -> Raw append t u = let fc = getFC t in (RApp (getFC t) (RApp fc (RVar fc "_++_") t Explicit) u Explicit) intLit : Parser Raw intLit = do fc <- getPos t <- token Number pure $ RLit fc (LInt (stringToInt t)) charLit : Parser Raw charLit = do fc <- getPos v <- token Character pure $ RLit fc (LChar $ strIndex v 0) lit : Parser Raw lit = intLit <|> interpString <|> stringLit <|> charLit -- helpful when we've got some / many and need FC for each addPos : ∀ a. Parser a -> Parser (FC × a) addPos pa = _,_ <$> getPos <*> pa asAtom : Parser Raw asAtom = do fc <- getPos nm <- ident asPat <- optional $ keyword "@" *> parenWrap typeExpr case asPat of Just exp => pure $ RAs fc nm exp Nothing => pure $ RVar fc nm -- the inside of Raw recordUpdate : Parser Raw atom : Parser Raw atom = do pure MkUnit RU <$> getPos <* keyword "U" -- <|> RVar <$> getPos <*> ident <|> asAtom <|> RVar <$> getPos <*> uident <|> RVar <$> getPos <*> token Projection <|> lit <|> RImplicit <$> getPos <* keyword "_" <|> RHole <$> getPos <* keyword "?" <|> parenWrap typeExpr <|> recordUpdate updateClause : Parser UpdateClause updateClause = do fc <- getPos nm <- ident op <- True <$ symbol ":=" <|> False <$ symbol "$=" tm <- term case op of True => pure $ AssignField fc nm tm _ => pure $ ModifyField fc nm tm -- ambiguity vs {a} or {a} -> ... is tough, we can do [] or put a keyword in front. recordUpdate = do fc <- getPos symbol "[" clauses <- sepBy (symbol ";") updateClause symbol "]" tm <- optional atom pure $ RUpdateRec fc clauses tm -- Argument to a Spine pArg : Parser (Icit × FC × Raw) pArg = do fc <- getPos (\x => Implicit, fc, x) <$> braces typeExpr <|> (\x => Auto, fc, x) <$> dbraces typeExpr <|> (\x => Explicit, fc, x) <$> atom AppSpine : U AppSpine = List (Icit × FC × Raw) pratt : Operators -> Int -> String -> Raw -> AppSpine -> Parser (Raw × AppSpine) pratt ops prec stop left spine = do (left, spine) <- runPrefix stop left spine let (left, spine) = projectHead left spine let spine = runProject spine case spine of Nil => pure (left, Nil) ((Explicit, fc, tm@(RVar x nm)) :: rest) => if nm == stop then pure (left,spine) else case lookupMap' nm ops of Just (MkOp name p fix False rule) => if p < prec then pure (left, spine) else runRule p fix stop rule (RApp fc (RVar fc name) left Explicit) rest Just _ => fail "expected operator" Nothing => if isPrefixOf "." nm then pratt ops prec stop (RApp (getFC tm) tm left Explicit) rest else pratt ops prec stop (RApp (getFC left) left tm Explicit) rest ((icit, fc, tm) :: rest) => pratt ops prec stop (RApp (getFC left) left tm icit) rest where projectHead : Raw -> AppSpine -> (Raw × AppSpine) projectHead t sp@((Explicit, fc', RVar fc nm) :: rest) = if isPrefixOf "." nm then projectHead (RApp fc (RVar fc nm) t Explicit) rest else (t,sp) projectHead t sp = (t, sp) -- we need to check left/AppSpine first -- we have a case above for when the next token is a projection, but -- we need this to make projection bind tighter than app runProject : AppSpine -> AppSpine runProject (t@(Explicit, fc', tm) :: u@(Explicit, _, RVar fc nm) :: rest) = if isPrefixOf "." nm then runProject ((Explicit, fc', RApp fc (RVar fc nm) tm Explicit) :: rest) else (t :: u :: rest) runProject tms = tms -- left has our partially applied operator and we're picking up args -- for the rest of the `_` runRule : Int -> Fixity -> String -> List String -> Raw -> AppSpine -> Parser (Raw × AppSpine) runRule p fix stop Nil left spine = pure (left, spine) runRule p fix stop ("" :: Nil) left spine = do let pr = case fix of InfixR => p _ => p + 1 case spine of ((_, fc, right) :: rest) => do (right, rest) <- pratt ops pr stop right rest pratt ops prec stop (RApp (getFC left) left right Explicit) rest _ => fail "trailing operator" runRule p fix stop (nm :: rule) left spine = do case spine of Nil => fail "short" ((_, _, right) :: rest) => do (right,rest) <- pratt ops 0 nm right rest -- stop!! let ((_,fc',RVar fc name) :: rest) = rest | _ => fail "expected \{nm}" if name == nm then runRule p fix stop rule (RApp (getFC left) left right Explicit) rest else fail "expected \{nm}" -- run any prefix operators runPrefix : String -> Raw -> AppSpine -> Parser (Raw × AppSpine) runPrefix stop (RVar fc nm) spine = case lookupMap' nm ops of -- TODO False should be an error here Just (MkOp name p fix True rule) => do runRule p fix stop rule (RVar fc name) spine _ => pure (left, spine) runPrefix stop left spine = pure (left, spine) parseOp : Parser Raw parseOp = do fc <- getPos ops <- getOps hd <- atom rest <- many pArg (res, Nil) <- pratt ops 0 "" hd rest | _ => fail "extra stuff" pure res -- TODO case let? We see to only have it for `do` -- try (keyword "let" >> symbol "(") letExpr : Parser Raw letExpr = do keyword "let" alts <- startBlock $ someSame $ letAssign keyword' "in" scope <- typeExpr pure $ foldl mkLet scope (reverse alts) where mkLet : Raw → String × FC × Maybe Raw × Raw → Raw mkLet acc (n,fc,ty,v) = RLet fc n (fromMaybe (RImplicit fc) ty) v acc letAssign : Parser (Name × FC × Maybe Raw × Raw) letAssign = do fc <- getPos name <- ident -- TODO type assertion ty <- optional (keyword ":" *> typeExpr) keyword "=" t <- typeExpr pure (name,fc,ty,t) pLamArg : Parser (Icit × String × Maybe Raw) pLamArg = impArg <|> autoArg <|> expArg <|> (\ x => (Explicit, x, Nothing)) <$> (ident <|> uident) <|> keyword "_" *> pure (Explicit, "_", Nothing) where impArg : Parser (Icit × String × Maybe Raw) impArg = do nm <- braces (ident <|> uident) ty <- optional (symbol ":" >> typeExpr) pure (Implicit, nm, ty) autoArg : Parser (Icit × String × Maybe Raw) autoArg = do nm <- dbraces (ident <|> uident) ty <- optional (symbol ":" >> typeExpr) pure (Auto, nm, ty) expArg : Parser (Icit × String × Maybe Raw) expArg = do nm <- parenWrap (ident <|> uident) ty <- optional (symbol ":" >> typeExpr) pure (Explicit, nm, ty) lamExpr : Parser Raw lamExpr = do pos <- getPos keyword "\\" <|> keyword "λ" args <- some $ addPos pLamArg keyword "=>" scope <- typeExpr pure $ foldr mkLam scope args where mkLam : FC × Icit × Name × Maybe Raw → Raw → Raw mkLam (fc, icit, name, ty) sc = RLam fc (BI fc name icit Many) sc caseAlt : Parser RCaseAlt caseAlt = do pure MkUnit pat <- typeExpr keyword "=>" t <- term pure $ MkAlt pat t caseExpr : Parser Raw caseExpr = do fc <- getPos keyword "case" sc <- term keyword "of" alts <- startBlock $ someSame $ caseAlt pure $ RCase fc sc alts caseLamExpr : Parser Raw caseLamExpr = do fc <- getPos try ((keyword "\\" <|> keyword "λ") *> keyword "case") alts <- startBlock $ someSame $ caseAlt pure $ RLam fc (BI fc "$case" Explicit Many) $ RCase fc (RVar fc "$case") alts doExpr : Parser Raw doStmt : Parser DoStmt caseLet : Parser Raw caseLet = do -- look ahead so we can fall back to normal let fc <- getPos try (keyword "let" >> symbol "(") pat <- typeExpr symbol ")" keyword "=" sc <- typeExpr alts <- startBlock $ manySame $ symbol "|" *> caseAlt keyword "in" body <- term pure $ RCase fc sc (MkAlt pat body :: alts) doCaseLet : Parser DoStmt doCaseLet = do -- look ahead so we can fall back to normal let -- Maybe make it work like arrow? fc <- getPos try (keyword "let" >> symbol "(") pat <- typeExpr symbol ")" keyword "=" sc <- typeExpr alts <- startBlock $ manySame $ symbol "|" *> caseAlt bodyFC <- getPos body <- RDo <$> getPos <*> someSame doStmt pure $ DoExpr fc (RCase fc sc (MkAlt pat body :: alts)) doArrow : Parser DoStmt doArrow = do fc <- getPos left <- typeExpr (Just _) <- optional $ keyword "<-" | _ => pure $ DoExpr fc left right <- term alts <- startBlock $ manySame $ symbol "|" *> caseAlt pure $ DoArrow fc left right alts doLet : Parser DoStmt doLet = do fc <- getPos keyword "let" nm <- ident keyword "=" tm <- term pure $ DoLet fc nm tm doStmt = doCaseLet <|> doLet <|> doArrow doExpr = RDo <$> getPos <* keyword "do" <*> (startBlock $ someSame doStmt) parseIfThen : Parser Raw parseIfThen = do fc <- getPos keyword "if" a <- term keyword "then" b <- term keyword "else" c <- term pure $ RIf fc a b c term' : Parser Raw term' = caseExpr <|> caseLet <|> letExpr <|> caseLamExpr <|> lamExpr <|> doExpr <|> parseIfThen -- Make this last for better error messages <|> parseOp term = do t <- term' rest <- many (_,_ <$> getPos <* keyword "$" <*> term') pure $ apply t rest where apply : Raw -> List (FC × Raw) -> Raw apply t Nil = t apply t ((fc,x) :: xs) = RApp fc t (apply x xs) Explicit varname : Parser String varname = (ident <|> uident <|> keyword "_" *> pure "_") quantity : Parser Quant quantity = fromMaybe Many <$> optional (Zero <$ keyword "0") ebind : Parser Telescope ebind = do -- don't commit until we see the ":" symbol "(" quant <- quantity names <- try (some (addPos varname) <* symbol ":") ty <- typeExpr symbol ")" pure $ map (makeBind quant ty) names where makeBind : Quant → Raw → FC × String → (BindInfo × Raw) makeBind quant ty (pos, name) = (BI pos name Explicit quant, ty) ibind : Parser Telescope ibind = do -- I've gone back and forth on this, but I think {m a b} is more useful than {Int} symbol "{" quant <- quantity names <- (some (addPos varname)) ty <- optional (symbol ":" *> typeExpr) symbol "}" pure $ map (makeBind quant ty) names where makeBind : Quant → Maybe Raw → FC × String → BindInfo × Raw makeBind quant ty (pos, name) = (BI pos name Implicit quant, fromMaybe (RImplicit pos) ty) abind : Parser Telescope abind = do -- for this, however, it would be nice to allow {{Monad A}} symbol "{{" name <- optional $ try (addPos varname <* symbol ":") ty <- typeExpr symbol "}}" case name of Just (pos,name) => pure ((BI pos name Auto Many, ty) :: Nil) Nothing => pure ((BI (getFC ty) "_" Auto Many, ty) :: Nil) arrow : Parser Unit arrow = symbol "->" <|> symbol "→" -- Collect a bunch of binders (A : U) {y : A} -> ... forAll : Parser Raw forAll = do keyword "forall" <|> keyword "∀" all <- some (addPos varname) keyword "." scope <- typeExpr pure $ foldr mkPi scope all where mkPi : FC × String → Raw → Raw mkPi (fc, n) sc = RPi fc (BI fc n Implicit Zero) (RImplicit fc) sc binders : Parser Raw binders = do binds <- many (abind <|> ibind <|> ebind) arrow scope <- typeExpr pure $ foldr mkBind scope (join binds) where mkBind : (BindInfo × Raw) -> Raw -> Raw mkBind (info, ty) scope = RPi (getFC info) info ty scope typeExpr = binders <|> forAll <|> (do fc <- getPos exp <- term scope <- optional (arrow *> typeExpr) case scope of Nothing => pure exp -- consider Maybe String to represent missing (Just scope) => pure $ RPi fc (BI fc "_" Explicit Many) exp scope) -- And top level stuff parseSig : Parser Decl parseSig = TypeSig <$> getPos <*> try (some (ident <|> uident <|> token Projection) <* keyword ":") <*> typeExpr parseImport : Parser Import parseImport = do fc <- getPos keyword "import" ident <- uident rest <- many $ token Projection let name = joinBy "" (ident :: rest) pure $ MkImport fc name -- Do we do pattern stuff now? or just name = lambda? -- TODO multiple names parseMixfix : Parser Decl parseMixfix = do fc <- getPos fix <- InfixL <$ keyword "infixl" <|> InfixR <$ keyword "infixr" <|> Infix <$ keyword "infix" prec <- token Number ops <- some $ token MixFix for ops $ \ op => addOp op (cast prec) fix pure $ PMixFix fc ops (cast prec) fix getName : Raw -> Parser String getName (RVar x nm) = pure nm getName (RApp x t u icit) = getName t getName tm = fail "bad LHS" parseDef : Parser Decl parseDef = do fc <- getPos t <- typeExpr nm <- getName t keyword "=" body <- typeExpr wfc <- getPos w <- optional $ do keyword "where" startBlock $ manySame $ (parseSig <|> parseDef) let body = maybe body (\ decls => RWhere wfc decls body) w -- these get collected later pure $ FunDef fc nm ((t, body) :: Nil) -- (MkClause fc Nil t body :: Nil) parsePType : Parser Decl parsePType = do fc <- getPos keyword "ptype" id <- uident ty <- optional $ do keyword ":" typeExpr pure $ PType fc id ty parsePFunc : Parser Decl parsePFunc = do fc <- getPos keyword "pfunc" nm <- ident used <- optional (keyword "uses" >> parenWrap (many $ uident <|> ident <|> token MixFix)) keyword ":" ty <- typeExpr keyword ":=" src <- token JSLit pure $ PFunc fc nm (fromMaybe Nil used) ty src parseShortData : Parser Decl parseShortData = do fc <- getPos keyword "data" lhs <- typeExpr keyword "=" sigs <- sepBy (keyword "|") typeExpr pure $ ShortData fc lhs sigs parseData : Parser Decl parseData = do fc <- getPos -- commit when we hit ":" name <- try $ (keyword "data" *> (uident <|> ident <|> token MixFix) <* keyword ":") ty <- typeExpr keyword "where" decls <- startBlock $ manySame $ parseSig pure $ Data fc name ty decls nakedBind : Parser Telescope nakedBind = do names <- some (addPos varname) pure $ map makeBind names where makeBind : FC × String → (BindInfo × Raw) makeBind (pos, name) = (BI pos name Explicit Many, RImplicit pos) parseRecord : Parser Decl parseRecord = do fc <- getPos keyword "record" name <- uident teles <- many $ ebind <|> nakedBind keyword "where" cname <- optional $ keyword "constructor" *> (uident <|> token MixFix) decls <- startBlock $ manySame $ parseSig pure $ Record fc name (join teles) cname decls parseClass : Parser Decl parseClass = do fc <- getPos keyword "class" name <- uident teles <- many $ ebind <|> nakedBind keyword "where" decls <- startBlock $ manySame $ parseSig pure $ Class fc name (join teles) decls parseInstance : Parser Decl parseInstance = do fc <- getPos keyword "instance" ty <- typeExpr -- is it a forward declaration (Just _) <- optional $ keyword "where" | _ => pure $ Instance fc ty Nothing decls <- startBlock $ manySame $ parseDef pure $ Instance fc ty (Just decls) -- Not sure what I want here. -- I can't get a Tm without a type, and then we're covered by the other stuff parseNorm : Parser Decl parseNorm = DCheck <$> getPos <* keyword "#check" <*> typeExpr <* keyword ":" <*> typeExpr parseDecl : Parser Decl parseDecl = parseMixfix <|> parsePType <|> parsePFunc <|> parseNorm <|> parseData <|> parseShortData <|> parseSig <|> parseDef <|> parseClass <|> parseInstance <|> parseRecord parseModHeader : Parser (FC × String) parseModHeader = do sameLevel (keyword "module") fc <- getPos name <- uident rest <- many $ token Projection -- FIXME use QName let name = joinBy "" (name :: rest) pure (fc, name) parseImports : Parser (List Import) parseImports = manySame parseImport parseMod : Parser Module parseMod = do sameLevel (keyword "module") name <- uident rest <- many $ token Projection imports <- manySame parseImport decls <- manySame parseDecl let name = joinBy "" (name :: rest) pure $ MkModule name imports decls -- 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. -- parseRepl : Parser ReplCmd -- parseRepl = Def <$> parseDecl <|> Norm <$ keyword "#nf" <*> typeExpr -- <|> Check <$ keyword "#check" <*> typeExpr