fables-tales / rubyfmt

Ruby Autoformatter!
MIT License
1.07k stars 50 forks source link

Ruby tree deserialization error on valid programme #448

Open jamesrweb opened 7 months ago

jamesrweb commented 7 months ago

Ruby executes the code perfectly fine and outputs the correct values but rubyfmt cannot format the document.

Input file

# @param [String] filename
#
# @return [Array<String>]
def read_lines_from_file(filename)
  File.open(filename, 'r', chomp: true)
end

# @param [Hash<Integer>] folder_sizes
# @param [Array<String>] stack
# @param [String] line
#
# @return [Array<Hash<Integer>, Array<String>>]
def parse_instruction(folder_sizes, stack, line)
  if line in ['$', 'cd', '..']
    stack.pop
  elsif line in ['$', 'cd', folder]
    stack.push folder
  elsif line in [size, _] and size.match?(/^\d+$/)
    stack.reduce('') do |previous_path, current_path|
      path_key = previous_path + current_path

      folder_sizes[path_key] += size.to_i

      path_key
    end
  end

  [folder_sizes, stack]
end

# @param [Array<Hash<Integer>, Array<String>>] accumulator
# @param [String] line
#
# @return [Array<Hash<Integer>, Array<String>>]
def instruction_accumulator(accumulator, line)
  folder_sizes, stack = accumulator

  parse_instruction(folder_sizes, stack, line)
end

# @param [Array<String>] instructions
#
# @return [Hash<Integer>]
def folder_sizes_from_instructions(instructions)
  instructions
    .map(&:split)
    .reduce([Hash.new(0), []], &method(:instruction_accumulator))
    .shift
end

# @param [Hash<Integer>] folder_sizes
#
# @return [Integer]
def solve_part_one(folder_sizes)
  size_limit = 100_000

  folder_sizes.values.select { |folder_size| folder_size <= size_limit }.sum
end

# @param [Hash<Integer>] folder_sizes
#
# @return [Integer]
def solve_part_two(folder_sizes)
  total_disk_space = 70_000_000
  minimum_unused_space_requirement = 30_000_000
  difference = total_disk_space - minimum_unused_space_requirement

  folder_sizes
    .values
    .reject { |folder_size| folder_size < folder_sizes['/'] - difference }
    .min
end

def main
  instructions = read_lines_from_file(__dir__ + '/input.txt')
  folder_sizes = folder_sizes_from_instructions(instructions)

  puts 'Part one: ' + solve_part_one(folder_sizes).to_s
  puts 'Part two: ' + solve_part_two(folder_sizes).to_s
end

main

Rubyfmt's output

Error! source: <redacted>/index.rb

!!! Ruby Tree Deserialization Error !!!

Rubyfmt failed to correctly deserialize a tree from ripper. This is a bug that needs to be reported.
File a bug report at https://github.com/penelopezone/rubyfmt/issues/new.
Ideally you would include the full source code of the program you ran rubyfmt with.
If you can't do that for some reason, the best thing you can do is rerun rubyfmt on this program
with the debug binary with `2>log_file` on the end and then send us the log file that gets generated.

This breaks because

Rubyfmt failed to correctly deserialize a tree from ripper. (What is ripper even?)

reese commented 7 months ago

I believe this is a dup of #447

jamesrweb commented 7 months ago

I believe this is a dup of #447

Actually no, I believe this is similar but different in that, it fails on THIS file specifically but not on 2 others with similar constructs in place. Ruby runs all 3 perfectly fine.

If you want to mark it as a duplicate, you can, but it is still unique in my case to this code sample specifically.

reese commented 7 months ago

What do you mean by "with similar constructs"? My point in calling this a duplicate is that all the individual expression types represented by this file are marked as bugs in other places: #447 for and expressions and #399/#325 for pattern matching. If there are additional constructs in those other files you're referencing, then this may be distinct, but I'd need some additional information there.

jamesrweb commented 7 months ago

What do you mean by "with similar constructs"? My point in calling this a duplicate is that all the individual expression types represented by this file are marked as bugs in other places: #447 for and expressions and #399/#325 for pattern matching. If there are additional constructs in those other files you're referencing, then this may be distinct, but I'd need some additional information there.

I mean the way to load and parse the files and output is roughly similar in those other files.

As I said, up to you, I appreciate the tool either way, just strange how it failed for this one. ☺️

pjg commented 7 months ago

Side question, are you considering switching to prism from ripper for ruby parsing?

reese commented 7 months ago

Switching to prism would be great (largely because right now we have to have a whole Ruby checkout just to build Ripper, and just having prism on its own would make getting development way more lightweight), but it's also pretty far down on the priority list at the moment compared to getting things feature-complete for Ruby 3, fixing bugs, etc.

jamesrweb commented 2 months ago

@reese what's the current plan for this? Is there a roadmap for prism or anything else we could use to resolve the issue?