Skip to content

Commit 6f26a47

Browse files
committed
Combine the LambdaVar and BlockVar nodes
Both `LambdaVar` and `BlockVar` represent a collection of arguments (positional, optional, keyword, block) as well as block or lambda-local variables. This commit removes the `LambdaVar` node and folds its functionality into the `BlockVar` node, which reduces the number of nodes and simplifies logic.
1 parent 491b86d commit 6f26a47

File tree

3 files changed

+76
-96
lines changed

3 files changed

+76
-96
lines changed

lib/syntax_tree/node.rb

Lines changed: 43 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,13 +2131,18 @@ def ===(other)
21312131
end
21322132
end
21332133

2134-
# BlockVar represents the parameters being declared for a block. Effectively
2135-
# this node is everything contained within the pipes. This includes all of the
2136-
# various parameter types, as well as block-local variable declarations.
2134+
# BlockVar represents the parameters being declared for a block or lambda.
2135+
# This includes all of the various parameter types, as well as
2136+
# block-local/lambda-local variable declarations.
21372137
#
21382138
# method do |positional, optional = value, keyword:, █ local|
21392139
# end
21402140
#
2141+
# OR
2142+
#
2143+
# -> (positional, optional = value, keyword:, █ local) do
2144+
# end
2145+
#
21412146
class BlockVar < Node
21422147
# [Params] the parameters being declared with the block
21432148
attr_reader :params
@@ -2148,10 +2153,15 @@ class BlockVar < Node
21482153
# [Array[ Comment | EmbDoc ]] the comments attached to this node
21492154
attr_reader :comments
21502155

2151-
def initialize(params:, locals:, location:)
2156+
# [boolean] whether or not the variables are within pipes
2157+
attr_reader :pipe
2158+
alias pipe? pipe
2159+
2160+
def initialize(params:, locals:, location:, pipe:)
21522161
@params = params
21532162
@locals = locals
21542163
@location = location
2164+
@pipe = pipe
21552165
@comments = []
21562166
end
21572167

@@ -2163,12 +2173,13 @@ def child_nodes
21632173
[params, *locals]
21642174
end
21652175

2166-
def copy(params: nil, locals: nil, location: nil)
2176+
def copy(params: nil, locals: nil, location: nil, pipe: nil)
21672177
node =
21682178
BlockVar.new(
21692179
params: params || self.params,
21702180
locals: locals || self.locals,
2171-
location: location || self.location
2181+
location: location || self.location,
2182+
pipe: pipe || self.pipe
21722183
)
21732184

21742185
node.comments.concat(comments.map(&:copy))
@@ -2178,7 +2189,13 @@ def copy(params: nil, locals: nil, location: nil)
21782189
alias deconstruct child_nodes
21792190

21802191
def deconstruct_keys(_keys)
2181-
{ params: params, locals: locals, location: location, comments: comments }
2192+
{
2193+
params: params,
2194+
locals: locals,
2195+
location: location,
2196+
comments: comments,
2197+
pipe: pipe
2198+
}
21822199
end
21832200

21842201
# Within the pipes of the block declaration, we don't want any spaces. So
@@ -2194,23 +2211,23 @@ def call(q)
21942211
SEPARATOR = Separator.new.freeze
21952212

21962213
def format(q)
2197-
q.text("|")
2198-
q.group do
2199-
q.remove_breaks(q.format(params))
2214+
pipe? ? q.remove_breaks(q.format(params)) : q.format(params)
22002215

2201-
if locals.any?
2202-
q.text("; ")
2203-
q.seplist(locals, SEPARATOR) { |local| q.format(local) }
2204-
end
2216+
if locals.any?
2217+
q.text("; ")
2218+
q.seplist(locals, SEPARATOR) { |local| q.format(local) }
22052219
end
2206-
q.text("|")
22072220
end
22082221

22092222
def ===(other)
22102223
other.is_a?(BlockVar) && params === other.params &&
22112224
ArrayMatch.call(locals, other.locals)
22122225
end
22132226

2227+
def empty?
2228+
params.empty? && locals.empty?
2229+
end
2230+
22142231
# When a single required parameter is declared for a block, it gets
22152232
# automatically expanded if the values being yielded into it are an array.
22162233
def arg0?
@@ -4486,8 +4503,11 @@ def format_break(q, break_opening, break_closing)
44864503
q.format(BlockOpenFormatter.new(break_opening, opening), stackable: false)
44874504

44884505
if block_var
4489-
q.text(" ")
4490-
q.format(block_var)
4506+
q.text(" |")
4507+
q.group do
4508+
q.format(block_var)
4509+
end
4510+
q.text("|")
44914511
end
44924512

44934513
unless bodystmt.empty?
@@ -4507,7 +4527,11 @@ def format_flat(q, flat_opening, flat_closing)
45074527

45084528
if block_var
45094529
q.breakable_space
4510-
q.format(block_var)
4530+
q.text("|")
4531+
q.group do
4532+
q.format(block_var)
4533+
end
4534+
q.text("|")
45114535
q.breakable_space
45124536
end
45134537

@@ -7140,7 +7164,7 @@ def ===(other)
71407164
# ->(value) { value * 2 }
71417165
#
71427166
class Lambda < Node
7143-
# [LambdaVar | Paren] the parameter declaration for this lambda
7167+
# [BlockVar | Paren] the parameter declaration for this lambda
71447168
attr_reader :params
71457169

