Skip to content

Commit b57f351

Browse files
committed
Floating-point ranges and multi-step loops
For loops can now have step as a tuple, to allow iterating over different types of ranges in one loop.
1 parent 1f5edc0 commit b57f351

18 files changed

+136
-58
lines changed

src/Checker.hs

+30-14
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,7 @@ checkStmt statement cont = case statement of
327327
SFor pos expr1 expr2 block -> do
328328
checkStmt (SForStep pos expr1 expr2 (eInt 1) block) cont
329329
SForStep pos expr1 expr2 expr3 block -> do
330-
(t1, _) <- checkExpr expr3
331-
checkCast pos t1 tInt
332-
checkFor pos expr1 expr2 (checkBlock block >> cont)
330+
checkFor pos expr1 expr2 expr3 (checkBlock block >> cont)
333331
SBreak pos -> do
334332
r <- asks (M.lookup (Ident "#loop"))
335333
case r of
@@ -403,14 +401,20 @@ checkIf pos expr = do
403401
return $ t
404402

405403
-- | Checks a single `for` statement and continues with changed environment.
406-
checkFor :: Pos -> Expr Pos -> Expr Pos -> Run a -> Run a
407-
checkFor pos expr1 expr2 cont = do
408-
t <- checkForExpr pos expr2
409-
localLevel "#loop" 0 $ case (expr1, t) of
410-
(ETuple _ es, TTuple _ ts) -> do
411-
if length es == length ts then checkAssgs pos es ts cont
412-
else throw pos $ CannotUnpack t (length es)
413-
otherwise -> checkAssgs pos [expr1] [t] cont
404+
checkFor :: Pos -> Expr Pos -> Expr Pos -> Expr Pos -> Run a -> Run a
405+
checkFor pos expr1 expr2 expr3 cont = do
406+
t1 <- checkForExpr pos expr2
407+
(t2, _) <- checkExpr expr3
408+
checkForStep pos t2 t1
409+
localLevel "#loop" 0 $ case (expr1, t1) of
410+
(ETuple _ es, TTuple _ ts1) -> do
411+
if length es == length ts1 then case t2 of
412+
TTuple _ ts2 -> do
413+
if length ts2 == length ts1 then checkAssgs pos es ts1 cont
414+
else throw pos $ CannotUnpack t2 (length es)
415+
otherwise -> checkAssgs pos es ts1 cont
416+
else throw pos $ CannotUnpack t1 (length es)
417+
otherwise -> checkAssgs pos [expr1] [t1] cont
414418
where
415419
checkForExpr pos expr = case expr of
416420
ERangeIncl _ e1 e2 -> checkForRange pos e1 e2
@@ -426,6 +430,8 @@ checkFor pos expr1 expr2 cont = do
426430
case (t1, t2) of
427431
(TInt _, TInt _) -> return $ t1
428432
(TInt _, _) -> throw pos $ IllegalAssignment t2 tInt
433+
(TFloat _, TFloat _) -> return $ t1
434+
(TFloat _, _) -> throw pos $ IllegalAssignment t2 tFloat
429435
(TChar _, TChar _) -> return $ t1
430436
(TChar _, _) -> throw pos $ IllegalAssignment t2 tChar
431437
otherwise -> throw pos $ UnknownType
@@ -435,6 +441,18 @@ checkFor pos expr1 expr2 cont = do
435441
TString _ -> return $ tChar
436442
TArray _ t' -> return $ t'
437443
otherwise -> throw pos $ NotIterable t
444+
checkForStep pos step typ = case typ of
445+
TTuple _ ts2 -> case step of
446+
TTuple _ ts1 -> do
447+
if length ts1 == length ts2 then do
448+
ts <- forM (zip ts1 ts2) $ uncurry (checkForStep pos)
449+
return $ tTuple ts
450+
else throw pos $ CannotUnpack step (length ts2)
451+
otherwise -> do
452+
ts <- forM ts2 $ checkForStep pos step
453+
return $ tTuple ts
454+
TFloat _ -> checkCast pos step (tClass cNum)
455+
otherwise -> checkCast pos step tInt
438456

