git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
Initial add
commit100d787f41d8d9be80a6173e50041dede4c7daa9 authorPhil Freeman <paf31@cantab.net> dateSun 29 Sep 2013, 22:52 (13 years ago) parent291a6d4
20 files changed+1549
.gitignore+1 PureScript.cabal+36 Setup.lhs+6 src/Main.hs+64 src/PureScript.hs+31 src/PureScript/CodeGen.hs+119 src/PureScript/Declarations.hs+32 src/PureScript/Kinds.hs+29 src/PureScript/Parser.hs+24 src/PureScript/Parser/Common.hs+72 src/PureScript/Parser/Declarations.hs+47 src/PureScript/Parser/Types.hs+84 src/PureScript/Parser/Values.hs+190 src/PureScript/TypeChecker.hs+67 src/PureScript/TypeChecker/Kinds.hs+131 src/PureScript/TypeChecker/Monad.hs+46 src/PureScript/TypeChecker/Types.hs+412 src/PureScript/Types.hs+58 src/PureScript/Values.hs+80 src/Test.hs+20
.gitignore+1
@@ -4,4 +4,5 @@ cabal-dev
44*.hi
55*.chi
66*.chs.h
7+*.lksh*
78.virthualenv
PureScript.cabalnew file+36
@@ -0,0 +1,36 @@
1+name: Purescript
2+version: 0.0.1
3+cabal-version: >=1.2
4+build-type: Simple
5+license: MIT
6+license-file: LICENSE
7+copyright: (c) Phil Freeman 2013
8+maintainer: Phil Freeman <paf31@cantab.net>
9+stability: experimental
10+synopsis: PureScript Programming Language Compiler
11+description: A small compile-to-JS language with extensible records and type-safe blocks
12+author: Phil Freeman <paf31@cantab.net>
13+data-dir: ""
14+
15+library
16+ build-depends: base -any, cmdtheline -any, containers -any,
17+ mtl -any, parsec -any, utf8-string -any
18+ exposed-modules: PureScript
19+ exposed: True
20+ buildable: True
21+ hs-source-dirs: src
22+ other-modules: Test Main PureScript.CodeGen PureScript.Kinds
23+ PureScript.Parser PureScript.TypeChecker PureScript.Types
24+ PureScript.Values PureScript.Parser.Common
25+ PureScript.Parser.Declarations PureScript.Parser.Types
26+ PureScript.Parser.Values PureScript.TypeChecker.Kinds
27+ PureScript.TypeChecker.Monad PureScript.TypeChecker.Types
28+
29+executable psc
30+ build-depends: base -any, cmdtheline -any, containers -any,
31+ mtl -any, parsec -any, utf8-string -any
32+ main-is: Main.hs
33+ buildable: True
34+ hs-source-dirs: src
35+ other-modules: Test
36+
Setup.lhsnew file+6
@@ -0,0 +1,6 @@
1+#!/usr/bin/runhaskell
2+> module Main where
3+> import Distribution.Simple
4+> main :: IO ()
5+> main = defaultMain
6+
src/Main.hsnew file+64
@@ -0,0 +1,64 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Main
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Main where
16+
17+import PureScript
18+import Text.Parsec
19+import Data.Maybe (mapMaybe)
20+import Data.List (intercalate)
21+import System.Console.CmdTheLine
22+import Control.Applicative
23+import System.Exit (exitSuccess, exitFailure)
24+import qualified System.IO.UTF8 as U
25+
26+compile :: [FilePath] -> Maybe FilePath -> IO ()
27+compile inputFiles outputFile = do
28+ input <- fmap concat $ mapM U.readFile inputFiles
29+ let ast = parse parseDeclarations "" input
30+ case ast of
31+ Left err -> do
32+ U.print err
33+ exitFailure
34+ Right decls -> do
35+ case check (typeCheckAll decls) of
36+ Left typeError -> do
37+ U.putStrLn typeError
38+ exitFailure
39+ Right _ -> do
40+ let js = intercalate "\n" $ mapMaybe declToJs decls
41+ case outputFile of
42+ Just path -> U.writeFile path js
43+ Nothing -> U.putStrLn js
44+ exitSuccess
45+
46+inputFiles :: Term [FilePath]
47+inputFiles = nonEmpty $ posAny [] $ posInfo
48+ { posDoc = "The input .ps files" }
49+
50+outputFile :: Term (Maybe FilePath)
51+outputFile = value $ opt Nothing $ (optInfo [ "o", "output" ])
52+ { optDoc = "The output .js file" }
53+
54+term :: Term (IO ())
55+term = compile <$> inputFiles <*> outputFile
56+
57+termInfo :: TermInfo
58+termInfo = defTI
59+ { termName = "psc"
60+ , version = "1.0"
61+ , termDoc = "Compiles PureScript to Javascript"
62+ }
63+
64+main = run (term, termInfo)
src/PureScript.hsnew file+31
@@ -0,0 +1,31 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript (
16+ module PureScript.Values,
17+ module PureScript.Types,
18+ module PureScript.Kinds,
19+ module PureScript.Declarations,
20+ module PureScript.Parser,
21+ module PureScript.CodeGen,
22+ module PureScript.TypeChecker
23+) where
24+
25+import PureScript.Values
26+import PureScript.Types
27+import PureScript.Kinds
28+import PureScript.Declarations
29+import PureScript.Parser
30+import PureScript.CodeGen
31+import PureScript.TypeChecker
src/PureScript/CodeGen.hsnew file+119
@@ -0,0 +1,119 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.CodeGen
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.CodeGen (
16+ declToJs
17+) where
18+
19+import Data.List (nub, intersperse, intercalate)
20+import PureScript.Values
21+import PureScript.Declarations
22+
23+declToJs :: Declaration -> Maybe String
24+declToJs (ValueDeclaration name val) = Just $ "var " ++ name ++ " = " ++ valueToJs val ++ ";"
25+declToJs (DataDeclaration dcs@(DataConstructors { dataConstructors = ctors })) =
26+ Just $ concatMap (\(ctor, _) -> "var " ++ ctor ++ " = function (value) { return { ctor: '" ++ ctor ++ "', value: value }; };") ctors
27+declToJs _ = Nothing
28+
29+valueToJs :: Value -> String
30+valueToJs (NumericLiteral n) = show n
31+valueToJs (StringLiteral s) = show s
32+valueToJs (BooleanLiteral True) = "true"
33+valueToJs (BooleanLiteral False) = "false"
34+valueToJs (Unary op value) = unaryOperatorString op ++ "(" ++ valueToJs value ++ ")"
35+valueToJs (Binary op left right) = "(" ++ valueToJs left ++ ") " ++ binaryOperatorString op ++ " (" ++ valueToJs right ++ ")"
36+valueToJs (ArrayLiteral xs) = "[" ++ intercalate "," (map valueToJs xs) ++ "]"
37+valueToJs (ObjectLiteral ps) = "{" ++ intercalate "," (map objectPropertyToJs ps) ++ "}"
38+valueToJs (Accessor prop val) = "(" ++ valueToJs val ++ ")." ++ prop
39+valueToJs (Abs args value) = "function (" ++ intercalate "," args ++ ") { return " ++ valueToJs value ++ "; }"
40+valueToJs (Var ident) = ident
41+valueToJs (App f xs) = "(" ++ valueToJs f ++ ") (" ++ intercalate "," (map valueToJs xs) ++ ")"
42+valueToJs (Block sts) = "(function () {" ++ intercalate ";" (map statementToJs sts) ++ "})()"
43+valueToJs (Constructor name) = name
44+valueToJs (Case value binders) =
45+ "(function (_0) {"
46+ ++ concatMap (\(b, val) -> fst $ binderToJs 0 0 b $ "return " ++ valueToJs val ++ ";") binders
47+ ++ "throw \"Failed pattern match\";"
48+ ++ "})(" ++ valueToJs value ++ ")"
49+valueToJs (TypedValue value _) = valueToJs value
50+
51+unaryOperatorString :: UnaryOperator -> String
52+unaryOperatorString Negate = "-"
53+unaryOperatorString Not = "!"
54+unaryOperatorString BitwiseNot = "~"
55+
56+binaryOperatorString :: BinaryOperator -> String
57+binaryOperatorString Add = "+"
58+binaryOperatorString Subtract = "-"
59+binaryOperatorString Multiply = "*"
60+binaryOperatorString Divide = "/"
61+binaryOperatorString Modulus = "%"
62+binaryOperatorString LessThan = "<"
63+binaryOperatorString LessThanOrEqualTo = "<="
64+binaryOperatorString GreaterThan = ">"
65+binaryOperatorString GreaterThanOrEqualTo = ">="
66+binaryOperatorString BitwiseAnd = "&"
67+binaryOperatorString BitwiseOr = "|"
68+binaryOperatorString BitwiseXor = "^"
69+binaryOperatorString ShiftLeft = "<<"
70+binaryOperatorString ShiftRight = ">>"
71+binaryOperatorString ZeroFillShiftRight = ">>>"
72+binaryOperatorString EqualTo = "==="
73+binaryOperatorString NotEqualTo = "!=="
74+binaryOperatorString And = "&&"
75+binaryOperatorString Or = "||"
76+binaryOperatorString Concat = "+"
77+
78+binderToJs :: Int -> Int -> Binder -> String -> (String, Int)
79+binderToJs varName fresh (VarBinder s) done = ("var " ++ s ++ " = _" ++ show varName ++ "; " ++ done, fresh)
80+binderToJs varName fresh (ConstructorBinder ctor b) done =
81+ ("if (_" ++ show varName ++ ".ctor === \"" ++ ctor ++ "\") {"
82+ ++ "var _" ++ show fresh' ++ " = _" ++ show varName ++ ".value;"
83+ ++ js
84+ ++ "}", fresh'')
85+ where
86+ fresh' = succ fresh
87+ (js, fresh'') = binderToJs fresh' fresh' b done
88+binderToJs varName fresh (ObjectBinder bs) done = go fresh bs done
89+ where
90+ go fresh [] done = (done, fresh)
91+ go fresh ((prop, binder):bs') done =
92+ ( "var _" ++ show fresh' ++ " = _" ++ show varName ++ "." ++ prop ++ ";"
93+ ++ js
94+ , fresh''')
95+ where
96+ fresh' = succ fresh
97+ (done', fresh'') = go fresh' bs' done
98+ (js, fresh''') = binderToJs fresh' fresh'' binder done'
99+
100+objectPropertyToJs :: (String, Value) -> String
101+objectPropertyToJs (key, value) = key ++ ":" ++ valueToJs value
102+
103+statementToJs :: Statement -> String
104+statementToJs (VariableIntroduction name value) = "var " ++ name ++ " = " ++ valueToJs value
105+statementToJs (Assignment target value) = target ++ " = " ++ valueToJs value
106+statementToJs (While cond sts) = "while ("
107+ ++ valueToJs cond ++ ") {"
108+ ++ intercalate ";" (map statementToJs sts) ++ "}"
109+statementToJs (For (init, cond, done) sts) = "for (" ++
110+ statementToJs init
111+ ++ "; " ++ valueToJs cond
112+ ++ "; " ++ statementToJs done
113+ ++ ") {" ++ intercalate ";" (map statementToJs sts) ++ "}"
114+statementToJs (IfThenElse cond thens elses) = "if ("
115+ ++ valueToJs cond ++ ") {"
116+ ++ intercalate ";" (map statementToJs thens) ++ "}"
117+ ++ flip (maybe "") elses (\sts ->
118+ " else {" ++ intercalate ";" (map statementToJs sts) ++ "}")
119+statementToJs (Return value) = "return " ++ valueToJs value
src/PureScript/Declarations.hsnew file+32
@@ -0,0 +1,32 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Declarations
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Declarations (
16+ Declaration (..),
17+ DataConstructors (..)
18+) where
19+
20+import PureScript.Values
21+import PureScript.Types
22+
23+data Declaration
24+ = DataDeclaration DataConstructors
25+ | TypeDeclaration String Type
26+ | ValueDeclaration String Value deriving Show
27+
28+data DataConstructors = DataConstructors
29+ { typeConstructorName :: String
30+ , typeArguments :: [String]
31+ , dataConstructors :: [(String, Type)]
32+ } deriving Show
src/PureScript/Kinds.hsnew file+29
@@ -0,0 +1,29 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Kinds
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Kinds (
16+ Kind (..)
17+) where
18+
19+data Kind
20+ = KUnknown Int
21+ | Star
22+ | Row
23+ | FunKind Kind Kind deriving Eq
24+
25+instance Show Kind where
26+ show (KUnknown _) = "?"
27+ show Star = "*"
28+ show Row = "R"
29+ show (FunKind k1 k2) = "(" ++ show k1 ++ ") -> (" ++ show k2 ++ ")"
src/PureScript/Parser.hsnew file+24
@@ -0,0 +1,24 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Parser
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Parser (
16+ module PureScript.Parser.Types,
17+ module PureScript.Parser.Values,
18+ module PureScript.Parser.Declarations
19+) where
20+
21+import PureScript.Parser.Types
22+import PureScript.Parser.Values
23+import PureScript.Parser.Declarations
24+
src/PureScript/Parser/Common.hsnew file+72
@@ -0,0 +1,72 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Parser.Common
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Parser.Common where
16+
17+import Control.Applicative
18+import qualified Text.Parsec as P
19+import qualified Text.Parsec.Token as PT
20+import qualified Text.Parsec.Language as PL
21+
22+langDef = PL.haskellStyle
23+ { PT.identStart = P.lower <|> P.char '_'
24+ , PT.reservedNames = [ "case"
25+ , "data"
26+ , "var"
27+ , "while"
28+ , "for"
29+ , "if"
30+ , "then"
31+ , "else"
32+ , "return"
33+ , "true"
34+ , "false" ]
35+ }
36+
37+tokenParser = PT.makeTokenParser langDef
38+lexeme = PT.lexeme tokenParser
39+identifier = PT.identifier tokenParser
40+reserved = PT.reserved tokenParser
41+operator = PT.operator tokenParser
42+stringLiteral = PT.stringLiteral tokenParser
43+natural = PT.natural tokenParser
44+naturalOrFloat = PT.naturalOrFloat tokenParser
45+whiteSpace = PT.whiteSpace tokenParser
46+parens = PT.parens tokenParser
47+braces = PT.braces tokenParser
48+angles = PT.angles tokenParser
49+brackets = PT.brackets tokenParser
50+squares = PT.squares tokenParser
51+semi = PT.semi tokenParser
52+comma = PT.comma tokenParser
53+colon = PT.colon tokenParser
54+dot = PT.dot tokenParser
55+semiSep = PT.semiSep tokenParser
56+semiSep1 = PT.semiSep1 tokenParser
57+commaSep = PT.commaSep tokenParser
58+commaSep1 = PT.commaSep1 tokenParser
59+
60+properName :: P.Parsec String u String
61+properName = lexeme $ P.try properName'
62+ where
63+ properName' = (:) <$> P.upper <*> many (PT.identLetter langDef) P.<?> "name"
64+
65+augment :: P.Stream s m t => P.ParsecT s u m a -> P.ParsecT s u m b -> (a -> b -> a) -> P.ParsecT s u m a
66+augment p q f = (flip $ maybe id $ flip f) <$> p <*> P.optionMaybe q
67+
68+fold :: P.Stream s m t => P.ParsecT s u m a -> P.ParsecT s u m b -> (a -> b -> a) -> P.ParsecT s u m a
69+fold first more combine = do
70+ a <- first
71+ bs <- P.many more
72+ return $ foldl combine a bs
src/PureScript/Parser/Declarations.hsnew file+47
@@ -0,0 +1,47 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Parser.Declarations
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Parser.Declarations (
16+ parseDeclaration,
17+ parseDeclarations
18+) where
19+
20+import PureScript.Values
21+import PureScript.Types
22+import PureScript.Parser.Common
23+import Control.Applicative
24+import qualified Text.Parsec as P
25+import Control.Arrow (Arrow(..))
26+import PureScript.Declarations
27+import PureScript.Parser.Values
28+import PureScript.Parser.Types
29+
30+parseDataDeclaration :: P.Parsec String () Declaration
31+parseDataDeclaration = do
32+ reserved "data"
33+ name <- properName
34+ tyArgs <- many identifier
35+ lexeme $ P.char '='
36+ ctors <- P.sepBy1 ((,) <$> properName <*> parseType) (lexeme $ P.char '|')
37+ return $ DataDeclaration $ DataConstructors name tyArgs ctors
38+
39+parseValueDeclaration :: P.Parsec String () Declaration
40+parseValueDeclaration = ValueDeclaration <$> identifier <*> (lexeme (P.char '=') *> parseValue)
41+
42+parseDeclaration :: P.Parsec String () Declaration
43+parseDeclaration = parseDataDeclaration
44+ P.<|> parseValueDeclaration
45+
46+parseDeclarations :: P.Parsec String () [Declaration]
47+parseDeclarations = whiteSpace *> P.sepEndBy parseDeclaration semi <* P.eof
src/PureScript/Parser/Types.hsnew file+84
@@ -0,0 +1,84 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Parser.Types
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Parser.Types (
16+ parseType,
17+ parseRow
18+) where
19+
20+import PureScript.Types
21+import PureScript.Parser.Common
22+import Control.Applicative
23+import qualified Text.Parsec as P
24+import Control.Arrow (Arrow(..))
25+
26+parseNumber :: P.Parsec String () Type
27+parseNumber = const Number <$> P.string "Number"
28+
29+parseString :: P.Parsec String () Type
30+parseString = const String <$> P.string "String"
31+
32+parseBoolean :: P.Parsec String () Type
33+parseBoolean = const Boolean <$> P.string "Boolean"
34+
35+parseArray :: P.Parsec String () Type
36+parseArray = squares $ Array <$> parseType
37+
38+parseObject :: P.Parsec String () Type
39+parseObject = braces $ Object <$> parseRow
40+
41+parseFunction :: P.Parsec String () Type
42+parseFunction = do
43+ args <- lexeme $ parens $ commaSep parseType
44+ lexeme $ P.string "->"
45+ resultType <- parseType
46+ return $ Function args resultType
47+
48+parseTypeVariable :: P.Parsec String () Type
49+parseTypeVariable = TypeVar <$> identifier
50+
51+parseTypeConstructor :: P.Parsec String () Type
52+parseTypeConstructor = TypeConstructor <$> properName
53+
54+parseTypeAtom :: P.Parsec String () Type
55+parseTypeAtom = P.choice $ map P.try
56+ [ parseNumber
57+ , parseString
58+ , parseBoolean
59+ , parseArray
60+ , parseObject
61+ , parseFunction
62+ , parseTypeVariable
63+ , parseTypeConstructor
64+ , parens parseType ]
65+
66+parseType :: P.Parsec String () Type
67+parseType = fold (lexeme parseTypeAtom) (lexeme parseTypeAtom) TypeApp
68+
69+parseREmpty :: P.Parsec String () Row
70+parseREmpty = braces whiteSpace >> return REmpty
71+
72+parseRCons :: P.Parsec String () Row
73+parseRCons = RCons <$> (identifier <* lexeme (P.string "::"))
74+ <*> (parseType <* semi)
75+ <*> parseRow
76+
77+parseRowVariable :: P.Parsec String () Row
78+parseRowVariable = RowVar <$> identifier
79+
80+parseRow :: P.Parsec String () Row
81+parseRow = P.choice $ map P.try
82+ [ parseRCons
83+ , parseRowVariable
84+ , parseREmpty ]
src/PureScript/Parser/Values.hsnew file+190
@@ -0,0 +1,190 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Parser.Values
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Parser.Values (
16+ parseValue,
17+ parseBinder
18+) where
19+
20+import PureScript.Values
21+import qualified PureScript.Parser.Common as C
22+import Control.Applicative
23+import qualified Text.Parsec as P
24+import Text.Parsec.Expr
25+import Control.Arrow (Arrow(..))
26+import PureScript.Parser.Types
27+import PureScript.Types
28+
29+parseNumericLiteral :: P.Parsec String () Value
30+parseNumericLiteral = NumericLiteral <$> either fromIntegral id <$> C.naturalOrFloat
31+
32+parseStringLiteral :: P.Parsec String () Value
33+parseStringLiteral = StringLiteral <$> C.stringLiteral
34+
35+parseBooleanLiteral :: P.Parsec String () Value
36+parseBooleanLiteral = BooleanLiteral <$> ((P.string "true" >> return True)
37+ P.<|> (P.string "false" >> return False))
38+
39+parseArrayLiteral :: P.Parsec String () Value
40+parseArrayLiteral = ArrayLiteral <$> (C.squares $ C.commaSep parseValue)
41+
42+parseObjectLiteral :: P.Parsec String () Value
43+parseObjectLiteral = ObjectLiteral <$> (C.braces $ C.commaSep parseIdentifierAndValue)
44+
45+parseIdentifierAndValue :: P.Parsec String () (String, Value)
46+parseIdentifierAndValue = do
47+ name <- C.lexeme C.identifier
48+ C.colon
49+ value <- parseValue
50+ return (name, value)
51+
52+parseAbs :: P.Parsec String () Value
53+parseAbs = do
54+ P.char '\\'
55+ args <- C.commaSep1 C.identifier
56+ C.lexeme $ P.string "->"
57+ value <- parseValue
58+ return $ Abs args value
59+
60+parseApp :: P.Parsec String () Value
61+parseApp = App <$> parseValue
62+ <*> (C.parens $ C.commaSep parseValue)
63+
64+parseVar :: P.Parsec String () Value
65+parseVar = Var <$> C.identifier
66+
67+parseCase :: P.Parsec String () Value
68+parseCase = Case <$> P.between (C.reserved "case") (C.reserved "of") parseValue
69+ <*> C.braces (C.semiSep parseCaseAlternative)
70+
71+parseCaseAlternative :: P.Parsec String () (Binder, Value)
72+parseCaseAlternative = (,) <$> (parseBinder <* C.lexeme (P.string "->")) <*> parseValue
73+
74+parseBlock :: P.Parsec String () Value
75+parseBlock = Block <$> (C.braces $ P.many parseStatement)
76+
77+parseValueAtom :: P.Parsec String () Value
78+parseValueAtom = P.choice $ map P.try
79+ [ parseNumericLiteral
80+ , parseStringLiteral
81+ , parseBooleanLiteral
82+ , parseArrayLiteral
83+ , parseObjectLiteral
84+ , parseAbs
85+ , parseVar
86+ , parseBlock
87+ , parseCase
88+ , C.parens parseValue ]
89+
90+parseValue :: P.Parsec String () Value
91+parseValue = buildExpressionParser operators $ C.fold (C.lexeme typedValue') (C.lexeme funArgs) App
92+ where
93+ typedValue' = C.augment parseValueAtom parseTypeAnnotation TypedValue
94+ funArgs = C.parens $ C.commaSep parseValue
95+ parseTypeAnnotation = C.lexeme (P.string "::") *> parseType
96+ operators = [ [ Postfix $ (Accessor <$> (C.dot *> C.identifier)) ]
97+ , [ Infix (C.lexeme (P.try (P.string "||")) >> return (Binary Or)) AssocRight ]
98+ , [ Infix (C.lexeme (P.try (P.string "&&")) >> return (Binary And)) AssocRight ]
99+ , [ Infix (C.lexeme (P.char '|') >> return (Binary BitwiseOr)) AssocRight ]
100+ , [ Infix (C.lexeme (P.char '^') >> return (Binary BitwiseXor)) AssocRight ]
101+ , [ Infix (C.lexeme (P.char '&') >> return (Binary BitwiseAnd)) AssocRight ]
102+ , [ Infix (C.lexeme (P.string "==") >> return (Binary EqualTo)) AssocRight
103+ , Infix (C.lexeme (P.try (P.string "!=")) >> return (Binary NotEqualTo)) AssocRight ]
104+ , [ Infix (C.lexeme (P.string "<<") >> return (Binary ShiftLeft)) AssocRight
105+ , Infix (C.lexeme (P.try (P.string ">>>")) >> return (Binary ZeroFillShiftRight)) AssocRight
106+ , Infix (C.lexeme (P.string ">>") >> return (Binary ShiftRight)) AssocRight ]
107+ , [ Infix (C.lexeme (P.try (P.string "++")) >> return (Binary Concat)) AssocRight
108+ , Infix (C.lexeme (P.char '+') >> return (Binary Add)) AssocRight
109+ , Infix (C.lexeme (P.char '-') >> return (Binary Subtract)) AssocRight ]
110+ , [ Infix (C.lexeme (P.char '*') >> return (Binary Multiply)) AssocRight
111+ , Infix (C.lexeme (P.char '/') >> return (Binary Divide)) AssocRight
112+ , Infix (C.lexeme (P.char '%') >> return (Binary Modulus)) AssocRight ]
113+ , [ Prefix $ C.lexeme (P.char '!') >> return (Unary Not)
114+ , Prefix $ C.lexeme (P.char '~') >> return (Unary BitwiseNot)
115+ , Prefix $ C.lexeme (P.char '-') >> return (Unary Negate) ]
116+ ]
117+
118+parseVariableIntroduction :: P.Parsec String () Statement
119+parseVariableIntroduction = do
120+ C.reserved "var"
121+ name <- C.identifier
122+ C.lexeme $ P.char '='
123+ value <- parseValue
124+ C.semi
125+ return $ VariableIntroduction name value
126+
127+parseAssignment :: P.Parsec String () (Statement)
128+parseAssignment = do
129+ tgt <- C.identifier
130+ C.lexeme $ P.char '='
131+ value <- parseValue
132+ C.semi
133+ return $ Assignment tgt value
134+
135+parseManyStatements :: P.Parsec String () [Statement]
136+parseManyStatements = C.braces $ P.many parseStatement
137+
138+parseWhile :: P.Parsec String () Statement
139+parseWhile = While <$> (C.reserved "while" *> C.parens parseValue)
140+ <*> parseManyStatements
141+
142+parseFor :: P.Parsec String () Statement
143+parseFor = For <$> (C.reserved "for" *> C.parens forIntro)
144+ <*> parseManyStatements
145+ where
146+ forIntro = (,,) <$> parseStatement
147+ <*> (C.semi *> parseValue)
148+ <*> (C.semi *> parseStatement)
149+
150+parseIfThenElse :: P.Parsec String () Statement
151+parseIfThenElse = IfThenElse
152+ <$> (C.reserved "if" *> C.parens parseValue)
153+ <*> parseManyStatements
154+ <*> P.optionMaybe (C.reserved "else" *> parseManyStatements)
155+
156+parseReturn :: P.Parsec String () Statement
157+parseReturn = Return <$> (C.reserved "return" *> parseValue <* C.semi)
158+
159+parseStatement :: P.Parsec String () Statement
160+parseStatement = P.choice $ map P.try
161+ [ parseVariableIntroduction
162+ , parseAssignment
163+ , parseWhile
164+ , parseFor
165+ , parseIfThenElse
166+ , parseReturn ]
167+
168+parseVarBinder :: P.Parsec String () Binder
169+parseVarBinder = VarBinder <$> C.lexeme C.identifier
170+
171+parseConstructorBinder :: P.Parsec String () Binder
172+parseConstructorBinder = ConstructorBinder <$> C.lexeme C.properName
173+ <*> parseBinder
174+
175+parseObjectBinder :: P.Parsec String () Binder
176+parseObjectBinder = ObjectBinder <$> C.braces (C.commaSep parseIdentifierAndBinder)
177+
178+parseIdentifierAndBinder :: P.Parsec String () (String, Binder)
179+parseIdentifierAndBinder = do
180+ name <- C.lexeme C.identifier
181+ C.lexeme $ P.char '='
182+ binder <- parseBinder
183+ return (name, binder)
184+
185+parseBinder :: P.Parsec String () Binder
186+parseBinder = P.choice $ map P.try
187+ [ parseVarBinder
188+ , parseConstructorBinder
189+ , parseObjectBinder
190+ , C.parens parseBinder ]
src/PureScript/TypeChecker.hsnew file+67
@@ -0,0 +1,67 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.TypeChecker
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleInstances #-}
16+
17+module PureScript.TypeChecker (
18+ module PureScript.TypeChecker.Monad,
19+ module PureScript.TypeChecker.Kinds,
20+ module PureScript.TypeChecker.Types,
21+ typeCheck,
22+ typeCheckAll,
23+) where
24+
25+import PureScript.TypeChecker.Monad
26+import PureScript.TypeChecker.Kinds
27+import PureScript.TypeChecker.Types
28+
29+import Data.List
30+import Data.Function
31+
32+import PureScript.Values
33+import PureScript.Types
34+import PureScript.Kinds
35+import PureScript.Declarations
36+
37+import Control.Monad.State
38+import Control.Monad.Error
39+
40+import qualified Data.Map as M
41+
42+typeCheck :: Declaration -> Check ()
43+typeCheck (DataDeclaration dcs@(DataConstructors
44+ { typeConstructorName = name
45+ , typeArguments = args
46+ , dataConstructors = ctors
47+ })) = rethrow (("Error in type constructor " ++ name ++ ": ") ++) $ do
48+ env <- get
49+ guardWith (name ++ " is already defined") $ not $ M.member name (types env)
50+ kinds <- kindsOf args (map snd ctors)
51+ let ctorKind = foldl FunKind Star kinds
52+ put $ env { types = M.insert name ctorKind (types env) }
53+ flip mapM_ ctors $ \(dctor, ty) -> rethrow (("Error in data constructor " ++ name ++ ": ") ++) $ do
54+ env' <- get
55+ guardWith (dctor ++ " is already defined") $ not $ flip M.member (names env') dctor
56+ let ctorType = Function [ty] $ foldl TypeApp (TypeConstructor name) (map TypeVar args)
57+ put $ env' { names = M.insert dctor ctorType (names env) }
58+typeCheck (ValueDeclaration name val) = rethrow (("Error in declaration " ++ name ++ ": ") ++) $ do
59+ env <- get
60+ case M.lookup name (names env) of
61+ Just ty -> throwError $ name ++ " is already defined"
62+ Nothing -> do
63+ ty <- typeOf val
64+ put (env { names = M.insert name ty (names env) })
65+
66+typeCheckAll :: [Declaration] -> Check ()
67+typeCheckAll = mapM_ typeCheck
src/PureScript/TypeChecker/Kinds.hsnew file+131
@@ -0,0 +1,131 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.TypeChecker.Kinds
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.TypeChecker.Kinds (
16+ KindConstraint(..),
17+ KindSolution(..),
18+ kindsOf
19+) where
20+
21+import Data.List
22+import Data.Function
23+
24+import PureScript.Types
25+import PureScript.Kinds
26+import PureScript.Declarations
27+import PureScript.TypeChecker.Monad
28+
29+import Control.Monad.State
30+import Control.Monad.Error
31+
32+import Control.Applicative
33+import Control.Arrow (Kleisli(..), (***))
34+import qualified Control.Category as C
35+
36+import qualified Data.Map as M
37+
38+data KindConstraint = KindConstraint Int Kind deriving Show
39+
40+newtype KindSolution = KindSolution { runKindSolution :: Int -> Kind }
41+
42+emptyKindSolution :: KindSolution
43+emptyKindSolution = KindSolution KUnknown
44+
45+kindsOf :: [String] -> [Type] -> Check [Kind]
46+kindsOf args ts = do
47+ (cs, _, m) <- kindConstraintsAll 0 M.empty ts
48+ solution <- solveKindConstraints cs emptyKindSolution
49+ return $ map (maybe Star starIfUnknown . fmap (runKindSolution solution) . flip M.lookup m) args
50+
51+starIfUnknown :: Kind -> Kind
52+starIfUnknown (KUnknown _) = Star
53+starIfUnknown (FunKind k1 k2) = FunKind (starIfUnknown k1) (starIfUnknown k2)
54+starIfUnknown k = k
55+
56+kindConstraintsAll :: Int -> M.Map String Int -> [Type] -> Check ([KindConstraint], [Int], M.Map String Int)
57+kindConstraintsAll _ m [] = return ([], [], m)
58+kindConstraintsAll n m (t:ts) = do
59+ (cs, n', m') <- kindConstraints n m t
60+ (cs', ns, m'') <- kindConstraintsAll (n' + 1) m' ts
61+ return ((KindConstraint n' Star) : cs ++ cs', n':ns, m'')
62+
63+kindConstraints :: Int -> M.Map String Int -> Type -> Check ([KindConstraint], Int, M.Map String Int)
64+kindConstraints n m (Array t) = do
65+ (cs, n', m') <- kindConstraints n m t
66+ return ((KindConstraint n' Star) : (KindConstraint (n' + 1) Star) : cs, n' + 1, m')
67+kindConstraints n m (Object row) = do
68+ let me = n
69+ (cs, row, m') <- kindConstraintsForRow (n + 2) m row
70+ return ((KindConstraint me Star) : (KindConstraint row Row) : cs, me, m')
71+kindConstraints n m (Function args ret) = do
72+ (cs, ns, m') <- kindConstraintsAll n m args
73+ let next = maximum ns + 1
74+ (cs', retN, m'') <- kindConstraints next m' ret
75+ let me = retN + 1
76+ return ((KindConstraint retN Star) : (KindConstraint me Star) : (map (flip KindConstraint Star) ns) ++ cs, me, m'')
77+kindConstraints n m (TypeVar v) =
78+ let
79+ n' = maybe n id (M.lookup v m)
80+ in return ([], n', M.insert v n' m)
81+kindConstraints n m (TypeConstructor v) = do
82+ env <- get
83+ kind <- maybe (throwError "Unknown type constructor") return (M.lookup v (types env))
84+ return ([KindConstraint n kind], n, m)
85+kindConstraints n m (TypeApp t1 t2) = do
86+ (cs1, n1, m1) <- kindConstraints n m t1
87+ (cs2, n2, m2) <- kindConstraints (n1 + 1) m1 t2
88+ return ((KindConstraint n1 (FunKind (KUnknown n2) (KUnknown (n2 + 1)))) : cs1 ++ cs2, n2 + 1, m2)
89+kindConstraints n m _ = return ([KindConstraint n Star], n, m)
90+
91+kindConstraintsForRow :: Int -> M.Map String Int -> Row -> Check ([KindConstraint], Int, M.Map String Int)
92+kindConstraintsForRow = undefined
93+
94+solveKindConstraints :: [KindConstraint] -> KindSolution -> Check KindSolution
95+solveKindConstraints [] s = return s
96+solveKindConstraints (KindConstraint n k:cs) s = do
97+ guardWith "Occurs check failed" $ not $ kindOccursCheck n k
98+ let s' = KindSolution $ replaceUnknownKind n k . runKindSolution s
99+ cs' <- fmap concat $ mapM (substituteKindConstraint n k) cs
100+ solveKindConstraints cs' s'
101+
102+substituteKindConstraint :: Int -> Kind -> KindConstraint -> Check [KindConstraint]
103+substituteKindConstraint n k (KindConstraint m l)
104+ | n == m = unifyKinds k l
105+ | otherwise = return [KindConstraint m (replaceUnknownKind n k l)]
106+
107+replaceUnknownKind :: Int -> Kind -> Kind -> Kind
108+replaceUnknownKind n k = f
109+ where
110+ f (KUnknown m) | m == n = k
111+ f (FunKind k1 k2) = FunKind (f k2) (f k2)
112+ f other = other
113+
114+unifyKinds :: Kind -> Kind -> Check [KindConstraint]
115+unifyKinds (KUnknown u) k = do
116+ guardWith "Occurs check failed" $ not $ kindOccursCheck u k
117+ return [KindConstraint u k]
118+unifyKinds k (KUnknown u) = do
119+ guardWith "Occurs check failed" $ not $ kindOccursCheck u k
120+ return [KindConstraint u k]
121+unifyKinds Star Star = return []
122+unifyKinds (FunKind k1 k2) (FunKind k3 k4) = do
123+ cs1 <- unifyKinds k1 k3
124+ cs2 <- unifyKinds k2 k4
125+ return $ cs1 ++ cs2
126+unifyKinds k1 k2 = throwError $ "Cannot unify " ++ show k1 ++ " with " ++ show k2 ++ "."
127+
128+kindOccursCheck :: Int -> Kind -> Bool
129+kindOccursCheck u (KUnknown u') | u == u' = True
130+kindOccursCheck u (FunKind k1 k2) = kindOccursCheck u k1 || kindOccursCheck u k2
131+kindOccursCheck _ _ = False
src/PureScript/TypeChecker/Monad.hsnew file+46
@@ -0,0 +1,46 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.TypeChecker.Monad
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleInstances #-}
16+
17+module PureScript.TypeChecker.Monad where
18+
19+import PureScript.Types
20+import PureScript.Kinds
21+
22+import Control.Applicative
23+import Control.Monad.State
24+import Control.Monad.Error
25+
26+import qualified Data.Map as M
27+
28+data Environment = Environment
29+ { names :: M.Map String Type
30+ , types :: M.Map String Kind
31+ } deriving (Show)
32+
33+emptyEnvironment :: Environment
34+emptyEnvironment = Environment M.empty M.empty
35+
36+newtype Check a = Check { unCheck :: StateT Environment (Either String) a } deriving (Functor, Monad, Applicative, MonadPlus, MonadState Environment, MonadError String)
37+
38+check :: Check a -> Either String a
39+check = flip evalStateT emptyEnvironment . unCheck
40+
41+guardWith :: (MonadError e m) => e -> Bool -> m ()
42+guardWith _ True = return ()
43+guardWith e False = throwError e
44+
45+rethrow :: (MonadError e m) => (e -> e) -> m a -> m a
46+rethrow f = flip catchError $ \e -> throwError (f e)
src/PureScript/TypeChecker/Types.hsnew file+412
@@ -0,0 +1,412 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.TypeChecker.Types
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.TypeChecker.Types (
16+ TypeConstraint(..),
17+ TypeSolution(..),
18+ typeOf
19+) where
20+
21+import Data.List
22+import Data.Function
23+
24+import PureScript.Values
25+import PureScript.Types
26+import PureScript.TypeChecker.Monad
27+
28+import Control.Monad.State
29+import Control.Monad.Error
30+
31+import Control.Applicative
32+import Control.Arrow (Kleisli(..), (***))
33+import qualified Control.Category as C
34+
35+import qualified Data.Map as M
36+
37+data TypeConstraint
38+ = TypeConstraint Int Type
39+ | RowConstraint Int Row deriving Show
40+
41+newtype TypeSolution = TypeSolution { runTypeSolution :: (Int -> Type, Int -> Row) }
42+
43+emptyTypeSolution :: TypeSolution
44+emptyTypeSolution = TypeSolution (TUnknown, RUnknown)
45+
46+typeOf :: Value -> Check Type
47+typeOf val = do
48+ (cs, n, _) <- typeConstraints 0 M.empty val
49+ solution <- solveTypeConstraints cs emptyTypeSolution
50+ return $ varIfUnknown $ fst (runTypeSolution solution) n
51+
52+varIfUnknown :: Type -> Type
53+varIfUnknown = flip evalState M.empty . varIfUnknown'
54+ where
55+ varIfUnknown' :: Type -> State (M.Map Int String) Type
56+ varIfUnknown' (TUnknown n) = do
57+ m <- get
58+ case M.lookup n m of
59+ Nothing -> do
60+ let name = "t" ++ show (M.size m)
61+ put $ M.insert n name m
62+ return $ TypeVar name
63+ Just name -> return $ TypeVar name
64+ varIfUnknown' (Array t) = Array <$> varIfUnknown' t
65+ varIfUnknown' (Object row) = Object <$> varIfUnknown'' row
66+ varIfUnknown' (Function args ret) = Function <$> mapM varIfUnknown' args <*> varIfUnknown' ret
67+ varIfUnknown' (TypeApp t1 t2) = TypeApp <$> varIfUnknown' t1 <*> varIfUnknown' t2
68+ varIfUnknown' t = return t
69+ varIfUnknown'' :: Row -> State (M.Map Int String) Row
70+ varIfUnknown'' (RUnknown n) = do
71+ m <- get
72+ case M.lookup n m of
73+ Nothing -> do
74+ let name = "r" ++ show (M.size m)
75+ put $ M.insert n name m
76+ return $ RowVar name
77+ Just name -> return $ RowVar name
78+ varIfUnknown'' (RCons name ty row) = RCons name ty <$> varIfUnknown'' row
79+ varIfUnknown'' r = return r
80+
81+replaceVarsWithUnknowns :: Int -> Type -> (Type, Int)
82+replaceVarsWithUnknowns n = (id *** fst) . flip runState (n, M.empty) . replaceVarsWithUnknowns'
83+ where
84+ replaceVarsWithUnknowns' :: Type -> State (Int, M.Map String Int) Type
85+ replaceVarsWithUnknowns' (Array t) = Array <$> replaceVarsWithUnknowns' t
86+ replaceVarsWithUnknowns' (Object row) = Object <$> replaceVarsWithUnknowns'' row
87+ replaceVarsWithUnknowns' (Function args ret) = Function <$> mapM replaceVarsWithUnknowns' args <*> replaceVarsWithUnknowns' ret
88+ replaceVarsWithUnknowns' (TypeApp t1 t2) = TypeApp <$> replaceVarsWithUnknowns' t1 <*> replaceVarsWithUnknowns' t2
89+ replaceVarsWithUnknowns' (TypeVar var) = do
90+ (n, m) <- get
91+ case M.lookup var m of
92+ Nothing -> do
93+ put (n + 1, M.insert var n m)
94+ return $ TUnknown n
95+ Just u -> return $ TUnknown u
96+ replaceVarsWithUnknowns' t = return t
97+ replaceVarsWithUnknowns'' :: Row -> State (Int, M.Map String Int) Row
98+ replaceVarsWithUnknowns'' (RowVar var) = do
99+ (n, m) <- get
100+ case M.lookup var m of
101+ Nothing -> do
102+ put (n + 1, M.insert var n m)
103+ return $ RUnknown n
104+ Just u -> return $ RUnknown u
105+ replaceVarsWithUnknowns'' (RCons name ty row) = RCons name <$> replaceVarsWithUnknowns' ty <*> replaceVarsWithUnknowns'' row
106+ replaceVarsWithUnknowns'' r = return r
107+
108+typeConstraints :: Int -> M.Map String Int -> Value -> Check ([TypeConstraint], Int, Int)
109+typeConstraints n _ (NumericLiteral _) = return ([TypeConstraint n Number], n, n)
110+typeConstraints n _ (StringLiteral _) = return ([TypeConstraint n String], n, n)
111+typeConstraints n _ (BooleanLiteral _) = return ([TypeConstraint n Boolean], n, n)
112+typeConstraints n m (ArrayLiteral vals) = do
113+ (cs, ns, max1) <- typeConstraintsAll n m vals
114+ let me = max1 + 1
115+ return (cs ++ map (TypeConstraint me . Array . TUnknown) ns, me, me)
116+typeConstraints n m (Unary op val) = do
117+ (cs, n1, max1) <- typeConstraints n m val
118+ let me = max1 + 1
119+ return (cs ++ unaryOperatorConstraints op n1 me, me, me)
120+typeConstraints n m (Binary op left right) = do
121+ (cs1, n1, max1) <- typeConstraints n m left
122+ (cs2, n2, max2) <- typeConstraints (max1 + 1) m right
123+ let me = max1 + 1
124+ return (cs1 ++ cs2 ++ binaryOperatorConstraints op n1 n2 me, me, me)
125+typeConstraints n m (ObjectLiteral ps) = do
126+ (cs, ns, max1) <- typeConstraintsAll n m (map snd ps)
127+ let me = max1 + 1
128+ let tys = zipWith (\(name, _) u -> (name, TUnknown u)) ps ns
129+ return ((TypeConstraint me (Object (typesToRow tys))) : cs, me, me)
130+ where
131+ typesToRow [] = REmpty
132+ typesToRow ((name, ty):tys) = RCons name ty (typesToRow tys)
133+typeConstraints n m (Accessor prop val) = do
134+ (cs, n1, max1) <- typeConstraints n m val
135+ let me = max1 + 1
136+ let rest = max1 + 2
137+ return ((TypeConstraint n1 (Object (RCons prop (TUnknown me) (RUnknown rest)))) : cs, me, rest)
138+typeConstraints n m (Abs args ret) = do
139+ let ns = [n..n + length args - 1]
140+ let next = n + length args
141+ let m' = m `M.union` M.fromList (zipWith (,) args ns)
142+ (cs, n', max1) <- typeConstraints next m' ret
143+ let me = max1 + 1
144+ return ((TypeConstraint me (Function (map TUnknown ns) (TUnknown n'))) : cs, me, me)
145+typeConstraints n m (App f xs) = do
146+ (cs1, n1, max1) <- typeConstraints n m f
147+ (cs2, ns, max2) <- typeConstraintsAll (max1 + 1) m xs
148+ let me = max2 + 1
149+ return ((TypeConstraint n1 (Function (map TUnknown ns) (TUnknown me))) : cs1 ++ cs2, me, me)
150+typeConstraints n m (Var var) =
151+ case M.lookup var m of
152+ Nothing -> do
153+ env <- get
154+ case M.lookup var (names env) of
155+ Nothing -> throwError $ var ++ " is undefined"
156+ Just ty -> let (replaced, max1) = replaceVarsWithUnknowns (n + 1) ty
157+ in return ([TypeConstraint n replaced], n, max1)
158+ Just u -> return ([TypeConstraint u (TUnknown n)], n, n)
159+typeConstraints n m (Block ss) = do
160+ let ret = n + 1
161+ (cs, allCodePathsReturn, max1, _) <- typeConstraintsForBlock (ret + 1) m M.empty ret ss
162+ guardWith "Block is missing a return statement" allCodePathsReturn
163+ return (cs, ret, max1)
164+typeConstraints n m (Constructor c) = do
165+ env <- get
166+ case M.lookup c (names env) of
167+ Nothing -> throwError $ c ++ " is undefined"
168+ Just ty -> let (replaced, max1) = replaceVarsWithUnknowns (n + 1) ty
169+ in return ([TypeConstraint n replaced], n, max1)
170+typeConstraints n m (Case val binders) = do
171+ (cs1, n1, max1) <- typeConstraints n m val
172+ let ret = max1 + 1
173+ (cs2, max2) <- typeConstraintsForBinders (ret + 1) m n1 ret binders
174+ return (cs1 ++ cs2, ret, max2)
175+typeConstraints n m (TypedValue val ty) = do
176+ (cs, n1, max1) <- typeConstraints n m val
177+ return ([TypeConstraint n1 ty], n1, max1)
178+ -- TODO: Kind-check the type
179+
180+unaryOperatorConstraints :: UnaryOperator -> Int -> Int -> [TypeConstraint]
181+unaryOperatorConstraints Negate val result = [TypeConstraint val Number, TypeConstraint result Number]
182+unaryOperatorConstraints Not val result = [TypeConstraint val Boolean, TypeConstraint result Boolean]
183+unaryOperatorConstraints BitwiseNot val result = [TypeConstraint val Number, TypeConstraint result Number]
184+
185+binaryOperatorConstraints :: BinaryOperator -> Int -> Int -> Int -> [TypeConstraint]
186+binaryOperatorConstraints Add = symBinOpConstraints Number
187+binaryOperatorConstraints Subtract = symBinOpConstraints Number
188+binaryOperatorConstraints Multiply = symBinOpConstraints Number
189+binaryOperatorConstraints Divide = symBinOpConstraints Number
190+binaryOperatorConstraints Modulus = symBinOpConstraints Number
191+binaryOperatorConstraints LessThan = symBinOpConstraints Number
192+binaryOperatorConstraints LessThanOrEqualTo = symBinOpConstraints Number
193+binaryOperatorConstraints GreaterThan = symBinOpConstraints Number
194+binaryOperatorConstraints GreaterThanOrEqualTo = symBinOpConstraints Number
195+binaryOperatorConstraints BitwiseAnd = symBinOpConstraints Number
196+binaryOperatorConstraints BitwiseOr = symBinOpConstraints Number
197+binaryOperatorConstraints BitwiseXor = symBinOpConstraints Number
198+binaryOperatorConstraints ShiftLeft = symBinOpConstraints Number
199+binaryOperatorConstraints ShiftRight = symBinOpConstraints Number
200+binaryOperatorConstraints ZeroFillShiftRight = symBinOpConstraints Number
201+binaryOperatorConstraints EqualTo = equalityBinOpConstraints
202+binaryOperatorConstraints NotEqualTo = equalityBinOpConstraints
203+binaryOperatorConstraints And = symBinOpConstraints Boolean
204+binaryOperatorConstraints Or = symBinOpConstraints Boolean
205+binaryOperatorConstraints Concat = symBinOpConstraints String
206+
207+equalityBinOpConstraints :: Int -> Int -> Int -> [TypeConstraint]
208+equalityBinOpConstraints left right result = [TypeConstraint left (TUnknown right), TypeConstraint result Boolean]
209+
210+symBinOpConstraints :: Type -> Int -> Int -> Int -> [TypeConstraint]
211+symBinOpConstraints ty left right result = [TypeConstraint left ty, TypeConstraint right ty, TypeConstraint result ty]
212+
213+typeConstraintsForBinder :: Int -> M.Map String Int -> Int -> Binder -> Check ([TypeConstraint], Int, M.Map String Int)
214+typeConstraintsForBinder n m val (VarBinder name) =
215+ return ([TypeConstraint n (TUnknown val)], n, M.insert name n m)
216+typeConstraintsForBinder n m val (ConstructorBinder ctor binder) = do
217+ env <- get
218+ case M.lookup ctor (names env) of
219+ Just f@(Function [ty] ret) -> do
220+ let obj = n
221+ let (Function [ty'] ret', max1) = replaceVarsWithUnknowns n f
222+ (cs, max2, m1) <- typeConstraintsForBinder (max1 + 1) m obj binder
223+ return ((TypeConstraint val ret') : (TypeConstraint obj ty') : cs, max2, m1)
224+ Just _ -> throwError $ ctor ++ " is not a constructor"
225+ _ -> throwError $ "Constructor " ++ ctor ++ " is not defined"
226+typeConstraintsForBinder n m val (ObjectBinder props) = do undefined
227+
228+typeConstraintsForBinders :: Int -> M.Map String Int -> Int -> Int -> [(Binder, Value)] -> Check ([TypeConstraint], Int)
229+typeConstraintsForBinders n _ _ _ [] = return ([], n)
230+typeConstraintsForBinders n m nval ret ((binder, val):bs) = do
231+ (cs1, max1, m1) <- typeConstraintsForBinder n m nval binder
232+ (cs2, n2, max2) <- typeConstraints (max1 + 1) m1 val
233+ (cs3, max2) <- typeConstraintsForBinders (max2 + 1) m nval ret bs
234+ return ((TypeConstraint n2 (TUnknown ret)) : cs1 ++ cs2 ++ cs3, max2)
235+
236+typeConstraintsForStatement :: Int -> M.Map String Int -> M.Map String Int -> Int -> Statement -> Check ([TypeConstraint], Bool, Int, M.Map String Int)
237+typeConstraintsForStatement n m mass ret (VariableIntroduction name val) = do
238+ case M.lookup name (m `M.union` mass) of
239+ Nothing -> do
240+ (cs1, n1, max1) <- typeConstraints n m val
241+ return $ (cs1, False, max1, M.insert name n1 mass)
242+ Just ty -> throwError $ "Variable with name " ++ name ++ " already exists."
243+typeConstraintsForStatement n m mass ret (Assignment name val) = do
244+ case M.lookup name (m `M.union` mass) of
245+ Nothing -> throwError $ "No local variable with name " ++ name
246+ Just ty -> do
247+ (cs1, n1, max1) <- typeConstraints n m val
248+ return ((TypeConstraint n1 (TUnknown ty)) : cs1, False, max1, mass)
249+typeConstraintsForStatement n m mass ret (While val inner) = do
250+ (cs1, n1, max1) <- typeConstraints n m val
251+ (cs2, allCodePathsReturn, max2, _) <- typeConstraintsForBlock (max1 + 1) m mass ret inner
252+ return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2, allCodePathsReturn, max2, mass)
253+typeConstraintsForStatement n m mass ret (IfThenElse val thens Nothing) = do
254+ (cs1, n1, max1) <- typeConstraints n m val
255+ (cs2, allCodePathsReturn, max2, _) <- typeConstraintsForBlock (max1 + 1) m mass ret thens
256+ return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2, allCodePathsReturn, max2, mass)
257+typeConstraintsForStatement n m mass ret (IfThenElse val thens (Just elses)) = do
258+ (cs1, n1, max1) <- typeConstraints n m val
259+ (cs2, allCodePathsReturn1, max2, _) <- typeConstraintsForBlock (max1 + 1) m mass ret thens
260+ (cs3, allCodePathsReturn2, max3, _) <- typeConstraintsForBlock (max2 + 1) m mass ret elses
261+ return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2 ++ cs3, allCodePathsReturn1 && allCodePathsReturn2, max3, mass)
262+typeConstraintsForStatement n m mass ret (For (init, cond, cont) inner) = do
263+ (cs1, b1, max1, mass1) <- typeConstraintsForStatement n m mass ret init
264+ guardWith "Cannot return from inside for" $ not b1
265+ (cs2, n1, max2) <- typeConstraints (max1 + 1) (m `M.union` mass1) cond
266+ (cs3, b2, max3, mass2) <- typeConstraintsForStatement (max2 + 1) (m `M.union` mass1) mass1 ret cont
267+ guardWith "Cannot return from inside for" $ not b2
268+ (cs4, allCodePathsReturn, max4, _) <- typeConstraintsForBlock (max3 + 1) (m `M.union` mass2) mass2 ret inner
269+ return $ ((TypeConstraint n1 Boolean) : cs1 ++ cs2 ++ cs3 ++ cs4, allCodePathsReturn, max4, mass)
270+typeConstraintsForStatement n m mass ret (Return val) = do
271+ (cs1, n1, max1) <- typeConstraints n m val
272+ return ((TypeConstraint n1 (TUnknown ret)) : cs1, True, max1, mass)
273+
274+typeConstraintsForBlock :: Int -> M.Map String Int -> M.Map String Int -> Int -> [Statement] -> Check ([TypeConstraint], Bool, Int, M.Map String Int)
275+typeConstraintsForBlock n _ mass _ [] = return ([], False, n, mass)
276+typeConstraintsForBlock n m mass ret (s:ss) = do
277+ (cs1, b1, max1, mass1) <- typeConstraintsForStatement n (m `M.union` mass) mass ret s
278+ case (b1, ss) of
279+ (True, []) -> return (cs1, True, max1, mass1)
280+ (True, _) -> throwError "Unreachable code"
281+ (False, ss) -> do
282+ (cs2, b2, max2, mass2) <- typeConstraintsForBlock (max1 + 1) m mass1 ret ss
283+ return (cs1 ++ cs2, b2, max2, mass2)
284+
285+typeConstraintsAll :: Int -> M.Map String Int -> [Value] -> Check ([TypeConstraint], [Int], Int)
286+typeConstraintsAll n _ [] = return ([], [], n)
287+typeConstraintsAll n m (t:ts) = do
288+ (cs, n', max1) <- typeConstraints n m t
289+ (cs', ns, max2) <- typeConstraintsAll (max1 + 1) m ts
290+ return (cs ++ cs', n':ns, max2)
291+
292+solveTypeConstraints :: [TypeConstraint] -> TypeSolution -> Check TypeSolution
293+solveTypeConstraints [] s = return s
294+solveTypeConstraints (TypeConstraint n t:cs) s = do
295+ guardWith "Occurs check failed" $ not $ typeOccursCheck n t
296+ let s' = let (f, g) = runTypeSolution s
297+ in TypeSolution (replaceTypeInType n t . f, replaceTypeInRow n t . g)
298+ cs' <- fmap concat $ mapM (substituteTypeInConstraint n t) cs
299+ solveTypeConstraints cs' s'
300+solveTypeConstraints (RowConstraint n r:cs) s = do
301+ guardWith "Occurs check failed" $ not $ rowOccursCheck n r
302+ let s' = let (f, g) = runTypeSolution s
303+ in TypeSolution $ (replaceRowInType n r . f, replaceRowInRow n r . g)
304+ cs' <- fmap concat $ mapM (substituteRowInConstraint n r) cs
305+ solveTypeConstraints cs' s'
306+
307+substituteTypeInConstraint :: Int -> Type -> TypeConstraint -> Check [TypeConstraint]
308+substituteTypeInConstraint n s (TypeConstraint m t)
309+ | n == m = unifyTypes s t
310+ | otherwise = return [TypeConstraint m (replaceTypeInType n s t)]
311+substituteTypeInConstraint n s (RowConstraint m r)
312+ = return [RowConstraint m (replaceTypeInRow n s r)]
313+
314+substituteRowInConstraint :: Int -> Row -> TypeConstraint -> Check [TypeConstraint]
315+substituteRowInConstraint n r (TypeConstraint m t)
316+ = return [TypeConstraint m (replaceRowInType n r t)]
317+substituteRowInConstraint n r (RowConstraint m r1)
318+ | m == n = unifyRows r r1
319+ | otherwise = return [RowConstraint m (replaceRowInRow n r r1)]
320+
321+replaceTypeInType :: Int -> Type -> Type -> Type
322+replaceTypeInType n t (TUnknown m) | m == n = t
323+replaceTypeInType n t (Array t1) = Array $ replaceTypeInType n t t1
324+replaceTypeInType n t (Object row) = Object $ replaceTypeInRow n t row
325+replaceTypeInType n t (Function args ret) = Function (map (replaceTypeInType n t) args) (replaceTypeInType n t ret)
326+replaceTypeInType n t (TypeApp t1 t2) = TypeApp (replaceTypeInType n t t1) (replaceTypeInType n t t2)
327+replaceTypeInType _ _ other = other
328+
329+replaceTypeInRow :: Int -> Type -> Row -> Row
330+replaceTypeInRow n t (RCons name ty row) = RCons name (replaceTypeInType n t ty) (replaceTypeInRow n t row)
331+replaceTypeInRow n t other = other
332+
333+replaceRowInType :: Int -> Row -> Type -> Type
334+replaceRowInType n r (Array t1) = Array $ replaceRowInType n r t1
335+replaceRowInType n r (Object row) = Object $ replaceRowInRow n r row
336+replaceRowInType n r (Function args ret) = Function (map (replaceRowInType n r) args) (replaceRowInType n r ret)
337+replaceRowInType n r (TypeApp t1 t2) = TypeApp (replaceRowInType n r t1) (replaceRowInType n r t2)
338+replaceRowInType _ _ other = other
339+
340+replaceRowInRow :: Int -> Row -> Row -> Row
341+replaceRowInRow n r (RUnknown m) | m == n = r
342+replaceRowInRow n r (RCons name ty row) = RCons name (replaceRowInType n r ty) (replaceRowInRow n r row)
343+replaceRowInRow _ _ other = other
344+
345+unifyTypes :: Type -> Type -> Check [TypeConstraint]
346+unifyTypes (TUnknown u) t = do
347+ guardWith "Occurs check failed" $ not $ typeOccursCheck u t
348+ return [TypeConstraint u t]
349+unifyTypes t (TUnknown u) = do
350+ guardWith "Occurs check failed" $ not $ typeOccursCheck u t
351+ return [TypeConstraint u t]
352+unifyTypes Number Number = return []
353+unifyTypes String String = return []
354+unifyTypes Boolean Boolean = return []
355+unifyTypes (Array s) (Array t) = unifyTypes s t
356+unifyTypes (Object row1) (Object row2) = unifyRows row1 row2
357+unifyTypes (Function args1 ret1) (Function args2 ret2) = do
358+ cs1 <- fmap concat $ zipWithM unifyTypes args1 args2
359+ cs2 <- unifyTypes ret1 ret2
360+ return $ cs1 ++ cs2
361+unifyTypes (TypeVar v1) (TypeVar v2) | v1 == v2 = return []
362+unifyTypes (TypeConstructor c1) (TypeConstructor c2) | c1 == c2 = return []
363+unifyTypes (TypeVar v1) (TypeVar v2) = return []
364+unifyTypes (TypeApp t1 t2) (TypeApp t3 t4) = do
365+ cs1 <- unifyTypes t1 t3
366+ cs2 <- unifyTypes t2 t4
367+ return $ cs1 ++ cs2
368+unifyTypes t1 t2 = throwError $ "Cannot unify " ++ show t1 ++ " with " ++ show t2 ++ "."
369+
370+unifyRows :: Row -> Row -> Check [TypeConstraint]
371+unifyRows r1 r2 =
372+ let
373+ (s1, r1') = toList r1
374+ (s2, r2') = toList r2
375+ int = [ (t1, t2) | (name, t1) <- s1, (name', t2) <- s2, name == name' ]
376+ sd1 = [ (name, t1) | (name, t1) <- s1, not (elem name (map fst s2)) ]
377+ sd2 = [ (name, t2) | (name, t2) <- s2, not (elem name (map fst s1)) ]
378+ in rethrow (const $ "Cannot unify " ++ show r1 ++ " with " ++ show r2) $ do
379+ cs1 <- fmap concat $ mapM (uncurry unifyTypes) int
380+ cs2 <- unifyRows' (fromList (sd1, r1')) (fromList (sd2, r2'))
381+ return $ cs1 ++ cs2
382+ where
383+ unifyRows' :: Row -> Row -> Check [TypeConstraint]
384+ unifyRows' r (RUnknown u) = do
385+ guardWith "Occurs check failed" $ not $ rowOccursCheck u r
386+ return [RowConstraint u r]
387+ unifyRows' (RUnknown u) r = do
388+ guardWith "Occurs check failed" $ not $ rowOccursCheck u r
389+ return [RowConstraint u r]
390+ unifyRows' (RowVar v1) (RowVar v2) | v1 == v2 = return []
391+ unifyRows' REmpty REmpty = return []
392+ unifyRows' r1 r2 = throwError $ "Cannot unify " ++ show r1 ++ " with " ++ show r2 ++ "."
393+ toList :: Row -> ([(String, Type)], Row)
394+ toList (RCons name ty row) = let (tys, rest) = toList row
395+ in ((name, ty):tys, rest)
396+ toList r = ([], r)
397+ fromList :: ([(String, Type)], Row) -> Row
398+ fromList ([], r) = r
399+ fromList ((name, t):ts, r) = RCons name t (fromList (ts, r))
400+
401+typeOccursCheck :: Int -> Type -> Bool
402+typeOccursCheck u (TUnknown u') | u == u' = True
403+typeOccursCheck u (Array t) = typeOccursCheck u t
404+typeOccursCheck u (Object row) = rowOccursCheck u row
405+typeOccursCheck u (Function args ret) = any (typeOccursCheck u) args || typeOccursCheck u ret
406+typeOccursCheck u (TypeApp s t) = typeOccursCheck u s || typeOccursCheck u t
407+typeOccursCheck _ _ = False
408+
409+rowOccursCheck :: Int -> Row -> Bool
410+rowOccursCheck u (RUnknown u') | u == u' = True
411+rowOccursCheck u (RCons name ty row) = typeOccursCheck u ty || rowOccursCheck u row
412+rowOccursCheck _ _ = False
src/PureScript/Types.hsnew file+58
@@ -0,0 +1,58 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Purescript.Types
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+{-# LANGUAGE DeriveFunctor #-}
16+
17+module PureScript.Types (
18+ Type (..),
19+ Row(..)
20+) where
21+
22+import Data.List (intercalate)
23+
24+data Type
25+ = TUnknown Int
26+ | Number
27+ | String
28+ | Boolean
29+ | Array Type
30+ | Object Row
31+ | Function [Type] Type
32+ | TypeVar String
33+ | TypeConstructor String
34+ | TypeApp Type Type
35+
36+data Row
37+ = RUnknown Int
38+ | RowVar String
39+ | REmpty
40+ | RCons String Type Row
41+
42+instance Show Type where
43+ show (TUnknown _) = "?"
44+ show Number = "Number"
45+ show String = "String"
46+ show Boolean = "Boolean"
47+ show (Array t) = "[" ++ show t ++ "]"
48+ show (Object row) = "{ " ++ show row ++ " }"
49+ show (Function args ret) = "(" ++ intercalate ", " (map show args) ++ ") -> " ++ show ret
50+ show (TypeVar v) = v
51+ show (TypeConstructor c) = c
52+ show (TypeApp t1 t2) = "(" ++ show t1 ++ ") (" ++ show t2 ++ ")"
53+
54+instance Show Row where
55+ show (RUnknown _) = "?"
56+ show REmpty = "{}"
57+ show (RowVar v) = v
58+ show (RCons name ty row) = name ++ " :: " ++ show ty ++ "; " ++ show row
src/PureScript/Values.hsnew file+80
@@ -0,0 +1,80 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : PureScript.Values
4+-- Copyright : (c) Phil Freeman 2013
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module PureScript.Values (
16+ UnaryOperator (..),
17+ BinaryOperator (..),
18+ Value (..),
19+ Statement (..),
20+ Binder (..)
21+) where
22+
23+import PureScript.Types
24+
25+data UnaryOperator
26+ = Negate
27+ | Not
28+ | BitwiseNot deriving (Show, Eq)
29+
30+data BinaryOperator
31+ = Add
32+ | Subtract
33+ | Multiply
34+ | Divide
35+ | Modulus
36+ | EqualTo
37+ | NotEqualTo
38+ | LessThan
39+ | LessThanOrEqualTo
40+ | GreaterThan
41+ | GreaterThanOrEqualTo
42+ | And
43+ | Or
44+ | BitwiseAnd
45+ | BitwiseOr
46+ | BitwiseXor
47+ | ShiftLeft
48+ | ShiftRight
49+ | ZeroFillShiftRight
50+ | Concat deriving (Show, Eq)
51+
52+data Value
53+ = NumericLiteral Double
54+ | StringLiteral String
55+ | BooleanLiteral Bool
56+ | Unary UnaryOperator Value
57+ | Binary BinaryOperator Value Value
58+ | ArrayLiteral [Value]
59+ | ObjectLiteral [(String, Value)]
60+ | Accessor String Value
61+ | Abs [String] Value
62+ | App Value [Value]
63+ | Var String
64+ | Block [Statement]
65+ | Constructor String
66+ | Case Value [(Binder, Value)]
67+ | TypedValue Value Type deriving Show
68+
69+data Statement
70+ = VariableIntroduction String Value
71+ | Assignment String Value
72+ | While Value [Statement]
73+ | For (Statement, Value, Statement) [Statement]
74+ | IfThenElse (Value) [Statement] (Maybe [Statement])
75+ | Return Value deriving Show
76+
77+data Binder
78+ = VarBinder String
79+ | ConstructorBinder String Binder
80+ | ObjectBinder [(String, Binder)] deriving Show
src/Test.hsnew file+20
@@ -0,0 +1,20 @@
1+-----------------------------------------------------------------------------
2+--
3+-- Module : Test
4+-- Copyright : Phil Freeman <paf31@cantab.net>
5+-- License : MIT
6+--
7+-- Maintainer : Phil Freeman <paf31@cantab.net>
8+-- Stability : experimental
9+-- Portability :
10+--
11+-- |
12+--
13+-----------------------------------------------------------------------------
14+
15+module Test (
16+
17+) where
18+
19+
20+