Skip to content

Commit af31ffb

Browse files
authored
Merge pull request #356 from st0012/st0012-support-and-node
Add AndNode
2 parents 17928e9 + adac34a commit af31ffb

File tree

9 files changed

+171
-19
lines changed

9 files changed

+171
-19
lines changed

lib/syntax_tree/dsl.rb

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ def AliasNode(left, right)
3636
AliasNode.new(left: left, right: right, location: Location.default)
3737
end
3838

39+
# Create a new AndNode node.
40+
def AndNode(left, operator, right)
41+
AndNode.new(
42+
left: left,
43+
operator: operator,
44+
right: right,
45+
location: Location.default
46+
)
47+
end
48+
3949
# Create a new ARef node.
4050
def ARef(collection, index)
4151
ARef.new(collection: collection, index: index, location: Location.default)

lib/syntax_tree/field_visitor.rb

+9
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ def visit_alias(node)
7373
end
7474
end
7575

76+
def visit_and(node)
77+
node(node, "and") do
78+
field("left", node.left)
79+
text("operator", node.operator)
80+
field("right", node.right)
81+
comments(node)
82+
end
83+
end
84+
7685
def visit_arg_block(node)
7786
node(node, "arg_block") do
7887
field("value", node.value) if node.value

lib/syntax_tree/mutation_visitor.rb

+5
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ def visit_alias(node)
6565
node.copy(left: visit(node.left), right: visit(node.right))
6666
end
6767

68+
# Visit a AndNode node.
69+
def visit_and(node)
70+
node.copy(left: visit(node.left), right: visit(node.right))
71+
end
72+
6873
# Visit a ARef node.
6974
def visit_aref(node)
7075
node.copy(index: visit(node.index))

lib/syntax_tree/node.rb

+91-1
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,96 @@ def var_alias?
551551
end
552552
end
553553

554+
class AndNode < Node
555+
# Since AndNode's operator is a symbol, it's better to use the `name` method
556+
# than to allocate a new string every time. This is a tiny performance
557+
# optimization, but enough that it shows up in the profiler. Adding this in
558+
# for older Ruby versions.
559+
unless :+.respond_to?(:name)
560+
using Module.new {
561+
refine Symbol do
562+
def name
563+
to_s.freeze
564+
end
565+
end
566+
}
567+
end
568+
569+
# [Node] the left side of the && operator
570+
attr_reader :left
571+
572+
# [Symbol] the operator
573+
attr_reader :operator
574+
575+
# [Node] the right side of the && operator
576+
attr_reader :right
577+
578+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
579+
attr_reader :comments
580+
581+
def initialize(left:, operator:, right:, location:)
582+
@left = left
583+
@operator = operator
584+
@right = right
585+
@location = location
586+
@comments = []
587+
end
588+
589+
def accept(visitor)
590+
visitor.visit_and(self)
591+
end
592+
593+
def child_nodes
594+
[left, right]
595+
end
596+
597+
def copy(left: nil, operator: nil, right: nil, location: nil)
598+
node =
599+
AndNode.new(
600+
left: left || self.left,
601+
operator: operator || self.operator,
602+
right: right || self.right,
603+
location: location || self.location
604+
)
605+
node.comments.concat(comments.map(&:copy))
606+
node
607+
end
608+
609+
alias deconstruct child_nodes
610+
611+
def deconstruct_keys(_keys)
612+
{
613+
left: left,
614+
operator: operator,
615+
right: right,
616+
location: location,
617+
comments: comments
618+
}
619+
end
620+
621+
def format(q)
622+
left = self.left
623+
624+
q.group do
625+
q.group { q.format(left) }
626+
q.text(" ")
627+
628+
q.group do
629+
q.text(operator.name)
630+
q.indent do
631+
q.breakable_space
632+
q.format(right)
633+
end
634+
end
635+
end
636+
end
637+
638+
def ===(other)
639+
other.is_a?(AndNode) && left === other.left &&
640+
operator === other.operator && right === other.right
641+
end
642+
end
643+
554644
# ARef represents when you're pulling a value out of a collection at a
555645
# specific index. Put another way, it's any time you're calling the method
556646
# #[].
@@ -6267,7 +6357,7 @@ def call(q, node)
62676357
# want to force it to not be a ternary, like if the predicate is an
62686358
# assignment because it's hard to read.
62696359
case node.predicate
6270-
when Assign, Binary, Command, CommandCall, MAssign, OpAssign
6360+
when Assign, AndNode, Binary, Command, CommandCall, MAssign, OpAssign
62716361
return false
62726362
when Not
62736363
return false unless node.predicate.parentheses?

