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.
- checksums.yaml +4 -4
- data/.travis.yml +10 -10
- data/CHANGELOG.md +24 -2
- data/doc/AST_FORMAT.md +45 -3
- data/lib/parser.rb +1 -0
- data/lib/parser/ast/processor.rb +24 -1
- data/lib/parser/builders/default.rb +64 -12
- data/lib/parser/context.rb +8 -0
- data/lib/parser/current.rb +3 -3
- data/lib/parser/lexer.rl +84 -3
- data/lib/parser/lexer/max_numparam_stack.rb +42 -0
- data/lib/parser/messages.rb +26 -20
- data/lib/parser/meta.rb +1 -0
- data/lib/parser/ruby25.y +1 -1
- data/lib/parser/ruby27.y +65 -31
- data/lib/parser/runner/ruby_parse.rb +2 -2
- data/lib/parser/version.rb +1 -1
- data/parser.gemspec +1 -1
- data/test/test_lexer.rb +75 -0
- data/test/test_parser.rb +382 -3
- data/test/test_runner_parse.rb +35 -0
- metadata +7 -4
@@ -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
|
data/lib/parser/messages.rb
CHANGED
@@ -10,26 +10,31 @@ module Parser
|
|
10
10
|
#
|
11
11
|
MESSAGES = {
|
12
12
|
# Lexer errors
|
13
|
-
:unicode_point_too_large
|
14
|
-
:invalid_escape
|
15
|
-
:incomplete_escape
|
16
|
-
:invalid_hex_escape
|
17
|
-
:invalid_unicode_escape
|
18
|
-
:unterminated_unicode
|
19
|
-
:escape_eof
|
20
|
-
:string_eof
|
21
|
-
:regexp_options
|
22
|
-
:cvar_name
|
23
|
-
:ivar_name
|
24
|
-
:trailing_in_number
|
25
|
-
:empty_numeric
|
26
|
-
:invalid_octal
|
27
|
-
:no_dot_digit_literal
|
28
|
-
:bare_backslash
|
29
|
-
:unexpected
|
30
|
-
:embedded_document
|
31
|
-
:heredoc_id_has_newline
|
32
|
-
:heredoc_id_ends_with_nl
|
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',
|
data/lib/parser/meta.rb
CHANGED
data/lib/parser/ruby25.y
CHANGED
@@ -1885,7 +1885,7 @@ regexp_contents: # nothing
|
|
1885
1885
|
result = @builder.symbol(val[0])
|
1886
1886
|
}
|
1887
1887
|
|
1888
|
-
dsym: tSYMBEG
|
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])
|
data/lib/parser/ruby27.y
CHANGED
@@ -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
|
1314
|
+
| f_marg_list tCOMMA f_rest_marg
|
1298
1315
|
{
|
1299
1316
|
result = val[0].
|
1300
|
-
push(
|
1317
|
+
push(val[2])
|
1301
1318
|
}
|
1302
|
-
| f_marg_list tCOMMA
|
1319
|
+
| f_marg_list tCOMMA f_rest_marg tCOMMA f_marg_list
|
1303
1320
|
{
|
1304
1321
|
result = val[0].
|
1305
|
-
push(
|
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
|
-
|
|
1325
|
+
| f_rest_marg
|
1320
1326
|
{
|
1321
|
-
result = [
|
1327
|
+
result = [ val[0] ]
|
1322
1328
|
}
|
1323
|
-
|
|
1329
|
+
| f_rest_marg tCOMMA f_marg_list
|
1324
1330
|
{
|
1325
|
-
result = [
|
1326
|
-
*val[3] ]
|
1331
|
+
result = [ val[0], *val[2] ]
|
1327
1332
|
}
|
1328
|
-
|
1333
|
+
|
1334
|
+
f_rest_marg: tSTAR f_norm_arg
|
1329
1335
|
{
|
1330
|
-
result =
|
1336
|
+
result = @builder.restarg(val[0], val[1])
|
1331
1337
|
}
|
1332
|
-
|
|
1338
|
+
| tSTAR
|
1333
1339
|
{
|
1334
|
-
result =
|
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.
|
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
|
-
|
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
|
-
|
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
|
data/lib/parser/version.rb
CHANGED
data/parser.gemspec
CHANGED
@@ -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.
|
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'
|
data/test/test_lexer.rb
CHANGED
@@ -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
|