lib/yarp.rb in yarp-0.10.0 vs lib/yarp.rb in yarp-0.11.0

- old
+ new

@@ -18,10 +18,14 @@ def line(value) offsets.bsearch_index { |offset| offset > value } || offsets.length end + def line_offset(value) + offsets[line(value) - 1] + end + def column(value) value - offsets[line(value) - 1] end private @@ -44,14 +48,18 @@ attr_reader :start_offset # The length of this location in bytes. attr_reader :length + # The list of comments attached to this location + attr_reader :comments + def initialize(source, start_offset, length) @source = source @start_offset = start_offset @length = length + @comments = [] end # Create a new location object with the given options. def copy(**options) Location.new( @@ -79,10 +87,16 @@ # The line number where this location starts. def start_line source.line(start_offset) end + # The content of the line where this location starts before this location. + def start_line_slice + offset = source.line_offset(start_offset) + source.slice(offset, start_offset - offset) + end + # The line number where this location ends. def end_line source.line(end_offset - 1) end @@ -93,11 +107,11 @@ end # The column number in bytes where this location ends from the start of the # line. def end_column - source.column(end_offset - 1) + source.column(end_offset) end def deconstruct_keys(keys) { start_offset: start_offset, end_offset: end_offset } end @@ -139,10 +153,19 @@ end def deconstruct_keys(keys) { type: type, location: location } end + + # Returns true if the comment happens on the same line as other code and false if the comment is by itself + def trailing? + type == :inline && !location.start_line_slice.strip.empty? + end + + def inspect + "#<YARP::Comment @type=#{@type.inspect} @location=#{@location.inspect}>" + end end # This represents an error that was encountered during parsing. class ParseError attr_reader :message, :location @@ -153,10 +176,14 @@ end def deconstruct_keys(keys) { message: message, location: location } end + + def inspect + "#<YARP::ParseError @message=#{@message.inspect} @location=#{@location.inspect}>" + end end # This represents a warning that was encountered during parsing. class ParseWarning attr_reader :message, :location @@ -167,10 +194,14 @@ end def deconstruct_keys(keys) { message: message, location: location } end + + def inspect + "#<YARP::ParseWarning @message=#{@message.inspect} @location=#{@location.inspect}>" + end end # A class that knows how to walk down the tree. None of the individual visit # methods are implemented on this visitor, so it forces the consumer to # implement each one that they need. For a default implementation that @@ -215,49 +246,10 @@ end def failure? !success? end - - # Keep in sync with Java MarkNewlinesVisitor - class MarkNewlinesVisitor < YARP::Visitor - def initialize(newline_marked) - @newline_marked = newline_marked - end - - def visit_block_node(node) - old_newline_marked = @newline_marked - @newline_marked = Array.new(old_newline_marked.size, false) - begin - super(node) - ensure - @newline_marked = old_newline_marked - end - end - alias_method :visit_lambda_node, :visit_block_node - - def visit_if_node(node) - node.set_newline_flag(@newline_marked) - super(node) - end - alias_method :visit_unless_node, :visit_if_node - - def visit_statements_node(node) - node.body.each do |child| - child.set_newline_flag(@newline_marked) - end - super(node) - end - end - private_constant :MarkNewlinesVisitor - - def mark_newlines - newline_marked = Array.new(1 + @source.offsets.size, false) - visitor = MarkNewlinesVisitor.new(newline_marked) - value.accept(visitor) - value - end end # This represents a token from the Ruby source. class Token attr_reader :type, :value, :location @@ -321,20 +313,84 @@ q.text("[Li:#{location.start_line}]") if newline? q.text("(") q.nest(2) do deconstructed = deconstruct_keys([]) deconstructed.delete(:location) - q.breakable("") q.seplist(deconstructed, lambda { q.comma_breakable }, :each_value) { |value| q.pp(value) } end q.breakable("") q.text(")") end end end + # This object is responsible for generating the output for the inspect method + # implementations of child nodes. + class NodeInspector + attr_reader :prefix, :output + + def initialize(prefix = "") + @prefix = prefix + @output = +"" + end + + # Appends a line to the output with the current prefix. + def <<(line) + output << "#{prefix}#{line}" + end + + # This generates a string that is used as the header of the inspect output + # for any given node. + def header(node) + output = +"@ #{node.class.name.split("::").last} (" + output << "location: (#{node.location.start_offset}...#{node.location.end_offset})" + output << ", newline: true" if node.newline? + output << ")\n" + output + end + + # Generates a string that represents a list of nodes. It handles properly + # using the box drawing characters to make the output look nice. + def list(prefix, nodes) + output = +"(length: #{nodes.length})\n" + last_index = nodes.length - 1 + + nodes.each_with_index do |node, index| + pointer, preadd = (index == last_index) ? ["└── ", " "] : ["├── ", "│ "] + node_prefix = "#{prefix}#{preadd}" + output << node.inspect(NodeInspector.new(node_prefix)).sub(node_prefix, "#{prefix}#{pointer}") + end + + output + end + + # Generates a string that represents a location field on a node. + def location(value) + if value + "(#{value.start_offset}...#{value.end_offset}) = #{value.slice.inspect}" + else + "∅" + end + end + + # Generates a string that represents a child node. + def child_node(node, append) + node.inspect(child_inspector(append)).delete_prefix(prefix) + end + + # Returns a new inspector that can be used to inspect a child node. + def child_inspector(append) + NodeInspector.new("#{prefix}#{append}") + end + + # Returns the output as a string. + def to_str + output + end + end + class FloatNode < Node def value Float(slice) end end @@ -462,14 +518,14 @@ # order here so that we can compare properly. if params sorted = [ *params.requireds.grep(RequiredParameterNode).map(&:name), *params.optionals.map(&:name), - *((params.rest.name ? params.rest.name.to_sym : :*) if params.rest && params.rest.operator != ","), + *((params.rest.name || :*) if params.rest && params.rest.operator != ","), *params.posts.grep(RequiredParameterNode).map(&:name), - *params.keywords.reject(&:value).map { |param| param.name.chomp(":").to_sym }, - *params.keywords.select(&:value).map { |param| param.name.chomp(":").to_sym } + *params.keywords.reject(&:value).map(&:name), + *params.keywords.select(&:value).map(&:name) ] # TODO: When we get a ... parameter, we should be pushing * and & # onto the local list. We don't do that yet, so we need to add them # in here. @@ -531,9 +587,13 @@ require_relative "yarp/desugar_visitor" require_relative "yarp/node" require_relative "yarp/ripper_compat" require_relative "yarp/serialize" require_relative "yarp/pack" +require_relative "yarp/pattern" + +require_relative "yarp/parse_result/comments" +require_relative "yarp/parse_result/newlines" if RUBY_ENGINE == "ruby" and !ENV["YARP_FFI_BACKEND"] require "yarp/yarp" else require_relative "yarp/ffi"