parser 2.6.5.0 → 2.7.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +7 -0
- data/doc/AST_FORMAT.md +346 -20
- data/lib/parser.rb +3 -1
- data/lib/parser/ast/processor.rb +15 -0
- data/lib/parser/base.rb +19 -0
- data/lib/parser/builders/default.rb +245 -12
- data/lib/parser/context.rb +4 -0
- data/lib/parser/current.rb +4 -4
- data/lib/parser/current_arg_stack.rb +43 -0
- data/lib/parser/lexer.rl +93 -94
- data/lib/parser/lexer/dedenter.rb +48 -49
- data/lib/parser/{lexer/max_numparam_stack.rb → max_numparam_stack.rb} +10 -4
- data/lib/parser/messages.rb +34 -29
- data/lib/parser/meta.rb +6 -2
- data/lib/parser/ruby27.y +488 -35
- data/lib/parser/static_environment.rb +10 -0
- data/lib/parser/variables_stack.rb +32 -0
- data/lib/parser/version.rb +1 -1
- data/test/helper.rb +1 -0
- data/test/test_lexer.rb +7 -66
- data/test/test_parser.rb +1776 -123
- metadata +5 -3
data/lib/parser.rb
CHANGED
@@ -67,13 +67,15 @@ module Parser
|
|
67
67
|
require 'parser/lexer/literal'
|
68
68
|
require 'parser/lexer/stack_state'
|
69
69
|
require 'parser/lexer/dedenter'
|
70
|
-
require 'parser/lexer/max_numparam_stack'
|
71
70
|
|
72
71
|
module Builders
|
73
72
|
require 'parser/builders/default'
|
74
73
|
end
|
75
74
|
|
76
75
|
require 'parser/context'
|
76
|
+
require 'parser/max_numparam_stack'
|
77
|
+
require 'parser/current_arg_stack'
|
78
|
+
require 'parser/variables_stack'
|
77
79
|
|
78
80
|
require 'parser/base'
|
79
81
|
|
data/lib/parser/ast/processor.rb
CHANGED
@@ -236,6 +236,21 @@ module Parser
|
|
236
236
|
alias on_preexe process_regular_node
|
237
237
|
alias on_postexe process_regular_node
|
238
238
|
|
239
|
+
alias on_case_match process_regular_node
|
240
|
+
alias on_in_match process_regular_node
|
241
|
+
alias on_in_pattern process_regular_node
|
242
|
+
alias on_if_guard process_regular_node
|
243
|
+
alias on_unless_guard process_regular_node
|
244
|
+
alias on_match_var process_variable_node
|
245
|
+
alias on_match_rest process_regular_node
|
246
|
+
alias on_pin process_regular_node
|
247
|
+
alias on_match_alt process_regular_node
|
248
|
+
alias on_match_as process_regular_node
|
249
|
+
alias on_array_pattern process_regular_node
|
250
|
+
alias on_array_pattern_with_tail process_regular_node
|
251
|
+
alias on_hash_pattern process_regular_node
|
252
|
+
alias on_const_pattern process_regular_node
|
253
|
+
|
239
254
|
# @private
|
240
255
|
def process_variable_node(node)
|
241
256
|
warn 'Parser::AST::Processor#process_variable_node is deprecated as a' \
|
data/lib/parser/base.rb
CHANGED
@@ -114,6 +114,10 @@ module Parser
|
|
114
114
|
attr_reader :static_env
|
115
115
|
attr_reader :source_buffer
|
116
116
|
attr_reader :context
|
117
|
+
attr_reader :max_numparam_stack
|
118
|
+
attr_reader :current_arg_stack
|
119
|
+
attr_reader :pattern_variables
|
120
|
+
attr_reader :pattern_hash_keys
|
117
121
|
|
118
122
|
##
|
119
123
|
# @param [Parser::Builders::Default] builder The AST builder to use.
|
@@ -126,6 +130,18 @@ module Parser
|
|
126
130
|
# Stack that holds current parsing context
|
127
131
|
@context = Context.new
|
128
132
|
|
133
|
+
# Maximum numbered parameters stack
|
134
|
+
@max_numparam_stack = MaxNumparamStack.new
|
135
|
+
|
136
|
+
# Current argument names stack
|
137
|
+
@current_arg_stack = CurrentArgStack.new
|
138
|
+
|
139
|
+
# Stack of set of variables used in the current pattern
|
140
|
+
@pattern_variables = VariablesStack.new
|
141
|
+
|
142
|
+
# Stack of set of keys used in the current hash in pattern matchinig
|
143
|
+
@pattern_hash_keys = VariablesStack.new
|
144
|
+
|
129
145
|
@lexer = Lexer.new(version)
|
130
146
|
@lexer.diagnostics = @diagnostics
|
131
147
|
@lexer.static_env = @static_env
|
@@ -153,6 +169,9 @@ module Parser
|
|
153
169
|
@lexer.reset
|
154
170
|
@static_env.reset
|
155
171
|
@context.reset
|
172
|
+
@current_arg_stack.reset
|
173
|
+
@pattern_variables.reset
|
174
|
+
@pattern_hash_keys.reset
|
156
175
|
|
157
176
|
self
|
158
177
|
end
|
@@ -453,11 +453,6 @@ module Parser
|
|
453
453
|
variable_map(token))
|
454
454
|
end
|
455
455
|
|
456
|
-
def numparam(token)
|
457
|
-
n(:numparam, [ value(token).to_i ],
|
458
|
-
variable_map(token))
|
459
|
-
end
|
460
|
-
|
461
456
|
def back_ref(token)
|
462
457
|
n(:back_ref, [ value(token).to_sym ],
|
463
458
|
token_map(token))
|
@@ -498,6 +493,11 @@ module Parser
|
|
498
493
|
name, = *node
|
499
494
|
|
500
495
|
if @parser.static_env.declared?(name)
|
496
|
+
if name.to_s == parser.current_arg_stack.top
|
497
|
+
diagnostic :error, :circular_argument_reference,
|
498
|
+
{ :var_name => name.to_s }, node.loc.expression
|
499
|
+
end
|
500
|
+
|
501
501
|
node.updated(:lvar)
|
502
502
|
else
|
503
503
|
name, = *node
|
@@ -556,6 +556,9 @@ module Parser
|
|
556
556
|
|
557
557
|
when :ident
|
558
558
|
name, = *node
|
559
|
+
|
560
|
+
check_assignment_to_numparam(node)
|
561
|
+
|
559
562
|
@parser.static_env.declare(name)
|
560
563
|
|
561
564
|
node.updated(:lvasgn)
|
@@ -688,6 +691,10 @@ module Parser
|
|
688
691
|
n(:numargs, [ max_numparam ], nil)
|
689
692
|
end
|
690
693
|
|
694
|
+
def forward_args(begin_t, dots_t, end_t)
|
695
|
+
n(:forward_args, [], collection_map(begin_t, token_map(dots_t), end_t))
|
696
|
+
end
|
697
|
+
|
691
698
|
def arg(name_t)
|
692
699
|
n(:arg, [ value(name_t).to_sym ],
|
693
700
|
variable_map(name_t))
|
@@ -837,6 +844,10 @@ module Parser
|
|
837
844
|
end
|
838
845
|
end
|
839
846
|
|
847
|
+
def forwarded_args(dots_t)
|
848
|
+
n(:forwarded_args, [], token_map(dots_t))
|
849
|
+
end
|
850
|
+
|
840
851
|
def call_method(receiver, dot_t, selector_t,
|
841
852
|
lparen_t=nil, args=[], rparen_t=nil)
|
842
853
|
type = call_type_for_dot(dot_t)
|
@@ -866,11 +877,10 @@ module Parser
|
|
866
877
|
end
|
867
878
|
|
868
879
|
last_arg = call_args.last
|
869
|
-
if last_arg && last_arg.type == :block_pass
|
880
|
+
if last_arg && (last_arg.type == :block_pass || last_arg.type == :forwarded_args)
|
870
881
|
diagnostic :error, :block_and_blockarg, nil, last_arg.loc.expression, [loc(begin_t)]
|
871
882
|
end
|
872
883
|
|
873
|
-
|
874
884
|
if args.type == :numargs
|
875
885
|
block_type = :numblock
|
876
886
|
args = args.children[0]
|
@@ -1006,11 +1016,6 @@ module Parser
|
|
1006
1016
|
end
|
1007
1017
|
end
|
1008
1018
|
|
1009
|
-
def method_ref(receiver, dot_t, selector_t)
|
1010
|
-
n(:meth_ref, [ receiver, value(selector_t).to_sym ],
|
1011
|
-
send_map(receiver, dot_t, selector_t, nil, [], nil))
|
1012
|
-
end
|
1013
|
-
|
1014
1019
|
#
|
1015
1020
|
# Control flow
|
1016
1021
|
#
|
@@ -1203,6 +1208,188 @@ module Parser
|
|
1203
1208
|
end
|
1204
1209
|
end
|
1205
1210
|
|
1211
|
+
#
|
1212
|
+
# PATTERN MATCHING
|
1213
|
+
#
|
1214
|
+
|
1215
|
+
def case_match(case_t, expr, in_bodies, else_t, else_body, end_t)
|
1216
|
+
n(:case_match, [ expr, *(in_bodies << else_body)],
|
1217
|
+
condition_map(case_t, expr, nil, nil, else_t, else_body, end_t))
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
def in_match(lhs, in_t, rhs)
|
1221
|
+
n(:in_match, [lhs, rhs],
|
1222
|
+
binary_op_map(lhs, in_t, rhs))
|
1223
|
+
end
|
1224
|
+
|
1225
|
+
def in_pattern(in_t, pattern, guard, then_t, body)
|
1226
|
+
children = [pattern, guard, body]
|
1227
|
+
n(:in_pattern, children,
|
1228
|
+
keyword_map(in_t, then_t, children.compact, nil))
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
def if_guard(if_t, if_body)
|
1232
|
+
n(:if_guard, [if_body], guard_map(if_t, if_body))
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
def unless_guard(unless_t, unless_body)
|
1236
|
+
n(:unless_guard, [unless_body], guard_map(unless_t, unless_body))
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
def match_var(name_t)
|
1240
|
+
name = value(name_t).to_sym
|
1241
|
+
|
1242
|
+
check_duplicate_pattern_variable(name, loc(name_t))
|
1243
|
+
@parser.static_env.declare(name)
|
1244
|
+
|
1245
|
+
n(:match_var, [ name ],
|
1246
|
+
variable_map(name_t))
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
def match_hash_var(name_t)
|
1250
|
+
name = value(name_t).to_sym
|
1251
|
+
|
1252
|
+
expr_l = loc(name_t)
|
1253
|
+
name_l = expr_l.adjust(end_pos: -1)
|
1254
|
+
|
1255
|
+
check_duplicate_pattern_variable(name, name_l)
|
1256
|
+
@parser.static_env.declare(name)
|
1257
|
+
|
1258
|
+
n(:match_var, [ name ],
|
1259
|
+
Source::Map::Variable.new(name_l, expr_l))
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
def match_hash_var_from_str(begin_t, strings, end_t)
|
1263
|
+
if strings.length > 1
|
1264
|
+
diagnostic :error, :pm_interp_in_var_name, nil, loc(begin_t).join(loc(end_t))
|
1265
|
+
end
|
1266
|
+
|
1267
|
+
string = strings[0]
|
1268
|
+
|
1269
|
+
case string.type
|
1270
|
+
when :str
|
1271
|
+
# MRI supports plain strings in hash pattern matching
|
1272
|
+
name, = *string
|
1273
|
+
name_l = string.loc.expression
|
1274
|
+
|
1275
|
+
check_lvar_name(name, name_l)
|
1276
|
+
check_duplicate_pattern_variable(name, name_l)
|
1277
|
+
|
1278
|
+
@parser.static_env.declare(name)
|
1279
|
+
|
1280
|
+
if (begin_l = string.loc.begin)
|
1281
|
+
# exclude beginning of the string from the location of the variable
|
1282
|
+
name_l = name_l.adjust(begin_pos: begin_l.length)
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
if (end_l = string.loc.end)
|
1286
|
+
# exclude end of the string from the location of the variable
|
1287
|
+
name_l = name_l.adjust(end_pos: -end_l.length)
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
expr_l = loc(begin_t).join(string.loc.expression).join(loc(end_t))
|
1291
|
+
n(:match_var, [ name.to_sym ],
|
1292
|
+
Source::Map::Variable.new(name_l, expr_l))
|
1293
|
+
when :begin
|
1294
|
+
match_hash_var_from_str(begin_t, string.children, end_t)
|
1295
|
+
end
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
def match_rest(star_t, name_t = nil)
|
1299
|
+
if name_t.nil?
|
1300
|
+
n0(:match_rest,
|
1301
|
+
unary_op_map(star_t))
|
1302
|
+
else
|
1303
|
+
name = match_var(name_t)
|
1304
|
+
n(:match_rest, [ name ],
|
1305
|
+
unary_op_map(star_t, name))
|
1306
|
+
end
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
def hash_pattern(lbrace_t, kwargs, rbrace_t)
|
1310
|
+
args = check_duplicate_args(kwargs)
|
1311
|
+
n(:hash_pattern, args,
|
1312
|
+
collection_map(lbrace_t, args, rbrace_t))
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
def array_pattern(lbrack_t, elements, rbrack_t)
|
1316
|
+
trailing_comma = false
|
1317
|
+
|
1318
|
+
elements = elements.map do |element|
|
1319
|
+
if element.type == :match_with_trailing_comma
|
1320
|
+
trailing_comma = true
|
1321
|
+
element.children.first
|
1322
|
+
else
|
1323
|
+
trailing_comma = false
|
1324
|
+
element
|
1325
|
+
end
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
node_type = trailing_comma ? :array_pattern_with_tail : :array_pattern
|
1329
|
+
n(node_type, elements,
|
1330
|
+
collection_map(lbrack_t, elements, rbrack_t))
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
def match_with_trailing_comma(match)
|
1334
|
+
n(:match_with_trailing_comma, [ match ], nil)
|
1335
|
+
end
|
1336
|
+
|
1337
|
+
def const_pattern(const, ldelim_t, pattern, rdelim_t)
|
1338
|
+
n(:const_pattern, [const, pattern],
|
1339
|
+
collection_map(ldelim_t, [pattern], rdelim_t))
|
1340
|
+
end
|
1341
|
+
|
1342
|
+
def pin(pin_t, var)
|
1343
|
+
n(:pin, [ var ],
|
1344
|
+
send_unary_op_map(pin_t, var))
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
def match_alt(left, pipe_t, right)
|
1348
|
+
source_map = binary_op_map(left, pipe_t, right)
|
1349
|
+
|
1350
|
+
n(:match_alt, [ left, right ],
|
1351
|
+
source_map)
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
def match_as(value, assoc_t, as)
|
1355
|
+
source_map = binary_op_map(value, assoc_t, as)
|
1356
|
+
|
1357
|
+
n(:match_as, [ value, as ],
|
1358
|
+
source_map)
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
def match_nil_pattern(dstar_t, nil_t)
|
1362
|
+
n0(:match_nil_pattern,
|
1363
|
+
arg_prefix_map(dstar_t, nil_t))
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
def match_pair(label_type, label, value)
|
1367
|
+
if label_type == :label
|
1368
|
+
check_duplicate_pattern_key(label[0], label[1])
|
1369
|
+
pair_keyword(label, value)
|
1370
|
+
else
|
1371
|
+
begin_t, parts, end_t = label
|
1372
|
+
|
1373
|
+
# quoted label like "label": value
|
1374
|
+
if (var_name = static_string(parts))
|
1375
|
+
loc = loc(begin_t).join(loc(end_t))
|
1376
|
+
check_duplicate_pattern_key(var_name, loc)
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
pair_quoted(begin_t, parts, end_t, value)
|
1380
|
+
end
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
def match_label(label_type, label)
|
1384
|
+
if label_type == :label
|
1385
|
+
match_hash_var(label)
|
1386
|
+
else
|
1387
|
+
# quoted label like "label": value
|
1388
|
+
begin_t, strings, end_t = label
|
1389
|
+
match_hash_var_from_str(begin_t, strings, end_t)
|
1390
|
+
end
|
1391
|
+
end
|
1392
|
+
|
1206
1393
|
private
|
1207
1394
|
|
1208
1395
|
#
|
@@ -1292,6 +1479,19 @@ module Parser
|
|
1292
1479
|
end
|
1293
1480
|
end
|
1294
1481
|
|
1482
|
+
def check_assignment_to_numparam(node)
|
1483
|
+
name = node.children[0].to_s
|
1484
|
+
|
1485
|
+
assigning_to_numparam =
|
1486
|
+
@parser.context.in_dynamic_block? &&
|
1487
|
+
name =~ /\A_([1-9])\z/ &&
|
1488
|
+
@parser.max_numparam_stack.has_numparams?
|
1489
|
+
|
1490
|
+
if assigning_to_numparam
|
1491
|
+
diagnostic :error, :cant_assign_to_numparam, { :name => name }, node.loc.expression
|
1492
|
+
end
|
1493
|
+
end
|
1494
|
+
|
1295
1495
|
def arg_name_collides?(this_name, that_name)
|
1296
1496
|
case @parser.version
|
1297
1497
|
when 18
|
@@ -1307,6 +1507,32 @@ module Parser
|
|
1307
1507
|
end
|
1308
1508
|
end
|
1309
1509
|
|
1510
|
+
def check_lvar_name(name, loc)
|
1511
|
+
if name =~ /\A[[[:lower:]]|_][[[:alnum:]]_]*\z/
|
1512
|
+
# OK
|
1513
|
+
else
|
1514
|
+
diagnostic :error, :lvar_name, { name: name }, loc
|
1515
|
+
end
|
1516
|
+
end
|
1517
|
+
|
1518
|
+
def check_duplicate_pattern_variable(name, loc)
|
1519
|
+
return if name.to_s.start_with?('_')
|
1520
|
+
|
1521
|
+
if @parser.pattern_variables.declared?(name)
|
1522
|
+
diagnostic :error, :duplicate_variable_name, { name: name.to_s }, loc
|
1523
|
+
end
|
1524
|
+
|
1525
|
+
@parser.pattern_variables.declare(name)
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
def check_duplicate_pattern_key(name, loc)
|
1529
|
+
if @parser.pattern_hash_keys.declared?(name)
|
1530
|
+
diagnostic :error, :duplicate_pattern_key, { name: name.to_s }, loc
|
1531
|
+
end
|
1532
|
+
|
1533
|
+
@parser.pattern_hash_keys.declare(name)
|
1534
|
+
end
|
1535
|
+
|
1310
1536
|
#
|
1311
1537
|
# SOURCE MAPS
|
1312
1538
|
#
|
@@ -1655,6 +1881,13 @@ module Parser
|
|
1655
1881
|
begin_l.join(end_l))
|
1656
1882
|
end
|
1657
1883
|
|
1884
|
+
def guard_map(keyword_t, guard_body_e)
|
1885
|
+
keyword_l = loc(keyword_t)
|
1886
|
+
guard_body_l = guard_body_e.loc.expression
|
1887
|
+
|
1888
|
+
Source::Map::Keyword.new(keyword_l, nil, nil, keyword_l.join(guard_body_l))
|
1889
|
+
end
|
1890
|
+
|
1658
1891
|
#
|
1659
1892
|
# HELPERS
|
1660
1893
|
#
|
data/lib/parser/context.rb
CHANGED
data/lib/parser/current.rb
CHANGED
@@ -75,7 +75,7 @@ module Parser
|
|
75
75
|
CurrentRuby = Ruby26
|
76
76
|
|
77
77
|
when /^2\.7\./
|
78
|
-
current_version = '2.7.0
|
78
|
+
current_version = '2.7.0'
|
79
79
|
if RUBY_VERSION != current_version
|
80
80
|
warn_syntax_deviation 'parser/ruby27', current_version
|
81
81
|
end
|
@@ -85,8 +85,8 @@ module Parser
|
|
85
85
|
|
86
86
|
else # :nocov:
|
87
87
|
# Keep this in sync with released Ruby.
|
88
|
-
warn_syntax_deviation 'parser/
|
89
|
-
require 'parser/
|
90
|
-
CurrentRuby =
|
88
|
+
warn_syntax_deviation 'parser/ruby27', '2.7.x'
|
89
|
+
require 'parser/ruby27'
|
90
|
+
CurrentRuby = Ruby27
|
91
91
|
end
|
92
92
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Parser
|
4
|
+
# Stack that holds names of current arguments,
|
5
|
+
# i.e. while parsing
|
6
|
+
# def m1(a = (def m2(b = def m3(c = 1); end); end)); end
|
7
|
+
# ^
|
8
|
+
# stack is [:a, :b, :c]
|
9
|
+
#
|
10
|
+
# Emulates `p->cur_arg` in MRI's parse.y
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
class CurrentArgStack
|
15
|
+
attr_reader :stack
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@stack = []
|
19
|
+
freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
def push(value)
|
23
|
+
@stack << value
|
24
|
+
end
|
25
|
+
|
26
|
+
def set(value)
|
27
|
+
pop
|
28
|
+
push(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def pop
|
32
|
+
@stack.pop
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset
|
36
|
+
@stack.clear
|
37
|
+
end
|
38
|
+
|
39
|
+
def top
|
40
|
+
@stack.last
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|