character lits, initial work on literal case trees
This commit is contained in:
5
TODO.md
5
TODO.md
@@ -1,6 +1,11 @@
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
- [ ] Default cases (currently gets expanded to all constructors)
|
||||||
|
- [ ] Case for primitives
|
||||||
|
- [ ] aoc2023 translation
|
||||||
|
- some "real world" examples
|
||||||
|
- [ ] Maybe Eq and stuff would work for typeclass without dealing with unification issues yet
|
||||||
- [ ] unsolved meta errors repeat (need to freeze or only report at end)
|
- [ ] unsolved meta errors repeat (need to freeze or only report at end)
|
||||||
- [x] Sanitize JS idents, e.g. `_+_`
|
- [x] Sanitize JS idents, e.g. `_+_`
|
||||||
- [ ] Generate some programs that do stuff
|
- [ ] Generate some programs that do stuff
|
||||||
|
|||||||
@@ -17,6 +17,20 @@
|
|||||||
{
|
{
|
||||||
"name": "keyword.newt",
|
"name": "keyword.newt",
|
||||||
"match": "\\b(data|where|case|of|let|in|U|module|import|ptype|pfunc|infix|infixl|infixr)\\b"
|
"match": "\\b(data|where|case|of|let|in|U|module|import|ptype|pfunc|infix|infixl|infixr)\\b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string.js",
|
||||||
|
"begin": ":=\\s*\"",
|
||||||
|
"end": "\"",
|
||||||
|
"patterns": [
|
||||||
|
{ "include": "source.js" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "string.newt",
|
||||||
|
"begin": "\"",
|
||||||
|
"end": "\""
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ import Data.Nat
|
|||||||
data Kind = Plain | Return | Assign String
|
data Kind = Plain | Return | Assign String
|
||||||
|
|
||||||
data JSStmt : Kind -> Type
|
data JSStmt : Kind -> Type
|
||||||
|
data JSExp : Type
|
||||||
|
|
||||||
data JAlt : Type where
|
data JAlt : Type where
|
||||||
JConAlt : String -> JSStmt e -> JAlt
|
JConAlt : String -> JSStmt e -> JAlt
|
||||||
JDefAlt : JSStmt e -> JAlt
|
JDefAlt : JSStmt e -> JAlt
|
||||||
|
JLitAlt : JSExp -> JSStmt e -> JAlt
|
||||||
|
|
||||||
data JSExp : Type where
|
data JSExp : Type where
|
||||||
LitArray : List JSExp -> JSExp
|
LitArray : List JSExp -> JSExp
|
||||||
@@ -49,6 +51,11 @@ Cont e = JSExp -> JSStmt e
|
|||||||
JSEnv : Type
|
JSEnv : Type
|
||||||
JSEnv = List JSExp
|
JSEnv = List JSExp
|
||||||
|
|
||||||
|
litToJS : Literal -> JSExp
|
||||||
|
litToJS (LString str) = LitString str
|
||||||
|
litToJS (LChar c) = LitString $ cast c
|
||||||
|
litToJS (LInt i) = LitInt i
|
||||||
|
|
||||||
-- Stuff nm.h1, nm.h2, ... into environment
|
-- Stuff nm.h1, nm.h2, ... into environment
|
||||||
mkEnv : String -> Nat -> List JSExp -> List String -> List JSExp
|
mkEnv : String -> Nat -> List JSExp -> List String -> List JSExp
|
||||||
mkEnv nm k env [] = env
|
mkEnv nm k env [] = env
|
||||||
@@ -89,6 +96,7 @@ termToJS env (CFun nms t) f =
|
|||||||
termToJS env (CRef nm) f = f $ Var nm
|
termToJS env (CRef nm) f = f $ Var nm
|
||||||
termToJS env (CMeta k) f = f $ LitString "META \{show k}"
|
termToJS env (CMeta k) f = f $ LitString "META \{show k}"
|
||||||
termToJS env (CLit (LString str)) f = f (LitString str)
|
termToJS env (CLit (LString str)) f = f (LitString str)
|
||||||
|
termToJS env (CLit (LChar c)) f = f (LitString $ cast c)
|
||||||
termToJS env (CLit (LInt i)) f = f (LitInt i)
|
termToJS env (CLit (LInt i)) f = f (LitInt i)
|
||||||
-- if it's a var, just use the original
|
-- if it's a var, just use the original
|
||||||
termToJS env (CLet nm (CBnd k) u) f = case getAt k env of
|
termToJS env (CLet nm (CBnd k) u) f = case getAt k env of
|
||||||
@@ -125,11 +133,12 @@ termToJS env (CCase t alts) f =
|
|||||||
termToJSAlt env nm (CConAlt name args u) = JConAlt name (termToJS (mkEnv nm 0 env args) u f)
|
termToJSAlt env nm (CConAlt name args u) = JConAlt name (termToJS (mkEnv nm 0 env args) u f)
|
||||||
-- intentionally reusing scrutinee name here
|
-- intentionally reusing scrutinee name here
|
||||||
termToJSAlt env nm (CDefAlt u) = JDefAlt (termToJS (Var nm :: env) u f)
|
termToJSAlt env nm (CDefAlt u) = JDefAlt (termToJS (Var nm :: env) u f)
|
||||||
|
termToJSAlt env nm (CLitAlt lit u) = JLitAlt (litToJS lit) (termToJS (Var nm :: env) u f)
|
||||||
|
|
||||||
maybeCaseStmt : List JSExp -> String -> List CAlt -> JSStmt e
|
maybeCaseStmt : List JSExp -> String -> List CAlt -> JSStmt e
|
||||||
-- If there is a single alt, assume it matched
|
-- If there is a single alt, assume it matched
|
||||||
maybeCaseStmt env nm [(CConAlt _ args u)] = (termToJS (mkEnv nm 0 env args) u f)
|
maybeCaseStmt env nm [(CConAlt _ args u)] = (termToJS (mkEnv nm 0 env args) u f)
|
||||||
maybeCaseStmt env nm alts =
|
maybeCaseStmt env nm alts =
|
||||||
(JCase (Dot (Var nm) "tag") (map (termToJSAlt env nm) alts))
|
(JCase (Dot (Var nm) "tag") (map (termToJSAlt env nm) alts))
|
||||||
|
|
||||||
|
|
||||||
@@ -185,18 +194,21 @@ expToDoc (Dot obj nm) = expToDoc obj ++ "." ++ jsIdent nm
|
|||||||
|
|
||||||
caseBody : JSStmt e -> Doc
|
caseBody : JSStmt e -> Doc
|
||||||
caseBody stmt@(JReturn x) = nest 2 (line ++ stmtToDoc stmt)
|
caseBody stmt@(JReturn x) = nest 2 (line ++ stmtToDoc stmt)
|
||||||
caseBody stmt = "{" </> nest 2 (line ++ stmtToDoc stmt </> text "break;") </> "}"
|
-- caseBody {e = Return} stmt@(JCase{}) = nest 2 (line ++ stmtToDoc stmt)
|
||||||
|
caseBody {e} stmt@(JCase{}) = nest 2 (line ++ stmtToDoc stmt </> text "break;")
|
||||||
|
caseBody stmt = line ++ "{" ++ nest 2 (line ++ stmtToDoc stmt </> text "break;") </> "}"
|
||||||
|
|
||||||
altToDoc : JAlt -> Doc
|
altToDoc : JAlt -> Doc
|
||||||
altToDoc (JConAlt nm stmt) = text "case" <+> jsString nm ++ ":" ++ caseBody stmt
|
altToDoc (JConAlt nm stmt) = text "case" <+> jsString nm ++ ":" ++ caseBody stmt
|
||||||
altToDoc (JDefAlt stmt) = text "default" ++ ":" ++ caseBody stmt
|
altToDoc (JDefAlt stmt) = text "default" ++ ":" ++ caseBody stmt
|
||||||
|
altToDoc (JLitAlt a stmt) = text "case" <+> expToDoc a ++ ":" ++ caseBody stmt
|
||||||
|
|
||||||
stmtToDoc (JSnoc x y) = stmtToDoc x </> stmtToDoc y
|
stmtToDoc (JSnoc x y) = stmtToDoc x </> stmtToDoc y
|
||||||
stmtToDoc (JPlain x) = expToDoc x ++ ";"
|
stmtToDoc (JPlain x) = expToDoc x ++ ";"
|
||||||
-- I might not need these split yet.
|
-- I might not need these split yet.
|
||||||
stmtToDoc (JLet nm body) = "let" <+> jsIdent nm ++ ";" </> stmtToDoc body
|
stmtToDoc (JLet nm body) = "let" <+> jsIdent nm ++ ";" </> stmtToDoc body
|
||||||
stmtToDoc (JAssign nm expr) = jsIdent nm <+> "=" <+> expToDoc expr ++ ";"
|
stmtToDoc (JAssign nm expr) = jsIdent nm <+> "=" <+> expToDoc expr ++ ";"
|
||||||
stmtToDoc (JConst nm x) = text "const" <+> jsIdent nm <+> "=" <+/> expToDoc x ++ ";"
|
stmtToDoc (JConst nm x) = text "const" <+> jsIdent nm <+> nest 2 ("=" <+/> expToDoc x ++ ";")
|
||||||
stmtToDoc (JReturn x) = text "return" <+> expToDoc x ++ ";"
|
stmtToDoc (JReturn x) = text "return" <+> expToDoc x ++ ";"
|
||||||
stmtToDoc (JError str) = text "throw new Error(" ++ jsString str ++ ");"
|
stmtToDoc (JError str) = text "throw new Error(" ++ jsString str ++ ");"
|
||||||
stmtToDoc (JCase sc alts) =
|
stmtToDoc (JCase sc alts) =
|
||||||
@@ -220,9 +232,10 @@ entryToDoc (MkEntry name ty (Fn tm)) = do
|
|||||||
-- and we might need betas? It seems like a mirror of what happens in CExp
|
-- and we might need betas? It seems like a mirror of what happens in CExp
|
||||||
debug "compileFun \{pprint [] tm}"
|
debug "compileFun \{pprint [] tm}"
|
||||||
ct <- compileFun tm
|
ct <- compileFun tm
|
||||||
-- now show for ct...
|
-- If ct has zero arity and is a compount expression, this fails..
|
||||||
let body = stmtToDoc $ termToJS [] ct JPlain
|
let body@(JPlain {}) = termToJS [] ct JPlain
|
||||||
pure (text "const" <+> jsIdent name <+> text "=" <+/> body)
|
| js => error (getFC tm) "Not a plain expression: \{render 80 $ stmtToDoc js}"
|
||||||
|
pure (text "const" <+> jsIdent name <+> text "=" <+/> stmtToDoc body)
|
||||||
entryToDoc (MkEntry name type Axiom) = pure ""
|
entryToDoc (MkEntry name type Axiom) = pure ""
|
||||||
entryToDoc (MkEntry name type (TCon strs)) = pure $ dcon name (piArity type)
|
entryToDoc (MkEntry name type (TCon strs)) = pure $ dcon name (piArity type)
|
||||||
entryToDoc (MkEntry name type (DCon arity str)) = pure $ dcon name arity
|
entryToDoc (MkEntry name type (DCon arity str)) = pure $ dcon name arity
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ data CAlt : Type where
|
|||||||
-- REVIEW keep var name?
|
-- REVIEW keep var name?
|
||||||
CDefAlt : CExp -> CAlt
|
CDefAlt : CExp -> CAlt
|
||||||
-- literal
|
-- literal
|
||||||
|
CLitAlt : Literal -> CExp -> CAlt
|
||||||
|
|
||||||
data CExp : Type where
|
data CExp : Type where
|
||||||
CBnd : Nat -> CExp
|
CBnd : Nat -> CExp
|
||||||
@@ -110,7 +111,8 @@ compileTerm (Case _ t alts) = do
|
|||||||
t' <- compileTerm t
|
t' <- compileTerm t
|
||||||
alts' <- traverse (\case
|
alts' <- traverse (\case
|
||||||
CaseDefault tm => pure $ CDefAlt !(compileTerm tm)
|
CaseDefault tm => pure $ CDefAlt !(compileTerm tm)
|
||||||
CaseCons nm args tm => pure $ CConAlt nm args !(compileTerm tm)) alts
|
CaseCons nm args tm => pure $ CConAlt nm args !(compileTerm tm)
|
||||||
|
CaseLit lit tm => pure $ CLitAlt lit !(compileTerm tm)) alts
|
||||||
pure $ CCase t' alts'
|
pure $ CCase t' alts'
|
||||||
compileTerm (Lit _ lit) = pure $ CLit lit
|
compileTerm (Lit _ lit) = pure $ CLit lit
|
||||||
compileTerm (Let _ nm t u) = pure $ CLet nm !(compileTerm t) !(compileTerm u)
|
compileTerm (Let _ nm t u) = pure $ CLet nm !(compileTerm t) !(compileTerm u)
|
||||||
|
|||||||
145
src/Lib/Elab.idr
145
src/Lib/Elab.idr
@@ -321,6 +321,9 @@ data Bind = MkBind String Icit Val
|
|||||||
Show Bind where
|
Show Bind where
|
||||||
show (MkBind str icit x) = "\{str} \{show icit}"
|
show (MkBind str icit x) = "\{str} \{show icit}"
|
||||||
|
|
||||||
|
|
||||||
|
---------------- Case builder
|
||||||
|
|
||||||
public export
|
public export
|
||||||
record Problem where
|
record Problem where
|
||||||
constructor MkProb
|
constructor MkProb
|
||||||
@@ -356,7 +359,8 @@ findSplit : List Constraint -> Maybe Constraint
|
|||||||
findSplit [] = Nothing
|
findSplit [] = Nothing
|
||||||
-- FIXME look up type, ensure it's a constructor
|
-- FIXME look up type, ensure it's a constructor
|
||||||
findSplit (x@(nm, PatCon _ _ cnm pats) :: xs) = Just x
|
findSplit (x@(nm, PatCon _ _ cnm pats) :: xs) = Just x
|
||||||
findSplit (_ :: xs) = findSplit xs
|
findSplit (x@(nm, PatLit _ val) :: xs) = Just x
|
||||||
|
findSplit (x :: xs) = findSplit xs
|
||||||
|
|
||||||
|
|
||||||
-- ***************
|
-- ***************
|
||||||
@@ -550,20 +554,22 @@ buildCase ctx prob scnm scty (dcName, arity, ty) = do
|
|||||||
else (nm, pat) :: makeConst xs pats
|
else (nm, pat) :: makeConst xs pats
|
||||||
makeConst ((MkBind nm Explicit x) :: xs) (pat :: pats) = (nm, pat) :: makeConst xs pats
|
makeConst ((MkBind nm Explicit x) :: xs) (pat :: pats) = (nm, pat) :: makeConst xs pats
|
||||||
|
|
||||||
rewriteCons : List Bind -> List Constraint -> List Constraint -> Maybe (List Constraint)
|
-- replace constraint with constraints on parameters, or nothing if non-matching clause
|
||||||
rewriteCons vars [] acc = Just acc
|
rewriteConstraint : List Bind -> List Constraint -> List Constraint -> Maybe (List Constraint)
|
||||||
rewriteCons vars (c@(nm, y) :: xs) acc =
|
rewriteConstraint vars [] acc = Just acc
|
||||||
|
rewriteConstraint vars (c@(nm, y) :: xs) acc =
|
||||||
if nm == scnm
|
if nm == scnm
|
||||||
then case y of
|
then case y of
|
||||||
PatVar _ _ s => Just $ c :: (xs ++ acc)
|
PatVar _ _ s => Just $ c :: (xs ++ acc)
|
||||||
PatWild _ _ => Just $ c :: (xs ++ acc)
|
PatWild _ _ => Just $ c :: (xs ++ acc)
|
||||||
|
PatLit fc lit => Nothing -- error fc "Literal \{show lit} in constructor split"
|
||||||
PatCon _ _ str ys => if str == dcName
|
PatCon _ _ str ys => if str == dcName
|
||||||
then Just $ (makeConst vars ys) ++ xs ++ acc
|
then Just $ (makeConst vars ys) ++ xs ++ acc
|
||||||
else Nothing
|
else Nothing
|
||||||
else rewriteCons vars xs (c :: acc)
|
else rewriteConstraint vars xs (c :: acc)
|
||||||
|
|
||||||
rewriteClause : List Bind -> Clause -> Maybe Clause
|
rewriteClause : List Bind -> Clause -> Maybe Clause
|
||||||
rewriteClause vars (MkClause fc cons pats expr) = pure $ MkClause fc !(rewriteCons vars cons []) pats expr
|
rewriteClause vars (MkClause fc cons pats expr) = pure $ MkClause fc !(rewriteConstraint vars cons []) pats expr
|
||||||
|
|
||||||
|
|
||||||
splitArgs : Raw -> List (Raw, Icit) -> (Raw, List (Raw, Icit))
|
splitArgs : Raw -> List (Raw, Icit) -> (Raw, List (Raw, Icit))
|
||||||
@@ -583,8 +589,9 @@ mkPat top (tm, icit) = do
|
|||||||
[] => pure $ PatVar fc icit nm
|
[] => pure $ PatVar fc icit nm
|
||||||
_ => error (getFC tm) "patvar applied to args"
|
_ => error (getFC tm) "patvar applied to args"
|
||||||
((RImplicit fc), []) => pure $ PatWild fc icit
|
((RImplicit fc), []) => pure $ PatWild fc icit
|
||||||
((RImplicit fc), _) => error fc "implicit pat can't be applied"
|
((RImplicit fc), _) => error fc "implicit pat can't be applied to arguments"
|
||||||
-- ((RLit x y), b) => ?rhs_19
|
((RLit fc lit), []) => pure $ PatLit fc lit
|
||||||
|
((RLit fc y), b) => error fc "lit cannot be applied to arguments"
|
||||||
(a,b) => error (getFC a) "expected pat var or constructor"
|
(a,b) => error (getFC a) "expected pat var or constructor"
|
||||||
|
|
||||||
|
|
||||||
@@ -624,7 +631,74 @@ checkDone ctx ((nm, (PatVar _ _ nm')) :: xs) body ty = checkDone ({ types $= ren
|
|||||||
then (nm', ty) :: xs
|
then (nm', ty) :: xs
|
||||||
else (name, ty) :: rename xs
|
else (name, ty) :: rename xs
|
||||||
|
|
||||||
checkDone ctx ((x, pat) :: xs) body ty = error emptyFC "stray constraint \{x} /? \{show pat}"
|
checkDone ctx ((x, pat) :: xs) body ty = error (getFC pat) "stray constraint \{x} /? \{show pat}"
|
||||||
|
|
||||||
|
-- need to run constructors, then run default
|
||||||
|
|
||||||
|
-- wild/var can come before 'x' so we need a list first
|
||||||
|
getLits : String -> List Clause -> List Literal
|
||||||
|
getLits nm [] = []
|
||||||
|
getLits nm ((MkClause fc cons pats expr) :: cs) = case find ((nm==) . fst) cons of
|
||||||
|
Just (_, (PatLit _ lit)) => lit :: getLits nm cs
|
||||||
|
_ => getLits nm cs
|
||||||
|
|
||||||
|
|
||||||
|
-- then build a lit case for each of those
|
||||||
|
|
||||||
|
buildLitCase : Context -> Problem -> FC -> String -> Val -> Literal -> M CaseAlt
|
||||||
|
buildLitCase ctx prob fc scnm scty lit = do
|
||||||
|
|
||||||
|
-- Constrain the scrutinee's variable to be lit value
|
||||||
|
let Just ix = findIndex ((==scnm) . fst) ctx.types
|
||||||
|
| Nothing => error ctx.fc "\{scnm} not is scope?"
|
||||||
|
let lvl = ((length ctx.env) `minus` (cast ix)) `minus` 1
|
||||||
|
let scon : (Nat, Val) = (lvl, VLit fc lit)
|
||||||
|
ctx' <- updateContext ctx [scon]
|
||||||
|
let clauses = mapMaybe rewriteClause prob.clauses
|
||||||
|
|
||||||
|
when (length clauses == 0) $ error ctx.fc "Missing case for \{show lit} splitting \{scnm}"
|
||||||
|
tm <- buildTree ctx' (MkProb clauses prob.ty)
|
||||||
|
pure $ CaseLit lit tm
|
||||||
|
|
||||||
|
where
|
||||||
|
-- replace constraint with constraints on parameters, or nothing if non-matching clause
|
||||||
|
rewriteConstraint : List Constraint -> List Constraint -> Maybe (List Constraint)
|
||||||
|
rewriteConstraint [] acc = Just acc
|
||||||
|
rewriteConstraint (c@(nm, y) :: xs) acc =
|
||||||
|
if nm == scnm
|
||||||
|
then case y of
|
||||||
|
PatVar _ _ s => Just $ c :: (xs ++ acc)
|
||||||
|
PatWild _ _ => Just $ c :: (xs ++ acc)
|
||||||
|
PatLit fc lit' => if lit' == lit then Just $ (xs ++ acc) else Nothing
|
||||||
|
PatCon _ _ str ys => Nothing -- error matching lit against constructor
|
||||||
|
else rewriteConstraint xs (c :: acc)
|
||||||
|
|
||||||
|
rewriteClause : Clause -> Maybe Clause
|
||||||
|
rewriteClause (MkClause fc cons pats expr) = pure $ MkClause fc !(rewriteConstraint cons []) pats expr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
buildLitCases : Context -> Problem -> FC -> String -> Val -> M (List CaseAlt)
|
||||||
|
buildLitCases ctx prob fc scnm scty = do
|
||||||
|
let lits = getLits scnm prob.clauses
|
||||||
|
alts <- traverse (buildLitCase ctx prob fc scnm scty) lits
|
||||||
|
-- TODO build default case
|
||||||
|
-- run getLits
|
||||||
|
-- buildLitCase for each
|
||||||
|
|
||||||
|
let defclauses = filter isDefault prob.clauses
|
||||||
|
when (length defclauses == 0) $ error fc "no default for literal slot on \{show scnm}"
|
||||||
|
tm <- buildTree ctx (MkProb defclauses prob.ty)
|
||||||
|
|
||||||
|
pure $ alts ++ [ CaseDefault tm ]
|
||||||
|
where
|
||||||
|
isDefault : Clause -> Bool
|
||||||
|
isDefault cl = case find ((==scnm) . fst) cl.cons of
|
||||||
|
Just (_, (PatVar _ _ _)) => True
|
||||||
|
Just (_, (PatWild _ _)) => True
|
||||||
|
Nothing => True
|
||||||
|
_ => False
|
||||||
|
|
||||||
|
|
||||||
-- This process is similar to extendPi, but we need to stop
|
-- This process is similar to extendPi, but we need to stop
|
||||||
-- if one clause is short on patterns.
|
-- if one clause is short on patterns.
|
||||||
@@ -643,26 +717,38 @@ buildTree ctx prob@(MkProb ((MkClause fc cons pats@(x :: xs) expr) :: cs) ty) =
|
|||||||
-- need to find some name we can split in (x :: xs)
|
-- need to find some name we can split in (x :: xs)
|
||||||
-- so LHS of constraint is name (or VVar - if we do Val)
|
-- so LHS of constraint is name (or VVar - if we do Val)
|
||||||
-- then run the split
|
-- then run the split
|
||||||
|
-- some of this is copied into check
|
||||||
buildTree ctx prob@(MkProb ((MkClause fc constraints [] expr) :: cs) ty) = do
|
buildTree ctx prob@(MkProb ((MkClause fc constraints [] expr) :: cs) ty) = do
|
||||||
debug "buildTree \{show constraints} \{show expr}"
|
debug "buildTree \{show constraints} \{show expr}"
|
||||||
let Just (scnm, pat) := findSplit constraints
|
let Just (scnm, pat) := findSplit constraints
|
||||||
| _ => checkDone ctx constraints expr ty
|
| _ => do
|
||||||
|
debug "checkDone \{show constraints}"
|
||||||
|
checkDone ctx constraints expr ty
|
||||||
|
|
||||||
debug "SPLIT on \{scnm} because \{show pat} \{show $ getFC pat}"
|
debug "SPLIT on \{scnm} because \{show pat} \{show $ getFC pat}"
|
||||||
let Just (sctm, scty) := lookupName ctx scnm
|
let Just (sctm, scty) := lookupName ctx scnm
|
||||||
| _ => error fc "Internal Error: can't find \{scnm} in environment"
|
| _ => error fc "Internal Error: can't find \{scnm} in environment"
|
||||||
|
|
||||||
-- expand vars that may be solved
|
case pat of
|
||||||
scty' <- unlet ctx scty
|
PatCon _ _ _ _ => do
|
||||||
debug "EXP \{show scty} -> \{show scty'}"
|
-- expand vars that may be solved
|
||||||
cons <- getConstructors ctx (getFC pat) scty'
|
scty' <- unlet ctx scty
|
||||||
debug "CONS \{show $ map fst cons}"
|
debug "EXP \{show scty} -> \{show scty'}"
|
||||||
alts <- traverse (buildCase ctx prob scnm scty) cons
|
-- this is per the paper, but it would be nice to coalesce
|
||||||
debug "GOTALTS \{show alts}"
|
-- default cases
|
||||||
when (length (catMaybes alts) == 0) $ error (fc) "no alts for \{show scty'}"
|
cons <- getConstructors ctx (getFC pat) scty'
|
||||||
-- TODO check for empty somewhere?
|
debug "CONS \{show $ map fst cons}"
|
||||||
pure $ Case fc sctm (catMaybes alts)
|
alts <- traverse (buildCase ctx prob scnm scty) cons
|
||||||
|
debug "GOTALTS \{show alts}"
|
||||||
|
when (length (catMaybes alts) == 0) $ error (fc) "no alts for \{show scty'}"
|
||||||
|
-- TODO check for empty somewhere?
|
||||||
|
pure $ Case fc sctm (catMaybes alts)
|
||||||
|
PatLit fc v => do
|
||||||
|
-- need to run through all of the PatLits in this slot and then find a fallback
|
||||||
|
-- walk the list of patterns, stop if we hit a PatVar / PatWild, fail if we don't
|
||||||
|
alts <- buildLitCases ctx prob fc scnm scty
|
||||||
|
pure $ Case fc sctm alts
|
||||||
|
pat => error (getFC pat) "Internal error - tried to split on \{show pat}"
|
||||||
|
|
||||||
showDef : Context -> List String -> Nat -> Val -> M String
|
showDef : Context -> List String -> Nat -> Val -> M String
|
||||||
showDef ctx names n v@(VVar _ n' [<]) = if n == n' then pure "" else pure "= \{pprint names !(quote ctx.lvl v)}"
|
showDef ctx names n v@(VVar _ n' [<]) = if n == n' then pure "" else pure "= \{pprint names !(quote ctx.lvl v)}"
|
||||||
@@ -670,8 +756,6 @@ showDef ctx names n v = pure "= \{pprint names !(quote ctx.lvl v)}"
|
|||||||
|
|
||||||
check ctx tm ty = case (tm, !(forceType ty)) of
|
check ctx tm ty = case (tm, !(forceType ty)) of
|
||||||
(RCase fc rsc alts, ty) => do
|
(RCase fc rsc alts, ty) => do
|
||||||
-- We've got a beta redex or need to do something...
|
|
||||||
-- Maybe we can let the scrutinee and jump into the middle?
|
|
||||||
(sc, scty) <- infer ctx rsc
|
(sc, scty) <- infer ctx rsc
|
||||||
scty <- forceMeta scty
|
scty <- forceMeta scty
|
||||||
debug "SCTM \{pprint (names ctx) sc}"
|
debug "SCTM \{pprint (names ctx) sc}"
|
||||||
@@ -679,16 +763,10 @@ check ctx tm ty = case (tm, !(forceType ty)) of
|
|||||||
|
|
||||||
let scnm = fresh "sc"
|
let scnm = fresh "sc"
|
||||||
top <- get
|
top <- get
|
||||||
-- FIXME FC
|
clauses <- traverse (\(MkAlt pat rawRHS) => pure $ MkClause (getFC pat) [(scnm, !(mkPat top (pat, Explicit)))] [] rawRHS ) alts
|
||||||
clauses <- traverse (\(MkAlt pat rawRHS) => pure $ MkClause fc [(scnm, !(mkPat top (pat, Explicit)))] [] rawRHS ) alts
|
-- buildCase expects scrutinee to be a name in the context, so we need to let it.
|
||||||
|
|
||||||
-- buildCase expects scrutinee to be a name in the context because
|
|
||||||
-- it's compared against the first part of Constraint. We could switch
|
|
||||||
-- to a level and only let if the scrutinee is not a var.
|
|
||||||
let ctx' = extend ctx scnm scty
|
let ctx' = extend ctx scnm scty
|
||||||
cons <- getConstructors ctx' fc scty
|
pure $ Let fc scnm sc !(buildTree ctx' $ MkProb clauses ty)
|
||||||
alts <- traverse (buildCase ctx' (MkProb clauses ty) scnm scty) cons
|
|
||||||
pure $ Let fc scnm sc $ Case fc (Bnd fc 0) (catMaybes alts)
|
|
||||||
|
|
||||||
-- Document a hole, pretend it's implemented
|
-- Document a hole, pretend it's implemented
|
||||||
(RHole fc, ty) => do
|
(RHole fc, ty) => do
|
||||||
@@ -697,8 +775,7 @@ check ctx tm ty = case (tm, !(forceType ty)) of
|
|||||||
-- I want to know which ones are defines. I should skip the `=` bit if they match, I'll need indices in here too.
|
-- I want to know which ones are defines. I should skip the `=` bit if they match, I'll need indices in here too.
|
||||||
env <- for (zip ctx.env (toList ctx.types)) $ \(v, n, ty) => pure " \{n} : \{pprint names !(quote ctx.lvl ty)} = \{pprint names !(quote ctx.lvl v)}"
|
env <- for (zip ctx.env (toList ctx.types)) $ \(v, n, ty) => pure " \{n} : \{pprint names !(quote ctx.lvl ty)} = \{pprint names !(quote ctx.lvl v)}"
|
||||||
let msg = unlines (toList $ reverse env) ++ " -----------\n" ++ " goal \{pprint names ty'}"
|
let msg = unlines (toList $ reverse env) ++ " -----------\n" ++ " goal \{pprint names ty'}"
|
||||||
putStrLn "INFO at \{show fc}: "
|
info fc "\n\{msg}"
|
||||||
putStrLn msg
|
|
||||||
-- let context = unlines foo
|
-- let context = unlines foo
|
||||||
-- need to print 'warning' with position
|
-- need to print 'warning' with position
|
||||||
-- fixme - just put a name on it like idris and stuff it into top.
|
-- fixme - just put a name on it like idris and stuff it into top.
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ zonkApp top l env t sp = pure $ appSpine !(zonk top l env t) sp
|
|||||||
|
|
||||||
zonkAlt : TopContext -> Nat -> Env -> CaseAlt -> M CaseAlt
|
zonkAlt : TopContext -> Nat -> Env -> CaseAlt -> M CaseAlt
|
||||||
zonkAlt top l env (CaseDefault t) = CaseDefault <$> zonkBind top l env t
|
zonkAlt top l env (CaseDefault t) = CaseDefault <$> zonkBind top l env t
|
||||||
|
zonkAlt top l env (CaseLit lit t) = CaseLit lit <$> zonkBind top l env t
|
||||||
zonkAlt top l env (CaseCons name args t) = CaseCons name args <$> go l env args t
|
zonkAlt top l env (CaseCons name args t) = CaseCons name args <$> go l env args t
|
||||||
where
|
where
|
||||||
go : Nat -> Env -> List String -> Tm -> M Tm
|
go : Nat -> Env -> List String -> Tm -> M Tm
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
module Lib.Parser
|
module Lib.Parser
|
||||||
import Lib.Types
|
import Lib.Types
|
||||||
import Debug.Trace
|
import Debug.Trace
|
||||||
|
import Data.String
|
||||||
|
|
||||||
-- app: foo {a} a b
|
-- app: foo {a} a b
|
||||||
-- lam: λ {A} {b : A} (c : Blah) d e f => something
|
-- lam: λ {A} {b : A} (c : Blah) d e f => something
|
||||||
@@ -50,8 +50,14 @@ intLit = do
|
|||||||
pure $ RLit fc (LInt (cast t))
|
pure $ RLit fc (LInt (cast t))
|
||||||
|
|
||||||
|
|
||||||
|
charLit : Parser Raw
|
||||||
|
charLit = do
|
||||||
|
fc <- getPos
|
||||||
|
v <- token Character
|
||||||
|
pure $ RLit fc (LChar $ assert_total $ strIndex v 1)
|
||||||
|
|
||||||
lit : Parser Raw
|
lit : Parser Raw
|
||||||
lit = intLit <|> stringLit
|
lit = intLit <|> stringLit <|> charLit
|
||||||
|
|
||||||
-- typeExpr is term with arrows.
|
-- typeExpr is term with arrows.
|
||||||
export typeExpr : Parser Raw
|
export typeExpr : Parser Raw
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ be fit acc w k [] = Just (acc <>> [])
|
|||||||
be fit acc w k ((i, Empty) :: xs) = be fit acc w k xs
|
be fit acc w k ((i, Empty) :: xs) = be fit acc w k xs
|
||||||
be fit acc w k ((i, Line) :: xs) = (be False (acc :< LINE i) w i xs)
|
be fit acc w k ((i, Line) :: xs) = (be False (acc :< LINE i) w i xs)
|
||||||
be fit acc w k ((i, (Text s)) :: xs) =
|
be fit acc w k ((i, (Text s)) :: xs) =
|
||||||
if not fit || (k + length s < w)
|
if not fit || (k + length s < w)
|
||||||
then (be fit (acc :< TEXT s) w (k + length s) xs)
|
then (be fit (acc :< TEXT s) w (k + length s) xs)
|
||||||
else Nothing
|
else Nothing
|
||||||
be fit acc w k ((i, (Nest j x)) :: xs) = be fit acc w k ((i+j,x)::xs)
|
be fit acc w k ((i, (Nest j x)) :: xs) = be fit acc w k ((i+j,x)::xs)
|
||||||
be fit acc w k ((i, (Seq x y)) :: xs) = be fit acc w k ((i,x) :: (i,y) :: xs)
|
be fit acc w k ((i, (Seq x y)) :: xs) = be fit acc w k ((i,x) :: (i,y) :: xs)
|
||||||
@@ -124,7 +124,7 @@ export
|
|||||||
bracket : String -> Doc -> String -> Doc
|
bracket : String -> Doc -> String -> Doc
|
||||||
bracket l x r = group (text l ++ nest 2 (line ++ x) ++ line ++ text r)
|
bracket l x r = group (text l ++ nest 2 (line ++ x) ++ line ++ text r)
|
||||||
|
|
||||||
infixl 5 <+/>
|
export infixl 5 <+/>
|
||||||
|
|
||||||
||| Either space or newline
|
||| Either space or newline
|
||||||
export
|
export
|
||||||
|
|||||||
@@ -18,13 +18,14 @@ data Pattern
|
|||||||
| PatCon FC Icit Name (List Pattern)
|
| PatCon FC Icit Name (List Pattern)
|
||||||
| PatWild FC Icit
|
| PatWild FC Icit
|
||||||
-- Not handling this yet, but we need to be able to work with numbers and strings...
|
-- Not handling this yet, but we need to be able to work with numbers and strings...
|
||||||
-- | PatLit Literal
|
| PatLit FC Literal
|
||||||
|
|
||||||
export
|
export
|
||||||
getIcit : Pattern -> Icit
|
getIcit : Pattern -> Icit
|
||||||
getIcit (PatVar x icit str) = icit
|
getIcit (PatVar x icit str) = icit
|
||||||
getIcit (PatCon x icit str xs) = icit
|
getIcit (PatCon x icit str xs) = icit
|
||||||
getIcit (PatWild x icit) = icit
|
getIcit (PatWild x icit) = icit
|
||||||
|
getIcit (PatLit fc lit) = Explicit
|
||||||
|
|
||||||
|
|
||||||
export
|
export
|
||||||
@@ -32,6 +33,7 @@ HasFC Pattern where
|
|||||||
getFC (PatVar fc _ _) = fc
|
getFC (PatVar fc _ _) = fc
|
||||||
getFC (PatCon fc _ _ _) = fc
|
getFC (PatCon fc _ _ _) = fc
|
||||||
getFC (PatWild fc _) = fc
|
getFC (PatWild fc _) = fc
|
||||||
|
getFC (PatLit fc lit) = fc
|
||||||
|
|
||||||
-- %runElab deriveShow `{Pattern}
|
-- %runElab deriveShow `{Pattern}
|
||||||
public export
|
public export
|
||||||
@@ -117,9 +119,10 @@ record Module where
|
|||||||
foo : List String -> String
|
foo : List String -> String
|
||||||
foo ts = "(" ++ unwords ts ++ ")"
|
foo ts = "(" ++ unwords ts ++ ")"
|
||||||
|
|
||||||
Show Literal where
|
-- Show Literal where
|
||||||
show (LString str) = foo [ "LString", show str]
|
-- show (LString str) = foo [ "LString", show str]
|
||||||
show (LInt i) = foo [ "LInt", show i]
|
-- show (LInt i) = foo [ "LInt", show i]
|
||||||
|
-- show (LChar c) = foo [ "LChar", show c]
|
||||||
|
|
||||||
export
|
export
|
||||||
covering
|
covering
|
||||||
@@ -160,6 +163,7 @@ Show Pattern where
|
|||||||
show (PatVar _ icit str) = foo ["PatVar", show icit, show str]
|
show (PatVar _ icit str) = foo ["PatVar", show icit, show str]
|
||||||
show (PatCon _ icit str xs) = foo ["PatCon", show icit, show str, assert_total $ show xs]
|
show (PatCon _ icit str xs) = foo ["PatCon", show icit, show str, assert_total $ show xs]
|
||||||
show (PatWild _ icit) = foo ["PatWild", show icit]
|
show (PatWild _ icit) = foo ["PatWild", show icit]
|
||||||
|
show (PatLit _ lit) = foo ["PatLit", show lit]
|
||||||
|
|
||||||
covering
|
covering
|
||||||
Show RCaseAlt where
|
Show RCaseAlt where
|
||||||
@@ -180,12 +184,19 @@ Show Raw where
|
|||||||
show (RParseError _ str) = foo [ "ParseError", "str"]
|
show (RParseError _ str) = foo [ "ParseError", "str"]
|
||||||
show (RU _) = "U"
|
show (RU _) = "U"
|
||||||
|
|
||||||
|
export
|
||||||
|
Pretty Literal where
|
||||||
|
pretty (LString str) = text $ interpolate str
|
||||||
|
pretty (LInt i) = text $ show i
|
||||||
|
pretty (LChar c) = text $ show c
|
||||||
|
|
||||||
export
|
export
|
||||||
Pretty Pattern where
|
Pretty Pattern where
|
||||||
-- FIXME - wrap Implicit with {}
|
-- FIXME - wrap Implicit with {}
|
||||||
pretty (PatVar _ icit nm) = text nm
|
pretty (PatVar _ icit nm) = text nm
|
||||||
pretty (PatCon _ icit nm args) = text nm <+> spread (map pretty args)
|
pretty (PatCon _ icit nm args) = text nm <+> spread (map pretty args)
|
||||||
pretty (PatWild _icit)= "_"
|
pretty (PatWild _icit) = "_"
|
||||||
|
pretty (PatLit _ lit) = pretty lit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -218,8 +229,7 @@ Pretty Raw where
|
|||||||
<+/> text "in" <+> asDoc p scope
|
<+/> text "in" <+> asDoc p scope
|
||||||
-- does this exist?
|
-- does this exist?
|
||||||
asDoc p (RAnn _ x y) = text "TODO - RAnn"
|
asDoc p (RAnn _ x y) = text "TODO - RAnn"
|
||||||
asDoc p (RLit _ (LString str)) = text $ interpolate str
|
asDoc p (RLit _ lit) = pretty lit
|
||||||
asDoc p (RLit _ (LInt i)) = text $ show i
|
|
||||||
asDoc p (RCase _ x xs) = text "TODO - RCase"
|
asDoc p (RCase _ x xs) = text "TODO - RCase"
|
||||||
asDoc p (RImplicit _) = text "_"
|
asDoc p (RImplicit _) = text "_"
|
||||||
asDoc p (RHole _) = text "?"
|
asDoc p (RHole _) = text "?"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ data Kind
|
|||||||
| Oper
|
| Oper
|
||||||
| MixFix
|
| MixFix
|
||||||
| Number
|
| Number
|
||||||
|
| Character
|
||||||
| StringKind
|
| StringKind
|
||||||
| Symbol
|
| Symbol
|
||||||
| Space
|
| Space
|
||||||
@@ -31,6 +32,7 @@ Show Kind where
|
|||||||
show Oper = "Oper"
|
show Oper = "Oper"
|
||||||
show MixFix = "MixFix"
|
show MixFix = "MixFix"
|
||||||
show Number = "Number"
|
show Number = "Number"
|
||||||
|
show Character = "Character"
|
||||||
show Symbol = "Symbol"
|
show Symbol = "Symbol"
|
||||||
show Space = "Space"
|
show Space = "Space"
|
||||||
show LBrace = "LBrace"
|
show LBrace = "LBrace"
|
||||||
@@ -48,6 +50,7 @@ Eq Kind where
|
|||||||
Oper == Oper = True
|
Oper == Oper = True
|
||||||
MixFix == MixFix = True
|
MixFix == MixFix = True
|
||||||
Number == Number = True
|
Number == Number = True
|
||||||
|
Character == Character = True
|
||||||
Symbol == Symbol = True
|
Symbol == Symbol = True
|
||||||
Space == Space = True
|
Space == Space = True
|
||||||
LBrace == LBrace = True
|
LBrace == LBrace = True
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ opChar : Lexer
|
|||||||
opChar = pred isOpChar
|
opChar = pred isOpChar
|
||||||
|
|
||||||
identMore : Lexer
|
identMore : Lexer
|
||||||
identMore = alphaNum <|> exact "." <|> exact "'"
|
identMore = alphaNum <|> exact "." <|> exact "'" <|> exact "_"
|
||||||
|
|
||||||
quo : Recognise True
|
quo : Recognise True
|
||||||
quo = is '"'
|
quo = is '"'
|
||||||
@@ -53,6 +53,7 @@ rawTokens
|
|||||||
<|> match (upper <+> many identMore) checkUKW
|
<|> match (upper <+> many identMore) checkUKW
|
||||||
<|> match (some digit) (Tok Number)
|
<|> match (some digit) (Tok Number)
|
||||||
<|> match (is '#' <+> many alpha) (Tok Pragma)
|
<|> match (is '#' <+> many alpha) (Tok Pragma)
|
||||||
|
<|> match charLit (Tok Character)
|
||||||
<|> match (exact "_" <+> (some opChar <|> exact ",") <+> exact "_") (Tok MixFix)
|
<|> match (exact "_" <+> (some opChar <|> exact ",") <+> exact "_") (Tok MixFix)
|
||||||
<|> match (quo <+> manyUntil quo ((esc any <+> any) <|> any) <+> opt quo) (Tok StringKind . unquote)
|
<|> match (quo <+> manyUntil quo ((esc any <+> any) <|> any) <+> opt quo) (Tok StringKind . unquote)
|
||||||
<|> match (lineComment (exact "--")) (Tok Space)
|
<|> match (lineComment (exact "--")) (Tok Space)
|
||||||
|
|||||||
@@ -54,25 +54,38 @@ data PrimType = StringType | IntType
|
|||||||
data PrimVal : Type where
|
data PrimVal : Type where
|
||||||
PrimString : String -> PrimVal
|
PrimString : String -> PrimVal
|
||||||
PrimInt : Int -> PrimVal
|
PrimInt : Int -> PrimVal
|
||||||
|
PrimChar : Char -> PrimVal
|
||||||
|
|
||||||
public export
|
public export
|
||||||
data Tm : Type
|
data Tm : Type
|
||||||
|
|
||||||
|
public export
|
||||||
|
data Literal = LString String | LInt Int | LChar Char
|
||||||
|
|
||||||
|
%name Literal lit
|
||||||
|
|
||||||
|
public export
|
||||||
|
Show Literal where
|
||||||
|
show (LString str) = show str
|
||||||
|
show (LInt i) = show i
|
||||||
|
show (LChar c) = show c
|
||||||
|
|
||||||
public export
|
public export
|
||||||
data CaseAlt : Type where
|
data CaseAlt : Type where
|
||||||
CaseDefault : Tm -> CaseAlt
|
CaseDefault : Tm -> CaseAlt
|
||||||
-- I've also seen a list of stuff that gets replaced
|
-- I've also seen a list of stuff that gets replaced
|
||||||
CaseCons : (name : String) -> (args : List String) -> Tm -> CaseAlt
|
CaseCons : (name : String) -> (args : List String) -> Tm -> CaseAlt
|
||||||
-- CaseLit : Literal -> Tm -> CaseAlt
|
CaseLit : Literal -> Tm -> CaseAlt
|
||||||
|
|
||||||
data Def : Type
|
data Def : Type
|
||||||
|
|
||||||
public export
|
|
||||||
data Literal = LString String | LInt Int
|
|
||||||
|
|
||||||
Show Literal where
|
public export
|
||||||
show (LString str) = show str
|
Eq Literal where
|
||||||
show (LInt i) = show i
|
LString x == LString y = x == y
|
||||||
|
LInt x == LInt y = x == y
|
||||||
|
LChar x == LChar y = x == y
|
||||||
|
_ == _ = False
|
||||||
|
|
||||||
data Tm : Type where
|
data Tm : Type where
|
||||||
Bnd : FC -> Nat -> Tm
|
Bnd : FC -> Nat -> Tm
|
||||||
@@ -113,6 +126,7 @@ public export covering
|
|||||||
Show CaseAlt where
|
Show CaseAlt where
|
||||||
show (CaseDefault tm) = "_ => \{show tm}"
|
show (CaseDefault tm) = "_ => \{show tm}"
|
||||||
show (CaseCons nm args tm) = "\{nm} \{unwords args} => \{show tm}"
|
show (CaseCons nm args tm) = "\{nm} \{unwords args} => \{show tm}"
|
||||||
|
show (CaseLit lit tm) = "\{show lit} => \{show tm}"
|
||||||
|
|
||||||
public export covering
|
public export covering
|
||||||
Show Tm where
|
Show Tm where
|
||||||
@@ -159,7 +173,8 @@ pprint names tm = render 80 $ go names tm
|
|||||||
goAlt : List String -> CaseAlt -> Doc
|
goAlt : List String -> CaseAlt -> Doc
|
||||||
|
|
||||||
goAlt names (CaseDefault t) = "_" <+> "=>" <+> go ("_" :: names) t
|
goAlt names (CaseDefault t) = "_" <+> "=>" <+> go ("_" :: names) t
|
||||||
goAlt names (CaseCons name args t) = text name <+> spread (map text args) <+> "=>" <+> go (args ++ names) t
|
goAlt names (CaseCons name args t) = text name <+> spread (map text args) <+> "=>" <+/> go (args ++ names) t
|
||||||
|
goAlt names (CaseLit lit t) = text (show lit) <+> "=>" <+/> go names t
|
||||||
|
|
||||||
go names (Bnd _ k) = case getAt k names of
|
go names (Bnd _ k) = case getAt k names of
|
||||||
Nothing => text "BND:\{show k}"
|
Nothing => text "BND:\{show k}"
|
||||||
@@ -175,7 +190,7 @@ pprint names tm = render 80 $ go names tm
|
|||||||
text "((" <+> text nm <+> ":" <+> go names t <+> ")" <+> "->" <+> go (nm :: names) u <+> ")"
|
text "((" <+> text nm <+> ":" <+> go names t <+> ")" <+> "->" <+> go (nm :: names) u <+> ")"
|
||||||
-- FIXME - probably way wrong on the names here. There is implicit binding going on
|
-- FIXME - probably way wrong on the names here. There is implicit binding going on
|
||||||
go names (Case _ sc alts) = text "case" <+> go names sc <+> text "of" </> (nest 2 (line ++ stack (map (goAlt names) alts)))
|
go names (Case _ sc alts) = text "case" <+> go names sc <+> text "of" </> (nest 2 (line ++ stack (map (goAlt names) alts)))
|
||||||
go names (Lit _ lit) = text "\{show lit}"
|
go names (Lit _ lit) = text (show lit)
|
||||||
go names (Let _ nm t u) = text "let" <+> text nm <+> ":=" <+> go names t </> (nest 2 $ go names u)
|
go names (Let _ nm t u) = text "let" <+> text nm <+> ":=" <+> go names t </> (nest 2 $ go names u)
|
||||||
|
|
||||||
-- public export
|
-- public export
|
||||||
|
|||||||
Reference in New Issue
Block a user