parser 2.6.3.0 → 2.6.4.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.
@@ -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