Newt in Newt compiles (but does not run)

This commit is contained in:
2025-01-04 09:26:33 -08:00
parent 46434cc555
commit 6b1eef86a7
21 changed files with 2970 additions and 91 deletions

View File

@@ -8,3 +8,20 @@ record List1 a where
constructor _:::_ constructor _:::_
head1 : a head1 : a
tail1 : List a tail1 : List a
split1 : String String List1 String
split1 str by = case split str by of
Nil => str ::: Nil
x :: xs => x ::: xs
unsnoc : a. List1 a List a × a
unsnoc {a} (x ::: xs) = go x xs
where
go : a List a List a × a
go x Nil = (Nil, x)
go x (y :: ys) = let (as, a) = go y ys in (x :: as, a)
splitFileName : String String × String
splitFileName fn = case split1 fn "." of
part ::: Nil => (part, "")
xs => mapFst (joinBy ".") $ unsnoc xs

View File

@@ -200,3 +200,6 @@ foldMap f m Nil = m
foldMap f m ((a,b) :: xs) = case lookupMap a m of foldMap f m ((a,b) :: xs) = case lookupMap a m of
Nothing => foldMap f (updateMap a b m) xs Nothing => foldMap f (updateMap a b m) xs
Just (_, b') => foldMap f (updateMap a (f b' b) m) xs Just (_, b') => foldMap f (updateMap a (f b' b) m) xs
listValues : k v. SortedMap k v List v
listValues sm = map snd $ toList sm

361
done/Lib/Compile.newt Normal file
View File

