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
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/impl/config_node_complex_value'
5
+
6
+ class Hocon::Impl::ConfigNodeConcatenation
7
+ include Hocon::Impl::ConfigNodeComplexValue
8
+ def new_node(nodes)
9
+ self.class.new(nodes)
10
+ end
11
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/config_error'
5
+ require 'hocon/impl/abstract_config_node'
6
+ require 'hocon/impl/abstract_config_node_value'
7
+ require 'hocon/impl/config_node_comment'
8
+ require 'hocon/impl/config_node_path'
9
+ require 'hocon/impl/config_node_single_token'
10
+ require 'hocon/impl/tokens'
11
+
12
+ class Hocon::Impl::ConfigNodeField
13
+ include Hocon::Impl::AbstractConfigNode
14
+
15
+ Tokens = Hocon::Impl::Tokens
16
+
17
+ def initialize(children)
18
+ @children = children
19
+ end
20
+
21
+ attr_reader :children
22
+
23
+ def tokens
24
+ tokens = []
25
+ @children.each do |child|
26
+ tokens += child.tokens
27
+ end
28
+ tokens
29
+ end
30
+
31
+ def replace_value(new_value)
32
+ children_copy = @children.clone
33
+ children_copy.each_with_index do |child, i|
34
+ if child.is_a?(Hocon::Impl::AbstractConfigNodeValue)
35
+ children_copy[i] = new_value
36
+ return self.class.new(children_copy)
37
+ end
38
+ end
39
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value"
40
+ end
41
+
42
+ def value
43
+ @children.each do |child|
44
+ if child.is_a?(Hocon::Impl::AbstractConfigNodeValue)
45
+ return child
46
+ end
47
+ end
48
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value"
49
+ end
50
+
51
+ def path
52
+ @children.each do |child|
53
+ if child.is_a?(Hocon::Impl::ConfigNodePath)
54
+ return child
55
+ end
56
+ end
57
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a path"
58
+ end
59
+
60
+ def separator
61
+ @children.each do |child|
62
+ if child.is_a?(Hocon::Impl::ConfigNodeSingleToken)
63
+ t = child.token
64
+ if t == Tokens::PLUS_EQUALS or t == Tokens::COLON or t == Tokens::EQUALS
65
+ return t
66
+ end
67
+ end
68
+ end
69
+ nil
70
+ end
71
+
72
+ def comments
73
+ comments = []
74
+ @children.each do |child|
75
+ if child.is_a?(Hocon::Impl::ConfigNodeComment)
76
+ comments << child.comment_text
77
+ end
78
+ end
79
+ comments
80
+ end
81
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/config_error'
5
+ require 'hocon/impl/abstract_config_node'
6
+ require 'hocon/impl/config_node_simple_value'
7
+
8
+ class Hocon::Impl::ConfigNodeInclude
9
+ include Hocon::Impl::AbstractConfigNode
10
+ def initialize(children, kind)
11
+ @children = children
12
+ @kind = kind
13
+ end
14
+
15
+ attr_reader :kind, :children
16
+
17
+ def tokens
18
+ tokens = []
19
+ @children.each do |child|
20
+ tokens += child.tokens
21
+ end
22
+ tokens
23
+ end
24
+
25
+ def name
26
+ @children.each do |child|
27
+ if child.is_a?(Hocon::Impl::ConfigNodeSimpleValue)
28
+ return Hocon::Impl::Tokens.value(child.token).unwrapped
29
+ end
30
+ end
31
+ nil
32
+ end
33
+ end
@@ -0,0 +1,276 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/config_syntax'
4
+ require 'hocon/impl'
5
+ require 'hocon/impl/config_node_complex_value'
6
+ require 'hocon/impl/config_node_field'
7
+ require 'hocon/impl/config_node_single_token'
8
+ require 'hocon/impl/tokens'
9
+
10
+ class Hocon::Impl::ConfigNodeObject
11
+ include Hocon::Impl::ConfigNodeComplexValue
12
+
13
+ ConfigSyntax = Hocon::ConfigSyntax
14
+ Tokens = Hocon::Impl::Tokens
15
+
16
+ def new_node(nodes)
17
+ self.class.new(nodes)
18
+ end
19
+
20
+ def has_value(desired_path)
21
+ @children.each do |node|
22
+ if node.is_a?(Hocon::Impl::ConfigNodeField)
23
+ field = node
24
+ key = field.path.value
25
+ if key == desired_path || key.starts_with(desired_path)
26
+ return true
27
+ elsif desired_path.starts_with(key)
28
+ if field.value.is_a?(self.class)
29
+ obj = field.value
30
+ remaining_path = desired_path.sub_path_to_end(key.length)
31
+ if obj.has_value(remaining_path)
32
+ return true
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ false
39
+ end
40
+
41
+ def change_value_on_path(desired_path, value, flavor)
42
+ children_copy = @children.clone
43
+ seen_non_matching = false
44
+ # Copy the value so we can change it to null but not modify the original parameter
45
+ value_copy = value
46
+ i = children_copy.size
47
+ while i >= 0 do
48
+ if children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken)
49
+ t = children_copy[i].token
50
+ # Ensure that, when we are removing settings in JSON, we don't end up with a trailing comma
51
+ if flavor.equal?(ConfigSyntax::JSON) && !seen_non_matching && t.equal?(Tokens::COMMA)
52
+ children_copy.delete_at(i)
53
+ end
54
+ i -= 1
55
+ next
56
+ elsif !children_copy[i].is_a?(Hocon::Impl::ConfigNodeField)
57
+ i -= 1
58
+ next
59
+ end
60
+ node = children_copy[i]
61
+ key = node.path.value
62
+
63
+ # Delete all multi-element paths that start with the desired path, since technically they are duplicates
64
+ if (value_copy.nil? && key == desired_path) || (key.starts_with(desired_path) && !(key == desired_path))
65
+ children_copy.delete_at(i)
66
+ # Remove any whitespace or commas after the deleted setting
67
+ j = i
68
+ while j < children_copy.size
69
+ if children_copy[j].is_a?(Hocon::Impl::ConfigNodeSingleToken)
70
+ t = children_copy[j].token
71
+ if Tokens.ignored_whitespace?(t) || t.equal?(Tokens::COMMA)
72
+ children_copy.delete_at(j)
73
+ j -= 1
74
+ else
75
+ break
76
+ end
77
+ else
78
+ break
79
+ end
80
+ j += 1
81
+ end
82
+ elsif key == desired_path
83
+ seen_non_matching = true
84
+ before = i - 1 > 0 ? children_copy[i - 1] : nil
85
+ if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) && before.is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
86
+ Tokens.ignored_whitespace?(before.token)
87
+ indented_value = value.indent_text(before)
88
+ else
89
+ indented_value = value
90
+ end
91
+ children_copy[i] = node.replace_value(indented_value)
92
+ value_copy = nil
93
+ elsif desired_path.starts_with(key)
94
+ seen_non_matching = true
95
+ if node.value.is_a?(self.class)
96
+ remaining_path = desired_path.sub_path_to_end(key.length)
97
+ children_copy[i] = node.replace_value(node.value.change_value_on_path(remaining_path, value_copy, flavor))
98
+ if !value_copy.nil? && !(node == @children[i])
99
+ value_copy = nil
100
+ end
101
+ end
102
+ else
103
+ seen_non_matching = true
104
+ end
105
+ i -= 1
106
+ end
107
+ self.class.new(children_copy)
108
+ end
109
+
110
+ def set_value_on_path(desired_path, value, flavor = ConfigSyntax::CONF)
111
+ path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor)
112
+ set_value_on_path_node(path, value, flavor)
113
+ end
114
+
115
+ def set_value_on_path_node(desired_path, value, flavor)
116
+ node = change_value_on_path(desired_path.value, value, flavor)
117
+
118
+ # If the desired Path did not exist, add it
119
+ unless node.has_value(desired_path.value)
120
+ return node.add_value_on_path(desired_path, value, flavor)
121
+ end
122
+ node
123
+ end
124
+
125
+ def indentation
126
+ seen_new_line = false
127
+ indentation = []
128
+
129
+ @children.each_index do |i|
130
+ unless seen_new_line
131
+ if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(@children[i].token)
132
+ seen_new_line = true
133
+ indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_line(nil)))
134
+ end
135
+ else
136
+ if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
137
+ Tokens.ignored_whitespace?(@children[i].token) &&
138
+ i + 1 < @children.size &&
139
+ (@children[i + 1].is_a?(Hocon::Impl::ConfigNodeField) || @children[i + 1].is_a?(Hocon::Impl::ConfigNodeInclude))
140
+ # Return the indentation of the first setting on its own line
141
+ indentation.push(@children[i])
142
+ return indentation
143
+ end
144
+ end
145
+ end
146
+ if indentation.empty?
147
+ indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, " ")))
148
+ return indentation
149
+ else
150
+ # Calculate the indentation of the ending curly-brace to get the indentation of the root object
151
+ last = @children[-1]
152
+ if last.is_a?(Hocon::Impl::ConfigNodeSingleToken) && last.token.equal?(Tokens::CLOSE_CURLY)
153
+ beforeLast = @children[-2]
154
+ if beforeLast.is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
155
+ Tokens.ignored_whitespace?(beforeLast.token)
156
+ indent = beforeLast.token.token_text
157
+ indent += " "
158
+ indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, indent)))
159
+ return indentation
160
+ end
161
+ end
162
+ end
163
+
164
+ # The object has no curly braces and is at the root level, so don't indent
165
+ indentation
166
+ end
167
+
168
+ def add_value_on_path(desired_path, value, flavor)
169
+ path = desired_path.value
170
+ children_copy = @children.clone
171
+ indentation = indentation().clone
172
+
173
+ # If the value we're inserting is a complex value, we'll need to indent it for insertion
174
+ if value.is_a?(Hocon::Impl::ConfigNodeComplexValue)
175
+ indented_value = value.indent_text(indentation[-1])
176
+ else
177
+ indented_value = value
178
+ end
179
+ same_line = !(indentation[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
180
+ Tokens.newline?(indentation[0].token))
181
+
182
+ # If the path is of length greater than one, see if the value needs to be added further down
183
+ if path.length > 1
184
+ (0..@children.size - 1).reverse_each do |i|
185
+ unless @children[i].is_a?(Hocon::Impl::ConfigNodeField)
186
+ next
187
+ end
188
+ node = @children[i]
189
+ key = node.path.value
190
+ if path.starts_with(key) && node.value.is_a?(self.class)
191
+ remaining_path = desired_path.sub_path(key.length)
192
+ new_value = node.value
193
+ children_copy[i] = node.replace_value(new_value.add_value_on_path(remaining_path, value, flavor))
194
+ return self.class.new(children_copy)
195
+ end
196
+ end
197
+ end
198
+
199
+ # Otherwise, construct the new setting
200
+ starts_with_brace = @children[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) && @children[0].token.equal?(Tokens::OPEN_CURLY)
201
+ new_nodes = []
202
+ new_nodes += indentation
203
+ new_nodes.push(desired_path.first)
204
+ new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
205
+ new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COLON))
206
+ new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
207
+
208
+ if path.length == 1
209
+ new_nodes.push(indented_value)
210
+ else
211
+ # If the path is of length greater than one add the required new objects along the path
212
+ new_object_nodes = []
213
+ new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::OPEN_CURLY))
214
+ new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
215
+ new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::CLOSE_CURLY))
216
+ new_object = self.class.new(new_object_nodes)
217
+ new_nodes.push(new_object.add_value_on_path(desired_path.sub_path(1), indented_value, flavor))
218
+ end
219
+
220
+ # Combine these two cases so that we only have to iterate once
221
+ if flavor.equal?(ConfigSyntax::JSON) || starts_with_brace || same_line
222
+ i = children_copy.size - 1
223
+ while i >= 0
224
+
225
+ # If we are in JSON or are adding a setting on the same line, we need to add a comma to the
226
+ # last setting
227
+ if (flavor.equal?(ConfigSyntax::JSON) || same_line) && children_copy[i].is_a?(Hocon::Impl::ConfigNodeField)
228
+ if i + 1 >= children_copy.size ||
229
+ !(children_copy[i + 1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && children_copy[i + 1].token.equal?(Tokens::COMMA))
230
+ children_copy.insert(i + 1, Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COMMA))
231
+ break
232
+ end
233
+ end
234
+
235
+ # Add the value into the copy of the children map, keeping any whitespace/newlines
236
+ # before the close curly brace
237
+ if starts_with_brace && children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
238
+ children_copy[i].token == Tokens::CLOSE_CURLY
239
+ previous = children_copy[i - 1]
240
+ if previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(previous.token)
241
+ children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
242
+ i -= 1
243
+ elsif previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(previous.token)
244
+ before_previous = children_copy[i - 2]
245
+ if same_line
246
+ children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
247
+ i -= 1
248
+ elsif before_previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(before_previous.token)
249
+ children_copy.insert(i - 2, Hocon::Impl::ConfigNodeField.new(new_nodes))
250
+ i -= 2
251
+ else
252
+ children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
253
+ end
254
+ else
255
+ children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
256
+ end
257
+ end
258
+
259
+ i -= 1
260
+ end
261
+ end
262
+ unless starts_with_brace
263
+ if children_copy[-1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(children_copy[-1].token)
264
+ children_copy.insert(-2, Hocon::Impl::ConfigNodeField.new(new_nodes))
265
+ else
266
+ children_copy.push(Hocon::Impl::ConfigNodeField.new(new_nodes))
267
+ end
268
+ end
269
+ self.class.new(children_copy)
270
+ end
271
+
272
+ def remove_value_on_path(desired_path, flavor)
273
+ path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor).value
274
+ change_value_on_path(path, nil, flavor)
275
+ end
276
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/impl/tokens'
5
+ require 'hocon/impl/abstract_config_node'
6
+
7
+ class Hocon::Impl::ConfigNodePath
8
+ include Hocon::Impl::AbstractConfigNode
9
+ Tokens = Hocon::Impl::Tokens
10
+
11
+ def initialize(path, tokens)
12
+ @path = path
13
+ @tokens = tokens
14
+ end
15
+
16
+ attr_reader :tokens
17
+
18
+ def value
19
+ @path
20
+ end
21
+
22
+ def sub_path(to_remove)
23
+ period_count = 0
24
+ tokens_copy = tokens.clone
25
+ (0..tokens_copy.size - 1).each do |i|
26
+ if Tokens.unquoted_text?(tokens_copy[i]) &&
27
+ tokens_copy[i].token_text == "."
28
+ period_count += 1
29
+ end
30
+
31
+ if period_count == to_remove
32
+ return self.class.new(@path.sub_path_to_end(to_remove), tokens_copy[i + 1..tokens_copy.size])
33
+ end
34
+ end
35
+ raise ConfigBugOrBrokenError, "Tried to remove too many elements from a Path node"
36
+ end
37
+
38
+ def first
39
+ tokens_copy = tokens.clone
40
+ (0..tokens_copy.size - 1).each do |i|
41
+ if Tokens.unquoted_text?(tokens_copy[i]) &&
42
+ tokens_copy[i].token_text == "."
43
+ return self.class.new(@path.sub_path(0, 1), tokens_copy[0, i])
44
+ end
45
+ end
46
+ self
47
+ end
48
+ end