parser 2.6.3.0 → 2.6.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parser
4
+
5
+ class Lexer::MaxNumparamStack
6
+ def initialize
7
+ @stack = []
8
+ end
9
+
10
+ def cant_have_numparams!
11
+ set(-1)
12
+ end
13
+
14
+ def can_have_numparams?
15
+ top >= 0
16
+ end
17
+
18
+ def register(numparam)
19
+ set( [top, numparam].max )
20
+ end
21
+
22
+ def top
23
+ @stack.last
24
+ end
25
+
26
+ def push
27
+ @stack.push(0)
28
+ end
29
+
30
+ def pop
31
+ @stack.pop
32
+ end
33
+
34
+ private
35
+
36
+ def set(value)
37
+ @stack.pop
38
+ @stack.push(value)
39
+ end
40
+ end
41
+
42
+ end
@@ -10,26 +10,31 @@ module Parser
10
10
  #
11
11
  MESSAGES = {
12
12
  # Lexer errors
13
- :unicode_point_too_large => 'invalid Unicode codepoint (too large)',
14
- :invalid_escape => 'invalid escape character syntax',
15
- :incomplete_escape => 'incomplete character syntax',
16
- :invalid_hex_escape => 'invalid hex escape',
17
- :invalid_unicode_escape => 'invalid Unicode escape',
18
- :unterminated_unicode => 'unterminated Unicode escape',
19
- :escape_eof => 'escape sequence meets end of file',
20
- :string_eof => 'unterminated string meets end of file',
21
- :regexp_options => 'unknown regexp options: %{options}',
22
- :cvar_name => "`%{name}' is not allowed as a class variable name",
23
- :ivar_name => "`%{name}' is not allowed as an instance variable name",
24
- :trailing_in_number => "trailing `%{character}' in number",
25
- :empty_numeric => 'numeric literal without digits',
26
- :invalid_octal => 'invalid octal digit',
27
- :no_dot_digit_literal => 'no .<digit> floating literal anymore; put 0 before dot',
28
- :bare_backslash => 'bare backslash only allowed before newline',
29
- :unexpected => "unexpected `%{character}'",
30
- :embedded_document => 'embedded document meets end of file (and they embark on a romantic journey)',
31
- :heredoc_id_has_newline => 'here document identifier across newlines, never match',
32
- :heredoc_id_ends_with_nl => 'here document identifier ends with a newline',
13
+ :unicode_point_too_large => 'invalid Unicode codepoint (too large)',
14
+ :invalid_escape => 'invalid escape character syntax',
15
+ :incomplete_escape => 'incomplete character syntax',
16
+ :invalid_hex_escape => 'invalid hex escape',
17
+ :invalid_unicode_escape => 'invalid Unicode escape',
18
+ :unterminated_unicode => 'unterminated Unicode escape',
19
+ :escape_eof => 'escape sequence meets end of file',
20
+ :string_eof => 'unterminated string meets end of file',
21
+ :regexp_options => 'unknown regexp options: %{options}',
22
+ :cvar_name => "`%{name}' is not allowed as a class variable name",
23
+ :ivar_name => "`%{name}' is not allowed as an instance variable name",
24
+ :trailing_in_number => "trailing `%{character}' in number",
25
+ :empty_numeric => 'numeric literal without digits',
26
+ :invalid_octal => 'invalid octal digit',
27
+ :no_dot_digit_literal => 'no .<digit> floating literal anymore; put 0 before dot',
28
+ :bare_backslash => 'bare backslash only allowed before newline',
29
+ :unexpected => "unexpected `%{character}'",
30
+ :embedded_document => 'embedded document meets end of file (and they embark on a romantic journey)',
31
+ :heredoc_id_has_newline => 'here document identifier across newlines, never match',
32
+ :heredoc_id_ends_with_nl => 'here document identifier ends with a newline',
33
+ :unterminated_heredoc_id => 'unterminated heredoc id',
34
+ :leading_zero_in_numparam => 'leading zero is not allowed as a numbered parameter',
35
+ :numparam_outside_block => 'numbered parameter outside block',
36
+ :too_large_numparam => 'too large numbered parameter',
37
+ :ordinary_param_defined => 'ordinary parameter is defined',
33
38
 
34
39
  # Lexer warnings
35
40
  :invalid_escape_use => 'invalid character syntax; use ?%{escape}',
@@ -61,6 +66,7 @@ module Parser
61
66
  :block_given_to_yield => 'block given to yield',
62
67
  :invalid_regexp => '%{message}',
63
68
  :invalid_return => 'Invalid return in class/module body',
69
+ :csend_in_lhs_of_masgn => '&. inside multiple assignment destination',
64
70
 
65
71
  # Parser warnings
66
72
  :useless_else => 'else without rescue is useless',
@@ -26,6 +26,7 @@ module Parser
26
26
  ident root lambda indexasgn index procarg0
27
27
  meth_ref restarg_expr blockarg_expr
28
28
  objc_kwarg objc_restarg objc_varargs
29
+ numargs numblock numparam
29
30
  ).map(&:to_sym).to_set.freeze
