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 / a4 / a4b306b7b7bfa99b0d494098e7c07938749fdf68.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (7.12 KB)

1
module CodeRay
2
module Scanners
3
  
4
  # Scanner for JavaScript.
5
  # 
6
  # Aliases: +ecmascript+, +ecma_script+, +javascript+
7
  class JavaScript < Scanner
8
    
9
    register_for :java_script
10
    file_extension 'js'
11
    
12
    # The actual JavaScript keywords.
13
    KEYWORDS = %w[
14
      break case catch continue default delete do else
15
      finally for function if in instanceof new
16
      return switch throw try typeof var void while with
17
    ]  # :nodoc:
18
    PREDEFINED_CONSTANTS = %w[
19
      false null true undefined NaN Infinity
20
    ]  # :nodoc:
21
    
22
    MAGIC_VARIABLES = %w[ this arguments ]  # :nodoc: arguments was introduced in JavaScript 1.4
23
    
24
    KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
25
      case delete in instanceof new return throw typeof with
26
    ]  # :nodoc:
27
    
28
    # Reserved for future use.
29
    RESERVED_WORDS = %w[
30
      abstract boolean byte char class debugger double enum export extends
31
      final float goto implements import int interface long native package
32
      private protected public short static super synchronized throws transient
33
      volatile
34
    ]  # :nodoc:
35
    
36
    IDENT_KIND = WordList.new(:ident).
37
      add(RESERVED_WORDS, :reserved).
38
      add(PREDEFINED_CONSTANTS, :predefined_constant).
39
      add(MAGIC_VARIABLES, :local_variable).
40
      add(KEYWORDS, :keyword)  # :nodoc:
41
    
42
    ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x  # :nodoc:
43
    UNICODE_ESCAPE =  / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x  # :nodoc:
44
    REGEXP_ESCAPE =  / [bBdDsSwW] /x  # :nodoc:
45
    STRING_CONTENT_PATTERN = {
46
      "'" => /[^\\']+/,
47
      '"' => /[^\\"]+/,
48
      '/' => /[^\\\/]+/,
49
    }  # :nodoc:
50
    KEY_CHECK_PATTERN = {
51
      "'" => / (?> [^\\']* (?: \\. [^\\']* )* ) ' \s* : /mx,
52
      '"' => / (?> [^\\"]* (?: \\. [^\\"]* )* ) " \s* : /mx,
53
    }  # :nodoc:
54
    
55
  protected
56
    
57
    def scan_tokens encoder, options
58
      
59
      state = :initial
60
      string_delimiter = nil
61
      value_expected = true
62
      key_expected = false
63
      function_expected = false
64
      
65
      until eos?
66
        
67
        case state
68
          
69
        when :initial
70
          
71
          if match = scan(/ \s+ | \\\n /x)
72
            value_expected = true if !value_expected && match.index(?\n)
73
            encoder.text_token match, :space
74
            
75
          elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
76
            value_expected = true
77
            encoder.text_token match, :comment
78
            
79
          elsif check(/\.?\d/)
80
            key_expected = value_expected = false
81
            if match = scan(/0[xX][0-9A-Fa-f]+/)
82
              encoder.text_token match, :hex
83
            elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/)
84
              encoder.text_token match, :octal
85
            elsif match = scan(/\d+[fF]|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
86
              encoder.text_token match, :float
87
            elsif match = scan(/\d+/)
88
              encoder.text_token match, :integer
89
            end
90
            
91
          elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim)
92
            # TODO: scan over nested tags
93
            xml_scanner.tokenize match, :tokens => encoder
94
            value_expected = false
95
            next
96
            
97
          elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x)
98
            value_expected = true
99
            last_operator = match[-1]
100
            key_expected = (last_operator == ?{) || (last_operator == ?,)
101
            function_expected = false
102
            encoder.text_token match, :operator
103
            
104
          elsif match = scan(/ [)\]}]+ /x)
105
            function_expected = key_expected = value_expected = false
106
            encoder.text_token match, :operator
107
            
108
          elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x)
109
            kind = IDENT_KIND[match]
110
            value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
111
            # TODO: labels
112
            if kind == :ident
113
              if match.index(?$)  # $ allowed inside an identifier
114
                kind = :predefined
115
              elsif function_expected
116
                kind = :function
117
              elsif check(/\s*[=:]\s*function\b/)
118
                kind = :function
119
              elsif key_expected && check(/\s*:/)
120
                kind = :key
121
              end
122
            end
123
            function_expected = (kind == :keyword) && (match == 'function')
124
            key_expected = false
125
            encoder.text_token match, kind
126
            
127
          elsif match = scan(/["']/)
128
            if key_expected && check(KEY_CHECK_PATTERN[match])
129
              state = :key
130
            else
131
              state = :string
132
            end
133
            encoder.begin_group state
134
            string_delimiter = match
135
            encoder.text_token match, :delimiter
136
            
137
          elsif value_expected && (match = scan(/\//))
138
            encoder.begin_group :regexp
139
            state = :regexp
140
            string_delimiter = '/'
141
            encoder.text_token match, :delimiter
142
            
143
          elsif match = scan(/ \/ /x)
144
            value_expected = true
145
            key_expected = false
146
            encoder.text_token match, :operator
147
            
148
          else
149
            encoder.text_token getch, :error
150
            
151
          end
152
          
153
        when :string, :regexp, :key
154
          if match = scan(STRING_CONTENT_PATTERN[string_delimiter])
155
            encoder.text_token match, :content
156
          elsif match = scan(/["'\/]/)
157
            encoder.text_token match, :delimiter
158
            if state == :regexp
159
              modifiers = scan(/[gim]+/)
160
              encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty?
161
            end
162
            encoder.end_group state
163
            string_delimiter = nil
164
            key_expected = value_expected = false
165
            state = :initial
166
          elsif state != :regexp && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
167
            if string_delimiter == "'" && !(match == "\\\\" || match == "\\'")
168
              encoder.text_token match, :content
169
            else
170
              encoder.text_token match, :char
171
            end
172
          elsif state == :regexp && match = scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
173
            encoder.text_token match, :char
174
          elsif match = scan(/\\./m)
175
            encoder.text_token match, :content
176
          elsif match = scan(/ \\ | $ /x)
177
            encoder.end_group state
178
            encoder.text_token match, :error
179
            key_expected = value_expected = false
180
            state = :initial
181
          else
182
            raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
183
          end
184
          
185
        else
186
          raise_inspect 'Unknown state', encoder
187
          
188
        end
189
        
190
      end
191
      
192
      if [:string, :regexp].include? state
193
        encoder.end_group state
194
      end
195
      
196
      encoder
197
    end
198
    
199
  protected
200
    
201
    def reset_instance
202
      super
203
      @xml_scanner.reset if defined? @xml_scanner
204
    end
205
    
206
    def xml_scanner
207
      @xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => false
208
    end
209
    
210
  end
211
  
212
end
213
end