···5353 JReturn : JSExp -> JSStmt Return
5454 JLet : (nm : String) -> JSStmt (Assign nm) -> JSStmt Plain -- need somebody to assign
5555 JAssign : (nm : String) -> JSExp -> JSStmt (Assign nm)
5656- -- TODO - switch to Int tags
5756 JCase : ∀ a. JSExp -> List JAlt -> JSStmt a
5857 JIfThen : ∀ a. JSExp -> JSStmt a -> JSStmt a -> JSStmt a
5958 -- throw can't be used
6059 JError : ∀ a. String -> JSStmt a
6060+ -- FIXME We're routing around the index here
6161+ -- Might be able to keep the index if
6262+ -- we add `Loop : List String -> StKind`
6363+ -- JLoopAssign peels one off
6464+ -- JContinue is a Loop Nil
6565+ -- And LoopReturn
6666+ JWhile : ∀ a. JSStmt a → JSStmt a
6767+ JLoopAssign : (nm : String) → JSExp → JSStmt Plain
6868+ JContinue : ∀ a. JSStmt a
61696270Cont : StKind → U
6371Cont e = JSExp -> JSStmt e
···109117 env' = push env (Var nm')
110118 in (nm', env')
111119120120+-- get list of arg names and an environment with either references or undefined
121121+-- depending on quantity
112122freshNames : List (Quant × String) -> JSEnv -> (List String × JSEnv)
113123freshNames nms env = go nms env Lin
114124 where
···132142simpleJSExp (LitBool _) = True
133143simpleJSExp _ = False
134144145145+getEnv : Int → List JSExp → JSExp
146146+getEnv ix env = case getAt' ix env of
147147+ Just e => e
148148+ Nothing => fatalError "Bad bounds \{show ix}"
149149+135150-- This is inspired by A-normalization, look into the continuation monad
136151-- There is an index on JSStmt, adopted from Stefan Hoeck's code.
137152--
···139154-- is a continuation, which turns the final JSExpr into a JSStmt, and the function returns
140155-- a JSStmt, wrapping recursive calls in JSnoc if necessary.
141156termToJS : ∀ e. JSEnv -> CExp -> Cont e -> JSStmt e
142142-termToJS env (CBnd k) f = case getAt (cast k) env.jsenv of
143143- (Just e) => f e
144144- Nothing => fatalError "Bad bounds"
157157+termToJS env (CBnd k) f = f $ getEnv k env.jsenv
145158termToJS env CErased f = f JUndefined
146159termToJS env (CRaw str _) f = f (Raw str)
147160termToJS env (CLam nm t) f =
···155168termToJS env (CMeta k) f = f $ LitString "META \{show k}"
156169termToJS env (CLit lit) f = f (litToJS lit)
157170-- if it's a var, just use the original
158158-termToJS env (CLet nm (CBnd k) u) f = case getAt (cast k) env.jsenv of
159159- Just e => termToJS (push env e) u f
160160- Nothing => fatalError "bad bounds"
171171+termToJS env (CLet nm (CBnd k) u) f = termToJS (push env $ getEnv k env.jsenv) u f
161172-- For a let, we run with a continuation to JAssign to a pre-declared variable
162173-- if JAssign comes back out, we either push the JSExpr into the environment or JConst it,
163174-- depending on complexity. Otherwise, stick the declaration in front.
···169180 then termToJS (push env exp) u f
170181 else JSnoc (JConst nm' exp) (termToJS env' u f)
171182 t' => JSnoc (JLet nm' t') (termToJS env' u f)
183183+termToJS env (CLetLoop args body) f =
184184+ let off = length' args in
185185+ -- Add lets for the args, we put this in a while and
186186+ -- mutate the args, then continue for the self-call
187187+ let (lets, env') = go (length' args - 1) args env Lin in
188188+ JWhile $ foldr (\a b => JSnoc a b) (termToJS env' body f) lets
189189+ where
190190+ go : Int → List (Quant × String) -> JSEnv -> SnocList (JSStmt Plain) -> (List (JSStmt Plain) × JSEnv)
191191+ go off Nil env acc = (acc <>> Nil, env)
192192+ go off ((Many, n) :: ns) env acc =
193193+ let (n', env') = freshName' n env
194194+ in go off ns env' (acc :< JConst n' (getEnv off env.jsenv))
195195+ go off ((Zero, n) :: ns) env acc =
196196+ let env' = push env JUndefined
197197+ in go off ns env' acc
198198+172199termToJS env (CLetRec nm CErased u) f = termToJS (push env JUndefined) u f
173200termToJS env (CLetRec nm t u) f =
174201 -- this shouldn't happen if where is lifted
···184211 go (t :: ts) (Many :: qs) ix k = termToJS env t $ \ t' => go ts qs (ix + 1) $ \ args => k $ ("h\{show ix}", t') :: args
185212 go (t :: ts) (q :: qs) ix k = go ts qs (ix + 1) $ \ args => k args
186213 go _ _ ix k = k Nil
214214+termToJS {e} env (CLoop args quants) f = runArgs (reverse env.jsenv) args quants
215215+ where
216216+ -- Here we drop the continuation. It _should_ be a JReturn wrapper, because of how we insert JLoop.
217217+ -- But we're not statically checking that.
218218+ runArgs : List JSExp → List CExp → List Quant → JSStmt e
219219+ runArgs _ Nil Nil = JContinue
220220+ runArgs _ Nil _ = fatalError "too few CExp"
221221+ runArgs (Var x :: rest) (arg :: args) (Many :: qs) =
222222+ termToJS env arg $ \ arg' => JSnoc (JLoopAssign x arg') $ runArgs rest args qs
223223+ -- TODO check arg erased
224224+ runArgs (JUndefined :: rest) (arg :: args) (q :: qs) = runArgs rest args qs
225225+ runArgs (wat :: rest) (arg :: args) (q :: qs) = fatalError "bad env for quant \{show q}"
226226+ runArgs a b c = fatalError "FALLBACK \{show $ length' a} \{show $ length' b} \{show $ length' c}"
187227termToJS env (CAppRef nm args quants) f = termToJS env (CRef nm) (\ t' => (argsToJS env t' args quants Lin f))
188228 where
189229 etaExpand : JSEnv -> List Quant -> SnocList JSExp -> JSExp -> JSExp
···329369-- I might not need these split yet.
330370stmtToDoc (JLet nm body) = text "let" <+> jsIdent nm ++ text ";" </> stmtToDoc body
331371stmtToDoc (JAssign nm expr) = jsIdent nm <+> text "=" <+> expToDoc expr ++ text ";"
332332-stmtToDoc (JConst nm x) = text "const" <+> jsIdent nm <+> nest 2 (text "=" <+/> expToDoc x ++ text ";")
372372+stmtToDoc (JLoopAssign nm expr) = jsIdent nm <+> text "=" <+> expToDoc expr ++ text ";"
373373+stmtToDoc (JContinue) = text "continue" ++ text ";"
374374+stmtToDoc (JWhile stmt) = text "while (1)" <+> bracket "{" (stmtToDoc stmt) "}"
375375+-- In the loop case, this may be reassigned
376376+stmtToDoc (JConst nm x) = text "let" <+> jsIdent nm <+> nest 2 (text "=" <+/> expToDoc x ++ text ";")
333377stmtToDoc (JReturn x) = text "return" <+> expToDoc x ++ text ";"
334378stmtToDoc (JError str) = text "throw new Error(" ++ text (quoteString str) ++ text ");"
335379stmtToDoc (JIfThen sc t e) =
···431475 getNames : (deep : Bool) → List (Bool × QName) → CExp → List (Bool × QName)
432476 -- liftIO calls a lambda statically
433477 getNames deep acc (CLam _ t) = getNames deep acc t
478478+ getNames deep acc (CLetLoop _ t) = getNames deep acc t
434479 -- top level 0-ary function, doesn't happen
435480 getNames deep acc (CFun _ t) = if deep then getNames deep acc t else acc
436436-481481+ -- REVIEW - True or deep?
482482+ getNames deep acc (CLoop args qs) = foldl (getNames True) acc args
437483 getNames deep acc (CAppRef nm args qs) =
438484 if length' args == length' qs
439485 then case args of
+5
src/Lib/CompileExp.newt
···3636 CLit : Literal -> CExp
3737 CLet : Name -> CExp -> CExp -> CExp
3838 CLetRec : Name -> CExp -> CExp -> CExp
3939+ -- Might be able to use a bunch of flagged lets or something
4040+ CLetLoop : List (Quant × Name) → CExp → CExp
4141+ -- This is like a CAppRef, self-call
4242+ -- If we know it's a tail call fn, we could handle all of this in codegen...
4343+ CLoop : List CExp → List Quant → CExp
3944 CErased : CExp
4045 -- Data / type constructor
4146 CConstr : Nat → Name → List CExp → List Quant → CExp
+30-1
src/Lib/TCO.newt
···2020-- Find names of applications in tail position
2121tailNames : CExp → List QName
2222tailNames (CAppRef nm args n) = nm :: Nil
2323+-- these two shouldn't exist yet
2424+tailNames (CLoop _ _) = Nil
2525+tailNames (CLetLoop _ _) = Nil
2326tailNames (CCase _ alts) = join $ map altTailNames alts
2427 where
2528 altTailNames : CAlt → List QName
···4043tailNames (CRaw _ _) = Nil
4144tailNames (CPrimOp _ _ _) = Nil
42454343--- rewrite tail calls to return an object
4646+-- rewrite tail calls to return an object to a trampoline
4747+-- takes a list of the names in the group and the function body
4448rewriteTailCalls : List QName → CExp → CExp
4549rewriteTailCalls nms tm = case tm of
4650 CAppRef nm args qs =>
···6367 rewriteAlt (CDefAlt t) = CDefAlt $ rewriteTailCalls nms t
6468 rewriteAlt (CLitAlt lit t) = CLitAlt lit $ rewriteTailCalls nms t
65697070+-- A looping version of TCO, specialized for single function calls
7171+-- takes a list of the name of the function and the function body
7272+rewriteLoop : QName → CExp → CExp
7373+rewriteLoop qn tm = case tm of
7474+ (CAppRef nm args qs) =>
7575+ if length' args == length' qs && nm == qn
7676+ then CLoop args qs
7777+ else tm
7878+ (CLetRec nm t u) => CLetRec nm t $ rewriteLoop qn u
7979+ (CLet nm t u) => CLet nm t $ rewriteLoop qn u
8080+ (CCase sc alts) => CCase sc $ map rewriteAlt alts
8181+ tm => tm
8282+ where
8383+ rewriteAlt : CAlt → CAlt
8484+ rewriteAlt (CConAlt ix nm info args t) = CConAlt ix nm info args $ rewriteLoop qn t
8585+ rewriteAlt (CDefAlt t) = CDefAlt $ rewriteLoop qn t
8686+ rewriteAlt (CLitAlt lit t) = CLitAlt lit $ rewriteLoop qn t
8787+6688-- the name of our trampoline
6789bouncer : QName
6890bouncer = QN "" "bouncer"
69917092doOptimize : List (QName × CExp) → M (List (QName × CExp))
9393+doOptimize ((qn, exp) :: Nil) = do
9494+ let (CFun args body) = exp | _ => error emptyFC "doOptimize \{show qn} not a CFun"
9595+ let body = rewriteLoop qn body
9696+ pure $ (qn, CFun args (CLetLoop args body)) :: Nil
9797+7198doOptimize fns = do
7299 splitFuns <- traverse splitFun fns
73100 let nms = map fst fns
···112139113140 processGroup : ExpMap → List QName → M ExpMap
114141 processGroup expMap names = do
142142+ -- Looks like only two are > 1
143143+ debug $ \ _ => "compile.tco: group \{show $ length' names} \{show names}"
115144 let pairs = mapMaybe (flip lookupMap expMap) names
116145 updates <- doOptimize pairs
117146 pure $ foldl doUpdate expMap updates
+3-5
vim/syntax/newt.vim
···11syn keyword newtInfix infix infixl infixr
22-syn keyword newtKW data where let in case of
33-syn keyword newtLet let in
22+syn keyword newtKW data where let in case of derive module import
43syn match newtIdentifier "[a-zA-Z][a-zA-Z]*" contained
55-syn keyword newtStructure data import module where
64syn region newtBlockComment start="/-" end="-/" contained
75syn match newtLineComment "--.*$" contains=@Spell
86···119highlight def link newtInfix PreProc
1210highlight def link newtBlockComment Comment
1311highlight def link newtLineComment Comment
1414-highlight def link newtLet Structure
1515-highlight def link newtStructure Structure
1212+highlight def link newtStructure Keyword
1313+highlight def link newtKW Keyword
16141715let b:current_syntax = "newt"