123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- local lpeg = require 'lpeg'
- local P, R, S, V, C, Ct, Cmt, Cg, Cc, Cf = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cc, lpeg.Cf
- local function pack(...)
- return { n = select('#', ...), ... }
- end
- -- Utility
- local space = S(' \t\r\n') ^ 0
- local comma = P(',') ^ 0
- local function cName(name)
- if #name == 0 then return nil end
- return {
- kind = 'name',
- value = name
- }
- end
- local function cInt(value)
- return {
- kind = 'int',
- value = value
- }
- end
- local function cFloat(value)
- return {
- kind = 'float',
- value = value
- }
- end
- local function cBoolean(value)
- return {
- kind = 'boolean',
- value = value
- }
- end
- local function cString(value)
- return {
- kind = 'string',
- value = value
- }
- end
- local function cEnum(value)
- return {
- kind = 'enum',
- value = value
- }
- end
- local function cList(value)
- return {
- kind = 'list',
- values = value
- }
- end
- local function cObjectField(name, value)
- return {
- name = name,
- value = value
- }
- end
- local function cObject(fields)
- return {
- kind = 'object',
- values = fields
- }
- end
- local function cAlias(name)
- return {
- kind = 'alias',
- name = name
- }
- end
- local function cArgument(name, value)
- return {
- kind = 'argument',
- name = name,
- value = value
- }
- end
- local function cField(...)
- local tokens = pack(...)
- local field = { kind = 'field' }
- for i = 1, #tokens do
- local key = tokens[i].kind
- if not key then
- if tokens[i][1].kind == 'argument' then
- key = 'arguments'
- elseif tokens[i][1].kind == 'directive' then
- key = 'directives'
- end
- end
- field[key] = tokens[i]
- end
- return field
- end
- local function cSelectionSet(selections)
- return {
- kind = 'selectionSet',
- selections = selections
- }
- end
- local function cFragmentSpread(name)
- return {
- kind = 'fragmentSpread',
- name = name
- }
- end
- local function cOperation(...)
- local args = pack(...)
- if args[1].kind == 'selectionSet' then
- return {
- kind = 'operation',
- operation = 'query',
- selectionSet = args[1]
- }
- else
- local result = {
- kind = 'operation',
- operation = args[1]
- }
- for i = 2, #args do
- local key = args[i].kind
- if not key then
- if args[i][1].kind == 'variableDefinition' then
- key = 'variableDefinitions'
- elseif args[i][1].kind == 'directive' then
- key = 'directives'
- end
- end
- result[key] = args[i]
- end
- return result
- end
- end
- local function cDocument(definitions)
- return {
- kind = 'document',
- definitions = definitions
- }
- end
- local function cFragmentDefinition(name, typeCondition, selectionSet)
- return {
- kind = 'fragmentDefinition',
- name = name,
- typeCondition = typeCondition,
- selectionSet = selectionSet
- }
- end
- local function cNamedType(name)
- return {
- kind = 'namedType',
- name = name
- }
- end
- local function cListType(type)
- return {
- kind = 'listType',
- type = type
- }
- end
- local function cNonNullType(type)
- return {
- kind = 'nonNullType',
- type = type
- }
- end
- local function cInlineFragment(...)
- local args = pack(...)
- if #args == 2 then
- return {
- kind = 'inlineFragment',
- typeCondition = args[1],
- selectionSet = args[2]
- }
- elseif #args == 1 then
- return {
- kind = 'inlineFragment',
- selectionSet = args[1]
- }
- end
- end
- local function cVariable(name)
- return {
- kind = 'variable',
- name = name
- }
- end
- local function cVariableDefinition(variable, type, defaultValue)
- return {
- kind = 'variableDefinition',
- variable = variable,
- type = type,
- defaultValue = defaultValue
- }
- end
- local function cDirective(name, arguments)
- return {
- kind = 'directive',
- name = name,
- arguments = arguments
- }
- end
- -- "Terminals"
- local rawName = R('az', 'AZ') * (P('_') + R('09') + R('az', 'AZ')) ^ 0
- local name = rawName / cName
- local alias = space * name * P(':') * space / cAlias
- local integerPart = P('-') ^ -1 * (P('0') + R('19') * R('09') ^ 0)
- local intValue = integerPart / cInt
- local fractionalPart = P('.') * R('09') ^ 1
- local exponentialPart = S('Ee') * S('+-') ^ -1 * R('09') ^ 1
- local floatValue = integerPart * (fractionalPart + exponentialPart + (fractionalPart * exponentialPart)) / cFloat
- local booleanValue = (P('true') + P('false')) / cBoolean
- local stringValue = P('"') * C((P('\\"') + 1 - S('"\n')) ^ 0) * P('"') / cString
- local enumValue = (rawName - 'true' - 'false' - 'null') / cEnum
- local fragmentName = (rawName - 'on') / cName
- local fragmentSpread = space * P('...') * fragmentName / cFragmentSpread
- local operationType = C(P('query') + P('mutation'))
- local variable = space * P('$') * name / cVariable
- -- Nonterminals
- local graphQL = P {
- 'document',
- document = space * Ct((V('definition') * comma * space) ^ 0) / cDocument * -1,
- definition = V('operation') + V('fragmentDefinition'),
- operation = (operationType * space * name ^ -1 * V('variableDefinitions') ^ -1 * V('directives') ^ -1 * V('selectionSet') + V('selectionSet')) / cOperation,
- fragmentDefinition = P('fragment') * space * fragmentName * space * V('typeCondition') * space * V('selectionSet') / cFragmentDefinition,
- inlineFragment = P('...') * space * V('typeCondition') ^ -1 * V('selectionSet') / cInlineFragment,
- selectionSet = space * P('{') * space * Ct(V('selection') ^ 0) * space * P('}') / cSelectionSet,
- selection = space * (V('field') + fragmentSpread + V('inlineFragment')),
- field = space * alias ^ -1 * name * V('arguments') ^ -1 * V('directives') ^ -1 * V('selectionSet') ^ -1 * comma / cField,
- argument = space * name * P(':') * V('value') * comma / cArgument,
- arguments = P('(') * Ct(V('argument') ^ 1) * P(')'),
- value = space * (variable + V('objectValue') + V('listValue') + enumValue + stringValue + booleanValue + floatValue + intValue),
- listValue = P('[') * Ct((V('value') * comma) ^ 0) * P(']') / cList,
- objectFieldValue = C(rawName) * space * P(':') * space * V('value') * comma / cObjectField,
- objectValue = P('{') * space * Ct(V('objectFieldValue') ^ 0) * space * P('}') / cObject,
- type = V('nonNullType') + V('listType') + V('namedType'),
- namedType = name / cNamedType,
- listType = P('[') * space * V('type') * space * P(']') / cListType,
- nonNullType = (V('namedType') + V('listType')) * P('!') / cNonNullType,
- typeCondition = P('on') * space * V('namedType'),
- variableDefinition = space * variable * space * P(':') * space * V('type') * (space * P('=') * V('value')) ^ -1 * comma * space / cVariableDefinition,
- variableDefinitions = P('(') * Ct(V('variableDefinition') ^ 1) * P(')'),
- directive = P('@') * name * V('arguments') ^ -1 / cDirective,
- directives = space * Ct((V('directive') * comma * space) ^ 1) * space
- }
- return function(str)
- assert(type(str) == 'string', 'parser expects a string')
- return graphQL:match(str)
- end
|