parse.lua 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. local lpeg = require 'lpeg'
  2. 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
  3. local function pack(...)
  4. return { n = select('#', ...), ... }
  5. end
  6. -- Utility
  7. local space = S(' \t\r\n') ^ 0
  8. local comma = P(',') ^ 0
  9. local function cName(name)
  10. if #name == 0 then return nil end
  11. return {
  12. kind = 'name',
  13. value = name
  14. }
  15. end
  16. local function cInt(value)
  17. return {
  18. kind = 'int',
  19. value = value
  20. }
  21. end
  22. local function cFloat(value)
  23. return {
  24. kind = 'float',
  25. value = value
  26. }
  27. end
  28. local function cBoolean(value)
  29. return {
  30. kind = 'boolean',
  31. value = value
  32. }
  33. end
  34. local function cString(value)
  35. return {
  36. kind = 'string',
  37. value = value
  38. }
  39. end
  40. local function cEnum(value)
  41. return {
  42. kind = 'enum',
  43. value = value
  44. }
  45. end
  46. local function cList(value)
  47. return {
  48. kind = 'list',
  49. values = value
  50. }
  51. end
  52. local function cObjectField(name, value)
  53. return {
  54. name = name,
  55. value = value
  56. }
  57. end
  58. local function cObject(fields)
  59. return {
  60. kind = 'object',
  61. values = fields
  62. }
  63. end
  64. local function cAlias(name)
  65. return {
  66. kind = 'alias',
  67. name = name
  68. }
  69. end
  70. local function cArgument(name, value)
  71. return {
  72. kind = 'argument',
  73. name = name,
  74. value = value
  75. }
  76. end
  77. local function cField(...)
  78. local tokens = pack(...)
  79. local field = { kind = 'field' }
  80. for i = 1, #tokens do
  81. local key = tokens[i].kind
  82. if not key then
  83. if tokens[i][1].kind == 'argument' then
  84. key = 'arguments'
  85. elseif tokens[i][1].kind == 'directive' then
  86. key = 'directives'
  87. end
  88. end
  89. field[key] = tokens[i]
  90. end
  91. return field
  92. end
  93. local function cSelectionSet(selections)
  94. return {
  95. kind = 'selectionSet',
  96. selections = selections
  97. }
  98. end
  99. local function cFragmentSpread(name)
  100. return {
  101. kind = 'fragmentSpread',
  102. name = name
  103. }
  104. end
  105. local function cOperation(...)
  106. local args = pack(...)
  107. if args[1].kind == 'selectionSet' then
  108. return {
  109. kind = 'operation',
  110. operation = 'query',
  111. selectionSet = args[1]
  112. }
  113. else
  114. local result = {
  115. kind = 'operation',
  116. operation = args[1]
  117. }
  118. for i = 2, #args do
  119. local key = args[i].kind
  120. if not key then
  121. if args[i][1].kind == 'variableDefinition' then
  122. key = 'variableDefinitions'
  123. elseif args[i][1].kind == 'directive' then
  124. key = 'directives'
  125. end
  126. end
  127. result[key] = args[i]
  128. end
  129. return result
  130. end
  131. end
  132. local function cDocument(definitions)
  133. return {
  134. kind = 'document',
  135. definitions = definitions
  136. }
  137. end
  138. local function cFragmentDefinition(name, typeCondition, selectionSet)
  139. return {
  140. kind = 'fragmentDefinition',
  141. name = name,
  142. typeCondition = typeCondition,
  143. selectionSet = selectionSet
  144. }
  145. end
  146. local function cNamedType(name)
  147. return {
  148. kind = 'namedType',
  149. name = name
  150. }
  151. end
  152. local function cListType(type)
  153. return {
  154. kind = 'listType',
  155. type = type
  156. }
  157. end
  158. local function cNonNullType(type)
  159. return {
  160. kind = 'nonNullType',
  161. type = type
  162. }
  163. end
  164. local function cInlineFragment(...)
  165. local args = pack(...)
  166. if #args == 2 then
  167. return {
  168. kind = 'inlineFragment',
  169. typeCondition = args[1],
  170. selectionSet = args[2]
  171. }
  172. elseif #args == 1 then
  173. return {
  174. kind = 'inlineFragment',
  175. selectionSet = args[1]
  176. }
  177. end
  178. end
  179. local function cVariable(name)
  180. return {
  181. kind = 'variable',
  182. name = name
  183. }
  184. end
  185. local function cVariableDefinition(variable, type, defaultValue)
  186. return {
  187. kind = 'variableDefinition',
  188. variable = variable,
  189. type = type,
  190. defaultValue = defaultValue
  191. }
  192. end
  193. local function cDirective(name, arguments)
  194. return {
  195. kind = 'directive',
  196. name = name,
  197. arguments = arguments
  198. }
  199. end
  200. -- "Terminals"
  201. local rawName = R('az', 'AZ') * (P('_') + R('09') + R('az', 'AZ')) ^ 0
  202. local name = rawName / cName
  203. local alias = space * name * P(':') * space / cAlias
  204. local integerPart = P('-') ^ -1 * (P('0') + R('19') * R('09') ^ 0)
  205. local intValue = integerPart / cInt
  206. local fractionalPart = P('.') * R('09') ^ 1
  207. local exponentialPart = S('Ee') * S('+-') ^ -1 * R('09') ^ 1
  208. local floatValue = integerPart * (fractionalPart + exponentialPart + (fractionalPart * exponentialPart)) / cFloat
  209. local booleanValue = (P('true') + P('false')) / cBoolean
  210. local stringValue = P('"') * C((P('\\"') + 1 - S('"\n')) ^ 0) * P('"') / cString
  211. local enumValue = (rawName - 'true' - 'false' - 'null') / cEnum
  212. local fragmentName = (rawName - 'on') / cName
  213. local fragmentSpread = space * P('...') * fragmentName / cFragmentSpread
  214. local operationType = C(P('query') + P('mutation'))
  215. local variable = space * P('$') * name / cVariable
  216. -- Nonterminals
  217. local graphQL = P {
  218. 'document',
  219. document = space * Ct((V('definition') * comma * space) ^ 0) / cDocument * -1,
  220. definition = V('operation') + V('fragmentDefinition'),
  221. operation = (operationType * space * name ^ -1 * V('variableDefinitions') ^ -1 * V('directives') ^ -1 * V('selectionSet') + V('selectionSet')) / cOperation,
  222. fragmentDefinition = P('fragment') * space * fragmentName * space * V('typeCondition') * space * V('selectionSet') / cFragmentDefinition,
  223. inlineFragment = P('...') * space * V('typeCondition') ^ -1 * V('selectionSet') / cInlineFragment,
  224. selectionSet = space * P('{') * space * Ct(V('selection') ^ 0) * space * P('}') / cSelectionSet,
  225. selection = space * (V('field') + fragmentSpread + V('inlineFragment')),
  226. field = space * alias ^ -1 * name * V('arguments') ^ -1 * V('directives') ^ -1 * V('selectionSet') ^ -1 * comma / cField,
  227. argument = space * name * P(':') * V('value') * comma / cArgument,
  228. arguments = P('(') * Ct(V('argument') ^ 1) * P(')'),
  229. value = space * (variable + V('objectValue') + V('listValue') + enumValue + stringValue + booleanValue + floatValue + intValue),
  230. listValue = P('[') * Ct((V('value') * comma) ^ 0) * P(']') / cList,
  231. objectFieldValue = C(rawName) * space * P(':') * space * V('value') * comma / cObjectField,
  232. objectValue = P('{') * space * Ct(V('objectFieldValue') ^ 0) * space * P('}') / cObject,
  233. type = V('nonNullType') + V('listType') + V('namedType'),
  234. namedType = name / cNamedType,
  235. listType = P('[') * space * V('type') * space * P(']') / cListType,
  236. nonNullType = (V('namedType') + V('listType')) * P('!') / cNonNullType,
  237. typeCondition = P('on') * space * V('namedType'),
  238. variableDefinition = space * variable * space * P(':') * space * V('type') * (space * P('=') * V('value')) ^ -1 * comma * space / cVariableDefinition,
  239. variableDefinitions = P('(') * Ct(V('variableDefinition') ^ 1) * P(')'),
  240. directive = P('@') * name * V('arguments') ^ -1 / cDirective,
  241. directives = space * Ct((V('directive') * comma * space) ^ 1) * space
  242. }
  243. return function(str)
  244. assert(type(str) == 'string', 'parser expects a string')
  245. return graphQL:match(str)
  246. end