class CodeRay::Scanners::JSON

Scanner for JSON (JavaScript Object Notation).

Protected Instance Methods

scan_tokens(encoder, options) click to toggle source

See json.org/ for a definition of the JSON lexic/grammar.

# File lib/coderay/scanners/json.rb, line 21
def scan_tokens encoder, options
  
  state = :initial
  stack = []
  key_expected = false
  
  until eos?
    
    case state
    
    when :initial
      if match = scan(%r \s+ /)
        encoder.text_token match, :space
      elsif match = scan(%r"/)
        state = key_expected ? :key : :string
        encoder.begin_group state
        encoder.text_token match, :delimiter
      elsif match = scan(%r [:,\[{\]}] /)
        encoder.text_token match, :operator
        case match
        when ':' then key_expected = false
        when ',' then key_expected = true if stack.last == :object
        when '{' then stack << :object; key_expected = true
        when '[' then stack << :array
        when '}', ']' then stack.pop  # no error recovery, but works for valid JSON
        end
      elsif match = scan(%r true | false | null /)
        encoder.text_token match, :value
      elsif match = scan(%r -? (?: 0 | [1-9]\d* ) /)
        if scan(%r \.\d+ (?:[eE][-+]?\d+)? | [eE][-+]? \d+ /)
          match << matched
          encoder.text_token match, :float
        else
          encoder.text_token match, :integer
        end
      else
        encoder.text_token getch, :error
      end
      
    when :string, :key
      if match = scan(%r[^\\"]+/)
        encoder.text_token match, :content
      elsif match = scan(%r"/)
        encoder.text_token match, :delimiter
        encoder.end_group state
        state = :initial
      elsif match = scan(%r \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /ox)
        encoder.text_token match, :char
      elsif match = scan(%r\\./)
        encoder.text_token match, :content
      elsif match = scan(%r \\ | $ /)
        encoder.end_group state
        encoder.text_token match, :error
        state = :initial
      else
        raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
      end
      
    else
      raise_inspect 'Unknown state: %p' % [state], encoder
      
    end
  end
  
  if [:string, :key].include? state
    encoder.end_group state
  end
  
  encoder
end