hocon 0.0.7 → 0.1.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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +4 -2
  3. data/lib/hocon.rb +2 -0
  4. data/lib/hocon/config.rb +1010 -0
  5. data/lib/hocon/config_error.rb +32 -2
  6. data/lib/hocon/config_factory.rb +46 -0
  7. data/lib/hocon/config_include_context.rb +49 -0
  8. data/lib/hocon/config_includer_file.rb +27 -0
  9. data/lib/hocon/config_list.rb +49 -0
  10. data/lib/hocon/config_mergeable.rb +74 -0
  11. data/lib/hocon/config_object.rb +144 -1
  12. data/lib/hocon/config_parse_options.rb +33 -9
  13. data/lib/hocon/config_parseable.rb +51 -0
  14. data/lib/hocon/config_render_options.rb +4 -2
  15. data/lib/hocon/config_resolve_options.rb +31 -0
  16. data/lib/hocon/config_syntax.rb +5 -2
  17. data/lib/hocon/config_util.rb +73 -0
  18. data/lib/hocon/config_value.rb +122 -0
  19. data/lib/hocon/config_value_factory.rb +66 -2
  20. data/lib/hocon/config_value_type.rb +5 -2
  21. data/lib/hocon/impl.rb +2 -0
  22. data/lib/hocon/impl/abstract_config_node.rb +29 -0
  23. data/lib/hocon/impl/abstract_config_node_value.rb +11 -0
  24. data/lib/hocon/impl/abstract_config_object.rb +148 -42
  25. data/lib/hocon/impl/abstract_config_value.rb +251 -11
  26. data/lib/hocon/impl/array_iterator.rb +19 -0
  27. data/lib/hocon/impl/config_boolean.rb +7 -1
  28. data/lib/hocon/impl/config_concatenation.rb +177 -28
  29. data/lib/hocon/impl/config_delayed_merge.rb +329 -0
  30. data/lib/hocon/impl/config_delayed_merge_object.rb +274 -0
  31. data/lib/hocon/impl/config_document_parser.rb +647 -0
  32. data/lib/hocon/impl/config_double.rb +44 -0
  33. data/lib/hocon/impl/config_impl.rb +143 -19
  34. data/lib/hocon/impl/config_impl_util.rb +18 -0
  35. data/lib/hocon/impl/config_include_kind.rb +10 -0
  36. data/lib/hocon/impl/config_int.rb +13 -1
  37. data/lib/hocon/impl/config_node_array.rb +11 -0
  38. data/lib/hocon/impl/config_node_comment.rb +19 -0
  39. data/lib/hocon/impl/config_node_complex_value.rb +54 -0
  40. data/lib/hocon/impl/config_node_concatenation.rb +11 -0
  41. data/lib/hocon/impl/config_node_field.rb +81 -0
  42. data/lib/hocon/impl/config_node_include.rb +33 -0
  43. data/lib/hocon/impl/config_node_object.rb +276 -0
  44. data/lib/hocon/impl/config_node_path.rb +48 -0
  45. data/lib/hocon/impl/config_node_root.rb +60 -0
  46. data/lib/hocon/impl/config_node_simple_value.rb +42 -0
  47. data/lib/hocon/impl/config_node_single_token.rb +17 -0
  48. data/lib/hocon/impl/config_null.rb +15 -7
  49. data/lib/hocon/impl/config_number.rb +43 -4
  50. data/lib/hocon/impl/config_parser.rb +403 -0
  51. data/lib/hocon/impl/config_reference.rb +142 -0
  52. data/lib/hocon/impl/config_string.rb +55 -7
  53. data/lib/hocon/impl/container.rb +29 -0
  54. data/lib/hocon/impl/default_transformer.rb +24 -15
  55. data/lib/hocon/impl/from_map_mode.rb +3 -1
  56. data/lib/hocon/impl/full_includer.rb +2 -0
  57. data/lib/hocon/impl/memo_key.rb +42 -0
  58. data/lib/hocon/impl/mergeable_value.rb +8 -0
  59. data/lib/hocon/impl/origin_type.rb +8 -2
  60. data/lib/hocon/impl/parseable.rb +455 -91
  61. data/lib/hocon/impl/path.rb +181 -59
  62. data/lib/hocon/impl/path_builder.rb +24 -3
  63. data/lib/hocon/impl/path_parser.rb +280 -0
  64. data/lib/hocon/impl/replaceable_merge_stack.rb +22 -0
  65. data/lib/hocon/impl/resolve_context.rb +254 -0
  66. data/lib/hocon/impl/resolve_memos.rb +21 -0
  67. data/lib/hocon/impl/resolve_result.rb +39 -0
  68. data/lib/hocon/impl/resolve_source.rb +354 -0
  69. data/lib/hocon/impl/resolve_status.rb +3 -1
  70. data/lib/hocon/impl/simple_config.rb +264 -10
  71. data/lib/hocon/impl/simple_config_document.rb +48 -0
  72. data/lib/hocon/impl/simple_config_list.rb +282 -8
  73. data/lib/hocon/impl/simple_config_object.rb +424 -88
  74. data/lib/hocon/impl/simple_config_origin.rb +263 -71
  75. data/lib/hocon/impl/simple_include_context.rb +31 -1
  76. data/lib/hocon/impl/simple_includer.rb +196 -1
  77. data/lib/hocon/impl/substitution_expression.rb +38 -0
  78. data/lib/hocon/impl/token.rb +17 -4
  79. data/lib/hocon/impl/token_type.rb +6 -2
  80. data/lib/hocon/impl/tokenizer.rb +339 -109
  81. data/lib/hocon/impl/tokens.rb +330 -79
  82. data/lib/hocon/impl/unmergeable.rb +14 -1
  83. data/lib/hocon/impl/unsupported_operation_error.rb +6 -0
  84. data/lib/hocon/impl/url.rb +37 -0
  85. data/lib/hocon/parser.rb +7 -0
  86. data/lib/hocon/parser/config_document.rb +92 -0
  87. data/lib/hocon/parser/config_document_factory.rb +36 -0
  88. data/lib/hocon/parser/config_node.rb +30 -0
  89. metadata +67 -43
  90. data/lib/hocon/impl/config_float.rb +0 -13
  91. data/lib/hocon/impl/parser.rb +0 -977
  92. data/lib/hocon/impl/properties_parser.rb +0 -83
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon/impl'
2
4
  require 'hocon/impl/path_builder'
