PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/gems/syntax-1.0.0/lib/syntax/lang/ruby.rb

https://github.com/wangmh/webistrano
Ruby | 317 lines | 298 code | 11 blank | 8 comment | 15 complexity | 1f0b7fab9cc209acdbb143c33d00d1f4 MD5 | raw file
  1. require 'syntax'
  2. module Syntax
  3. # A tokenizer for the Ruby language. It recognizes all common syntax
  4. # (and some less common syntax) but because it is not a true lexer, it
  5. # will make mistakes on some ambiguous cases.
  6. class Ruby < Tokenizer
  7. # The list of all identifiers recognized as keywords.
  8. KEYWORDS =
  9. %w{if then elsif else end begin do rescue ensure while for
  10. class module def yield raise until unless and or not when
  11. case super undef break next redo retry in return alias
  12. defined?}
  13. # Perform ruby-specific setup
  14. def setup
  15. @selector = false
  16. @allow_operator = false
  17. @heredocs = []
  18. end
  19. # Step through a single iteration of the tokenization process.
  20. def step
  21. case
  22. when bol? && check( /=begin/ )
  23. start_group( :comment, scan_until( /^=end#{EOL}/ ) )
  24. when bol? && check( /__END__#{EOL}/ )
  25. start_group( :comment, scan_until( /\Z/ ) )
  26. else
  27. case
  28. when check( /def\s+/ )
  29. start_group :keyword, scan( /def\s+/ )
  30. start_group :method, scan_until( /(?=[;(\s]|#{EOL})/ )
  31. when check( /class\s+/ )
  32. start_group :keyword, scan( /class\s+/ )
  33. start_group :class, scan_until( /(?=[;\s<]|#{EOL})/ )
  34. when check( /module\s+/ )
  35. start_group :keyword, scan( /module\s+/ )
  36. start_group :module, scan_until( /(?=[;\s]|#{EOL})/ )
  37. when check( /::/ )
  38. start_group :punct, scan(/::/)
  39. when check( /:"/ )
  40. start_group :symbol, scan(/:/)
  41. scan_delimited_region :symbol, :symbol, "", true
  42. @allow_operator = true
  43. when check( /:'/ )
  44. start_group :symbol, scan(/:/)
  45. scan_delimited_region :symbol, :symbol, "", false
  46. @allow_operator = true
  47. when scan( /:[_a-zA-Z@$][$@\w]*[=!?]?/ )
  48. start_group :symbol, matched
  49. @allow_operator = true
  50. when scan( /\?(\\[^\n\r]|[^\\\n\r\s])/ )
  51. start_group :char, matched
  52. @allow_operator = true
  53. when check( /(__FILE__|__LINE__|true|false|nil|self)[?!]?/ )
  54. if @selector || matched[-1] == ?? || matched[-1] == ?!
  55. start_group :ident,
  56. scan(/(__FILE__|__LINE__|true|false|nil|self)[?!]?/)
  57. else
  58. start_group :constant,
  59. scan(/(__FILE__|__LINE__|true|false|nil|self)/)
  60. end
  61. @selector = false
  62. @allow_operator = true
  63. when scan(/0([bB][01]+|[oO][0-7]+|[dD][0-9]+|[xX][0-9a-fA-F]+)/)
  64. start_group :number, matched
  65. @allow_operator = true
  66. else
  67. case peek(2)
  68. when "%r"
  69. scan_delimited_region :punct, :regex, scan( /../ ), true
  70. @allow_operator = true
  71. when "%w", "%q"
  72. scan_delimited_region :punct, :string, scan( /../ ), false
  73. @allow_operator = true
  74. when "%s"
  75. scan_delimited_region :punct, :symbol, scan( /../ ), false
  76. @allow_operator = true
  77. when "%W", "%Q", "%x"
  78. scan_delimited_region :punct, :string, scan( /../ ), true
  79. @allow_operator = true
  80. when /%[^\sa-zA-Z0-9]/
  81. scan_delimited_region :punct, :string, scan( /./ ), true
  82. @allow_operator = true
  83. when "<<"
  84. saw_word = ( chunk[-1,1] =~ /[\w!?]/ )
  85. start_group :punct, scan( /<</ )
  86. if saw_word
  87. @allow_operator = false
  88. return
  89. end
  90. float_right = scan( /-/ )
  91. append "-" if float_right
  92. if ( type = scan( /['"]/ ) )
  93. append type
  94. delim = scan_until( /(?=#{type})/ )
  95. if delim.nil?
  96. append scan_until( /\Z/ )
  97. return
  98. end
  99. else
  100. delim = scan( /\w+/ ) or return
  101. end
  102. start_group :constant, delim
  103. start_group :punct, scan( /#{type}/ ) if type
  104. @heredocs << [ float_right, type, delim ]
  105. @allow_operator = true
  106. else
  107. case peek(1)
  108. when /[\n\r]/
  109. unless @heredocs.empty?
  110. scan_heredoc(*@heredocs.shift)
  111. else
  112. start_group :normal, scan( /\s+/ )
  113. end
  114. @allow_operator = false
  115. when /\s/
  116. start_group :normal, scan( /\s+/ )
  117. when "#"
  118. start_group :comment, scan( /#[^\n\r]*/ )
  119. when /[A-Z]/
  120. start_group @selector ? :ident : :constant, scan( /\w+/ )
  121. @allow_operator = true
  122. when /[a-z_]/
  123. word = scan( /\w+[?!]?/ )
  124. if !@selector && KEYWORDS.include?( word )
  125. start_group :keyword, word
  126. @allow_operator = false
  127. elsif
  128. start_group :ident, word
  129. @allow_operator = true
  130. end
  131. @selector = false
  132. when /\d/
  133. start_group :number,
  134. scan( /[\d_]+(\.[\d_]+)?([eE][\d_]+)?/ )
  135. @allow_operator = true
  136. when '"'
  137. scan_delimited_region :punct, :string, "", true
  138. @allow_operator = true
  139. when '/'
  140. if @allow_operator
  141. start_group :punct, scan(%r{/})
  142. @allow_operator = false
  143. else
  144. scan_delimited_region :punct, :regex, "", true
  145. @allow_operator = true
  146. end
  147. when "'"
  148. scan_delimited_region :punct, :string, "", false
  149. @allow_operator = true
  150. when "."
  151. dots = scan( /\.{1,3}/ )
  152. start_group :punct, dots
  153. @selector = ( dots.length == 1 )
  154. when /[@]/
  155. start_group :attribute, scan( /@{1,2}\w*/ )
  156. @allow_operator = true
  157. when /[$]/
  158. start_group :global, scan(/\$/)
  159. start_group :global, scan( /\w+|./ ) if check(/./)
  160. @allow_operator = true
  161. when /[-!?*\/+=<>(\[\{}:;,&|%]/
  162. start_group :punct, scan(/./)
  163. @allow_operator = false
  164. when /[)\]]/
  165. start_group :punct, scan(/./)
  166. @allow_operator = true
  167. else
  168. # all else just falls through this, to prevent
  169. # infinite loops...
  170. append getch
  171. end
  172. end
  173. end
  174. end
  175. end
  176. private
  177. # Scan a delimited region of text. This handles the simple cases (strings
  178. # delimited with quotes) as well as the more complex cases of %-strings
  179. # and here-documents.
  180. #
  181. # * +delim_group+ is the group to use to classify the delimiters of the
  182. # region
  183. # * +inner_group+ is the group to use to classify the contents of the
  184. # region
  185. # * +starter+ is the text to use as the starting delimiter
  186. # * +exprs+ is a boolean flag indicating whether the region is an
  187. # interpolated string or not
  188. # * +delim+ is the text to use as the delimiter of the region. If +nil+,
  189. # the next character will be treated as the delimiter.
  190. # * +heredoc+ is either +false+, meaning the region is not a heredoc, or
  191. # <tt>:flush</tt> (meaning the delimiter must be flushed left), or
  192. # <tt>:float</tt> (meaning the delimiter doens't have to be flush left).
  193. def scan_delimited_region( delim_group, inner_group, starter, exprs,
  194. delim=nil, heredoc=false )
  195. # begin
  196. if !delim
  197. start_group delim_group, starter
  198. delim = scan( /./ )
  199. append delim
  200. delim = case delim
  201. when '{' then '}'
  202. when '(' then ')'
  203. when '[' then ']'
  204. when '<' then '>'
  205. else delim
  206. end
  207. end
  208. start_region inner_group
  209. items = "\\\\|"
  210. if heredoc
  211. items << "(^"
  212. items << '\s*' if heredoc == :float
  213. items << "#{Regexp.escape(delim)}\s*?)#{EOL}"
  214. else
  215. items << "#{Regexp.escape(delim)}"
  216. end
  217. items << "|#(\\$|@@?|\\{)" if exprs
  218. items = Regexp.new( items )
  219. loop do
  220. p = pos
  221. match = scan_until( items )
  222. if match.nil?
  223. start_group inner_group, scan_until( /\Z/ )
  224. break
  225. else
  226. text = pre_match[p..-1]
  227. start_group inner_group, text if text.length > 0
  228. case matched.strip
  229. when "\\"
  230. unless exprs
  231. case peek(1)
  232. when "'"
  233. scan(/./)
  234. start_group :escape, "\\'"
  235. when "\\"
  236. scan(/./)
  237. start_group :escape, "\\\\"
  238. else
  239. start_group inner_group, "\\"
  240. end
  241. else
  242. start_group :escape, "\\"
  243. c = getch
  244. append c
  245. case c
  246. when 'x'
  247. append scan( /[a-fA-F0-9]{1,2}/ )
  248. when /[0-7]/
  249. append scan( /[0-7]{0,2}/ )
  250. end
  251. end
  252. when delim
  253. end_region inner_group
  254. start_group delim_group, matched
  255. break
  256. when /^#/
  257. do_highlight = (option(:expressions) == :highlight)
  258. start_region :expr if do_highlight
  259. start_group :expr, matched
  260. case matched[1]
  261. when ?{
  262. depth = 1
  263. content = ""
  264. while depth > 0
  265. p = pos
  266. c = scan_until( /[\{}]/ )
  267. if c.nil?
  268. content << scan_until( /\Z/ )
  269. break
  270. else
  271. depth += ( matched == "{" ? 1 : -1 )
  272. content << pre_match[p..-1]
  273. content << matched if depth > 0
  274. end
  275. end
  276. if do_highlight
  277. subtokenize "ruby", content
  278. start_group :expr, "}"
  279. else
  280. append content + "}"
  281. end
  282. when ?$, ?@
  283. append scan( /\w+/ )
  284. end
  285. end_region :expr if do_highlight
  286. else raise "unexpected match on #{matched}"
  287. end
  288. end
  289. end
  290. end
  291. # Scan a heredoc beginning at the current position.
  292. #
  293. # * +float+ indicates whether the delimiter may be floated to the right
  294. # * +type+ is +nil+, a single quote, or a double quote
  295. # * +delim+ is the delimiter to look for
  296. def scan_heredoc(float, type, delim)
  297. scan_delimited_region( :constant, :string, "", type != "'",
  298. delim, float ? :float : :flush )
  299. end
  300. end
  301. SYNTAX["ruby"] = Ruby
  302. end