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

Match visitor #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/syntax_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
require_relative "syntax_tree/parser"
require_relative "syntax_tree/version"
require_relative "syntax_tree/visitor"
require_relative "syntax_tree/visitor/field_visitor"
require_relative "syntax_tree/visitor/json_visitor"
require_relative "syntax_tree/visitor/match_visitor"
require_relative "syntax_tree/visitor/pretty_print_visitor"

# If PrettyPrint::Align isn't defined, then we haven't gotten the updated
Expand Down
32 changes: 31 additions & 1 deletion lib/syntax_tree/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def failure
# An action of the CLI that prints out the doc tree IR for the given source.
class Doc < Action
def run(handler, filepath, source)
formatter = Formatter.new([])
formatter = Formatter.new(source, [])
handler.parse(source).format(formatter)
pp formatter.groups.first
end
Expand All @@ -116,6 +116,26 @@ def run(handler, filepath, source)
end
end

# An action of the CLI that converts the source into its equivalent JSON
# representation.
class Json < Action
def run(handler, filepath, source)
object = Visitor::JSONVisitor.new.visit(handler.parse(source))
puts JSON.pretty_generate(object)
end
end

# An action of the CLI that outputs a pattern-matching Ruby expression that
# would match the input given.
class Match < Action
def run(handler, filepath, source)
formatter = Formatter.new(source, [])
Visitor::MatchVisitor.new(formatter).visit(handler.parse(source))
formatter.flush
puts formatter.output.join
end
end

# An action of the CLI that formats the input source and writes the
# formatted output back to the file.
class Write < Action
Expand Down Expand Up @@ -154,6 +174,12 @@ def run(handler, filepath, source)
#{Color.bold("stree format [OPTIONS] [FILE]")}
Print out the formatted version of the given files

#{Color.bold("stree json [OPTIONS] [FILE]")}
Print out the JSON representation of the given files

#{Color.bold("stree match [OPTIONS] [FILE]")}
Print out a pattern-matching Ruby expression that would match the given files

#{Color.bold("stree help")}
Display this help message

Expand Down Expand Up @@ -201,6 +227,10 @@ def run(argv)
Debug.new
when "doc"
Doc.new
when "j", "json"
Json.new
when "m", "match"
Match.new
when "f", "format"
Format.new
when "w", "write"
Expand Down
118 changes: 80 additions & 38 deletions lib/syntax_tree/language_server/inlay_hints.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,72 @@

module SyntaxTree
class LanguageServer
class InlayHints
attr_reader :before, :after
class InlayHints < Visitor
attr_reader :stack, :before, :after

def initialize
@stack = []
@before = Hash.new { |hash, key| hash[key] = +"" }
@after = Hash.new { |hash, key| hash[key] = +"" }
end

def visit(node)
stack << node
result = super
stack.pop
result
end

# Adds parentheses around assignments contained within the default values
# of parameters. For example,
#
# def foo(a = b = c)
# end
#
# becomes
#
# def foo(a = ₍b = c₎)
# end
#
def visit_assign(node)
parentheses(node.location) if stack[-2].is_a?(Params)
end

# Adds parentheses around binary expressions to make it clear which
# subexpression will be evaluated first. For example,
#
# a + b * c
#
# becomes
#
# a + ₍b * c₎
#
def visit_binary(node)
case stack[-2]
in Assign | OpAssign
parentheses(node.location)
in Binary[operator: operator] if operator != node.operator
parentheses(node.location)
else
end
end

# Adds parentheses around ternary operators contained within certain
# expressions where it could be confusing which subexpression will get
# evaluated first. For example,
#
# a ? b : c ? d : e
#
# becomes
#
# a ? b : ₍c ? d : e₎
#
def visit_if_op(node)
if stack[-2] in Assign | Binary | IfOp | OpAssign
parentheses(node.location)
end
end

# Adds the implicitly rescued StandardError into a bare rescue clause. For
# example,
#
Expand All @@ -23,54 +81,38 @@ def initialize
# rescue StandardError
# end
#
def bare_rescue(location)
after[location.start_char + "rescue".length] << " StandardError"
def visit_rescue(node)
if node.exception.nil?
after[node.location.start_char + "rescue".length] << " StandardError"
end
end

# Adds implicit parentheses around certain expressions to make it clear
# which subexpression will be evaluated first. For example,
# Adds parentheses around unary statements using the - operator that are
# contained within Binary nodes. For example,
#
# a + b * c
# -a + b
#
# becomes
#
# a + ₍b * c₎
# ₍-a₎ + b
#
def precedence_parentheses(location)
before[location.start_char] << "₍"
after[location.end_char] << "₎"
def visit_unary(node)
if stack[-2].is_a?(Binary) && (node.operator == "-")
parentheses(node.location)
end
end

def self.find(program)
inlay_hints = new
queue = [[nil, program]]

until queue.empty?
parent_node, child_node = queue.shift

child_node.child_nodes.each do |grand_child_node|
queue << [child_node, grand_child_node] if grand_child_node
end
visitor = new
visitor.visit(program)
visitor
end

case [parent_node, child_node]
in _, Rescue[exception: nil, location:]
inlay_hints.bare_rescue(location)
in Assign | Binary | IfOp | OpAssign, IfOp[location:]
inlay_hints.precedence_parentheses(location)
in Assign | OpAssign, Binary[location:]
inlay_hints.precedence_parentheses(location)
in Binary[operator: parent_oper], Binary[operator: child_oper, location:] if parent_oper != child_oper
inlay_hints.precedence_parentheses(location)
in Binary, Unary[operator: "-", location:]
inlay_hints.precedence_parentheses(location)
in Params, Assign[location:]
inlay_hints.precedence_parentheses(location)
else
# do nothing
end
end
private

inlay_hints
def parentheses(location)
before[location.start_char] << "₍"
after[location.end_char] << "₎"
end
end
end
Expand Down
Loading