rhiannon morris
c81aabcc14
- top level semicolons optional - type optional [the def will need to be an elim] - `load` statement - namespaces
328 lines
7.9 KiB
Idris
328 lines
7.9 KiB
Idris
module Quox.Parser.Parser
|
||
|
||
import public Quox.Parser.Lexer
|
||
import public Quox.Parser.Syntax
|
||
|
||
import Data.Fin
|
||
import Data.Vect
|
||
import public Text.Parser
|
||
|
||
%default total
|
||
|
||
|
||
public export
|
||
0 Grammar : Bool -> Type -> Type
|
||
Grammar = Core.Grammar () Token
|
||
%hide Core.Grammar
|
||
|
||
|
||
export
|
||
res : (str : String) -> (0 _ : IsReserved str) => Grammar True ()
|
||
res str = terminal "expecting \"\{str}\"" $
|
||
\x => guard $ x == Reserved str
|
||
|
||
export
|
||
resC : (str : String) -> (0 _ : IsReserved str) => Grammar True ()
|
||
resC str = do res str; commit
|
||
|
||
|
||
parameters {default True commit : Bool}
|
||
private
|
||
maybeCommit : Grammar False ()
|
||
maybeCommit = when commit Core.commit
|
||
|
||
export
|
||
betweenR : {c : Bool} -> (op, cl : String) ->
|
||
(0 _ : IsReserved op) => (0 _ : IsReserved cl) =>
|
||
Grammar c a -> Grammar True a
|
||
betweenR o c p = res o *> maybeCommit *> p <* res c <* maybeCommit
|
||
|
||
export
|
||
parens, bracks, braces : {c : Bool} -> Grammar c a -> Grammar True a
|
||
parens = betweenR "(" ")"
|
||
bracks = betweenR "[" "]"
|
||
braces = betweenR "{" "}"
|
||
|
||
|
||
export
|
||
commaSep : {c : Bool} -> Grammar c a -> Grammar False (List a)
|
||
commaSep = sepEndBy (res ",")
|
||
-- don't commit because of the possible terminating ","
|
||
|
||
export
|
||
semiSep : {c : Bool} -> Grammar c a -> Grammar False (List a)
|
||
semiSep = sepEndBy (res ";")
|
||
|
||
export
|
||
commaSep1 : {c : Bool} -> Grammar c a -> Grammar c (List1 a)
|
||
commaSep1 = sepEndBy1 (res ",")
|
||
|
||
export
|
||
darr : Grammar True ()
|
||
darr = resC "⇒"
|
||
|
||
|
||
export
|
||
name : Grammar True PName
|
||
name = terminal "expecting name" $
|
||
\case Name i => Just i; _ => Nothing
|
||
|
||
export
|
||
mods : Grammar True Mods
|
||
mods = name <&> \n => n.mods :< n.base
|
||
|
||
export
|
||
baseName : Grammar True String
|
||
baseName = terminal "expecting unqualified name" $
|
||
\case Name i => guard (null i.mods) $> i.base
|
||
_ => Nothing
|
||
|
||
export
|
||
nat : Grammar True Nat
|
||
nat = terminal "expecting natural number" $
|
||
\case Nat n => pure n; _ => Nothing
|
||
|
||
export
|
||
string : Grammar True String
|
||
string = terminal "expecting string literal" $
|
||
\case Str s => pure s; _ => Nothing
|
||
|
||
export
|
||
tag : Grammar True String
|
||
tag = terminal "expecting tag constructor" $
|
||
\case Tag t => pure t; _ => Nothing
|
||
|
||
export
|
||
bareTag : Grammar True String
|
||
bareTag = string <|> [|toDotsP name|]
|
||
|
||
export
|
||
universe : Grammar True PUniverse
|
||
universe = terminal "expecting type universe" $
|
||
\case TYPE u => Just u; _ => Nothing
|
||
|
||
export
|
||
bname : Grammar True BName
|
||
bname = Nothing <$ res "_"
|
||
<|> [|Just baseName|]
|
||
|
||
export
|
||
zeroOne : (zero, one : a) -> Grammar True a
|
||
zeroOne zero one = terminal "expecting zero or one" $
|
||
\case Nat 0 => Just zero; Nat 1 => Just one; _ => Nothing
|
||
|
||
|
||
export covering
|
||
qty : Grammar True PQty
|
||
qty = zeroOne Zero One
|
||
<|> Any <$ res "ω"
|
||
<|> parens qty
|
||
|
||
private covering
|
||
qtys : Grammar False (List PQty)
|
||
qtys = option [] [|toList $ some qty <* res "·"|]
|
||
|
||
export
|
||
dimConst : Grammar True DimConst
|
||
dimConst = zeroOne Zero One
|
||
|
||
export covering
|
||
dim : Grammar True PDim
|
||
dim = [|V baseName|]
|
||
<|> [|K dimConst|]
|
||
<|> parens dim
|
||
|
||
private
|
||
0 PBinderHead : Nat -> Type
|
||
PBinderHead n = (Vect n PQty, BName, PTerm)
|
||
|
||
private
|
||
toVect : List a -> (n ** Vect n a)
|
||
toVect [] = (_ ** [])
|
||
toVect (x :: xs) = (_ ** x :: snd (toVect xs))
|
||
|
||
private
|
||
0 MakeBinder : Nat -> Type
|
||
MakeBinder n = (String, PBinderHead n -> PTerm -> PTerm)
|
||
|
||
private
|
||
makePi : MakeBinder 1
|
||
makePi = ("→", \([pi], x, s) => Pi pi x s)
|
||
|
||
private
|
||
makeSig : MakeBinder 0
|
||
makeSig = ("×", \([], x, s) => Sig x s)
|
||
|
||
private
|
||
makeBinder : {m, n : Nat} -> MakeBinder m -> PBinderHead n -> PTerm ->
|
||
Grammar False PTerm
|
||
makeBinder (str, f) h t =
|
||
case decEq m n of
|
||
Yes Refl => pure $ f h t
|
||
No _ =>
|
||
let q = if m == 1 then "quantity" else "quantities" in
|
||
fatalError "'\{str}' expects \{show m} \{q}, got \{show n}"
|
||
|
||
private
|
||
binderInfix : Grammar True (n ** MakeBinder n)
|
||
binderInfix = res "→" $> (1 ** makePi)
|
||
<|> res "×" $> (0 ** makeSig)
|
||
|
||
private
|
||
lamIntro : Grammar True (BName -> PTerm -> PTerm)
|
||
lamIntro = Lam <$ resC "λ"
|
||
<|> DLam <$ resC "δ"
|
||
|
||
private covering
|
||
caseIntro : Grammar True PQty
|
||
caseIntro = resC "case1" $> One
|
||
<|> resC "caseω" $> Any
|
||
<|> resC "case" *> qty <* resC "·"
|
||
|
||
private
|
||
optNameBinder : Grammar False BName
|
||
optNameBinder = [|join $ optional $ bname <* darr|]
|
||
|
||
mutual
|
||
export covering
|
||
term : Grammar True PTerm
|
||
term = lamTerm
|
||
<|> caseTerm
|
||
<|> bindTerm
|
||
<|> [|annotate infixEqTerm (optional $ resC "∷" *> term)|]
|
||
where
|
||
annotate : PTerm -> Maybe PTerm -> PTerm
|
||
annotate s a = maybe s (s :#) a
|
||
|
||
private covering
|
||
lamTerm : Grammar True PTerm
|
||
lamTerm = flip . foldr <$> lamIntro <*> some bname <* darr <*> term
|
||
|
||
private covering
|
||
caseTerm : Grammar True PTerm
|
||
caseTerm =
|
||
[|Case caseIntro term
|
||
(resC "return" *> optBinderTerm)
|
||
(resC "of" *> braces caseBody)|]
|
||
|
||
private covering
|
||
caseBody : Grammar False PCaseBody
|
||
caseBody = [|CasePair (pairPat <* darr) (term <* optSemi)|]
|
||
<|> [|CaseEnum $ semiSep [|MkPair tag (darr *> term)|]|]
|
||
where
|
||
optSemi = ignore $ optional $ res ";"
|
||
pairPat = parens [|MkPair bname (resC "," *> bname)|]
|
||
|
||
private covering
|
||
bindTerm : Grammar True PTerm
|
||
bindTerm = do
|
||
bnd <- binderHead
|
||
inf <- binderInfix
|
||
body <- term
|
||
makeBinder (snd inf) (snd bnd) body
|
||
|
||
private covering
|
||
infixEqTerm : Grammar True PTerm
|
||
infixEqTerm = do
|
||
l <- appTerm
|
||
rty <- optional [|MkPair (resC "≡" *> term) (resC ":" *> appTerm)|]
|
||
pure $ maybe l (\rty => Eq (Nothing, snd rty) l (fst rty)) rty
|
||
|
||
private covering
|
||
appTerm : Grammar True PTerm
|
||
appTerm = resC "★" *> [|TYPE nat|]
|
||
<|> resC "Eq" *> [|Eq (bracks optBinderTerm) aTerm aTerm|]
|
||
<|> [|apply aTerm (many appArg)|]
|
||
where
|
||
data PArg = TermArg PTerm | DimArg PDim
|
||
|
||
appArg : Grammar True PArg
|
||
appArg = [|DimArg $ resC "@" *> dim|]
|
||
<|> [|TermArg aTerm|]
|
||
|
||
apply : PTerm -> List PArg -> PTerm
|
||
apply = foldl apply1 where
|
||
apply1 : PTerm -> PArg -> PTerm
|
||
apply1 f (TermArg x) = f :@ x
|
||
apply1 f (DimArg p) = f :% p
|
||
|
||
private covering
|
||
aTerm : Grammar True PTerm
|
||
aTerm = [|Enum $ braces $ commaSep bareTag|]
|
||
<|> [|TYPE universe|]
|
||
<|> [|V name|]
|
||
<|> [|Tag tag|]
|
||
<|> foldr1 Pair <$> parens (commaSep1 term)
|
||
|
||
private covering
|
||
binderHead : Grammar True (n ** PBinderHead n)
|
||
binderHead = parens {commit = False} $ do
|
||
qs <- [|toVect qtys|]
|
||
name <- bname
|
||
resC ":"
|
||
ty <- term
|
||
pure (qs.fst ** (qs.snd, name, ty))
|
||
|
||
private covering
|
||
optBinderTerm : Grammar True (BName, PTerm)
|
||
optBinderTerm = [|MkPair optNameBinder term|]
|
||
|
||
|
||
export covering
|
||
defIntro : Grammar True PQty
|
||
defIntro = Zero <$ resC "def0"
|
||
<|> Any <$ resC "defω"
|
||
<|> resC "def" *> option Any (qty <* resC "·")
|
||
|
||
export covering
|
||
definition : Grammar True PDefinition
|
||
definition =
|
||
[|MkPDef defIntro name
|
||
(optional (resC ":" *> term))
|
||
(resC "≔" *> term)|]
|
||
|
||
export
|
||
load : Grammar True String
|
||
load = resC "load" *> string
|
||
|
||
mutual
|
||
export covering
|
||
namespace_ : Grammar True PNamespace
|
||
namespace_ =
|
||
[|MkPNamespace (resC "namespace" *> mods) (braces $ many decl)|]
|
||
|
||
export covering
|
||
decl : Grammar True PDecl
|
||
decl = [|PDef definition|]
|
||
<|> [|PNs namespace_|]
|
||
|
||
export covering
|
||
topLevel : Grammar True PTopLevel
|
||
topLevel = [|PLoad load|]
|
||
<|> [|PD decl|]
|
||
|
||
export covering
|
||
input : Grammar False (List PTopLevel)
|
||
input = skipSemis *> many (topLevel <* skipSemis)
|
||
where skipSemis = ignore $ many $ resC ";"
|
||
|
||
|
||
public export
|
||
data Error =
|
||
LexError Lexer.Error
|
||
| ParseError (List1 (ParsingError Token))
|
||
%hide Lexer.Error
|
||
|
||
export
|
||
lexParseWith : {c : Bool} -> Grammar c a -> String -> Either Error a
|
||
lexParseWith grm input = do
|
||
toks <- mapFst LexError $ lex input
|
||
bimap ParseError fst $ parse (grm <* eof) toks
|
||
|
||
export covering %inline
|
||
lexParseInput : String -> Either Error (List PTopLevel)
|
||
lexParseInput = lexParseWith input
|
||
|
||
export covering %inline
|
||
lexParseTerm : String -> Either Error PTerm
|
||
lexParseTerm = lexParseWith term
|