@@ -0,0 +1,361 @@
-- TODO Audit how much "outside" stuff could pile up in the continuation.
module Lib.Compile
import Lib.Types
import Lib.Prettier
import Lib.CompileExp
import Lib.TopContext
import Data.String
import Data.Maybe
import Data.Int
data StKind = Plain | Return | Assign String
JSStmt : StKind -> U
JSExp : U
data JAlt : U where
JConAlt : e. String -> JSStmt e -> JAlt
JDefAlt : e. JSStmt e -> JAlt
JLitAlt : e. JSExp -> JSStmt e -> JAlt
data JSExp : U where
LitArray : List JSExp -> JSExp
LitObject : List (String × JSExp) -> JSExp
LitString : String -> JSExp
LitInt : Int -> JSExp
Apply : JSExp -> List JSExp -> JSExp
Var : String -> JSExp
JLam : List String -> JSStmt Return -> JSExp
JUndefined : JSExp
Index : JSExp -> JSExp -> JSExp
Dot : JSExp -> String -> JSExp
data JSStmt : StKind -> U where
-- Maybe make this a snoc...
JSnoc : a. JSStmt Plain -> JSStmt a -> JSStmt a
JPlain : JSExp -> JSStmt Plain
JConst : (nm : String) -> JSExp -> JSStmt Plain
JReturn : JSExp -> JSStmt Return
JLet : (nm : String) -> JSStmt (Assign nm) -> JSStmt Plain -- need somebody to assign
JAssign : (nm : String) -> JSExp -> JSStmt (Assign nm)
-- TODO - switch to Int tags
-- FIXME add e to JAlt (or just drop it?)
JCase : a. JSExp -> List JAlt -> JSStmt a
-- throw can't be used
JError : a. String -> JSStmt a
Cont : StKind U
Cont e = JSExp -> JSStmt e
-- JSEnv contains `Var` for binders or `Dot` for destructured data. It
-- used to translate binders
record JSEnv where
constructor MkEnv
jsenv : List JSExp
depth : Int
-- this was like this, are we not using depth?
push : JSEnv -> JSExp -> JSEnv
push (MkEnv env depth) exp = MkEnv (exp :: env) depth
emptyJSEnv : JSEnv
emptyJSEnv = MkEnv Nil 0
litToJS : Literal -> JSExp
litToJS (LString str) = LitString str
litToJS (LChar c) = LitString $ pack (c :: Nil)
litToJS (LInt i) = LitInt i
-- Stuff nm.h1, nm.h2, ... into environment
-- TODO consider JSExp instead of nm, so we can have $foo.h1 instead of assigning a sc.
mkEnv : String -> Int -> JSEnv -> List String -> JSEnv
mkEnv nm k env Nil = env
mkEnv nm k env (x :: xs) = mkEnv nm (1 + k) (push env (Dot (Var nm) "h\{show k}")) xs
envNames : Env -> List String
-- given a name, find a similar one that doesn't shadow in Env
freshName : String -> JSEnv -> String
freshName nm env = if free env.jsenv nm then nm else go nm 1
where
free : List JSExp -> String -> Bool
free Nil nm = True
free (Var n :: xs) nm = if n == nm then False else free xs nm
free (_ :: xs) nm = free xs nm
go : String -> Int -> String
go nm k = let nm' = "\{nm}\{show k}" in if free env.jsenv nm' then nm' else go nm (1 + k)
freshName' : String -> JSEnv -> (String × JSEnv)
freshName' nm env =
let nm' = freshName nm env -- "\{nm}$\{show $ length env}"
env' = push env (Var nm')
in (nm', env')
freshNames : List String -> JSEnv -> (List String × JSEnv)
freshNames nms env = go nms env Lin
where
go : List Name -> JSEnv -> SnocList Name -> (List String × JSEnv)
go Nil env acc = (acc <>> Nil, env)
go (n :: ns) env acc =
let (n', env') = freshName' n env
in go ns env' (acc :< n')
-- This is inspired by A-normalization, look into the continuation monad
-- There is an index on JSStmt, adopted from Stefan Hoeck's code.
--
-- Here we turn a Term into a statement (which may be a sequence of statements), there
-- is a continuation, which turns the final JSExpr into a JSStmt, and the function returns
-- a JSStmt, wrapping recursive calls in JSnoc if necessary.
termToJS : ∀ e. JSEnv -> CExp -> Cont e -> JSStmt e
termToJS env (CBnd k) f = case getAt (cast k) env.jsenv of
(Just e) => f e
Nothing => fatalError "Bad bounds"
termToJS env CErased f = f JUndefined
termToJS env (CLam nm t) f =
let (nm',env') = freshName' nm env -- "\{nm}$\{show $ length env}"
in f $ JLam (nm' :: Nil) (termToJS env' t JReturn)
termToJS env (CFun nms t) f =
let (nms', env') = freshNames nms env
in f $ JLam nms' (termToJS env' t JReturn)
termToJS env (CRef nm) f = f $ Var nm
termToJS env (CMeta k) f = f $ LitString "META \{show k}"
termToJS env (CLit lit) f = f (litToJS lit)
-- if it's a var, just use the original
termToJS env (CLet nm (CBnd k) u) f = case getAt (cast k) env.jsenv of
Just e => termToJS (push env e) u f
Nothing => fatalError "bad bounds"
termToJS env (CLet nm t u) f =
let nm' = freshName nm env
env' = push env (Var nm')
-- If it's a simple term, use const
in case termToJS env t (JAssign nm') of
(JAssign _ exp) => JSnoc (JConst nm' exp) (termToJS env' u f)
t' => JSnoc (JLet nm' t') (termToJS env' u f)
termToJS env (CLetRec nm t u) f =
let nm' = freshName nm env
env' = push env (Var nm')
-- If it's a simple term, use const
in case termToJS env' t (JAssign nm') of
(JAssign _ exp) => JSnoc (JConst nm' exp) (termToJS env' u f)
t' => JSnoc (JLet nm' t') (termToJS env' u f)
termToJS env (CApp t args etas) f = termToJS env t (\ t' => (argsToJS t' args Lin f)) -- (f (Apply t' args'))))
where
etaExpand : JSEnv -> Nat -> SnocList JSExp -> JSExp -> JSExp
etaExpand env Z args tm = Apply tm (args <>> Nil)
etaExpand env (S etas) args tm =
let nm' = freshName "eta" env
env' = push env (Var nm')
in JLam (nm' :: Nil) $ JReturn $ etaExpand (push env (Var nm')) etas (args :< Var nm') tm
argsToJS : e. JSExp -> List CExp -> SnocList JSExp -> (JSExp -> JSStmt e) -> JSStmt e
argsToJS tm Nil acc k = k (etaExpand env (cast etas) acc tm)
-- k (acc <>> Nil)
argsToJS tm (x :: xs) acc k = termToJS env x (\ x' => argsToJS tm xs (acc :< x') k)
termToJS {e} env (CCase t alts) f =
-- need to assign the scrutinee to a variable (unless it is a var already?)
-- and add (Bnd -> JSExpr map)
-- TODO default case, let's drop the extra field.
termToJS env t $ \case
(Var nm) => maybeCaseStmt env nm alts
t' => do
-- TODO refactor nm to be a JSExp with Var{} or Dot{}
-- FIXME sc$ seemed to shadow something else, lets get this straightened out
-- we need freshName names that are not in env (i.e. do not play in debruijn)
let nm = "_sc$\{show env.depth}"
let env' = MkEnv env.jsenv (1 + env.depth)
JSnoc (JConst nm t') (maybeCaseStmt env' nm alts)
where
termToJSAlt : JSEnv -> String -> CAlt -> JAlt
termToJSAlt env nm (CConAlt name args u) = JConAlt name (termToJS (mkEnv nm 0 env args) u f)
-- intentionally reusing scrutinee name here
termToJSAlt env nm (CDefAlt u) = JDefAlt (termToJS (env) u f)
termToJSAlt env nm (CLitAlt lit u) = JLitAlt (litToJS lit) (termToJS env u f)
maybeCaseStmt : JSEnv -> String -> List CAlt -> JSStmt e
-- If there is a single alt, assume it matched
maybeCaseStmt env nm ((CConAlt _ args u) :: Nil) = (termToJS (mkEnv nm 0 env args) u f)
maybeCaseStmt env nm alts@(CLitAlt _ _ :: _) =
(JCase (Var nm) (map (termToJSAlt env nm) alts))
maybeCaseStmt env nm alts =
(JCase (Dot (Var nm) "tag") (map (termToJSAlt env nm) alts))
jsKeywords : List String
jsKeywords = (
"break" :: "case" :: "catch" :: "continue" :: "debugger" :: "default" :: "delete" :: "do" :: "else" ::
"finally" :: "for" :: "function" :: "if" :: "in" :: "instanceof" :: "new" :: "return" :: "switch" ::
"this" :: "throw" :: "try" :: "typeof" :: "var" :: "void" :: "while" :: "with" ::
"class" :: "const" :: "enum" :: "export" :: "extends" :: "import" :: "super" ::
"implements" :: "class" :: "let" :: "package" :: "private" :: "protected" :: "public" ::
"static" :: "yield" ::
"null" :: "true" :: "false" ::
-- might not be a big issue with namespaces on names now.
"String" :: "Number" :: "Array" :: "BigInt" :: Nil)
-- escape identifiers for js
jsIdent : String -> Doc
jsIdent id = if elem id jsKeywords then text ("$" ++ id) else text $ pack $ fix (unpack id)
where
fix : List Char -> List Char
fix Nil = Nil
fix (x :: xs) =
if isAlphaNum x || x == '_' then
x :: fix xs
-- make qualified names more readable
else if x == '.' then '_' :: fix xs
else if x == '$' then
'$' :: '$' :: fix xs
else
'$' :: (toHex (cast x)) ++ fix xs
stmtToDoc : e. JSStmt e -> Doc
expToDoc : JSExp -> Doc
expToDoc (LitArray xs) = fatalError "TODO - LitArray to doc"
expToDoc (LitObject xs) = text "{" <+> folddoc (\ a e => a ++ text ", " <+/> e) (map entry xs) <+> text "}"
where
entry : (String × JSExp) -> Doc
-- TODO quote if needed
entry (nm, exp) = jsIdent nm ++ text ":" <+> expToDoc exp
expToDoc (LitString str) = text $ quoteString str
expToDoc (LitInt i) = text $ show i
-- TODO add precedence
expToDoc (Apply x@(JLam _ _) xs) = text "(" ++ expToDoc x ++ text ")" ++ text "(" ++ nest 2 (commaSep (map expToDoc xs)) ++ text ")"
expToDoc (Apply x xs) = expToDoc x ++ text "(" ++ nest 2 (commaSep (map expToDoc xs)) ++ text ")"
expToDoc (Var nm) = jsIdent nm
expToDoc (JLam nms (JReturn exp)) = text "(" <+> commaSep (map jsIdent nms) <+> text ") =>" <+> text "(" ++ expToDoc exp ++ text ")"
expToDoc (JLam nms body) = text "(" <+> commaSep (map jsIdent nms) <+> text ") =>" <+> bracket "{" (stmtToDoc body) "}"
expToDoc JUndefined = text "undefined"
expToDoc (Index obj ix) = expToDoc obj ++ text "(" ++ expToDoc ix ++ text " :: Nil)"
expToDoc (Dot obj nm) = expToDoc obj ++ text "." ++ jsIdent nm
caseBody : e. JSStmt e -> Doc
caseBody stmt@(JReturn x) = nest 2 (line ++ stmtToDoc stmt)
-- caseBody {e = Return} stmt@(JCase{}) = nest 2 (line ++ stmtToDoc stmt)
caseBody {e} stmt@(JCase _ _) = nest 2 (line ++ stmtToDoc stmt </> text "break;")
caseBody stmt = line ++ text "{" ++ nest 2 (line ++ stmtToDoc stmt </> text "break;") </> text "}"
altToDoc : JAlt -> Doc
altToDoc (JConAlt nm stmt) = text "case" <+> text (quoteString nm) ++ text ":" ++ caseBody stmt
altToDoc (JDefAlt stmt) = text "default" ++ text ":" ++ caseBody stmt
altToDoc (JLitAlt a stmt) = text "case" <+> expToDoc a ++ text ":" ++ caseBody stmt
stmtToDoc (JSnoc x y) = stmtToDoc x </> stmtToDoc y
stmtToDoc (JPlain x) = expToDoc x ++ text ";"
-- I might not need these split yet.
stmtToDoc (JLet nm body) = text "let" <+> jsIdent nm ++ text ";" </> stmtToDoc body
stmtToDoc (JAssign nm expr) = jsIdent nm <+> text "=" <+> expToDoc expr ++ text ";"
stmtToDoc (JConst nm x) = text "const" <+> jsIdent nm <+> nest 2 (text "=" <+/> expToDoc x ++ text ";")
stmtToDoc (JReturn x) = text "return" <+> expToDoc x ++ text ";"
stmtToDoc (JError str) = text "throw new Error(" ++ text (quoteString str) ++ text ");"
stmtToDoc (JCase sc alts) =
text "switch (" ++ expToDoc sc ++ text ")" <+> bracket "{" (stack $ map altToDoc alts) "}"
mkArgs : Nat -> List String -> List String
mkArgs Z acc = acc
mkArgs (S k) acc = mkArgs k ("h\{show k}" :: acc)
dcon : QName -> Nat -> Doc
dcon qn@(QN ns nm) Z = stmtToDoc $ JConst (show qn) $ LitObject (("tag", LitString nm) :: Nil)
dcon qn@(QN ns nm) arity =
let args = mkArgs arity Nil
obj = ("tag", LitString nm) :: map (\x => (x, Var x)) args
in stmtToDoc $ JConst (show qn) (JLam args (JReturn (LitObject obj)))
-- use iife to turn stmts into expr
maybeWrap : JSStmt Return -> JSExp
maybeWrap (JReturn exp) = exp
maybeWrap stmt = Apply (JLam Nil stmt) Nil
entryToDoc : TopEntry -> M Doc
entryToDoc (MkEntry _ name ty (Fn tm)) = do
debug $ \ _ => "compileFun \{render 90 $ pprint Nil tm}"
ct <- compileFun tm
let exp = maybeWrap $ termToJS emptyJSEnv ct JReturn
pure $ text "const" <+> jsIdent (show name) <+> text "=" <+/> expToDoc exp ++ text ";"
entryToDoc (MkEntry _ name type Axiom) = pure $ text ""
entryToDoc (MkEntry _ name type (TCon strs)) = pure $ dcon name (piArity type)
entryToDoc (MkEntry _ name type (DCon arity str)) = pure $ dcon name (cast arity)
entryToDoc (MkEntry _ name type PrimTCon) = pure $ dcon name (piArity type)
entryToDoc (MkEntry _ name _ (PrimFn src _)) = pure $ text "const" <+> jsIdent (show name) <+> text "=" <+> text src
process : (List QName × List Doc) -> QName -> M (List QName × List Doc)
walkTm : Tm -> (List QName × List Doc) -> M (List QName × List Doc)
walkAlt : (List QName × List Doc) -> CaseAlt -> M (List QName × List Doc)
walkAlt acc (CaseDefault t) = walkTm t acc
walkAlt acc (CaseCons name args t) = walkTm t acc
walkAlt acc (CaseLit lit t) = walkTm t acc
walkTm (Ref x nm y) acc = process acc nm
walkTm (Lam x str _ _ t) acc = walkTm t acc
walkTm (App x t u) acc = walkTm u acc >>= walkTm t
walkTm (Pi x str icit y t u) acc = walkTm u acc >>= walkTm t
walkTm (Let x str t u) acc = walkTm u acc >>= walkTm t
walkTm (LetRec x str _ t u) acc = walkTm u acc >>= walkTm t
walkTm (Case x t alts) acc = foldlM walkAlt acc alts
walkTm _ acc = pure acc
-- This version (call `reverse ∘ snd <$> process "main" (Nil × Nil)`) will do dead
-- code elimination, but the Prelude js primitives are reaching for
-- stuff like True, False, MkUnit, fs which get eliminated
process (done,docs) nm = do
let (False) = elem nm done | _ => pure (done,docs)
top <- get
case lookup nm top of
Nothing => error emptyFC "\{show nm} not in scope"
Just entry@(MkEntry _ name ty (PrimFn src used)) => do
(done,docs) <- foldlM assign (nm :: done, docs) used
edoc <- entryToDoc entry
pure (done, edoc :: docs)
Just (MkEntry _ name ty (Fn tm)) => do
debug $ \ _ => "compileFun \{render 90 $ pprint Nil tm}"
ct <- compileFun tm
-- If ct has zero arity and is a compount expression, this fails..
let exp = maybeWrap $ termToJS emptyJSEnv ct JReturn
let doc = text "const" <+> jsIdent (show name) <+> text "=" <+/> expToDoc exp ++ text ";"
(done,docs) <- walkTm tm (nm :: done, docs)
pure (done, doc :: docs)
Just entry => do
edoc <- entryToDoc entry
pure (nm :: done, edoc :: docs)
where
assign : (List QName × List Doc) -> String -> M (List QName × List Doc)
assign (done, docs) nm = do
top <- get
case lookupRaw nm top of
Nothing => pure (done, docs)
(Just (MkEntry fc name type def)) => do
let tag = QN Nil nm
let (False) = elem tag done | _ => pure (done,docs)
(done,docs) <- process (done, docs) name
let doc = text "const" <+> jsIdent nm <+> text "=" <+> jsIdent (show name) ++ text ";"
pure (tag :: done, doc :: docs)
compile : M (List Doc)
compile = do
top <- get
case lookupRaw "main" top of
Just (MkEntry fc name type def) => do
tmp <- snd <$> process (Nil, Nil) name
let exec = stmtToDoc $ JPlain $ Apply (Var $ show name) Nil
pure $ reverse (exec :: tmp)
-- If there is no main, compile everything for the benefit of the playground
Nothing => do
top <- get
traverse entryToDoc $ map snd $ toList top.defs

170
done/Lib/CompileExp.newt Normal file
View File

