parse.lua 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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 and tokens[i][1].kind == 'argument' then
  83. key = 'arguments'
  84. end
  85. field[key] = tokens[i]
  86. end
  87. return field
  88. end
  89. local function cSelectionSet(selections)
  90. return {
  91. kind = 'selectionSet',
  92. selections = selections
  93. }
  94. end
  95. local function cFragmentSpread(name)
  96. return {
  97. kind = 'fragmentSpread',
  98. name = name
  99. }
  100. end
  101. local function cOperation(...)
  102. local args = pack(...)
  103. if args[1].kind == 'selectionSet' then
  104. return {
  105. kind = 'operation',
  106. operation = 'query',
  107. selectionSet = args[1]
  108. }
  109. else
  110. local result = {
  111. kind = 'operation',
  112. operation = args[1]
  113. }
  114. if #args >= 3 then
  115. result.name, result.selectionSet = unpack(args, 2, 3)
  116. else
  117. result.selectionSet = args[2]
  118. end
  119. return result
  120. end
  121. end
  122. local function cDocument(definitions)
  123. return {
  124. kind = 'document',
  125. definitions = definitions
  126. }
  127. end
  128. local function cFragmentDefinition(name, typeCondition, selectionSet)
  129. return {
  130. kind = 'fragmentDefinition',
  131. name = name,
  132. typeCondition = typeCondition,
  133. selectionSet = selectionSet
  134. }
  135. end
  136. local function cNamedType(name)
  137. return {
  138. kind = 'namedType',
  139. name = name
  140. }
  141. end
  142. local function cInlineFragment(...)
  143. local args = pack(...)
  144. if #args == 2 then
  145. return {
  146. kind = 'inlineFragment',
  147. typeCondition = args[1],
  148. selectionSet = args[2]
  149. }
  150. elseif #args == 1 then
  151. return {
  152. kind = 'inlineFragment',
  153. selectionSet = args[1]
  154. }
  155. end
  156. end
  157. -- "Terminals"
  158. local rawName = R('az', 'AZ') * (P('_') + R('09') + R('az', 'AZ')) ^ 0
  159. local name = rawName / cName
  160. local alias = space * name * P(':') / cAlias
  161. local integerPart = P('-') ^ -1 * (P('0') + R('19') * R('09') ^ 0)
  162. local intValue = integerPart / cInt
  163. local fractionalPart = P('.') * R('09') ^ 1
  164. local exponentialPart = S('Ee') * S('+-') ^ -1 * R('09') ^ 1
  165. local floatValue = integerPart * (fractionalPart ^ -1 * exponentialPart ^ -1) / cFloat
  166. local booleanValue = (P('true') + P('false')) / cBoolean
  167. local stringValue = P('"') * C((P('\\"') + 1 - S('"\n')) ^ 0) * P('"') / cString
  168. local enumValue = (rawName - 'true' - 'false' - 'null') / cEnum
  169. local typeCondition = P('on') * space * name / cNamedType
  170. local fragmentName = (rawName - 'on') / cName
  171. local fragmentSpread = space * P('...') * fragmentName / cFragmentSpread
  172. local operationType = C(P('query') + P('mutation'))
  173. -- Nonterminals
  174. local graphQL = P {
  175. 'document',
  176. document = space * Ct((V('definition') * comma * space) ^ 0) / cDocument * -1,
  177. definition = V('operation') + V('fragmentDefinition'),
  178. operation = (operationType * space * name ^ -1 * V('selectionSet') + V('selectionSet')) / cOperation,
  179. fragmentDefinition = P('fragment') * space * fragmentName * space * typeCondition * space * V('selectionSet') / cFragmentDefinition,
  180. inlineFragment = P('...') * space * typeCondition ^ -1 * V('selectionSet') / cInlineFragment,
  181. selectionSet = space * P('{') * space * Ct(V('selection') ^ 0) * space * P('}') / cSelectionSet,
  182. selection = space * (V('field') + fragmentSpread + V('inlineFragment')),
  183. field = space * alias ^ -1 * name * V('arguments') ^ -1 * V('selectionSet') ^ -1 * comma / cField,
  184. argument = space * name * P(':') * V('value') * comma / cArgument,
  185. arguments = P('(') * Ct(V('argument') ^ 1) * P(')'),
  186. value = space * (V('objectValue') + V('listValue') + enumValue + stringValue + booleanValue + floatValue + intValue),
  187. listValue = P('[') * Ct((V('value') * comma) ^ 0) * P(']') / cList,
  188. objectFieldValue = C(rawName) * space * P(':') * space * V('value') * comma / cObjectField,
  189. objectValue = P('{') * space * Ct(V('objectFieldValue') ^ 0) * space * P('}') / cObject
  190. }
  191. return function(str)
  192. return graphQL:match(str)
  193. end