rails-graphql 0.2.0 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (297) hide show
  1. checksums.yaml +4 -4
  2. data/ext/console.rb +18 -0
  3. data/ext/extconf.h +3 -0
  4. data/ext/extconf.rb +1 -54
  5. data/ext/gql_parser.c +646 -0
  6. data/ext/shared.c +482 -0
  7. data/ext/shared.h +177 -0
  8. data/lib/gql_parser.so +0 -0
  9. data/lib/rails/graphql/adapters/mysql_adapter.rb +59 -0
  10. data/lib/rails/graphql/adapters/pg_adapter.rb +25 -22
  11. data/lib/rails/graphql/adapters/sqlite_adapter.rb +17 -14
  12. data/lib/rails/graphql/alternative/field_set.rb +36 -0
  13. data/lib/rails/graphql/alternative/mutation.rb +17 -0
  14. data/lib/rails/graphql/alternative/query.rb +93 -0
  15. data/lib/rails/graphql/alternative/subscription.rb +17 -0
  16. data/lib/rails/graphql/alternative.rb +20 -0
  17. data/lib/rails/graphql/argument.rb +21 -24
  18. data/lib/rails/graphql/callback.rb +24 -9
  19. data/lib/rails/graphql/collectors/hash_collector.rb +14 -6
  20. data/lib/rails/graphql/collectors/idented_collector.rb +10 -7
  21. data/lib/rails/graphql/collectors/json_collector.rb +22 -17
  22. data/lib/rails/graphql/collectors.rb +4 -4
  23. data/lib/rails/graphql/config.rb +130 -15
  24. data/lib/rails/graphql/directive/cached_directive.rb +33 -0
  25. data/lib/rails/graphql/directive/deprecated_directive.rb +10 -10
  26. data/lib/rails/graphql/directive/include_directive.rb +5 -4
  27. data/lib/rails/graphql/directive/skip_directive.rb +5 -4
  28. data/lib/rails/graphql/directive.rb +118 -63
  29. data/lib/rails/graphql/errors.rb +33 -4
  30. data/lib/rails/graphql/event.rb +16 -5
  31. data/lib/rails/graphql/field/authorized_field.rb +19 -3
  32. data/lib/rails/graphql/field/input_field.rb +11 -10
  33. data/lib/rails/graphql/field/mutation_field.rb +42 -7
  34. data/lib/rails/graphql/field/output_field.rb +102 -13
  35. data/lib/rails/graphql/field/proxied_field.rb +31 -22
  36. data/lib/rails/graphql/field/resolved_field.rb +26 -24
  37. data/lib/rails/graphql/field/scoped_config.rb +10 -4
  38. data/lib/rails/graphql/field/subscription_field.rb +140 -0
  39. data/lib/rails/graphql/field/typed_field.rb +43 -22
  40. data/lib/rails/graphql/field.rb +70 -56
  41. data/lib/rails/graphql/global_id.rb +85 -0
  42. data/lib/rails/graphql/helpers/attribute_delegator.rb +5 -5
  43. data/lib/rails/graphql/helpers/inherited_collection/array.rb +50 -0
  44. data/lib/rails/graphql/helpers/inherited_collection/base.rb +43 -0
  45. data/lib/rails/graphql/helpers/inherited_collection/hash.rb +87 -0
  46. data/lib/rails/graphql/helpers/inherited_collection.rb +25 -76
  47. data/lib/rails/graphql/helpers/instantiable.rb +15 -0
  48. data/lib/rails/graphql/helpers/leaf_from_ar.rb +7 -7
  49. data/lib/rails/graphql/helpers/registerable.rb +44 -62
  50. data/lib/rails/graphql/helpers/unregisterable.rb +16 -0
  51. data/lib/rails/graphql/helpers/with_arguments.rb +31 -27
  52. data/lib/rails/graphql/helpers/with_assignment.rb +10 -8
  53. data/lib/rails/graphql/helpers/with_callbacks.rb +25 -8
  54. data/lib/rails/graphql/helpers/with_description.rb +71 -0
  55. data/lib/rails/graphql/helpers/with_directives.rb +54 -30
  56. data/lib/rails/graphql/helpers/with_events.rb +21 -23
  57. data/lib/rails/graphql/helpers/with_fields.rb +76 -22
  58. data/lib/rails/graphql/helpers/with_global_id.rb +22 -0
  59. data/lib/rails/graphql/helpers/with_name.rb +43 -0
  60. data/lib/rails/graphql/helpers/with_namespace.rb +7 -4
  61. data/lib/rails/graphql/helpers/with_owner.rb +8 -7
  62. data/lib/rails/graphql/helpers/with_schema_fields.rb +137 -55
  63. data/lib/rails/graphql/helpers/with_validator.rb +9 -9
  64. data/lib/rails/graphql/helpers.rb +10 -3
  65. data/lib/rails/graphql/introspection.rb +43 -36
  66. data/lib/rails/graphql/railtie.rb +88 -33
  67. data/lib/rails/graphql/railties/base_generator.rb +3 -9
  68. data/lib/rails/graphql/railties/channel.rb +157 -0
  69. data/lib/rails/graphql/railties/controller.rb +62 -17
  70. data/lib/rails/graphql/railties/controller_runtime.rb +5 -5
  71. data/lib/rails/graphql/railties/log_subscriber.rb +81 -14
  72. data/lib/rails/graphql/request/arguments.rb +24 -49
  73. data/lib/rails/graphql/request/backtrace.rb +191 -0
  74. data/lib/rails/graphql/request/component/field.rb +86 -65
  75. data/lib/rails/graphql/request/component/fragment.rb +72 -24
  76. data/lib/rails/graphql/request/component/operation/subscription.rb +164 -4
  77. data/lib/rails/graphql/request/component/operation.rb +63 -31
  78. data/lib/rails/graphql/request/component/spread.rb +68 -25
  79. data/lib/rails/graphql/request/component/typename.rb +27 -12
  80. data/lib/rails/graphql/request/component.rb +75 -36
  81. data/lib/rails/graphql/request/context.rb +18 -8
  82. data/lib/rails/graphql/request/errors.rb +16 -6
  83. data/lib/rails/graphql/request/event.rb +19 -8
  84. data/lib/rails/graphql/request/helpers/directives.rb +68 -27
  85. data/lib/rails/graphql/request/helpers/selection_set.rb +51 -25
  86. data/lib/rails/graphql/request/helpers/value_writers.rb +18 -16
  87. data/lib/rails/graphql/request/prepared_data.rb +98 -0
  88. data/lib/rails/graphql/request/steps/authorizable.rb +24 -14
  89. data/lib/rails/graphql/request/steps/organizable.rb +110 -48
  90. data/lib/rails/graphql/request/steps/{prepareable.rb → preparable.rb} +20 -7
  91. data/lib/rails/graphql/request/steps/{resolveable.rb → resolvable.rb} +15 -6
  92. data/lib/rails/graphql/request/strategy/cached_strategy.rb +64 -0
  93. data/lib/rails/graphql/request/strategy/dynamic_instance.rb +6 -6
  94. data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +6 -13
  95. data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +9 -9
  96. data/lib/rails/graphql/request/strategy.rb +131 -75
  97. data/lib/rails/graphql/request/subscription.rb +80 -0
  98. data/lib/rails/graphql/request.rb +305 -86
  99. data/lib/rails/graphql/schema.rb +240 -48
  100. data/lib/rails/graphql/shortcuts.rb +22 -3
  101. data/lib/rails/graphql/source/active_record/builders.rb +50 -36
  102. data/lib/rails/graphql/source/active_record_source.rb +70 -54
  103. data/lib/rails/graphql/source/base.rb +111 -0
  104. data/lib/rails/graphql/source/builder.rb +128 -0
  105. data/lib/rails/graphql/source/scoped_arguments.rb +31 -19
  106. data/lib/rails/graphql/source.rb +89 -213
  107. data/lib/rails/graphql/subscription/provider/action_cable.rb +112 -0
  108. data/lib/rails/graphql/subscription/provider/base.rb +191 -0
  109. data/lib/rails/graphql/subscription/provider.rb +18 -0
  110. data/lib/rails/graphql/subscription/store/base.rb +145 -0
  111. data/lib/rails/graphql/subscription/store/memory.rb +127 -0
  112. data/lib/rails/graphql/subscription/store.rb +19 -0
  113. data/lib/rails/graphql/subscription.rb +17 -0
  114. data/lib/rails/graphql/to_gql.rb +29 -32
  115. data/lib/rails/graphql/type/enum/directive_location_enum.rb +11 -11
  116. data/lib/rails/graphql/type/enum/type_kind_enum.rb +3 -3
  117. data/lib/rails/graphql/type/enum.rb +34 -48
  118. data/lib/rails/graphql/type/input.rb +74 -23
  119. data/lib/rails/graphql/type/interface.rb +16 -26
  120. data/lib/rails/graphql/type/object/directive_object.rb +4 -4
  121. data/lib/rails/graphql/type/object/enum_value_object.rb +3 -3
  122. data/lib/rails/graphql/type/object/field_object.rb +24 -6
  123. data/lib/rails/graphql/type/object/input_value_object.rb +3 -3
  124. data/lib/rails/graphql/type/object/schema_object.rb +5 -8
  125. data/lib/rails/graphql/type/object/type_object.rb +29 -19
  126. data/lib/rails/graphql/type/object.rb +26 -23
  127. data/lib/rails/graphql/type/scalar/any_scalar.rb +30 -0
  128. data/lib/rails/graphql/type/scalar/bigint_scalar.rb +5 -5
  129. data/lib/rails/graphql/type/scalar/binary_scalar.rb +3 -3
  130. data/lib/rails/graphql/type/scalar/boolean_scalar.rb +8 -8
  131. data/lib/rails/graphql/type/scalar/date_scalar.rb +3 -3
  132. data/lib/rails/graphql/type/scalar/date_time_scalar.rb +3 -3
  133. data/lib/rails/graphql/type/scalar/decimal_scalar.rb +3 -3
  134. data/lib/rails/graphql/type/scalar/float_scalar.rb +5 -5
  135. data/lib/rails/graphql/type/scalar/id_scalar.rb +6 -5
  136. data/lib/rails/graphql/type/scalar/int_scalar.rb +6 -5
  137. data/lib/rails/graphql/type/scalar/json_scalar.rb +39 -0
  138. data/lib/rails/graphql/type/scalar/string_scalar.rb +18 -4
  139. data/lib/rails/graphql/type/scalar/time_scalar.rb +5 -5
  140. data/lib/rails/graphql/type/scalar.rb +25 -22
  141. data/lib/rails/graphql/type/union.rb +14 -16
  142. data/lib/rails/graphql/type.rb +34 -25
  143. data/lib/rails/graphql/type_map.rb +257 -165
  144. data/lib/rails/graphql/uri.rb +166 -0
  145. data/lib/rails/graphql/version.rb +15 -3
  146. data/lib/rails/graphql.rake +3 -0
  147. data/lib/rails/graphql.rb +85 -52
  148. data/lib/rails-graphql.rb +1 -1
  149. data/test/assets/en.yml +29 -0
  150. data/test/assets/introspection-mem.txt +1 -1
  151. data/test/assets/mem.gql +18 -45
  152. data/test/assets/mysql.gql +392 -0
  153. data/test/assets/sqlite.gql +21 -12
  154. data/test/assets/translate.gql +335 -0
  155. data/test/config.rb +18 -8
  156. data/test/graphql/schema_test.rb +12 -19
  157. data/test/graphql/source_test.rb +8 -75
  158. data/test/graphql/type/enum_test.rb +207 -203
  159. data/test/graphql/type/input_test.rb +14 -9
  160. data/test/graphql/type/interface_test.rb +4 -4
  161. data/test/graphql/type/scalar/any_scalar_test.rb +38 -0
  162. data/test/graphql/type/scalar/boolean_scalar_test.rb +6 -3
  163. data/test/graphql/type/scalar/json_scalar_test.rb +23 -0
  164. data/test/graphql/type_map_test.rb +51 -66
  165. data/test/graphql/type_test.rb +0 -19
  166. data/test/graphql_test.rb +1 -1
  167. data/test/integration/{authorization/authorization_test.rb → authorization_test.rb} +40 -14
  168. data/test/integration/config.rb +36 -3
  169. data/test/integration/customization_test.rb +39 -0
  170. data/test/integration/global_id_test.rb +99 -0
  171. data/test/integration/memory/star_wars_introspection_test.rb +24 -16
  172. data/test/integration/memory/star_wars_query_test.rb +54 -3
  173. data/test/integration/memory/star_wars_validation_test.rb +1 -1
  174. data/test/integration/mysql/star_wars_introspection_test.rb +25 -0
  175. data/test/integration/persisted_query_test.rb +87 -0
  176. data/test/integration/resolver_precedence_test.rb +154 -0
  177. data/test/integration/schemas/memory.rb +22 -7
  178. data/test/integration/schemas/mysql.rb +62 -0
  179. data/test/integration/schemas/sqlite.rb +21 -12
  180. data/test/integration/sqlite/star_wars_global_id_test.rb +83 -0
  181. data/test/integration/sqlite/star_wars_introspection_test.rb +10 -0
  182. data/test/integration/sqlite/star_wars_query_test.rb +14 -1
  183. data/test/integration/translate_test.rb +61 -0
  184. data/test/test_ext.rb +16 -13
  185. metadata +108 -157
  186. data/ext/depend +0 -3
  187. data/ext/graphqlparser/Ast.cpp +0 -346
  188. data/ext/graphqlparser/Ast.h +0 -1214
  189. data/ext/graphqlparser/AstNode.h +0 -36
  190. data/ext/graphqlparser/AstVisitor.h +0 -137
  191. data/ext/graphqlparser/GraphQLParser.cpp +0 -76
  192. data/ext/graphqlparser/GraphQLParser.h +0 -55
  193. data/ext/graphqlparser/JsonVisitor.cpp +0 -161
  194. data/ext/graphqlparser/JsonVisitor.cpp.inc +0 -456
  195. data/ext/graphqlparser/JsonVisitor.h +0 -121
  196. data/ext/graphqlparser/JsonVisitor.h.inc +0 -110
  197. data/ext/graphqlparser/VERSION +0 -1
  198. data/ext/graphqlparser/c/GraphQLAst.cpp +0 -324
  199. data/ext/graphqlparser/c/GraphQLAst.h +0 -180
  200. data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +0 -44
  201. data/ext/graphqlparser/c/GraphQLAstNode.cpp +0 -25
  202. data/ext/graphqlparser/c/GraphQLAstNode.h +0 -33
  203. data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +0 -21
  204. data/ext/graphqlparser/c/GraphQLAstToJSON.h +0 -24
  205. data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +0 -55
  206. data/ext/graphqlparser/c/GraphQLAstVisitor.h +0 -53
  207. data/ext/graphqlparser/c/GraphQLParser.cpp +0 -35
  208. data/ext/graphqlparser/c/GraphQLParser.h +0 -54
  209. data/ext/graphqlparser/dump_json_ast.cpp +0 -48
  210. data/ext/graphqlparser/lexer.lpp +0 -324
  211. data/ext/graphqlparser/parser.ypp +0 -693
  212. data/ext/graphqlparser/parsergen/lexer.cpp +0 -2633
  213. data/ext/graphqlparser/parsergen/lexer.h +0 -528
  214. data/ext/graphqlparser/parsergen/location.hh +0 -189
  215. data/ext/graphqlparser/parsergen/parser.tab.cpp +0 -3300
  216. data/ext/graphqlparser/parsergen/parser.tab.hpp +0 -646
  217. data/ext/graphqlparser/parsergen/position.hh +0 -179
  218. data/ext/graphqlparser/parsergen/stack.hh +0 -156
  219. data/ext/graphqlparser/syntaxdefs.h +0 -19
  220. data/ext/libgraphqlparser/AstNode.h +0 -36
  221. data/ext/libgraphqlparser/CMakeLists.txt +0 -148
  222. data/ext/libgraphqlparser/CONTRIBUTING.md +0 -23
  223. data/ext/libgraphqlparser/GraphQLParser.cpp +0 -76
  224. data/ext/libgraphqlparser/GraphQLParser.h +0 -55
  225. data/ext/libgraphqlparser/JsonVisitor.cpp +0 -161
  226. data/ext/libgraphqlparser/JsonVisitor.h +0 -121
  227. data/ext/libgraphqlparser/LICENSE +0 -22
  228. data/ext/libgraphqlparser/README.clang-tidy +0 -7
  229. data/ext/libgraphqlparser/README.md +0 -84
  230. data/ext/libgraphqlparser/ast/ast.ast +0 -203
  231. data/ext/libgraphqlparser/ast/ast.py +0 -61
  232. data/ext/libgraphqlparser/ast/c.py +0 -100
  233. data/ext/libgraphqlparser/ast/c.pyc +0 -0
  234. data/ext/libgraphqlparser/ast/c_impl.py +0 -61
  235. data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
  236. data/ext/libgraphqlparser/ast/c_visitor_impl.py +0 -39
  237. data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
  238. data/ext/libgraphqlparser/ast/casing.py +0 -26
  239. data/ext/libgraphqlparser/ast/casing.pyc +0 -0
  240. data/ext/libgraphqlparser/ast/cxx.py +0 -197
  241. data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
  242. data/ext/libgraphqlparser/ast/cxx_impl.py +0 -61
  243. data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
  244. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +0 -42
  245. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
  246. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +0 -80
  247. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
  248. data/ext/libgraphqlparser/ast/cxx_visitor.py +0 -64
  249. data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
  250. data/ext/libgraphqlparser/ast/js.py +0 -65
  251. data/ext/libgraphqlparser/ast/license.py +0 -10
  252. data/ext/libgraphqlparser/ast/license.pyc +0 -0
  253. data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +0 -25
  254. data/ext/libgraphqlparser/c/GraphQLAstNode.h +0 -33
  255. data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +0 -21
  256. data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +0 -24
  257. data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +0 -55
  258. data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +0 -53
  259. data/ext/libgraphqlparser/c/GraphQLParser.cpp +0 -35
  260. data/ext/libgraphqlparser/c/GraphQLParser.h +0 -54
  261. data/ext/libgraphqlparser/clang-tidy-all.sh +0 -3
  262. data/ext/libgraphqlparser/cmake/version.cmake +0 -16
  263. data/ext/libgraphqlparser/dump_json_ast.cpp +0 -48
  264. data/ext/libgraphqlparser/go/README.md +0 -20
  265. data/ext/libgraphqlparser/go/callbacks.go +0 -18
  266. data/ext/libgraphqlparser/go/gotest.go +0 -64
  267. data/ext/libgraphqlparser/lexer.lpp +0 -324
  268. data/ext/libgraphqlparser/libgraphqlparser.pc.in +0 -11
  269. data/ext/libgraphqlparser/parser.ypp +0 -693
  270. data/ext/libgraphqlparser/parsergen/lexer.cpp +0 -2633
  271. data/ext/libgraphqlparser/parsergen/lexer.h +0 -528
  272. data/ext/libgraphqlparser/parsergen/location.hh +0 -189
  273. data/ext/libgraphqlparser/parsergen/parser.tab.cpp +0 -3300
  274. data/ext/libgraphqlparser/parsergen/parser.tab.hpp +0 -646
  275. data/ext/libgraphqlparser/parsergen/position.hh +0 -179
  276. data/ext/libgraphqlparser/parsergen/stack.hh +0 -156
  277. data/ext/libgraphqlparser/python/CMakeLists.txt +0 -14
  278. data/ext/libgraphqlparser/python/README.md +0 -5
  279. data/ext/libgraphqlparser/python/example.py +0 -31
  280. data/ext/libgraphqlparser/syntaxdefs.h +0 -19
  281. data/ext/libgraphqlparser/test/BuildCAPI.c +0 -5
  282. data/ext/libgraphqlparser/test/CMakeLists.txt +0 -25
  283. data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +0 -28
  284. data/ext/libgraphqlparser/test/ParserTests.cpp +0 -352
  285. data/ext/libgraphqlparser/test/kitchen-sink.graphql +0 -59
  286. data/ext/libgraphqlparser/test/kitchen-sink.json +0 -1
  287. data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +0 -78
  288. data/ext/libgraphqlparser/test/schema-kitchen-sink.json +0 -1
  289. data/ext/libgraphqlparser/test/valgrind.supp +0 -33
  290. data/ext/version.cpp +0 -21
  291. data/lib/graphqlparser.so +0 -0
  292. data/lib/rails/graphql/native/functions.rb +0 -38
  293. data/lib/rails/graphql/native/location.rb +0 -41
  294. data/lib/rails/graphql/native/pointers.rb +0 -23
  295. data/lib/rails/graphql/native/visitor.rb +0 -349
  296. data/lib/rails/graphql/native.rb +0 -56
  297. data/test/integration/schemas/authorization.rb +0 -12
