ruby_parser 3.13.1 → 3.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.autotest +18 -29
  4. data/History.rdoc +312 -0
  5. data/Manifest.txt +16 -15
  6. data/README.rdoc +13 -9
  7. data/Rakefile +237 -106
  8. data/bin/ruby_parse +3 -1
  9. data/bin/ruby_parse_extract_error +9 -4
  10. data/compare/normalize.rb +54 -6
  11. data/debugging.md +172 -0
  12. data/gauntlet.md +107 -0
  13. data/lib/rp_extensions.rb +15 -36
  14. data/lib/rp_stringscanner.rb +20 -51
  15. data/lib/ruby_lexer.rb +515 -812
  16. data/lib/ruby_lexer.rex +33 -27
  17. data/lib/ruby_lexer.rex.rb +64 -31
  18. data/lib/ruby_lexer_strings.rb +638 -0
  19. data/lib/ruby_parser.rb +46 -36
  20. data/lib/{ruby_parser.yy → ruby_parser2.yy} +1400 -488
  21. data/lib/ruby_parser20.rb +10953 -0
  22. data/lib/ruby_parser21.rb +10978 -0
  23. data/lib/ruby_parser22.rb +11119 -0
  24. data/lib/ruby_parser23.rb +11160 -0
  25. data/lib/ruby_parser24.rb +11209 -0
  26. data/lib/ruby_parser25.rb +11209 -0
  27. data/lib/ruby_parser26.rb +11231 -0
  28. data/lib/ruby_parser27.rb +12960 -0
  29. data/lib/{ruby26_parser.y → ruby_parser3.yy} +1652 -521
  30. data/lib/ruby_parser30.rb +13292 -0
  31. data/lib/ruby_parser31.rb +13625 -0
  32. data/lib/ruby_parser32.rb +13577 -0
  33. data/lib/ruby_parser33.rb +13577 -0
  34. data/lib/ruby_parser_extras.rb +988 -474
  35. data/test/test_ruby_lexer.rb +1339 -1155
  36. data/test/test_ruby_parser.rb +4255 -2103
  37. data/test/test_ruby_parser_extras.rb +39 -4
  38. data/tools/munge.rb +52 -13
  39. data/tools/ripper.rb +24 -6
  40. data.tar.gz.sig +0 -0
  41. metadata +73 -56
  42. metadata.gz.sig +0 -0
  43. data/lib/ruby20_parser.rb +0 -6869
  44. data/lib/ruby20_parser.y +0 -2431
  45. data/lib/ruby21_parser.rb +0 -6944
  46. data/lib/ruby21_parser.y +0 -2449
  47. data/lib/ruby22_parser.rb +0 -6968
  48. data/lib/ruby22_parser.y +0 -2458
  49. data/lib/ruby23_parser.rb +0 -6987
  50. data/lib/ruby23_parser.y +0 -2460
  51. data/lib/ruby24_parser.rb +0 -6994
  52. data/lib/ruby24_parser.y +0 -2466
  53. data/lib/ruby25_parser.rb +0 -6994
  54. data/lib/ruby25_parser.y +0 -2466
  55. data/lib/ruby26_parser.rb +0 -7012
data/lib/ruby_lexer.rex CHANGED
@@ -4,11 +4,16 @@
4
4
 
5
5
  class RubyLexer
6
6
 
7
+ option
8
+
9
+ lineno
10
+ column
11
+
7
12
  macro
8
13
 
9
- IDENT /^#{IDENT_CHAR}+/o
14
+ IDENT_CHAR /[a-zA-Z0-9_[:^ascii:]]/
10
15
 
