Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e8e043c

Browse files
authored
Merge pull request #289 from ruby-syntax-tree/more-locations
Even more locations
2 parents a5ad966 + 5cc7e3d commit e8e043c

File tree

6 files changed

+634
-191
lines changed

6 files changed

+634
-191
lines changed

bin/compare renamed to bin/whitequark

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ $:.unshift(File.expand_path("../lib", __dir__))
88
require "syntax_tree"
99

1010
# First, opt in to every AST feature.
11-
# Parser::Builders::Default.modernize
11+
Parser::Builders::Default.modernize
1212

1313
# Modify the source map == check so that it doesn't check against the node
1414
# itself so we don't get into a recursive loop.
@@ -46,14 +46,34 @@ ptree = parser.parse(buffer)
4646

4747
if stree == ptree
4848
puts "Syntax trees are equivalent."
49-
else
50-
warn "Syntax trees are different."
49+
elsif stree.inspect == ptree.inspect
50+
warn "Syntax tree locations are different."
51+
52+
queue = [[stree, ptree]]
53+
while (left, right = queue.shift)
54+
if left.location != right.location
55+
warn "Different node:"
56+
pp left
57+
58+
warn "Different location:"
59+
60+
warn "Syntax Tree:"
61+
pp left.location
62+
63+
warn "whitequark/parser:"
64+
pp right.location
5165

52-
warn "syntax_tree:"
66+
exit
67+
end
68+
69+
left.children.zip(right.children).each do |left_child, right_child|
70+
queue << [left_child, right_child] if left_child.is_a?(Parser::AST::Node)
71+
end
72+
end
73+
else
74+
warn "Syntax Tree:"
5375
pp stree
5476

55-
warn "parser:"
77+
warn "whitequark/parser:"
5678
pp ptree
57-
58-
binding.irb
5979
end

lib/syntax_tree/node.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,6 +2149,14 @@ def ===(other)
21492149
other.is_a?(BlockVar) && params === other.params &&
21502150
ArrayMatch.call(locals, other.locals)
21512151
end
2152+
2153+
# When a single required parameter is declared for a block, it gets
2154+
# automatically expanded if the values being yielded into it are an array.
2155+
def arg0?
2156+
params.requireds.length == 1 && params.optionals.empty? &&
2157+
params.rest.nil? && params.posts.empty? && params.keywords.empty? &&
2158+
params.keyword_rest.nil? && params.block.nil?
2159+
end
21522160
end
21532161

21542162
# BlockArg represents declaring a block parameter on a method definition.

lib/syntax_tree/parser.rb

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -670,18 +670,22 @@ def self.visit(node, tokens)
670670
# (nil | Array[untyped]) posts
671671
# ) -> AryPtn
672672
def on_aryptn(constant, requireds, rest, posts)
673-
parts = [constant, *requireds, rest, *posts].compact
673+
lbracket = find_token(LBracket)
674+
lbracket ||= find_token(LParen) if constant
674675

675-
# If there aren't any parts (no constant, no positional arguments), then
676-
# we're matching an empty array. In this case, we're going to look for the
677-
# left and right brackets explicitly. Otherwise, we'll just use the bounds
678-
# of the various parts.
679-
location =
680-
if parts.empty?
681-
consume_token(LBracket).location.to(consume_token(RBracket).location)
682-
else
683-
parts[0].location.to(parts[-1].location)
684-
end
676+
rbracket = find_token(RBracket)
677+
rbracket ||= find_token(RParen) if constant
678+
679+
parts = [constant, lbracket, *requireds, rest, *posts, rbracket].compact
680+
681+
# The location is going to be determined by the first part to the last
682+
# part. This includes potential brackets.
683+
location = parts[0].location.to(parts[-1].location)
684+
685+
# Now that we have the location calculated, we can remove the brackets
686+
# from the list of tokens.
687+
tokens.delete(lbracket) if lbracket
688+
tokens.delete(rbracket) if rbracket
685689

686690
# If there is a plain *, then we're going to fix up the location of it
687691
# here because it currently doesn't have anything to use for its precise
@@ -2353,23 +2357,30 @@ def on_method_add_arg(call, arguments)
23532357

23542358
# :call-seq:
23552359
# on_method_add_block: (
2356-
# (Call | Command | CommandCall) call,
2360+
# (Break | Call | Command | CommandCall) call,
23572361
# Block block
2358-
# ) -> MethodAddBlock
2362+
# ) -> Break | MethodAddBlock
23592363
def on_method_add_block(call, block)
23602364
location = call.location.to(block.location)
23612365

23622366
case call
2367+
when Break
2368+
parts = call.arguments.parts
2369+
2370+
node = parts.pop
2371+
copied =
2372+
node.copy(block: block, location: node.location.to(block.location))
2373+
2374+
copied.comments.concat(call.comments)
2375+
parts << copied
2376+
2377+
call.copy(location: location)
23632378
when Command, CommandCall
23642379
node = call.copy(block: block, location: location)
23652380
node.comments.concat(call.comments)
23662381
node
23672382
else
2368-
MethodAddBlock.new(
2369-
call: call,
2370-
block: block,
2371-
location: call.location.to(block.location)
2372-
)
2383+
MethodAddBlock.new(call: call, block: block, location: location)
23732384
end
23742385
end
23752386

@@ -2592,19 +2603,40 @@ def on_params(
25922603
# have a `nil` for the value instead of a `false`.
25932604
keywords&.map! { |(key, value)| [key, value || nil] }
25942605

2595-
parts = [
2596-
*requireds,
2597-
*optionals&.flatten(1),
2598-
rest,
2599-
*posts,
2600-
*keywords&.flatten(1),
2601-
(keyword_rest if keyword_rest != :nil),
2602-
(block if block != :&)
2603-
].compact
2606+
# Here we're going to build up a list of all of the params so that we can
2607+
# determine our location information.
2608+
parts = []
2609+
2610+
requireds&.each { |required| parts << required.location }
2611+
optionals&.each do |(key, value)|
2612+
parts << key.location
2613+
parts << value.location if value
2614+
end
2615+
2616+
parts << rest.location if rest
2617+
posts&.each { |post| parts << post.location }
2618+
2619+
keywords&.each do |(key, value)|
2620+
parts << key.location
2621+
parts << value.location if value
2622+
end
2623+
2624+
if keyword_rest == :nil
2625+
# When we get a :nil here, it means that we have **nil syntax, which
2626+
# means this set of parameters accepts no more keyword arguments. In
2627+
# this case we need to go and find the location of these two tokens.
2628+
operator = consume_operator(:**)
2629+
parts << operator.location.to(consume_keyword(:nil).location)
2630+
elsif keyword_rest
2631+
parts << keyword_rest.location
2632+
end
2633+
2634+
parts << block.location if block && block != :&
2635+
parts = parts.compact
26042636

26052637
location =
26062638
if parts.any?
2607-
parts[0].location.to(parts[-1].location)
2639+
parts[0].to(parts[-1])
26082640
else
26092641
Location.fixed(line: lineno, char: char_pos, column: current_column)
26102642
end

0 commit comments

Comments
 (0)