summaryrefslogtreecommitdiff
path: root/lib/coderay/scanners/java_script7.rb
blob: 082a781ea95b9727d25a5d71969c0a26c130951f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# Trying to imitate https://github.com/jneen/rouge/blob/master/lib/rouge/lexers/javascript.rb.
module CodeRay
module Scanners
  
  # Scanner for JavaScript.
  # 
  # Aliases: +ecmascript+, +ecma_script+, +javascript+
  class JavaScript7 < RougeScanner
    register_for :java_script7
    file_extension 'js'
    
    state :multiline_comment do
      rule %r([*]/), Comment::Multiline, :pop!
      rule %r([^*/]+), Comment::Multiline
      rule %r([*/]), Comment::Multiline
    end

    state :comments_and_whitespace do
      rule /\s+/, Text
      rule /<!--/, Comment # really...?
      rule %r(//.*?$), Comment::Single
      rule %r(/[*]), Comment::Multiline, :multiline_comment
    end

    state :expr_start do
      mixin :comments_and_whitespace

      rule %r(/) do
        token Str::Regex
        goto :regex
      end

      rule /[{]/ do
        token Punctuation
        goto :object
      end

      rule //, Text, :pop!
    end

    state :regex do
      rule %r(/) do
        token Str::Regex
        goto :regex_end
      end

      rule %r([^/]\n), Error, :pop!

      rule /\n/, Error, :pop!
      rule /\[\^/, Str::Escape, :regex_group
      rule /\[/, Str::Escape, :regex_group
      rule /\\./, Str::Escape
      rule %r{[(][?][:=<!]}, Str::Escape
      rule /[{][\d,]+[}]/, Str::Escape
      rule /[()?]/, Str::Escape
      rule /./, Str::Regex
    end

    state :regex_end do
      rule /[gim]+/, Str::Regex, :pop!
      rule(//) { pop! }
    end

    state :regex_group do
      # specially highlight / in a group to indicate that it doesn't
      # close the regex
      rule /\//, Str::Escape

      rule %r([^/]\n) do
        token Error
        pop! 2
      end

      rule /\]/, Str::Escape, :pop!
      rule /\\./, Str::Escape
      rule /./, Str::Regex
    end

    state :bad_regex do
      rule /[^\n]+/, Error, :pop!
    end

    def self.keywords
      @keywords ||= Set.new %w(
        for in of while do break return continue switch case default
        if else throw try catch finally new delete typeof instanceof
        void this yield import export from as async super this
      )
    end

    def self.declarations
      @declarations ||= Set.new %w(
        var let const with function class
        extends constructor get set
      )
    end

    def self.reserved
      @reserved ||= Set.new %w(
        abstract boolean byte char debugger double enum
        final float goto implements int interface
        long native package private protected public short static
        synchronized throws transient volatile
        eval arguments await
      )
    end

    def self.constants
      @constants ||= Set.new %w(true false null NaN Infinity undefined)
    end

    def self.builtins
      @builtins ||= %w(
        Array Boolean Date Error Function Math netscape
        Number Object Packages RegExp String sun decodeURI
        decodeURIComponent encodeURI encodeURIComponent
        Error eval isFinite isNaN parseFloat parseInt
        document window navigator self global
        Promise Set Map WeakSet WeakMap Symbol Proxy Reflect
        Int8Array Uint8Array Uint8ClampedArray
        Int16Array Uint16Array Uint16ClampedArray
        Int32Array Uint32Array Uint32ClampedArray
        Float32Array Float64Array DataView ArrayBuffer
      )
    end

    def self.id_regex
      /[$a-z_][a-z0-9_]*/io
    end

    id = self.id_regex

    state :root do
      rule /\A\s*#!.*?\n/m, Comment::Preproc, :statement
      rule %r((?<=\n)(?=\s|/|<!--)), Text, :expr_start
      mixin :comments_and_whitespace
      rule %r(\+\+ | -- | ~ | && | \|\| | \\(?=\n) | << | >>>? | ===
             | !== )x,
        Operator, :expr_start
      rule %r([-<>+*%&|\^/!=]=?), Operator, :expr_start
      rule /[(\[,]/, Punctuation, :expr_start
      rule /;/, Punctuation, :statement
      rule /[)\].]/, Punctuation

      rule /`/ do
        token Str::Double
        push :template_string
      end

      rule /[?]/ do
        token Punctuation
        push :ternary
        push :expr_start
      end

      rule /(\@)(\w+)?/ do
        groups Punctuation, Name::Decorator
        push :expr_start
      end

      rule /[{}]/, Punctuation, :statement

      rule id do |m|
        if self.class.keywords.include? m[0]
          token Keyword
          push :expr_start
        elsif self.class.declarations.include? m[0]
          token Keyword::Declaration
          push :expr_start
        elsif self.class.reserved.include? m[0]
          token Keyword::Reserved
        elsif self.class.constants.include? m[0]
          token Keyword::Constant
        elsif self.class.builtins.include? m[0]
          token Name::Builtin
        else
          token Name::Other
        end
      end

      rule /[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?/, Num::Float
      rule /0x[0-9a-fA-F]+/i, Num::Hex
      rule /0o[0-7][0-7_]*/i, Num::Oct
      rule /0b[01][01_]*/i, Num::Bin
      rule /[0-9]+/, Num::Integer

      rule /"/, Str::Double, :dq
      rule /'/, Str::Single, :sq
      rule /:/, Punctuation
    end

    state :dq do
      rule /[^\\"]+/, Str::Double
      rule /\\"/, Str::Escape
      rule /"/, Str::Double, :pop!
    end

    state :sq do
      rule /[^\\']+/, Str::Single
      rule /\\'/, Str::Escape
      rule /'/, Str::Single, :pop!
    end

    # braced parts that aren't object literals
    state :statement do
      rule /case\b/ do
        token Keyword
        goto :expr_start
      end

      rule /(#{id})(\s*)(:)/ do
        groups Name::Label, Text, Punctuation
      end

      rule /[{}]/, Punctuation

      mixin :expr_start
    end

    # object literals
    state :object do
      mixin :comments_and_whitespace

      rule /[{]/ do
        token Punctuation
        push
      end

      rule /[}]/ do
        token Punctuation
        goto :statement
      end

      rule /(#{id})(\s*)(:)/ do
        groups Name::Attribute, Text, Punctuation
        push :expr_start
      end

      rule /:/, Punctuation
      mixin :root
    end

    # ternary expressions, where <id>: is not a label!
    state :ternary do
      rule /:/ do
        token Punctuation
        goto :expr_start
      end

      mixin :root
    end

    # template strings
    state :template_string do
      rule /\${/, Punctuation, :template_string_expr
      rule /`/, Str::Double, :pop!
      rule /(\\\\|\\[\$`]|[^\$`]|\$(?!{))*/, Str::Double
    end

    state :template_string_expr do
      rule /}/, Punctuation, :pop!
      mixin :root
    end
    
  end
  
end
end