summaryrefslogtreecommitdiff
path: root/Data/GraphQL/Parser.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Data/GraphQL/Parser.hs')
-rw-r--r--Data/GraphQL/Parser.hs166
1 files changed, 51 insertions, 115 deletions
diff --git a/Data/GraphQL/Parser.hs b/Data/GraphQL/Parser.hs
index 35f42d5..03083d5 100644
--- a/Data/GraphQL/Parser.hs
+++ b/Data/GraphQL/Parser.hs
@@ -1,50 +1,22 @@
-{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}
--- | This module defines a parser for @GraphQL@ request documents.
+{-# LANGUAGE OverloadedStrings #-}
module Data.GraphQL.Parser where
-import Prelude hiding (takeWhile)
-
-import Control.Applicative ((<|>), Alternative, empty, many, optional)
-import Control.Monad (when)
-import Data.Char (isDigit, isSpace)
-import Data.Foldable (traverse_)
-import Data.Monoid ((<>))
-import Data.List.NonEmpty (NonEmpty((:|)))
-import Data.Scientific (floatingOrInteger, scientific, toBoundedInteger)
-
-import Data.Text (Text, append)
-import Data.Attoparsec.Combinator (lookAhead)
-import Data.Attoparsec.Text
- ( Parser
- , (<?>)
- , anyChar
- , endOfLine
- , inClass
- , many1
- , manyTill
- , option
- , peekChar
- , takeWhile
- , takeWhile1
- )
-import qualified Data.Attoparsec.Text as Attoparsec (scientific)
-
+import Control.Applicative ( Alternative(..)
+ , optional
+ )
import Data.GraphQL.AST
-
--- * Name
-
-name :: Parser Name
-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
+import Language.GraphQL.Lexer
+import Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.Text as T
+import Text.Megaparsec ( lookAhead
+ , option
+ , try
+ , (<?>)
+ )
document :: Parser Document
-document = whiteSpace *> manyNE definition
+document = spaceConsumer >> lexeme (manyNE definition)
definition :: Parser Definition
definition = DefinitionOperation <$> operationDefinition
@@ -61,9 +33,9 @@ operationDefinition = OperationSelectionSet <$> selectionSet
<?> "operationDefinition error"
operationType :: Parser OperationType
-operationType = Query <$ tok "query"
- <|> Mutation <$ tok "mutation"
- <?> "operationType error"
+operationType = Query <$ symbol "query"
+ <|> Mutation <$ symbol "mutation"
+ <?> "operationType error"
-- * SelectionSet
@@ -71,11 +43,11 @@ selectionSet :: Parser SelectionSet
selectionSet = braces $ manyNE selection
selectionSetOpt :: Parser SelectionSetOpt
-selectionSetOpt = braces $ many1 selection
+selectionSetOpt = braces $ some selection
selection :: Parser Selection
selection = SelectionField <$> field
- <|> SelectionFragmentSpread <$> fragmentSpread
+ <|> try (SelectionFragmentSpread <$> fragmentSpread)
<|> SelectionInlineFragment <$> inlineFragment
<?> "selection error!"
@@ -89,160 +61,124 @@ field = Field <$> optional alias
<*> opt selectionSetOpt
alias :: Parser Alias
-alias = name <* tok ":"
+alias = try $ name <* colon
-- * Arguments
arguments :: Parser Arguments
-arguments = parens $ many1 argument
+arguments = parens $ some argument
argument :: Parser Argument
-argument = Argument <$> name <* tok ":" <*> value
+argument = Argument <$> name <* colon <*> value
-- * Fragments
fragmentSpread :: Parser FragmentSpread
-fragmentSpread = FragmentSpread <$ tok "..."
+fragmentSpread = FragmentSpread <$ spread
<*> fragmentName
<*> opt directives
inlineFragment :: Parser InlineFragment
-inlineFragment = InlineFragment <$ tok "..."
+inlineFragment = InlineFragment <$ spread
<*> optional typeCondition
<*> opt directives
<*> selectionSet
fragmentDefinition :: Parser FragmentDefinition
fragmentDefinition = FragmentDefinition
- <$ tok "fragment"
+ <$ symbol "fragment"
<*> name
<*> typeCondition
<*> opt directives
<*> selectionSet
fragmentName :: Parser FragmentName
-fragmentName = but (tok "on") *> name
+fragmentName = but (symbol "on") *> name
typeCondition :: Parser TypeCondition
-typeCondition = tok "on" *> name
+typeCondition = symbol "on" *> name
-- * Input Values
value :: Parser Value
value = ValueVariable <$> variable
- <|> tok floatOrInt32Value
+ <|> ValueFloat <$> try float
+ <|> ValueInt <$> integer
<|> ValueBoolean <$> booleanValue
- <|> ValueNull <$ tok "null"
- <|> ValueString <$> stringValue
- <|> ValueEnum <$> enumValue
+ <|> ValueNull <$ symbol "null"
+ <|> ValueString <$> string
+ <|> ValueString <$> blockString
+ <|> ValueEnum <$> try enumValue
<|> ValueList <$> listValue
<|> ValueObject <$> objectValue
<?> "value error!"
where
booleanValue :: Parser Bool
- booleanValue = True <$ tok "true"
- <|> False <$ tok "false"
-
- floatOrInt32Value :: Parser Value
- floatOrInt32Value =
- Attoparsec.scientific >>=
- either (pure . ValueFloat)
- (maybe (fail "Integer value is out of range.")
- (pure . ValueInt)
- . toBoundedInteger . (`scientific` 0))
- . floatingOrInteger
-
- -- TODO: Escape characters. Look at `jsstring_` in aeson package.
- stringValue :: Parser Text
- stringValue = quotes (takeWhile (/= '"'))
+ booleanValue = True <$ symbol "true"
+ <|> False <$ symbol "false"
enumValue :: Parser Name
- enumValue = but (tok "true") *> but (tok "false") *> but (tok "null") *> name
+ enumValue = but (symbol "true") *> but (symbol "false") *> but (symbol "null") *> name
listValue :: Parser [Value]
- listValue = brackets $ many1 value
+ listValue = brackets $ some value
objectValue :: Parser [ObjectField]
- objectValue = braces $ many1 objectField
+ objectValue = braces $ some objectField
objectField :: Parser ObjectField
-objectField = ObjectField <$> name <* tok ":" <*> value
+objectField = ObjectField <$> name <* symbol ":" <*> value
-- * Variables
variableDefinitions :: Parser VariableDefinitions
-variableDefinitions = parens $ many1 variableDefinition
+variableDefinitions = parens $ some variableDefinition
variableDefinition :: Parser VariableDefinition
variableDefinition = VariableDefinition <$> variable
- <* tok ":"
+ <* colon
<*> type_
<*> optional defaultValue
-
variable :: Parser Variable
-variable = tok "$" *> name
+variable = dollar *> name
defaultValue :: Parser DefaultValue
-defaultValue = tok "=" *> value
+defaultValue = equals *> value
-- * Input Types
type_ :: Parser Type
-type_ = TypeNamed <$> name <* but "!"
- <|> TypeList <$> brackets type_
- <|> TypeNonNull <$> nonNullType
+type_ = try (TypeNamed <$> name <* but "!")
+ <|> TypeList <$> brackets type_
+ <|> TypeNonNull <$> nonNullType
<?> "type_ error!"
nonNullType :: Parser NonNullType
-nonNullType = NonNullTypeNamed <$> name <* tok "!"
- <|> NonNullTypeList <$> brackets type_ <* tok "!"
+nonNullType = NonNullTypeNamed <$> name <* bang
+ <|> NonNullTypeList <$> brackets type_ <* bang
<?> "nonNullType error!"
-- * Directives
directives :: Parser Directives
-directives = many1 directive
+directives = some directive
directive :: Parser Directive
directive = Directive
- <$ tok "@"
+ <$ at
<*> name
<*> opt arguments
-- * Internal
-tok :: Parser a -> Parser a
-tok p = p <* whiteSpace
-
-parens :: Parser a -> Parser a
-parens = between "(" ")"
-
-braces :: Parser a -> Parser a
-braces = between "{" "}"
-
-quotes :: Parser a -> Parser a
-quotes = between "\"" "\""
-
-brackets :: Parser a -> Parser a
-brackets = between "[" "]"
-
-between :: Parser Text -> Parser Text -> Parser a -> Parser a
-between open close p = tok open *> p <* tok close
-
opt :: Monoid a => Parser a -> Parser a
opt = option mempty
-- Hack to reverse parser success
but :: Parser a -> Parser ()
but pn = False <$ lookAhead pn <|> pure True >>= \case
- False -> empty
- True -> pure ()
+ False -> empty
+ True -> pure ()
manyNE :: Alternative f => f a -> f (NonEmpty a)
manyNE p = (:|) <$> p <*> many p
-
-whiteSpace :: Parser ()
-whiteSpace = peekChar >>= traverse_ (\c ->
- if isSpace c || c == ','
- then anyChar *> whiteSpace
- else when (c == '#') $ manyTill anyChar endOfLine *> whiteSpace)