439457
-- | Checks function's arguments and body.
440458
checkFunc :: Pos -> Ident -> [FVar Pos] -> [FArg Pos] -> Type -> Maybe (Block Pos) -> Run a -> Run a
@@ -826,9 +844,7 @@ checkExpr expression = case expression of
826844
CprFor pos e1 e2 -> do
827845
checkArrayCpr (CprForStep pos e1 e2 (eInt 1)) cont
828846
CprForStep pos e1 e2 e3 -> do
829-
(t1, _) <- checkExpr e3
830-
checkCast pos t1 tInt
831-
checkFor pos e1 e2 cont
847+
checkFor pos e1 e2 e3 cont
832848
CprIf pos e -> do
833849
r <- asks (M.lookup (Ident "#loop"))
834850
case r of

src/CodeGen.hs

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ defaultValue typ = do
167167
castValue :: Type -> Value -> Type -> Run Value
168168
castValue typ1 val typ2 = case (typ1, typ2) of
169169
(TInt _, TFloat _) -> sitofp val
170+
(TInt _, TChar _) -> trunc typ1 typ2 val
170171
otherwise -> return $ val
171172

172173
-- | Casts given values to a common type.

src/Compiler.hs

+53-44
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,9 @@ compileStmt statement cont = case statement of
161161
label l2
162162
cont
163163
SFor _ expr1 expr2 block -> do
164-
compileFor expr1 expr2 "1" (compileBlock block) (const cont)
164+
compileFor expr1 expr2 (EInt _pos 1) (compileBlock block) (const cont)
165165
SForStep _ expr1 expr2 expr3 block -> do
166-
(_, v) <- compileExpr expr3
167-
compileFor expr1 expr2 v (compileBlock block) (const cont)
166+
compileFor expr1 expr2 expr3 (compileBlock block) (const cont)
168167
SBreak _ -> do
169168
(_, l) <- asks (M.! (Ident "#break"))
170169
goto l
@@ -187,7 +186,7 @@ compileStmt statement cont = case statement of
187186
let a = eVar ("a" ++ show n)
188187
let i = eVar ("i" ++ show n)
189188
compileAssg typ a val $ do
190-
flip (compileFor i (ERangeExcl _pos (eInt 0) (EAttr _pos a (Ident "length"))) "1") (const skip) $ do
189+
flip (compileFor i (ERangeExcl _pos (eInt 0) (EAttr _pos a (Ident "length"))) (eInt 1)) (const skip) $ do
191190
l <- nextLabel
192191
flip (compileIf (ECmp _pos (Cmp1 _pos i (CmpGT _pos) (eInt 0)))) l $ do
193192
call tInt "@putchar" [(tChar, "44")] -- ,
@@ -245,12 +244,12 @@ compileIf expr body exit = do
245244
return $ x
246245

