hocon 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +4 -2
- data/lib/hocon.rb +2 -0
- data/lib/hocon/config.rb +1010 -0
- data/lib/hocon/config_error.rb +32 -2
- data/lib/hocon/config_factory.rb +46 -0
- data/lib/hocon/config_include_context.rb +49 -0
- data/lib/hocon/config_includer_file.rb +27 -0
- data/lib/hocon/config_list.rb +49 -0
- data/lib/hocon/config_mergeable.rb +74 -0
- data/lib/hocon/config_object.rb +144 -1
- data/lib/hocon/config_parse_options.rb +33 -9
- data/lib/hocon/config_parseable.rb +51 -0
- data/lib/hocon/config_render_options.rb +4 -2
- data/lib/hocon/config_resolve_options.rb +31 -0
- data/lib/hocon/config_syntax.rb +5 -2
- data/lib/hocon/config_util.rb +73 -0
- data/lib/hocon/config_value.rb +122 -0
- data/lib/hocon/config_value_factory.rb +66 -2
- data/lib/hocon/config_value_type.rb +5 -2
- data/lib/hocon/impl.rb +2 -0
- data/lib/hocon/impl/abstract_config_node.rb +29 -0
- data/lib/hocon/impl/abstract_config_node_value.rb +11 -0
- data/lib/hocon/impl/abstract_config_object.rb +148 -42
- data/lib/hocon/impl/abstract_config_value.rb +251 -11
- data/lib/hocon/impl/array_iterator.rb +19 -0
- data/lib/hocon/impl/config_boolean.rb +7 -1
- data/lib/hocon/impl/config_concatenation.rb +177 -28
- data/lib/hocon/impl/config_delayed_merge.rb +329 -0
- data/lib/hocon/impl/config_delayed_merge_object.rb +274 -0
- data/lib/hocon/impl/config_document_parser.rb +647 -0
- data/lib/hocon/impl/config_double.rb +44 -0
- data/lib/hocon/impl/config_impl.rb +143 -19
- data/lib/hocon/impl/config_impl_util.rb +18 -0
- data/lib/hocon/impl/config_include_kind.rb +10 -0
- data/lib/hocon/impl/config_int.rb +13 -1
- data/lib/hocon/impl/config_node_array.rb +11 -0
- data/lib/hocon/impl/config_node_comment.rb +19 -0
- data/lib/hocon/impl/config_node_complex_value.rb +54 -0
- data/lib/hocon/impl/config_node_concatenation.rb +11 -0
- data/lib/hocon/impl/config_node_field.rb +81 -0
- data/lib/hocon/impl/config_node_include.rb +33 -0
- data/lib/hocon/impl/config_node_object.rb +276 -0
- data/lib/hocon/impl/config_node_path.rb +48 -0
- data/lib/hocon/impl/config_node_root.rb +60 -0
- data/lib/hocon/impl/config_node_simple_value.rb +42 -0
- data/lib/hocon/impl/config_node_single_token.rb +17 -0
- data/lib/hocon/impl/config_null.rb +15 -7
- data/lib/hocon/impl/config_number.rb +43 -4
- data/lib/hocon/impl/config_parser.rb +403 -0
- data/lib/hocon/impl/config_reference.rb +142 -0
- data/lib/hocon/impl/config_string.rb +55 -7
- data/lib/hocon/impl/container.rb +29 -0
- data/lib/hocon/impl/default_transformer.rb +24 -15
- data/lib/hocon/impl/from_map_mode.rb +3 -1
- data/lib/hocon/impl/full_includer.rb +2 -0
- data/lib/hocon/impl/memo_key.rb +42 -0
- data/lib/hocon/impl/mergeable_value.rb +8 -0
- data/lib/hocon/impl/origin_type.rb +8 -2
- data/lib/hocon/impl/parseable.rb +455 -91
- data/lib/hocon/impl/path.rb +181 -59
- data/lib/hocon/impl/path_builder.rb +24 -3
- data/lib/hocon/impl/path_parser.rb +280 -0
- data/lib/hocon/impl/replaceable_merge_stack.rb +22 -0
- data/lib/hocon/impl/resolve_context.rb +254 -0
- data/lib/hocon/impl/resolve_memos.rb +21 -0
- data/lib/hocon/impl/resolve_result.rb +39 -0
- data/lib/hocon/impl/resolve_source.rb +354 -0
- data/lib/hocon/impl/resolve_status.rb +3 -1
- data/lib/hocon/impl/simple_config.rb +264 -10
- data/lib/hocon/impl/simple_config_document.rb +48 -0
- data/lib/hocon/impl/simple_config_list.rb +282 -8
- data/lib/hocon/impl/simple_config_object.rb +424 -88
- data/lib/hocon/impl/simple_config_origin.rb +263 -71
- data/lib/hocon/impl/simple_include_context.rb +31 -1
- data/lib/hocon/impl/simple_includer.rb +196 -1
- data/lib/hocon/impl/substitution_expression.rb +38 -0
- data/lib/hocon/impl/token.rb +17 -4
- data/lib/hocon/impl/token_type.rb +6 -2
- data/lib/hocon/impl/tokenizer.rb +339 -109
- data/lib/hocon/impl/tokens.rb +330 -79
- data/lib/hocon/impl/unmergeable.rb +14 -1
- data/lib/hocon/impl/unsupported_operation_error.rb +6 -0
- data/lib/hocon/impl/url.rb +37 -0
- data/lib/hocon/parser.rb +7 -0
- data/lib/hocon/parser/config_document.rb +92 -0
- data/lib/hocon/parser/config_document_factory.rb +36 -0
- data/lib/hocon/parser/config_node.rb +30 -0
- metadata +67 -43
- data/lib/hocon/impl/config_float.rb +0 -13
- data/lib/hocon/impl/parser.rb +0 -977
- data/lib/hocon/impl/properties_parser.rb +0 -83
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon/impl'
|
4
|
+
require 'hocon/impl/config_node_array'
|
5
|
+
require 'hocon/impl/config_node_complex_value'
|
6
|
+
require 'hocon/impl/config_node_object'
|
7
|
+
|
8
|
+
class Hocon::Impl::ConfigNodeRoot
|
9
|
+
include Hocon::Impl::ConfigNodeComplexValue
|
10
|
+
def initialize(children, origin)
|
11
|
+
super(children)
|
12
|
+
@origin = origin
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_node(nodes)
|
16
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "Tried to indent the root object"
|
17
|
+
end
|
18
|
+
|
19
|
+
def value
|
20
|
+
@children.each do |node|
|
21
|
+
if node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
|
22
|
+
return node
|
23
|
+
end
|
24
|
+
end
|
25
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value"
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_value(desired_path, value, flavor)
|
29
|
+
children_copy = @children.clone
|
30
|
+
children_copy.each_with_index do |node, index|
|
31
|
+
if node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
|
32
|
+
if node.is_a?(Hocon::Impl::ConfigNodeArray)
|
33
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array."
|
34
|
+
elsif node.is_a?(Hocon::Impl::ConfigNodeObject)
|
35
|
+
if value.nil?
|
36
|
+
children_copy[index] = node.remove_value_on_path(desired_path, flavor)
|
37
|
+
else
|
38
|
+
children_copy[index] = node.set_value_on_path(desired_path, value, flavor)
|
39
|
+
end
|
40
|
+
return self.class.new(children_copy, @origin)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value"
|
45
|
+
end
|
46
|
+
|
47
|
+
def has_value(desired_path)
|
48
|
+
path = Hocon::Impl::PathParser.parse_path(desired_path)
|
49
|
+
@children.each do |node|
|
50
|
+
if node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
|
51
|
+
if node.is_a?(Hocon::Impl::ConfigNodeArray)
|
52
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "The ConfigDocument had an array at the root level, and values cannot be modified inside an array."
|
53
|
+
elsif node.is_a?(Hocon::Impl::ConfigNodeObject)
|
54
|
+
return node.has_value(path)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "ConfigNodeRoot did not contain a value"
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon/config_error'
|
4
|
+
require 'hocon/impl'
|
5
|
+
require 'hocon/impl/abstract_config_node_value'
|
6
|
+
require 'hocon/impl/array_iterator'
|
7
|
+
require 'hocon/impl/config_reference'
|
8
|
+
require 'hocon/impl/config_string'
|
9
|
+
require 'hocon/impl/path_parser'
|
10
|
+
require 'hocon/impl/substitution_expression'
|
11
|
+
require 'hocon/impl/tokens'
|
12
|
+
|
13
|
+
class Hocon::Impl::ConfigNodeSimpleValue
|
14
|
+
include Hocon::Impl::AbstractConfigNodeValue
|
15
|
+
|
16
|
+
Tokens = Hocon::Impl::Tokens
|
17
|
+
|
18
|
+
def initialize(value)
|
19
|
+
@token = value
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :token
|
23
|
+
|
24
|
+
def tokens
|
25
|
+
[@token]
|
26
|
+
end
|
27
|
+
|
28
|
+
def value
|
29
|
+
if Tokens.value?(@token)
|
30
|
+
return Tokens.value(@token)
|
31
|
+
elsif Tokens.unquoted_text?(@token)
|
32
|
+
return Hocon::Impl::ConfigString::Unquoted.new(@token.origin, Tokens.unquoted_text(@token))
|
33
|
+
elsif Tokens.substitution?(@token)
|
34
|
+
expression = Tokens.get_substitution_path_expression(@token)
|
35
|
+
path = Hocon::Impl::PathParser.parse_path_expression(Hocon::Impl::ArrayIterator.new(expression), @token.origin)
|
36
|
+
optional = Tokens.get_substitution_optional(@token)
|
37
|
+
|
38
|
+
return Hocon::Impl::ConfigReference.new(@token.origin, Hocon::Impl::SubstitutionExpression.new(path, optional))
|
39
|
+
end
|
40
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, 'ConfigNodeSimpleValue did not contain a valid value token'
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'hocon/impl'
|
4
|
+
require 'hocon/impl/abstract_config_node'
|
5
|
+
|
6
|
+
class Hocon::Impl::ConfigNodeSingleToken
|
7
|
+
include Hocon::Impl::AbstractConfigNode
|
8
|
+
def initialize(t)
|
9
|
+
@token = t
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :token
|
13
|
+
|
14
|
+
def tokens
|
15
|
+
[@token]
|
16
|
+
end
|
17
|
+
end
|
@@ -1,9 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
2
4
|
require 'hocon/config_value_type'
|
3
5
|
|
4
|
-
class Hocon::Impl::ConfigNull
|
6
|
+
class Hocon::Impl::ConfigNull
|
7
|
+
include Hocon::Impl::AbstractConfigValue
|
8
|
+
|
9
|
+
def initialize(origin)
|
10
|
+
super(origin)
|
11
|
+
end
|
12
|
+
|
5
13
|
def value_type
|
6
|
-
Hocon::
|
14
|
+
Hocon::ConfigValueType::NULL
|
7
15
|
end
|
8
16
|
|
9
17
|
def unwrapped
|
@@ -14,12 +22,12 @@ class Hocon::Impl::ConfigNull < Hocon::Impl::AbstractConfigValue
|
|
14
22
|
"null"
|
15
23
|
end
|
16
24
|
|
17
|
-
def
|
18
|
-
sb
|
25
|
+
def render_value_to_sb(sb, indent, at_root, options)
|
26
|
+
sb << "null"
|
19
27
|
end
|
20
28
|
|
21
|
-
def
|
22
|
-
|
29
|
+
def new_copy(origin)
|
30
|
+
self.class.new(origin)
|
23
31
|
end
|
24
32
|
|
25
|
-
end
|
33
|
+
end
|
@@ -1,18 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'hocon/impl'
|
2
4
|
require 'hocon/impl/abstract_config_value'
|
3
5
|
|
4
|
-
class Hocon::Impl::ConfigNumber
|
6
|
+
class Hocon::Impl::ConfigNumber
|
7
|
+
include Hocon::Impl::AbstractConfigValue
|
5
8
|
## sigh... requiring these subclasses before this class
|
6
9
|
## is declared would cause an error. Thanks, ruby.
|
7
10
|
require 'hocon/impl/config_int'
|
8
|
-
require 'hocon/impl/
|
11
|
+
require 'hocon/impl/config_double'
|
9
12
|
|
10
13
|
def self.new_number(origin, number, original_text)
|
11
14
|
as_int = number.to_i
|
12
15
|
if as_int == number
|
13
16
|
Hocon::Impl::ConfigInt.new(origin, as_int, original_text)
|
14
17
|
else
|
15
|
-
Hocon::Impl::
|
18
|
+
Hocon::Impl::ConfigDouble.new(origin, number, original_text)
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
@@ -20,8 +23,44 @@ class Hocon::Impl::ConfigNumber < Hocon::Impl::AbstractConfigValue
|
|
20
23
|
super(origin)
|
21
24
|
@original_text = original_text
|
22
25
|
end
|
26
|
+
attr_reader :original_text
|
23
27
|
|
24
28
|
def transform_to_string
|
25
29
|
@original_text
|
26
30
|
end
|
27
|
-
|
31
|
+
|
32
|
+
def int_value_range_checked(path)
|
33
|
+
# We don't need to do any range checking here due to the way Ruby handles
|
34
|
+
# integers (doesn't have the 32-bit/64-bit distinction that Java does).
|
35
|
+
long_value
|
36
|
+
end
|
37
|
+
|
38
|
+
def long_value
|
39
|
+
raise "long_value needs to be overriden by sub-classes of #{Hocon::Impl::ConfigNumber}, in this case #{self.class}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def can_equal(other)
|
43
|
+
other.is_a?(Hocon::Impl::ConfigNumber)
|
44
|
+
end
|
45
|
+
|
46
|
+
def ==(other)
|
47
|
+
if other.is_a?(Hocon::Impl::ConfigNumber) && can_equal(other)
|
48
|
+
@value == other.value
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def hash
|
55
|
+
# This hash function makes it so that a ConfigNumber with a 3.0
|
56
|
+
# and one with a 3 will return the hash code
|
57
|
+
to_int = @value.round
|
58
|
+
|
59
|
+
# If the value is an integer or a floating point equal to an integer
|
60
|
+
if to_int == @value
|
61
|
+
to_int.hash
|
62
|
+
else
|
63
|
+
@value.hash
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,403 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
require 'hocon/impl'
|
5
|
+
require 'hocon/impl/path_builder'
|
6
|
+
require 'hocon/config_syntax'
|
7
|
+
require 'hocon/impl/config_string'
|
8
|
+
require 'hocon/impl/config_concatenation'
|
9
|
+
require 'hocon/config_error'
|
10
|
+
require 'hocon/impl/simple_config_list'
|
11
|
+
require 'hocon/impl/simple_config_object'
|
12
|
+
require 'hocon/impl/path'
|
13
|
+
require 'hocon/impl/url'
|
14
|
+
require 'hocon/impl/config_reference'
|
15
|
+
require 'hocon/impl/substitution_expression'
|
16
|
+
require 'hocon/impl/config_node_simple_value'
|
17
|
+
require 'hocon/impl/config_node_object'
|
18
|
+
require 'hocon/impl/config_node_array'
|
19
|
+
require 'hocon/impl/config_node_concatenation'
|
20
|
+
require 'hocon/impl/config_include_kind'
|
21
|
+
|
22
|
+
class Hocon::Impl::ConfigParser
|
23
|
+
|
24
|
+
ConfigSyntax = Hocon::ConfigSyntax
|
25
|
+
ConfigConcatenation = Hocon::Impl::ConfigConcatenation
|
26
|
+
ConfigReference = Hocon::Impl::ConfigReference
|
27
|
+
ConfigParseError = Hocon::ConfigError::ConfigParseError
|
28
|
+
ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
|
29
|
+
SimpleConfigObject = Hocon::Impl::SimpleConfigObject
|
30
|
+
SimpleConfigList = Hocon::Impl::SimpleConfigList
|
31
|
+
Path = Hocon::Impl::Path
|
32
|
+
ConfigIncludeKind = Hocon::Impl::ConfigIncludeKind
|
33
|
+
ConfigNodeInclude = Hocon::Impl::ConfigNodeInclude
|
34
|
+
ConfigNodeComment = Hocon::Impl::ConfigNodeComment
|
35
|
+
ConfigNodeSingleToken = Hocon::Impl::ConfigNodeSingleToken
|
36
|
+
Tokens = Hocon::Impl::Tokens
|
37
|
+
|
38
|
+
def self.parse(document, origin, options, include_context)
|
39
|
+
context = Hocon::Impl::ConfigParser::ParseContext.new(
|
40
|
+
options.syntax, origin, document,
|
41
|
+
Hocon::Impl::SimpleIncluder.make_full(options.includer),
|
42
|
+
include_context)
|
43
|
+
context.parse
|
44
|
+
end
|
45
|
+
|
46
|
+
class ParseContext
|
47
|
+
def initialize(flavor, origin, document, includer, include_context)
|
48
|
+
@line_number = 1
|
49
|
+
@document = document
|
50
|
+
@flavor = flavor
|
51
|
+
@base_origin = origin
|
52
|
+
@includer = includer
|
53
|
+
@include_context = include_context
|
54
|
+
@path_stack = []
|
55
|
+
@array_count = 0
|
56
|
+
end
|
57
|
+
|
58
|
+
# merge a bunch of adjacent values into one
|
59
|
+
# value; change unquoted text into a string
|
60
|
+
# value.
|
61
|
+
def parse_concatenation(n)
|
62
|
+
# this trick is not done in JSON
|
63
|
+
if @flavor.equal?(ConfigSyntax::JSON)
|
64
|
+
raise ConfigBugOrBrokenError, "Found a concatenation node in JSON"
|
65
|
+
end
|
66
|
+
|
67
|
+
values = []
|
68
|
+
|
69
|
+
n.children.each do |node|
|
70
|
+
if node.is_a?(Hocon::Impl::AbstractConfigNodeValue)
|
71
|
+
v = parse_value(node, nil)
|
72
|
+
values.push(v)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
ConfigConcatenation.concatenate(values)
|
77
|
+
end
|
78
|
+
|
79
|
+
def line_origin
|
80
|
+
@base_origin.with_line_number(@line_number)
|
81
|
+
end
|
82
|
+
|
83
|
+
def parse_error(message, cause = nil)
|
84
|
+
ConfigParseError.new(line_origin, message, cause)
|
85
|
+
end
|
86
|
+
|
87
|
+
def full_current_path
|
88
|
+
# pathStack has top of stack at front
|
89
|
+
if @path_stack.empty?
|
90
|
+
raise ConfigBugOrBrokenError, "Bug in parser; tried to get current path when at root"
|
91
|
+
else
|
92
|
+
Path.from_path_list(@path_stack.reverse)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_value(n, comments)
|
97
|
+
starting_array_count = @array_count
|
98
|
+
|
99
|
+
if n.is_a?(Hocon::Impl::ConfigNodeSimpleValue)
|
100
|
+
v = n.value
|
101
|
+
elsif n.is_a?(Hocon::Impl::ConfigNodeObject)
|
102
|
+
v = parse_object(n)
|
103
|
+
elsif n.is_a?(Hocon::Impl::ConfigNodeArray)
|
104
|
+
v = parse_array(n)
|
105
|
+
elsif n.is_a?(Hocon::Impl::ConfigNodeConcatenation)
|
106
|
+
v = parse_concatenation(n)
|
107
|
+
else
|
108
|
+
raise parse_error("Expecting a value but got wrong node type: #{n.class}")
|
109
|
+
end
|
110
|
+
|
111
|
+
unless comments.nil? || comments.empty?
|
112
|
+
v = v.with_origin(v.origin.prepend_comments(comments.clone))
|
113
|
+
comments.clear
|
114
|
+
end
|
115
|
+
|
116
|
+
unless @array_count == starting_array_count
|
117
|
+
raise ConfigBugOrBrokenError, "Bug in config parser: unbalanced array count"
|
118
|
+
end
|
119
|
+
|
120
|
+
v
|
121
|
+
end
|
122
|
+
|
123
|
+
def create_value_under_path(path, value)
|
124
|
+
# for path foo.bar, we are creating
|
125
|
+
# { "foo" : { "bar" : value } }
|
126
|
+
keys = []
|
127
|
+
|
128
|
+
key = path.first
|
129
|
+
remaining = path.remainder
|
130
|
+
until key.nil?
|
131
|
+
keys.push(key)
|
132
|
+
if remaining.nil?
|
133
|
+
break
|
134
|
+
else
|
135
|
+
key = remaining.first
|
136
|
+
remaining = remaining.remainder
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# the setComments(null) is to ensure comments are only
|
141
|
+
# on the exact leaf node they apply to.
|
142
|
+
# a comment before "foo.bar" applies to the full setting
|
143
|
+
# "foo.bar" not also to "foo"
|
144
|
+
keys = keys.reverse
|
145
|
+
# this is just a ruby means for doing first/rest
|
146
|
+
deepest, *rest = *keys
|
147
|
+
o = SimpleConfigObject.new(value.origin.with_comments(nil),
|
148
|
+
{deepest => value})
|
149
|
+
while !rest.empty?
|
150
|
+
deepest, *rest = *rest
|
151
|
+
o = SimpleConfigObject.new(value.origin.with_comments(nil),
|
152
|
+
{deepest => o})
|
153
|
+
end
|
154
|
+
|
155
|
+
o
|
156
|
+
end
|
157
|
+
|
158
|
+
def parse_include(values, n)
|
159
|
+
case n.kind
|
160
|
+
when ConfigIncludeKind::URL
|
161
|
+
url = nil
|
162
|
+
begin
|
163
|
+
url = Hocon::Impl::Url.new(n.name)
|
164
|
+
rescue Hocon::Impl::Url::MalformedUrlError => e
|
165
|
+
raise parse_error("include url() specifies an invalid URL: #{n.name}", e)
|
166
|
+
end
|
167
|
+
obj = @includer.include_url(@include_context, url)
|
168
|
+
when ConfigIncludeKind::FILE
|
169
|
+
obj = @includer.include_file(@include_context, n.name)
|
170
|
+
when ConfigIncludeKind::CLASSPATH
|
171
|
+
obj = @includer.include_resources(@include_context, n.name)
|
172
|
+
when ConfigIncludeKind::HEURISTIC
|
173
|
+
obj = @includer.include(@include_context, n.name)
|
174
|
+
else
|
175
|
+
raise ConfigBugOrBrokenError, "should not be reached"
|
176
|
+
end
|
177
|
+
|
178
|
+
# we really should make this work, but for now throwing an
|
179
|
+
# exception is better than producing an incorrect result.
|
180
|
+
# See https://github.com/typesafehub/config/issues/160
|
181
|
+
if @array_count > 0 && (obj.resolve_status != Hocon::Impl::ResolveStatus::RESOLVED)
|
182
|
+
raise parse_error("Due to current limitations of the config parser, when an include statement is nested inside a list value, " +
|
183
|
+
"${} substitutions inside the included file cannot be resolved correctly. Either move the include outside of the list value or " +
|
184
|
+
"remove the ${} statements from the included file.")
|
185
|
+
end
|
186
|
+
|
187
|
+
if !(@path_stack.empty?)
|
188
|
+
prefix = full_current_path
|
189
|
+
obj = obj.relativized(prefix)
|
190
|
+
end
|
191
|
+
|
192
|
+
obj.key_set.each do |key|
|
193
|
+
v = obj.get(key)
|
194
|
+
existing = values[key]
|
195
|
+
if !(existing.nil?)
|
196
|
+
values[key] = v.with_fallback(existing)
|
197
|
+
else
|
198
|
+
values[key] = v
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def parse_object(n)
|
204
|
+
values = Hash.new
|
205
|
+
object_origin = line_origin
|
206
|
+
last_was_new_line = false
|
207
|
+
|
208
|
+
nodes = n.children.clone
|
209
|
+
comments = []
|
210
|
+
i = 0
|
211
|
+
while i < nodes.size
|
212
|
+
node = nodes[i]
|
213
|
+
if node.is_a?(ConfigNodeComment)
|
214
|
+
last_was_new_line = false
|
215
|
+
comments.push(node.comment_text)
|
216
|
+
elsif node.is_a?(ConfigNodeSingleToken) && Tokens.newline?(node.token)
|
217
|
+
@line_number += 1
|
218
|
+
if last_was_new_line
|
219
|
+
# Drop all comments if there was a blank line and start a new comment block
|
220
|
+
comments.clear
|
221
|
+
end
|
222
|
+
last_was_new_line = true
|
223
|
+
elsif !@flavor.equal?(ConfigSyntax::JSON) && node.is_a?(ConfigNodeInclude)
|
224
|
+
parse_include(values, node)
|
225
|
+
last_was_new_line = false
|
226
|
+
elsif node.is_a?(Hocon::Impl::ConfigNodeField)
|
227
|
+
last_was_new_line = false
|
228
|
+
path = node.path.value
|
229
|
+
comments += node.comments
|
230
|
+
|
231
|
+
# path must be on-stack while we parse the value
|
232
|
+
# Note that, in upstream, pathStack is a LinkedList, so use unshift instead of push
|
233
|
+
@path_stack.unshift(path)
|
234
|
+
if node.separator.equal?(Tokens::PLUS_EQUALS)
|
235
|
+
# we really should make this work, but for now throwing
|
236
|
+
# an exception is better than producing an incorrect
|
237
|
+
# result. See
|
238
|
+
# https://github.com/typesafehub/config/issues/160
|
239
|
+
if @array_count > 0
|
240
|
+
raise parse_error("Due to current limitations of the config parser, += does not work nested inside a list. " +
|
241
|
+
"+= expands to a ${} substitution and the path in ${} cannot currently refer to list elements. " +
|
242
|
+
"You might be able to move the += outside of the list and then refer to it from inside the list with ${}.")
|
243
|
+
end
|
244
|
+
|
245
|
+
# because we will put it in an array after the fact so
|
246
|
+
# we want this to be incremented during the parseValue
|
247
|
+
# below in order to throw the above exception.
|
248
|
+
@array_count += 1
|
249
|
+
end
|
250
|
+
|
251
|
+
value_node = node.value
|
252
|
+
|
253
|
+
# comments from the key token go to the value token
|
254
|
+
new_value = parse_value(value_node, comments)
|
255
|
+
|
256
|
+
if node.separator.equal?(Tokens::PLUS_EQUALS)
|
257
|
+
@array_count -= 1
|
258
|
+
|
259
|
+
concat = []
|
260
|
+
previous_ref = ConfigReference.new(new_value.origin,
|
261
|
+
Hocon::Impl::SubstitutionExpression.new(full_current_path, true))
|
262
|
+
list = SimpleConfigList.new(new_value.origin, [new_value])
|
263
|
+
concat << previous_ref
|
264
|
+
concat << list
|
265
|
+
new_value = ConfigConcatenation.concatenate(concat)
|
266
|
+
end
|
267
|
+
|
268
|
+
# Grab any trailing comments on the same line
|
269
|
+
if i < nodes.size - 1
|
270
|
+
i += 1
|
271
|
+
while i < nodes.size
|
272
|
+
if nodes[i].is_a?(ConfigNodeComment)
|
273
|
+
comment = nodes[i]
|
274
|
+
new_value = new_value.with_origin(new_value.origin.append_comments([comment.comment_text]))
|
275
|
+
break
|
276
|
+
elsif nodes[i].is_a?(ConfigNodeSingleToken)
|
277
|
+
curr = nodes[i]
|
278
|
+
if curr.token.equal?(Tokens::COMMA) || Tokens.ignored_whitespace?(curr.token)
|
279
|
+
# keep searching, as there could still be a comment
|
280
|
+
else
|
281
|
+
i -= 1
|
282
|
+
break
|
283
|
+
end
|
284
|
+
else
|
285
|
+
i -= 1
|
286
|
+
break
|
287
|
+
end
|
288
|
+
i += 1
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
@path_stack.shift
|
293
|
+
|
294
|
+
key = path.first
|
295
|
+
remaining = path.remainder
|
296
|
+
|
297
|
+
if remaining.nil?
|
298
|
+
existing = values[key]
|
299
|
+
unless existing.nil?
|
300
|
+
# In strict JSON, dups should be an error; while in
|
301
|
+
# our custom config language, they should be merged
|
302
|
+
# if the value is an object (or substitution that
|
303
|
+
# could become an object).
|
304
|
+
|
305
|
+
if @flavor.equal?(ConfigSyntax::JSON)
|
306
|
+
raise parse_error("JSON does not allow duplicate fields: '#{key}'" +
|
307
|
+
" was already seen at #{existing.origin().description()}")
|
308
|
+
else
|
309
|
+
new_value = new_value.with_fallback(existing)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
values[key] = new_value
|
313
|
+
else
|
314
|
+
if @flavor == ConfigSyntax::JSON
|
315
|
+
raise Hocon::ConfigError::ConfigBugOrBrokenError, "somehow got multi-element path in JSON mode"
|
316
|
+
end
|
317
|
+
|
318
|
+
obj = create_value_under_path(remaining, new_value)
|
319
|
+
existing = values[key]
|
320
|
+
if !existing.nil?
|
321
|
+
obj = obj.with_fallback(existing)
|
322
|
+
end
|
323
|
+
values[key] = obj
|
324
|
+
end
|
325
|
+
end
|
326
|
+
i += 1
|
327
|
+
end
|
328
|
+
|
329
|
+
SimpleConfigObject.new(object_origin, values)
|
330
|
+
end
|
331
|
+
|
332
|
+
def parse_array(n)
|
333
|
+
@array_count += 1
|
334
|
+
|
335
|
+
array_origin = line_origin
|
336
|
+
values = []
|
337
|
+
|
338
|
+
last_was_new_line = false
|
339
|
+
comments = []
|
340
|
+
|
341
|
+
v = nil
|
342
|
+
|
343
|
+
n.children.each do |node|
|
344
|
+
if node.is_a?(ConfigNodeComment)
|
345
|
+
comments << node.comment_text
|
346
|
+
last_was_new_line = false
|
347
|
+
elsif node.is_a?(ConfigNodeSingleToken) && Tokens.newline?(node.token)
|
348
|
+
@line_number += 1
|
349
|
+
if last_was_new_line && v.nil?
|
350
|
+
comments.clear
|
351
|
+
elsif !v.nil?
|
352
|
+
values << v.with_origin(v.origin.append_comments(comments.clone))
|
353
|
+
comments.clear
|
354
|
+
v = nil
|
355
|
+
end
|
356
|
+
last_was_new_line = true
|
357
|
+
elsif node.is_a?(Hocon::Impl::AbstractConfigNodeValue)
|
358
|
+
last_was_new_line = false
|
359
|
+
unless v.nil?
|
360
|
+
values << v.with_origin(v.origin.append_comments(comments.clone))
|
361
|
+
comments.clear
|
362
|
+
end
|
363
|
+
v = parse_value(node, comments)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
# There shouldn't be any comments at this point, but add them just in case
|
367
|
+
unless v.nil?
|
368
|
+
values << v.with_origin(v.origin.append_comments(comments.clone))
|
369
|
+
end
|
370
|
+
@array_count -= 1
|
371
|
+
SimpleConfigList.new(array_origin, values)
|
372
|
+
end
|
373
|
+
|
374
|
+
def parse
|
375
|
+
result = nil
|
376
|
+
comments = []
|
377
|
+
last_was_new_line = false
|
378
|
+
@document.children.each do |node|
|
379
|
+
if node.is_a?(ConfigNodeComment)
|
380
|
+
comments << node.comment_text
|
381
|
+
last_was_new_line = false
|
382
|
+
elsif node.is_a?(ConfigNodeSingleToken)
|
383
|
+
t = node.token
|
384
|
+
if Tokens.newline?(t)
|
385
|
+
@line_number += 1
|
386
|
+
if last_was_new_line && result.nil?
|
387
|
+
comments.clear
|
388
|
+
elsif !result.nil?
|
389
|
+
result = result.with_origin(result.origin.append_comments(comments.clone))
|
390
|
+
comments.clear
|
391
|
+
break
|
392
|
+
end
|
393
|
+
last_was_new_line = true
|
394
|
+
end
|
395
|
+
elsif node.is_a?(Hocon::Impl::ConfigNodeComplexValue)
|
396
|
+
result = parse_value(node, comments)
|
397
|
+
last_was_new_line = false
|
398
|
+
end
|
399
|
+
end
|
400
|
+
result
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|