# -*- coding: utf-8 -*- # # frozen_string_literal: true module Rouge module Lexers class Livescript < RegexLexer tag 'livescript' aliases 'ls' filenames '*.ls' mimetypes 'text/livescript' title 'LiveScript' desc 'LiveScript, a language which compiles to JavaScript (livescript.net)' def self.detect?(text) return text.shebang? 'lsc' end def self.declarations @declarations ||= Set.new %w(const let var function class extends implements) end def self.keywords @keywords ||= Set.new %w( loop until for in of while break return continue switch case fallthrough default otherwise when then if unless else throw try catch finally new delete typeof instanceof super by from to til with require do debugger import export yield ) end def self.constants @constants ||= Javascript.constants + %w(yes no on off void) end def self.builtins @builtins ||= Javascript.builtins + %w(this it that arguments) end def self.loop_control_keywords @loop_control_keywords ||= Set.new %w(break continue) end id = /[$a-z_]((-(?=[a-z]))?[a-z0-9_])*/i int_number = /\d[\d_]*/ int = /#{int_number}(e[+-]?#{int_number})?[$\w]*/ # the last class matches units state :root do rule(%r(^(?=\s|/))) { push :slash_starts_regex } mixin :comments mixin :whitespace # list of words rule %r/(<\[)(.*?)(\]>)/m do groups Punctuation, Str, Punctuation end # function declarations rule %r/!\s*function\b/, Keyword::Declaration rule %r/!?[-~]>|<[-~]!?/, Keyword::Declaration # switch arrow rule %r/(=>)/, Keyword # prototype attributes rule %r/(::)(#{id})/ do groups Punctuation, Name::Attribute push :id end rule %r/(::)(#{int})/ do groups Punctuation, Num::Integer push :id end # instance attributes rule %r/(@)(#{id})/ do groups Name::Variable::Instance, Name::Attribute push :id end rule %r/([.])(#{id})/ do groups Punctuation, Name::Attribute push :id end rule %r/([.])(\d+)/ do groups Punctuation, Num::Integer push :id end rule %r/#{id}(?=\s*:[^:=])/, Name::Attribute # operators rule %r( [+][+]|--|&&|\b(and|x?or|is(nt)?|not)\b(?!-[a-zA-Z]|_)|[|][|]| [.]([|&^]|<<|>>>?)[.]|\\(?=\n)|[.:]=|<<<| (<<|>>|==?|!=?|[-<>+*%^/~?])=? )x, Operator, :slash_starts_regex # arguments shorthand rule %r/(&)(#{id})?/ do groups Name::Builtin, Name::Attribute end # switch case rule %r/[|]|\bcase(?=\s)/, Keyword, :switch_underscore rule %r/@/, Name::Variable::Instance rule %r/[.]{3}/, Punctuation rule %r/:/, Punctuation # keywords rule %r/#{id}/ do |m| if self.class.loop_control_keywords.include? m[0] token Keyword push :loop_control next elsif self.class.keywords.include? m[0] token Keyword elsif self.class.constants.include? m[0] token Name::Constant elsif self.class.builtins.include? m[0] token Name::Builtin elsif self.class.declarations.include? m[0] token Keyword::Declaration elsif /^[A-Z]/.match(m[0]) && /[^-][a-z]/.match(m[0]) token Name::Class else token Name::Variable end push :id end # punctuation and brackets rule %r/\](?=[!?.]|#{id})/, Punctuation, :id rule %r/[{(\[;,]/, Punctuation, :slash_starts_regex rule %r/[})\].]/, Punctuation # literals rule %r/#{int_number}[.]#{int}/, Num::Float rule %r/0x[0-9A-Fa-f]+/, Num::Hex rule %r/#{int}/, Num::Integer # strings rule %r/"""/ do token Str push do rule %r/"""/, Str, :pop! rule %r/"/, Str mixin :double_strings end end rule %r/'''/ do token Str push do rule %r/'''/, Str, :pop! rule %r/'/, Str mixin :single_strings end end rule %r/"/ do token Str push do rule %r/"/, Str, :pop! mixin :double_strings end end rule %r/'/ do token Str push do rule %r/'/, Str, :pop! mixin :single_strings end end # words rule %r/\\\S[^\s,;\])}]*/, Str end state :code_escape do rule %r(\\( c[A-Z]| x[0-9a-fA-F]{2}| u[0-9a-fA-F]{4}| u\{[0-9a-fA-F]{4}\} ))x, Str::Escape end state :interpolated_expression do rule %r/}/, Str::Interpol, :pop! mixin :root end state :interpolation do # with curly braces rule %r/[#][{]/, Str::Interpol, :interpolated_expression # without curly braces rule %r/(#)(#{id})/ do |m| groups Str::Interpol, (self.class.builtins.include? m[2]) ? Name::Builtin : Name::Variable end end state :whitespace do # white space and loop labels rule %r/(\s+?)(?:^([^\S\n]*)(:#{id}))?/m do groups Text, Text, Name::Label end end state :whitespace_single_line do rule %r([^\S\n]+), Text end state :slash_starts_regex do mixin :comments mixin :whitespace mixin :multiline_regex_begin rule %r( /(\\.|[^\[/\\\n]|\[(\\.|[^\]\\\n])*\])+/ # a regex ([gimy]+\b|\B) )x, Str::Regex, :pop! rule(//) { pop! } end state :multiline_regex_begin do rule %r(//) do token Str::Regex goto :multiline_regex end end state :multiline_regex_end do rule %r(//([gimy]+\b|\B)), Str::Regex, :pop! end state :multiline_regex do mixin :multiline_regex_end mixin :regex_comment mixin :interpolation mixin :code_escape rule %r/\\\D/, Str::Escape rule %r/\\\d+/, Name::Variable rule %r/./m, Str::Regex end state :regex_comment do rule %r/^#(\s+.*)?$/, Comment::Single rule %r/(\s+)(#)(\s+.*)?$/ do groups Text, Comment::Single, Comment::Single end end state :comments do rule %r(/\*.*?\*/)m, Comment::Multiline rule %r/#.*$/, Comment::Single end state :switch_underscore do mixin :whitespace_single_line rule %r/_(?=\s*=>|\s+then\b)/, Keyword rule(//) { pop! } end state :loop_control do mixin :whitespace_single_line rule %r/#{id}(?=[);\n])/, Name::Label rule(//) { pop! } end state :id do rule %r/[!?]|[.](?!=)/, Punctuation rule %r/[{]/ do # destructuring token Punctuation push do rule %r/[,;]/, Punctuation rule %r/#{id}/, Name::Attribute rule %r/#{int}/, Num::Integer mixin :whitespace rule %r/[}]/, Punctuation, :pop! end end rule %r/#{id}/, Name::Attribute rule %r/#{int}/, Num::Integer rule(//) { goto :slash_starts_regex } end state :strings do # all strings are multi-line rule %r/[^#\\'"]+/m, Str mixin :code_escape rule %r/\\./, Str::Escape rule %r/#/, Str end state :double_strings do rule %r/'/, Str mixin :interpolation mixin :strings end state :single_strings do rule %r/"/, Str mixin :strings end end end end