comparison vendor/gems/coderay-1.0.0/lib/coderay/scanners/cpp.rb @ 909:cbb26bc654de redmine-1.3

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