Skip to content

Commit 15b8925

Browse files
committed
Map Bool-shaped things to javascript bool, add if/then and tertiary to code gen
1 parent e45d194 commit 15b8925

File tree

9 files changed

+68
-15
lines changed

9 files changed

+68
-15
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ newt3.js: newt2.js
2121
time $(RUNJS) newt2.js src/Main.newt -o newt3.js
2222
cmp newt2.js newt3.js
2323

24+
min.js: newt3.js
25+
scripts/pack
26+
2427
test: newt.js
2528
scripts/test
2629

TODO.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
## TODO
33

44
- [x] Take the parens off of FC to make vscode happy
5+
- [x] Magic to make Bool a boolean
6+
- [ ] Look into using holes for errors (https://types.pl/@AndrasKovacs/115401455046442009)
7+
- This would let us hit more cases in a function when we hit an error.
8+
- I've been wanting to try holes for parse errors too.
59
- [ ] in-scope type at point in vscode
610
- So the idea here is that the references will be via FC, we remember the type at declaration and then point the usage back to the declaration (FC -> FC). We could dump all of this. (If we're still doing json.)
711
- Do we want to (maybe later) keep the scope as a FC? We could do scope at point then.
@@ -29,7 +33,7 @@
2933
- [ ] Add `export` keywords
3034
- [ ] vscode - run newt when switching editors
3135
- [ ] who calls X? We can only do this scoped to the current context for now. Someday whole source dir. #lsp
32-
- [ ] Magic to make Bool a boolean
36+
q
3337
- [ ] case split
3438
- We could fake this up:
3539
- given a name and a point in the editor

src/Lib/Compile.newt

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,16 @@ data JAlt : U where
3131
JLitAlt : ∀ e. JSExp -> JSStmt e -> JAlt
3232

3333
data JSExp : U where
34-
LitArray : List JSExp -> JSExp
3534
LitObject : List (String × JSExp) -> JSExp
3635
LitString : String -> JSExp
36+
LitBool : Bool -> JSExp
3737
LitInt : Int -> JSExp
3838
Apply : JSExp -> List JSExp -> JSExp
3939
Var : String -> JSExp
4040
JLam : List String -> JSStmt Return -> JSExp
4141
JPrimOp : String → JSExp → JSExp → JSExp
4242
JUndefined : JSExp
43+
JTernary : JSExp → JSExp → JSExp → JSExp
4344
Index : JSExp -> JSExp -> JSExp
4445
Dot : JSExp -> String -> JSExp
4546
Raw : String -> JSExp
@@ -54,6 +55,7 @@ data JSStmt : StKind -> U where
5455
JAssign : (nm : String) -> JSExp -> JSStmt (Assign nm)
5556
-- TODO - switch to Int tags
5657
JCase : ∀ a. JSExp -> List JAlt -> JSStmt a
58+
JIfThen : ∀ a. JSExp -> JSStmt a -> JSStmt a -> JSStmt a
5759
-- throw can't be used
5860
JError : ∀ a. String -> JSStmt a
5961

@@ -80,6 +82,7 @@ emptyJSEnv = MkEnv Nil 0
8082

8183
litToJS : Literal -> JSExp
8284
litToJS (LString str) = LitString str
85+
litToJS (LBool b) = LitBool b
8386
litToJS (LChar c) = LitString $ pack (c :: Nil)
8487
litToJS (LInt i) = LitInt i
8588

@@ -88,8 +91,6 @@ mkEnv : JSExp -> Int -> JSEnv -> List String -> JSEnv
8891
mkEnv nm k env Nil = env
8992
mkEnv nm k env (x :: xs) = mkEnv nm (1 + k) (push env (Dot nm "h\{show k}")) xs
9093

91-
envNames : Env -> List String
92-
9394
-- given a name, find a similar one that doesn't shadow in Env
9495
freshName : String -> JSEnv -> String
9596
freshName nm env = if free env.jsenv nm then nm else go nm 1
@@ -195,8 +196,11 @@ termToJS env (CApp t arg) f = termToJS env t (\ t' => termToJS env arg (\arg' =>
195196

196197
termToJS {e} env (CCase t alts) f =
197198
termToJS env t $ \case
198-
(Var nm) => maybeCaseStmt env (Var nm) alts
199+
(Var nm) => do
200+
let (Nothing) = jsITE (Var nm) alts f | Just rval => rval
201+
maybeCaseStmt env (Var nm) alts
199202
t' => do
203+
let (Nothing) = jsITE t' alts f | Just rval => rval
200204
-- TODO with inlining, we hit cases where the let gets pulled forward more than once
201205
-- two cases as separate args, se we need actual unique names. For now, we're calling
202206
-- incr when processing App, as a stopgap, we probably need a fresh names state monad
@@ -207,6 +211,18 @@ termToJS {e} env (CCase t alts) f =
207211
then (maybeCaseStmt env' t' alts)
208212
else JSnoc (JConst nm t') (maybeCaseStmt env' (Var nm) alts)
209213
where
214+
tertiary : JSExp → JSStmt e → JSStmt e → Cont e → JSStmt e
215+
tertiary sc (JReturn t) (JReturn f) k = JReturn $ JTernary sc t f
216+
tertiary sc (JAssign nm t) (JAssign _ f) k = JAssign nm $ JTernary sc t f
217+
tertiary sc t f k = JIfThen sc t f
218+
219+
jsITE : JSExp → List CAlt → Cont e → Maybe (JSStmt e)
220+
jsITE sc (CLitAlt (LBool b) rhs :: alt :: Nil) f =
221+
let t = termToJS env rhs f
222+
e = termToJS env (getBody alt) f
223+
in Just $ if b then tertiary sc t e f else tertiary sc e t f
224+
jsITE sc alts f = Nothing
225+
210226
termToJSAlt : JSEnv -> JSExp -> CAlt -> JAlt
211227
termToJSAlt env nm (CConAlt ix name info args u) = JConAlt ix (termToJS (mkEnv nm 0 env args) u f)
212228
-- intentionally reusing scrutinee name here
@@ -253,7 +269,8 @@ jsIdent id = if elem id jsKeywords then text ("$" ++ id) else text $ pack $ fix
253269
stmtToDoc : ∀ e. JSStmt e -> Doc
254270

255271
expToDoc : JSExp -> Doc
256-
expToDoc (LitArray xs) = fatalError "TODO - LitArray to doc"
272+
expToDoc (LitBool b) = if b then text "true" else text "false"
273+
expToDoc (JTernary sc t f) = bracket "(" (expToDoc sc <+> text "?" <+> expToDoc t <+> text ":" <+> expToDoc f )")"
257274
expToDoc (LitObject xs) = text "{" <+> folddoc (\ a e => a ++ text ", " <+/> e) (map entry xs) <+> text "}"
258275
where
259276
entry : (String × JSExp) -> Doc
@@ -291,6 +308,10 @@ stmtToDoc (JAssign nm expr) = jsIdent nm <+> text "=" <+> expToDoc expr ++ text
291308
stmtToDoc (JConst nm x) = text "const" <+> jsIdent nm <+> nest 2 (text "=" <+/> expToDoc x ++ text ";")
292309
stmtToDoc (JReturn x) = text "return" <+> expToDoc x ++ text ";"
293310
stmtToDoc (JError str) = text "throw new Error(" ++ text (quoteString str) ++ text ");"
311+
stmtToDoc (JIfThen sc t e) =
312+
text "if (" ++ expToDoc sc ++ text ")"
313+
<+> bracket "{" (stmtToDoc t) "}"
314+
<+> text "else" <+> bracket "{" (stmtToDoc e) "}"
294315
stmtToDoc (JCase sc alts) =
295316
text "switch (" ++ expToDoc sc ++ text ")" <+> bracket "{" (stack $ map altToDoc alts) "}"
296317

src/Lib/CompileExp.newt

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,17 @@ lamArity : Tm -> Nat
5252
lamArity (Lam _ _ _ _ t) = S (lamArity t)
5353
lamArity _ = Z
5454

55+
-- It would be nice to be able to declare these
5556
compilePrimOp : String → List CExp → Maybe CExp
5657
compilePrimOp "Prelude.addString" (x :: y :: Nil) = Just (CPrimOp "+" x y)
5758
compilePrimOp "Prelude.addInt" (x :: y :: Nil) = Just (CPrimOp "+" x y)
5859
compilePrimOp "Prelude.mulInt" (x :: y :: Nil) = Just (CPrimOp "*" x y)
5960
compilePrimOp "Prelude.subInt" (x :: y :: Nil) = Just (CPrimOp "-" x y)
61+
compilePrimOp "Prelude._&&_" (x :: y :: Nil) = Just (CPrimOp "&&" x y)
62+
compilePrimOp "Prelude._||_" (x :: y :: Nil) = Just (CPrimOp "||" x y)
63+
-- Assumes Bool is in the right order!
64+
compilePrimOp "Prelude.jsEq" (_ :: x :: y :: Nil) = Just (CPrimOp "==" x y)
65+
compilePrimOp "Prelude.jsLt" (_ :: x :: y :: Nil) = Just (CPrimOp "<" x y)
6066
compilePrimOp "Prelude.divInt" (x :: y :: Nil) = Just (CPrimOp "|" (CPrimOp "/" x y) (CLit $ LInt 0))
6167
compilePrimOp _ _ = Nothing
6268

@@ -104,6 +110,11 @@ lookupDef fc nm = do
104110
Nothing => error fc "\{show nm} not in scope"
105111
Just def => pure def
106112

113+
getBody : CAlt → CExp
114+
getBody (CConAlt _ _ _ _ t) = t
115+
getBody (CLitAlt _ t) = t
116+
getBody (CDefAlt t) = t
117+
107118
compileTerm : {{Ref2 Defs St}} → Tm -> M CExp
108119
compileTerm (Bnd _ k) = pure $ CBnd k
109120
-- need to eta expand to arity
@@ -116,6 +127,8 @@ compileTerm t@(Ref fc nm@(QN _ tag)) = do
116127
Z =>
117128
case the (Maybe Def) $ lookupMap' nm defs of
118129
Just (DCon ix EnumCon _ _) => pure $ CLit $ LInt $ cast ix
130+
Just (DCon ix FalseCon _ _) => pure $ CLit $ LBool False
131+
Just (DCon ix TrueCon _ _) => pure $ CLit $ LBool True
119132
Just (DCon _ ZeroCon _ _) => pure $ CLit $ LInt 0
120133
Just (DCon _ SuccCon _ _) =>
121134
pure $ CLam "x" $ CPrimOp "+" (CLit $ LInt 1) (CBnd 0)
@@ -176,6 +189,8 @@ compileTerm (Case fc t alts) = do
176189

177190
enumAlt : CAlt → CAlt
178191
enumAlt (CConAlt ix nm EnumCon args tm) = CLitAlt (LInt $ cast ix) tm
192+
enumAlt (CConAlt ix nm FalseCon args tm) = CLitAlt (LBool False) tm
193+
enumAlt (CConAlt ix nm TrueCon args tm) = CLitAlt (LBool True) tm
179194
enumAlt alt = alt
180195

181196
isInfo : ConInfo → CAlt → Bool
@@ -186,10 +201,6 @@ compileTerm (Case fc t alts) = do
186201
isDef (CDefAlt _) = True
187202
isDef _ = False
188203

189-
getBody : CAlt → CExp
190-
getBody (CConAlt _ _ _ _ t) = t
191-
getBody (CLitAlt _ t) = t
192-
getBody (CDefAlt t) = t
193204

194205
doNumCon : CExp → List CAlt → List CAlt
195206
doNumCon sc alts =
@@ -237,6 +248,8 @@ compileFun tm = go tm Lin
237248
-- What are the Defs used for above? (Arity for name)
238249
compileDCon : Nat → QName → ConInfo → Int → CExp
239250
compileDCon ix (QN _ nm) EnumCon 0 = CLit $ LInt $ cast ix
251+
compileDCon ix (QN _ nm) TrueCon 0 = CLit $ LBool True
252+
compileDCon ix (QN _ nm) FalseCon 0 = CLit $ LBool False
240253
compileDCon ix (QN _ nm) info 0 = CConstr ix nm Nil
241254
compileDCon ix (QN _ nm) info arity =
242255
let args = map (\k => "h\{show k}") (range 0 arity) in

src/Lib/Elab.newt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1189,15 +1189,17 @@ buildLitCases ctx prob fc scnm scty = do
11891189
-- TODO - figure out if these need to be in Prelude or have a special namespace
11901190
-- If we lookupRaw "String", we could get different answers in different contexts.
11911191
-- maybe Hardwire this one
1192-
stringType intType charType : QName
1192+
stringType intType charType boolType : QName
11931193
stringType = QN primNS "String"
11941194
intType = QN primNS "Int"
11951195
charType = QN primNS "Char"
1196+
boolType = QN primNS "Bool"
11961197

11971198
litTyName : Literal -> QName
11981199
litTyName (LString str) = stringType
11991200
litTyName (LInt i) = intType
12001201
litTyName (LChar c) = charType
1202+
litTyName (LBool _) = boolType -- not used
12011203

12021204
renameContext : String -> String -> Context -> Context
12031205
renameContext from to ctx = MkCtx ctx.lvl ctx.env (go ctx.types) ctx.bds ctx.ctxFC

src/Lib/ProcessDecl.newt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ processPrimFn ns fc nm used ty src = do
137137
let arity = piArity ty'
138138
setDef (QN ns nm) fc ty' (PrimFn src arity used') Nil
139139

140+
-- Heuristic for whether a function is simple enough to inline
140141
-- I'm trying to get ++ and + inlined as javascript +
141142
complexity : Tm → Int
142143
complexity (Ref _ _) = 1
@@ -186,7 +187,7 @@ processDef ns fc nm clauses = do
186187
-- putStrLn $ show tm
187188
-- TODO we need some protection against inlining a function calling itself.
188189
-- We need better heuristics, maybe fuel and deciding while inlining.
189-
-- bind is explicit here because the complexity has a 100 in it.
190+
-- IO,bind is explicit here because the complexity has a 100 in it.
190191
let name = show $ QN ns nm
191192
if complexity tm < 15 || name == "Prelude.Prelude.Monad Prelude.IO,bind" || name == "Prelude._>>=_"
192193
then setFlag (QN ns nm) fc Inline
@@ -404,6 +405,8 @@ processShortData ns fc lhs sigs = do
404405
populateConInfo : List TopEntry → List TopEntry
405406
populateConInfo entries =
406407
let (Nothing) = traverse checkEnum entries
408+
-- Boolean
409+
| Just (a :: b :: Nil) => (setInfo a FalseCon :: setInfo b TrueCon :: Nil)
407410
| Just entries => entries in
408411
let (a :: b :: Nil) = entries | _ => entries in
409412
let (Just succ) = find isSucc entries | _ => entries in

src/Lib/Syntax.newt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ instance Show Raw where
212212

213213
instance Pretty Literal where
214214
pretty (LString t) = text t
215+
pretty (LBool b) = if b then text "true" else text "false"
215216
pretty (LInt i) = text $ show i
216217
pretty (LChar c) = text $ show c
217218

src/Lib/Types.newt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ instance HasFC BindInfo where
5151

5252
Tm : U
5353

54-
data Literal = LString String | LInt Int | LChar Char
54+
data Literal = LString String | LInt Int | LChar Char | LBool Bool
5555

5656
instance Show Literal where
5757
show (LString str) = quoteString str
58+
show (LBool b) = if b then "true" else "false"
5859
show (LInt i) = show i
5960
show (LChar c) = "'\{show c}'" -- FIXME single quote
6061

@@ -337,17 +338,21 @@ instance Eq MetaMode where
337338
NoCheck == NoCheck = True
338339
_ == _ = False
339340

340-
data ConInfo = NormalCon | SuccCon | ZeroCon | EnumCon
341+
data ConInfo = NormalCon | SuccCon | ZeroCon | EnumCon | TrueCon | FalseCon
341342

342343
instance Eq ConInfo where
343344
NormalCon == NormalCon = True
344345
SuccCon == SuccCon = True
345346
ZeroCon == ZeroCon = True
346347
EnumCon == EnumCon = True
348+
TrueCon == TrueCon = True
349+
FalseCon == FalseCon = True
347350
_ == _ = False
348351

349352
instance Show ConInfo where
350353
show NormalCon = ""
354+
show FalseCon = "[F]"
355+
show TrueCon = "[T]"
351356
show SuccCon = "[S]"
352357
show ZeroCon = "[Z]"
353358
show EnumCon = "[E]"

src/Prelude.newt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const : ∀ a b. a → b → a
1010
const a b = a
1111

1212
data Unit = MkUnit
13-
data Bool = True | False
13+
-- False first so it ends up being false
14+
data Bool = False | True
1415

1516
not : Bool → Bool
1617
not True = False

0 commit comments

Comments
 (0)