@@ -256,11 +256,37 @@ def find_token(type)
256
256
tokens [ index ] if index
257
257
end
258
258
259
+ def find_token_between ( type , left , right )
260
+ bounds = left . location . end_char ...right . location . start_char
261
+ index =
262
+ tokens . rindex do |token |
263
+ char = token . location . start_char
264
+ break if char < bounds . begin
265
+
266
+ token . is_a? ( type ) && bounds . cover? ( char )
267
+ end
268
+
269
+ tokens [ index ] if index
270
+ end
271
+
259
272
def find_keyword ( name )
260
273
index = tokens . rindex { |token | token . is_a? ( Kw ) && ( token . name == name ) }
261
274
tokens [ index ] if index
262
275
end
263
276
277
+ def find_keyword_between ( name , left , right )
278
+ bounds = left . end_char ...right . start_char
279
+ index =
280
+ tokens . rindex do |token |
281
+ char = token . location . start_char
282
+ break if char < bounds . begin
283
+
284
+ token . is_a? ( Kw ) && ( token . name == name ) && bounds . cover? ( char )
285
+ end
286
+
287
+ tokens [ index ] if index
288
+ end
289
+
264
290
def find_operator ( name )
265
291
index = tokens . rindex { |token | token . is_a? ( Op ) && ( token . name == name ) }
266
292
tokens [ index ] if index
@@ -645,7 +671,7 @@ def visit_var_ref(node)
645
671
end
646
672
647
673
def self . visit ( node , tokens )
648
- start_char = node . location . start_char
674
+ start_char = node . start_char
649
675
allocated = [ ]
650
676
651
677
tokens . reverse_each do |token |
@@ -874,13 +900,34 @@ def on_binary(left, operator, right)
874
900
# on_block_var: (Params params, (nil | Array[Ident]) locals) -> BlockVar
875
901
def on_block_var ( params , locals )
876
902
index =
877
- tokens . rindex do |node |
878
- node . is_a? ( Op ) && %w[ | || ] . include? ( node . value ) &&
879
- node . location . start_char < params . location . start_char
880
- end
903
+ tokens . rindex { |node | node . is_a? ( Op ) && %w[ | || ] . include? ( node . value ) }
904
+
905
+ ending = tokens . delete_at ( index )
906
+ beginning = ending . value == "||" ? ending : consume_operator ( :| )
907
+
908
+ # If there are no parameters, then we didn't have anything to base the
909
+ # location information of off. Now that we have an opening of the
910
+ # block, we can correct this.
911
+ if params . empty?
912
+ start_line = params . location . start_line
913
+ start_char =
914
+ (
915
+ if beginning . value == "||"
916
+ beginning . location . start_char
917
+ else
918
+ find_next_statement_start ( beginning . location . end_char )
919
+ end
920
+ )
881
921
882
- beginning = tokens [ index ]
883
- ending = tokens [ -1 ]
922
+ location =
923
+ Location . fixed (
924
+ line : start_line ,
925
+ char : start_char ,
926
+ column : start_char - line_counts [ start_line - 1 ] . start
927
+ )
928
+
929
+ params = params . copy ( location : location )
930
+ end
884
931
885
932
BlockVar . new (
886
933
params : params ,
@@ -1760,21 +1807,19 @@ def on_for(index, collection, statements)
1760
1807
in_keyword = consume_keyword ( :in )
1761
1808
ending = consume_keyword ( :end )
1762
1809
1763
- # Consume the do keyword if it exists so that it doesn't get confused for
1764
- # some other block
1765
- keyword = find_keyword ( :do )
1766
- if keyword &&
1767
- keyword . location . start_char > collection . location . end_char &&
1768
- keyword . location . end_char < ending . location . start_char
1769
- tokens . delete ( keyword )
1770
- end
1810
+ delimiter =
1811
+ find_keyword_between ( :do , collection , ending ) ||
1812
+ find_token_between ( Semicolon , collection , ending )
1813
+
1814
+ tokens . delete ( delimiter ) if delimiter
1771
1815
1772
1816
start_char =
1773
- find_next_statement_start ( ( keyword || collection ) . location . end_char )
1817
+ find_next_statement_start ( ( delimiter || collection ) . location . end_char )
1818
+
1774
1819
statements . bind (
1775
1820
start_char ,
1776
1821
start_char -
1777
- line_counts [ ( keyword || collection ) . location . end_line - 1 ] . start ,
1822
+ line_counts [ ( delimiter || collection ) . location . end_line - 1 ] . start ,
1778
1823
ending . location . start_char ,
1779
1824
ending . location . start_column
1780
1825
)
@@ -1984,7 +2029,12 @@ def on_if(predicate, statements, consequent)
1984
2029
beginning = consume_keyword ( :if )
1985
2030
ending = consequent || consume_keyword ( :end )
1986
2031
1987
- start_char = find_next_statement_start ( predicate . location . end_char )
2032
+ if ( keyword = find_keyword_between ( :then , predicate , ending ) )
2033
+ tokens . delete ( keyword )
2034
+ end
2035
+
2036
+ start_char =
2037
+ find_next_statement_start ( ( keyword || predicate ) . location . end_char )
1988
2038
statements . bind (
1989
2039
start_char ,
1990
2040
start_char - line_counts [ predicate . location . end_line - 1 ] . start ,
@@ -2068,7 +2118,8 @@ def on_in(pattern, statements, consequent)
2068
2118
statements_start = token
2069
2119
end
2070
2120
2071
- start_char = find_next_statement_start ( statements_start . location . end_char )
2121
+ start_char =
2122
+ find_next_statement_start ( ( token || statements_start ) . location . end_char )
2072
2123
statements . bind (
2073
2124
start_char ,
2074
2125
start_char -
@@ -2194,12 +2245,19 @@ def on_lambda(params, statements)
2194
2245
token . location . start_char > beginning . location . start_char
2195
2246
end
2196
2247
2248
+ if braces
2249
+ opening = consume_token ( TLamBeg )
2250
+ closing = consume_token ( RBrace )
2251
+ else
2252
+ opening = consume_keyword ( :do )
2253
+ closing = consume_keyword ( :end )
2254
+ end
2255
+
2197
2256
# We need to do some special mapping here. Since ripper doesn't support
2198
- # capturing lambda var until 3.2 , we need to normalize all of that here.
2257
+ # capturing lambda vars , we need to normalize all of that here.
2199
2258
params =
2200
- case params
2201
- when Paren
2202
- # In this case we've gotten to the <3.2 parentheses wrapping a set of
2259
+ if params . is_a? ( Paren )
2260
+ # In this case we've gotten to the parentheses wrapping a set of
2203
2261
# parameters case. Here we need to manually scan for lambda locals.
2204
2262
range = ( params . location . start_char + 1 ) ...params . location . end_char
2205
2263
locals = lambda_locals ( source [ range ] )
@@ -2221,25 +2279,28 @@ def on_lambda(params, statements)
2221
2279
2222
2280
node . comments . concat ( params . comments )
2223
2281
node
2224
- when Params
2225
- # In this case we've gotten to the <3.2 plain set of parameters. In
2226
- # this case there cannot be lambda locals, so we will wrap the
2227
- # parameters into a lambda var that has no locals.
2282
+ else
2283
+ # If there are no parameters, then we didn't have anything to base the
2284
+ # location information of off. Now that we have an opening of the
2285
+ # block, we can correct this.
2286
+ if params . empty?
2287
+ opening_location = opening . location
2288
+ location =
2289
+ Location . fixed (
2290
+ line : opening_location . start_line ,
2291
+ char : opening_location . start_char ,
2292
+ column : opening_location . start_column
2293
+ )
2294
+
2295
+ params = params . copy ( location : location )
2296
+ end
2297
+
2298
+ # In this case we've gotten to the plain set of parameters. In this
2299
+ # case there cannot be lambda locals, so we will wrap the parameters
2300
+ # into a lambda var that has no locals.
2228
2301
LambdaVar . new ( params : params , locals : [ ] , location : params . location )
2229
- when LambdaVar
2230
- # In this case we've gotten to 3.2+ lambda var. In this case we don't
2231
- # need to do anything and can just the value as given.
2232
- params
2233
2302
end
2234
2303
2235
- if braces
2236
- opening = consume_token ( TLamBeg )
2237
- closing = consume_token ( RBrace )
2238
- else
2239
- opening = consume_keyword ( :do )
2240
- closing = consume_keyword ( :end )
2241
- end
2242
-
2243
2304
start_char = find_next_statement_start ( opening . location . end_char )
2244
2305
statements . bind (
2245
2306
start_char ,
@@ -3134,7 +3195,7 @@ def on_rescue(exceptions, variable, statements, consequent)
3134
3195
exceptions = exceptions [ 0 ] if exceptions . is_a? ( Array )
3135
3196
3136
3197
last_node = variable || exceptions || keyword
3137
- start_char = find_next_statement_start ( last_node . location . end_char )
3198
+ start_char = find_next_statement_start ( last_node . end_char )
3138
3199
statements . bind (
3139
3200
start_char ,
3140
3201
start_char - line_counts [ last_node . location . start_line - 1 ] . start ,
@@ -3156,7 +3217,7 @@ def on_rescue(exceptions, variable, statements, consequent)
3156
3217
start_char : keyword . location . end_char + 1 ,
3157
3218
start_column : keyword . location . end_column + 1 ,
3158
3219
end_line : last_node . location . end_line ,
3159
- end_char : last_node . location . end_char ,
3220
+ end_char : last_node . end_char ,
3160
3221
end_column : last_node . location . end_column
3161
3222
)
3162
3223
)
@@ -3267,9 +3328,29 @@ def on_sclass(target, bodystmt)
3267
3328
)
3268
3329
end
3269
3330
3270
- # def on_semicolon(value)
3271
- # value
3272
- # end
3331
+ # Semicolons are tokens that get added to the token list but never get
3332
+ # attached to the AST. Because of this they only need to track their
3333
+ # associated location so they can be used for computing bounds.
3334
+ class Semicolon
3335
+ attr_reader :location
3336
+
3337
+ def initialize ( location )
3338
+ @location = location
3339
+ end
3340
+ end
3341
+
3342
+ # :call-seq:
3343
+ # on_semicolon: (String value) -> Semicolon
3344
+ def on_semicolon ( value )
3345
+ tokens << Semicolon . new (
3346
+ Location . token (
3347
+ line : lineno ,
3348
+ char : char_pos ,
3349
+ column : current_column ,
3350
+ size : value . size
3351
+ )
3352
+ )
3353
+ end
3273
3354
3274
3355
# def on_sp(value)
3275
3356
# value
@@ -3706,7 +3787,12 @@ def on_unless(predicate, statements, consequent)
3706
3787
beginning = consume_keyword ( :unless )
3707
3788
ending = consequent || consume_keyword ( :end )
3708
3789
3709
- start_char = find_next_statement_start ( predicate . location . end_char )
3790
+ if ( keyword = find_keyword_between ( :then , predicate , ending ) )
3791
+ tokens . delete ( keyword )
3792
+ end
3793
+
3794
+ start_char =
3795
+ find_next_statement_start ( ( keyword || predicate ) . location . end_char )
3710
3796
statements . bind (
3711
3797
start_char ,
3712
3798
start_char - line_counts [ predicate . location . end_line - 1 ] . start ,
@@ -3742,16 +3828,16 @@ def on_until(predicate, statements)
3742
3828
beginning = consume_keyword ( :until )
3743
3829
ending = consume_keyword ( :end )
3744
3830
3745
- # Consume the do keyword if it exists so that it doesn't get confused for
3746
- # some other block
3747
- keyword = find_keyword ( :do )
3748
- if keyword && keyword . location . start_char > predicate . location . end_char &&
3749
- keyword . location . end_char < ending . location . start_char
3750
- tokens . delete ( keyword )
3751
- end
3831
+ delimiter =
3832
+ find_keyword_between ( :do , predicate , statements ) ||
3833
+ find_token_between ( Semicolon , predicate , statements )
3834
+
3835
+ tokens . delete ( delimiter ) if delimiter
3752
3836
3753
3837
# Update the Statements location information
3754
- start_char = find_next_statement_start ( predicate . location . end_char )
3838
+ start_char =
3839
+ find_next_statement_start ( ( delimiter || predicate ) . location . end_char )
3840
+
3755
3841
statements . bind (
3756
3842
start_char ,
3757
3843
start_char - line_counts [ predicate . location . end_line - 1 ] . start ,
@@ -3845,7 +3931,8 @@ def on_when(arguments, statements, consequent)
3845
3931
statements_start = token
3846
3932
end
3847
3933
3848
- start_char = find_next_statement_start ( statements_start . location . end_char )
3934
+ start_char =
3935
+ find_next_statement_start ( ( token || statements_start ) . location . end_char )
3849
3936
3850
3937
statements . bind (
3851
3938
start_char ,
@@ -3869,16 +3956,16 @@ def on_while(predicate, statements)
3869
3956
beginning = consume_keyword ( :while )
3870
3957
ending = consume_keyword ( :end )
3871
3958
3872
- # Consume the do keyword if it exists so that it doesn't get confused for
3873
- # some other block
3874
- keyword = find_keyword ( :do )
3875
- if keyword && keyword . location . start_char > predicate . location . end_char &&
3876
- keyword . location . end_char < ending . location . start_char
3877
- tokens . delete ( keyword )
3878
- end
3959
+ delimiter =
3960
+ find_keyword_between ( :do , predicate , statements ) ||
3961
+ find_token_between ( Semicolon , predicate , statements )
3962
+
3963
+ tokens . delete ( delimiter ) if delimiter
3879
3964
3880
3965
# Update the Statements location information
3881
- start_char = find_next_statement_start ( predicate . location . end_char )
3966
+ start_char =
3967
+ find_next_statement_start ( ( delimiter || predicate ) . location . end_char )
3968
+
3882
3969
statements . bind (
3883
3970
start_char ,
3884
3971
start_char - line_counts [ predicate . location . end_line - 1 ] . start ,
0 commit comments