# http://pastie.textmate.org/50774/ module CodeRay module Scanners class JavaScript < Scanner register_for :javascript RESERVED_WORDS = [ 'asm', 'break', 'case', 'continue', 'default', 'do', 'else', 'for', 'goto', 'if', 'return', 'switch', 'while', # 'struct', 'union', 'enum', 'typedef', # 'static', 'register', 'auto', 'extern', # 'sizeof', 'typeof', # 'volatile', 'const', # C89 # 'inline', 'restrict', # C99 'var', 'function','try','new','in', 'instanceof','throw','catch' ] PREDEFINED_CONSTANTS = [ 'void', 'null', 'this', 'true', 'false','undefined', ] IDENT_KIND = WordList.new(:ident). add(RESERVED_WORDS, :reserved). add(PREDEFINED_CONSTANTS, :pre_constant) ESCAPE = / [rbfnrtv\n\\\/'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x def scan_tokens tokens, options state = :initial string_type = nil regexp_allowed = true until eos? kind = :error match = nil if state == :initial if scan(/ \s+ | \\\n /x) kind = :space elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx) kind = :comment regexp_allowed = false elsif match = scan(/ \# \s* if \s* 0 /x) match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos? kind = :comment regexp_allowed = false elsif regexp_allowed and scan(/\//) tokens << [:open, :regexp] state = :regex kind = :delimiter elsif scan(/ [-+*\/=<>?:;,!&^|()\[\]{}~%] | \.(?!\d) /x) kind = :operator regexp_allowed=true elsif match = scan(/ [$A-Za-z_][A-Za-z_0-9]* /x) kind = IDENT_KIND[match] # if kind == :ident and check(/:(?!:)/) # match << scan(/:/) # kind = :label # end regexp_allowed=false elsif match = scan(/["']/) tokens << [:open, :string] string_type = matched state = :string kind = :delimiter # elsif scan(/#\s*(\w*)/) # kind = :preprocessor # FIXME multiline preprocs # state = :include_expected if self[1] == 'include' # # elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox) # kind = :char elsif scan(/0[xX][0-9A-Fa-f]+/) kind = :hex regexp_allowed=false elsif scan(/(?:0[0-7]+)(?![89.eEfF])/) kind = :oct regexp_allowed=false elsif scan(/(?:\d+)(?![.eEfF])/) kind = :integer regexp_allowed=false elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/) kind = :float regexp_allowed=false else getch end elsif state == :regex if scan(/[^\\\/]+/) kind = :content elsif scan(/\\\/|\\\\/) kind = :content elsif scan(/\//) tokens << [matched, :delimiter] tokens << [:close, :regexp] state = :initial next else getch kind = :content end elsif state == :string if scan(/[^\\"']+/) kind = :content elsif scan(/["']/) if string_type==matched tokens << [matched, :delimiter] tokens << [:close, :string] state = :initial string_type=nil next else kind = :content end elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox) kind = :char elsif scan(/ \\ | $ /x) kind = :error state = :initial else raise "else case \" reached; %p not handled." % peek(1), tokens end # elsif state == :include_expected # if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/) # kind = :include # state = :initial # # elsif match = scan(/\s+/) # kind = :space # state = :initial if match.index ?\n # # else # getch # # end # else raise 'else-case reached', tokens end match ||= matched # raise [match, kind], tokens if kind == :error tokens << [match, kind] end tokens end end end end