Class: CodeRay::Scanners::JSON

Inherits:
Scanner
  • Object
show all
Includes:
CodeRay::Streamable
Defined in:
lib/coderay/scanners/json.rb

Constant Summary collapse

KINDS_NOT_LOC =
[
  :float, :char, :content, :delimiter,
  :error, :integer, :operator, :value,
]
CONSTANTS =
%w( true false null )
IDENT_KIND =
WordList.new(:key).add(CONSTANTS, :value)
ESCAPE =
/ [bfnrt\\"\/] /x
UNICODE_ESCAPE =
/ u[a-fA-F0-9]{4} /x

Constants inherited from Scanner

Scanner::DEFAULT_OPTIONS, Scanner::ScanError

Instance Method Summary collapse

Methods inherited from Scanner

#column, #each, file_extension, #initialize, #line, #marshal_dump, #marshal_load, normify, #reset, streamable?, #streaming?, #string=, #tokenize, #tokens

Methods included from Plugin

#helper, #included, #plugin_host, #plugin_id, #register_for, #title

Constructor Details

This class inherits a constructor from CodeRay::Scanners::Scanner

Instance Method Details

#scan_tokens(tokens, options) ⇒ Object



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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/coderay/scanners/json.rb', line 22

def scan_tokens tokens, options
  
  state = :initial
  stack = []
  string_delimiter = nil
  key_expected = false
  
  until eos?
    
    kind = nil
    match = nil
    
    case state
    
    when :initial
      if match = scan(/ \s+ | \\\n /x)
        tokens << [match, :space]
        next
      elsif match = scan(/ [:,\[{\]}] /x)
        kind = :operator
        case match
        when '{' then stack << :object; key_expected = true
        when '[' then stack << :array
        when ':' then key_expected = false
        when ',' then key_expected = true if stack.last == :object
        when '}', ']' then stack.pop  # no error recovery, but works for valid JSON
        end
      elsif match = scan(/ true | false | null /x)
        kind = IDENT_KIND[match]
      elsif match = scan(/-?(?:0|[1-9]\d*)/)
        kind = :integer
        if scan(/\.\d+(?:[eE][-+]?\d+)?|[eE][-+]?\d+/)
          match << matched
          kind = :float
        end
      elsif match = scan(/"/)
        state = key_expected ? :key : :string
        tokens << [:open, state]
        kind = :delimiter
      else
        getch
        kind = :error
      end
      
    when :string, :key
      if scan(/[^\\"]+/)
        kind = :content
      elsif scan(/"/)
        tokens << ['"', :delimiter]
        tokens << [:close, state]
        state = :initial
        next
      elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
        kind = :char
      elsif scan(/\\./m)
        kind = :content
      elsif scan(/ \\ | $ /x)
        tokens << [:close, :delimiter]
        kind = :error
        state = :initial
      else
        raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
      end
      
    else
      raise_inspect 'Unknown state', tokens
      
    end
    
    match ||= matched
    if $DEBUG and not kind
      raise_inspect 'Error token %p in line %d' %
        [[match, kind], line], tokens
    end
    raise_inspect 'Empty token', tokens unless match
    
    tokens << [match, kind]
    
  end
  
  if [:string, :key].include? state
    tokens << [:close, state]
  end
  
  tokens
end