@@ -0,0 +1,170 @@
-- First pass of compilation
-- - work out arities and fully apply functions / constructors (currying)
-- currying is problemmatic because we need to insert lambdas (η-expand) and
-- it breaks all of the de Bruijn indices
-- - expand metas (this is happening earlier)
-- - erase stuff (there is another copy that essentially does the same thing)
-- I could make names unique (e.q. on lambdas), but I might want that to vary per backend?
module Lib.CompileExp
import Data.List
import Lib.Types -- Name / Tm
import Lib.TopContext
import Lib.Prettier
import Lib.Util
CExp : U
data CAlt : U where
CConAlt : String -> List String -> CExp -> CAlt
-- REVIEW keep var name?
CDefAlt : CExp -> CAlt
-- literal
CLitAlt : Literal -> CExp -> CAlt
data CExp : U where
CBnd : Int -> CExp
CLam : Name -> CExp -> CExp
CFun : List Name -> CExp -> CExp
-- REVIEW This feels like a hack, but if we put CLam here, the
-- deBruijn gets messed up in code gen
CApp : CExp -> List CExp -> Int -> CExp
-- TODO make DCon/TCon app separate so we can specialize
-- U / Pi are compiled to type constructors
CCase : CExp -> List CAlt -> CExp
CRef : Name -> CExp
CMeta : Int -> CExp
CLit : Literal -> CExp
CLet : Name -> CExp -> CExp -> CExp
CLetRec : Name -> CExp -> CExp -> CExp
CErased : CExp
-- I'm counting Lam in the term for arity. This matches what I need in
-- code gen.
lamArity : Tm -> Nat
lamArity (Lam _ _ _ _ t) = S (lamArity t)
lamArity _ = Z
piArity : Tm -> Nat
piArity (Pi _ _ _ quant _ b) = S (piArity b)
piArity _ = Z
-- This is how much we want to curry at top level
-- leading lambda Arity is used for function defs and metas
-- TODO - figure out how this will work with erasure
arityForName : FC -> QName -> M Nat
arityForName fc nm = do
top <- get
case lookup nm top of
-- let the magic hole through for now (will generate bad JS)
Nothing => error fc "Name \{show nm} not in scope"
(Just (MkEntry _ name type Axiom)) => pure Z
(Just (MkEntry _ name type (TCon strs))) => pure $ piArity type
(Just (MkEntry _ name type (DCon k str))) => pure $ cast k
(Just (MkEntry _ name type (Fn t))) => pure $ lamArity t
(Just (MkEntry _ name type (PrimTCon))) => pure $ piArity type
-- Assuming a primitive can't return a function
(Just (MkEntry _ name type (PrimFn t used))) => pure $ piArity type
compileTerm : Tm -> M CExp
-- need to eta out extra args, fill in the rest of the apps
apply : CExp -> List CExp -> SnocList CExp -> Nat -> Tm -> M CExp
-- out of args, make one up (fix that last arg)
apply t Nil acc (S k) ty = pure $ CApp t (acc <>> Nil) (1 + cast k)
-- inserting Clam, index wrong?
-- CLam "eta\{show k}" !(apply t Nil (acc :< CBnd k) k ty)
apply t (x :: xs) acc (S k) (Pi y str icit Zero a b) = apply t xs (acc :< CErased) k b
apply t (x :: xs) acc (S k) (Pi y str icit Many a b) = apply t xs (acc :< x) k b
-- see if there is anything we have to handle here
apply t (x :: xs) acc (S k) ty = error (getFC ty) "Expected pi \{showTm ty}. Overapplied function that escaped type checking?"
-- once we hit zero, we fold the rest
apply t ts acc Z ty = go (CApp t (acc <>> Nil) 0) ts
where
go : CExp -> List CExp -> M CExp
-- drop zero arg call
go (CApp t Nil 0) args = go t args
go t Nil = pure t
go t (arg :: args) = go (CApp t (arg :: Nil) 0) args
-- apply : CExp -> List CExp -> SnocList CExp -> Int -> M CExp
-- -- out of args, make one up
-- apply t Nil acc (S k) = pure $
-- CLam "eta\{show k}" !(apply t Nil (acc :< CBnd k) k)
-- apply t (x :: xs) acc (S k) = apply t xs (acc :< x) k
-- apply t ts acc 0 = go (CApp t (acc <>> Nil)) ts
-- where
-- go : CExp -> List CExp -> M CExp
-- -- drop zero arg call
-- go (CApp t Nil) args = go t args
-- go t Nil = pure t
-- go t (arg :: args) = go (CApp t (arg :: Nil)) args
compileTerm (Bnd _ k) = pure $ CBnd k
-- need to eta expand to arity
compileTerm t@(Ref fc nm _) = do
top <- get
let (Just (MkEntry _ _ type _)) = lookup nm top
| Nothing => error fc "Undefined name \{show nm}"
arity <- arityForName fc nm
apply (CRef (show nm)) Nil Lin arity type
compileTerm (Meta _ k) = pure $ CRef "meta$\{show k}" -- FIXME
compileTerm (Lam _ nm _ _ t) = CLam nm <$> compileTerm t
compileTerm tm@(App _ _ _) = case funArgs tm of
(Meta _ k, args) => do
-- this will be undefined, should only happen for use metas
pure $ CApp (CRef "Meta\{show k}") Nil 0
(t@(Ref fc nm _), args) => do
args' <- traverse compileTerm args
arity <- arityForName fc nm
top <- get
let (Just (MkEntry _ _ type _)) = lookup nm top
| Nothing => error fc "Undefined name \{show nm}"
apply (CRef (show nm)) args' Lin arity type
(t, args) => do
debug $ \ _ => "apply other \{render 90 $ pprint Nil t}"
t' <- compileTerm t
args' <- traverse compileTerm args
apply t' args' Lin Z (UU emptyFC)
-- error (getFC t) "Don't know how to apply \{showTm t}"
compileTerm (UU _) = pure $ CRef "U"
compileTerm (Pi _ nm icit rig t u) = do
t' <- compileTerm t
u' <- compileTerm u
pure $ CApp (CRef "PiType") (t' :: u' :: Nil) 0
compileTerm (Case _ t alts) = do
t' <- compileTerm t
alts' <- for alts $ \case
CaseDefault tm => CDefAlt <$> compileTerm tm
-- we use the base name for the tag, some primitives assume this
CaseCons (QN ns nm) args tm => CConAlt nm args <$> compileTerm tm
CaseLit lit tm => CLitAlt lit <$> compileTerm tm
pure $ CCase t' alts'
compileTerm (Lit _ lit) = pure $ CLit lit
compileTerm (Let _ nm t u) = do
t' <- compileTerm t
u' <- compileTerm u
pure $ CLet nm t' u'
compileTerm (LetRec _ nm _ t u) = do
t' <- compileTerm t
u' <- compileTerm u
pure $ CLetRec nm t' u'
compileTerm (Erased _) = pure CErased
compileFun : Tm -> M CExp
compileFun tm = go tm Lin
where
go : Tm -> SnocList String -> M CExp
go (Lam _ nm _ _ t) acc = go t (acc :< nm)
go tm Lin = compileTerm tm
go tm args = CFun (args <>> Nil) <$> compileTerm tm

1508
done/Lib/Elab.newt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -79,6 +79,11 @@ tryEval env (VRef fc k _ sp) = do
val <- vappSpine vtm sp val <- vappSpine vtm sp
case val of case val of
VCase _ _ _ => pure Nothing VCase _ _ _ => pure Nothing
-- For now? There is a spot in Compile.newt that has
-- two applications of fresh that is getting unfolded even
-- though it has the same head and spine. Possibly because it's
-- coming out of a let and is instantly applied
VLetRec _ _ _ _ _ => pure Nothing
v => pure $ Just v) v => pure $ Just v)
(\ _ => pure Nothing) (\ _ => pure Nothing)
_ => pure Nothing _ => pure Nothing

View File

@@ -654,14 +654,14 @@ parseMod = do
pure $ MkModule name imports decls pure $ MkModule name imports decls
data ReplCmd = -- data ReplCmd =
Def Decl -- Def Decl
| Norm Raw -- or just name? -- | Norm Raw -- or just name?
| Check Raw -- | Check Raw
-- Eventually I'd like immediate actions in the file, like lean, but I -- -- 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. -- -- also want to REPL to work and we can do that first.
parseRepl : Parser ReplCmd -- parseRepl : Parser ReplCmd
parseRepl = Def <$> parseDecl <|> Norm <$ keyword "#nf" <*> typeExpr -- parseRepl = Def <$> parseDecl <|> Norm <$ keyword "#nf" <*> typeExpr
<|> Check <$ keyword "#check" <*> typeExpr -- <|> Check <$ keyword "#check" <*> typeExpr

471
done/Lib/ProcessDecl.newt Normal file
View File

