hocon 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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