comparison vendor/plugins/coderay-0.9.2/lib/coderay/scanners/cpp.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 CPlusPlus < Scanner
5
6 include Streamable
7
8 register_for :cpp
9 file_extension 'cpp'
10 title 'C++'
11
12 # http://www.cppreference.com/wiki/keywords/start
13 RESERVED_WORDS = [
14 'and', 'and_eq', 'asm', 'bitand', 'bitor', 'break',
15 'case', 'catch', 'class', 'compl', 'const_cast',
16 'continue', 'default', 'delete', 'do', 'dynamic_cast', 'else',
17 'enum', 'export', 'for', 'goto', 'if', 'namespace', 'new',
18 'not', 'not_eq', 'or', 'or_eq', 'reinterpret_cast', 'return',
19 'sizeof', 'static_cast', 'struct', 'switch', 'template',
20 'throw', 'try', 'typedef', 'typeid', 'typename', 'union',
21 'while', 'xor', 'xor_eq'
22 ]
23
24 PREDEFINED_TYPES = [
25 'bool', 'char', 'double', 'float', 'int', 'long',
26 'short', 'signed', 'unsigned', 'wchar_t', 'string'
27 ]
28 PREDEFINED_CONSTANTS = [
29 'false', 'true',
30 'EOF', 'NULL',
31 ]
32 PREDEFINED_VARIABLES = [
33 'this'
34 ]
35 DIRECTIVES = [
36 'auto', 'const', 'explicit', 'extern', 'friend', 'inline', 'mutable', 'operator',
37 'private', 'protected', 'public', 'register', 'static', 'using', 'virtual', 'void',
38 'volatile'
39 ]
40
41 IDENT_KIND = WordList.new(:ident).
42 add(RESERVED_WORDS, :reserved).
43 add(PREDEFINED_TYPES, :pre_type).
44 add(PREDEFINED_VARIABLES, :local_variable).
45 add(DIRECTIVES, :directive).
46 add(PREDEFINED_CONSTANTS, :pre_constant)
47
48 ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
49 UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
50
51 def scan_tokens tokens, options
52
53 state = :initial
54 label_expected = true
55 case_expected = false
56 label_expected_before_preproc_line = nil
57 in_preproc_line = false
58
59 until eos?
60
61 kind = nil
62 match = nil
63
64 case state
65
66 when :initial
67
68 if match = scan(/ \s+ | \\\n /x)
69 if in_preproc_line && match != "\\\n" && match.index(?\n)
70 in_preproc_line = false
71 label_expected = label_expected_before_preproc_line
72 end
73 tokens << [match, :space]
74 next
75
76 elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
77 kind = :comment
78
79 elsif match = scan(/ \# \s* if \s* 0 /x)
80 match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
81 kind = :comment
82
83 elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x)
84 label_expected = match =~ /[;\{\}]/
85 if case_expected
86 label_expected = true if match == ':'
87 case_expected = false
88 end
89 kind = :operator
90
91 elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
92 kind = IDENT_KIND[match]
93 if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/)
94 kind = :label
95 match << matched
96 else
97 label_expected = false
98 if kind == :reserved
99 case match
100 when 'class'
101 state = :class_name_expected
102 when 'case', 'default'
103 case_expected = true
104 end
105 end
106 end
107
108 elsif scan(/\$/)
109 kind = :ident
110
111 elsif match = scan(/L?"/)
112 tokens << [:open, :string]
113 if match[0] == ?L
114 tokens << ['L', :modifier]
115 match = '"'
116 end
117 state = :string
118 kind = :delimiter
119
120 elsif scan(/#[ \t]*(\w*)/)
121 kind = :preprocessor
122 in_preproc_line = true
123 label_expected_before_preproc_line = label_expected
124 state = :include_expected if self[1] == 'include'
125
126 elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
127 label_expected = false
128 kind = :char
129
130 elsif scan(/0[xX][0-9A-Fa-f]+/)
131 label_expected = false
132 kind = :hex
133
134 elsif scan(/(?:0[0-7]+)(?![89.eEfF])/)
135 label_expected = false
136 kind = :oct
137
138 elsif scan(/(?:\d+)(?![.eEfF])L?L?/)
139 label_expected = false
140 kind = :integer
141
142 elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
143 label_expected = false
144 kind = :float
145
146 else
147 getch
148 kind = :error
149
150 end
151
152 when :string
153 if scan(/[^\\"]+/)
154 kind = :content
155 elsif scan(/"/)
156 tokens << ['"', :delimiter]
157 tokens << [:close, :string]
158 state = :initial
159 label_expected = false
160 next
161 elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
162 kind = :char
163 elsif scan(/ \\ | $ /x)
164 tokens << [:close, :string]
165 kind = :error
166 state = :initial
167 label_expected = false
168 else
169 raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
170 end
171
172 when :include_expected
173 if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
174 kind = :include
175 state = :initial
176
177 elsif match = scan(/\s+/)
178 kind = :space
179 state = :initial if match.index ?\n
180
181 else
182 state = :initial
183 next
184
185 end
186
187 when :class_name_expected
188 if scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
189 kind = :class
190 state = :initial
191
192 elsif match = scan(/\s+/)
193 kind = :space
194
195 else
196 getch
197 kind = :error
198 state = :initial
199
200 end
201
202 else
203 raise_inspect 'Unknown state', tokens
204
205 end
206
207 match ||= matched
208 if $CODERAY_DEBUG and not kind
209 raise_inspect 'Error token %p in line %d' %
210 [[match, kind], line], tokens
211 end
212 raise_inspect 'Empty token', tokens unless match
213
214 tokens << [match, kind]
215
216 end
217
218 if state == :string
219 tokens << [:close, :string]
220 end
221
222 tokens
223 end
224
225 end
226
227 end
228 end