30
31
 
31
32
  end # Meta
@@ -1885,7 +1885,7 @@ regexp_contents: # nothing
1885
1885
  result = @builder.symbol(val[0])
1886
1886
  }
1887
1887
 
1888
- dsym: tSYMBEG xstring_contents tSTRING_END
1888
+ dsym: tSYMBEG string_contents tSTRING_END
1889
1889
  {
1890
1890
  @lexer.state = :expr_end
1891
1891
  result = @builder.symbol_compose(val[0], val[1], val[2])
@@ -17,7 +17,7 @@ token kCLASS kMODULE kDEF kUNDEF kBEGIN kRESCUE kENSURE kEND kIF kUNLESS
17
17
  tWORDS_BEG tQWORDS_BEG tSYMBOLS_BEG tQSYMBOLS_BEG tSTRING_DBEG
18
18
  tSTRING_DVAR tSTRING_END tSTRING_DEND tSTRING tSYMBOL
19
19
  tNL tEH tCOLON tCOMMA tSPACE tSEMI tLAMBDA tLAMBEG tCHARACTER
20
- tRATIONAL tIMAGINARY tLABEL_END tANDDOT tMETHREF tBDOT2 tBDOT3
20
+ tRATIONAL tIMAGINARY tLABEL_END tANDDOT tMETHREF tBDOT2 tBDOT3 tNUMPARAM
21
21
 
22
22
  prechigh
23
23
  right tBANG tTILDE tUPLUS
@@ -193,6 +193,15 @@ rule
193
193
  result = @builder.assign(val[0], val[1],
194
194
  @builder.array(nil, val[2], nil))
195
195
  }
196
+ | mlhs tEQL mrhs_arg kRESCUE_MOD stmt
197
+ {
198
+ rescue_body = @builder.rescue_body(val[3],
199
+ nil, nil, nil,
200
+ nil, val[4])
201
+ begin_body = @builder.begin_body(val[2], [ rescue_body ])
202
+
203
+ result = @builder.multi_assign(val[0], val[1], begin_body)
204
+ }
196
205
  | mlhs tEQL mrhs_arg
197
206
  {
198
207
  result = @builder.multi_assign(val[0], val[1], val[2])
@@ -480,6 +489,10 @@ rule
480
489
  }
481
490
  | primary_value call_op tIDENTIFIER
482
491
  {
492
+ if (val[1][0] == :anddot)
493
+ diagnostic :error, :csend_in_lhs_of_masgn, nil, val[1]
494
+ end
495
+
483
496
  result = @builder.attr_asgn(val[0], val[1], val[2])
484
497
  }
485
498
  | primary_value tCOLON2 tIDENTIFIER
@@ -488,6 +501,10 @@ rule
488
501
  }
489
502
  | primary_value call_op tCONSTANT
490
503
  {
504
+ if (val[1][0] == :anddot)
505
+ diagnostic :error, :csend_in_lhs_of_masgn, nil, val[1]
506
+ end
507
+
491
508
  result = @builder.attr_asgn(val[0], val[1], val[2])
492
509
  }
493
510
  | primary_value tCOLON2 tCONSTANT
@@ -1294,45 +1311,33 @@ rule
1294
1311
  }
1295
1312
 
1296
1313
  f_margs: f_marg_list
