module Language.TypeScript.Parser (
declarationSourceFile,
nextIdentifier
) where
import Language.TypeScript.Types
import Language.TypeScript.Lexer
import Text.Parsec
import Text.Parsec.Expr
import Text.Parsec.String (parseFromFile)
import Control.Applicative
(Applicative(..), (<$>), (<*>), (<*), (*>))
commentPlaceholder = fmap toOffset getPosition where
toOffset pos = Left $ (sourceLine pos, sourceColumn pos)
nextIdentifier =
skipMany (choice (map (try . reserved) [ "export", "declare", "public", "private", "static" ]))
>> choice (map (try . reserved) [ "var", "function", "class", "interface", "enum", "module" ])
>> identifier
declarationSourceFile = whiteSpace >> many declarationElement <* eof
declarationElement = choice $ map try
[ InterfaceDeclaration <$> commentPlaceholder <*> optionMaybe (reserved "export" >> return Exported) <*> interface
, ImportDeclaration <$> optionMaybe (reserved "export" >> return Exported) <*> (reserved "import" *> identifier) <*> (lexeme (char '=') *> entityName)
, ExportDeclaration <$> (reserved "export" >> lexeme (char '=') *> identifier)
, ExternalImportDeclaration <$> optionMaybe (reserved "export" >> return Exported) <*> (reserved "import" *> identifier) <*> (lexeme (char '=') *> reserved "require" *> parens stringLiteral)
, AmbientDeclaration <$> commentPlaceholder <*> optionMaybe (reserved "export" >> return Exported) <*> (reserved "declare" *> ambientDeclaration)
]
ambientDeclaration = choice (map try
[ ambientVariableDeclaration
, ambientFunctionDeclaration
, ambientClassDeclaration
, ambientInterfaceDeclaration
, ambientEnumDeclaration
, ambientModuleDeclaration
, ambientExternalModuleDeclaration
])
ambientVariableDeclaration = AmbientVariableDeclaration <$> commentPlaceholder <*> (reserved "var" *> identifier) <*> (optionMaybe typeAnnotation <* semi)
ambientFunctionDeclaration = AmbientFunctionDeclaration <$> commentPlaceholder <*> (reserved "function" *> identifier) <*> (parameterListAndReturnType <* semi)
ambientClassDeclaration = AmbientClassDeclaration <$> commentPlaceholder <*> (reserved "class" *> identifier) <*> optionMaybe typeParameters <*> optionMaybe extendsClause <*> optionMaybe implementsClause <*> braces (sepEndBy ambientClassBodyElement semi)
ambientInterfaceDeclaration = AmbientInterfaceDeclaration <$> interface
ambientEnumDeclaration = AmbientEnumDeclaration <$> commentPlaceholder <*> (reserved "enum" *> identifier) <*> braces (sepEndBy enumMember comma)
where
enumMember = (,) <$> propertyName <*> optionMaybe (lexeme (char '=') >> integer)
ambientModuleDeclaration = AmbientModuleDeclaration <$> commentPlaceholder <*> (reserved "module" *> sepBy identifier dot) <*> braces (many ambientDeclaration)
ambientExternalModuleDeclaration = AmbientExternalModuleDeclaration <$> commentPlaceholder <*> (reserved "module" *> stringLiteral) <*> braces (many ambientDeclaration)
ambientClassBodyElement = (,) <$> commentPlaceholder <*> (choice $ map try
[ ambientConstructorDeclaration
, ambientMemberDeclaration
, ambientIndexSignature ])
ambientConstructorDeclaration = AmbientConstructorDeclaration <$> (reserved "constructor" *> parameterList <* semi)
ambientMemberDeclaration = AmbientMemberDeclaration <$> optionMaybe publicOrPrivate <*> optionMaybe static <*> propertyName <*> choice [fmap Right parameterListAndReturnType, fmap Left (optionMaybe typeAnnotation)]
ambientIndexSignature = AmbientIndexSignature <$> indexSignature
interface = Interface <$> commentPlaceholder <*> (reserved "interface" *> identifier) <*> optionMaybe typeParameters <*> optionMaybe extendsClause <*> objectType
extendsClause = reserved "extends" >> classOrInterfaceTypeList
implementsClause = reserved "implements" >> classOrInterfaceTypeList
classOrInterfaceTypeList = commaSep typeRef
objectType = braces typeBody
typeBody = TypeBody <$> sepEndBy typeMember semi
where
typeMember = (,) <$> commentPlaceholder <*> (choice $ map try [ methodSignature, propertySignature, callSignature, constructSignature, typeIndexSignature ])
propertySignature = PropertySignature <$> propertyName <*> optionMaybe (lexeme (char '?' >> return Optional)) <*> optionMaybe typeAnnotation
propertyName = identifier <|> stringLiteral
typeAnnotation = colon >> _type
callSignature = CallSignature <$> parameterListAndReturnType
parameterListAndReturnType = ParameterListAndReturnType <$> optionMaybe typeParameters <*> parens parameterList <*> optionMaybe typeAnnotation
parameterList = commaSep parameter
parameter = choice
[ try $ RequiredOrOptionalParameter <$> optionMaybe publicOrPrivate <*> identifier <*> optionMaybe (lexeme (char '?' >> return Optional)) <*> optionMaybe typeAnnotation
, RestParameter <$> (lexeme (string "...") *> identifier) <*> optionMaybe typeAnnotation
]
static = reserved "static" >> return Static
publicOrPrivate = choice
[ reserved "public" >> return Public
, reserved "private" >> return Private ]
stringOrNumber = choice
[ reserved "string" >> return String
, reserved "number" >> return Number ]
constructSignature = ConstructSignature <$> (reserved "new" *> optionMaybe typeParameters) <*> parens parameterList <*> optionMaybe typeAnnotation
typeIndexSignature = TypeIndexSignature <$> indexSignature
indexSignature = squares (IndexSignature <$> identifier <*> (colon *> stringOrNumber)) <*> typeAnnotation
methodSignature = MethodSignature <$> propertyName <*> optionMaybe (lexeme (char '?' >> return Optional)) <*> parameterListAndReturnType
typeParameters = angles $ commaSep1 typeParameter
typeParameter = TypeParameter <$> identifier <*> optionMaybe (reserved "extends" >> _type)
fold :: Stream s m t => ParsecT s u m a -> ParsecT s u m b -> (a -> b -> a) -> ParsecT s u m a
fold first more combine = do
a <- first
bs <- many more
return $ foldl combine a bs
_type = lexeme $ choice [ arrayType, functionType, constructorType ]
where
arrayType = fold atomicType (squares whiteSpace) (flip $ const ArrayType)
atomicType = choice $ map try
[ Predefined <$> predefinedType
, TypeReference <$> typeRef
, ObjectType <$> objectType
]
functionType = FunctionType <$> optionMaybe typeParameters <*> parens parameterList <*> returnType
constructorType = ConstructorType <$> (reserved "new" *> optionMaybe typeParameters) <*> parens parameterList <*> returnType
returnType = lexeme (string "=>") *> _type
typeRef = TypeRef <$> typeName <*> optionMaybe typeArguments
predefinedType = choice
[ reserved "any" >> return AnyType
, reserved "number" >> return NumberType
, (reserved "boolean" <|> reserved "bool") >> return BooleanType
, reserved "string" >> return StringType
, reserved "void" >> return VoidType
]
entityName = fmap toEntityName (sepBy1 identifier dot)
where
toEntityName [t] = EntityName Nothing t
toEntityName ts = EntityName (Just $ ModuleName $ init ts) (last ts)
typeName = fmap toTypeName (sepBy1 identifier dot)
where
toTypeName [t] = TypeName Nothing t
toTypeName ts = TypeName (Just $ ModuleName $ init ts) (last ts)
typeArguments = angles $ commaSep1 _type