aboutsummaryrefslogtreecommitdiff
path: root/Data/GraphQL/Parser.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Data/GraphQL/Parser.hs')
-rw-r--r--Data/GraphQL/Parser.hs107
1 files changed, 58 insertions, 49 deletions
diff --git a/Data/GraphQL/Parser.hs b/Data/GraphQL/Parser.hs
index c999004..1f5b8e6 100644
--- a/Data/GraphQL/Parser.hs
+++ b/Data/GraphQL/Parser.hs
@@ -1,6 +1,5 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE LambdaCase #-}
module Data.GraphQL.Parser where
import Prelude hiding (takeWhile)
@@ -11,8 +10,10 @@ import Data.Monoid (Monoid, mempty)
#endif
import Control.Applicative ((<|>), empty, many, optional)
import Control.Monad (when)
-import Data.Char
-import Data.Text (Text, pack)
+import Data.Char (isDigit, isSpace)
+import Data.Foldable (traverse_)
+
+import Data.Text (Text, append)
import Data.Attoparsec.Text
( Parser
, (<?>)
@@ -20,25 +21,27 @@ import Data.Attoparsec.Text
, decimal
, double
, endOfLine
+ , inClass
, many1
, manyTill
, option
, peekChar
- , satisfy
, sepBy1
, signed
+ , takeWhile
+ , takeWhile1
)
import Data.GraphQL.AST
-- * Name
--- XXX: Handle starting `_` and no number at the beginning:
--- https://facebook.github.io/graphql/#sec-Names
--- TODO: Use takeWhile1 instead for efficiency. With takeWhile1 there is no
--- parsing failure.
name :: Parser Name
-name = tok $ pack <$> many1 (satisfy isAlphaNum)
+name = tok $ append <$> takeWhile1 isA_z
+ <*> takeWhile ((||) <$> isDigit <*> isA_z)
+ where
+ -- `isAlpha` handles many more Unicode Chars
+ isA_z = inClass $ '_' : ['A'..'Z'] ++ ['a'..'z']
-- * Document
@@ -48,7 +51,8 @@ document = whiteSpace
-- Try SelectionSet when no definition
<|> (Document . pure
. DefinitionOperation
- . Query mempty empty empty
+ . Query
+ . Node mempty empty empty
<$> selectionSet)
<?> "document error!"
@@ -60,14 +64,15 @@ definition = DefinitionOperation <$> operationDefinition
operationDefinition :: Parser OperationDefinition
operationDefinition =
- op Query "query"
- <|> op Mutation "mutation"
+ Query <$ tok "query" <*> node
+ <|> Mutation <$ tok "mutation" <*> node
<?> "operationDefinition error!"
- where
- op f n = f <$ tok n <*> tok name
- <*> optempty variableDefinitions
- <*> optempty directives
- <*> selectionSet
+
+node :: Parser Node
+node = Node <$> name
+ <*> optempty variableDefinitions
+ <*> optempty directives
+ <*> selectionSet
variableDefinitions :: Parser [VariableDefinition]
variableDefinitions = parens (many1 variableDefinition)
@@ -148,16 +153,24 @@ typeCondition = namedType
-- explicit types use the `typedValue` parser.
value :: Parser Value
value = ValueVariable <$> variable
- -- TODO: Handle arbitrary precision.
- <|> ValueInt <$> tok (signed decimal)
- <|> ValueFloat <$> tok (signed double)
- <|> ValueBoolean <$> bool
- -- TODO: Handle escape characters, unicode, etc
- <|> ValueString <$> quotes name
+ -- TODO: Handle maxBound, Int32 in spec.
+ <|> ValueInt <$> tok (signed decimal)
+ <|> ValueFloat <$> tok (signed double)
+ <|> ValueBoolean <$> booleanValue
+ <|> ValueString <$> stringValue
-- `true` and `false` have been tried before
- <|> ValueEnum <$> name
- <|> ValueList <$> listValue
- <|> ValueObject <$> objectValue
+ <|> ValueEnum <$> name
+ <|> ValueList <$> listValue
+ <|> ValueObject <$> objectValue
+ <?> "value error!"
+
+booleanValue :: Parser Bool
+booleanValue = True <$ tok "true"
+ <|> False <$ tok "false"
+
+-- TODO: Escape characters. Look at `jsstring_` in aeson package.
+stringValue :: Parser StringValue
+stringValue = StringValue <$> quotes (takeWhile (/= '"'))
-- Notice it can be empty
listValue :: Parser ListValue
@@ -170,10 +183,6 @@ objectValue = ObjectValue <$> braces (many objectField)
objectField :: Parser ObjectField
objectField = ObjectField <$> name <* tok ":" <*> value
-bool :: Parser Bool
-bool = True <$ tok "true"
- <|> False <$ tok "false"
-
-- * Directives
directives :: Parser [Directive]
@@ -188,9 +197,10 @@ directive = Directive
-- * Type Reference
type_ :: Parser Type
-type_ = TypeNamed <$> namedType
- <|> TypeList <$> listType
+type_ = TypeList <$> listType
<|> TypeNonNull <$> nonNullType
+ <|> TypeNamed <$> namedType
+ <?> "type_ error!"
namedType :: Parser NamedType
namedType = NamedType <$> name
@@ -201,6 +211,7 @@ listType = ListType <$> brackets type_
nonNullType :: Parser NonNullType
nonNullType = NonNullTypeNamed <$> namedType <* tok "!"
<|> NonNullTypeList <$> listType <* tok "!"
+ <?> "nonNullType error!"
-- * Type Definition
@@ -221,7 +232,6 @@ objectTypeDefinition = ObjectTypeDefinition
<*> name
<*> optempty interfaces
<*> fieldDefinitions
- <?> "objectTypeDefinition error!"
interfaces :: Parser Interfaces
interfaces = tok "implements" *> many1 namedType
@@ -237,17 +247,7 @@ fieldDefinition = FieldDefinition
<*> type_
argumentsDefinition :: Parser ArgumentsDefinition
-argumentsDefinition = inputValueDefinitions
-
-inputValueDefinitions :: Parser [InputValueDefinition]
-inputValueDefinitions = parens $ many1 inputValueDefinition
-
-inputValueDefinition :: Parser InputValueDefinition
-inputValueDefinition = InputValueDefinition
- <$> name
- <* tok ":"
- <*> type_
- <*> optional defaultValue
+argumentsDefinition = parens $ many1 inputValueDefinition
interfaceTypeDefinition :: Parser InterfaceTypeDefinition
interfaceTypeDefinition = InterfaceTypeDefinition
@@ -288,6 +288,16 @@ inputObjectTypeDefinition = InputObjectTypeDefinition
<*> name
<*> inputValueDefinitions
+inputValueDefinitions :: Parser [InputValueDefinition]
+inputValueDefinitions = braces $ many1 inputValueDefinition
+
+inputValueDefinition :: Parser InputValueDefinition
+inputValueDefinition = InputValueDefinition
+ <$> name
+ <* tok ":"
+ <*> type_
+ <*> optional defaultValue
+
typeExtensionDefinition :: Parser TypeExtensionDefinition
typeExtensionDefinition = TypeExtensionDefinition
<$ tok "extend"
@@ -320,8 +330,7 @@ optempty = option mempty
-- ** WhiteSpace
--
whiteSpace :: Parser ()
-whiteSpace = peekChar >>= \case
- Just c -> if isSpace c || c == ','
- then anyChar *> whiteSpace
- else when (c == '#') $ manyTill anyChar endOfLine *> whiteSpace
- _ -> return ()
+whiteSpace = peekChar >>= traverse_ (\c ->
+ if isSpace c || c == ','
+ then anyChar *> whiteSpace
+ else when (c == '#') $ manyTill anyChar endOfLine *> whiteSpace)