1297
- | f_marg_list tCOMMA tSTAR f_norm_arg
1314
+ | f_marg_list tCOMMA f_rest_marg
1298
1315
  {
1299
1316
  result = val[0].
1300
- push(@builder.restarg(val[2], val[3]))
1317
+ push(val[2])
1301
1318
  }
1302
- | f_marg_list tCOMMA tSTAR f_norm_arg tCOMMA f_marg_list
1319
+ | f_marg_list tCOMMA f_rest_marg tCOMMA f_marg_list
1303
1320
  {
1304
1321
  result = val[0].
1305
- push(@builder.restarg(val[2], val[3])).
1306
- concat(val[5])
1307
- }
1308
- | f_marg_list tCOMMA tSTAR
1309
- {
1310
- result = val[0].
1311
- push(@builder.restarg(val[2]))
1312
- }
1313
- | f_marg_list tCOMMA tSTAR tCOMMA f_marg_list
1314
- {
1315
- result = val[0].
1316
- push(@builder.restarg(val[2])).
1322
+ push(val[2]).
1317
1323
  concat(val[4])
1318
1324
  }
1319
- | tSTAR f_norm_arg
1325
+ | f_rest_marg
1320
1326
  {
1321
- result = [ @builder.restarg(val[0], val[1]) ]
1327
+ result = [ val[0] ]
1322
1328
  }
1323
- | tSTAR f_norm_arg tCOMMA f_marg_list
1329
+ | f_rest_marg tCOMMA f_marg_list
1324
1330
  {
1325
- result = [ @builder.restarg(val[0], val[1]),
1326
- *val[3] ]
1331
+ result = [ val[0], *val[2] ]
1327
1332
  }
1328
- | tSTAR
1333
+
1334
+ f_rest_marg: tSTAR f_norm_arg
1329
1335
  {
1330
- result = [ @builder.restarg(val[0]) ]
1336
+ result = @builder.restarg(val[0], val[1])
1331
1337
  }
1332
- | tSTAR tCOMMA f_marg_list
1338
+ | tSTAR
1333
1339
  {
1334
- result = [ @builder.restarg(val[0]),
1335
- *val[2] ]
1340
+ result = @builder.restarg(val[0])
1336
1341
  }
1337
1342
 
1338
1343
  block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
@@ -1460,14 +1465,17 @@ opt_block_args_tail:
1460
1465
 
1461
1466
  block_param_def: tPIPE opt_bv_decl tPIPE
1462
1467
  {
1468
+ @lexer.max_numparam_stack.cant_have_numparams!
1463
1469
  result = @builder.args(val[0], val[1], val[2])
1464
1470
  }
1465
1471
  | tOROP
1466
1472
  {
1473
+ @lexer.max_numparam_stack.cant_have_numparams!
1467
1474
  result = @builder.args(val[0], [], val[0])
1468
1475
  }
1469
1476
  | tPIPE block_param opt_bv_decl tPIPE
1470
1477
  {
1478
+ @lexer.max_numparam_stack.cant_have_numparams!
1471
1479
  result = @builder.args(val[0], val[1].concat(val[2]), val[3])
1472
1480
  }
1473
1481
 
@@ -1498,26 +1506,34 @@ opt_block_args_tail:
1498
1506
 
1499
1507
  lambda: {
1500
1508
  @static_env.extend_dynamic
1509
+ @lexer.max_numparam_stack.push
1510
+ @context.push(:lambda)
1501
1511
  }
1502
1512
  f_larglist
1503
1513
  {
1514
+ @context.pop
1504
1515
  @lexer.cmdarg.push(false)
1505
1516
  }
1506
1517
  lambda_body
1507
1518
  {
1508
- @lexer.cmdarg.pop
1509
-
1510
- result = [ val[1], val[3] ]
1519
+ args = @lexer.max_numparam > 0 ? @builder.numargs(@lexer.max_numparam) : val[1]
1520
+ result = [ args, val[3] ]
1511
1521
 
1522
+ @lexer.max_numparam_stack.pop
1512
1523
  @static_env.unextend
1524
+ @lexer.cmdarg.pop
1513
1525
  }
1514
1526
 
