Class: Hocon::Impl::PathParser
- Inherits:
-
Object
- Object
- Hocon::Impl::PathParser
- Defined in:
- lib/hocon/impl/path_parser.rb
Defined Under Namespace
Classes: Element
Constant Summary collapse
- ConfigSyntax =
Hocon::ConfigSyntax
- SimpleConfigOrigin =
Hocon::Impl::SimpleConfigOrigin
- Tokenizer =
Hocon::Impl::Tokenizer
- Tokens =
Hocon::Impl::Tokens
- ConfigNodePath =
Hocon::Impl::ConfigNodePath
- ConfigValueType =
Hocon::ConfigValueType
- ConfigBadPathError =
Hocon::ConfigError::ConfigBadPathError
Class Method Summary collapse
- .add_path_text(buf, was_quoted, new_text) ⇒ Object
- .api_origin ⇒ Object
- .fast_path_build(tail, s, path_end) ⇒ Object
-
.looks_unsafe_for_fast_parser(s) ⇒ Object
the idea is to see if the string has any chars or features that might require the full parser to deal with.
- .parse_path(path) ⇒ Object
- .parse_path_expression(expression, origin, original_text = nil, path_tokens = nil, flavor = ConfigSyntax::CONF) ⇒ Object
- .parse_path_node(path, flavor = ConfigSyntax::CONF) ⇒ Object
- .parse_path_node_expression(expression, origin, original_text = nil, flavor = ConfigSyntax::CONF) ⇒ Object
-
.speculative_fast_parse_path(path) ⇒ Object
do something much faster than the full parser if we just have something like “foo” or “foo.bar”.
- .split_token_on_period(t, flavor) ⇒ Object
Class Method Details
.add_path_text(buf, was_quoted, new_text) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/hocon/impl/path_parser.rb', line 188 def self.add_path_text(buf, was_quoted, new_text) i = if was_quoted -1 else new_text.index('.') || -1 end current = buf.last if i < 0 # add to current path element current.sb << new_text # any empty quoted string means this element can # now be empty. if was_quoted && (current.sb.length == 0) current.can_be_empty = true end else # "buf" plus up to the period is an element current.sb << new_text[0, i] # then start a new element buf.push(Element.new("", false)) # recurse to consume remainder of new_text add_path_text(buf, false, new_text[i + 1, new_text.length - 1]) end end |
.api_origin ⇒ Object
36 37 38 |
# File 'lib/hocon/impl/path_parser.rb', line 36 def self.api_origin SimpleConfigOrigin.new_simple("path parameter") end |
.fast_path_build(tail, s, path_end) ⇒ Object
255 256 257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/hocon/impl/path_parser.rb', line 255 def self.fast_path_build(tail, s, path_end) # rindex takes last index it should look at, end - 1 not end split_at = s.rindex(".", path_end - 1) tokens = [] tokens << Tokens.new_unquoted_text(nil, s) # this works even if split_at is -1; then we start the substring at 0 with_one_more_element = Path.new(s[split_at + 1..path_end], tail) if split_at < 0 with_one_more_element else fast_path_build(with_one_more_element, s, split_at) end end |
.looks_unsafe_for_fast_parser(s) ⇒ Object
the idea is to see if the string has any chars or features that might require the full parser to deal with.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/hocon/impl/path_parser.rb', line 215 def self.looks_unsafe_for_fast_parser(s) last_was_dot = true # // start of path is also a "dot" len = s.length if s.empty? return true end if s[0] == "." return true end if s[len - 1] == "." return true end (0..len).each do |i| c = s[i] if c =~ /^\w$/ last_was_dot = false next elsif c == '.' if last_was_dot return true # ".." means we need to throw an error end last_was_dot = true elsif c == '-' if last_was_dot return true end next else return true end end if last_was_dot return true end false end |
.parse_path(path) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/hocon/impl/path_parser.rb', line 53 def self.parse_path(path) speculated = speculative_fast_parse_path(path) if not speculated.nil? return speculated end reader = StringIO.new(path) begin tokens = Tokenizer.tokenize(api_origin, reader, ConfigSyntax::CONF) tokens.next # drop START return parse_path_expression(tokens, api_origin, path) ensure reader.close end end |
.parse_path_expression(expression, origin, original_text = nil, path_tokens = nil, flavor = ConfigSyntax::CONF) ⇒ Object
77 78 79 80 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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/hocon/impl/path_parser.rb', line 77 def self.parse_path_expression(expression, origin, original_text = nil, path_tokens = nil, flavor = ConfigSyntax::CONF) # each builder in "buf" is an element in the path buf = [] buf.push(Element.new("", false)) if !expression.has_next? raise ConfigBadPathError.new( origin, original_text, "Expecting a field name or path here, but got nothing") end while expression.has_next? t = expression.next if ! path_tokens.nil? path_tokens << t end # Ignore all IgnoredWhitespace tokens next if Tokens.ignored_whitespace?(t) if Tokens.value_with_type?(t, ConfigValueType::STRING) v = Tokens.value(t) # this is a quoted string; so any periods # in here don't count as path separators s = v.transform_to_string add_path_text(buf, true, s) elsif t == Tokens::EOF # ignore this; when parsing a file, it should not happen # since we're parsing a token list rather than the main # token iterator, and when parsing a path expression from the # API, it's expected to have an EOF. else # any periods outside of a quoted string count as # separators text = nil if Tokens.value?(t) # appending a number here may add # a period, but we _do_ count those as path # separators, because we basically want # "foo 3.0bar" to parse as a string even # though there's a number in it. The fact that # we tokenize non-string values is largely an # implementation detail. v = Tokens.value(t) # We need to split the tokens on a . so that we can get sub-paths but still preserve # the original path text when doing an insertion if ! path_tokens.nil? path_tokens.delete_at(path_tokens.size - 1) path_tokens.concat(split_token_on_period(t, flavor)) end text = v.transform_to_string elsif Tokens.unquoted_text?(t) # We need to split the tokens on a . so that we can get sub-paths but still preserve # the original path text when doing an insertion on ConfigNodeObjects if ! path_tokens.nil? path_tokens.delete_at(path_tokens.size - 1) path_tokens.concat(split_token_on_period(t, flavor)) end text = Tokens.unquoted_text(t) else raise ConfigBadPathError.new( origin, original_text, "Token not allowed in path expression: #{t}" + " (you can double-quote this token if you really want it here)") end add_path_text(buf, false, text) end end pb = Hocon::Impl::PathBuilder.new buf.each do |e| if (e.sb.length == 0) && !e.can_be_empty raise Hocon::ConfigError::ConfigBadPathError.new( origin, original_text, "path has a leading, trailing, or two adjacent period '.' (use quoted \"\" empty string if you want an empty element)") else pb.append_key(e.sb.string) end end pb.result end |
.parse_path_node(path, flavor = ConfigSyntax::CONF) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/hocon/impl/path_parser.rb', line 40 def self.parse_path_node(path, flavor = ConfigSyntax::CONF) reader = StringIO.new(path) begin tokens = Tokenizer.tokenize(api_origin, reader, flavor) tokens.next # drop START parse_path_node_expression(tokens, api_origin, path, flavor) ensure reader.close end end |
.parse_path_node_expression(expression, origin, original_text = nil, flavor = ConfigSyntax::CONF) ⇒ Object
70 71 72 73 74 75 |
# File 'lib/hocon/impl/path_parser.rb', line 70 def self.parse_path_node_expression(expression, origin, original_text = nil, flavor = ConfigSyntax::CONF) path_tokens = [] path = parse_path_expression(expression, origin, original_text, path_tokens, flavor) ConfigNodePath.new(path, path_tokens); end |
.speculative_fast_parse_path(path) ⇒ Object
do something much faster than the full parser if we just have something like “foo” or “foo.bar”
271 272 273 274 275 276 277 278 |
# File 'lib/hocon/impl/path_parser.rb', line 271 def self.speculative_fast_parse_path(path) s = Hocon::Impl::ConfigImplUtil.unicode_trim(path) if looks_unsafe_for_fast_parser(s) return nil end fast_path_build(nil, s, s.length) end |
.split_token_on_period(t, flavor) ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/hocon/impl/path_parser.rb', line 167 def self.split_token_on_period(t, flavor) token_text = t.token_text if token_text == "." return [t] end split_token = token_text.split('.') split_tokens = [] split_token.each do |s| if flavor == ConfigSyntax::CONF split_tokens << Tokens.new_unquoted_text(t.origin, s) else split_tokens << Tokens.new_string(t.origin, s, "\"#{s}\"") end split_tokens << Tokens.new_unquoted_text(t.origin, ".") end if token_text[-1] != "." split_tokens.delete_at(split_tokens.size - 1) end split_tokens end |