3
5
  require 'hocon/config_error'
@@ -8,37 +10,85 @@ class Hocon::Impl::Path
8
10
  ConfigBugOrBrokenError = Hocon::ConfigError::ConfigBugOrBrokenError
9
11
  ConfigImplUtil = Hocon::Impl::ConfigImplUtil
10
12
 
11
- # this doesn't have a very precise meaning, just to reduce
12
- # noise from quotes in the rendered path for average cases
13
- def self.has_funky_chars?(s)
14
- length = s.length
15
- if length == 0
16
- return false
17
- end
13
+ def initialize(first, remainder)
14
+ # first: String, remainder: Path
18
15
 
19
- # if the path starts with something that could be a number,
20
- # we need to quote it because the number could be invalid,
21
- # for example it could be a hyphen with no digit afterward
22
- # or the exponent "e" notation could be mangled.
23
- first = s[0]
24
- unless first =~ /[[:alpha:]]/
25
- return true
16
+ @first = first
17
+ @remainder = remainder
18
+ end
19
+ attr_reader :first, :remainder
20
+
21
+ def self.from_string_list(elements)
22
+ # This method was translated from the Path constructor in the
23
+ # Java hocon library that has this signature:
24
+ # Path(String... elements)
25
+ #
26
+ # It figures out what @first and @remainder should be, then
27
+ # pass those to the ruby constructor
28
+ if elements.length == 0
29
+ raise Hocon::ConfigError::ConfigBugOrBrokenError.new("empty path")
26
30
  end
