parse.lua 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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. print(name, value)
  54. return {
  55. name = name,
  56. value = value
  57. }
  58. end
  59. local function cObject(fields)
  60. return {
  61. kind = 'object',
  62. values = fields
  63. }
  64. end
  65. local function cAlias(name)
  66. return {
  67. kind = 'alias',
  68. name = name
  69. }
  70. end
  71. local function cArgument(name, value)
  72. return {
  73. kind = 'argument',
  74. name = name,
  75. value = value
  76. }
  77. end
  78. local function cField(...)
  79. local tokens = pack(...)
  80. local field = { kind = 'field' }
  81. for i = 1, #tokens do
  82. local key = tokens[i].kind
  83. if not key and tokens[i][1].kind == 'argument' then
  84. key = 'arguments'
  85. end
  86. field[key] = tokens[i]
  87. end
  88. return field
  89. end
  90. local function cSelectionSet(selections)
  91. return {
  92. kind = 'selectionSet',
  93. selections = selections
  94. }
  95. end
  96. local function cFragmentSpread(name)
  97. if name == 'on' then error('Fragment name cannot be "on"') end
  98. return {
  99. kind = 'fragmentSpread',
  100. name = name
  101. }
  102. end
  103. -- "Terminals"
  104. local rawName = space * R('az', 'AZ') * (P('_') + R('09') + R('az', 'AZ')) ^ 0
  105. local name = rawName / cName
  106. local alias = space * name * P(':') / cAlias
  107. local integerPart = P('-') ^ -1 * (P('0') + R('19') * R('09') ^ 0)
  108. local intValue = integerPart / cInt
  109. local fractionalPart = P('.') * R('09') ^ 1
  110. local exponentialPart = S('Ee') * S('+-') ^ -1 * R('09') ^ 1
  111. local floatValue = integerPart * (fractionalPart ^ -1 * exponentialPart ^ -1) / cFloat
  112. local booleanValue = (P('true') + P('false')) / cBoolean
  113. local stringValue = P('"') * C((P('\\"') + 1 - S('"\n')) ^ 0) * P('"') / cString
  114. local enumValue = (rawName - 'true' - 'false' - 'null') / cEnum
  115. local fragmentSpread = space * P('...') * name / cFragmentSpread
  116. -- Nonterminals
  117. local graphQL = P {
  118. 'input',
  119. input = space * V('selectionSet') * -1,
  120. selectionSet = space * P('{') * space * Ct(V('selection') ^ 0) * space * P('}') / cSelectionSet,
  121. selection = space * (V('field') + fragmentSpread),
  122. field = space * alias ^ -1 * name * V('arguments') ^ -1 * V('selectionSet') ^ -1 * comma / cField,
  123. argument = space * name * P(':') * V('value') * comma / cArgument,
  124. arguments = P('(') * Ct(V('argument') ^ 1) * P(')'),
  125. value = space * (V('objectValue') + V('listValue') + enumValue + stringValue + booleanValue + floatValue + intValue),
  126. listValue = P('[') * Ct((V('value') * comma) ^ 0) * P(']') / cList,
  127. objectFieldValue = C(rawName) * space * P(':') * space * V('value') * comma / cObjectField,
  128. objectValue = P('{') * space * Ct(V('objectFieldValue') ^ 0) * space * P('}') / cObject
  129. }
  130. return function(str)
  131. return graphQL:match(str)
  132. end