247246
-- | Compiles a single `for` statement and continues with changed environment.
248-
compileFor :: Expr Pos -> Expr Pos -> Value -> Run a -> (a -> Run b) -> Run b
249-
compileFor expr1 expr2 step body cont = do
247+
compileFor :: Expr Pos -> Expr Pos -> Expr Pos -> Run a -> (a -> Run b) -> Run b
248+
compileFor expr1 expr2 expr3 body cont = do
250249
es <- case expr1 of
251250
ETuple _ es -> return $ es
252251
otherwise -> return $ [expr1]
253-
rs <- initFor expr2 step
252+
rs <- initFor expr2 expr3
254253
let (ts1, ts2, starts, steps, cmps, gets) = unzip6 rs
255254
ps <- mapM alloca ts1
256255
forM (zip3 ts1 starts ps) $ \(t, v, p) -> store t v p
@@ -278,41 +277,47 @@ compileFor expr1 expr2 step body cont = do
278277
otherwise -> forM_ (zip3 ts2 es vs2) $ \(t, e, v) -> compileAssg t e v skip
279278
x <- localLabel "#break" l2 $ localLabel "#continue" l3 $ body
280279
goto l3 >> label l3
281-
vs3 <- forM (zip3 ts1 vs1 steps) $ \(t, v, s) -> binop "add" t v s
280+
vs3 <- forM (zip3 ts1 vs1 steps) $ \(t, v, s) -> case t of
281+
TFloat _ -> binop "fadd" t v s
282+
otherwise -> binop "add" t v s
282283
forM (zip3 ts1 vs3 ps) $ \(t, v, p) -> store t v p
283284
goto l1
284285
label l2
285286
cont x
286287
where
287288
initForRangeIncl from to step = do
288-
[(t, v1), (_, v2)] <- mapM compileExpr [from, to]
289-
v3 <- case t of
290-
TInt _ -> return $ step
291-
otherwise -> trunc tInt t step
292-
v4 <- binop "icmp sgt" t v3 "0"
289+
[(t1, v1), (_, v2), (t2, v3)] <- mapM compileExpr [from, to, step]
290+
v4 <- castValue t2 v3 t1
291+
c <- case t1 of
292+
TFloat _ -> return $ "fcmp o"
293+
otherwise -> return $ "icmp s"
294+
v5 <- case t1 of
295+
TFloat _ -> binop (c ++ "gt") t1 v4 "0.0"
296+
otherwise -> binop (c ++ "gt") t1 v4 "0"
293297
let cmp v = do
294-
v5 <- binop "icmp sle" t v v2
295-
v6 <- binop "icmp sge" t v v2
296-
select v4 tBool v5 v6
297-
return $ (t, t, v1, v3, cmp, return)
298+
v6 <- binop (c ++ "le") t1 v v2
299+
v7 <- binop (c ++ "ge") t1 v v2
300+
select v5 tBool v6 v7
301+
return $ (t1, t1, v1, v4, cmp, return)
298302
initForRangeExcl from to step = do
299-
[(t, v1), (_, v2)] <- mapM compileExpr [from, to]
300-
v3 <- case t of
301-
TInt _ -> return $ step
302-
otherwise -> trunc tInt t step
303-
v4 <- binop "icmp sgt" t v3 "0"
303+
[(t1, v1), (_, v2), (t2, v3)] <- mapM compileExpr [from, to, step]
304+
v4 <- castValue t2 v3 t1
305+
c <- case t1 of
306+
TFloat _ -> return $ "fcmp o"
307+
otherwise -> return $ "icmp s"
308+
v5 <- case t1 of
309+
TFloat _ -> binop (c ++ "gt") t1 v4 "0.0"
310+
otherwise -> binop (c ++ "gt") t1 v4 "0"
304311
let cmp v = do
305-
v5 <- binop "icmp slt" t v v2
306-
v6 <- binop "icmp sgt" t v v2
307-
select v4 tBool v5 v6
308-
return $ (t, t, v1, v3, cmp, return)
312+
v6 <- binop (c ++ "lt") t1 v v2
313+
v7 <- binop (c ++ "gt") t1 v v2
314+
select v5 tBool v6 v7
315+
return $ (t1, t1, v1, v4, cmp, return)
309316
initForRangeInf from step = do
310-
(t, v1) <- compileExpr from
311-
v2 <- case t of
312-
TInt _ -> return $ step
313-
otherwise -> trunc tInt t step
317+
[(t1, v1), (t2, v2)] <- mapM compileExpr [from, step]
318+
v3 <- castValue t2 v2 t1
314319
let cmp _ = return $ "true"
315-
return $ (t, t, v1, v2, cmp, return)
320+
return $ (t1, t1, v1, v3, cmp, return)
316321
initForIterable iter step = do
317322
(t, v1) <- compileExpr iter
318323
t' <- case t of
@@ -321,22 +326,27 @@ compileFor expr1 expr2 step body cont = do
321326
v2 <- gep t v1 ["0"] [0] >>= load (tPtr t')
322327
v3 <- gep t v1 ["0"] [1] >>= load tInt
323328
v4 <- binop "sub" tInt v3 "1"
324-
v5 <- binop "icmp sgt" tInt step "0"
325-
v6 <- select v5 tInt "0" v4
329+
(_, v5) <- compileExpr step
330+
v6 <- binop "icmp sgt" tInt v5 "0"
331+
v7 <- select v6 tInt "0" v4
326332
let cmp v = do
327-
v7 <- binop "icmp sle" tInt v v4
328-
v8 <- binop "icmp sge" tInt v "0"
329-
select v5 tBool v7 v8
333+
v8 <- binop "icmp sle" tInt v v4
334+
v9 <- binop "icmp sge" tInt v "0"
335+
select v6 tBool v8 v9
330336
let get v = gep (tPtr t') v2 [v] [] >>= load t'
331-
return $ (tInt, t', v6, step, cmp, get)
337+
return $ (tInt, t', v7, v5, cmp, get)
332338
initFor expr step = do
333339
case expr of
334340
ERangeIncl _ e1 e2 -> forM [step] $ initForRangeIncl e1 e2
335341
ERangeExcl _ e1 e2 -> forM [step] $ initForRangeExcl e1 e2
336342
ERangeInf _ e1 -> forM [step] $ initForRangeInf e1
337-
ETuple _ es -> forM es $ \e -> do
338-
r <- initFor e step
339-
return $ head r
343+
ETuple _ es1 -> case step of
344+
ETuple _ es2 -> forM (zip es1 es2) $ \(e1, e2) -> do
345+
r <- initFor e1 e2
346+
return $ head r
347+
otherwise -> forM es1 $ \e -> do
348+
r <- initFor e step
349+
return $ head r
340350
otherwise -> forM [step] $ initForIterable expr
341351

342352
-- | Outputs LLVM code for a function definition and initialization of its default arguments.
@@ -526,7 +536,7 @@ compileExpr expression = case expression of
526536
b <- return $ SBlock _pos [
527537
SAssg _pos [EIndex _pos (eVar "result") (eVar "i"), EIndex _pos (eVar "source") (eVar "j")],
528538
SAssgAdd _pos (eVar "j") (eVar "c")]
529-
compileFor (eVar "i") (ERangeExcl _pos (eInt 0) (eVar "d")) "1" (compileBlock b) (const skip)
539+
compileFor (eVar "i") (ERangeExcl _pos (eInt 0) (eVar "d")) (eInt 1) (compileBlock b) (const skip)
530540
return $ (t, p2)
531541
EAttr _ _ _ -> compileRval expression
532542
ECall _ expr args -> do
@@ -803,10 +813,9 @@ compileExpr expression = case expression of
803813
cpr:cprs -> compileArrayCpr cpr $ compileArrayCprs cprs cont
804814
compileArrayCpr cpr cont = case cpr of
805815
CprFor _ e1 e2 -> do
806-
compileFor e1 e2 "1" cont return
816+
compileFor e1 e2 (EInt _pos 1) cont return
807817
CprForStep _ e1 e2 e3 -> do
808-
(_, v) <- compileExpr e3
809-
compileFor e1 e2 v cont return
818+
compileFor e1 e2 e3 cont return
810819
CprIf _ e -> do
811820
(_, l) <- asks (M.! (Ident "#continue"))
812821
x <- compileIf e cont l

test/bad/loops/for13.px

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
for t in 1..2 step 1, 2 do
3+
skip

test/bad/loops/for14.px

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
for t in 1..2, 3..4, 5..6 step 1, -1 do
3+
skip

test/bad/loops/for15.px

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
for x in 0..1 step 0.5 do
3+
skip

test/good/arrays/comprehension09.out

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
0.5
2+
0.25
3+
1
4+
0.5

test/good/arrays/comprehension09.px

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
a = [x/y for x in 1.0..2.0 for y in 2.0...6.0 step 2.0]
3+
for x in a do
4+
print x

test/good/arrays/for07.out

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1 A
2+
3 C

test/good/arrays/for07.px

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
a = [1, 2, 3]
3+
b = ['C', 'A']
4+
5+
for x, y in a, b step 2, -1 do
6+
print x, y

test/good/loops/for09.out

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
1.75
2+
2.75
3+
3.75

test/good/loops/for09.px

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
for x in 1.75..3.75 do
3+
print x

test/good/loops/for10.out

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
40

test/good/loops/for10.px

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
n = 0
3+
for x in 0.0...5.0, 1.125..6.0 step 0.125 do
4+
n += 1
5+
print n

test/good/loops/for11.out

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
0 z 0
2+
2.5 x -1.5
3+
5 v -3

test/good/loops/for11.px

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
for t in 0.0..5.0, 'z'..'a', 0.0...-5.0 step 2.5, -2, -1.5 do
3+
print t

test/good/strings/for04.out

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t T
2+
e S
3+
s E
4+
t T

test/good/strings/for04.px

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
s1 = "test"
3+
s2 = "TEST"
4+
for t in s1, s2 step 1, -1 do
5+
print t

0 commit comments

Comments
 (0)