4 module Ruby::Patterns # :nodoc:
7 and def end in or unless begin
8 defined? ensure module redo super until
9 BEGIN break do next rescue then
10 when END case else for retry
11 while alias class elsif if not return
15 DEF_KEYWORDS = %w[ def ]
16 UNDEF_KEYWORDS = %w[ undef ]
17 MODULE_KEYWORDS = %w[class module]
18 DEF_NEW_STATE = WordList.new(:initial).
19 add(DEF_KEYWORDS, :def_expected).
20 add(UNDEF_KEYWORDS, :undef_expected).
21 add(MODULE_KEYWORDS, :module_expected)
23 IDENTS_ALLOWING_REGEXP = %w[
24 and or not while until unless if then elsif when sub sub! gsub gsub!
25 scan slice slice! split
27 REGEXP_ALLOWED = WordList.new(false).
28 add(IDENTS_ALLOWING_REGEXP, :set)
30 PREDEFINED_CONSTANTS = %w[
32 DATA ARGV ARGF __FILE__ __LINE__
35 IDENT_KIND = WordList.new(:ident).
36 add(RESERVED_WORDS, :reserved).
37 add(PREDEFINED_CONSTANTS, :pre_constant)
39 IDENT = /[a-z_][\w_]*/i
41 METHOD_NAME = / #{IDENT} [?!]? /ox
42 METHOD_NAME_OPERATOR = /
43 \*\*? # multiplication and power
44 | [-+]@? # plus, minus
45 | [\/%&|^`~] # division, modulo or format strings, &and, |or, ^xor, `system`, tilde
46 | \[\]=? # array getter and setter
47 | << | >> # append or shift left, shift right
48 | <=?>? | >=? # comparison, rocket operator
49 | ===? # simple equality and case equality
51 METHOD_NAME_EX = / #{IDENT} (?:[?!]|=(?!>))? | #{METHOD_NAME_OPERATOR} /ox
52 INSTANCE_VARIABLE = / @ #{IDENT} /ox
53 CLASS_VARIABLE = / @@ #{IDENT} /ox
54 OBJECT_VARIABLE = / @@? #{IDENT} /ox
55 GLOBAL_VARIABLE = / \$ (?: #{IDENT} | [1-9]\d* | 0\w* | [~&+`'=\/,;_.<>!@$?*":\\] | -[a-zA-Z_0-9] ) /ox
56 PREFIX_VARIABLE = / #{GLOBAL_VARIABLE} |#{OBJECT_VARIABLE} /ox
57 VARIABLE = / @?@? #{IDENT} | #{GLOBAL_VARIABLE} /ox
63 QUOTE_TO_TYPE.default = :string
65 REGEXP_MODIFIERS = /[mixounse]*/
66 REGEXP_SYMBOLS = /[|?*+?(){}\[\].^$]/
68 DECIMAL = /\d+(?:_\d+)*/
69 OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
70 HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
71 BINARY = /0b[01]+(?:_[01]+)*/
73 EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
74 FLOAT_SUFFIX = / #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? /ox
75 FLOAT_OR_INT = / #{DECIMAL} (?: #{FLOAT_SUFFIX} () )? /ox
76 NUMERIC = / [-+]? (?: (?=0) (?: #{OCTAL} | #{HEXADECIMAL} | #{BINARY} ) | #{FLOAT_OR_INT} ) /ox
87 # TODO investigste \M, \c and \C escape sequences
88 # (?: M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-)? (?: \\ (?: [0-7]{3} | x[0-9A-Fa-f]{2} | . ) )
89 # assert_equal(225, ?\M-a)
90 # assert_equal(129, ?\M-\C-a)
93 | M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-
107 # NOTE: This is not completely correct, but
108 # nobody needs heredoc delimiters ending with \n.
112 ( [A-Za-z_0-9]+ ) # $2 = delim
114 ( ["'`\/] ) # $3 = quote, type
115 ( [^\n]*? ) \3 # $4 = delim
122 (?: \Z | ^=end (?!\S) [^\n]* )
128 (?: \Z | (?=^\#CODE) )
131 # Checks for a valid value to follow. This enables
132 # fancy_allowed in method calls.
144 RUBYDOC_OR_DATA = / #{RUBYDOC} | #{DATA} /xo
146 RDOC_DATA_START = / ^=begin (?!\S) | ^__END__$ /x
148 # FIXME: \s and = are only a workaround, they are still allowed
150 FANCY_START_SAVE = / % ( [qQwWxsr] | (?![a-zA-Z0-9\s=]) ) ([^a-zA-Z0-9]) /mx
151 FANCY_START_CORRECT = / % ( [qQwWxsr] | (?![a-zA-Z0-9]) ) ([^a-zA-Z0-9]) /mx
154 'q' => [:string, false],
155 'Q' => [:string, true],
156 'r' => [:regexp, true],
157 's' => [:symbol, false],
158 'x' => [:shell, true]
160 FancyStringType['w'] = FancyStringType['q']
161 FancyStringType['W'] = FancyStringType[''] = FancyStringType['Q']
163 class StringState < Struct.new :type, :interpreted, :delim, :heredoc,
164 :paren, :paren_depth, :pattern, :next_state
166 CLOSING_PAREN = Hash[ *%w[
173 CLOSING_PAREN.values.each { |o| o.freeze } # debug, if I try to change it with <<
174 OPENING_PAREN = CLOSING_PAREN.invert
176 STRING_PATTERN = Hash.new { |h, k|
177 delim, interpreted = *k
178 delim_pattern = Regexp.escape(delim.dup)
179 if closing_paren = CLOSING_PAREN[delim]
180 delim_pattern << Regexp.escape(closing_paren)
187 '| ' + REGEXP_SYMBOLS.source
193 if interpreted and not delim == '#'
194 / (?= [#{delim_pattern}\\] | \# [{$@] #{special_escapes} ) /mx
196 / (?= [#{delim_pattern}\\] #{special_escapes} ) /mx
200 HEREDOC_PATTERN = Hash.new { |h, k|
201 delim, interpreted, indented = *k
202 delim_pattern = Regexp.escape(delim.dup)
203 delim_pattern = / \n #{ '(?>[\ \t]*)' if indented } #{ Regexp.new delim_pattern } $ /x
206 / (?= #{delim_pattern}() | \\ | \# [{$@] ) /mx # $1 set == end of heredoc
208 / (?= #{delim_pattern}() | \\ ) /mx
212 def initialize kind, interpreted, delim, heredoc = false
214 pattern = HEREDOC_PATTERN[ [delim, interpreted, heredoc == :indented] ]
217 pattern = STRING_PATTERN[ [delim, interpreted] ]
218 if paren = CLOSING_PAREN[delim]
219 delim, paren = paren, delim
223 super kind, interpreted, delim, heredoc, paren, paren_depth, pattern, :initial
225 end unless defined? StringState