move idris version to orig and newt version to src.
Development is being done on the newt version now.
This commit is contained in:
426
orig/Lib/ProcessDecl.idr
Normal file
426
orig/Lib/ProcessDecl.idr
Normal file
@@ -0,0 +1,426 @@
|
||||
module Lib.ProcessDecl
|
||||
|
||||
import Data.IORef
|
||||
import Data.String
|
||||
import Data.Vect
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
|
||||
import Lib.Elab
|
||||
import Lib.Parser
|
||||
import Lib.Syntax
|
||||
import Lib.TopContext
|
||||
import Lib.Eval
|
||||
import Lib.Types
|
||||
import Lib.Util
|
||||
import Lib.Erasure
|
||||
|
||||
dumpEnv : Context -> M String
|
||||
dumpEnv ctx =
|
||||
unlines . reverse <$> go (names ctx) 0 (reverse $ zip ctx.env (toList ctx.types)) []
|
||||
where
|
||||
isVar : Nat -> Val -> Bool
|
||||
isVar k (VVar _ k' [<]) = k == k'
|
||||
isVar _ _ = False
|
||||
|
||||
go : List String -> Nat -> List (Val, String, Val) -> List String -> M (List String)
|
||||
go _ _ [] acc = pure acc
|
||||
go names k ((v, n, ty) :: xs) acc = if isVar k v
|
||||
-- TODO - use Doc and add <+/> as appropriate to printing
|
||||
then go names (S k) xs (" \{n} : \{pprint names !(quote ctx.lvl ty)}":: acc)
|
||||
else go names (S k) xs (" \{n} = \{pprint names !(quote ctx.lvl v)} : \{pprint names !(quote ctx.lvl ty)}":: acc)
|
||||
|
||||
export
|
||||
logMetas : Nat -> M ()
|
||||
logMetas mstart = do
|
||||
-- FIXME, now this isn't logged for Sig / Data.
|
||||
top <- get
|
||||
mc <- readIORef top.metaCtx
|
||||
let mlen = length mc.metas `minus` mstart
|
||||
for_ (reverse $ take mlen mc.metas) $ \case
|
||||
(Solved fc k soln) => do
|
||||
-- TODO put a flag on this, vscode is getting overwhelmed and
|
||||
-- dropping errors
|
||||
--info fc "solve \{show k} as \{pprint [] !(quote 0 soln)}"
|
||||
pure ()
|
||||
(Unsolved fc k ctx ty User cons) => do
|
||||
ty' <- quote ctx.lvl ty
|
||||
let names = (toList $ map fst ctx.types)
|
||||
env <- dumpEnv ctx
|
||||
let msg = "\{env} -----------\n \{pprint names ty'}"
|
||||
info fc "User Hole\n\{msg}"
|
||||
|
||||
(Unsolved fc k ctx ty kind cons) => do
|
||||
tm <- quote ctx.lvl !(forceMeta ty)
|
||||
-- Now that we're collecting errors, maybe we simply check at the end
|
||||
-- TODO - log constraints?
|
||||
-- FIXME in Combinatory, the val doesn't match environment?
|
||||
let msg = "Unsolved meta \{show k} \{show kind} type \{pprint (names ctx) tm} \{show $ length cons} constraints"
|
||||
msgs <- for cons $ \ (MkMc fc env sp val) => do
|
||||
pure " * (m\{show k} (\{unwords $ map show $ sp <>> []}) =?= \{show val}"
|
||||
sols <- case kind of
|
||||
AutoSolve => do
|
||||
x <- quote ctx.lvl ty
|
||||
ty <- eval ctx.env CBN x
|
||||
debug "AUTO ---> \{show ty}"
|
||||
-- we want the context here too.
|
||||
top <- get
|
||||
-- matches <- case !(contextMatches ctx ty) of
|
||||
-- [] => findMatches ctx ty $ toList top.defs
|
||||
-- xs => pure xs
|
||||
matches <- findMatches ctx ty $ toList top.defs
|
||||
-- TODO try putting mc into TopContext for to see if it gives better terms
|
||||
pure $ [" \{show $ length matches} Solutions: \{show matches}"]
|
||||
-- pure $ " \{show $ length matches} Solutions:" :: map ((" " ++) . interpolate . pprint (names ctx) . fst) matches
|
||||
|
||||
_ => pure []
|
||||
info fc $ unlines ([msg] ++ msgs ++ sols)
|
||||
-- addError $ E fc $ unlines ([msg] ++ msgs ++ sols)
|
||||
|
||||
|
||||
-- Used for Class and Record
|
||||
getSigs : List Decl -> List (FC, String, Raw)
|
||||
getSigs [] = []
|
||||
getSigs ((TypeSig _ [] _) :: xs) = getSigs xs
|
||||
getSigs ((TypeSig fc (nm :: nms) ty) :: xs) = (fc, nm, ty) :: getSigs xs
|
||||
getSigs (_ :: xs) = getSigs xs
|
||||
|
||||
teleToPi : Telescope -> Raw -> Raw
|
||||
teleToPi [] end = end
|
||||
teleToPi ((info, ty) :: tele) end = RPi (getFC info) info ty (teleToPi tele end)
|
||||
|
||||
impTele : Telescope -> Telescope
|
||||
impTele tele = map (\(BI fc nm _ quant, ty) => (BI fc nm Implicit Zero, ty)) tele
|
||||
|
||||
|
||||
export
|
||||
processDecl : List String -> Decl -> M ()
|
||||
|
||||
-- REVIEW I supposed I could have updated top here instead of the dance with the parser...
|
||||
processDecl ns (PMixFix{}) = pure ()
|
||||
|
||||
processDecl ns (TypeSig fc names tm) = do
|
||||
putStrLn "-----"
|
||||
|
||||
top <- get
|
||||
mc <- readIORef top.metaCtx
|
||||
let mstart = length mc.metas
|
||||
for_ names $ \nm => do
|
||||
let Nothing := lookupRaw nm top
|
||||
| Just entry => error fc "\{show nm} is already defined at \{show entry.fc}"
|
||||
pure ()
|
||||
ty <- check (mkCtx fc) tm (VU fc)
|
||||
ty <- zonk top 0 [] ty
|
||||
putStrLn "TypeSig \{unwords names} : \{pprint [] ty}"
|
||||
for_ names $ \nm => setDef (QN ns nm) fc ty Axiom
|
||||
-- Zoo4eg has metas in TypeSig, need to decide if I want to support leaving them unsolved here
|
||||
-- logMetas mstart
|
||||
|
||||
processDecl ns (PType fc nm ty) = do
|
||||
top <- get
|
||||
ty' <- check (mkCtx fc) (maybe (RU fc) id ty) (VU fc)
|
||||
setDef (QN ns nm) fc ty' PrimTCon
|
||||
|
||||
processDecl ns (PFunc fc nm uses ty src) = do
|
||||
top <- get
|
||||
ty <- check (mkCtx fc) ty (VU fc)
|
||||
ty' <- nf [] ty
|
||||
putStrLn "pfunc \{nm} : \{pprint [] ty'} := \{show src}"
|
||||
-- TODO wire through fc?
|
||||
for_ uses $ \ name => case lookupRaw name top of
|
||||
Nothing => error fc "\{name} not in scope"
|
||||
_ => pure ()
|
||||
setDef (QN ns nm) fc ty' (PrimFn src uses)
|
||||
|
||||
processDecl ns (Def fc nm clauses) = do
|
||||
putStrLn "-----"
|
||||
putStrLn "Def \{show nm}"
|
||||
top <- get
|
||||
mc <- readIORef top.metaCtx
|
||||
let mstart = length mc.metas
|
||||
let Just entry = lookupRaw nm top
|
||||
| Nothing => throwError $ E fc "No declaration for \{nm}"
|
||||
let (MkEntry fc name ty Axiom) := entry
|
||||
| _ => throwError $ E fc "\{nm} already defined at \{show entry.fc}"
|
||||
|
||||
putStrLn "check \{nm} at \{pprint [] ty}"
|
||||
vty <- eval empty CBN ty
|
||||
|
||||
debug "\{nm} vty is \{show vty}"
|
||||
|
||||
|
||||
-- I can take LHS apart syntactically or elaborate it with an infer
|
||||
clauses' <- traverse (makeClause top) clauses
|
||||
tm <- buildTree (mkCtx fc) (MkProb clauses' vty)
|
||||
-- putStrLn "Ok \{pprint [] tm}"
|
||||
|
||||
mc <- readIORef top.metaCtx
|
||||
let mlen = length mc.metas `minus` mstart
|
||||
solveAutos mstart
|
||||
-- TODO - make nf that expands all metas and drop zonk
|
||||
-- Day1.newt is a test case
|
||||
-- tm' <- nf [] tm
|
||||
tm' <- zonk top 0 [] tm
|
||||
when top.verbose $ putStrLn "NF\n\{render 80 $ pprint[] tm'}"
|
||||
-- TODO we want to keep both versions, but this is checking in addition to erasing
|
||||
-- currently CompileExp is also doing erasure.
|
||||
-- TODO we need erasure info on the lambdas or to fake up an appropriate environment
|
||||
-- and erase inside. Currently the checking is imprecise
|
||||
tm'' <- erase [] tm' []
|
||||
when top.verbose $ putStrLn "ERASED\n\{render 80 $ pprint[] tm'}"
|
||||
debug "Add def \{nm} \{pprint [] tm'} : \{pprint [] ty}"
|
||||
updateDef (QN ns nm) fc ty (Fn tm')
|
||||
-- logMetas mstart
|
||||
|
||||
processDecl ns (DCheck fc tm ty) = do
|
||||
putStrLn "----- DCheck"
|
||||
top <- get
|
||||
|
||||
putStrLn "INFO at \{show fc}: check \{show tm} at \{show ty}"
|
||||
ty' <- check (mkCtx fc) ty (VU fc)
|
||||
putStrLn " got type \{pprint [] ty'}"
|
||||
vty <- eval [] CBN ty'
|
||||
res <- check (mkCtx fc) tm vty
|
||||
putStrLn " got \{pprint [] res}"
|
||||
norm <- nf [] res
|
||||
putStrLn " NF \{pprint [] norm}"
|
||||
norm <- nfv [] res
|
||||
putStrLn " NFV \{pprint [] norm}"
|
||||
|
||||
processDecl ns (Class classFC nm tele decls) = do
|
||||
-- REVIEW maybe we can leverage Record for this
|
||||
-- a couple of catches, we don't want the dotted accessors and
|
||||
-- the self argument should be an auto-implicit
|
||||
putStrLn "-----"
|
||||
putStrLn "Class \{nm}"
|
||||
let fields = getSigs decls
|
||||
-- We'll need names for the telescope
|
||||
let dcName = "Mk\{nm}"
|
||||
let tcType = teleToPi tele (RU classFC)
|
||||
let tail = foldl (\ acc, (BI fc nm icit _, _) => RApp fc acc (RVar fc nm) icit) (RVar classFC nm) tele
|
||||
let dcType = teleToPi (impTele tele) $
|
||||
foldr (\(fc, nm, ty), acc => RPi fc (BI fc nm Explicit Many) ty acc ) tail fields
|
||||
|
||||
putStrLn "tcon type \{pretty tcType}"
|
||||
putStrLn "dcon type \{pretty dcType}"
|
||||
let decl = Data classFC nm tcType [TypeSig classFC [dcName] dcType]
|
||||
putStrLn "Decl:"
|
||||
putStrLn $ render 90 $ pretty decl
|
||||
processDecl ns decl
|
||||
for_ fields $ \ (fc,name,ty) => do
|
||||
let funType = teleToPi (impTele tele) $ RPi fc (BI fc "_" Auto Many) tail ty
|
||||
let autoPat = foldl (\acc, (fc,nm,ty) => RApp fc acc (RVar fc nm) Explicit) (RVar classFC dcName) fields
|
||||
let lhs = foldl (\acc, (BI fc' nm icit quant, _) => RApp fc' acc (RVar fc' nm) Implicit) (RVar fc name) tele
|
||||
let lhs = RApp classFC lhs autoPat Auto
|
||||
let decl = Def fc name [(lhs, (RVar fc name))]
|
||||
|
||||
putStrLn "\{name} : \{pretty funType}"
|
||||
putStrLn "\{pretty decl}"
|
||||
processDecl ns $ TypeSig fc [name] funType
|
||||
processDecl ns decl
|
||||
|
||||
|
||||
processDecl ns (Instance instfc ty decls) = do
|
||||
|
||||
putStrLn "-----"
|
||||
putStrLn "Instance \{pretty ty}"
|
||||
top <- get
|
||||
let tyFC = getFC ty
|
||||
|
||||
vty <- check (mkCtx instfc) ty (VU instfc)
|
||||
-- Here `tele` holds arguments to the instance
|
||||
let (codomain, tele) = splitTele vty
|
||||
-- env represents the environment of those arguments
|
||||
let env = tenv (length tele)
|
||||
debug "codomain \{pprint [] codomain}"
|
||||
debug "tele is \{show tele}"
|
||||
|
||||
-- ok so we need a name, a hack for now.
|
||||
-- Maybe we need to ask the user (e.g. `instance someName : Monad Foo where`)
|
||||
-- or use "Monad\{show $ length defs}"
|
||||
let instname = interpolate $ pprint [] codomain
|
||||
let sigDecl = TypeSig instfc [instname] ty
|
||||
-- This needs to be declared before processing the defs, but the defs need to be
|
||||
-- declared before this - side effect is that a duplicate def is noted at the first
|
||||
-- member
|
||||
case lookupRaw instname top of
|
||||
Just _ => pure MkUnit -- TODO check that the types match
|
||||
Nothing => processDecl ns sigDecl
|
||||
|
||||
let (Just decls) = collectDecl <$> decls
|
||||
| _ => do
|
||||
debug "Forward declaration \{show sigDecl}"
|
||||
|
||||
let (Ref _ tconName _, args) := funArgs codomain
|
||||
| (tm, _) => error tyFC "\{pprint [] codomain} doesn't appear to be a TCon application"
|
||||
let (Just (MkEntry _ name type (TCon cons))) = lookup tconName top
|
||||
| _ => error tyFC "\{tconName} is not a type constructor"
|
||||
let [con] = cons
|
||||
| _ => error tyFC "\{tconName} has multiple constructors \{show cons}"
|
||||
let (Just (MkEntry _ _ dcty (DCon _ _))) = lookup con top
|
||||
| _ => error tyFC "can't find constructor \{show con}"
|
||||
vdcty@(VPi _ nm icit rig a b) <- eval [] CBN dcty
|
||||
| x => error (getFC x) "dcty not Pi"
|
||||
debug "dcty \{pprint [] dcty}"
|
||||
let (_,args) = funArgs codomain
|
||||
|
||||
debug "traverse \{show $ map showTm args}"
|
||||
-- This is a little painful because we're reverse engineering the
|
||||
-- individual types back out from the composite type
|
||||
args' <- traverse (eval env CBN) args
|
||||
debug "args' is \{show args'}"
|
||||
conTele <- getFields !(apply vdcty args') env []
|
||||
-- declare individual functions, collect their defs
|
||||
defs <- for conTele $ \case
|
||||
(MkBinder fc nm Explicit rig ty) => do
|
||||
let ty' = foldr (\(MkBinder fc nm' icit rig ty'), acc => Pi fc nm' icit rig ty' acc) ty tele
|
||||
let nm' = "\{instname},\{nm}"
|
||||
-- we're working with a Tm, so we define directly instead of processDecl
|
||||
let Just (Def fc name xs) = find (\case (Def y name xs) => name == nm; _ => False) decls
|
||||
| _ => error instfc "no definition for \{nm}"
|
||||
|
||||
setDef (QN ns nm') fc ty' Axiom
|
||||
let decl = (Def fc nm' xs)
|
||||
putStrLn "***"
|
||||
putStrLn "«\{nm'}» : \{pprint [] ty'}"
|
||||
putStrLn $ render 80 $ pretty decl
|
||||
pure $ Just decl
|
||||
_ => pure Nothing
|
||||
|
||||
for_ (mapMaybe id defs) $ \decl => do
|
||||
-- debug because already printed above, but nice to have it near processing
|
||||
debug $ render 80 $ pretty decl
|
||||
processDecl ns decl
|
||||
let (QN _ con') = con
|
||||
let decl = Def instfc instname [(RVar instfc instname, mkRHS instname conTele (RVar instfc con'))]
|
||||
putStrLn "SIGDECL"
|
||||
putStrLn "\{pretty sigDecl}"
|
||||
putStrLn $ render 80 $ pretty decl
|
||||
processDecl ns decl
|
||||
where
|
||||
-- try to extract types of individual fields from the typeclass dcon
|
||||
-- We're assuming they don't depend on each other.
|
||||
getFields : Val -> Env -> List Binder -> M (List Binder)
|
||||
getFields tm@(VPi fc nm Explicit rig ty sc) env bnds = do
|
||||
bnd <- MkBinder fc nm Explicit rig <$> quote (length env) ty
|
||||
getFields !(sc $$ VVar fc (length env) [<]) env (bnd :: bnds)
|
||||
getFields tm@(VPi fc nm _ rig ty sc) env bnds = getFields !(sc $$ VVar fc (length env) [<]) env bnds
|
||||
getFields tm xs bnds = pure $ reverse bnds
|
||||
|
||||
tenv : Nat -> Env
|
||||
tenv Z = []
|
||||
tenv (S k) = (VVar emptyFC k [<] :: tenv k)
|
||||
|
||||
mkRHS : String -> List Binder -> Raw -> Raw
|
||||
mkRHS instName (MkBinder fc nm Explicit rig ty :: bs) tm = mkRHS instName bs (RApp fc tm (RVar fc "\{instName},\{nm}") Explicit)
|
||||
mkRHS instName (b :: bs) tm = mkRHS instName bs tm
|
||||
mkRHS instName [] tm = tm
|
||||
|
||||
apply : Val -> List Val -> M Val
|
||||
apply x [] = pure x
|
||||
apply (VPi fc nm icit rig a b) (x :: xs) = apply !(b $$ x) xs
|
||||
apply x (y :: xs) = error instfc "expected pi type \{show x}"
|
||||
|
||||
processDecl ns (ShortData fc lhs sigs) = do
|
||||
(nm,args) <- getArgs lhs []
|
||||
let ty = foldr (\ (fc,n), a => (RPi fc (BI fc n Explicit Zero) (RU fc) a)) (RU fc) args
|
||||
cons <- traverse (mkDecl args []) sigs
|
||||
let dataDecl = Data fc nm ty cons
|
||||
putStrLn "SHORTDATA"
|
||||
putStrLn "\{pretty dataDecl}"
|
||||
processDecl ns dataDecl
|
||||
where
|
||||
getArgs : Raw -> List (FC, String) -> M (String, List (FC, String))
|
||||
getArgs (RVar fc1 nm) acc = pure (nm, acc)
|
||||
getArgs (RApp _ t (RVar fc' nm) _) acc = getArgs t ((fc', nm) :: acc)
|
||||
getArgs tm _ = error (getFC tm) "Expected contructor application, got: \{show tm}"
|
||||
|
||||
mkDecl : List (FC, Name) -> List Raw -> Raw -> M Decl
|
||||
mkDecl args acc (RVar fc' name) = do
|
||||
let base = foldr (\ ty, acc => RPi (getFC ty) (BI (getFC ty) "_" Explicit Many) ty acc) lhs acc
|
||||
let ty = foldr (\ (fc,nm), acc => RPi fc (BI fc nm Implicit Zero) (RU fc) acc) base args
|
||||
pure $ TypeSig fc' [name] ty
|
||||
mkDecl args acc (RApp fc' t u icit) = mkDecl args (u :: acc) t
|
||||
mkDecl args acc tm = error (getFC tm) "Expected contructor application, got: \{show tm}"
|
||||
|
||||
processDecl ns (Data fc nm ty cons) = do
|
||||
putStrLn "-----"
|
||||
putStrLn "Data \{nm}"
|
||||
top <- get
|
||||
mc <- readIORef top.metaCtx
|
||||
tyty <- check (mkCtx fc) ty (VU fc)
|
||||
case lookupRaw nm top of
|
||||
Just (MkEntry _ name type Axiom) => do
|
||||
unifyCatch fc (mkCtx fc) !(eval [] CBN tyty) !(eval [] CBN type)
|
||||
Just (MkEntry _ name type _) => error fc "\{show nm} already declared"
|
||||
Nothing => setDef (QN ns nm) fc tyty Axiom
|
||||
cnames <- for cons $ \x => case x of
|
||||
(TypeSig fc names tm) => do
|
||||
debug "check dcon \{show names} \{show tm}"
|
||||
dty <- check (mkCtx fc) tm (VU fc)
|
||||
debug "dty \{show names} is \{pprint [] dty}"
|
||||
|
||||
-- We only check that codomain uses the right type constructor
|
||||
-- We know it's in U because it's part of a checked Pi type
|
||||
let (codomain, tele) = splitTele dty
|
||||
-- for printing
|
||||
let tnames = reverse $ map (\(MkBinder _ nm _ _ _) => nm) tele
|
||||
let (Ref _ hn _, args) := funArgs codomain
|
||||
| (tm, _) => error (getFC tm) "expected \{nm} got \{pprint tnames tm}"
|
||||
when (hn /= QN ns nm) $
|
||||
error (getFC codomain) "Constructor codomain is \{pprint tnames codomain} rather than \{nm}"
|
||||
|
||||
for_ names $ \ nm' => do
|
||||
setDef (QN ns nm') fc dty (DCon (getArity dty) hn)
|
||||
pure $ map (QN ns) names
|
||||
decl => throwError $ E (getFC decl) "expected constructor declaration"
|
||||
putStrLn "setDef \{nm} TCon \{show $ join cnames}"
|
||||
updateDef (QN ns nm) fc tyty (TCon (join cnames))
|
||||
-- logMetas mstart
|
||||
where
|
||||
checkDeclType : Tm -> M ()
|
||||
checkDeclType (UU _) = pure ()
|
||||
checkDeclType (Pi _ str icit rig t u) = checkDeclType u
|
||||
checkDeclType _ = error fc "data type doesn't return U"
|
||||
|
||||
processDecl ns (Record recordFC nm tele cname decls) = do
|
||||
putStrLn "-----"
|
||||
putStrLn "Record"
|
||||
let fields = getSigs decls
|
||||
let dcName = fromMaybe "Mk\{nm}" cname
|
||||
let tcType = teleToPi tele (RU recordFC)
|
||||
-- REVIEW - I probably want to stick the telescope in front of the fields
|
||||
let tail = foldl (\ acc, (BI fc nm icit _, _) => RApp fc acc (RVar fc nm) icit) (RVar recordFC nm) tele
|
||||
let dcType = teleToPi (impTele tele) $
|
||||
foldr (\(fc, nm, ty), acc => RPi fc (BI fc nm Explicit Many) ty acc ) tail fields
|
||||
|
||||
putStrLn "tcon type \{pretty tcType}"
|
||||
putStrLn "dcon type \{pretty dcType}"
|
||||
let decl = Data recordFC nm tcType [TypeSig recordFC [dcName] dcType]
|
||||
putStrLn "Decl:"
|
||||
putStrLn $ render 90 $ pretty decl
|
||||
processDecl ns decl
|
||||
for_ fields $ \ (fc,name,ty) => do
|
||||
-- TODO dependency isn't handled yet
|
||||
-- we'll need to replace stuff like `len` with `len self`.
|
||||
let funType = teleToPi (impTele tele) $ RPi fc (BI fc "_" Explicit Many) tail ty
|
||||
let autoPat = foldl (\acc, (fc,nm,ty) => RApp fc acc (RVar fc nm) Explicit) (RVar recordFC dcName) fields
|
||||
|
||||
-- `fieldName` - consider dropping to keep namespace clean
|
||||
-- let lhs = foldl (\acc, (BI fc' nm icit quant, _) => RApp fc' acc (RVar fc' nm) Implicit) (RVar fc name) tele
|
||||
-- let lhs = RApp recordFC lhs autoPat Explicit
|
||||
-- let decl = Def fc name [(lhs, (RVar fc name))]
|
||||
-- putStrLn "\{name} : \{pretty funType}"
|
||||
-- putStrLn "\{pretty decl}"
|
||||
-- processDecl ns $ TypeSig fc [name] funType
|
||||
-- processDecl ns decl
|
||||
|
||||
-- `.fieldName`
|
||||
let pname = "." ++ name
|
||||
let lhs = foldl (\acc, (BI fc' nm icit quant, _) => RApp fc' acc (RVar fc' nm) Implicit) (RVar fc pname) tele
|
||||
let lhs = RApp recordFC lhs autoPat Explicit
|
||||
let pdecl = Def fc pname [(lhs, (RVar fc name))]
|
||||
putStrLn "\{pname} : \{pretty funType}"
|
||||
putStrLn "\{pretty pdecl}"
|
||||
processDecl ns $ TypeSig fc [pname] funType
|
||||
processDecl ns pdecl
|
||||
Reference in New Issue
Block a user