rails-graphql 0.2.1 → 1.0.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +6 -6
  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 +49 -35
  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 +256 -164
  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
+ }