27
31
 
28
- s.chars.each do |c|
29
- unless (c =~ /[[:alnum:]]/) || (c == '-') || (c == '_')
30
- return true
32
+ new_first = elements.first
33
+
34
+ if elements.length > 1
35
+ pb = Hocon::Impl::PathBuilder.new
36
+
37
+ # Skip first element
38
+ elements.drop(1).each do |element|
39
+ pb.append_key(element)
31
40
  end
41
+
42
+ new_remainder = pb.result
43
+ else
44
+ new_remainder = nil
32
45
  end
33
46
 
34
- false
47
+ self.new(new_first, new_remainder)
35
48
  end
36
49
 
37
- def initialize(first, remainder)
38
- @first = first
39
- @remainder = remainder
50
+ def self.from_path_list(path_list)
51
+ # This method was translated from the Path constructors in the
52
+ # Java hocon library that take in a list of Paths
53
+ #
54
+ # It just passes an iterator to self.from_path_iterator, which
55
+ # will return a new Path object
56
+ from_path_iterator(path_list.each)
57
+ end
58
+
59
+ def self.from_path_iterator(path_iterator)
60
+ # This method was translated from the Path constructors in the
61
+ # Java hocon library that takes in an iterator of Paths
62
+ #
63
+ # It figures out what @first and @remainder should be, then
64
+ # pass those to the ruby constructor
65
+
66
+ # Try to get first path from iterator
67
+ # Ruby iterators have no .hasNext() method like java
68
+ # So we try to catch the StopIteration exception
69
+ begin
70
+ first_path = path_iterator.next
71
+ rescue StopIteration
72
+ raise Hocon::ConfigError::ConfigBugOrBrokenError("empty path")
73
+ end
74
+
75
+ new_first = first_path.first
76
+
77
+ pb = Hocon::Impl::PathBuilder.new
78
+
79
+ unless first_path.remainder.nil?
80
+ pb.append_path(first_path.remainder)
81
+ end
82
+
83
+ # Skip first path
84
+ path_iterator.drop(1).each do |path|
85
+ pb.append_path(path)
86
+ end
87
+
88
+ new_remainder = pb.result
89
+
90
+ self.new(new_first, new_remainder)
40
91
  end
41
- attr_reader :first, :remainder
42
92
 
43
93
  def first
44
94
  @first
@@ -64,45 +114,50 @@ class Hocon::Impl::Path
64
114
 
65
115
  def last
66
116
  p = self
67
- while not p.remainder.nil?
117
+ while p.remainder != nil
68
118
  p = p.remainder
69
119
  end
70
120
  p.first
71
121
  end
72
122
 
123
+ def prepend(to_prepend)
124
+ pb = Hocon::Impl::PathBuilder.new
125
+
126
+ pb.append_path(to_prepend)
127
+ pb.append_path(self)
128
+
129
+ pb.result
130
+ end
131
+
73
132
  def length
74
133
  count = 1
75
134
  p = remainder
76
- while not p.nil? do
135
+ while p != nil do
77
136
  count += 1
78
137
  p = p.remainder
79
138
  end
80
- return count
81
- end
82
-
83
- #
84
- # toString() is a debugging-oriented version while this is an
85
- # error-message-oriented human-readable one.
86
- #
87
- def render
88
- sb = StringIO.new
89
- append_to_string_builder(sb)
90
- sb.string
139
+ count
91
140
  end
92
141
 
93
- def append_to_string_builder(sb)
94
- if self.class.has_funky_chars?(@first) || @first.empty?
95
- sb << ConfigImplUtil.render_json_string(@first)
96
- else
97
- sb << @first
142
+ def sub_path(first_index, last_index)
143
+ if last_index < first_index
144
+ raise ConfigBugOrBrokenError.new("bad call to sub_path")
98
145
  end
