graphql 0.9.5 → 0.10.0
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/lib/graphql/base_type.rb +24 -0
- data/lib/graphql/definition_helpers/defined_by_config.rb +5 -4
- data/lib/graphql/definition_helpers/non_null_with_bang.rb +1 -1
- data/lib/graphql/definition_helpers/type_definer.rb +1 -1
- data/lib/graphql/enum_type.rb +16 -3
- data/lib/graphql/input_object_type.rb +10 -0
- data/lib/graphql/language.rb +0 -5
- data/lib/graphql/language/nodes.rb +79 -75
- data/lib/graphql/language/parser.rb +109 -106
- data/lib/graphql/language/transform.rb +100 -91
- data/lib/graphql/language/visitor.rb +78 -74
- data/lib/graphql/list_type.rb +5 -0
- data/lib/graphql/non_null_type.rb +6 -2
- data/lib/graphql/query.rb +58 -9
- data/lib/graphql/query/arguments.rb +29 -26
- data/lib/graphql/query/base_execution/value_resolution.rb +3 -3
- data/lib/graphql/query/directive_chain.rb +1 -1
- data/lib/graphql/query/executor.rb +6 -27
- data/lib/graphql/query/literal_input.rb +89 -0
- data/lib/graphql/query/ruby_input.rb +20 -0
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/variables.rb +39 -0
- data/lib/graphql/scalar_type.rb +27 -5
- data/lib/graphql/schema.rb +5 -0
- data/lib/graphql/schema/type_expression.rb +28 -0
- data/lib/graphql/static_validation/literal_validator.rb +5 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +0 -2
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +3 -2
- data/spec/graphql/enum_type_spec.rb +7 -2
- data/spec/graphql/input_object_type_spec.rb +45 -0
- data/spec/graphql/language/parser_spec.rb +2 -1
- data/spec/graphql/language/transform_spec.rb +5 -0
- data/spec/graphql/query/base_execution/value_resolution_spec.rb +46 -0
- data/spec/graphql/query/context_spec.rb +37 -0
- data/spec/graphql/query/executor_spec.rb +33 -0
- data/spec/graphql/query_spec.rb +110 -26
- data/spec/graphql/schema/type_expression_spec.rb +38 -0
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +2 -2
- data/spec/support/dairy_app.rb +17 -17
- data/spec/support/dairy_data.rb +2 -2
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9cb8700392b11bc4b4e577e38b424c13b3d33dd9
|
4
|
+
data.tar.gz: 36f2b6d48b2d11084aeb4d0ea50c194e1f2a4b27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8498188be3457f626d74653e48bee19558fc3030f383d8fe65cdf3cfa7f0cf9c8eeabafb1de634a48f9f991de77a0e2630056356de5854af78adbc03881cc14
|
7
|
+
data.tar.gz: de3a1fcfdecefe84e9ed578c9840e861670df763dfb7bd96b11e97e7971c6cbd10c669674cc574904ae9fd0b0db29d70f3dac1bf2aa1f3e430320067dd88859a
|
data/lib/graphql/base_type.rb
CHANGED
@@ -20,6 +20,16 @@ module GraphQL
|
|
20
20
|
self
|
21
21
|
end
|
22
22
|
|
23
|
+
# @return [GraphQL::NonNullType] a non-null version of this type
|
24
|
+
def to_non_null_type
|
25
|
+
GraphQL::NonNullType.new(of_type: self)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [GraphQL::ListType] a list version of this type
|
29
|
+
def to_list_type
|
30
|
+
GraphQL::ListType.new(of_type: self)
|
31
|
+
end
|
32
|
+
|
23
33
|
module ModifiesAnotherType
|
24
34
|
def unwrap
|
25
35
|
self.of_type.unwrap
|
@@ -65,5 +75,19 @@ module GraphQL
|
|
65
75
|
end
|
66
76
|
|
67
77
|
alias :inspect :to_s
|
78
|
+
|
79
|
+
# Coerce `input_value` according to this type's `coerce` method.
|
80
|
+
# Raise an error if the value becomes nil.
|
81
|
+
# @param [Object] Incoming query value
|
82
|
+
# @return [Object] Coerced value for query execution
|
83
|
+
def coerce_input!(input_value)
|
84
|
+
coerced_value = coerce_input(input_value)
|
85
|
+
|
86
|
+
if coerced_value.nil?
|
87
|
+
raise GraphQL::ExecutionError.new("Couldn't coerce #{input_value.inspect} to #{self.unwrap.name}")
|
88
|
+
end
|
89
|
+
|
90
|
+
coerced_value
|
91
|
+
end
|
68
92
|
end
|
69
93
|
end
|
@@ -31,7 +31,9 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
31
31
|
:possible_types, # interface / union
|
32
32
|
:default_value, # argument
|
33
33
|
:on, # directive
|
34
|
-
:coerce #scalar
|
34
|
+
:coerce, #scalar
|
35
|
+
:coerce_input, #scalar
|
36
|
+
:coerce_result #scalar
|
35
37
|
|
36
38
|
attr_reader :fields, :input_fields, :arguments, :values
|
37
39
|
|
@@ -41,7 +43,7 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
41
43
|
@on = []
|
42
44
|
@fields = {}
|
43
45
|
@arguments = {}
|
44
|
-
@values =
|
46
|
+
@values = []
|
45
47
|
@input_fields = {}
|
46
48
|
end
|
47
49
|
|
@@ -64,8 +66,7 @@ module GraphQL::DefinitionHelpers::DefinedByConfig
|
|
64
66
|
|
65
67
|
# For EnumType
|
66
68
|
def value(name, desc = nil, deprecation_reason: nil, value: name)
|
67
|
-
|
68
|
-
values[name] = value
|
69
|
+
values << GraphQL::EnumType::EnumValue.new(name: name, description: description, deprecation_reason: deprecation_reason, value: value)
|
69
70
|
end
|
70
71
|
|
71
72
|
# For InputObjectType
|
data/lib/graphql/enum_type.rb
CHANGED
@@ -14,8 +14,17 @@ class GraphQL::EnumType < GraphQL::BaseType
|
|
14
14
|
attr_accessor :name, :description, :values
|
15
15
|
defined_by_config :name, :description, :values
|
16
16
|
|
17
|
+
def values=(values)
|
18
|
+
@values_by_name = {}
|
19
|
+
@values_by_value = {}
|
20
|
+
values.each do |enum_value|
|
21
|
+
@values_by_name[enum_value.name] = enum_value
|
22
|
+
@values_by_value[enum_value.value] = enum_value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
17
26
|
def values
|
18
|
-
@
|
27
|
+
@values_by_name
|
19
28
|
end
|
20
29
|
|
21
30
|
# Define a value within this enum
|
@@ -40,8 +49,12 @@ class GraphQL::EnumType < GraphQL::BaseType
|
|
40
49
|
#
|
41
50
|
# @param value_name [String] the string representation of this enum value
|
42
51
|
# @return [Object] the underlying value for this enum value
|
43
|
-
def
|
44
|
-
|
52
|
+
def coerce_input(value_name)
|
53
|
+
@values_by_name.fetch(value_name).value
|
54
|
+
end
|
55
|
+
|
56
|
+
def coerce_result(value)
|
57
|
+
@values_by_value.fetch(value).name
|
45
58
|
end
|
46
59
|
|
47
60
|
def to_s
|
@@ -18,4 +18,14 @@ class GraphQL::InputObjectType < GraphQL::BaseType
|
|
18
18
|
def kind
|
19
19
|
GraphQL::TypeKinds::INPUT_OBJECT
|
20
20
|
end
|
21
|
+
|
22
|
+
def coerce_input(value)
|
23
|
+
input_values = {}
|
24
|
+
input_fields.each do |input_key, input_field_defn|
|
25
|
+
raw_value = value.fetch(input_key, input_field_defn.default_value)
|
26
|
+
field_type = input_field_defn.type
|
27
|
+
input_values[input_key] = field_type.coerce_input!(raw_value)
|
28
|
+
end
|
29
|
+
GraphQL::Query::Arguments.new(input_values)
|
30
|
+
end
|
21
31
|
end
|
data/lib/graphql/language.rb
CHANGED
@@ -1,89 +1,93 @@
|
|
1
|
-
module GraphQL
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
module Nodes
|
4
|
+
# AbstractNode creates classes who:
|
5
|
+
# - require their keyword arguments, throw ArgumentError if they don't match
|
6
|
+
# - expose accessors for keyword arguments
|
7
|
+
class AbstractNode
|
8
|
+
attr_accessor :line, :col
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
# @param options [Hash] Must contain all attributes defined by {required_attrs}, may also include `position_source`
|
11
|
+
def initialize(options)
|
12
|
+
required_keys = self.class.required_attrs
|
13
|
+
allowed_keys = required_keys + [:line, :col]
|
14
|
+
position_source = options.delete(:position_source)
|
15
|
+
if !position_source.nil?
|
16
|
+
options[:line], options[:col] = position_source.line_and_column
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
present_keys = options.keys
|
20
|
+
extra_keys = present_keys - allowed_keys
|
21
|
+
if extra_keys.any?
|
22
|
+
raise ArgumentError, "#{self.class.name} Extra arguments: #{extra_keys}"
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
missing_keys = required_keys - present_keys
|
26
|
+
if missing_keys.any?
|
27
|
+
raise ArgumentError, "#{self.class.name} Missing arguments: #{missing_keys}"
|
28
|
+
end
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
allowed_keys.each do |key|
|
31
|
+
if options.has_key?(key)
|
32
|
+
value = options[key]
|
33
|
+
self.send("#{key}=", value)
|
34
|
+
end
|
35
|
+
end
|
32
36
|
end
|
33
|
-
end
|
34
|
-
end
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
# Test all attributes, checking for any other nodes below this one
|
39
|
+
def children
|
40
|
+
self.class.required_attrs
|
41
|
+
.map { |attr| send(attr) }
|
42
|
+
.flatten
|
43
|
+
.select { |val| val.is_a?(GraphQL::Language::Nodes::AbstractNode) }
|
44
|
+
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
class << self
|
47
|
+
attr_reader :required_attrs
|
48
|
+
# Defines attributes which are required at initialization.
|
49
|
+
def attr_required(*attr_names)
|
50
|
+
@required_attrs ||= []
|
51
|
+
@required_attrs += attr_names
|
52
|
+
attr_accessor(*attr_names)
|
53
|
+
end
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
# Create a new AbstractNode child which
|
56
|
+
# requires and exposes {attr_names}.
|
57
|
+
# @param attr_names [Array<Symbol>] Attributes this node class will have
|
58
|
+
# @param block [Block] Block passed to `Class.new`
|
59
|
+
# @return [Class] A new node class
|
60
|
+
def create(*attr_names, &block)
|
61
|
+
cls = Class.new(self, &block)
|
62
|
+
cls.attr_required(*attr_names)
|
63
|
+
cls
|
64
|
+
end
|
65
|
+
end
|
62
66
|
end
|
63
|
-
end
|
64
|
-
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
68
|
+
Argument = AbstractNode.create(:name, :value)
|
69
|
+
Directive = AbstractNode.create(:name, :arguments)
|
70
|
+
Document = AbstractNode.create(:parts)
|
71
|
+
Enum = AbstractNode.create(:name)
|
72
|
+
Field = AbstractNode.create(:name, :alias, :arguments, :directives, :selections)
|
73
|
+
FragmentDefinition = AbstractNode.create(:name, :type, :directives, :selections)
|
74
|
+
FragmentSpread = AbstractNode.create(:name, :directives)
|
75
|
+
InlineFragment = AbstractNode.create(:type, :directives, :selections)
|
76
|
+
InputObject = AbstractNode.create(:pairs) do
|
77
|
+
def to_h(options={})
|
78
|
+
pairs.inject({}) do |memo, pair|
|
79
|
+
v = pair.value
|
80
|
+
memo[pair.name] = v.is_a?(InputObject) ? v.to_h : v
|
81
|
+
memo
|
82
|
+
end
|
83
|
+
end
|
80
84
|
end
|
85
|
+
ListType = AbstractNode.create(:of_type)
|
86
|
+
NonNullType = AbstractNode.create(:of_type)
|
87
|
+
OperationDefinition = AbstractNode.create(:operation_type, :name, :variables, :directives, :selections)
|
88
|
+
TypeName = AbstractNode.create(:name)
|
89
|
+
Variable = AbstractNode.create(:name, :type, :default_value)
|
90
|
+
VariableIdentifier = AbstractNode.create(:name)
|
81
91
|
end
|
82
92
|
end
|
83
|
-
ListType = AbstractNode.create(:of_type)
|
84
|
-
NonNullType = AbstractNode.create(:of_type)
|
85
|
-
OperationDefinition = AbstractNode.create(:operation_type, :name, :variables, :directives, :selections)
|
86
|
-
TypeName = AbstractNode.create(:name)
|
87
|
-
Variable = AbstractNode.create(:name, :type, :default_value)
|
88
|
-
VariableIdentifier = AbstractNode.create(:name)
|
89
93
|
end
|
@@ -1,118 +1,121 @@
|
|
1
|
-
module GraphQL
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
module GraphQL
|
2
|
+
module Language
|
3
|
+
# Parser is a [parslet](http://kschiess.github.io/parslet/) parser for parsing queries.
|
4
|
+
#
|
5
|
+
class Parser < Parslet::Parser
|
6
|
+
root(:document)
|
7
|
+
rule(:document) { (
|
8
|
+
space |
|
9
|
+
operation_definition |
|
10
|
+
fragment_definition
|
11
|
+
).repeat.as(:document_parts)
|
12
|
+
}
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
# TODO: whitespace sensitive regarding `on`, eg `onFood`, see lookahead note in spec
|
15
|
+
rule(:fragment_definition) {
|
16
|
+
str("fragment").as(:fragment_keyword) >>
|
17
|
+
space? >> name.as(:fragment_name) >>
|
18
|
+
space? >> str("on") >> space? >> name.as(:type_condition) >>
|
19
|
+
space? >> directives.maybe.as(:optional_directives).as(:directives) >>
|
20
|
+
space? >> selections.as(:selections)
|
21
|
+
}
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
rule(:fragment_spread) {
|
24
|
+
spread.as(:fragment_spread_keyword) >> space? >>
|
25
|
+
name.as(:fragment_spread_name) >> space? >>
|
26
|
+
directives.maybe.as(:optional_directives).as(:directives)
|
27
|
+
}
|
28
|
+
rule(:spread) { str("...") }
|
29
|
+
# TODO: `on` bug, see spec
|
30
|
+
rule(:inline_fragment) {
|
31
|
+
spread.as(:fragment_spread_keyword) >> space? >>
|
32
|
+
str("on ") >> name.as(:inline_fragment_type) >> space? >>
|
33
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
34
|
+
selections.as(:selections)
|
35
|
+
}
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
37
|
+
rule(:operation_definition) { (unnamed_selections | typed_operation_definition) }
|
38
|
+
rule(:unnamed_selections) { selections.as(:unnamed_selections)}
|
39
|
+
rule(:typed_operation_definition) {
|
40
|
+
operation_type.as(:operation_type) >> space? >>
|
41
|
+
name.maybe.as(:name) >> space? >>
|
42
|
+
operation_variable_definitions.maybe.as(:optional_variables).as(:variables) >> space? >>
|
43
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
44
|
+
selections.as(:selections)
|
45
|
+
}
|
46
|
+
rule(:operation_type) { (str("query") | str("mutation")) }
|
47
|
+
rule(:operation_variable_definitions) { str("(") >> space? >> (operation_variable_definition >> separator?).repeat(1) >> space? >> str(")") }
|
48
|
+
rule(:operation_variable_definition) {
|
49
|
+
value_variable.as(:variable_name) >> space? >>
|
50
|
+
str(":") >> space? >>
|
51
|
+
type.as(:variable_type) >> space? >>
|
52
|
+
(str("=") >> space? >> value.as(:variable_default_value)).maybe.as(:variable_optional_default_value)
|
53
|
+
}
|
52
54
|
|
53
|
-
|
54
|
-
|
55
|
+
rule(:selection) { (inline_fragment | fragment_spread | field) >> space? >> separator? }
|
56
|
+
rule(:selections) { str("{") >> space? >> selection.repeat(1) >> space? >> str("}")}
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
58
|
+
rule(:field) {
|
59
|
+
field_alias.maybe.as(:alias) >>
|
60
|
+
name.as(:field_name) >>
|
61
|
+
field_arguments.maybe.as(:optional_field_arguments).as(:field_arguments) >> space? >>
|
62
|
+
directives.maybe.as(:optional_directives).as(:directives) >> space? >>
|
63
|
+
selections.maybe.as(:optional_selections).as(:selections)
|
64
|
+
}
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
|
66
|
+
rule(:field_alias) { name.as(:alias_name) >> space? >> str(":") >> space? }
|
67
|
+
rule(:field_arguments) { str("(") >> field_argument.repeat(1) >> str(")") }
|
68
|
+
rule(:field_argument) { name.as(:field_argument_name) >> str(":") >> space? >> value.as(:field_argument_value) >> separator? }
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
rule(:directives) { (directive >> separator?).repeat(1) }
|
71
|
+
rule(:directive) {
|
72
|
+
str("@") >> name.as(:directive_name) >>
|
73
|
+
directive_arguments.maybe.as(:optional_directive_arguments).as(:directive_arguments)
|
74
|
+
}
|
75
|
+
rule(:directive_arguments) { str("(") >> directive_argument.repeat(1) >> str(")") }
|
76
|
+
rule(:directive_argument) { name.as(:directive_argument_name) >> str(":") >> space? >> value.as(:directive_argument_value) >> separator? }
|
75
77
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
rule(:type) { (non_null_type | list_type | type_name)}
|
79
|
+
rule(:list_type) { str("[") >> type.as(:list_type) >> str("]")}
|
80
|
+
rule(:non_null_type) { (list_type | type_name).as(:non_null_type) >> str("!") }
|
81
|
+
rule(:type_name) { name.as(:type_name) }
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
83
|
+
rule(:value) {(
|
84
|
+
value_input_object |
|
85
|
+
value_float |
|
86
|
+
value_int |
|
87
|
+
value_string |
|
88
|
+
value_boolean |
|
89
|
+
value_array |
|
90
|
+
value_variable |
|
91
|
+
value_enum
|
92
|
+
)}
|
93
|
+
rule(:value_sign?) { match('[\-\+]').maybe }
|
94
|
+
rule(:value_array) { (str("[") >> (value >> separator?).repeat(0) >> str("]")).as(:array) }
|
95
|
+
rule(:value_boolean) { (str("true") | str("false")).as(:boolean) }
|
96
|
+
rule(:value_float) { (value_sign? >> match('\d').repeat(1) >> str(".") >> match('\d').repeat(1) >> (match("[eE]") >> value_sign? >> match('\d').repeat(1)).maybe).as(:float) }
|
97
|
+
rule(:value_input_object) { str("{") >> space? >> value_input_object_pair.repeat(1).as(:input_object) >> space? >> str("}") }
|
98
|
+
rule(:value_input_object_pair) { space? >> name.as(:input_object_name) >> space? >> str(":") >> space? >> value.as(:input_object_value) >> separator? }
|
99
|
+
rule(:value_int) { (value_sign? >> match('\d').repeat(1)).as(:int) }
|
100
|
+
rule(:value_string) { str('"') >> value_string_char.repeat.as(:string) >> str('"')}
|
101
|
+
rule(:value_string_char) { value_string_escaped_char | value_string_escaped_unicode | value_string_source_char}
|
102
|
+
rule(:value_string_escaped_char) { str("\\") >> match('["\/bfnrt]') }
|
103
|
+
rule(:value_string_escaped_unicode) { str("\\") >> match('u[\dA-Fa-f]{4}')}
|
104
|
+
rule(:value_string_source_char) { (str('"') | str("\\") | value_string_line_terminator).absent? >> any }
|
105
|
+
rule(:value_string_line_terminator) {
|
106
|
+
str('\u000A') | # new line
|
107
|
+
str('\u000D') | # carriage return
|
108
|
+
str('\u2028') | # line separator
|
109
|
+
str('\u2029') # paragraph separator
|
110
|
+
}
|
111
|
+
rule(:value_enum) { name.as(:enum) }
|
112
|
+
rule(:value_variable) { str("$") >> name.as(:variable) }
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
114
|
+
rule(:separator?) { space? >> str(",").maybe >> space? }
|
115
|
+
rule(:name) { match('[_A-Za-z]') >> match('[_0-9A-Za-z]').repeat(0) }
|
116
|
+
rule(:comment) { str("#") >> match('[^\r\n]').repeat(0) }
|
117
|
+
rule(:space) { (match('[\s\n]+') | comment).repeat(1) }
|
118
|
+
rule(:space?) { space.maybe }
|
119
|
+
end
|
117
120
|
end
|
118
121
|
end
|