71467170
# [BodyStmt | Statements] the expressions to be executed in this lambda
@@ -7258,76 +7282,6 @@ def ===(other)
72587282
end
72597283
end
72607284

7261-
# LambdaVar represents the parameters being declared for a lambda. Effectively
7262-
# this node is everything contained within the parentheses. This includes all
7263-
# of the various parameter types, as well as block-local variable
7264-
# declarations.
7265-
#
7266-
# -> (positional, optional = value, keyword:, &block; local) do
7267-
# end
7268-
#
7269-
class LambdaVar < Node
7270-
# [Params] the parameters being declared with the block
7271-
attr_reader :params
7272-
7273-
# [Array[ Ident ]] the list of block-local variable declarations
7274-
attr_reader :locals
7275-
7276-
# [Array[ Comment | EmbDoc ]] the comments attached to this node
7277-
attr_reader :comments
7278-
7279-
def initialize(params:, locals:, location:)
7280-
@params = params
7281-
@locals = locals
7282-
@location = location
7283-
@comments = []
7284-
end
7285-
7286-
def accept(visitor)
7287-
visitor.visit_lambda_var(self)
7288-
end
7289-
7290-
def child_nodes
7291-
[params, *locals]
7292-
end
7293-
7294-
def copy(params: nil, locals: nil, location: nil)
7295-
node =
7296-
LambdaVar.new(
7297-
params: params || self.params,
7298-
locals: locals || self.locals,
7299-
location: location || self.location
7300-
)
7301-
7302-
node.comments.concat(comments.map(&:copy))
7303-
node
7304-
end
7305-
7306-
alias deconstruct child_nodes
7307-
7308-
def deconstruct_keys(_keys)
7309-
{ params: params, locals: locals, location: location, comments: comments }
7310-
end
7311-
7312-
def empty?
7313-
params.empty? && locals.empty?
7314-
end
7315-
7316-
def format(q)
7317-
q.format(params)
7318-
7319-
if locals.any?
7320-
q.text("; ")
7321-
q.seplist(locals, BlockVar::SEPARATOR) { |local| q.format(local) }
7322-
end
7323-
end
7324-
7325-
def ===(other)
7326-
other.is_a?(LambdaVar) && params === other.params &&
7327-
ArrayMatch.call(locals, other.locals)
7328-
end
7329-
end
7330-
73317285
# LBrace represents the use of a left brace, i.e., {.
73327286
class LBrace < Node
73337287
# [String] the left brace

lib/syntax_tree/parser.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,8 @@ def on_block_var(params, locals)
937937
BlockVar.new(
938938
params: params,
939939
locals: locals || [],
940-
location: beginning.location.to(ending.location)
940+
location: beginning.location.to(ending.location),
941+
pipe: true
941942
)
942943
end
943944

@@ -2296,10 +2297,11 @@ def on_lambda(params, statements)
22962297
Paren.new(
22972298
lparen: params.lparen,
22982299
contents:
2299-
LambdaVar.new(
2300+
BlockVar.new(
23002301
params: params.contents,
23012302
locals: locals,
2302-
location: location
2303+
location: location,
2304+
pipe: false
23032305
),
23042306
location: params.location
23052307
)
@@ -2324,8 +2326,8 @@ def on_lambda(params, statements)
23242326

23252327
# In this case we've gotten to the plain set of parameters. In this
23262328
# case there cannot be lambda locals, so we will wrap the parameters
2327-
# into a lambda var that has no locals.
2328-
LambdaVar.new(params: params, locals: [], location: params.location)
2329+
# into a block var that has no locals.
2330+
BlockVar.new(params: params, locals: [], location: params.location, pipe: false)
23292331
end
23302332

23312333
start_char = find_next_statement_start(opening.location.end_char)
@@ -2345,12 +2347,17 @@ def on_lambda(params, statements)
23452347
end
23462348

23472349
# :call-seq:
2348-
# on_lambda_var: (Params params, Array[ Ident ] locals) -> LambdaVar
2350+
# on_lambda_var: (Params params, Array[ Ident ] locals) -> BlockVar
23492351
def on_lambda_var(params, locals)
23502352
location = params.location
23512353
location = location.to(locals.last.location) if locals.any?
23522354

2353-
LambdaVar.new(params: params, locals: locals || [], location: location)
2355+
BlockVar.new(
2356+
params: params,
2357+
locals: locals || [],
2358+
location: location,
2359+
pipe: false
2360+
)
23542361
end
23552362

23562363
# Ripper doesn't support capturing lambda local variables until 3.2. To

test/node_test.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,25 @@ def test_lambda
635635
source = "->(value) { value * 2 }"
636636

637637
assert_node(Lambda, source)
638+
639+
at = location(chars: 3..8)
640+
assert_node(BlockVar, source, at: at) { |node| node.params.contents }
641+
end
642+
643+
def test_lambda_no_parens
644+
source = "-> value { value * 2 }"
645+
646+
assert_node(Lambda, source)
647+
648+
at = location(chars: 3..8)
649+
assert_node(BlockVar, source, at: at, &:params)
650+
end
651+
652+
def test_lambda_braces
653+
source = "lambda { |value| value * 2 }"
654+
655+
at = location(chars: 9..16)
656+
assert_node(BlockVar, source, at: at) { |node| node.block.block_var }
638657
end
639658

640659
def test_lambda_do

0 commit comments

Comments
 (0)