99
-
100
- unless @remainder.nil?
101
- sb << "."
102
- @remainder.append_to_string_builder(sb)
146
+ from = sub_path_to_end(first_index)
147
+ pb = Hocon::Impl::PathBuilder.new
148
+ count = last_index - first_index
149
+ while count > 0 do
150
+ count -= 1
151
+ pb.append_key(from.first)
152
+ from = from.remainder
153
+ if from.nil?
154
+ raise ConfigBugOrBrokenError.new("sub_path last_index out of range #{last_index}")
155
+ end
103
156
  end
157
+ pb.result
104
158
  end
105
159
 
160
+ # translated from `subPath(int removeFromFront)` upstream
106
161
  def sub_path_to_end(remove_from_front)
107
162
  count = remove_from_front
108
163
  p = self
@@ -113,22 +168,88 @@ class Hocon::Impl::Path
113
168
  p
114
169
  end
115
170
 
116
- def sub_path(first_index, last_index)
117
- if last_index < first_index
118
- raise ConfigBugOrBrokenError.new("bad call to sub_path", nil)
171
+ def starts_with(other)
172
+ my_remainder = self
173
+ other_remainder = other
174
+ if other_remainder.length <= my_remainder.length
175
+ while ! other_remainder.nil?
176
+ if ! (other_remainder.first == my_remainder.first)
177
+ return false
178
+ end
179
+ my_remainder = my_remainder.remainder
180
+ other_remainder = other_remainder.remainder
181
+ end
182
+ return true
119
183
  end
120
- from = sub_path_to_end(first_index)
121
- pb = Hocon::Impl::PathBuilder.new
122
- count = last_index - first_index
123
- while count > 0 do
124
- count -= 1
125
- pb.append_key(from.first)
126
- from = from.remainder
127
- if from.nil?
128
- raise ConfigBugOrBrokenError.new("sub_path last_index out of range #{last_index}", nil)
184
+ false
185
+ end
186
+
187
+ def ==(other)
188
+ if other.is_a? Hocon::Impl::Path
189
+ that = other
190
+ first == that.first && ConfigImplUtil.equals_handling_nil?(remainder, that.remainder)
191
+ else
192
+ false
193
+ end
194
+ end
195
+
196
+ def hash
197
+ remainder_hash = remainder.nil? ? 0 : remainder.hash
198
+
199
+ 41 * (41 + first.hash) + remainder_hash
200
+ end
201
+
202
+ # this doesn't have a very precise meaning, just to reduce
203
+ # noise from quotes in the rendered path for average cases
204
+ def self.has_funky_chars?(s)
205
+ length = s.length
206
+ if length == 0
207
+ return false
208
+ end
209
+
210
+ s.chars.each do |c|
211
+ unless (c =~ /[[:alnum:]]/) || (c == '-') || (c == '_')
212
+ return true
129
213
  end
130
214
  end
131
- pb.result
215
+
216
+ false
217
+ end
218
+
219
+ def append_to_string_builder(sb)
220
+ if self.class.has_funky_chars?(@first) || @first.empty?
221
+ sb << ConfigImplUtil.render_json_string(@first)
222
+ else
223
+ sb << @first
224
+ end
225
+
226
+ unless @remainder.nil?
227
+ sb << "."
228
+ @remainder.append_to_string_builder(sb)
229
+ end
230
+ end
231
+
232
+ def to_s
233
+ sb = StringIO.new
234
+ sb << "Path("
235
+ append_to_string_builder(sb)
236
+ sb << ")"
237
+
238
+ sb.string
239
+ end
240
+
241
+ def inspect
242
+ to_s
243
+ end
244
+
245
+ #
246
+ # toString() is a debugging-oriented version while this is an
247
+ # error-message-oriented human-readable one.
248
+ #
249
+ def render
250
+ sb = StringIO.new
251
+ append_to_string_builder(sb)
252
+ sb.string
132
253
  end
133
254
 
134
255
  def self.new_key(key)
@@ -136,6 +257,7 @@ class Hocon::Impl::Path
136
257
  end
137
258
 