1515
1527
  f_larglist: tLPAREN2 f_args opt_bv_decl tRPAREN
1516
1528
  {
1529
+ @lexer.max_numparam_stack.cant_have_numparams!
1517
1530
  result = @builder.args(val[0], val[1].concat(val[2]), val[3])
1518
1531
  }
1519
1532
  | f_args
1520
1533
  {
1534
+ if val[0].any?
1535
+ @lexer.max_numparam_stack.cant_have_numparams!
1536
+ end
1521
1537
  result = @builder.args(nil, val[0], nil)
1522
1538
  }
1523
1539
 
@@ -1652,24 +1668,30 @@ opt_block_args_tail:
1652
1668
 
1653
1669
  brace_body: {
1654
1670
  @static_env.extend_dynamic
1671
+ @lexer.max_numparam_stack.push
1655
1672
  }
1656
1673
  opt_block_param compstmt
1657
1674
  {
1658
- result = [ val[1], val[2] ]
1675
+ args = @lexer.max_numparam > 0 ? @builder.numargs(@lexer.max_numparam) : val[1]
1676
+ result = [ args, val[2] ]
1659
1677
 
1678
+ @lexer.max_numparam_stack.pop
1660
1679
  @static_env.unextend
1661
1680
  }
1662
1681
 
1663
1682
  do_body: {
1664
1683
  @static_env.extend_dynamic
1684
+ @lexer.max_numparam_stack.push
1665
1685
  }
1666
1686
  {
1667
1687
  @lexer.cmdarg.push(false)
1668
1688
  }
1669
1689
  opt_block_param bodystmt
1670
1690
  {
1671
- result = [ val[2], val[3] ]
1691
+ args = @lexer.max_numparam > 0 ? @builder.numargs(@lexer.max_numparam) : val[2]
1692
+ result = [ args, val[3] ]
1672
1693
 
1694
+ @lexer.max_numparam_stack.pop
1673
1695
  @static_env.unextend
1674
1696
  @lexer.cmdarg.pop
1675
1697
  }
@@ -1892,6 +1914,10 @@ regexp_contents: # nothing
1892
1914
  {
1893
1915
  result = @builder.cvar(val[0])
1894
1916
  }
1917
+ | tNUMPARAM
1918
+ {
1919
+ result = @builder.numparam(val[0])
1920
+ }
1895
1921
  | backref
1896
1922
 
1897
1923
  symbol: ssym
@@ -1964,6 +1990,10 @@ regexp_contents: # nothing
1964
1990
  {
1965
1991
  result = @builder.cvar(val[0])
1966
1992
  }
1993
+ | tNUMPARAM
1994
+ {
1995
+ result = @builder.numparam(val[0])
1996
+ }
1967
1997
 
1968
1998
  keyword_variable: kNIL
1969
1999
  {
@@ -2188,6 +2218,8 @@ keyword_variable: kNIL
2188
2218
  {
2189
2219
  @static_env.declare val[0][0]
2190
2220
 
2221
+ @lexer.max_numparam_stack.cant_have_numparams!
2222
+
2191
2223
  result = val[0]
2192
2224
  }
2193
2225
 
@@ -2220,6 +2252,8 @@ keyword_variable: kNIL
2220
2252
 
2221
2253
  @static_env.declare val[0][0]
2222
2254
 
2255
+ @lexer.max_numparam_stack.cant_have_numparams!
2256
+
2223
2257
  result = val[0]
2224
2258
  }
2225
2259
 
@@ -123,7 +123,7 @@ module Parser
123
123
  opts.on '--emit-ruby', 'Emit S-expressions as valid Ruby code' do
124
124
  @emit_ruby = true
125
125
  end
126
-
126
+
127
127
  opts.on '--emit-json', 'Emit S-expressions as valid JSON' do
128
128
  @emit_json = true
129
129
  end
@@ -146,7 +146,7 @@ module Parser
146
146
  if @emit_ruby
147
147
  puts ast.inspect
148
148
  elsif @emit_json
149
- puts JSON.generate(ast.to_sexp_array)
149
+ puts(ast ? JSON.generate(ast.to_sexp_array) : nil)
150
150
  else
151
151
  puts ast.to_s
