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.
- checksums.yaml +4 -4
- data/ext/console.rb +18 -0
- data/ext/extconf.h +3 -0
- data/ext/extconf.rb +1 -54
- data/ext/gql_parser.c +646 -0
- data/ext/shared.c +482 -0
- data/ext/shared.h +177 -0
- data/lib/gql_parser.so +0 -0
- data/lib/rails/graphql/adapters/mysql_adapter.rb +59 -0
- data/lib/rails/graphql/adapters/pg_adapter.rb +25 -22
- data/lib/rails/graphql/adapters/sqlite_adapter.rb +17 -14
- data/lib/rails/graphql/alternative/field_set.rb +36 -0
- data/lib/rails/graphql/alternative/mutation.rb +17 -0
- data/lib/rails/graphql/alternative/query.rb +93 -0
- data/lib/rails/graphql/alternative/subscription.rb +17 -0
- data/lib/rails/graphql/alternative.rb +20 -0
- data/lib/rails/graphql/argument.rb +21 -24
- data/lib/rails/graphql/callback.rb +24 -9
- data/lib/rails/graphql/collectors/hash_collector.rb +14 -6
- data/lib/rails/graphql/collectors/idented_collector.rb +10 -7
- data/lib/rails/graphql/collectors/json_collector.rb +22 -17
- data/lib/rails/graphql/collectors.rb +4 -4
- data/lib/rails/graphql/config.rb +130 -15
- data/lib/rails/graphql/directive/cached_directive.rb +33 -0
- data/lib/rails/graphql/directive/deprecated_directive.rb +10 -10
- data/lib/rails/graphql/directive/include_directive.rb +5 -4
- data/lib/rails/graphql/directive/skip_directive.rb +5 -4
- data/lib/rails/graphql/directive.rb +118 -63
- data/lib/rails/graphql/errors.rb +33 -4
- data/lib/rails/graphql/event.rb +16 -5
- data/lib/rails/graphql/field/authorized_field.rb +19 -3
- data/lib/rails/graphql/field/input_field.rb +11 -10
- data/lib/rails/graphql/field/mutation_field.rb +42 -7
- data/lib/rails/graphql/field/output_field.rb +102 -13
- data/lib/rails/graphql/field/proxied_field.rb +31 -22
- data/lib/rails/graphql/field/resolved_field.rb +26 -24
- data/lib/rails/graphql/field/scoped_config.rb +10 -4
- data/lib/rails/graphql/field/subscription_field.rb +140 -0
- data/lib/rails/graphql/field/typed_field.rb +43 -22
- data/lib/rails/graphql/field.rb +70 -56
- data/lib/rails/graphql/global_id.rb +85 -0
- data/lib/rails/graphql/helpers/attribute_delegator.rb +5 -5
- data/lib/rails/graphql/helpers/inherited_collection/array.rb +50 -0
- data/lib/rails/graphql/helpers/inherited_collection/base.rb +43 -0
- data/lib/rails/graphql/helpers/inherited_collection/hash.rb +87 -0
- data/lib/rails/graphql/helpers/inherited_collection.rb +25 -76
- data/lib/rails/graphql/helpers/instantiable.rb +15 -0
- data/lib/rails/graphql/helpers/leaf_from_ar.rb +7 -7
- data/lib/rails/graphql/helpers/registerable.rb +44 -62
- data/lib/rails/graphql/helpers/unregisterable.rb +16 -0
- data/lib/rails/graphql/helpers/with_arguments.rb +31 -27
- data/lib/rails/graphql/helpers/with_assignment.rb +6 -6
- data/lib/rails/graphql/helpers/with_callbacks.rb +25 -8
- data/lib/rails/graphql/helpers/with_description.rb +71 -0
- data/lib/rails/graphql/helpers/with_directives.rb +54 -30
- data/lib/rails/graphql/helpers/with_events.rb +21 -23
- data/lib/rails/graphql/helpers/with_fields.rb +76 -22
- data/lib/rails/graphql/helpers/with_global_id.rb +22 -0
- data/lib/rails/graphql/helpers/with_name.rb +43 -0
- data/lib/rails/graphql/helpers/with_namespace.rb +7 -4
- data/lib/rails/graphql/helpers/with_owner.rb +8 -7
- data/lib/rails/graphql/helpers/with_schema_fields.rb +137 -55
- data/lib/rails/graphql/helpers/with_validator.rb +9 -9
- data/lib/rails/graphql/helpers.rb +10 -3
- data/lib/rails/graphql/introspection.rb +43 -36
- data/lib/rails/graphql/railtie.rb +88 -33
- data/lib/rails/graphql/railties/base_generator.rb +3 -9
- data/lib/rails/graphql/railties/channel.rb +157 -0
- data/lib/rails/graphql/railties/controller.rb +62 -17
- data/lib/rails/graphql/railties/controller_runtime.rb +5 -5
- data/lib/rails/graphql/railties/log_subscriber.rb +81 -14
- data/lib/rails/graphql/request/arguments.rb +24 -49
- data/lib/rails/graphql/request/backtrace.rb +191 -0
- data/lib/rails/graphql/request/component/field.rb +86 -65
- data/lib/rails/graphql/request/component/fragment.rb +72 -24
- data/lib/rails/graphql/request/component/operation/subscription.rb +164 -4
- data/lib/rails/graphql/request/component/operation.rb +63 -31
- data/lib/rails/graphql/request/component/spread.rb +68 -25
- data/lib/rails/graphql/request/component/typename.rb +27 -12
- data/lib/rails/graphql/request/component.rb +75 -36
- data/lib/rails/graphql/request/context.rb +18 -8
- data/lib/rails/graphql/request/errors.rb +16 -6
- data/lib/rails/graphql/request/event.rb +19 -8
- data/lib/rails/graphql/request/helpers/directives.rb +68 -27
- data/lib/rails/graphql/request/helpers/selection_set.rb +51 -25
- data/lib/rails/graphql/request/helpers/value_writers.rb +18 -16
- data/lib/rails/graphql/request/prepared_data.rb +98 -0
- data/lib/rails/graphql/request/steps/authorizable.rb +24 -14
- data/lib/rails/graphql/request/steps/organizable.rb +110 -48
- data/lib/rails/graphql/request/steps/{prepareable.rb → preparable.rb} +20 -7
- data/lib/rails/graphql/request/steps/{resolveable.rb → resolvable.rb} +15 -6
- data/lib/rails/graphql/request/strategy/cached_strategy.rb +64 -0
- data/lib/rails/graphql/request/strategy/dynamic_instance.rb +6 -6
- data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +6 -13
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +9 -9
- data/lib/rails/graphql/request/strategy.rb +131 -75
- data/lib/rails/graphql/request/subscription.rb +80 -0
- data/lib/rails/graphql/request.rb +305 -86
- data/lib/rails/graphql/schema.rb +240 -48
- data/lib/rails/graphql/shortcuts.rb +22 -3
- data/lib/rails/graphql/source/active_record/builders.rb +49 -35
- data/lib/rails/graphql/source/active_record_source.rb +70 -54
- data/lib/rails/graphql/source/base.rb +111 -0
- data/lib/rails/graphql/source/builder.rb +128 -0
- data/lib/rails/graphql/source/scoped_arguments.rb +31 -19
- data/lib/rails/graphql/source.rb +89 -213
- data/lib/rails/graphql/subscription/provider/action_cable.rb +112 -0
- data/lib/rails/graphql/subscription/provider/base.rb +191 -0
- data/lib/rails/graphql/subscription/provider.rb +18 -0
- data/lib/rails/graphql/subscription/store/base.rb +145 -0
- data/lib/rails/graphql/subscription/store/memory.rb +127 -0
- data/lib/rails/graphql/subscription/store.rb +19 -0
- data/lib/rails/graphql/subscription.rb +17 -0
- data/lib/rails/graphql/to_gql.rb +29 -32
- data/lib/rails/graphql/type/enum/directive_location_enum.rb +11 -11
- data/lib/rails/graphql/type/enum/type_kind_enum.rb +3 -3
- data/lib/rails/graphql/type/enum.rb +34 -48
- data/lib/rails/graphql/type/input.rb +74 -23
- data/lib/rails/graphql/type/interface.rb +16 -26
- data/lib/rails/graphql/type/object/directive_object.rb +4 -4
- data/lib/rails/graphql/type/object/enum_value_object.rb +3 -3
- data/lib/rails/graphql/type/object/field_object.rb +24 -6
- data/lib/rails/graphql/type/object/input_value_object.rb +3 -3
- data/lib/rails/graphql/type/object/schema_object.rb +5 -8
- data/lib/rails/graphql/type/object/type_object.rb +29 -19
- data/lib/rails/graphql/type/object.rb +26 -23
- data/lib/rails/graphql/type/scalar/any_scalar.rb +30 -0
- data/lib/rails/graphql/type/scalar/bigint_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/boolean_scalar.rb +8 -8
- data/lib/rails/graphql/type/scalar/date_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/float_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar/id_scalar.rb +6 -5
- data/lib/rails/graphql/type/scalar/int_scalar.rb +6 -5
- data/lib/rails/graphql/type/scalar/json_scalar.rb +39 -0
- data/lib/rails/graphql/type/scalar/string_scalar.rb +18 -4
- data/lib/rails/graphql/type/scalar/time_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar.rb +25 -22
- data/lib/rails/graphql/type/union.rb +14 -16
- data/lib/rails/graphql/type.rb +34 -25
- data/lib/rails/graphql/type_map.rb +256 -164
- data/lib/rails/graphql/uri.rb +166 -0
- data/lib/rails/graphql/version.rb +15 -3
- data/lib/rails/graphql.rake +3 -0
- data/lib/rails/graphql.rb +85 -52
- data/lib/rails-graphql.rb +1 -1
- data/test/assets/en.yml +29 -0
- data/test/assets/introspection-mem.txt +1 -1
- data/test/assets/mem.gql +18 -45
- data/test/assets/mysql.gql +392 -0
- data/test/assets/sqlite.gql +21 -12
- data/test/assets/translate.gql +335 -0
- data/test/config.rb +18 -8
- data/test/graphql/schema_test.rb +12 -19
- data/test/graphql/source_test.rb +8 -75
- data/test/graphql/type/enum_test.rb +207 -203
- data/test/graphql/type/input_test.rb +14 -9
- data/test/graphql/type/interface_test.rb +4 -4
- data/test/graphql/type/scalar/any_scalar_test.rb +38 -0
- data/test/graphql/type/scalar/boolean_scalar_test.rb +6 -3
- data/test/graphql/type/scalar/json_scalar_test.rb +23 -0
- data/test/graphql/type_map_test.rb +51 -66
- data/test/graphql/type_test.rb +0 -19
- data/test/graphql_test.rb +1 -1
- data/test/integration/{authorization/authorization_test.rb → authorization_test.rb} +40 -14
- data/test/integration/config.rb +36 -3
- data/test/integration/customization_test.rb +39 -0
- data/test/integration/global_id_test.rb +99 -0
- data/test/integration/memory/star_wars_introspection_test.rb +24 -16
- data/test/integration/memory/star_wars_query_test.rb +54 -3
- data/test/integration/memory/star_wars_validation_test.rb +1 -1
- data/test/integration/mysql/star_wars_introspection_test.rb +25 -0
- data/test/integration/persisted_query_test.rb +87 -0
- data/test/integration/resolver_precedence_test.rb +154 -0
- data/test/integration/schemas/memory.rb +22 -7
- data/test/integration/schemas/mysql.rb +62 -0
- data/test/integration/schemas/sqlite.rb +21 -12
- data/test/integration/sqlite/star_wars_global_id_test.rb +83 -0
- data/test/integration/sqlite/star_wars_introspection_test.rb +10 -0
- data/test/integration/sqlite/star_wars_query_test.rb +14 -1
- data/test/integration/translate_test.rb +61 -0
- data/test/test_ext.rb +16 -13
- metadata +108 -157
- data/ext/depend +0 -3
- data/ext/graphqlparser/Ast.cpp +0 -346
- data/ext/graphqlparser/Ast.h +0 -1214
- data/ext/graphqlparser/AstNode.h +0 -36
- data/ext/graphqlparser/AstVisitor.h +0 -137
- data/ext/graphqlparser/GraphQLParser.cpp +0 -76
- data/ext/graphqlparser/GraphQLParser.h +0 -55
- data/ext/graphqlparser/JsonVisitor.cpp +0 -161
- data/ext/graphqlparser/JsonVisitor.cpp.inc +0 -456
- data/ext/graphqlparser/JsonVisitor.h +0 -121
- data/ext/graphqlparser/JsonVisitor.h.inc +0 -110
- data/ext/graphqlparser/VERSION +0 -1
- data/ext/graphqlparser/c/GraphQLAst.cpp +0 -324
- data/ext/graphqlparser/c/GraphQLAst.h +0 -180
- data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +0 -44
- data/ext/graphqlparser/c/GraphQLAstNode.cpp +0 -25
- data/ext/graphqlparser/c/GraphQLAstNode.h +0 -33
- data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +0 -21
- data/ext/graphqlparser/c/GraphQLAstToJSON.h +0 -24
- data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +0 -55
- data/ext/graphqlparser/c/GraphQLAstVisitor.h +0 -53
- data/ext/graphqlparser/c/GraphQLParser.cpp +0 -35
- data/ext/graphqlparser/c/GraphQLParser.h +0 -54
- data/ext/graphqlparser/dump_json_ast.cpp +0 -48
- data/ext/graphqlparser/lexer.lpp +0 -324
- data/ext/graphqlparser/parser.ypp +0 -693
- data/ext/graphqlparser/parsergen/lexer.cpp +0 -2633
- data/ext/graphqlparser/parsergen/lexer.h +0 -528
- data/ext/graphqlparser/parsergen/location.hh +0 -189
- data/ext/graphqlparser/parsergen/parser.tab.cpp +0 -3300
- data/ext/graphqlparser/parsergen/parser.tab.hpp +0 -646
- data/ext/graphqlparser/parsergen/position.hh +0 -179
- data/ext/graphqlparser/parsergen/stack.hh +0 -156
- data/ext/graphqlparser/syntaxdefs.h +0 -19
- data/ext/libgraphqlparser/AstNode.h +0 -36
- data/ext/libgraphqlparser/CMakeLists.txt +0 -148
- data/ext/libgraphqlparser/CONTRIBUTING.md +0 -23
- data/ext/libgraphqlparser/GraphQLParser.cpp +0 -76
- data/ext/libgraphqlparser/GraphQLParser.h +0 -55
- data/ext/libgraphqlparser/JsonVisitor.cpp +0 -161
- data/ext/libgraphqlparser/JsonVisitor.h +0 -121
- data/ext/libgraphqlparser/LICENSE +0 -22
- data/ext/libgraphqlparser/README.clang-tidy +0 -7
- data/ext/libgraphqlparser/README.md +0 -84
- data/ext/libgraphqlparser/ast/ast.ast +0 -203
- data/ext/libgraphqlparser/ast/ast.py +0 -61
- data/ext/libgraphqlparser/ast/c.py +0 -100
- data/ext/libgraphqlparser/ast/c.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_impl.py +0 -61
- data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.py +0 -39
- data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/casing.py +0 -26
- data/ext/libgraphqlparser/ast/casing.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx.py +0 -197
- data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_impl.py +0 -61
- data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +0 -42
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +0 -80
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.py +0 -64
- data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
- data/ext/libgraphqlparser/ast/js.py +0 -65
- data/ext/libgraphqlparser/ast/license.py +0 -10
- data/ext/libgraphqlparser/ast/license.pyc +0 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +0 -25
- data/ext/libgraphqlparser/c/GraphQLAstNode.h +0 -33
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +0 -21
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +0 -24
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +0 -55
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +0 -53
- data/ext/libgraphqlparser/c/GraphQLParser.cpp +0 -35
- data/ext/libgraphqlparser/c/GraphQLParser.h +0 -54
- data/ext/libgraphqlparser/clang-tidy-all.sh +0 -3
- data/ext/libgraphqlparser/cmake/version.cmake +0 -16
- data/ext/libgraphqlparser/dump_json_ast.cpp +0 -48
- data/ext/libgraphqlparser/go/README.md +0 -20
- data/ext/libgraphqlparser/go/callbacks.go +0 -18
- data/ext/libgraphqlparser/go/gotest.go +0 -64
- data/ext/libgraphqlparser/lexer.lpp +0 -324
- data/ext/libgraphqlparser/libgraphqlparser.pc.in +0 -11
- data/ext/libgraphqlparser/parser.ypp +0 -693
- data/ext/libgraphqlparser/parsergen/lexer.cpp +0 -2633
- data/ext/libgraphqlparser/parsergen/lexer.h +0 -528
- data/ext/libgraphqlparser/parsergen/location.hh +0 -189
- data/ext/libgraphqlparser/parsergen/parser.tab.cpp +0 -3300
- data/ext/libgraphqlparser/parsergen/parser.tab.hpp +0 -646
- data/ext/libgraphqlparser/parsergen/position.hh +0 -179
- data/ext/libgraphqlparser/parsergen/stack.hh +0 -156
- data/ext/libgraphqlparser/python/CMakeLists.txt +0 -14
- data/ext/libgraphqlparser/python/README.md +0 -5
- data/ext/libgraphqlparser/python/example.py +0 -31
- data/ext/libgraphqlparser/syntaxdefs.h +0 -19
- data/ext/libgraphqlparser/test/BuildCAPI.c +0 -5
- data/ext/libgraphqlparser/test/CMakeLists.txt +0 -25
- data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +0 -28
- data/ext/libgraphqlparser/test/ParserTests.cpp +0 -352
- data/ext/libgraphqlparser/test/kitchen-sink.graphql +0 -59
- data/ext/libgraphqlparser/test/kitchen-sink.json +0 -1
- data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +0 -78
- data/ext/libgraphqlparser/test/schema-kitchen-sink.json +0 -1
- data/ext/libgraphqlparser/test/valgrind.supp +0 -33
- data/ext/version.cpp +0 -21
- data/lib/graphqlparser.so +0 -0
- data/lib/rails/graphql/native/functions.rb +0 -38
- data/lib/rails/graphql/native/location.rb +0 -41
- data/lib/rails/graphql/native/pointers.rb +0 -23
- data/lib/rails/graphql/native/visitor.rb +0 -349
- data/lib/rails/graphql/native.rb +0 -56
- 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
|
+
}
|