138
259
  def self.new_path(path)
139
- Hocon::Impl::Parser.parse_path(path)
260
+ Hocon::Impl::PathParser.parse_path(path)
140
261
  end
141
- end
262
+
263
+ end
@@ -1,5 +1,8 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'hocon/impl'
2
4
  require 'hocon/impl/path'
5
+ require 'hocon/config_error'
3
6
 
4
7
  class Hocon::Impl::PathBuilder
5
8
 
@@ -10,7 +13,7 @@ class Hocon::Impl::PathBuilder
10
13
 
11
14
  def check_can_append
12
15
  if @result
13
- raise ConfigBugError, "Adding to PathBuilder after getting result"
16
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "Adding to PathBuilder after getting result"
14
17
  end
15
18
  end
16
19
 
@@ -19,8 +22,26 @@ class Hocon::Impl::PathBuilder
19
22
  @keys.push(key)
20
23
  end
21
24
 
25
+ def append_path(path)
26
+ check_can_append
27
+
28
+ first = path.first
29
+ remainder = path.remainder
30
+
31
+ loop do
32
+ @keys.push(first)
33
+
34
+ if !remainder.nil?
35
+ first = remainder.first
36
+ remainder = remainder.remainder
37
+ else
38
+ break
39
+ end
40
+ end
41
+ end
42
+
22
43
  def result
23
- # note: if keys is empty, we want to return null, which is a valid
44
+ # note: if keys is empty, we want to return nil, which is a valid
24
45
  # empty path
25
46
  if @result.nil?
26
47
  remainder = nil
@@ -32,4 +53,4 @@ class Hocon::Impl::PathBuilder
32
53
  end
33
54
  @result
34
55
  end
