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

Commit 7bc0242

Browse files
authored
Merge pull request #64 from ruby-syntax-tree/match-visitor
Match visitor
2 parents 1ea3b6a + d26544c commit 7bc0242

File tree

7 files changed

+1447
-2501
lines changed

7 files changed

+1447
-2501
lines changed

lib/syntax_tree.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
require_relative "syntax_tree/parser"
1212
require_relative "syntax_tree/version"
1313
require_relative "syntax_tree/visitor"
14+
require_relative "syntax_tree/visitor/field_visitor"
1415
require_relative "syntax_tree/visitor/json_visitor"
16+
require_relative "syntax_tree/visitor/match_visitor"
1517
require_relative "syntax_tree/visitor/pretty_print_visitor"
1618

1719
# If PrettyPrint::Align isn't defined, then we haven't gotten the updated

lib/syntax_tree/cli.rb

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def failure
103103
# An action of the CLI that prints out the doc tree IR for the given source.
104104
class Doc < Action
105105
def run(handler, filepath, source)
106-
formatter = Formatter.new([])
106+
formatter = Formatter.new(source, [])
107107
handler.parse(source).format(formatter)
108108
pp formatter.groups.first
109109
end
@@ -116,6 +116,26 @@ def run(handler, filepath, source)
116116
end
117117
end
118118

119+
# An action of the CLI that converts the source into its equivalent JSON
120+
# representation.
121+
class Json < Action
122+
def run(handler, filepath, source)
123+
object = Visitor::JSONVisitor.new.visit(handler.parse(source))
124+
puts JSON.pretty_generate(object)
125+
end
126+
end
127+
128+
# An action of the CLI that outputs a pattern-matching Ruby expression that
129+
# would match the input given.
130+
class Match < Action
131+
def run(handler, filepath, source)
132+
formatter = Formatter.new(source, [])
133+
Visitor::MatchVisitor.new(formatter).visit(handler.parse(source))
134+
formatter.flush
135+
puts formatter.output.join
136+
end
137+
end
138+
119139
# An action of the CLI that formats the input source and writes the
120140
# formatted output back to the file.
121141
class Write < Action
@@ -154,6 +174,12 @@ def run(handler, filepath, source)
154174
#{Color.bold("stree format [OPTIONS] [FILE]")}
155175
Print out the formatted version of the given files
156176
177+
#{Color.bold("stree json [OPTIONS] [FILE]")}
178+
Print out the JSON representation of the given files
179+
180+
#{Color.bold("stree match [OPTIONS] [FILE]")}
181+
Print out a pattern-matching Ruby expression that would match the given files
182+
157183
#{Color.bold("stree help")}
158184
Display this help message
159185
@@ -201,6 +227,10 @@ def run(argv)
201227
Debug.new
202228
when "doc"
203229
Doc.new
230+
when "j", "json"
231+
Json.new
232+
when "m", "match"
233+
Match.new
204234
when "f", "format"
205235
Format.new
206236
when "w", "write"

lib/syntax_tree/language_server/inlay_hints.rb

Lines changed: 80 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,72 @@
22

33
module SyntaxTree
44
class LanguageServer
5-
class InlayHints
6-
attr_reader :before, :after
5+
class InlayHints < Visitor
6+
attr_reader :stack, :before, :after
77

88
def initialize
9+
@stack = []
910
@before = Hash.new { |hash, key| hash[key] = +"" }
1011
@after = Hash.new { |hash, key| hash[key] = +"" }
1112
end
1213

14+
def visit(node)
15+
stack << node
16+
result = super
17+
stack.pop
18+
result
19+
end
20+
21+
# Adds parentheses around assignments contained within the default values
22+
# of parameters. For example,
23+
#
24+
# def foo(a = b = c)
25+
# end
26+
#
27+
# becomes
28+
#
29+
# def foo(a = ₍b = c₎)
30+
# end
31+
#
32+
def visit_assign(node)
33+
parentheses(node.location) if stack[-2].is_a?(Params)
34+
end
35+
36+
# Adds parentheses around binary expressions to make it clear which
37+
# subexpression will be evaluated first. For example,
38+
#
39+
# a + b * c
40+
#
41+
# becomes
42+
#
43+
# a + ₍b * c₎
44+
#
45+
def visit_binary(node)
46+
case stack[-2]
47+
in Assign | OpAssign
48+
parentheses(node.location)
49+
in Binary[operator: operator] if operator != node.operator
50+
parentheses(node.location)
51+
else
52+
end
53+
end
54+
55+
# Adds parentheses around ternary operators contained within certain
56+
# expressions where it could be confusing which subexpression will get
57+
# evaluated first. For example,
58+
#
59+
# a ? b : c ? d : e
60+
#
61+
# becomes
62+
#
63+
# a ? b : ₍c ? d : e₎
64+
#
65+
def visit_if_op(node)
66+
if stack[-2] in Assign | Binary | IfOp | OpAssign
67+
parentheses(node.location)
68+
end
69+
end
70+
1371
# Adds the implicitly rescued StandardError into a bare rescue clause. For
1472
# example,
1573
#
@@ -23,54 +81,38 @@ def initialize
2381
# rescue StandardError
2482
# end
2583
#
26-
def bare_rescue(location)
27-
after[location.start_char + "rescue".length] << " StandardError"
84+
def visit_rescue(node)
85+
if node.exception.nil?
86+
after[node.location.start_char + "rescue".length] << " StandardError"
87+
end
2888
end
2989

30-
# Adds implicit parentheses around certain expressions to make it clear
31-
# which subexpression will be evaluated first. For example,
90+
# Adds parentheses around unary statements using the - operator that are
91+
# contained within Binary nodes. For example,
3292
#
33-
# a + b * c
93+
# -a + b
3494
#
3595
# becomes
3696
#
37-
# a + ₍b * c₎
97+
# ₍-a₎ + b
3898
#
39-
def precedence_parentheses(location)
40-
before[location.start_char] << "₍"
41-
after[location.end_char] << "₎"
99+
def visit_unary(node)
100+
if stack[-2].is_a?(Binary) && (node.operator == "-")
101+
parentheses(node.location)
102+
end
42103
end
43104

44105
def self.find(program)
45-
inlay_hints = new
46-
queue = [[nil, program]]
47-
48-
until queue.empty?
49-
parent_node, child_node = queue.shift
50-
51-
child_node.child_nodes.each do |grand_child_node|
52-
queue << [child_node, grand_child_node] if grand_child_node
53-
end
106+
visitor = new
107+
visitor.visit(program)
108+
visitor
109+
end
54110

55-
case [parent_node, child_node]
56-
in _, Rescue[exception: nil, location:]
57-
inlay_hints.bare_rescue(location)
58-
in Assign | Binary | IfOp | OpAssign, IfOp[location:]
59-
inlay_hints.precedence_parentheses(location)
60-
in Assign | OpAssign, Binary[location:]
61-
inlay_hints.precedence_parentheses(location)
62-
in Binary[operator: parent_oper], Binary[operator: child_oper, location:] if parent_oper != child_oper
63-
inlay_hints.precedence_parentheses(location)
64-
in Binary, Unary[operator: "-", location:]
65-
inlay_hints.precedence_parentheses(location)
66-
in Params, Assign[location:]
67-
inlay_hints.precedence_parentheses(location)
68-
else
69-
# do nothing
70-
end
71-
end
111+
private
72112

73-
inlay_hints
113+
def parentheses(location)
114+
before[location.start_char] << "₍"
115+
after[location.end_char] << "₎"
74116
end
75117
end
76118
end

0 commit comments

Comments
 (0)