To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / a1 / a194f771cf0697754774b1fd566535421f7cd91f.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (6.45 KB)

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