35
- end
56
+ end
@@ -0,0 +1,280 @@
1
+ # encoding: utf-8
2
+
3
+ require 'stringio'
4
+ require 'hocon/impl'
5
+ require 'hocon/config_syntax'
6
+ require 'hocon/impl/tokenizer'
7
+ require 'hocon/impl/config_node_path'
8
+ require 'hocon/impl/tokens'
9
+ require 'hocon/config_value_type'
10
+ require 'hocon/config_error'
11
+
12
+ class Hocon::Impl::PathParser
13
+ ConfigSyntax = Hocon::ConfigSyntax
14
+ SimpleConfigOrigin = Hocon::Impl::SimpleConfigOrigin
15
+ Tokenizer = Hocon::Impl::Tokenizer
16
+ Tokens = Hocon::Impl::Tokens
17
+ ConfigNodePath = Hocon::Impl::ConfigNodePath
18
+ ConfigValueType = Hocon::ConfigValueType
19
+ ConfigBadPathError = Hocon::ConfigError::ConfigBadPathError
20
+
21
+
22
+
23
+ class Element
24
+ def initialize(initial, can_be_empty)
25
+ @can_be_empty = can_be_empty
26
+ @sb = StringIO.new(initial)
27
+ end
28
+
29
+ attr_accessor :can_be_empty, :sb
30
+
31
+ def to_string
32
+ "Element(#{@sb.string},#{@can_be_empty})"
33
+ end
34
+ end
35
+
36
+ def self.api_origin
37
+ SimpleConfigOrigin.new_simple("path parameter")
38
+ end
39
+
40
+ def self.parse_path_node(path, flavor = ConfigSyntax::CONF)
41
+ reader = StringIO.new(path)
42
+
43
+ begin
44
+ tokens = Tokenizer.tokenize(api_origin, reader,
45
+ flavor)
46
+ tokens.next # drop START
47
+ parse_path_node_expression(tokens, api_origin, path, flavor)
48
+ ensure
49
+ reader.close
50
+ end
51
+ end
52
+
53
+ def self.parse_path(path)
54
+ speculated = speculative_fast_parse_path(path)
55
+ if not speculated.nil?
56
+ return speculated
57
+ end
58
+
59
+ reader = StringIO.new(path)
60
+
61
+ begin
62
+ tokens = Tokenizer.tokenize(api_origin, reader, ConfigSyntax::CONF)
63
+ tokens.next # drop START
64
+ return parse_path_expression(tokens, api_origin, path)
65
+ ensure
66
+ reader.close
67
+ end
68
+ end
69
+
70
+ def self.parse_path_node_expression(expression, origin, original_text = nil,
71
+ flavor = ConfigSyntax::CONF)
72
+ path_tokens = []
73
+ path = parse_path_expression(expression, origin, original_text, path_tokens, flavor)
74
+ ConfigNodePath.new(path, path_tokens);
75
+ end
76
+
77
+ def self.parse_path_expression(expression, origin, original_text = nil,
78
+ path_tokens = nil, flavor = ConfigSyntax::CONF)
79
+ # each builder in "buf" is an element in the path
80
+ buf = []
81
+ buf.push(Element.new("", false))
82
+
83
+ if !expression.has_next?
84
+ raise ConfigBadPathError.new(
85
+ origin,
86
+ original_text,
87
+ "Expecting a field name or path here, but got nothing")
88
+ end
89
+
90
+ while expression.has_next?
91
+ t = expression.next
92
+
93
+ if ! path_tokens.nil?
94
+ path_tokens << t
95
+ end
96
+
97
+ # Ignore all IgnoredWhitespace tokens
98
+ next if Tokens.ignored_whitespace?(t)
99
+
100
+ if Tokens.value_with_type?(t, ConfigValueType::STRING)
101
+ v = Tokens.value(t)
102
+ # this is a quoted string; so any periods
103
+ # in here don't count as path separators
104
+ s = v.transform_to_string
105
+ add_path_text(buf, true, s)
106
+ elsif t == Tokens::EOF
107
+ # ignore this; when parsing a file, it should not happen
108
+ # since we're parsing a token list rather than the main
109
+ # token iterator, and when parsing a path expression from the
110
+ # API, it's expected to have an EOF.
111
+ else
112
+ # any periods outside of a quoted string count as
113
+ # separators
114
+ text = nil
115
+ if Tokens.value?(t)
116
+ # appending a number here may add
117
+ # a period, but we _do_ count those as path
118
+ # separators, because we basically want
119
+ # "foo 3.0bar" to parse as a string even
120
+ # though there's a number in it. The fact that
121
+ # we tokenize non-string values is largely an
122
+ # implementation detail.
123
+ v = Tokens.value(t)
124
+
125
+ # We need to split the tokens on a . so that we can get sub-paths but still preserve
126
+ # the original path text when doing an insertion
127
+ if ! path_tokens.nil?
128
+ path_tokens.delete_at(path_tokens.size - 1)
129
+ path_tokens.concat(split_token_on_period(t, flavor))
130
+ end
131
+ text = v.transform_to_string
132
+ elsif Tokens.unquoted_text?(t)
133
+ # We need to split the tokens on a . so that we can get sub-paths but still preserve
134
+ # the original path text when doing an insertion on ConfigNodeObjects
135
+ if ! path_tokens.nil?
136
+ path_tokens.delete_at(path_tokens.size - 1)
137
+ path_tokens.concat(split_token_on_period(t, flavor))
138
+ end
139
+ text = Tokens.unquoted_text(t)
140
+ else
141
+ raise ConfigBadPathError.new(
142
+ origin,
143
+ original_text,
144
+ "Token not allowed in path expression: #{t}" +
145
+ " (you can double-quote this token if you really want it here)")
146
+ end
147
+
148
+ add_path_text(buf, false, text)
149
+ end
150
+ end
151
+
152
+ pb = Hocon::Impl::PathBuilder.new
153
+ buf.each do |e|
154
+ if (e.sb.length == 0) && !e.can_be_empty
155
+ raise Hocon::ConfigError::ConfigBadPathError.new(
156
+ origin,
157
+ original_text,
158
+ "path has a leading, trailing, or two adjacent period '.' (use quoted \"\" empty string if you want an empty element)")
159
+ else
160
+ pb.append_key(e.sb.string)
161
+ end
162
+ end
163
+
164
+ pb.result
165
+ end
166
+
167
+ def self.split_token_on_period(t, flavor)
168
+ token_text = t.token_text
169
+ if token_text == "."
170
+ return [t]
171
+ end
172
+ split_token = token_text.split('.')
173
+ split_tokens = []
174
+ split_token.each do |s|
175
+ if flavor == ConfigSyntax::CONF
176
+ split_tokens << Tokens.new_unquoted_text(t.origin, s)
177
+ else
178
+ split_tokens << Tokens.new_string(t.origin, s, "\"#{s}\"")
179
+ end
180
+ split_tokens << Tokens.new_unquoted_text(t.origin, ".")
181
+ end
182
+ if token_text[-1] != "."
183
+ split_tokens.delete_at(split_tokens.size - 1)
184
+ end
185
+ split_tokens
186
+ end
187
+
188
+ def self.add_path_text(buf, was_quoted, new_text)
189
+ i = if was_quoted
190
+ -1
191
+ else
192
+ new_text.index('.') || -1
193
+ end
194
+ current = buf.last
195
+ if i < 0
196
+ # add to current path element
197
+ current.sb << new_text
198
+ # any empty quoted string means this element can
199
+ # now be empty.
200
+ if was_quoted && (current.sb.length == 0)
201
+ current.can_be_empty = true
202
+ end
203
+ else
204
+ # "buf" plus up to the period is an element
205
+ current.sb << new_text[0, i]
206
+ # then start a new element
207
+ buf.push(Element.new("", false))
208
+ # recurse to consume remainder of new_text
209
+ add_path_text(buf, false, new_text[i + 1, new_text.length - 1])
210
+ end
211
+ end
212
+
213
+ # the idea is to see if the string has any chars or features
214
+ # that might require the full parser to deal with.
215
+ def self.looks_unsafe_for_fast_parser(s)
216
+ last_was_dot = true # // start of path is also a "dot"
217
+ len = s.length
218
+ if s.empty?
219
+ return true
220
+ end
221
+ if s[0] == "."
222
+ return true
223
+ end
224
+ if s[len - 1] == "."
225
+ return true
226
+ end
227
+
228
+ (0..len).each do |i|
229
+ c = s[i]
230
+ if c =~ /^\w$/
231
+ last_was_dot = false
232
+ next
233
+ elsif c == '.'
234
+ if last_was_dot
235
+ return true # ".." means we need to throw an error
236
+ end
237
+ last_was_dot = true
238
+ elsif c == '-'
239
+ if last_was_dot
240
+ return true
241
+ end
242
+ next
243
+ else
244
+ return true
245
+ end
246
+ end
247
+
248
+ if last_was_dot
249
+ return true
250
+ end
251
+
252
+ false
253
+ end
254
+
255
+ def self.fast_path_build(tail, s, path_end)
256
+ # rindex takes last index it should look at, end - 1 not end
257
+ split_at = s.rindex(".", path_end - 1)
258
+ tokens = []
259
+ tokens << Tokens.new_unquoted_text(nil, s)
260
+ # this works even if split_at is -1; then we start the substring at 0
261
+ with_one_more_element = Path.new(s[split_at + 1..path_end], tail)
262
+ if split_at < 0
263
+ with_one_more_element
264
+ else
265
+ fast_path_build(with_one_more_element, s, split_at)
266
+ end
267
+ end
268
+
269
+ # do something much faster than the full parser if
270
+ # we just have something like "foo" or "foo.bar"
271
+ def self.speculative_fast_parse_path(path)
272
+ s = Hocon::Impl::ConfigImplUtil.unicode_trim(path)
273
+ if looks_unsafe_for_fast_parser(s)
274
+ return nil
275
+ end
276
+
277
+ fast_path_build(nil, s, s.length)
278
+ end
279
+
280
+ end