@@ -0,0 +1,471 @@
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 ctx.types) Nil
where
isVar : Int -> Val -> Bool
isVar k (VVar _ k' Lin) = k == k'
isVar _ _ = False
go : List String -> Int -> List (Val × String × Val) -> List String -> M (List String)
go _ _ Nil 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 do
ty' <- quote ctx.lvl ty
go names (1 + k) xs (" \{n} : \{render 90 $ pprint names ty'}":: acc)
else do
v' <- quote ctx.lvl v
ty' <- quote ctx.lvl ty
go names (1 + k) xs (" \{n} = \{render 90 $ pprint names v'} : \{render 90 $ pprint names ty'}":: acc)
logMetas : Int -> M Unit
logMetas mstart = do
-- FIXME, now this isn't logged for Sig / Data.
top <- get
mc <- readIORef {M} top.metaCtx
let mlen = cast {Int} {Nat} $ length' mc.metas - mstart
ignore $ 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 \{render 90 $ pprint Nil !(quote 0 soln)}"
pure MkUnit
(Unsolved fc k ctx ty User cons) => do
ty' <- quote ctx.lvl ty
let names = map fst ctx.types
env <- dumpEnv ctx
let msg = "\{env} -----------\n \{render 90 $ pprint names ty'}"
info fc "User Hole\n\{msg}"
(Unsolved fc k ctx ty kind cons) => do
ty' <- forceMeta ty
tm <- quote ctx.lvl 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 \{render 90 $ pprint (names ctx) tm} \{show $ length' cons} constraints"
msgs <- for cons $ \case
(MkMc fc env sp val) => do
pure " * (m\{show k} (\{unwords $ map show $ sp <>> Nil}) =?= \{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
-- Nil => findMatches ctx ty $ toList top.defs
-- xs => pure xs
matches <- findMatches ctx ty $ map snd $ toList top.defs
-- TODO try putting mc into TopContext for to see if it gives better terms
pure $ (" \{show $ length' matches} Solutions: \{show matches}" :: Nil)
-- pure $ " \{show $ length' matches} Solutions:" :: map ((" " ++) ∘ interpolate ∘ pprint (names ctx) ∘ fst) matches
_ => pure Nil
info fc $ unlines ((msg :: Nil) ++ msgs ++ sols)
-- addError $ E fc $ unlines ((msg :: Nil) ++ msgs ++ sols)
-- Used for Class and Record
getSigs : List Decl -> List (FC × String × Raw)
getSigs Nil = Nil
getSigs ((TypeSig _ Nil _) :: xs) = getSigs xs
getSigs ((TypeSig fc (nm :: nms) ty) :: xs) = (fc, nm, ty) :: getSigs xs
getSigs (_ :: xs) = getSigs xs
teleToPi : Telescope -> Raw -> Raw
teleToPi Nil end = end
teleToPi ((info, ty) :: tele) end = RPi (getFC info) info ty (teleToPi tele end)
impTele : Telescope -> Telescope
impTele tele = map foo tele
where
foo : BindInfo × Raw BindInfo × Raw
foo (BI fc nm _ _ , ty) = (BI fc nm Implicit Zero, ty)
processDecl : List String -> Decl -> M Unit
-- REVIEW I supposed I could have updated top here instead of the dance with the parser...
processDecl ns (PMixFix _ _ _ _) = pure MkUnit
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 MkUnit
ty <- check (mkCtx fc) tm (VU fc)
ty <- zonk top 0 Nil ty
putStrLn "TypeSig \{unwords names} : \{render 90 $ pprint Nil 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 used ty src) = do
top <- get
ty <- check (mkCtx fc) ty (VU fc)
ty' <- nf Nil ty
putStrLn "pfunc \{nm} : \{render 90 $ pprint Nil ty'} = \{show src}"
-- TODO wire through fc?
for used $ \ name => case lookupRaw name top of
Nothing => error fc "\{name} not in scope"
_ => pure MkUnit
setDef (QN ns nm) fc ty' (PrimFn src used)
processDecl ns (Def fc nm claused) = 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 \{render 90 $ pprint Nil ty}"
vty <- eval Nil CBN ty
debug $ \ _ => "\{nm} vty is \{show vty}"
-- I can take LHS apart syntactically or elaborate it with an infer
claused' <- traverse (makeClause top) claused
tm <- buildTree (mkCtx fc) (MkProb claused' vty)
-- putStrLn "Ok \{render 90 $ pprint Nil tm}"
mc <- readIORef top.metaCtx
let mlen = length' mc.metas - mstart
solveAutos mstart
-- TODO - make nf that expands all metas and drop zonk
-- Day1.newt is a test case
-- tm' <- nf Nil tm
tm' <- zonk top 0 Nil tm
when top.verbose $ \ _ => putStrLn "NF\n\{render 80 $ pprint Nil 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 Nil tm' Nil
when top.verbose $ \ _ => putStrLn "ERASED\n\{render 80 $ pprint Nil tm'}"
debug $ \ _ => "Add def \{nm} \{render 90 $ pprint Nil tm'} : \{render 90 $ pprint Nil 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 \{render 90 $ pprint Nil ty'}"
vty <- eval Nil CBN ty'
res <- check (mkCtx fc) tm vty
putStrLn " got \{render 90 $ pprint Nil res}"
norm <- nf Nil res
putStrLn " NF \{render 90 $ pprint Nil norm}"
norm <- nfv Nil res
putStrLn " NFV \{render 90 $ pprint Nil 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 mkApp (RVar classFC nm) tele
let dcType = teleToPi (impTele tele) $ foldr mkPi tail fields
putStrLn "tcon type \{render 90 $ pretty tcType}"
putStrLn "dcon type \{render 90 $ pretty dcType}"
let decl = Data classFC nm tcType (TypeSig classFC (dcName :: Nil) dcType :: Nil)
putStrLn "Decl:"
putStrLn $ render 90 $ pretty decl
processDecl ns decl
ignore $ for fields $ \case
(fc,name,ty) => do
let funType = teleToPi (impTele tele) $ RPi fc (BI fc "_" Auto Many) tail ty
let autoPat = foldl mkAutoApp (RVar classFC dcName) fields
let lhs = makeLHS (RVar fc name) tele
let lhs = RApp classFC lhs autoPat Auto
let decl = Def fc name ((lhs, (RVar fc name)) :: Nil)
putStrLn "\{name} : \{render 90 $ pretty funType}"
putStrLn "\{render 90 $ pretty decl}"
processDecl ns $ TypeSig fc (name :: Nil) funType
processDecl ns decl
where
makeLHS : Raw Telescope Raw
makeLHS acc ((BI fc' nm icit quant, _) :: tele) = RApp fc' (makeLHS acc tele) (RVar fc' nm) Implicit
makeLHS acc Nil = acc
-- TODO probably should just do the fold ourselves then.
mkAutoApp : Raw FC × String × Raw Raw
mkAutoApp acc (fc, nm, ty) = RApp fc acc (RVar fc nm) Explicit
mkPi : FC × String × Raw Raw Raw
mkPi (fc, nm, ty) acc = RPi fc (BI fc nm Explicit Many) ty acc
mkApp : Raw BindInfo × Raw Raw
mkApp acc (BI fc nm icit _, _) = RApp fc acc (RVar fc nm) icit
-- TODO - these are big, break them out into individual functions
processDecl ns (Instance instfc ty decls) = do
putStrLn "-----"
putStrLn "Instance \{render 90 $ 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 \{render 90 $ pprint Nil 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 Nil codomain
let sigDecl = TypeSig instfc (instname :: Nil) 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 "\{render 90 $ pprint Nil codomain} doesn't appear to be a TCon application"
let (Just (MkEntry _ name type (TCon cons))) = lookup tconName top
| _ => error tyFC "\{show tconName} is not a type constructor"
let (con :: Nil) = cons
| _ => error tyFC "\{show 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 Nil CBN dcty
| x => error (getFC x) "dcty not Pi"
debug $ \ _ => "dcty \{render 90 $ pprint Nil 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'}"
appty <- apply vdcty args'
conTele <- getFields appty env Nil
-- declare individual functions, collect their defs
defs <- for conTele $ \case
(MkBinder fc nm Explicit rig ty) => do
let ty' = foldr (\ x acc => case the Binder x of (MkBinder fc nm' icit rig ty') => 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 (\x => case the Decl x of
(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'}» : \{render 90 $ pprint Nil 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')) :: Nil)
putStrLn "SIGDECL"
putStrLn "\{render 90 $ 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
appsc <- sc $$ VVar fc (length' env) Lin
getFields appsc env (bnd :: bnds)
getFields tm@(VPi fc nm _ rig ty sc) env bnds = do
appsc <- sc $$ VVar fc (length' env) Lin
getFields appsc env bnds
getFields tm xs bnds = pure $ reverse bnds
tenv : Nat -> Env
tenv Z = Nil
tenv (S k) = (VVar emptyFC (cast k) Lin :: 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 Nil tm = tm
apply : Val -> List Val -> M Val
apply x Nil = pure x
apply (VPi fc nm icit rig a b) (x :: xs) = do
bx <- b $$ x
apply bx xs
apply x (y :: xs) = error instfc "expected pi type \{show x}"
processDecl ns (ShortData fc lhs sigs) = do
(nm,args) <- getArgs lhs Nil
let ty = foldr mkPi (RU fc) args
cons <- traverse (mkDecl args Nil) sigs
let dataDecl = Data fc nm ty cons
putStrLn "SHORTDATA"
putStrLn "\{render 90 $ pretty dataDecl}"
processDecl ns dataDecl
where
mkPi : FC × Name Raw Raw
mkPi (fc,n) a = RPi fc (BI fc n Explicit Zero) (RU fc) a
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 mkPi base args
pure $ TypeSig fc' (name :: Nil) ty
where
mkPi : FC × String Raw Raw
mkPi (fc,nm) acc = RPi fc (BI fc nm Implicit Zero) (RU fc) acc
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
tyty' <- eval Nil CBN tyty
type' <- eval Nil CBN type
unifyCatch fc (mkCtx fc) tyty' 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 \{render 90 $ pprint Nil dty}"
-- We only check that codomain used 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 binderName tele
let (Ref _ hn _, args) = funArgs codomain
| (tm, _) => error (getFC tm) "expected \{nm} got \{render 90 $ pprint tnames tm}"
when (hn /= QN ns nm) $ \ _ =>
error (getFC codomain) "Constructor codomain is \{render 90 $ 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
binderName : Binder Name
binderName (MkBinder _ nm _ _ _) = nm
checkDeclType : Tm -> M Unit
checkDeclType (UU _) = pure MkUnit
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\{show 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 => case the (BindInfo × Raw) bi of (BI fc nm icit _, _) => RApp fc acc (RVar fc nm) icit) (RVar recordFC nm) tele
let dcType = teleToPi (impTele tele) $
foldr (\ x acc => case the (FC × String × Raw) x of (fc, nm, ty) => RPi fc (BI fc nm Explicit Many) ty acc ) tail fields
putStrLn "tcon type \{render 90 $ pretty tcType}"
putStrLn "dcon type \{render 90 $ pretty dcType}"
let decl = Data recordFC nm tcType (TypeSig recordFC (dcName :: Nil) dcType :: Nil)
putStrLn "Decl:"
putStrLn $ render 90 $ pretty decl
processDecl ns decl
for_ fields $ \case
(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 x => case the (FC × String × Raw) x of (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} : \{render 90 $ pretty funType}"
-- putStrLn "\{render 90 $ pretty decl}"
-- processDecl ns $ TypeSig fc (name :: Nil) funType
-- processDecl ns decl
-- `.fieldName`
let pname = "." ++ name
let lhs = foldl (\acc x => case the (BindInfo × Raw) x of (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)) :: Nil)
putStrLn "\{pname} : \{render 90 $ pretty funType}"
putStrLn "\{render 90 $ pretty pdecl}"
processDecl ns $ TypeSig fc (pname :: Nil) funType
processDecl ns pdecl

View File

@@ -146,6 +146,9 @@ showCaseAlt (CaseDefault tm) = "_ => \{show tm}"
showCaseAlt (CaseCons nm args tm) = "\{show nm} \{unwords args} => \{show tm}" showCaseAlt (CaseCons nm args tm) = "\{show nm} \{unwords args} => \{show tm}"
showCaseAlt (CaseLit lit tm) = "\{show lit} => \{show tm}" showCaseAlt (CaseLit lit tm) = "\{show lit} => \{show tm}"
instance Show CaseAlt where
show = showCaseAlt
showTm : Tm -> String showTm : Tm -> String
showTm = show showTm = show
@@ -488,20 +491,20 @@ error fc msg = throwError $ E fc msg
error' : a. String -> M a error' : a. String -> M a
error' msg = throwError $ E emptyFC msg error' msg = throwError $ E emptyFC msg
freshMeta : Context -> FC -> Val -> MetaKind -> M Tm -- freshMeta : Context -> FC -> Val -> MetaKind -> M Tm
freshMeta ctx fc ty kind = do -- freshMeta ctx fc ty kind = do
top <- get -- top <- get
mc <- readIORef top.metaCtx -- mc <- readIORef top.metaCtx
debug $ \ _ => "fresh meta \{show mc.next} : \{show ty} (\{show kind})" -- debug $ \ _ => "fresh meta \{show mc.next} : \{show ty} (\{show kind})"
writeIORef top.metaCtx $ MC (Unsolved fc mc.next ctx ty kind Nil :: mc.metas) (1 + mc.next) -- writeIORef top.metaCtx $ MC (Unsolved fc mc.next ctx ty kind Nil :: mc.metas) (1 + mc.next)
pure $ applyBDs 0 (Meta fc mc.next) ctx.bds -- pure $ applyBDs 0 (Meta fc mc.next) ctx.bds
where -- where
-- hope I got the right order here :) -- -- hope I got the right order here :)
applyBDs : Int -> Tm -> List BD -> Tm -- applyBDs : Int -> Tm -> List BD -> Tm
applyBDs k t Nil = t -- applyBDs k t Nil = t
-- review the order here -- -- review the order here
applyBDs k t (Bound :: xs) = App emptyFC (applyBDs (1 + k) t xs) (Bnd emptyFC k) -- applyBDs k t (Bound :: xs) = App emptyFC (applyBDs (1 + k) t xs) (Bnd emptyFC k)
applyBDs k t (Defined :: xs) = applyBDs (1 + k) t xs -- applyBDs k t (Defined :: xs) = applyBDs (1 + k) t xs
lookupMeta : Int -> M MetaEntry lookupMeta : Int -> M MetaEntry
lookupMeta ix = do lookupMeta ix = do

View File

@@ -11,16 +11,16 @@ funArgs tm = go tm Nil
go t args = (t, args) go t args = (t, args)
data Binder : U where data Binder : U where
MkBind : FC -> String -> Icit -> Quant -> Tm -> Binder MkBinder : FC -> String -> Icit -> Quant -> Tm -> Binder
-- I don't have a show for terms without a name list -- I don't have a show for terms without a name list
instance Show Binder where instance Show Binder where
show (MkBind _ nm icit quant t) = "(\{show quant}\{nm} \{show icit} : ... :: Nil)" show (MkBinder _ nm icit quant t) = "(\{show quant}\{nm} \{show icit} : ... :: Nil)"
splitTele : Tm -> (Tm × List Binder) splitTele : Tm -> (Tm × List Binder)
splitTele = go Nil splitTele = go Nil
where where
go : List Binder -> Tm -> (Tm × List Binder) go : List Binder -> Tm -> (Tm × List Binder)
go ts (Pi fc nm icit quant t u) = go (MkBind fc nm icit quant t :: ts) u go ts (Pi fc nm icit quant t u) = go (MkBinder fc nm icit quant t :: ts) u
go ts tm = (tm, reverse ts) go ts tm = (tm, reverse ts)

234
done/Main.newt Normal file
View File

@@ -0,0 +1,234 @@
module Main
import Data.List
import Data.List1
import Data.String
import Data.Vect
import Data.IORef
import Lib.Common
import Lib.Compile
import Lib.Parser
import Lib.Elab
import Lib.Parser.Impl
import Lib.Prettier
import Lib.ProcessDecl
import Lib.Token
import Lib.Tokenizer
import Lib.TopContext
import Lib.Types
import Lib.Syntax
import Lib.Syntax
import Node
-- import System
-- import System.Directory
-- import System.File
-- import System.Path
-- import Data.Buffer
jsonTopContext : M Json
jsonTopContext = do
top <- get
pure $ JsonObj (("context", JsonArray (map jsonDef $ listValues top.defs)) :: Nil)
where
jsonDef : TopEntry -> Json
-- There is no FC here...
jsonDef (MkEntry fc (QN ns name) type def) = JsonObj
( ("fc", toJson fc)
:: ("name", toJson name)
:: ("type", toJson (render 80 $ pprint Nil type) )
:: Nil)
dumpContext : TopContext -> M Unit
dumpContext top = do
putStrLn "Context:"
go $ listValues top.defs
putStrLn "---"
where
go : List TopEntry -> M Unit
go Nil = pure MkUnit
go (x :: xs) = putStrLn " \{show x}" >> go xs
writeSource : String -> M Unit
writeSource fn = do
docs <- compile
let src = unlines $
( "\"use strict\";"
:: "const PiType = (h0, h1) => ({ tag: \"PiType\", h0, h1 })"
:: Nil)
++ map (render 90) docs
(Right _) <- liftIO {M} $ writeFile fn src
| Left err => exitFailure (show err)
-- (Right _) <- chmodRaw fn 493 | Left err => exitFailure (show err)
pure MkUnit
parseDecls : String -> Operators -> TokenList -> SnocList Decl -> M (List Decl × Operators)
parseDecls fn ops Nil acc = pure (acc <>> Nil, ops)
parseDecls fn ops toks@(first :: _) acc =
case partialParse fn (sameLevel parseDecl) ops toks of
Left (err, toks) => do
putStrLn $ showError "" err
addError err
parseDecls fn ops (recover toks) acc
Right (decl,ops,toks) => parseDecls fn ops toks (acc :< decl)
where
recover : TokenList -> TokenList
recover Nil = Nil
-- skip to top token, but make sure there is progress
recover (tok :: toks) = if tok.bounds.startCol == 0 && tok.bounds /= first.bounds
then (tok :: toks)
else recover toks
-- New style loader, one def at a time
processModule : FC -> String -> List String -> QName -> M String
processModule importFC base stk qn@(QN ns nm) = do
top <- get
-- TODO make top.loaded a List QName
let name = joinBy "." (snoc ns nm)
let (False) = elem name top.loaded | _ => pure ""
modify (\ top => MkTop top.defs top.metaCtx top.verbose top.errors (name :: top.loaded)top.ops)
let fn = (joinBy "/" (base :: ns)) ++ "/" ++ nm ++ ".newt"
(Right src) <- liftIO {M} $ readFile fn
| Left err => exitFailure "ERROR at \{show importFC}: error reading \{fn}: \{show err}"
let (Right toks) = tokenise fn src
| Left err => exitFailure (showError src err)
let (Right ((nameFC, modName), ops, toks)) = partialParse fn parseModHeader top.ops toks
| Left (err, toks) => exitFailure (showError src err)
putStrLn "module \{modName}"
let ns = split modName "."
let (path, modName') = unsnoc $ split1 modName "."
-- let bparts = split base "/"
let (True) = qn == QN path modName'
| _ => exitFailure "ERROR at \{show nameFC}: module name \{show modName} doesn't match file name \{show fn}"
let (Right (imports, ops, toks)) = partialParse fn parseImports ops toks
| Left (err, toks) => exitFailure (showError src err)
for_ imports $ \case
MkImport fc name' => do
let (a,b) = unsnoc $ split1 name' "."
let qname = QN a b
-- we could use `fc` if it had a filename in it
when (elem name' stk) $ \ _ => error emptyFC "import loop \{show name} -> \{show name'}"
processModule fc base (name :: stk) qname
top <- get
mc <- readIORef top.metaCtx
-- REVIEW suppressing unsolved and solved metas from previous files
-- I may want to know about (or exitFailure early on) unsolved
let mstart = length mc.metas
-- let Right (decls, ops, toks) = partialParse fn (manySame parseDecl) top.ops toks
-- | Left (err, toks) => exitFailure (showError src err)
(decls, ops) <- parseDecls fn top.ops toks Lin
modify (\ top => MkTop top.defs top.metaCtx top.verbose top.errors top.loaded ops)
putStrLn "process Decls"
traverse (tryProcessDecl ns) (collectDecl decls)
-- we don't want implict errors from half-processed functions
-- but suppress them all on error for simplicity.
errors <- readIORef top.errors
if stk == Nil then logMetas (cast mstart) else pure MkUnit
pure src
where
-- parseDecls :
-- tryParseDecl :
tryProcessDecl : List String -> Decl -> M Unit
tryProcessDecl ns decl = do
Left err <- tryError $ processDecl ns decl | _ => pure MkUnit
addError err
baseDir : SnocList String -> SnocList String -> Either String String
baseDir dirs Lin = Right $ joinBy "/" (dirs <>> Nil)
baseDir (dirs :< d) (ns :< n) = if d == n
then baseDir dirs ns
else Left "module path doesn't match directory"
baseDir Lin _ = Left "module path doesn't match directory"
processFile : String -> M Unit
processFile fn = do
putStrLn "*** Process \{fn}"
let parts = split1 fn "/"
let (dirs,file) = unsnoc parts
let dir = if dirs == Nil then "." else joinBy "/" dirs
let (name, ext) = splitFileName file
putStrLn "\{show dir} \{show name} \{show ext}"
(Right src) <- liftIO {M} $ readFile fn
| Left err => error (MkFC fn (0,0)) "error reading \{fn}: \{show err}"
let (Right toks) = tokenise fn src
| Left err => throwError err
let (Right ((nameFC, modName), _, _)) = partialParse fn parseModHeader EmptyMap toks
| Left (err,toks) => throwError err
let ns = split modName "."
let (path, modName') = unsnoc $ split1 modName "."
-- Any case splits after this point causes it to loop, no idea why
-- let (True) = modName' == name
-- | False => throwError $ E (MkFC fn (0,0)) "module name \{modName'} doesn't match \{name}"
-- let (Right base) = baseDir (Lin <>< dirs) (Lin <>< path)
-- | Left err => pure MkUnit -- exitFailure "ERROR at \{show nameFC}: \{err}"
-- let base = if base == "" then "." else base
-- declare internal primitives
processDecl ("Prim" :: Nil) (PType emptyFC "Int" Nothing)
processDecl ("Prim" :: Nil) (PType emptyFC "String" Nothing)
processDecl ("Prim" :: Nil) (PType emptyFC "Char" Nothing)
let base = "port" -- FIXME
src <- processModule emptyFC base Nil (QN path modName')
top <- get
-- -- dumpContext top
-- (Nil) <- liftIO {M} $ readIORef top.errors
-- | errors => do
-- for_ errors $ \err =>
-- putStrLn (showError src err)
-- exitFailure "Compile failed"
pure MkUnit
cmdLine : List String -> M (Maybe String × List String)
cmdLine Nil = pure (Nothing, Nil)
cmdLine ("--top" :: args) = cmdLine args -- handled later
cmdLine ("-v" :: args) = do
modify (\ top => MkTop top.defs top.metaCtx True top.errors top.loaded top.ops)
cmdLine args
cmdLine ("-o" :: fn :: args) = do
(out, files) <- cmdLine args
pure ((out <|> Just fn), files)
cmdLine (fn :: args) = do
let (True) = isSuffixOf ".newt" fn
| _ => error emptyFC "Bad argument \{show fn}"
(out, files) <- cmdLine args
pure $ (out, fn :: files)
main' : M Unit
main' = do
let (arg0 :: args) = getArgs
| _ => error emptyFC "error reading args"
(out, files) <- cmdLine args
traverse_ processFile files
when (elem "--top" args) $ \ _ => do
json <- jsonTopContext
putStrLn "TOP:\{renderJson json}"
case out of
Nothing => pure MkUnit
Just name => writeSource name
main : IO Unit
main = do
-- we'll need to reset for each file, etc.
ctx <- emptyTop
(Right _) <- .runM main' ctx
| Left err => exitFailure "ERROR at \{show $ getFC err}: \{errorMsg err}"
putStrLn "done"

34
done/Node.newt Normal file
View File

@@ -0,0 +1,34 @@
module Node
import Prelude
pfunc fs : JSObject := `require('fs')`
pfunc getArgs : List String := `arrayToList(String, process.argv.slice(1))`
pfunc readFile uses (fs MkIORes Left Right) : (fn : String) -> IO (Either String String) := `(fn) => (w) => {
let result
try {
let content = fs.readFileSync(fn, 'utf8')
result = Right(undefined, undefined, content)
} catch (e) {
let err = ""+e
result = Left(undefined, undefined, e)
}
return MkIORes(undefined, result, w)
}`
-- I wonder if I should automatically `uses` the constructors in the types
pfunc writeFile uses (fs MkIORes MkUnit) : String String IO (Either String Unit) := `(fn, content) => {
let result
try {
fs.writeFileSync(fn, content, 'utf8')
result = Right(undefined, undefined, MkUnit)
} catch (e) {
let err = ""+e
result = Left(undefined, undefined, e)
}
return MkIORes(undefined, result, w)
}`
-- maybe System.exit or something, like the original putStrLn msg >> exitFailure
pfunc exitFailure : a. String a := `(_, msg) => { throw new Error(msg) }`

26
done/Test/Parser.newt Normal file
View File

@@ -0,0 +1,26 @@
module Test.Parser
import Prelude
import Lib.Parser
import Lib.Tokenizer
import Node
main : IO Unit
main = do
let fn = "port/Lib/Parser.newt"
(Right text) <- readFile fn
| Left msg => putStrLn $ "ERROR: " ++ msg
let (Right toks) = tokenise fn text
| Left (E fc msg) => putStrLn msg
| _ => putStrLn "postpone error"
-- debugLog toks
let (OK a toks com ops) = runP parseMod toks False EmptyMap (MkFC fn (0,0))
| fail => debugLog fail
putStrLn "Module"
debugLog $ a
let (MkModule name imports decls) = a
let lines = map (render 90 pretty) decls
putStrLn $ joinBy "\n" lines

View File

@@ -9,7 +9,7 @@ the _ a = a
const : a b. a b a const : a b. a b a
const a b = a const a b = a
data Unit = MkUnit
data Bool = True | False data Bool = True | False
not : Bool Bool not : Bool Bool
@@ -185,9 +185,16 @@ instance Traversable List where
traverse f Nil = return Nil traverse f Nil = return Nil
traverse f (x :: xs) = return _::_ <*> f x <*> traverse f xs traverse f (x :: xs) = return _::_ <*> f x <*> traverse f xs
traverse_ : t f a b. {{Traversable t}} {{Applicative f}} (a f b) t a f Unit
traverse_ f xs = return (const MkUnit) <*> traverse f xs
for : {t : U U} {f : U U} {{Traversable t}} {{appf : Applicative f}} {a : U} {b : U} t a (a f b) f (t b) for : {t : U U} {f : U U} {{Traversable t}} {{appf : Applicative f}} {a : U} {b : U} t a (a f b) f (t b)
for stuff fun = traverse fun stuff for stuff fun = traverse fun stuff
for_ : {t : U U} {f : U U} {{Traversable t}} {{appf : Applicative f}} {a : U} {b : U} t a (a f b) f Unit
for_ stuff fun = return (const MkUnit) <*> traverse fun stuff
instance Applicative Maybe where instance Applicative Maybe where
return a = Just a return a = Just a
Nothing <*> _ = Nothing Nothing <*> _ = Nothing
@@ -259,7 +266,7 @@ instance Eq String where
instance Eq Char where instance Eq Char where
a == b = jsEq a b a == b = jsEq a b
data Unit = MkUnit
ptype Array : U U ptype Array : U U
pfunc listToArray : {a : U} -> List a -> Array a := ` pfunc listToArray : {a : U} -> List a -> Array a := `
@@ -749,6 +756,10 @@ ordNub {a} {{ordA}} xs = go $ qsort _<_ xs
go (a :: b :: xs) = if a == b then go (a :: xs) else a :: go (b :: xs) go (a :: b :: xs) = if a == b then go (a :: xs) else a :: go (b :: xs)
go t = t go t = t
nub : a. {{Eq a}} List a List a
nub Nil = Nil
nub (x :: xs) = if elem x xs then nub xs else x :: nub xs
ite : a. Bool a a a ite : a. Bool a a a
ite c t e = if c then t else e ite c t e = if c then t else e
@@ -809,6 +820,9 @@ force f = f MkUnit
when : f. {{Applicative f}} Bool Lazy (f Unit) f Unit when : f. {{Applicative f}} Bool Lazy (f Unit) f Unit
when b fa = if b then force fa else return MkUnit when b fa = if b then force fa else return MkUnit
unless : f. {{Applicative f}} Bool Lazy (f Unit) f Unit
unless b fa = when (not b) fa
instance a. {{Ord a}} Ord (List a) where instance a. {{Ord a}} Ord (List a) where
compare Nil Nil = EQ compare Nil Nil = EQ
compare Nil ys = LT compare Nil ys = LT
@@ -838,6 +852,12 @@ isDigit _ = False
isUpper : Char Bool isUpper : Char Bool
isUpper c = let o = ord c in 64 < o && o < 91 isUpper c = let o = ord c in 64 < o && o < 91
isAlphaNum : Char Bool
isAlphaNum c = let o = ord c in
64 < o && o < 91 ||
47 < o && o < 58 ||
96 < o && o < 123
ignore : f a. {{Functor f}} f a f Unit ignore : f a. {{Functor f}} f a f Unit
ignore = map (const MkUnit) ignore = map (const MkUnit)
@@ -849,6 +869,7 @@ instance ∀ a. {{Show a}} → Show (Maybe a) where
-- TODO -- TODO
pfunc isPrefixOf uses (True False): String String Bool := `(pfx, s) => s.startsWith(pfx) ? True : False` pfunc isPrefixOf uses (True False): String String Bool := `(pfx, s) => s.startsWith(pfx) ? True : False`
pfunc isSuffixOf uses (True False): String String Bool := `(pfx, s) => s.endsWith(pfx) ? True : False`
pfunc strIndex : String Int Char := `(s, ix) => s[ix]` pfunc strIndex : String Int Char := `(s, ix) => s[ix]`
@@ -861,3 +882,22 @@ getAt' i xs = getAt (cast i) xs
length' : a. List a Int length' : a. List a Int
length' Nil = 0 length' Nil = 0
length' (x :: xs) = 1 + length' xs length' (x :: xs) = 1 + length' xs
unlines : List String String
unlines lines = joinBy "\n" lines
-- TODO inherit Semigroup
class Monoid a where
neutral : a
findIndex' : a. (a Bool) List a Maybe Int
findIndex' {a} pred xs = go xs 0
where
go : List a Int Maybe Int
go Nil ix = Nothing
go (x :: xs) ix = if pred x then Just ix else go xs (ix + 1)
pfunc fatalError : a. String a := `(_, msg) => { throw new Error(msg) }`
foldlM : m a e. {{Monad m}} (a e m a) a List e m a
foldlM f a xs = foldl (\ ma b => ma >>= flip f b) (pure a) xs

View File

@@ -198,3 +198,6 @@ foldMap f m Nil = m
foldMap f m ((a,b) :: xs) = case lookupMap a m of foldMap f m ((a,b) :: xs) = case lookupMap a m of
Nothing => foldMap f (updateMap a b m) xs Nothing => foldMap f (updateMap a b m) xs
Just (_, b') => foldMap f (updateMap a (f b' b) m) xs Just (_, b') => foldMap f (updateMap a (f b' b) m) xs
listValues : k v. SortedMap k v List v
listValues sm = map snd $ toList sm

View File

@@ -18,7 +18,7 @@ find src -type f -name '*.idr' | while read -r file; do
s/\binterface\b/class/g; s/\binterface\b/class/g;
s/import public/import/g; s/import public/import/g;
s/\[\]/Nil/g; s/\[\]/Nil/g;
s{\[([^<].*?)\]}{"(" . (join " ::", split /,/, $1) . " :: Nil)"}ge; s{\[([^<|][^()]*?)\]}{"(" . (join " ::", split /,/, $1) . " :: Nil)"}ge;
s/\bsym\b/symbol/g; s/\bsym\b/symbol/g;
s/^export//g; s/^export//g;
s/^\s*covering//g; s/^\s*covering//g;

View File

@@ -74,8 +74,8 @@ mkEnv nm k env (x :: xs) = mkEnv nm (S k) (push env (Dot (Var nm) "h\{show k}"))
envNames : Env -> List String envNames : Env -> List String
||| given a name, find a similar one that doesn't shadow in Env ||| given a name, find a similar one that doesn't shadow in Env
fresh : String -> JSEnv -> String freshName : String -> JSEnv -> String
fresh nm env = if free env.env nm then nm else go nm 1 freshName nm env = if free env.env nm then nm else go nm 1
where where
free : List JSExp -> String -> Bool free : List JSExp -> String -> Bool
free [] nm = True free [] nm = True
@@ -85,9 +85,9 @@ fresh nm env = if free env.env nm then nm else go nm 1
go : String -> Nat -> String go : String -> Nat -> String
go nm k = let nm' = "\{nm}\{show k}" in if free env.env nm' then nm' else go nm (S k) go nm k = let nm' = "\{nm}\{show k}" in if free env.env nm' then nm' else go nm (S k)
fresh' : String -> JSEnv -> (String, JSEnv) freshName' : String -> JSEnv -> (String, JSEnv)
fresh' nm env = freshName' nm env =
let nm' = fresh nm env -- "\{nm}$\{show $ length env}" let nm' = freshName nm env -- "\{nm}$\{show $ length env}"
env' = push env (Var nm') env' = push env (Var nm')
in (nm', env') in (nm', env')
@@ -97,7 +97,7 @@ freshNames nms env = go nms env [<]
go : List Name -> JSEnv -> SnocList Name -> (List String, JSEnv) go : List Name -> JSEnv -> SnocList Name -> (List String, JSEnv)
go Nil env acc = (acc <>> Nil, env) go Nil env acc = (acc <>> Nil, env)
go (n :: ns) env acc = go (n :: ns) env acc =
let (n', env') = fresh' n env let (n', env') = freshName' n env
in go ns env' (acc :< n') in go ns env' (acc :< n')
-- This is inspired by A-normalization, look into the continuation monad -- This is inspired by A-normalization, look into the continuation monad
@@ -112,7 +112,7 @@ termToJS env (CBnd k) f = case getAt k env.env of
Nothing => ?bad_bounds Nothing => ?bad_bounds
termToJS env CErased f = f JUndefined termToJS env CErased f = f JUndefined
termToJS env (CLam nm t) f = termToJS env (CLam nm t) f =
let (nm',env') = fresh' nm env -- "\{nm}$\{show $ length env}" let (nm',env') = freshName' nm env -- "\{nm}$\{show $ length env}"
in f $ JLam [nm'] (termToJS env' t JReturn) in f $ JLam [nm'] (termToJS env' t JReturn)
termToJS env (CFun nms t) f = termToJS env (CFun nms t) f =
let (nms', env') = freshNames nms env let (nms', env') = freshNames nms env
@@ -125,14 +125,14 @@ termToJS env (CLet nm (CBnd k) u) f = case getAt k env.env of
Just e => termToJS (push env e) u f Just e => termToJS (push env e) u f
Nothing => ?bad_bounds2 Nothing => ?bad_bounds2
termToJS env (CLet nm t u) f = termToJS env (CLet nm t u) f =
let nm' = fresh nm env let nm' = freshName nm env
env' = push env (Var nm') env' = push env (Var nm')
-- If it's a simple term, use const -- If it's a simple term, use const
in case termToJS env t (JAssign nm') of in case termToJS env t (JAssign nm') of
(JAssign _ exp) => JSnoc (JConst nm' exp) (termToJS env' u f) (JAssign _ exp) => JSnoc (JConst nm' exp) (termToJS env' u f)
t' => JSnoc (JLet nm' t') (termToJS env' u f) t' => JSnoc (JLet nm' t') (termToJS env' u f)
termToJS env (CLetRec nm t u) f = termToJS env (CLetRec nm t u) f =
let nm' = fresh nm env let nm' = freshName nm env
env' = push env (Var nm') env' = push env (Var nm')
-- If it's a simple term, use const -- If it's a simple term, use const
in case termToJS env' t (JAssign nm') of in case termToJS env' t (JAssign nm') of
@@ -144,7 +144,7 @@ termToJS env (CApp t args etas) f = termToJS env t (\ t' => (argsToJS t' args [<
etaExpand : JSEnv -> Nat -> SnocList JSExp -> JSExp -> JSExp etaExpand : JSEnv -> Nat -> SnocList JSExp -> JSExp -> JSExp
etaExpand env Z args tm = Apply tm (args <>> []) etaExpand env Z args tm = Apply tm (args <>> [])
etaExpand env (S etas) args tm = etaExpand env (S etas) args tm =
let nm' = fresh "eta" env let nm' = freshName "eta" env
env' = push env (Var nm') env' = push env (Var nm')
in JLam [nm'] $ JReturn $ etaExpand (push env (Var nm')) etas (args :< Var nm') tm in JLam [nm'] $ JReturn $ etaExpand (push env (Var nm')) etas (args :< Var nm') tm
@@ -164,7 +164,7 @@ termToJS env (CCase t alts) f =
t' => do t' => do
-- TODO refactor nm to be a JSExp with Var{} or Dot{} -- TODO refactor nm to be a JSExp with Var{} or Dot{}
-- FIXME sc$ seemed to shadow something else, lets get this straightened out -- FIXME sc$ seemed to shadow something else, lets get this straightened out
-- we need fresh names that are not in env (i.e. do not play in debruijn) -- we need freshName names that are not in env (i.e. do not play in debruijn)
let nm = "_sc$\{show env.depth}" let nm = "_sc$\{show env.depth}"
let env' = { depth $= S } env let env' = { depth $= S } env
JSnoc (JConst nm t') (maybeCaseStmt env' nm alts) JSnoc (JConst nm t') (maybeCaseStmt env' nm alts)
@@ -184,8 +184,8 @@ termToJS env (CCase t alts) 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))
keywords : List String jsKeywords : List String
keywords = [ jsKeywords = [
"break", "case", "catch", "continue", "debugger", "default", "delete", "do", "else", "break", "case", "catch", "continue", "debugger", "default", "delete", "do", "else",
"finally", "for", "function", "if", "in", "instanceof", "new", "return", "switch", "finally", "for", "function", "if", "in", "instanceof", "new", "return", "switch",
"this", "throw", "try", "typeof", "var", "void", "while", "with", "this", "throw", "try", "typeof", "var", "void", "while", "with",
@@ -199,7 +199,7 @@ keywords = [
||| escape identifiers for js ||| escape identifiers for js
jsIdent : String -> Doc jsIdent : String -> Doc
jsIdent id = if elem id keywords then text ("$" ++ id) else text $ pack $ fix (unpack id) jsIdent id = if elem id jsKeywords then text ("$" ++ id) else text $ pack $ fix (unpack id)
where where
fix : List Char -> List Char fix : List Char -> List Char
fix [] = [] fix [] = []

View File

@@ -320,21 +320,24 @@ invert lvl sp = go sp []
-- we have to "lift" the renaming when we go under a lambda -- we have to "lift" the renaming when we go under a lambda
-- I think that essentially means our domain ix are one bigger, since we're looking at lvl -- I think that essentially means our domain ix are one bigger, since we're looking at lvl
-- in the codomain, so maybe we can just keep that value -- in the codomain, so maybe we can just keep that value
rename : Nat -> List Nat -> Nat -> Val -> M Tm
rename meta ren lvl v = go ren lvl v
where
go : List Nat -> Nat -> Val -> M Tm
goSpine : List Nat -> Nat -> Tm -> SnocList Val -> M Tm
goSpine ren lvl tm [<] = pure tm
goSpine ren lvl tm (xs :< x) = do
xtm <- go ren lvl x
pure $ App emptyFC !(goSpine ren lvl tm xs) xtm
go ren lvl (VVar fc k sp) = case findIndex (== k) ren of
rename : Nat -> List Nat -> Nat -> Val -> M Tm
renameSpine : Nat -> List Nat -> Nat -> Tm -> SnocList Val -> M Tm
renameSpine meta ren lvl tm [<] = pure tm
renameSpine meta ren lvl tm (xs :< x) = do
xtm <- rename meta ren lvl x
pure $ App emptyFC !(renameSpine meta ren lvl tm xs) xtm
rename meta ren lvl (VVar fc k sp) = case findIndex (== k) ren of
Nothing => error fc "scope/skolem thinger VVar \{show k} ren \{show ren}" Nothing => error fc "scope/skolem thinger VVar \{show k} ren \{show ren}"
Just x => goSpine ren lvl (Bnd fc $ cast x) sp Just x => renameSpine meta ren lvl (Bnd fc $ cast x) sp
go ren lvl (VRef fc nm def sp) = goSpine ren lvl (Ref fc nm def) sp rename meta ren lvl (VRef fc nm def sp) = renameSpine meta ren lvl (Ref fc nm def) sp
go ren lvl (VMeta fc ix sp) = do rename meta ren lvl (VMeta fc ix sp) = do
-- So sometimes we have an unsolved meta in here which reference vars out of scope. -- So sometimes we have an unsolved meta in here which reference vars out of scope.
debug "rename Meta \{show ix} spine \{show sp}" debug "rename Meta \{show ix} spine \{show sp}"
if ix == meta if ix == meta
@@ -343,22 +346,22 @@ rename meta ren lvl v = go ren lvl v
else case !(lookupMeta ix) of else case !(lookupMeta ix) of
Solved fc _ val => do Solved fc _ val => do
debug "rename: \{show ix} is solved" debug "rename: \{show ix} is solved"
go ren lvl !(vappSpine val sp) rename meta ren lvl !(vappSpine val sp)
_ => do _ => do
debug "rename: \{show ix} is unsolved" debug "rename: \{show ix} is unsolved"
catchError (goSpine ren lvl (Meta fc ix) sp) (\err => throwError $ Postpone fc ix (errorMsg err)) catchError (renameSpine meta ren lvl (Meta fc ix) sp) (\err => throwError $ Postpone fc ix (errorMsg err))
go ren lvl (VLam fc n icit rig t) = pure (Lam fc n icit rig !(go (lvl :: ren) (S lvl) !(t $$ VVar fc lvl [<]))) rename meta ren lvl (VLam fc n icit rig t) = pure (Lam fc n icit rig !(rename meta (lvl :: ren) (S lvl) !(t $$ VVar fc lvl [<])))
go ren lvl (VPi fc n icit rig ty tm) = pure (Pi fc n icit rig !(go ren lvl ty) !(go (lvl :: ren) (S lvl) !(tm $$ VVar emptyFC lvl [<]))) rename meta ren lvl (VPi fc n icit rig ty tm) = pure (Pi fc n icit rig !(rename meta ren lvl ty) !(rename meta (lvl :: ren) (S lvl) !(tm $$ VVar emptyFC lvl [<])))
go ren lvl (VU fc) = pure (UU fc) rename meta ren lvl (VU fc) = pure (UU fc)
go ren lvl (VErased fc) = pure (Erased fc) rename meta ren lvl (VErased fc) = pure (Erased fc)
-- for now, we don't do solutions with case in them. -- for now, we don't do solutions with case in them.
go ren lvl (VCase fc sc alts) = error fc "Case in solution" rename meta ren lvl (VCase fc sc alts) = error fc "Case in solution"
go ren lvl (VLit fc lit) = pure (Lit fc lit) rename meta ren lvl (VLit fc lit) = pure (Lit fc lit)
go ren lvl (VLet fc name val body) = rename meta ren lvl (VLet fc name val body) =
pure $ Let fc name !(go ren lvl val) !(go (lvl :: ren) (S lvl) body) pure $ Let fc name !(rename meta ren lvl val) !(rename meta (lvl :: ren) (S lvl) body)
-- these probably shouldn't show up in solutions... -- these probably shouldn't show up in solutions...
go ren lvl (VLetRec fc name ty val body) = rename meta ren lvl (VLetRec fc name ty val body) =
pure $ LetRec fc name !(go ren lvl ty) !(go (lvl :: ren) (S lvl) val) !(go (lvl :: ren) (S lvl) body) pure $ LetRec fc name !(rename meta ren lvl ty) !(rename meta (lvl :: ren) (S lvl) val) !(rename meta (lvl :: ren) (S lvl) body)
lams : Nat -> List String -> Tm -> Tm lams : Nat -> List String -> Tm -> Tm
lams 0 _ tm = tm lams 0 _ tm = tm
@@ -564,7 +567,7 @@ unifyCatch fc ctx ty' ty = do
a <- quote ctx.lvl ty' a <- quote ctx.lvl ty'
b <- quote ctx.lvl ty b <- quote ctx.lvl ty
let names = toList $ map fst ctx.types let names = toList $ map fst ctx.types
let msg = "unification failure\n failed to unify \{pprint names a}\n with \{pprint names b}" let msg = "xxunification failure\n failed to unify \{pprint names a}\n with \{pprint names b}"
let msg = msg ++ "\nconstraints \{show cs.constraints}" let msg = msg ++ "\nconstraints \{show cs.constraints}"
throwError (E fc msg) throwError (E fc msg)
-- error fc "Unification yields constraints \{show cs.constraints}" -- error fc "Unification yields constraints \{show cs.constraints}"

View File

@@ -79,6 +79,7 @@ tryEval env (VRef fc k _ sp) = do
val <- vappSpine vtm sp val <- vappSpine vtm sp
case val of case val of
VCase _ _ _ => pure Nothing VCase _ _ _ => pure Nothing
VLetRec _ _ _ _ _ => pure Nothing
v => pure $ Just v) v => pure $ Just v)
(\ _ => pure Nothing) (\ _ => pure Nothing)
_ => pure Nothing _ => pure Nothing

View File

@@ -83,7 +83,7 @@ getSigs : List Decl -> List (FC, String, Raw)
getSigs [] = [] getSigs [] = []
getSigs ((TypeSig _ [] _) :: xs) = getSigs xs getSigs ((TypeSig _ [] _) :: xs) = getSigs xs
getSigs ((TypeSig fc (nm :: nms) ty) :: xs) = (fc, nm, ty) :: getSigs xs getSigs ((TypeSig fc (nm :: nms) ty) :: xs) = (fc, nm, ty) :: getSigs xs
getSigs (_:: xs) = getSigs xs getSigs (_ :: xs) = getSigs xs
teleToPi : Telescope -> Raw -> Raw teleToPi : Telescope -> Raw -> Raw
teleToPi [] end = end teleToPi [] end = end
@@ -272,8 +272,8 @@ processDecl ns (Instance instfc ty decls) = do
conTele <- getFields !(apply vdcty args') env [] conTele <- getFields !(apply vdcty args') env []
-- declare individual functions, collect their defs -- declare individual functions, collect their defs
defs <- for conTele $ \case defs <- for conTele $ \case
(MkBind fc nm Explicit rig ty) => do (MkBinder fc nm Explicit rig ty) => do
let ty' = foldr (\(MkBind fc nm' icit rig ty'), acc => Pi fc nm' icit rig ty' acc) ty tele let ty' = foldr (\(MkBinder fc nm' icit rig ty'), acc => Pi fc nm' icit rig ty' acc) ty tele
let nm' = "\{instname},\{nm}" let nm' = "\{instname},\{nm}"
-- we're working with a Tm, so we define directly instead of processDecl -- 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 let Just (Def fc name xs) = find (\case (Def y name xs) => name == nm; _ => False) decls
@@ -302,7 +302,7 @@ processDecl ns (Instance instfc ty decls) = do
-- We're assuming they don't depend on each other. -- We're assuming they don't depend on each other.
getFields : Val -> Env -> List Binder -> M (List Binder) getFields : Val -> Env -> List Binder -> M (List Binder)
getFields tm@(VPi fc nm Explicit rig ty sc) env bnds = do getFields tm@(VPi fc nm Explicit rig ty sc) env bnds = do
bnd <- MkBind fc nm Explicit rig <$> quote (length env) ty bnd <- MkBinder fc nm Explicit rig <$> quote (length env) ty
getFields !(sc $$ VVar fc (length env) [<]) env (bnd :: bnds) 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@(VPi fc nm _ rig ty sc) env bnds = getFields !(sc $$ VVar fc (length env) [<]) env bnds
getFields tm xs bnds = pure $ reverse bnds getFields tm xs bnds = pure $ reverse bnds
@@ -312,7 +312,7 @@ processDecl ns (Instance instfc ty decls) = do
tenv (S k) = (VVar emptyFC k [<] :: tenv k) tenv (S k) = (VVar emptyFC k [<] :: tenv k)
mkRHS : String -> List Binder -> Raw -> Raw mkRHS : String -> List Binder -> Raw -> Raw
mkRHS instName (MkBind fc nm Explicit rig ty :: bs) tm = mkRHS instName bs (RApp fc tm (RVar fc "\{instName},\{nm}") Explicit) 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 (b :: bs) tm = mkRHS instName bs tm
mkRHS instName [] tm = tm mkRHS instName [] tm = tm
@@ -364,7 +364,7 @@ processDecl ns (Data fc nm ty cons) = do
-- We know it's in U because it's part of a checked Pi type -- We know it's in U because it's part of a checked Pi type
let (codomain, tele) = splitTele dty let (codomain, tele) = splitTele dty
-- for printing -- for printing
let tnames = reverse $ map (\(MkBind _ nm _ _ _) => nm) tele let tnames = reverse $ map (\(MkBinder _ nm _ _ _) => nm) tele
let (Ref _ hn _, args) := funArgs codomain let (Ref _ hn _, args) := funArgs codomain
| (tm, _) => error (getFC tm) "expected \{nm} got \{pprint tnames tm}" | (tm, _) => error (getFC tm) "expected \{nm} got \{pprint tnames tm}"
when (hn /= QN ns nm) $ when (hn /= QN ns nm) $

View File

@@ -12,17 +12,17 @@ funArgs tm = go tm []
public export public export
data Binder : Type where data Binder : Type where
MkBind : FC -> String -> Icit -> Quant -> Tm -> Binder MkBinder : FC -> String -> Icit -> Quant -> Tm -> Binder
-- I don't have a show for terms without a name list -- I don't have a show for terms without a name list
export export
Show Binder where Show Binder where
show (MkBind _ nm icit quant t) = "[\{show quant}\{nm} \{show icit} : ...]" show (MkBinder _ nm icit quant t) = "[\{show quant}\{nm} \{show icit} : ...]"
export export
splitTele : Tm -> (Tm, List Binder) splitTele : Tm -> (Tm, List Binder)
splitTele = go [] splitTele = go []
where where
go : List Binder -> Tm -> (Tm, List Binder) go : List Binder -> Tm -> (Tm, List Binder)
go ts (Pi fc nm icit quant t u) = go (MkBind fc nm icit quant t :: ts) u go ts (Pi fc nm icit quant t u) = go (MkBinder fc nm icit quant t :: ts) u
go ts tm = (tm, reverse ts) go ts tm = (tm, reverse ts)