11
- ESC /\\((?>[0-7]{1,3}|x[0-9a-fA-F]{1,2}|M-[^\\]|(C-|c)[^\\]|u[0-9a-fA-F]{1,4}|u\{[0-9a-fA-F]+\}|[^0-7xMCc]))/
16
+ ESC /\\((?>[0-7]{1,3}|x\h{1,2}|M-[^\\]|(C-|c)[^\\]|u\h{1,4}|u\{\h+(?:\s+\h+)*\}|[^0-7xMCc]))/
12
17
  SIMPLE_STRING /((#{ESC}|\#(#{ESC}|[^\{\#\@\$\"\\])|[^\"\\\#])*)/o
13
18
  SSTRING /((\\.|[^\'])*)/
14
19
 
@@ -25,7 +30,8 @@ macro
25
30
 
26
31
  start
27
32
 
28
- return process_string if lex_strterm
33
+ maybe_pop_stack
34
+ return process_string_or_heredoc if lex_strterm
29
35
 
30
36
  self.cmd_state = self.command_start
31
37
  self.command_start = false
@@ -37,18 +43,18 @@ rule
37
43
  # [:state] pattern [actions]
38
44
 
39
45
  # \s - \n + \v
40
- /[\ \t\r\f\v]/ { self.space_seen = true; next }
46
+ /[\ \t\r\f\v]+/ { self.space_seen = true; next }
41
47
 
42
48
  /\n|\#/ process_newline_or_comment
43
49
 
44
50
  /[\]\)\}]/ process_brace_close
45
51
 
46
52
  : /\!/
47
- | is_after_operator? /\!\@/ { result EXPR_ARG, :tUBANG, "!@" }
53
+ | is_after_operator? /\!\@/ { result EXPR_ARG, TOKENS[text], text }
48
54
  | /\![=~]?/ { result :arg_state, TOKENS[text], text }
49
55
 
50
56
  : /\./
51
- | /\.\.\.?/ { result EXPR_BEG, TOKENS[text], text }
57
+ | /\.\.\.?/ process_dots
52
58
  | /\.\d/ { rb_compile_error "no .<digit> floating literal anymore put 0 before dot" }
53
59
  | /\./ { self.lex_state = EXPR_BEG; result EXPR_DOT, :tDOT, "." }
54
60
 
@@ -62,8 +68,8 @@ rule
62
68
  | /\=(?=begin\b)/ { result arg_state, TOKENS[text], text }
63
69
 
64
70
  ruby22_label? /\"#{SIMPLE_STRING}\":/o process_label
65
- /\"(#{SIMPLE_STRING})\"/o { result EXPR_END, :tSTRING, text[1..-2].gsub(ESC) { unescape $1 } }
66
- /\"/ { string STR_DQUOTE; result nil, :tSTRING_BEG, text }
71
+ /\"(#{SIMPLE_STRING})\"/o process_simple_string
72
+ /\"/ { string STR_DQUOTE, '"'; result nil, :tSTRING_BEG, text }
67
73
 
68
74
  /\@\@?\d/ { rb_compile_error "`#{text}` is not allowed as a variable name" }
69
75
  /\@\@?#{IDENT_CHAR}+/o process_ivar
@@ -75,7 +81,7 @@ ruby22_label? /\"#{SIMPLE_STRING}\":/o process_label
75
81
  | /\:\:/ process_colon2
76
82
  | /\:/ process_colon1
77
83
 
78
- /->/ { result EXPR_ENDFN, :tLAMBDA, nil }
84
+ /->/ { result EXPR_ENDFN, :tLAMBDA, text }
79
85
 
80
86
  /[+-]/ process_plus_minus
81
87
 
@@ -94,6 +100,7 @@ ruby22_label? /\"#{SIMPLE_STRING}\":/o process_label
94
100
  /\[/ process_square_bracket
95
101
 
96
102
  was_label? /\'#{SSTRING}\':?/o process_label_or_string
103
+ /\'/ { string STR_SQUOTE, "'"; result nil, :tSTRING_BEG, text }
97
104
 
98
105
  : /\|/
99
106
  | /\|\|\=/ { result EXPR_BEG, :tOP_ASGN, "||" }
@@ -105,9 +112,9 @@ was_label? /\'#{SSTRING}\':?/o process_label_or_string
105
112
 
106
113
  : /\*/
107
114
  | /\*\*=/ { result EXPR_BEG, :tOP_ASGN, "**" }
108
- | /\*\*/ { result(:arg_state, space_vs_beginning(:tDSTAR, :tDSTAR, :tPOW), "**") }
109
- | /\*\=/ { result(EXPR_BEG, :tOP_ASGN, "*") }
110
- | /\*/ { result(:arg_state, space_vs_beginning(:tSTAR, :tSTAR, :tSTAR2), "*") }
115
+ | /\*\*/ { result :arg_state, space_vs_beginning(:tDSTAR, :tDSTAR, :tPOW), "**" }
116
+ | /\*\=/ { result EXPR_BEG, :tOP_ASGN, "*" }
117
+ | /\*/ { result :arg_state, space_vs_beginning(:tSTAR, :tSTAR, :tSTAR2), "*" }
111
118
 
112
119
  # TODO: fix result+process_lchevron to set command_start = true
113
120
  : /</
@@ -124,30 +131,30 @@ was_label? /\'#{SSTRING}\':?/o process_label_or_string
124
131
  | /\>/ { result :arg_state, :tGT, ">" }
125
132
 
126
133
  : /\`/
127
- | expr_fname? /\`/ { result(EXPR_END, :tBACK_REF2, "`") }
134
+ | expr_fname? /\`/ { result EXPR_END, :tBACK_REF2, "`" }
128
135
  | expr_dot? /\`/ { result((cmd_state ? EXPR_CMDARG : EXPR_ARG), :tBACK_REF2, "`") }
129
- | /\`/ { string STR_XQUOTE, '`'; result(nil, :tXSTRING_BEG, "`") }
136
+ | /\`/ { string STR_XQUOTE, '`'; result nil, :tXSTRING_BEG, "`" }
130
137
 
131
138
  /\?/ process_questionmark
132
139
 
133
140
  : /&/
134
- | /\&\&\=/ { result(EXPR_BEG, :tOP_ASGN, "&&") }
135
- | /\&\&/ { result(EXPR_BEG, :tANDOP, "&&") }
136
- | /\&\=/ { result(EXPR_BEG, :tOP_ASGN, "&" ) }
137
- | /\&\./ { result(EXPR_DOT, :tLONELY, "&.") }
141
+ | /\&\&\=/ { result EXPR_BEG, :tOP_ASGN, "&&" }
142
+ | /\&\&/ { result EXPR_BEG, :tANDOP, "&&" }
143
+ | /\&\=/ { result EXPR_BEG, :tOP_ASGN, "&" }
144
+ | /\&\./ { result EXPR_DOT, :tLONELY, "&." }
138
145
  | /\&/ process_amper
139
146
 
140
147
  /\// process_slash
141
148
 
142
149
  : /\^/
143
- | /\^=/ { result(EXPR_BEG, :tOP_ASGN, "^") }
144
- | /\^/ { result(:arg_state, :tCARET, "^") }
150
+ | /\^=/ { result EXPR_BEG, :tOP_ASGN, "^" }
151
+ | /\^/ { result :arg_state, :tCARET, "^" }
145
152
 
146
- /\;/ { self.command_start = true; result(EXPR_BEG, :tSEMI, ";") }
153
+ /\;/ { self.command_start = true; result EXPR_BEG, :tSEMI, ";" }
147
154
 
148
155
  : /~/
149
- | is_after_operator? /\~@/ { result(:arg_state, :tTILDE, "~") }
150
- | /\~/ { result(:arg_state, :tTILDE, "~") }
156
+ | is_after_operator? /\~@/ { result :arg_state, :tTILDE, "~" }
157
+ | /\~/ { result :arg_state, :tTILDE, "~" }
151
158
 
152
159
  : /\\/
153
160
  | /\\\r?\n/ { self.lineno += 1; self.space_seen = true; next }
@@ -164,13 +171,12 @@ was_label? /\'#{SSTRING}\':?/o process_label_or_string
164
171
  | in_fname? /\$([1-9]\d*)/ process_gvar
165
172
  | /\$([1-9]\d*)/ process_nthref
166
173
  | /\$0/ process_gvar
167
- | /\$[^[:ascii:]]+/ process_gvar
168
- | /\$\W|\$\z/ process_gvar_oddity
169
- | /\$\w+/ process_gvar
174
+ | /\$#{IDENT_CHAR}+/ process_gvar
175
+ | /\$\W/ process_gvar_oddity
170
176
 
171
177
  /\_/ process_underscore
172
178
 
173
- /#{IDENT}/o process_token
179
+ /#{IDENT_CHAR}+/o process_token
174
180
 
175
181
  /\004|\032|\000|\Z/ { [RubyLexer::EOF, RubyLexer::EOF] }
176
182
 
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  # encoding: UTF-8
2
3
  #--
3
4
  # This file is automatically generated. Do not modify it.
4
- # Generated by: oedipus_lex version 2.5.0.
5
+ # Generated by: oedipus_lex version 2.6.2.
5
6
  # Source: lib/ruby_lexer.rex
6
7
  #++
7
8
 
@@ -16,8 +17,8 @@ class RubyLexer
16
17
  require 'strscan'
17
18
 
18
19
  # :stopdoc:
19
- IDENT = /^#{IDENT_CHAR}+/o
20
- ESC = /\\((?>[0-7]{1,3}|x[0-9a-fA-F]{1,2}|M-[^\\]|(C-|c)[^\\]|u[0-9a-fA-F]{1,4}|u\{[0-9a-fA-F]+\}|[^0-7xMCc]))/
20
+ IDENT_CHAR = /[a-zA-Z0-9_[:^ascii:]]/
21
+ ESC = /\\((?>[0-7]{1,3}|x\h{1,2}|M-[^\\]|(C-|c)[^\\]|u\h{1,4}|u\{\h+(?:\s+\h+)*\}|[^0-7xMCc]))/
21
22
  SIMPLE_STRING = /((#{ESC}|\#(#{ESC}|[^\{\#\@\$\"\\])|[^\"\\\#])*)/o
22
23
  SSTRING = /((\\.|[^\'])*)/
23
24
  INT_DEC = /[+]?(?:(?:[1-9][\d_]*|0)(?!\.\d)(ri|r|i)?\b|0d[0-9_]+)(ri|r|i)?/i
@@ -35,6 +36,10 @@ class RubyLexer
35
36
  class ScanError < LexerError ; end
36
37
  # :startdoc:
37
38
 
39
+ ##
40
+ # The current line number.
41
+
42
+ attr_accessor :lineno
38
43
  ##
39
44
  # The file name / path
40
45
 
@@ -68,6 +73,23 @@ class RubyLexer
68
73
  yield
69
74
  end
70
75
 
76
+ ##
77
+ # The previous position. Only available if the :column option is on.
78
+
79
+ attr_accessor :old_pos
80
+
81
+ ##
82
+ # The position of the start of the current line. Only available if the
83
+ # :column option is on.
84
+
85
+ attr_accessor :start_of_current_line_pos
86
+
87
+ ##
88
+ # The current column, starting at 0. Only available if the
89
+ # :column option is on.
90
+ def column
91
+ old_pos - start_of_current_line_pos
92
+ end
71
93
 
72
94
  ##
73
95
  # The current scanner class. Must be overridden in subclasses.
@@ -81,6 +103,8 @@ class RubyLexer
81
103
 
82
104
  def parse str
83
105
  self.ss = scanner_class.new str
106
+ self.lineno = 1
107
+ self.start_of_current_line_pos = 0
84
108
  self.state ||= nil
85
109
 
86
110
  do_parse
@@ -102,6 +126,8 @@ class RubyLexer
102
126
  def location
103
127
  [
104
128
  (filename || "<input>"),
129
+ lineno,
130
+ column,
105
131
  ].compact.join(":")
106
132
  end
107
133
 
@@ -109,7 +135,8 @@ class RubyLexer
109
135
  # Lex the next token.
110
136
 
111
137
  def next_token
112
- return process_string if lex_strterm
138
+ maybe_pop_stack
139
+ return process_string_or_heredoc if lex_strterm
113
140
  self.cmd_state = self.command_start
114
141
  self.command_start = false
115
142
  self.space_seen = false # TODO: rename token_seen?
@@ -118,11 +145,17 @@ class RubyLexer
118
145
  token = nil
119
146
 
120
147
  until ss.eos? or token do
148
+ if ss.check(/\n/) then
149
+ self.lineno += 1
150
+ # line starts 1 position after the newline
151
+ self.start_of_current_line_pos = ss.pos + 1
152
+ end
153
+ self.old_pos = ss.pos
121
154
  token =
122
155
  case state
123
156
  when nil then
124
157
  case
125
- when ss.skip(/[\ \t\r\f\v]/) then
158
+ when ss.skip(/[\ \t\r\f\v]+/) then
126
159
  action { self.space_seen = true; next }
127
160
  when text = ss.scan(/\n|\#/) then
128
161
  process_newline_or_comment text
@@ -130,15 +163,15 @@ class RubyLexer
130
163
  process_brace_close text
131
164
  when ss.match?(/\!/) then
132
165
  case
133
- when is_after_operator? && (ss.skip(/\!\@/)) then
134
- action { result EXPR_ARG, :tUBANG, "!@" }
166
+ when is_after_operator? && (text = ss.scan(/\!\@/)) then
167
+ action { result EXPR_ARG, TOKENS[text], text }
135
168
  when text = ss.scan(/\![=~]?/) then
136
169
  action { result :arg_state, TOKENS[text], text }
137
170
  end # group /\!/
138
171
  when ss.match?(/\./) then
139
172
  case
140
173
  when text = ss.scan(/\.\.\.?/) then
141
- action { result EXPR_BEG, TOKENS[text], text }
174
+ process_dots text
142
175
  when ss.skip(/\.\d/) then
143
176
  action { rb_compile_error "no .<digit> floating literal anymore put 0 before dot" }
144
177
  when ss.skip(/\./) then
@@ -160,9 +193,9 @@ class RubyLexer
160
193
  when ruby22_label? && (text = ss.scan(/\"#{SIMPLE_STRING}\":/o)) then
161
194
  process_label text
162
195
  when text = ss.scan(/\"(#{SIMPLE_STRING})\"/o) then
163
- action { result EXPR_END, :tSTRING, text[1..-2].gsub(ESC) { unescape $1 } }
196
+ process_simple_string text
164
197
  when text = ss.scan(/\"/) then
165
- action { string STR_DQUOTE; result nil, :tSTRING_BEG, text }
198
+ action { string STR_DQUOTE, '"'; result nil, :tSTRING_BEG, text }
166
199
  when text = ss.scan(/\@\@?\d/) then
167
200
  action { rb_compile_error "`#{text}` is not allowed as a variable name" }
168
201
  when text = ss.scan(/\@\@?#{IDENT_CHAR}+/o) then
@@ -180,8 +213,8 @@ class RubyLexer
180
213
  when text = ss.scan(/\:/) then
181
214
  process_colon1 text
182
215
  end # group /:/
183
- when ss.skip(/->/) then
184
- action { result EXPR_ENDFN, :tLAMBDA, nil }
216
+ when text = ss.scan(/->/) then
217
+ action { result EXPR_ENDFN, :tLAMBDA, text }
185
218
  when text = ss.scan(/[+-]/) then
186
219
  process_plus_minus text
187
220
  when ss.match?(/[+\d]/) then
@@ -211,6 +244,8 @@ class RubyLexer
211
244
  process_square_bracket text
212
245
  when was_label? && (text = ss.scan(/\'#{SSTRING}\':?/o)) then
213
246
  process_label_or_string text
247
+ when text = ss.scan(/\'/) then
248
+ action { string STR_SQUOTE, "'"; result nil, :tSTRING_BEG, text }
214
249
  when ss.match?(/\|/) then
215
250
  case
216
251
  when ss.skip(/\|\|\=/) then
@@ -229,11 +264,11 @@ class RubyLexer
229
264
  when ss.skip(/\*\*=/) then
230
265
  action { result EXPR_BEG, :tOP_ASGN, "**" }
231
266
  when ss.skip(/\*\*/) then
232
- action { result(:arg_state, space_vs_beginning(:tDSTAR, :tDSTAR, :tPOW), "**") }
267
+ action { result :arg_state, space_vs_beginning(:tDSTAR, :tDSTAR, :tPOW), "**" }
233
268
  when ss.skip(/\*\=/) then
234
- action { result(EXPR_BEG, :tOP_ASGN, "*") }
269
+ action { result EXPR_BEG, :tOP_ASGN, "*" }
235
270
  when ss.skip(/\*/) then
236
- action { result(:arg_state, space_vs_beginning(:tSTAR, :tSTAR, :tSTAR2), "*") }
271
+ action { result :arg_state, space_vs_beginning(:tSTAR, :tSTAR, :tSTAR2), "*" }
237
272
  end # group /\*/
238
273
  when ss.match?(/</) then
239
274
  case
@@ -262,24 +297,24 @@ class RubyLexer
262
297
  when ss.match?(/\`/) then
263
298
  case
264
299
  when expr_fname? && (ss.skip(/\`/)) then
265
- action { result(EXPR_END, :tBACK_REF2, "`") }
300
+ action { result EXPR_END, :tBACK_REF2, "`" }
266
301
  when expr_dot? && (ss.skip(/\`/)) then
267
302
  action { result((cmd_state ? EXPR_CMDARG : EXPR_ARG), :tBACK_REF2, "`") }
268
303
  when ss.skip(/\`/) then
269
- action { string STR_XQUOTE, '`'; result(nil, :tXSTRING_BEG, "`") }
304
+ action { string STR_XQUOTE, '`'; result nil, :tXSTRING_BEG, "`" }
270
305
  end # group /\`/
271
306
  when text = ss.scan(/\?/) then
272
307
  process_questionmark text
273
308
  when ss.match?(/&/) then
274
309
  case
275
310
  when ss.skip(/\&\&\=/) then
276
- action { result(EXPR_BEG, :tOP_ASGN, "&&") }
311
+ action { result EXPR_BEG, :tOP_ASGN, "&&" }
277
312
  when ss.skip(/\&\&/) then
278
- action { result(EXPR_BEG, :tANDOP, "&&") }
313
+ action { result EXPR_BEG, :tANDOP, "&&" }
279
314
  when ss.skip(/\&\=/) then
280
- action { result(EXPR_BEG, :tOP_ASGN, "&" ) }
315
+ action { result EXPR_BEG, :tOP_ASGN, "&" }
281
316
  when ss.skip(/\&\./) then
282
- action { result(EXPR_DOT, :tLONELY, "&.") }
317
+ action { result EXPR_DOT, :tLONELY, "&." }
283
318
  when text = ss.scan(/\&/) then
284
319
  process_amper text
285
320
  end # group /&/
@@ -288,18 +323,18 @@ class RubyLexer
288
323
  when ss.match?(/\^/) then
289
324
  case
290
325
  when ss.skip(/\^=/) then
291
- action { result(EXPR_BEG, :tOP_ASGN, "^") }
326
+ action { result EXPR_BEG, :tOP_ASGN, "^" }
292
327
  when ss.skip(/\^/) then
293
- action { result(:arg_state, :tCARET, "^") }
328
+ action { result :arg_state, :tCARET, "^" }
294
329
  end # group /\^/
295
330
  when ss.skip(/\;/) then
296
- action { self.command_start = true; result(EXPR_BEG, :tSEMI, ";") }
331
+ action { self.command_start = true; result EXPR_BEG, :tSEMI, ";" }
297
332
  when ss.match?(/~/) then
298
333
  case
299
334
  when is_after_operator? && (ss.skip(/\~@/)) then
300
- action { result(:arg_state, :tTILDE, "~") }
335
+ action { result :arg_state, :tTILDE, "~" }
301
336
  when ss.skip(/\~/) then
302
- action { result(:arg_state, :tTILDE, "~") }
337
+ action { result :arg_state, :tTILDE, "~" }
303
338
  end # group /~/
304
339
  when ss.match?(/\\/) then
305
340
  case
@@ -328,16 +363,14 @@ class RubyLexer
328
363
  process_nthref text
329
364
  when text = ss.scan(/\$0/) then
330
365
  process_gvar text
331
- when text = ss.scan(/\$[^[:ascii:]]+/) then
366
+ when text = ss.scan(/\$#{IDENT_CHAR}+/) then
332
367
  process_gvar text
333
- when text = ss.scan(/\$\W|\$\z/) then
368
+ when text = ss.scan(/\$\W/) then
334
369
  process_gvar_oddity text
335
- when text = ss.scan(/\$\w+/) then
336
- process_gvar text
337
370
  end # group /\$/
338
371
  when text = ss.scan(/\_/) then
339
372
  process_underscore text
340
- when text = ss.scan(/#{IDENT}/o) then
373
+ when text = ss.scan(/#{IDENT_CHAR}+/o) then
341
374
  process_token text
342
375
  when ss.skip(/\004|\032|\000|\Z/) then
343
376
  action { [RubyLexer::EOF, RubyLexer::EOF] }