Class | Sequel::Postgres::PGArray::Parser |
In: |
lib/sequel/extensions/pg_array.rb
|
Parent: | Object |
PostgreSQL array parser that handles all types of input.
This parser is very simple and unoptimized, but should still be O(n) where n is the length of the input string.
pos | [R] | Current position in the input string. |
Set the source for the input, and any converter callable to call with objects to be created. For nested parsers the source may contain text after the end current parse, which will be ignored.
# File lib/sequel/extensions/pg_array.rb, line 299 299: def initialize(source, converter=nil) 300: @source = source 301: @source_length = source.length 302: @converter = converter 303: @pos = -1 304: @entries = [] 305: @recorded = "" 306: @dimension = 0 307: end
Take the buffer of recorded characters and add it to the array of entries, and use a new buffer for recorded characters.
# File lib/sequel/extensions/pg_array.rb, line 328 328: def new_entry(include_empty=false) 329: if !@recorded.empty? || include_empty 330: entry = @recorded 331: if entry == NULL && !include_empty 332: entry = nil 333: elsif @converter 334: entry = @converter.call(entry) 335: end 336: @entries.push(entry) 337: @recorded = "" 338: end 339: end
Return 2 objects, whether the next character in the input was escaped with a backslash, and what the next character is.
# File lib/sequel/extensions/pg_array.rb, line 311 311: def next_char 312: @pos += 1 313: if (c = @source[@pos..@pos]) == BACKSLASH 314: @pos += 1 315: [true, @source[@pos..@pos]] 316: else 317: [false, c] 318: end 319: end
Parse the input character by character, returning an array of parsed (and potentially converted) objects.
# File lib/sequel/extensions/pg_array.rb, line 343 343: def parse(nested=false) 344: # quote sets whether we are inside of a quoted string. 345: quote = false 346: until @pos >= @source_length 347: escaped, char = next_char 348: if char == OPEN_BRACE && !quote 349: @dimension += 1 350: if (@dimension > 1) 351: # Multi-dimensional array encounter, use a subparser 352: # to parse the next level down. 353: subparser = self.class.new(@source[@pos..-1], @converter) 354: @entries.push(subparser.parse(true)) 355: @pos += subparser.pos - 1 356: end 357: elsif char == CLOSE_BRACE && !quote 358: @dimension -= 1 359: if (@dimension == 0) 360: new_entry 361: # Exit early if inside a subparser, since the 362: # text after parsing the current level should be 363: # ignored as it is handled by the parent parser. 364: return @entries if nested 365: end 366: elsif char == QUOTE && !escaped 367: # If already inside the quoted string, this is the 368: # ending quote, so add the entry. Otherwise, this 369: # is the opening quote, so set the quote flag. 370: new_entry(true) if quote 371: quote = !quote 372: elsif char == COMMA && !quote 373: # If not inside a string and a comma occurs, it indicates 374: # the end of the entry, so add the entry. 375: new_entry 376: else 377: # Add the character to the recorded character buffer. 378: record(char) 379: end 380: end 381: raise Sequel::Error, "array dimensions not balanced" unless @dimension == 0 382: @entries 383: end