Skip to content

Commit 8d50b53

Browse files
authored
Support tuple syntax without having to declare PairN (#162)
1 parent dd8d52f commit 8d50b53

File tree

12 files changed

+94
-165
lines changed

12 files changed

+94
-165
lines changed

regression-tests/tests/func_match_tuple.ssl

-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
// checking that 3-tuples and 4-tuples can be declared with new syntax
2-
type Pair2 a b
3-
Pair2 a b
4-
5-
6-
type Pair3 a b c
7-
Pair3 a b c
8-
9-
10-
111
putip_ putc x =
122
if x < 10
133
putc (x + 48)

regression-tests/tests/tuple-lib.lib

-12
This file was deleted.

regression-tests/tests/tuple1.ssl

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ main cin cout =
4040
let puti = puti_ putc
4141
let printl l = printl_ puti putc l
4242

43-
4443
let x_ = Pair3 1 2 3
4544
let y_ = Pair3 4 5 6
46-
45+
4746
printl x_
4847
putnl ()
4948
printl y_

regression-tests/tests/tuple2.ssl

+6-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
// tests whether a 2-tuple(Pair) can be implemented in Ssland with ADTs
2+
// without builtin syntax
3+
24
type Pair2 a b
35
Pair2 a b
46

5-
6-
type Pair3 a b c
7-
Pair3 a b c
8-
9-
10-
type Pair4 a b c d
11-
Pair4 a b c d
12-
137
putip_ putc x =
148
if x < 10
159
putc (x + 48)
@@ -27,12 +21,12 @@ puti_ putc x =
2721
// tests the pattern match of pairs
2822
printl_ puti putc p =
2923
match p
30-
(x,y) = puti x
31-
putc 32
32-
puti y
24+
Pair2 x y = puti x
25+
putc 32
26+
puti y
3327

3428

35-
main cin cout =
29+
main cin cout =
3630
let putc c = after 1, cout <- c
3731
wait cout
3832
let puti = puti_ putc

regression-tests/tests/tuple2_nsyntax.ssl

-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
// checking that 2-tuples can be declared with new syntax
2-
type Pair2 a b
3-
Pair2 a b
4-
5-
6-
type Pair3 a b c
7-
Pair3 a b c
8-
9-
10-
type Pair4 a b c d
11-
Pair4 a b c d
12-
131
putip_ putc x =
142
if x < 10
153
putc (x + 48)

regression-tests/tests/tuple3.ssl

-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
// checking that 3-tuples and 4-tuples can be declared with new syntax
2-
type Pair2 a b
3-
Pair2 a b
4-
5-
6-
type Pair3 a b c
7-
Pair3 a b c
8-
9-
10-
type Pair4 a b c d
11-
Pair4 a b c d
12-
131
putip_ putc x =
142
if x < 10
153
putc (x + 48)

regression-tests/tests/tuple_nested.ssl

-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
// checking that 3-tuples and 4-tuples can be declared with new syntax
2-
type Pair2 a b
3-
Pair2 a b
4-
5-
6-
type Pair3 a b c
7-
Pair3 a b c
8-
9-
10-
type Pair4 a b c d
11-
Pair4 a b c d
12-
131
putip_ putc x =
142
if x < 10
153
putc (x + 48)

regression-tests/tests/tuplen.ssl

-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
11
// checking that 5 tuples, 6 tuples.... can be declared with new syntax
2-
3-
type Pair6 a b c d e f
4-
Pair6 a b c d e f
5-
6-
type Pair5 a b c d e
7-
Pair5 a b c d e
8-
9-
102
putip_ putc x =
113
if x < 10
124
putc (x + 48)

src/Common/Identifiers.hs

+13-12
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ module Common.Identifiers (
5757
isVar,
5858
mangle,
5959
mangleVars,
60+
isReserved,
61+
reserved,
6062
isGenerated,
6163
genId,
6264
ungenId,
63-
tuple,
64-
tempTuple,
6565
cons,
6666
nil,
6767
) where
@@ -87,6 +87,7 @@ import Data.String (IsString (..))
8787

8888
import Language.C (Id (..))
8989
import Language.C.Quote (ToIdent (..))
90+
import Data.List (isPrefixOf)
9091

9192

9293
-- | A type that may be used as a Sslang identifier.
@@ -264,6 +265,16 @@ isVar :: Identifiable a => a -> Bool
264265
isVar = not . isCons
265266

266267

268+
-- | Create a reserved identifier (that can be code-generated as long as a can)
269+
reserved :: Identifiable a => a -> a
270+
reserved a = fromString $ "__sslang_builtin__" <> ident a
271+
272+
273+
-- | Whether an identifier is reserved.
274+
isReserved :: Identifiable a => a -> Bool
275+
isReserved i = "__sslang_builtin__" `isPrefixOf` s
276+
where s = ident i
277+
267278
-- | Whether an identifier is an compiler-generated variable name.
268279
isGenerated :: Identifiable a => a -> Bool
269280
isGenerated i
@@ -321,16 +332,6 @@ mangleVars :: (Data a) => a -> a
321332
mangleVars = mangle (Proxy :: Proxy VarId) . mangle (Proxy :: Proxy TVarId)
322333

323334

324-
-- | the tuple identifier
325-
tuple :: Identifier
326-
tuple = Identifier "(,)"
327-
328-
329-
-- | we'll use this temp tuple name for now due to the naming issue
330-
tempTuple :: Identifier
331-
tempTuple = Identifier "Pair"
332-
333-
334335
-- | Cons identifier for Lists
335336
cons :: Identifier
336337
cons = Identifier "Cons"

src/Front/Scope.hs

+13-9
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import Common.Compiler (
6060
warn,
6161
)
6262
import Common.Default (Default (..))
63-
import Common.Identifiers (Identifiable (..), Identifier (..), isCons, isVar)
63+
import Common.Identifiers (Identifiable (..), Identifier (..), isCons, isReserved, isVar)
6464

6565
import qualified Front.Ast as A
6666
import Front.Identifiers (
@@ -168,28 +168,32 @@ ensureUnique ids = do
168168
i : _ -> throwError $ ScopeError $ "Defined more than once: " <> showId i
169169

170170

171+
-- | Ensure that an identifier will not clash with compiler-generated ones
172+
ensureNotReserved :: Identifier -> ScopeFn ()
173+
ensureNotReserved i = do
174+
when (isReserved i) $ throwError nameErr
175+
where
176+
nameErr = NameError $ showId i <> " may clash with internal sslang-generated identifiers"
177+
178+
171179
-- | Check that a constructor 'Identifier' has the right naming convention.
172180
ensureCons :: Identifier -> ScopeFn ()
173181
ensureCons i = do
174182
ensureNonempty i
183+
ensureNotReserved i
175184
unless (isCons i) $ throwError nameErr
176185
where
177-
nameErr =
178-
NameError $
179-
showId i
180-
<> " should begin with upper case or begin and end with ':'"
186+
nameErr = NameError $ showId i <> " should begin with upper case or begin and end with ':'"
181187

182188

183189
-- | Check that a variable 'Identifier' has the right naming convention.
184190
ensureVar :: Identifier -> ScopeFn ()
185191
ensureVar i = do
186192
ensureNonempty i
193+
ensureNotReserved i
187194
unless (isVar i) $ throwError nameErr
188195
where
189-
nameErr =
190-
NameError $
191-
showId i
192-
<> " should begin with upper case or begin and end with ':'"
196+
nameErr = NameError $ showId i <> " should begin with upper case or begin and end with ':'"
193197

194198

195199
{- | Validate a declaration of a data 'Identifier'.

src/IR/LowerAst.hs

+57-19
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ import qualified IR.Types as I
3030

3131
import Control.Monad (unless)
3232
import Data.Bifunctor (Bifunctor (..))
33+
import Data.Generics (everything, mkQ)
3334
import Data.Maybe (fromMaybe)
3435
import qualified Data.Set as S
35-
import IR.Types.Type (tempTupleId)
36+
import IR.Types.Type (tupleId)
3637

3738

3839
-- | Unannotated terms appear as an empty stack.
@@ -52,9 +53,9 @@ annType = ann . I.AnnType
5253

5354
-- | Lower an AST 'Program' into IR.
5455
lowerProgram :: A.Program -> Compiler.Pass (I.Program I.Annotations)
55-
lowerProgram (A.Program ds) = do
56+
lowerProgram p@(A.Program ds) = do
5657
let (tds, cds, xds, dds) = A.getTops ds
57-
tds' <- mapM lowerTypeDef tds
58+
tds' <- (++) <$> lowerTupleDefs p <*> mapM lowerTypeDef tds
5859
xds' <- mapM lowerExternDecl xds
5960
dds' <- mapM lowerDef dds
6061
return $
@@ -66,21 +67,58 @@ lowerProgram (A.Program ds) = do
6667
, I.cDefs = concat cds
6768
, I.symTable = I.uninitializedSymTable
6869
}
70+
71+
72+
lowerTypeDef :: A.TypeDef -> Compiler.Pass (I.TConId, I.TypeDef)
73+
lowerTypeDef td = do
74+
tds <- mapM lowerTypeVariant $ A.typeVariants td
75+
return
76+
( fromId $ A.typeName td
77+
, I.TypeDef{I.targs = map TVarId $ A.typeParams td, I.variants = tds}
78+
)
79+
where
80+
lowerTypeVariant ::
81+
A.TypeVariant -> Compiler.Pass (I.DConId, I.TypeVariant)
82+
lowerTypeVariant (A.VariantUnnamed vn ts) =
83+
(fromId vn,) . I.VariantUnnamed <$> mapM lowerType ts
84+
85+
86+
lowerTupleDefs :: A.Program -> Compiler.Pass [(I.TConId, I.TypeDef)]
87+
lowerTupleDefs dds = return $ map lowerTupleDef $ S.toList tupleSizes
6988
where
70-
lowerTypeDef :: A.TypeDef -> Compiler.Pass (I.TConId, I.TypeDef)
71-
lowerTypeDef td = do
72-
tds <- mapM lowerTypeVariant $ A.typeVariants td
73-
return
74-
( fromId $ A.typeName td
75-
, I.TypeDef{I.targs = map TVarId $ A.typeParams td, I.variants = tds}
76-
)
77-
where
78-
lowerTypeVariant
79-
:: A.TypeVariant -> Compiler.Pass (I.DConId, I.TypeVariant)
80-
lowerTypeVariant (A.VariantUnnamed vn ts) =
81-
(fromId vn,) . I.VariantUnnamed <$> mapM lowerType ts
82-
lowerExternDecl :: A.ExternDecl -> Compiler.Pass (I.VarId, I.Type)
83-
lowerExternDecl (A.ExternDecl i t) = (fromId i,) <$> lowerType t
89+
tupleSizes :: S.Set Int
90+
tupleSizes =
91+
-- This is so hilariously inefficient but I tell myself it's ok
92+
everything S.union (mkQ S.empty dconTupleLen) dds
93+
`S.union` everything S.union (mkQ S.empty patTupleLen) dds
94+
`S.union` everything S.union (mkQ S.empty typeTupleLen) dds
95+
96+
dconTupleLen :: A.Expr -> S.Set Int
97+
dconTupleLen (A.Tuple es) = S.singleton $ length es
98+
dconTupleLen (A.Par es) = S.singleton $ length es
99+
dconTupleLen _ = S.empty
100+
101+
patTupleLen :: A.Pat -> S.Set Int
102+
patTupleLen (A.PatTup es) = S.singleton $ length es
103+
patTupleLen _ = S.empty
104+
105+
typeTupleLen :: A.Typ -> S.Set Int
106+
typeTupleLen (A.TTuple tys) = S.singleton $ length tys
107+
typeTupleLen _ = S.empty
108+
109+
lowerTupleDef :: Int -> (I.TConId, I.TypeDef)
110+
lowerTupleDef i =
111+
let targs = map (TVarId . fromString . (("tup" ++ show i ++ "arg") ++) . show) [1 .. i]
112+
in ( tupleId i
113+
, I.TypeDef
114+
{ I.targs = targs
115+
, I.variants = [(tupleId i, I.VariantUnnamed $ map I.TVar targs)]
116+
}
117+
)
118+
119+
120+
lowerExternDecl :: A.ExternDecl -> Compiler.Pass (I.VarId, I.Type)
121+
lowerExternDecl (A.ExternDecl i t) = (fromId i,) <$> lowerType t
84122

85123

86124
-- | Lower an 'A.Definition' into a name and bound expression.
@@ -207,7 +245,7 @@ lowerExpr (A.Match s ps) =
207245
where
208246
lowerArm (a, e) = (,) <$> lowerPatAlt a <*> lowerExpr e
209247
lowerExpr (A.Tuple es) =
210-
apply_recurse (I.Data (I.DConId (tempTupleId $ length es)) untyped) <$> mapM lowerExpr es
248+
apply_recurse (I.Data (I.DConId (tupleId $ length es)) untyped) <$> mapM lowerExpr es
211249
where
212250
apply_recurse e [] = e
213251
apply_recurse e (x : xs) = apply_recurse (I.App e x untyped) xs
@@ -223,7 +261,7 @@ lowerPatAlt (A.PatId i)
223261
| otherwise = return $ I.AltData (I.DConId i) [] untyped
224262
lowerPatAlt (A.PatLit l) = I.AltLit <$> lowerLit l <*> pure untyped
225263
lowerPatAlt (A.PatTup ps) =
226-
I.AltData (I.tempTupleId $ length ps) <$> mapM lowerPatAlt ps <*> pure untyped
264+
I.AltData (I.tupleId $ length ps) <$> mapM lowerPatAlt ps <*> pure untyped
227265
lowerPatAlt p@(A.PatApp _) = case A.collectPApp p of
228266
(A.PatId i, ps) | isCons i -> I.AltData (fromId i) <$> mapM lowerPatAlt ps <*> pure untyped
229267
_ -> Compiler.unexpected "lowerPatAlt: app head should be a data constructor"

0 commit comments

Comments
 (0)