data/ext/gql_parser.c ADDED
@@ -0,0 +1,646 @@
1
+ #include <string.h>
2
+ #include <math.h>
3
+
4
+ #include "ruby.h"
5
+ #include "shared.h"
6
+
7
+ #define GQL_SAFE_PUSH_AND_NEXT(source, scanner, action) ({ \
8
+ GQL_SAFE_PUSH(source, action); \
9
+ gql_next_lexeme_no_comments(scanner); \
10
+ })
11
+ #define GQL_ASSIGN_TOKEN_AND_NEXT(source, scanner) (GQL_ASSIGN_VALUE_AND_NEXT(source, scanner, gql_scanner_to_token(scanner)))
12
+ #define GQL_ASSIGN_VALUE_AND_NEXT(source, scanner, value) ({ \
13
+ source = value; \
14
+ gql_next_lexeme_no_comments(scanner); \
15
+ })
16
+ #define GQL_BUILD_PARSE_OUTER_TOKEN(type, size, pieces, scanner, mem) ({ \
17
+ gql_token_start_from_mem(GQL_BUILD_PARSE_TOKEN(type, size, pieces, scanner), mem); \
18
+ })
19
+ #define GQL_BUILD_PARSE_TOKEN(type, size, pieces, scanner) ({ \
20
+ gql_set_token_type(gql_as_token(rb_ary_new4(size, pieces), scanner, 0), type); \
21
+ })
22
+
23
+ // EXECUTION DOCUMENT [OPERATION*, FRAGMENT*]
24
+ VALUE gql_parse_execution(VALUE self, VALUE document);
25
+
26
+ // OPERATION [type?, name?, VARIABLE*, DIRECTIVE*, FIELD*]
27
+ VALUE gql_parse_operation(struct gql_scanner *scanner);
28
+
29
+ // FRAGMENT [name, type, DIRECTIVE*, FIELD*]
30
+ VALUE gql_parse_fragment(struct gql_scanner *scanner);
31
+
32
+ // VARIABLE [name, TYPE, value?, DIRECTIVE*]*
33
+ VALUE gql_parse_variables(struct gql_scanner *scanner);
34
+
35
+ // VARIABLE [name, TYPE, value?, DIRECTIVE*]
36
+ VALUE gql_parse_variable(struct gql_scanner *scanner);
37
+
38
+ // DIRECTIVE [name, ARGUMENT*]*
39
+ VALUE gql_parse_directives(struct gql_scanner *scanner);
40
+
41
+ // DIRECTIVE [name, ARGUMENT*]
42
+ VALUE gql_parse_directive(struct gql_scanner *scanner);
43
+
44
+ // FIELD [name, alias?, ARGUMENT*, DIRECTIVE*, FIELD*]*
45
+ VALUE gql_parse_fields(struct gql_scanner *scanner);
46
+
47
+ // FIELD [name, alias?, ARGUMENT*, DIRECTIVE*, FIELD*]
48
+ VALUE gql_parse_field(struct gql_scanner *scanner);
49
+
50
+ // ARGUMENT [name, value?, var_name?]*
51
+ VALUE gql_parse_arguments(struct gql_scanner *scanner);
52
+
53
+ // ARGUMENT [name, value?, var_name?]
54
+ VALUE gql_parse_argument(struct gql_scanner *scanner);
55
+
56
+ // SPREAD [name?, type?, DIRECTIVE*, FIELD*]
57
+ VALUE gql_parse_spread(struct gql_scanner *scanner);
58
+
59
+ // TYPE [name, dimensions?, nullability]
60
+ VALUE gql_parse_type(struct gql_scanner *scanner);
61
+
62
+ // Little helper to simplify returning problems
63
+ VALUE gql_nil_and_unknown(struct gql_scanner *scanner);
64
+
65
+ // Little helper to assign the start memoized position
66
+ VALUE gql_token_start_from_mem(VALUE instance, unsigned long memory[2]);
67
+
68
+ // Central error method
69
+ NORETURN(void gql_throw_parser_error(struct gql_scanner *scanner));
70
+
71
+ /* STRUCTURES
72
+ *
73
+ * EXECUTION DOCUMENT [OPERATION*, FRAGMENT*]
74
+ * OPERATION [type?, name?, VARIABLE*, DIRECTIVE*, FIELD*]
75
+ * FRAGMENT [name, type, DIRECTIVE*, FIELD*]
76
+ *
77
+ * VARIABLE [name, TYPE, value?, DIRECTIVE*]
78
+ * DIRECTIVE [name, ARGUMENT*]
79
+ * FIELD [alias?, name, ARGUMENT*, DIRECTIVE*, FIELD*]
80
+ * ARGUMENT [name, value?, var_name?]
81
+ */
82
+
83
+ /* ALL THE PARSERS METHODS FOR THE ABOVE STRUCTURES */
84
+ // Parse an execution document
85
+ // EXECUTION DOCUMENT [OPERATION*, FRAGMENT*]
86
+ VALUE gql_parse_execution(VALUE self, VALUE document)
87
+ {
88
+ if (!RB_TYPE_P(document, T_STRING))
89
+ rb_raise(rb_eArgError, "%+" PRIsVALUE " is not a string", document);
90
+
91
+ // Initialize its pieces
92
+ VALUE pieces[] = {Qnil, Qnil};
93
+ struct gql_scanner scanner = gql_new_scanner(document);
94
+ gql_next_lexeme_no_comments(&scanner);
95
+
96
+ // Go over all the operations and fragments
97
+ while (scanner.lexeme != gql_i_eof)
98
+ {
99
+ // Try to upgrade if the token is a name
100
+ if (scanner.lexeme == gql_i_name)
101
+ scanner.lexeme = gql_name_to_keyword(&scanner, GQL_EXECUTION_KEYWORDS);
102
+
103
+ // It can contain either operations or fragments, anything else is unknown and an error
104
+ if (QGL_I_OPERATION(scanner.lexeme) || scanner.lexeme == gql_is_op_curly)
105
+ GQL_SAFE_PUSH(pieces[0], gql_parse_operation(&scanner));
106
+ else if (scanner.lexeme == gql_ie_fragment)
107
+ GQL_SAFE_PUSH(pieces[1], gql_parse_fragment(&scanner));
108
+ else if (scanner.lexeme != gql_i_comment)
109
+ scanner.lexeme = gql_i_unknown;
110
+
111
+ // If anything made the scanner fall into an unknown, throw an error
112
+ if (scanner.lexeme == gql_i_unknown)
113
+ gql_throw_parser_error(&scanner);
114
+ }
115
+
116
+ // Return the plain array, no need to turn into a token
117
+ return rb_ary_new4(2, pieces);
118
+ }
119
+
120
+ // Parse an operation element
121
+ // OPERATION [type?, name?, VARIABLE*, DIRECTIVE*, FIELD*]
122
+ VALUE gql_parse_operation(struct gql_scanner *scanner)
123
+ {
124
+ // Common header
125
+ unsigned long mem[2];
126
+ GQL_SCAN_SAVE(scanner, mem);
127
+ VALUE pieces[] = {Qnil, Qnil, Qnil, Qnil, Qnil};
128
+
129
+ // Save the type
130
+ const char *type = "query";
131
+
132
+ // When we have the operation type, we may have all the other stuff as well
133
+ if (QGL_I_OPERATION(scanner->lexeme))
134
+ {
135
+ // Save the operation type
136
+ type = RSTRING_PTR(gql_scanner_to_s(scanner));
137
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
138
+
139
+ // Save the name of the operation
140
+ if (scanner->lexeme == gql_i_name)
141
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[1], scanner);
142
+
143
+ // Save the variables of the operation
144
+ if (scanner->lexeme == gql_is_op_paren)
145
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[2], scanner, gql_parse_variables(scanner));
146
+
147
+ // Save the directives of the operation
148
+ if (scanner->lexeme == gql_i_directive)
149
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[3], scanner, gql_parse_directives(scanner));
150
+ }
151
+
152
+ // Collect all the fields for this operation, or return nil for non-typed operation with empty body
153
+ // With empty body operation, make sure to move to the next token
154
+ if (scanner->lexeme == gql_is_op_curly)
155
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[4], scanner, gql_parse_fields(scanner));
156
+ else if (NIL_P(pieces[0]))
157
+ return gql_nil_and_unknown(scanner);
158
+
159
+ // Generate the result array with proper scan location and return
160
+ return GQL_BUILD_PARSE_OUTER_TOKEN(type, 5, pieces, scanner, mem);
161
+ }
162
+
163
+ // FRAGMENT [name, type, DIRECTIVE*, FIELD*]
164
+ VALUE gql_parse_fragment(struct gql_scanner *scanner)
165
+ {
166
+ // Common header
167
+ unsigned long mem[2];
168
+ GQL_SCAN_SAVE(scanner, mem);
169
+ VALUE pieces[] = {Qnil, Qnil, Qnil, Qnil};
170
+
171
+ // Make sure we have a name and it is not "on"
172
+ gql_next_lexeme_no_comments(scanner);
173
+ if (scanner->lexeme != gql_i_name)
174
+ return gql_nil_and_unknown(scanner);
175
+ else if (gql_name_to_keyword(scanner, GQL_EXECUTION_KEYWORDS) == gql_ie_on)
176
+ return gql_nil_and_unknown(scanner);
177
+
178
+ // Save the name of the fragment
179
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
180
+
181
+ // If we don't have an "on" next, we have a problem
182
+ if (gql_name_to_keyword(scanner, GQL_EXECUTION_KEYWORDS) != gql_ie_on)
183
+ return gql_nil_and_unknown(scanner);
184
+
185
+ // Skip the on and ensure that next is a name
186
+ gql_next_lexeme_no_comments(scanner);
187
+ if (scanner->lexeme != gql_i_name)
188
+ return gql_nil_and_unknown(scanner);
189
+
190
+ // Save the name of the type
191
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[1], scanner);
192
+
193
+ // Save the directives of the fragment
194
+ if (scanner->lexeme == gql_i_directive)
195
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[2], scanner, gql_parse_directives(scanner));
196
+
197
+ // Normally fields would be mandatory, but the gem will accept empty body fragments
198
+ if (scanner->lexeme == gql_is_op_curly)
199
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[3], scanner, gql_parse_fields(scanner));
200
+
201
+ // Generate the result array with proper scan location and return
202
+ return GQL_BUILD_PARSE_OUTER_TOKEN("fragment", 4, pieces, scanner, mem);
203
+ }
204
+
205
+ // VARIABLE [name, TYPE, value?, DIRECTIVE*]*
206
+ VALUE gql_parse_variables(struct gql_scanner *scanner)
207
+ {
208
+ // The list can be nil if "()"
209
+ VALUE result = Qnil;
210
+
211
+ // Skip the (
212
+ GQL_SCAN_NEXT(scanner);
213
+ gql_next_lexeme_no_comments(scanner);
214
+
215
+ // Look for the end of the parenthesis
216
+ while (scanner->lexeme != gql_is_cl_paren)
217
+ {
218
+ if (GQL_SCAN_ERROR(scanner))
219
+ return gql_nil_and_unknown(scanner);
220
+
221
+ GQL_SAFE_PUSH(result, gql_parse_variable(scanner));
222
+ }
223
+
224
+ // Just return the array filled with variables, no need to make it as a token
225
+ GQL_SCAN_NEXT(scanner);
226
+ return result;
227
+ }
228
+
229
+ // VARIABLE [name, TYPE, value?, DIRECTIVE*]
230
+ VALUE gql_parse_variable(struct gql_scanner *scanner)
231
+ {
232
+ // Common header
233
+ unsigned long mem[2];
234
+ GQL_SCAN_SAVE(scanner, mem);
235
+ VALUE pieces[] = {Qnil, Qnil, Qnil, Qnil};
236
+
237
+ // Make sure that it starts with an "$" sign
238
+ if (scanner->lexeme != gql_i_variable)
239
+ return gql_nil_and_unknown(scanner);
240
+
241
+ // Skip the $
242
+ GQL_SCAN_NEXT(scanner);
243
+ scanner->start_pos++;
244
+
245
+ // If we don't have a name indicator, we return an error
246
+ if (!GQL_S_CHARACTER(scanner->current))
247
+ return gql_nil_and_unknown(scanner);
248
+
249
+ // Read and save the name
250
+ scanner->lexeme = gql_read_name(scanner);
251
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
252
+
253
+ // Next is the colon before the type
254
+ if (scanner->lexeme != gql_is_colon)
255
+ return gql_nil_and_unknown(scanner);
256
+
257
+ // Skip the :
258
+ GQL_SCAN_NEXT(scanner);
259
+
260
+ // Now check for the type, which can be a brack for array or just the type
261
+ gql_next_lexeme_no_comments(scanner);
262
+ if (scanner->lexeme != gql_is_op_brack && scanner->lexeme != gql_i_name)
263
+ return gql_nil_and_unknown(scanner);
264
+
265
+ // Save the type of the variable
266
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[1], scanner, gql_parse_type(scanner));
267
+
268
+ // If the next lexeme is an equal sign, then we have to capture the value
269
+ if (scanner->lexeme == gql_is_equal)
270
+ {
271
+ GQL_SCAN_NEXT(scanner);
272
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[2], scanner, gql_value_to_token(scanner, 0));
273
+ }
274
+
275
+ // Save the directives of the variable
276
+ if (scanner->lexeme == gql_i_directive)
277
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[3], scanner, gql_parse_directives(scanner));
278
+
279
+ // Generate the result array with proper scan location and return
280
+ return GQL_BUILD_PARSE_OUTER_TOKEN("variable", 4, pieces, scanner, mem);
281
+ }
282
+
283
+ // DIRECTIVE [name, ARGUMENT*]*
284
+ VALUE gql_parse_directives(struct gql_scanner *scanner)
285
+ {
286
+ // Start the list of directives, we have at least one when it gets here
287
+ VALUE result = rb_ary_new();
288
+
289
+ // Look for all the directives
290
+ while (scanner->lexeme == gql_i_directive)
291
+ rb_ary_push(result, gql_parse_directive(scanner));
292
+
293
+ // Just return the array filled with variables, no need to make it as a token
294
+ return result;
295
+ }
296
+
297
+ // DIRECTIVE [name, ARGUMENT*]
298
+ VALUE gql_parse_directive(struct gql_scanner *scanner)
299
+ {
300
+ // Common header
301
+ unsigned long mem[2];
302
+ GQL_SCAN_SAVE(scanner, mem);
303
+ VALUE pieces[] = {Qnil, Qnil};
304
+
305
+ // Skip the @
306
+ GQL_SCAN_NEXT(scanner);
307
+ scanner->start_pos++;
308
+
309
+ // If we don't have a name indicator, we return an error
310
+ if (!GQL_S_CHARACTER(scanner->current))
311
+ return gql_nil_and_unknown(scanner);
312
+
313
+ // Read and save the name
314
+ scanner->lexeme = gql_read_name(scanner);
315
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
316
+
317
+ // Save the arguments of the directive
318
+ if (scanner->lexeme == gql_is_op_paren)
319
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[1], scanner, gql_parse_arguments(scanner));
320
+
321
+ // Generate the result array with proper scan location and return
322
+ return GQL_BUILD_PARSE_OUTER_TOKEN("directive", 2, pieces, scanner, mem);
323
+ }
324
+
325
+ // FIELD [alias?, name, ARGUMENT*, DIRECTIVE*, FIELD*]*
326
+ VALUE gql_parse_fields(struct gql_scanner *scanner)
327
+ {
328
+ // The list can be nil if "{}"
329
+ VALUE result = Qnil;
330
+
331
+ // Skip the {
332
+ GQL_SCAN_NEXT(scanner);
333
+ gql_next_lexeme_no_comments(scanner);
334
+
335
+ // Look for the end of the curly
336
+ while (scanner->lexeme != gql_is_cl_curly)
337
+ {
338
+ if (GQL_SCAN_ERROR(scanner))
339
+ return gql_nil_and_unknown(scanner);
340
+ else if (scanner->lexeme == gql_is_period)
341
+ GQL_SAFE_PUSH(result, gql_parse_spread(scanner));
342
+ else
343
+ GQL_SAFE_PUSH(result, gql_parse_field(scanner));
344
+ }
345
+
346
+ // Just return the array filled with fields, no need to make it as a token
347
+ GQL_SCAN_NEXT(scanner);
348
+ return result;
349
+ }
350
+
351
+ // FIELD [name, alias?, ARGUMENT*, DIRECTIVE*, FIELD*]
352
+ VALUE gql_parse_field(struct gql_scanner *scanner)
353
+ {
354
+ // Common header
355
+ unsigned long mem[2];
356
+ GQL_SCAN_SAVE(scanner, mem);
357
+ VALUE pieces[] = {Qnil, Qnil, Qnil, Qnil, Qnil};
358
+
359
+ // If we don't have a name, we have a problem
360
+ if (scanner->lexeme != gql_i_name)
361
+ return gql_nil_and_unknown(scanner);
362
+
363
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
364
+
365
+ // If we got a colon, then we actually had an alias and not the name
366
+ if (scanner->lexeme == gql_is_colon)
367
+ {
368
+ // Move one further and get the next lexeme
369
+ GQL_SCAN_NEXT(scanner);
370
+ gql_next_lexeme_no_comments(scanner);
371
+
372
+ // If we don't have a name after, we have a problem
373
+ if (scanner->lexeme != gql_i_name)
374
+ return gql_nil_and_unknown(scanner);
375
+
376
+ // Save the alias and the actual field name
377
+ pieces[1] = pieces[0];
378
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
379
+ }
380
+
381
+ // Save the arguments of the field
382
+ if (scanner->lexeme == gql_is_op_paren)
383
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[2], scanner, gql_parse_arguments(scanner));
384
+
385
+ // Save the directives of the field
386
+ if (scanner->lexeme == gql_i_directive)
387
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[3], scanner, gql_parse_directives(scanner));
388
+
389
+ // Save the fields of the field
390
+ if (scanner->lexeme == gql_is_op_curly)
391
+ {
392
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[4], scanner, gql_parse_fields(scanner));
393
+
394
+ // If fields were initiated but came back empty, we have a problem
395
+ if (NIL_P(pieces[4]))
396
+ return gql_nil_and_unknown(scanner);
397
+ }
398
+
399
+ // Generate the result array with proper scan location and return
400
+ return GQL_BUILD_PARSE_OUTER_TOKEN("field", 5, pieces, scanner, mem);
401
+ }
402
+
403
+ // ARGUMENT [name, value?, var_name?]*
404
+ VALUE gql_parse_arguments(struct gql_scanner *scanner)
405
+ {
406
+ // The list can be nil if "()"
407
+ VALUE result = Qnil;
408
+
409
+ // Skip the (
410
+ GQL_SCAN_NEXT(scanner);
411
+ gql_next_lexeme_no_comments(scanner);
412
+
413
+ // Look for the end of the parenthesis
414
+ while (scanner->lexeme != gql_is_cl_paren)
415
+ {
416
+ if (GQL_SCAN_ERROR(scanner))
417
+ return gql_nil_and_unknown(scanner);
418
+
419
+ GQL_SAFE_PUSH(result, gql_parse_argument(scanner));
420
+ }
421
+
422
+ // Just return the array filled with arguments, no need to make it as a token
423
+ GQL_SCAN_NEXT(scanner);
424
+ return result;
425
+ }
426
+
427
+ // ARGUMENT [name, value?, var_name?]
428
+ VALUE gql_parse_argument(struct gql_scanner *scanner)
429
+ {
430
+ // Common header
431
+ unsigned long mem[2];
432
+ GQL_SCAN_SAVE(scanner, mem);
433
+ VALUE pieces[] = {Qnil, Qnil, Qnil};
434
+
435
+ // If we don't have a name after, we have a problem
436
+ if (scanner->lexeme != gql_i_name)
437
+ return gql_nil_and_unknown(scanner);
438
+
439
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
440
+
441
+ // If we don't have a colon after, we have a problem, because we need a value
442
+ if (scanner->lexeme != gql_is_colon)
443
+ return gql_nil_and_unknown(scanner);
444
+
445
+ // Move one further and assume that the next lexeme will be a value
446
+ GQL_SCAN_NEXT(scanner);
447
+ pieces[1] = gql_value_to_rb(scanner, 1);
448
+
449
+ // If we successfully got a value, not a var,
450
+ // then just make it as a token and move to the next
451
+ if (GQL_I_VALUE(scanner->lexeme))
452
+ {
453
+ pieces[1] = gql_as_token(pieces[1], scanner, 1);
454
+ gql_next_lexeme_no_comments(scanner);
455
+ }
456
+ else if (scanner->lexeme == gql_i_variable)
457
+ {
458
+ // Skip the $ for a variable
459
+ GQL_SCAN_NEXT(scanner);
460
+ scanner->start_pos++;
461
+
462
+ // If we don't have a name indicator, we return an error
463
+ if (!GQL_S_CHARACTER(scanner->current))
464
+ return gql_nil_and_unknown(scanner);
465
+
466
+ // Read and save only the name
467
+ scanner->lexeme = gql_read_name(scanner);
468
+ pieces[2] = gql_set_token_type(gql_scanner_to_token(scanner), "variable");
469
+ gql_next_lexeme_no_comments(scanner);
470
+ }
471
+ else
472
+ return gql_nil_and_unknown(scanner);
473
+
474
+ // Generate the result array with proper scan location and return
475
+ return GQL_BUILD_PARSE_OUTER_TOKEN("argument", 3, pieces, scanner, mem);
476
+ }
477
+
478
+ // SPREAD [name?, type?, DIRECTIVE*, FIELD*]
479
+ VALUE gql_parse_spread(struct gql_scanner *scanner)
480
+ {
481
+ // Common header
482
+ unsigned long mem[2];
483
+ GQL_SCAN_SAVE(scanner, mem);
484
+ VALUE pieces[] = {Qnil, Qnil, Qnil, Qnil};
485
+
486
+ // Make sure that we have 2 other periods and something else right after
487
+ if (GQL_SCAN_LOOK(scanner, 1) != '.' || GQL_SCAN_LOOK(scanner, 2) != '.' || GQL_SCAN_LOOK(scanner, 3) == '.')
488
+ return gql_nil_and_unknown(scanner);
489
+
490
+ // Move after the periods and get the next lexeme
491
+ scanner->current_pos += 3;
492
+ scanner->current = GQL_SCAN_CHAR(scanner);
493
+ gql_next_lexeme_no_comments(scanner);
494
+
495
+ // According to the spec, the type condition or the name are optional
496
+ if (scanner->lexeme == gql_i_name)
497
+ {
498
+ // Upgrade the name because it will decide if it is an inline spread or not
499
+ scanner->lexeme = gql_name_to_keyword(scanner, GQL_EXECUTION_KEYWORDS);
500
+
501
+ // If we are at "on" then we have an inline spread, otherwise a fragment referenced by name
502
+ if (scanner->lexeme == gql_ie_on)
503
+ {
504
+ gql_next_lexeme_no_comments(scanner);
505
+
506
+ // If we don't have a name after, we have a problem
507
+ if (scanner->lexeme != gql_i_name)
508
+ return gql_nil_and_unknown(scanner);
509
+
510
+ // Save it as the type of the spread
511
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[1], scanner);
512
+ }
513
+ else
514
+ GQL_ASSIGN_TOKEN_AND_NEXT(pieces[0], scanner);
515
+ }
516
+
517
+ // Save the directives of the field
518
+ if (scanner->lexeme == gql_i_directive)
519
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[2], scanner, gql_parse_directives(scanner));
520
+
521
+ // Spread without a name needs fields
522
+ if (NIL_P(pieces[0]))
523
+ {
524
+ // No curly means we have a problem
525
+ if (scanner->lexeme != gql_is_op_curly)
526
+ return gql_nil_and_unknown(scanner);
527
+
528
+ // Save the fields
529
+ GQL_ASSIGN_VALUE_AND_NEXT(pieces[3], scanner, gql_parse_fields(scanner));
530
+
531
+ // If fields were initiated but came back empty, we have a problem
532
+ if (NIL_P(pieces[3]))
533
+ return gql_nil_and_unknown(scanner);
534
+ }
535
+
536
+ // Generate the result array with proper scan location and return
537
+ return GQL_BUILD_PARSE_OUTER_TOKEN("spread", 4, pieces, scanner, mem);
538
+ }
539
+
540
+ // TYPE [name, dimensions, nullability]
541
+ VALUE gql_parse_type(struct gql_scanner *scanner)
542
+ {
543
+ VALUE pieces[] = {Qnil, Qnil, Qnil};
544
+
545
+ // Important info about the type
546
+ unsigned int dimensions = 0;
547
+ unsigned int nullability = 0;
548
+
549
+ // Check for all the open brackets before the type
550
+ while (scanner->current == '[' || (dimensions > 0 && GQL_S_IGNORE(scanner->current)))
551
+ {
552
+ if (scanner->current == '\0')
553
+ return gql_nil_and_unknown(scanner);
554
+ else if (scanner->current == '[')
555
+ dimensions++;
556
+
557
+ GQL_SCAN_NEXT(scanner);
558
+ }
559
+
560
+ // If any dimensions where identified, then get the next lexeme for the name
561
+ if (dimensions > 0)
562
+ gql_next_lexeme(scanner);
563
+
564
+ // If it is not a name, then we have a problem, otherwise save the name
565
+ if (scanner->lexeme != gql_i_name)
566
+ return gql_nil_and_unknown(scanner);
567
+
568
+ pieces[0] = gql_scanner_to_token(scanner);
569
+ pieces[1] = UINT2NUM(dimensions);
570
+
571
+ // Now go over all the close brackets, exclamations, and ignorables
572
+ while (scanner->current == '!' || scanner->current == ']' || GQL_S_IGNORE(scanner->current))
573
+ {
574
+ if (scanner->current == '\0')
575
+ return gql_nil_and_unknown(scanner);
576
+ else if (scanner->current == '!')
577
+ nullability += pow(2, dimensions);
578
+ else if (scanner->current == ']')
579
+ dimensions--;
580
+
581
+ GQL_SCAN_NEXT(scanner);
582
+ }
583
+
584
+ // If there are dimensions still open, we have a problem
585
+ if (dimensions > 0)
586
+ return gql_nil_and_unknown(scanner);
587
+
588
+ // Save the last position, last line, and the nullability
589
+ GQL_SCAN_SET_END(scanner, 1);
590
+ pieces[2] = UINT2NUM(nullability);
591
+
592
+ // Return the valid parsed type
593
+ return GQL_BUILD_PARSE_TOKEN("type", 3, pieces, scanner);
594
+ }
595
+
596
+ // Simply set the scanner as unkown and return nil, to simplify validation
597
+ VALUE gql_nil_and_unknown(struct gql_scanner *scanner)
598
+ {
599
+ scanner->lexeme = gql_i_unknown;
600
+ return Qnil;
601
+ }
602
+
603
+ // Little helper to assign the start memoized position
604
+ VALUE gql_token_start_from_mem(VALUE instance, unsigned long memory[2])
605
+ {
606
+ int offset = memory[0] == 1 ? 1 : 0;
607
+ rb_iv_set(instance, "@begin_line", ULONG2NUM(memory[0]));
608
+ rb_iv_set(instance, "@begin_column", ULONG2NUM(memory[1] + offset));
609
+ return instance;
610
+ }
611
+
612
+ // A centralized way to express that the parser was unsuccessful
613
+ void gql_throw_parser_error(struct gql_scanner *scanner)
614
+ {
615
+ VALUE line = ULONG2NUM(scanner->begin_line);
616
+ VALUE column = ULONG2NUM(scanner->begin_column);
617
+ VALUE token;
618
+
619
+ if (GQL_SCAN_SIZE(scanner) > 0)
620
+ token = gql_scanner_to_s(scanner);
621
+ else if (scanner->current != '\0')
622
+ token = rb_str_new(&scanner->current, 1);
623
+ else
624
+ token = rb_str_new2("EOF");
625
+
626
+ const char *message = "Parser error: unexpected \"%" PRIsVALUE "\" at [%" PRIsVALUE ", %" PRIsVALUE "]";
627
+ rb_raise(gql_eParserError, message, token, line, column);
628
+ }
629
+
630
+ void Init_gql_parser()
631
+ {
632
+ GQLParser = rb_define_module("GQLParser");
633
+ rb_define_singleton_method(GQLParser, "parse_execution", gql_parse_execution, 1);
634
+ rb_define_const(GQLParser, "VERSION", rb_str_new2("October 2021"));
635
+
636
+ QLGParserToken = rb_define_class_under(GQLParser, "Token", rb_path2class("SimpleDelegator"));
637
+ rb_define_method(QLGParserToken, "of_type?", gql_token_of_type_check, 1);
638
+ rb_define_method(QLGParserToken, "inspect", gql_inspect_token, 0);
639
+ rb_define_attr(QLGParserToken, "begin_line", 1, 0);
640
+ rb_define_attr(QLGParserToken, "begin_column", 1, 0);
641
+ rb_define_attr(QLGParserToken, "end_line", 1, 0);
642
+ rb_define_attr(QLGParserToken, "end_column", 1, 0);
643
+ rb_define_attr(QLGParserToken, "type", 1, 0);
644
+
645
+ gql_eParserError = rb_define_class_under(GQLParser, "ParserError", rb_eStandardError);
646
+ }