lib/syntax_tree/parser.rb

+16-6
Original file line numberDiff line numberDiff line change
@@ -893,12 +893,22 @@ def on_binary(left, operator, right)
893893
operator = tokens.delete(operator).value
894894
end
895895

896-
Binary.new(
897-
left: left,
898-
operator: operator,
899-
right: right,
900-
location: left.location.to(right.location)
901-
)
896+
case operator
897+
when :"&&", :and
898+
AndNode.new(
899+
left: left,
900+
operator: operator,
901+
right: right,
902+
location: left.location.to(right.location)
903+
)
904+
else
905+
Binary.new(
906+
left: left,
907+
operator: operator,
908+
right: right,
909+
location: left.location.to(right.location)
910+
)
911+
end
902912
end
903913

904914
# :call-seq:

lib/syntax_tree/pretty_print_visitor.rb

+11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ def initialize(q)
99
@q = q
1010
end
1111

12+
# This is here because we need to make sure the operator is cast to a string
13+
# before we print it out.
14+
def visit_and(node)
15+
node(node, "and") do
16+
field("left", node.left)
17+
text("operator", node.operator.to_s)
18+
field("right", node.right)
19+
comments(node)
20+
end
21+
end
22+
1223
# This is here because we need to make sure the operator is cast to a string
1324
# before we print it out.
1425
def visit_binary(node)

lib/syntax_tree/translation/parser.rb

+14-2
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,18 @@ def visit_begin(node)
468468
end
469469
end
470470

471+
# Visit a AndNode node.
472+
def visit_and(node)
473+
s(
474+
:and,
475+
[visit(node.left), visit(node.right)],
476+
smap_operator(
477+
srange_find_between(node.left, node.right, node.operator.to_s),
478+
srange_node(node)
479+
)
480+
)
481+
end
482+
471483
# Visit a Binary node.
472484
def visit_binary(node)
473485
case node.operator
@@ -482,9 +494,9 @@ def visit_binary(node)
482494
else
483495
visit(canonical_binary(node))
484496
end
485-
when :"=>", :"&&", :and, :"||", :or
497+
when :"=>", :"||", :or
486498
s(
487-
{ "=>": :match_as, "&&": :and, "||": :or }.fetch(
499+
{ "=>": :match_as, "||": :or }.fetch(
488500
node.operator,
489501
node.operator
490502
),

lib/syntax_tree/visitor.rb

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class Visitor < BasicVisitor
1414
# Visit an AliasNode node.
1515
alias visit_alias visit_child_nodes
1616

17+
# Visit an AndNode node.
18+
alias visit_and visit_child_nodes
19+
1720
# Visit an ArgBlock node.
1821
alias visit_arg_block visit_child_nodes
1922

lib/syntax_tree/yarv/compiler.rb

+12-10
Original file line numberDiff line numberDiff line change
@@ -543,18 +543,20 @@ def visit_bare_assoc_hash(node)
543543
def visit_begin(node)
544544
end
545545

546-
def visit_binary(node)
547-
case node.operator
548-
when :"&&"
549-
done_label = iseq.label
546+
def visit_and(node)
547+
done_label = iseq.label
550548

551-
visit(node.left)
552-
iseq.dup
553-
iseq.branchunless(done_label)
549+
visit(node.left)
550+
iseq.dup
551+
iseq.branchunless(done_label)
554552

555-
iseq.pop
556-
visit(node.right)
557-
iseq.push(done_label)
553+
iseq.pop
554+
visit(node.right)
555+
iseq.push(done_label)
556+
end
557+
558+
def visit_binary(node)
559+
case node.operator
558560
when :"||"
559561
visit(node.left)
560562
iseq.dup

0 commit comments

Comments
 (0)