parse.lua 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. if name == 'on' then error('Fragment name cannot be "on"') end
  97. return {
  98. kind = 'fragmentSpread',
  99. name = name
  100. }
  101. end
  102. local function cOperation(...)
  103. local args = pack(...)
  104. if args[1].kind == 'selectionSet' then
  105. return {
  106. kind = 'operation',
  107. operation = 'query',
  108. selectionSet = args[1]
  109. }
  110. else
  111. local result = {
  112. kind = 'operation',
  113. operation = args[1]
  114. }
  115. if #args >= 3 then
  116. result.name, result.selectionSet = unpack(args, 2, 3)
  117. else
  118. result.selectionSet = args[2]
  119. end
  120. return result
  121. end
  122. end
  123. local function cDocument(definitions)
  124. return {
  125. kind = 'document',
  126. definitions = definitions
  127. }
  128. end
  129. -- "Terminals"
  130. local rawName = space * R('az', 'AZ') * (P('_') + R('09') + R('az', 'AZ')) ^ 0
  131. local name = rawName / cName
  132. local alias = space * name * P(':') / cAlias
  133. local integerPart = P('-') ^ -1 * (P('0') + R('19') * R('09') ^ 0)
  134. local intValue = integerPart / cInt
  135. local fractionalPart = P('.') * R('09') ^ 1
  136. local exponentialPart = S('Ee') * S('+-') ^ -1 * R('09') ^ 1
  137. local floatValue = integerPart * (fractionalPart ^ -1 * exponentialPart ^ -1) / cFloat
  138. local booleanValue = (P('true') + P('false')) / cBoolean
  139. local stringValue = P('"') * C((P('\\"') + 1 - S('"\n')) ^ 0) * P('"') / cString
  140. local enumValue = (rawName - 'true' - 'false' - 'null') / cEnum
  141. local fragmentSpread = space * P('...') * name / cFragmentSpread
  142. local operationType = C(P('query') + P('mutation'))
  143. -- Nonterminals
  144. local graphQL = P {
  145. 'document',
  146. document = space * Ct((V('definition') * comma * space) ^ 0) / cDocument * -1,
  147. definition = V('operation'),
  148. operation = (operationType * space * name ^ -1 * V('selectionSet') + V('selectionSet')) / cOperation,
  149. selectionSet = space * P('{') * space * Ct(V('selection') ^ 0) * space * P('}') / cSelectionSet,
  150. selection = space * (V('field') + fragmentSpread),
  151. field = space * alias ^ -1 * name * V('arguments') ^ -1 * V('selectionSet') ^ -1 * comma / cField,
  152. argument = space * name * P(':') * V('value') * comma / cArgument,
  153. arguments = P('(') * Ct(V('argument') ^ 1) * P(')'),
  154. value = space * (V('objectValue') + V('listValue') + enumValue + stringValue + booleanValue + floatValue + intValue),
  155. listValue = P('[') * Ct((V('value') * comma) ^ 0) * P(']') / cList,
  156. objectFieldValue = C(rawName) * space * P(':') * space * V('value') * comma / cObjectField,
  157. objectValue = P('{') * space * Ct(V('objectFieldValue') ^ 0) * space * P('}') / cObject
  158. }
  159. return function(str)
  160. return graphQL:match(str)
  161. end