comparison vendor/plugins/coderay-0.9.2/lib/coderay/scanners/json.rb @ 0:513646585e45

* Import Redmine trunk SVN rev 3859
author Chris Cannam
date Fri, 23 Jul 2010 15:52:44 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:513646585e45
1 module CodeRay
2 module Scanners
3
4 class JSON < Scanner
5
6 include Streamable
7
8 register_for :json
9 file_extension 'json'
10
11 KINDS_NOT_LOC = [
12 :float, :char, :content, :delimiter,
13 :error, :integer, :operator, :value,
14 ]
15
16 CONSTANTS = %w( true false null )
17 IDENT_KIND = WordList.new(:key).add(CONSTANTS, :value)
18
19 ESCAPE = / [bfnrt\\"\/] /x
20 UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x
21
22 def scan_tokens tokens, options
23
24 state = :initial
25 stack = []
26 string_delimiter = nil
27 key_expected = false
28
29 until eos?
30
31 kind = nil
32 match = nil
33
34 case state
35
36 when :initial
37 if match = scan(/ \s+ | \\\n /x)
38 tokens << [match, :space]
39 next
40 elsif match = scan(/ [:,\[{\]}] /x)
41 kind = :operator
42 case match
43 when '{' then stack << :object; key_expected = true
44 when '[' then stack << :array
45 when ':' then key_expected = false
46 when ',' then key_expected = true if stack.last == :object
47 when '}', ']' then stack.pop # no error recovery, but works for valid JSON
48 end
49 elsif match = scan(/ true | false | null /x)
50 kind = IDENT_KIND[match]
51 elsif match = scan(/-?(?:0|[1-9]\d*)/)
52 kind = :integer
53 if scan(/\.\d+(?:[eE][-+]?\d+)?|[eE][-+]?\d+/)
54 match << matched
55 kind = :float
56 end
57 elsif match = scan(/"/)
58 state = key_expected ? :key : :string
59 tokens << [:open, state]
60 kind = :delimiter
61 else
62 getch
63 kind = :error
64 end
65
66 when :string, :key
67 if scan(/[^\\"]+/)
68 kind = :content
69 elsif scan(/"/)
70 tokens << ['"', :delimiter]
71 tokens << [:close, state]
72 state = :initial
73 next
74 elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
75 kind = :char
76 elsif scan(/\\./m)
77 kind = :content
78 elsif scan(/ \\ | $ /x)
79 tokens << [:close, :delimiter]
80 kind = :error
81 state = :initial
82 else
83 raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
84 end
85
86 else
87 raise_inspect 'Unknown state', tokens
88
89 end
90
91 match ||= matched
92 if $CODERAY_DEBUG and not kind
93 raise_inspect 'Error token %p in line %d' %
94 [[match, kind], line], tokens
95 end
96 raise_inspect 'Empty token', tokens unless match
97
98 tokens << [match, kind]
99
100 end
101
102 if [:string, :key].include? state
103 tokens << [:close, state]
104 end
105
106 tokens
107 end
108
109 end
110
111 end
112 end