Module: JsonCompleter::ParserEngine

Included in:
JsonCompleter
Defined in:
lib/json_completer/parser_engine.rb

Instance Method Summary collapse

Instance Method Details

#parse(partial_json) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/json_completer/parser_engine.rb', line 5

def parse(partial_json)
  input = partial_json
  # The hot path works on raw bytes, not 1-character Ruby strings. JSON punctuation is ASCII,
  # so getbyte/bytesize let us compare cheap integers while multibyte UTF-8 payload stays intact.
  input_length = input.bytesize

  if @parse_state.nil? ||
     @parse_state.input_length > input_length ||
     (@parse_state.input_length < input_length && reset_parse_state_for_input_growth?(input))
    @parse_state = self.class.new_parse_state
  elsif @parse_state.input_length == input_length
    if @parse_state.input_snapshot == input
      finalize_parse_result
      return @parse_state.root
    end

    @parse_state = self.class.new_parse_state
  end

  return nil if input.empty?

  begin
    prepare_parse_state_for_incremental_input

    index = @parse_state.last_index
    while index < input_length
      if @parse_state.token_state
        index = continue_parse_token(input, index)
        next
      end

      byte = input.getbyte(index)
      if top_level_value_complete? && !whitespace_byte?(byte)
        raise ParseError, 'unexpected token after top-level value'
      end

      # ASCII byte values: 9/10/13/32 = whitespace, 34 = ", 44 = ,, 45 = -, 58 = :,
      # 91/93 = [] , 102/110/116 = f/n/t, 123/125 = {}.
      case byte
      when 9, 10, 13, 32
        index += 1
      when 34
        start_parse_string_token
        index += 1
      when 44
        parse_comma!
        index += 1
      when 45, 48..57
        start_parse_number_token(byte)
        index += 1
      when 58
        parse_colon!
        index += 1
      when 91
        start_parse_container([])
        index += 1
      when 93
        close_parse_array!
        index += 1
      when 102, 110, 116
        start_parse_keyword_token(byte)
        index += 1
      when 123
        start_parse_container({})
        index += 1
      when 125
        close_parse_object!
        index += 1
      else
        raise ParseError, "unexpected token #{input.byteslice(index, 1).inspect}"
      end
    end

    @parse_state.last_index = index
    @parse_state.input_length = input_length
    @parse_state.input_snapshot = input
    finalize_parse_result
    @parse_state.root
  rescue ParseError
    @parse_state = self.class.new_parse_state
    raise
  end
end