152
152
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parser
4
- VERSION = '2.6.3.0'
4
+ VERSION = '2.6.4.0'
5
5
  end
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
38
38
 
39
39
  spec.add_development_dependency 'bundler', '>= 1.15', '< 3.0.0'
40
40
  spec.add_development_dependency 'rake', '~> 10.0'
41
- spec.add_development_dependency 'racc', '= 1.4.14'
41
+ spec.add_development_dependency 'racc', '= 1.4.15'
42
42
  spec.add_development_dependency 'cliver', '~> 0.3.2'
43
43
 
44
44
  spec.add_development_dependency 'yard'
@@ -162,6 +162,24 @@ class TestLexer < Minitest::Test
162
162
  refute_escape 'u{123 f0'
163
163
  end
164
164
 
165
+ def test_read_escape_whitespaces__27
166
+ setup_lexer 27
167
+
168
+ [ *(0..8), *(14..31) ].each do |code|
169
+ @lex.reset
170
+ refute_scanned "\"\\C-" + code.chr + "\""
171
+
172
+ @lex.reset
173
+ refute_scanned "\"\\M-" + code.chr + "\""
174
+
175
+ @lex.reset
176
+ refute_scanned "\"\\C-\\M-" + code.chr + "\""
177
+
178
+ @lex.reset
179
+ refute_scanned "\"\\M-\\C-" + code.chr + "\""
180
+ end
181
+ end
182
+
165
183
  def test_ambiguous_uminus
166
184
  assert_scanned("m -3",
167
185
  :tIDENTIFIER, "m", [0, 1],
@@ -3581,4 +3599,61 @@ class TestLexer < Minitest::Test
3581
3599
  :tPLUS, '+', [6, 7])
3582
3600
  end
3583
3601
 
3602
+ def lex_numbered_parameter(input)
3603
+ @lex.max_numparam_stack.push
3604
+
3605
+ @lex.context = Parser::Context.new
3606
+ @lex.context.push(:block)
3607
+
3608
+ source_buffer = Parser::Source::Buffer.new('(assert_lex_numbered_parameter)')
3609
+ source_buffer.source = input
3610
+
3611
+ @lex.source_buffer = source_buffer
3612
+
3613
+ @lex.advance
3614
+ end
3615
+
3616
+ def assert_scanned_numbered_parameter(input)
3617
+ lex_token, (lex_value, lex_range) = lex_numbered_parameter(input)
3618
+
3619
+ assert_equal(lex_token, :tNUMPARAM)
3620
+ assert_equal(lex_value, input.tr('@', ''))
3621
+ assert_equal(lex_range.begin_pos, 0)
3622
+ assert_equal(lex_range.end_pos, input.length)
3623
+ end
3624
+
3625
+ def refute_scanned_numbered_parameter(input, message = nil)
3626
+ err = assert_raises Parser::SyntaxError do
3627
+ lex_token, (lex_value, lex_range) = lex_numbered_parameter(input)
3628
+ end
3629
+
3630
+ if message
3631
+ assert_equal(err.message, Parser::MESSAGES[message])
3632
+
3633
+ assert_equal(err.diagnostic.location.begin_pos, 0)
3634
+ assert_equal(err.diagnostic.location.end_pos, input.length)
3635
+ end
3636
+ end
3637
+
3638
+ def test_numbered_args_before_27
3639
+ setup_lexer(26)
3640
+ refute_scanned_numbered_parameter('@1')
3641
+ end
3642
+
3643
+ def test_numbered_args_27
3644
+ setup_lexer(27)
3645
+ assert_scanned_numbered_parameter('@1')
3646
+ assert_equal(@lex.max_numparam, 1)
3647
+
3648
+ setup_lexer(27)
3649
+ assert_scanned_numbered_parameter('@100')
3650
+ assert_equal(@lex.max_numparam, 100)
3651
+
3652
+ setup_lexer(27)
3653
+ refute_scanned_numbered_parameter('@101', :too_large_numparam)
3654
+
3655
+ setup_lexer(27)
3656
+ refute_scanned_numbered_parameter('@01', :leading_zero_in_numparam)
3657
+ end
3658
+
3584
3659
  end