seeing_is_believing 1.0.1 → 2.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Readme.md +24 -3
 - data/features/errors.feature +2 -2
 - data/features/examples.feature +9 -9
 - data/features/flags.feature +12 -10
 - data/features/regression.feature +17 -3
 - data/lib/seeing_is_believing.rb +23 -106
 - data/lib/seeing_is_believing/binary/add_annotations.rb +3 -3
 - data/lib/seeing_is_believing/program_rewriter.rb +280 -0
 - data/lib/seeing_is_believing/remove_inline_comments.rb +1 -1
 - data/lib/seeing_is_believing/result.rb +15 -5
 - data/lib/seeing_is_believing/syntax_analyzer.rb +0 -155
 - data/lib/seeing_is_believing/version.rb +1 -1
 - data/seeing_is_believing.gemspec +1 -1
 - data/spec/program_rewriter_spec.rb +687 -0
 - data/spec/seeing_is_believing_spec.rb +66 -114
 - data/spec/syntax_analyzer_spec.rb +3 -316
 - metadata +10 -10
 - data/lib/seeing_is_believing/expression_list.rb +0 -101
 - data/spec/expression_list_spec.rb +0 -277
 
    
        data/Readme.md
    CHANGED
    
    | 
         @@ -141,16 +141,37 @@ Known Issues 
     | 
|
| 
       141 
141 
     | 
    
         
             
            ============
         
     | 
| 
       142 
142 
     | 
    
         | 
| 
       143 
143 
     | 
    
         
             
            * `BEGIN/END` breaks things and I probably won't take the time to fix it, becuase it's nontrivial and its really meant for command-line scripts, but there is currently a cuke for it
         
     | 
| 
       144 
     | 
    
         
            -
            * Heredocs aren't recorded. It might actually be possible if the ExpressionList were to get smarter
         
     | 
| 
       145 
144 
     | 
    
         | 
| 
       146 
145 
     | 
    
         
             
            Todo
         
     | 
| 
       147 
146 
     | 
    
         
             
            ====
         
     | 
| 
       148 
147 
     | 
    
         | 
| 
       149 
     | 
    
         
            -
            * Make a Lines class which is a collection of lines, responsible for managing the trailing newlines in Binary::PrintResultsNextToLines and SeeingIsBelieving/ExpressionList
         
     | 
| 
       150 
148 
     | 
    
         
             
            * Add examples of invocations to the help screen
         
     | 
| 
       151 
149 
     | 
    
         
             
            * Add xmpfilter option to sublime text
         
     | 
| 
       152 
150 
     | 
    
         
             
            * Update TextMate examples to use same keys as sublime, add xmpfilter option on cmd+opt+N
         
     | 
| 
       153 
     | 
    
         
            -
            *  
     | 
| 
      
 151 
     | 
    
         
            +
            * Remove the SyntaxAnalyzer altogether!
         
     | 
| 
      
 152 
     | 
    
         
            +
            * How about if begin/rescue/end was able to record the result on the rescue section
         
     | 
| 
      
 153 
     | 
    
         
            +
            * Check how begin/rescue/end with multiple rescue blocks works
         
     | 
| 
      
 154 
     | 
    
         
            +
            * What about recording the result of a line inside of a string interpolation, e.g. "a#{\n1\n}b" could record line 2 is 1 and line 3 is "a\n1\nb"
         
     | 
| 
      
 155 
     | 
    
         
            +
            * Be able to clean an invalid file (used to be able to do this, but parser can't identify comments in an invalid file the way that I'm currently using it, cuke is still there, marked as @not-implemented)
         
     | 
| 
      
 156 
     | 
    
         
            +
            * Add a flag to allow you to just get the results so that it can be easily used without a Ruby runtime
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
            Up Next
         
     | 
| 
      
 159 
     | 
    
         
            +
            =======
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
            rename ProgramRewriter -> WrapExpressions
         
     | 
| 
      
 162 
     | 
    
         
            +
            find that fucking regex bug
         
     | 
| 
      
 163 
     | 
    
         
            +
            add the inspected result --debug output
         
     | 
| 
      
 164 
     | 
    
         
            +
            Fix high-level shit:
         
     | 
| 
      
 165 
     | 
    
         
            +
              remove all previous output except if -x flag is set, then leave `# =>`
         
     | 
| 
      
 166 
     | 
    
         
            +
              `gsub(/\s*$/, '')`
         
     | 
| 
      
 167 
     | 
    
         
            +
              run it through sib
         
     | 
| 
      
 168 
     | 
    
         
            +
              if -x flag is set
         
     | 
| 
      
 169 
     | 
    
         
            +
                for each comment
         
     | 
| 
      
 170 
     | 
    
         
            +
                  if it is `# =>`, update it
         
     | 
| 
      
 171 
     | 
    
         
            +
              else
         
     | 
| 
      
 172 
     | 
    
         
            +
                printable_list = get a list of each line that is an actual end and has no comments with sib (can ignore heredocs b/c they will have no value)
         
     | 
| 
      
 173 
     | 
    
         
            +
                                 this list is like line_number => [col_number, character_number] (e.g. where to insert on that line)
         
     | 
| 
      
 174 
     | 
    
         
            +
                for each line, add it at that character location, adjusting from the col_number to pad it correctly
         
     | 
| 
       154 
175 
     | 
    
         | 
| 
       155 
176 
     | 
    
         | 
| 
       156 
177 
     | 
    
         
             
            License
         
     | 
    
        data/features/errors.feature
    CHANGED
    
    | 
         @@ -29,11 +29,11 @@ Feature: Running the binary unsuccessfully 
     | 
|
| 
       29 
29 
     | 
    
         
             
                """
         
     | 
| 
       30 
30 
     | 
    
         
             
                def first_defined
         
     | 
| 
       31 
31 
     | 
    
         
             
                  second_defined
         
     | 
| 
       32 
     | 
    
         
            -
                end 
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                def second_defined
         
     | 
| 
       35 
35 
     | 
    
         
             
                  require_relative 'raises_exception'  # ~> RuntimeError: ZOMG\n!!!!
         
     | 
| 
       36 
     | 
    
         
            -
                end 
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                first_defined
         
     | 
| 
       39 
39 
     | 
    
         | 
    
        data/features/examples.feature
    CHANGED
    
    | 
         @@ -64,14 +64,14 @@ Feature: Running the binary successfully 
     | 
|
| 
       64 
64 
     | 
    
         
             
                And stdout is:
         
     | 
| 
       65 
65 
     | 
    
         
             
                """
         
     | 
| 
       66 
66 
     | 
    
         
             
                # iteration
         
     | 
| 
       67 
     | 
    
         
            -
                5.times do |i|
         
     | 
| 
      
 67 
     | 
    
         
            +
                5.times do |i|  # => 5
         
     | 
| 
       68 
68 
     | 
    
         
             
                  i * 2         # => 0, 2, 4, 6, 8
         
     | 
| 
       69 
69 
     | 
    
         
             
                end             # => 5
         
     | 
| 
       70 
70 
     | 
    
         | 
| 
       71 
71 
     | 
    
         
             
                # method and invocations
         
     | 
| 
       72 
72 
     | 
    
         
             
                def meth(n)
         
     | 
| 
       73 
73 
     | 
    
         
             
                  n          # => "12", "34"
         
     | 
| 
       74 
     | 
    
         
            -
                end 
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
       75 
75 
     | 
    
         | 
| 
       76 
76 
     | 
    
         
             
                meth "12"  # => "12"
         
     | 
| 
       77 
77 
     | 
    
         
             
                meth "34"  # => "34"
         
     | 
| 
         @@ -89,25 +89,25 @@ Feature: Running the binary successfully 
     | 
|
| 
       89 
89 
     | 
    
         
             
                 b/x  # => /a\n b/x
         
     | 
| 
       90 
90 
     | 
    
         | 
| 
       91 
91 
     | 
    
         
             
                # don't record heredocs b/c they're just too fucking different
         
     | 
| 
       92 
     | 
    
         
            -
                <<HERE
         
     | 
| 
      
 92 
     | 
    
         
            +
                <<HERE    # => "is a doc\n"
         
     | 
| 
       93 
93 
     | 
    
         
             
                is a doc
         
     | 
| 
       94 
94 
     | 
    
         
             
                HERE
         
     | 
| 
       95 
95 
     | 
    
         | 
| 
       96 
96 
     | 
    
         
             
                # method invocation that occurs entirely on the next line
         
     | 
| 
       97 
     | 
    
         
            -
                [*1..10]
         
     | 
| 
       98 
     | 
    
         
            -
                  .select(&:even?)
         
     | 
| 
      
 97 
     | 
    
         
            +
                [*1..10]              # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
         
     | 
| 
      
 98 
     | 
    
         
            +
                  .select(&:even?)    # => [2, 4, 6, 8, 10]
         
     | 
| 
       99 
99 
     | 
    
         
             
                  .map { |n| n * 2 }  # => [4, 8, 12, 16, 20]
         
     | 
| 
       100 
100 
     | 
    
         | 
| 
       101 
101 
     | 
    
         
             
                # mutliple levels of nesting
         
     | 
| 
       102 
102 
     | 
    
         
             
                class User
         
     | 
| 
       103 
103 
     | 
    
         
             
                  def initialize(name)
         
     | 
| 
       104 
104 
     | 
    
         
             
                    @name = name        # => "Josh", "Rick"
         
     | 
| 
       105 
     | 
    
         
            -
                  end 
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
       106 
106 
     | 
    
         | 
| 
       107 
107 
     | 
    
         
             
                  def name
         
     | 
| 
       108 
108 
     | 
    
         
             
                    @name   # => "Josh", "Rick"
         
     | 
| 
       109 
     | 
    
         
            -
                  end 
     | 
| 
       110 
     | 
    
         
            -
                end 
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
       111 
111 
     | 
    
         | 
| 
       112 
112 
     | 
    
         
             
                User.new("Josh").name  # => "Josh"
         
     | 
| 
       113 
113 
     | 
    
         
             
                User.new("Rick").name  # => "Rick"
         
     | 
| 
         @@ -187,7 +187,7 @@ Feature: Running the binary successfully 
     | 
|
| 
       187 
187 
     | 
    
         
             
                __LINE__            # => 2
         
     | 
| 
       188 
188 
     | 
    
         
             
                $stdout.puts "omg"  # => nil
         
     | 
| 
       189 
189 
     | 
    
         
             
                $stderr.puts "hi"   # => nil
         
     | 
| 
       190 
     | 
    
         
            -
                DATA.read           # => "1\n2"
         
     | 
| 
      
 190 
     | 
    
         
            +
                DATA.read           # => "1\n2\n"
         
     | 
| 
       191 
191 
     | 
    
         
             
                __LINE__            # => 6
         
     | 
| 
       192 
192 
     | 
    
         | 
| 
       193 
193 
     | 
    
         
             
                # >> omg
         
     | 
    
        data/features/flags.feature
    CHANGED
    
    | 
         @@ -459,6 +459,8 @@ Feature: Using flags 
     | 
|
| 
       459 
459 
     | 
    
         
             
                2+2    # => 10
         
     | 
| 
       460 
460 
     | 
    
         
             
                "a
         
     | 
| 
       461 
461 
     | 
    
         
             
                 b" # =>
         
     | 
| 
      
 462 
     | 
    
         
            +
                /a
         
     | 
| 
      
 463 
     | 
    
         
            +
                 b/ # =>
         
     | 
| 
       462 
464 
     | 
    
         
             
                1
         
     | 
| 
       463 
465 
     | 
    
         
             
                "omg"
         
     | 
| 
       464 
466 
     | 
    
         
             
                # =>
         
     | 
| 
         @@ -474,6 +476,8 @@ Feature: Using flags 
     | 
|
| 
       474 
476 
     | 
    
         
             
                2+2    # => 4
         
     | 
| 
       475 
477 
     | 
    
         
             
                "a
         
     | 
| 
       476 
478 
     | 
    
         
             
                 b" # => "a\n b"
         
     | 
| 
      
 479 
     | 
    
         
            +
                /a
         
     | 
| 
      
 480 
     | 
    
         
            +
                 b/ # => /a\n b/
         
     | 
| 
       477 
481 
     | 
    
         
             
                1
         
     | 
| 
       478 
482 
     | 
    
         
             
                "omg"
         
     | 
| 
       479 
483 
     | 
    
         
             
                # => "omg"
         
     | 
| 
         @@ -481,6 +485,7 @@ Feature: Using flags 
     | 
|
| 
       481 
485 
     | 
    
         
             
                # => "omg2"
         
     | 
| 
       482 
486 
     | 
    
         
             
                """
         
     | 
| 
       483 
487 
     | 
    
         | 
| 
      
 488 
     | 
    
         
            +
              # TODO: do we want to print the source without its comments?
         
     | 
| 
       484 
489 
     | 
    
         
             
              Scenario: --debug
         
     | 
| 
       485 
490 
     | 
    
         
             
                Given the file "simple_program.rb":
         
     | 
| 
       486 
491 
     | 
    
         
             
                """
         
     | 
| 
         @@ -492,16 +497,13 @@ Feature: Using flags 
     | 
|
| 
       492 
497 
     | 
    
         
             
                Then stderr is empty
         
     | 
| 
       493 
498 
     | 
    
         
             
                And the exit status is 0
         
     | 
| 
       494 
499 
     | 
    
         
             
                # source without comments
         
     | 
| 
       495 
     | 
    
         
            -
                And stdout includes "SOURCE WITHOUT COMMENTS:"
         
     | 
| 
       496 
     | 
    
         
            -
                And stdout includes:
         
     | 
| 
       497 
     | 
    
         
            -
                """
         
     | 
| 
       498 
     | 
    
         
            -
                # encoding: utf-8
         
     | 
| 
       499 
     | 
    
         
            -
                1
         
     | 
| 
       500 
     | 
    
         
            -
                2
         
     | 
| 
       501 
     | 
    
         
            -
                """
         
     | 
| 
       502 
     | 
    
         
            -
                # expression evaluation
         
     | 
| 
       503 
     | 
    
         
            -
                And stdout includes "EXPRESSION EVALUATION:"
         
     | 
| 
       504 
     | 
    
         
            -
                And stdout includes "GENERATED"
         
     | 
| 
      
 500 
     | 
    
         
            +
                # And stdout includes "SOURCE WITHOUT COMMENTS:"
         
     | 
| 
      
 501 
     | 
    
         
            +
                # And stdout includes:
         
     | 
| 
      
 502 
     | 
    
         
            +
                # """
         
     | 
| 
      
 503 
     | 
    
         
            +
                # # encoding: utf-8
         
     | 
| 
      
 504 
     | 
    
         
            +
                # 1
         
     | 
| 
      
 505 
     | 
    
         
            +
                # 2
         
     | 
| 
      
 506 
     | 
    
         
            +
                # """
         
     | 
| 
       505 
507 
     | 
    
         
             
                # translated program
         
     | 
| 
       506 
508 
     | 
    
         
             
                And stdout includes "TRANSLATED PROGRAM:"
         
     | 
| 
       507 
509 
     | 
    
         
             
                And stdout includes "$seeing_is_believing_current_result"
         
     | 
    
        data/features/regression.feature
    CHANGED
    
    | 
         @@ -62,10 +62,10 @@ Feature: 
     | 
|
| 
       62 
62 
     | 
    
         
             
                Then stdout is:
         
     | 
| 
       63 
63 
     | 
    
         
             
                """
         
     | 
| 
       64 
64 
     | 
    
         
             
                def m
         
     | 
| 
       65 
     | 
    
         
            -
                  if true
         
     | 
| 
       66 
     | 
    
         
            -
                    return 1
         
     | 
| 
      
 65 
     | 
    
         
            +
                  if true     # => true
         
     | 
| 
      
 66 
     | 
    
         
            +
                    return 1  # => 1
         
     | 
| 
       67 
67 
     | 
    
         
             
                  end
         
     | 
| 
       68 
     | 
    
         
            -
                end 
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
       69 
69 
     | 
    
         
             
                m             # => 1
         
     | 
| 
       70 
70 
     | 
    
         
             
                """
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
         @@ -163,3 +163,17 @@ Feature: 
     | 
|
| 
       163 
163 
     | 
    
         
             
                # encoding: utf-8
         
     | 
| 
       164 
164 
     | 
    
         
             
                'ç'  # => "ç"
         
     | 
| 
       165 
165 
     | 
    
         
             
                """
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
              @not-implemented
         
     | 
| 
      
 168 
     | 
    
         
            +
              Scenario: Some strings look like comments
         
     | 
| 
      
 169 
     | 
    
         
            +
                Given the file "strings_that_look_like_comments.rb":
         
     | 
| 
      
 170 
     | 
    
         
            +
                """
         
     | 
| 
      
 171 
     | 
    
         
            +
                "1
         
     | 
| 
      
 172 
     | 
    
         
            +
                 #{2}"
         
     | 
| 
      
 173 
     | 
    
         
            +
                """
         
     | 
| 
      
 174 
     | 
    
         
            +
                When I run "seeing_is_believing strings_that_look_like_comments.rb"
         
     | 
| 
      
 175 
     | 
    
         
            +
                Then stdout is:
         
     | 
| 
      
 176 
     | 
    
         
            +
                """
         
     | 
| 
      
 177 
     | 
    
         
            +
                "1
         
     | 
| 
      
 178 
     | 
    
         
            +
                 #{2}"  # => "1\n 2"
         
     | 
| 
      
 179 
     | 
    
         
            +
                """
         
     | 
    
        data/lib/seeing_is_believing.rb
    CHANGED
    
    | 
         @@ -6,9 +6,10 @@ require 'seeing_is_believing/queue' 
     | 
|
| 
       6 
6 
     | 
    
         
             
            require 'seeing_is_believing/result'
         
     | 
| 
       7 
7 
     | 
    
         
             
            require 'seeing_is_believing/version'
         
     | 
| 
       8 
8 
     | 
    
         
             
            require 'seeing_is_believing/debugger'
         
     | 
| 
       9 
     | 
    
         
            -
            require 'seeing_is_believing/expression_list'
         
     | 
| 
       10 
9 
     | 
    
         
             
            require 'seeing_is_believing/remove_inline_comments'
         
     | 
| 
       11 
10 
     | 
    
         
             
            require 'seeing_is_believing/evaluate_by_moving_files'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'seeing_is_believing/program_rewriter'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'seeing_is_believing/syntax_analyzer' # can we get rid of this?
         
     | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
14 
     | 
    
         
             
            # might not work on windows b/c of assumptions about line ends
         
     | 
| 
       14 
15 
     | 
    
         
             
            class SeeingIsBelieving
         
     | 
| 
         @@ -20,109 +21,50 @@ class SeeingIsBelieving 
     | 
|
| 
       20 
21 
     | 
    
         
             
              end
         
     | 
| 
       21 
22 
     | 
    
         | 
| 
       22 
23 
     | 
    
         
             
              def initialize(program, options={})
         
     | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
       24 
     | 
    
         
            -
                @stream          = to_stream program_string
         
     | 
| 
      
 24 
     | 
    
         
            +
                @program         = program
         
     | 
| 
       25 
25 
     | 
    
         
             
                @matrix_filename = options[:matrix_filename]
         
     | 
| 
       26 
26 
     | 
    
         
             
                @filename        = options[:filename]
         
     | 
| 
       27 
27 
     | 
    
         
             
                @stdin           = to_stream options.fetch(:stdin, '')
         
     | 
| 
       28 
28 
     | 
    
         
             
                @require         = options.fetch :require, []
         
     | 
| 
       29 
29 
     | 
    
         
             
                @load_path       = options.fetch :load_path, []
         
     | 
| 
       30 
30 
     | 
    
         
             
                @encoding        = options.fetch :encoding, nil
         
     | 
| 
       31 
     | 
    
         
            -
                @line_number     = 1
         
     | 
| 
       32 
31 
     | 
    
         
             
                @timeout         = options[:timeout]
         
     | 
| 
       33 
32 
     | 
    
         
             
                @debugger        = options.fetch :debugger, Debugger.new(enabled: false)
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                debugger.context("SOURCE WITHOUT COMMENTS") { program_string }
         
     | 
| 
       36 
33 
     | 
    
         
             
              end
         
     | 
| 
       37 
34 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
              # I'd like to refactor this, but I was unsatisfied with the three different things I tried.
         
     | 
| 
       39 
     | 
    
         
            -
              # In the end, I prefer keeping all manipulation of the line number here in the main function
         
     | 
| 
       40 
     | 
    
         
            -
              # And I like that the higher-level construct of how the program gets built can be found here.
         
     | 
| 
       41 
35 
     | 
    
         
             
              def call
         
     | 
| 
       42 
36 
     | 
    
         
             
                @memoized_result ||= begin
         
     | 
| 
       43 
     | 
    
         
            -
                   
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                    leading_comments << lines
         
     | 
| 
       61 
     | 
    
         
            -
                  end
         
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
                  # extract program body
         
     | 
| 
       64 
     | 
    
         
            -
                  body = ''
         
     | 
| 
       65 
     | 
    
         
            -
                  until next_line_queue.empty? || data_segment?
         
     | 
| 
       66 
     | 
    
         
            -
                    expression, expression_size = expression_list.call
         
     | 
| 
       67 
     | 
    
         
            -
                    body << expression
         
     | 
| 
       68 
     | 
    
         
            -
                    track_line_number @line_number
         
     | 
| 
       69 
     | 
    
         
            -
                    @line_number += expression_size
         
     | 
| 
       70 
     | 
    
         
            -
                  end
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                  # extract data segment
         
     | 
| 
       73 
     | 
    
         
            -
                  data_segment = ''
         
     | 
| 
       74 
     | 
    
         
            -
                  data_segment = "\n#{the_rest_of_the_stream}" if data_segment?
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                  # build the program
         
     | 
| 
       77 
     | 
    
         
            -
                  program = leading_comments << record_exceptions_in(body) << data_segment
         
     | 
| 
       78 
     | 
    
         
            -
                  debugger.context("TRANSLATED PROGRAM") { program }
         
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
                  # return the result
         
     | 
| 
       81 
     | 
    
         
            -
                  result_for program, max_line_number
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # must use newline after code, or comments will comment out rescue section
         
     | 
| 
      
 38 
     | 
    
         
            +
                  # FIXME: IS THIS STILL TRUE?
         
     | 
| 
      
 39 
     | 
    
         
            +
                  wrapped = ProgramReWriter.call "#@program\n",
         
     | 
| 
      
 40 
     | 
    
         
            +
                                                 before_all:  "begin;",
         
     | 
| 
      
 41 
     | 
    
         
            +
                                                 after_all:   "\n"\
         
     | 
| 
      
 42 
     | 
    
         
            +
                                                              "rescue Exception;"\
         
     | 
| 
      
 43 
     | 
    
         
            +
                                                                "line_number = $!.backtrace.grep(/\#{__FILE__}/).first[/:\\d+/][1..-1].to_i;"\
         
     | 
| 
      
 44 
     | 
    
         
            +
                                                                "$seeing_is_believing_current_result.record_exception line_number, $!;"\
         
     | 
| 
      
 45 
     | 
    
         
            +
                                                                "$seeing_is_believing_current_result.exitstatus = 1;"\
         
     | 
| 
      
 46 
     | 
    
         
            +
                                                                "$seeing_is_believing_current_result.exitstatus = $!.status if $!.kind_of? SystemExit;"\
         
     | 
| 
      
 47 
     | 
    
         
            +
                                                              "end",
         
     | 
| 
      
 48 
     | 
    
         
            +
                                                 before_each: -> line_number { "($seeing_is_believing_current_result.record_result(#{line_number}, (" },
         
     | 
| 
      
 49 
     | 
    
         
            +
                                                 after_each:  -> line_number { ")))" }
         
     | 
| 
      
 50 
     | 
    
         
            +
                  debugger.context("TRANSLATED PROGRAM") { wrapped }
         
     | 
| 
      
 51 
     | 
    
         
            +
                  result = result_for wrapped
         
     | 
| 
      
 52 
     | 
    
         
            +
                  debugger.context("RESULT") { result.inspect }
         
     | 
| 
      
 53 
     | 
    
         
            +
                  result
         
     | 
| 
       82 
54 
     | 
    
         
             
                end
         
     | 
| 
       83 
55 
     | 
    
         
             
              end
         
     | 
| 
       84 
56 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
              private
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
              attr_reader :stream, :matrix_filename, :debugger
         
     | 
| 
       88 
57 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
               
     | 
| 
       90 
     | 
    
         
            -
                @expression_list ||= ExpressionList.new debugger:       debugger,
         
     | 
| 
       91 
     | 
    
         
            -
                                                        get_next_line:  lambda { next_line_queue.dequeue },
         
     | 
| 
       92 
     | 
    
         
            -
                                                        peek_next_line: lambda { next_line_queue.peek },
         
     | 
| 
       93 
     | 
    
         
            -
                                                        on_complete:    lambda { |line, children, completions, offset|
         
     | 
| 
       94 
     | 
    
         
            -
                                                          expression = [line, *children, *completions].map(&:chomp).join("\n")
         
     | 
| 
      
 58 
     | 
    
         
            +
              private
         
     | 
| 
       95 
59 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
                                                            expression + "\n"
         
     | 
| 
       98 
     | 
    
         
            -
                                                          else
         
     | 
| 
       99 
     | 
    
         
            -
                                                            record_yahself(expression, @line_number+offset) + "\n"
         
     | 
| 
       100 
     | 
    
         
            -
                                                          end
         
     | 
| 
       101 
     | 
    
         
            -
                                                        }
         
     | 
| 
       102 
     | 
    
         
            -
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
              attr_reader :matrix_filename, :debugger
         
     | 
| 
       103 
61 
     | 
    
         | 
| 
       104 
62 
     | 
    
         
             
              def to_stream(string_or_stream)
         
     | 
| 
       105 
63 
     | 
    
         
             
                return string_or_stream if string_or_stream.respond_to? :gets
         
     | 
| 
       106 
64 
     | 
    
         
             
                StringIO.new string_or_stream
         
     | 
| 
       107 
65 
     | 
    
         
             
              end
         
     | 
| 
       108 
66 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
              def  
     | 
| 
       110 
     | 
    
         
            -
                "($seeing_is_believing_current_result.record_result(#{line_number}, (#{line})))"
         
     | 
| 
       111 
     | 
    
         
            -
              end
         
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
              def record_exceptions_in(code)
         
     | 
| 
       114 
     | 
    
         
            -
                # must use newline after code, or comments will comment out rescue section
         
     | 
| 
       115 
     | 
    
         
            -
                "begin;"\
         
     | 
| 
       116 
     | 
    
         
            -
                  "#{code}\n"\
         
     | 
| 
       117 
     | 
    
         
            -
                "rescue Exception;"\
         
     | 
| 
       118 
     | 
    
         
            -
                  "line_number = $!.backtrace.grep(/\#{__FILE__}/).first[/:\\d+/][1..-1].to_i;"\
         
     | 
| 
       119 
     | 
    
         
            -
                  "$seeing_is_believing_current_result.record_exception line_number, $!;"\
         
     | 
| 
       120 
     | 
    
         
            -
                  "$seeing_is_believing_current_result.exitstatus = 1;"\
         
     | 
| 
       121 
     | 
    
         
            -
                  "$seeing_is_believing_current_result.exitstatus = $!.status if $!.kind_of? SystemExit;"\
         
     | 
| 
       122 
     | 
    
         
            -
                "end"
         
     | 
| 
       123 
     | 
    
         
            -
              end
         
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
              def result_for(program, max_line_number)
         
     | 
| 
      
 67 
     | 
    
         
            +
              def result_for(program)
         
     | 
| 
       126 
68 
     | 
    
         
             
                Dir.mktmpdir "seeing_is_believing_temp_dir" do |dir|
         
     | 
| 
       127 
69 
     | 
    
         
             
                  filename = @filename || File.join(dir, 'program.rb')
         
     | 
| 
       128 
70 
     | 
    
         
             
                  EvaluateByMovingFiles.new(program,
         
     | 
| 
         @@ -134,31 +76,6 @@ class SeeingIsBelieving 
     | 
|
| 
       134 
76 
     | 
    
         
             
                                            encoding:        @encoding,
         
     | 
| 
       135 
77 
     | 
    
         
             
                                            timeout:         @timeout)
         
     | 
| 
       136 
78 
     | 
    
         
             
                                       .call
         
     | 
| 
       137 
     | 
    
         
            -
                                       .track_line_number(max_line_number)
         
     | 
| 
       138 
79 
     | 
    
         
             
                end
         
     | 
| 
       139 
80 
     | 
    
         
             
              end
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
              def eof?
         
     | 
| 
       142 
     | 
    
         
            -
                next_line_queue.peek.nil?
         
     | 
| 
       143 
     | 
    
         
            -
              end
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
              def data_segment?
         
     | 
| 
       146 
     | 
    
         
            -
                SyntaxAnalyzer.begins_data_segment?(next_line_queue.peek)
         
     | 
| 
       147 
     | 
    
         
            -
              end
         
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
              def next_line_queue
         
     | 
| 
       150 
     | 
    
         
            -
                @next_line_queue ||= Queue.new { (line = stream.gets) && line.chomp }
         
     | 
| 
       151 
     | 
    
         
            -
              end
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
              # eh? -.^ (can't we pull the rest of this out of the queue instead of breaking encapsulation?)
         
     | 
| 
       154 
     | 
    
         
            -
              def the_rest_of_the_stream
         
     | 
| 
       155 
     | 
    
         
            -
                next_line_queue.dequeue << "\n" << stream.read
         
     | 
| 
       156 
     | 
    
         
            -
              end
         
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
              def do_not_record?(code)
         
     | 
| 
       159 
     | 
    
         
            -
                code =~ BLANK_REGEX                           ||
         
     | 
| 
       160 
     | 
    
         
            -
                  SyntaxAnalyzer.ends_in_comment?(code)       ||
         
     | 
| 
       161 
     | 
    
         
            -
                  SyntaxAnalyzer.void_value_expression?(code) ||
         
     | 
| 
       162 
     | 
    
         
            -
                  SyntaxAnalyzer.here_doc?(code)
         
     | 
| 
       163 
     | 
    
         
            -
              end
         
     | 
| 
       164 
81 
     | 
    
         
             
            end
         
     | 
| 
         @@ -89,9 +89,9 @@ class SeeingIsBelieving 
     | 
|
| 
       89 
89 
     | 
    
         
             
                  def add_line(line, line_number)
         
     | 
| 
       90 
90 
     | 
    
         
             
                    should_record = should_record? line, line_number
         
     | 
| 
       91 
91 
     | 
    
         
             
                    if should_record && xmpfilter_style && line.strip =~ /^# =>/
         
     | 
| 
       92 
     | 
    
         
            -
                      new_body << xmpfilter_update(line, file_result[line_number - 1])
         
     | 
| 
      
 92 
     | 
    
         
            +
                      new_body << xmpfilter_update(line, file_result[line_number - 1]) # There is probably a bug in this since it doesn't go through the LineFormatter it can probably be to long
         
     | 
| 
       93 
93 
     | 
    
         
             
                    elsif should_record && xmpfilter_style
         
     | 
| 
       94 
     | 
    
         
            -
                      new_body << xmpfilter_update(line, file_result[line_number])
         
     | 
| 
      
 94 
     | 
    
         
            +
                      new_body << xmpfilter_update(line, file_result[line_number]) # There is probably a bug in this since it doesn't go through the LineFormatter it can probably be to long
         
     | 
| 
       95 
95 
     | 
    
         
             
                    elsif should_record
         
     | 
| 
       96 
96 
     | 
    
         
             
                      new_body << format_line(line.chomp, line_number, file_result[line_number])
         
     | 
| 
       97 
97 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -108,7 +108,7 @@ class SeeingIsBelieving 
     | 
|
| 
       108 
108 
     | 
    
         | 
| 
       109 
109 
     | 
    
         
             
                  # again, this is too naive, should actually parse the comments and update them
         
     | 
| 
       110 
110 
     | 
    
         
             
                  def xmpfilter_update(line, line_results)
         
     | 
| 
       111 
     | 
    
         
            -
                    line.gsub /# =>.*?$/, "# => #{line_results.join ', '}"
         
     | 
| 
      
 111 
     | 
    
         
            +
                    line.gsub /# =>.*?$/, "# => #{line_results.join(', ').gsub("\n", '\n')}"
         
     | 
| 
       112 
112 
     | 
    
         
             
                  end
         
     | 
| 
       113 
113 
     | 
    
         | 
| 
       114 
114 
     | 
    
         
             
                  def add_stdout
         
     | 
| 
         @@ -0,0 +1,280 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'parser/current'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # hack rewriter to apply insertions in stable order
         
     | 
| 
      
 4 
     | 
    
         
            +
            # until https://github.com/whitequark/parser/pull/102 gets merged in
         
     | 
| 
      
 5 
     | 
    
         
            +
            module Parser
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Source
         
     | 
| 
      
 7 
     | 
    
         
            +
                class Rewriter
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def process
         
     | 
| 
      
 9 
     | 
    
         
            +
                    adjustment   = 0
         
     | 
| 
      
 10 
     | 
    
         
            +
                    source       = @source_buffer.source.dup
         
     | 
| 
      
 11 
     | 
    
         
            +
                    sorted_queue = @queue.sort_by.with_index do |action, index|
         
     | 
| 
      
 12 
     | 
    
         
            +
                      [action.range.begin_pos, index]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
                    sorted_queue.each do |action|
         
     | 
| 
      
 15 
     | 
    
         
            +
                      begin_pos = action.range.begin_pos + adjustment
         
     | 
| 
      
 16 
     | 
    
         
            +
                      end_pos   = begin_pos + action.range.length
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                      source[begin_pos...end_pos] = action.replacement
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                      adjustment += (action.replacement.length - action.range.length)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    source
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            # comprehensive list of syntaxes that can come up
         
     | 
| 
      
 31 
     | 
    
         
            +
            # https://github.com/whitequark/parser/blob/master/doc/AST_FORMAT.md
         
     | 
| 
      
 32 
     | 
    
         
            +
            class SeeingIsBelieving
         
     | 
| 
      
 33 
     | 
    
         
            +
              class ProgramReWriter
         
     | 
| 
      
 34 
     | 
    
         
            +
                def self.call(program, wrappings)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  new(program, wrappings).call
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def initialize(program, wrappings)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  self.program     = program
         
     | 
| 
      
 40 
     | 
    
         
            +
                  self.before_all  = wrappings.fetch :before_all,  ''.freeze
         
     | 
| 
      
 41 
     | 
    
         
            +
                  self.after_all   = wrappings.fetch :after_all,   ''.freeze
         
     | 
| 
      
 42 
     | 
    
         
            +
                  self.before_each = wrappings.fetch :before_each, -> * { '' }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  self.after_each  = wrappings.fetch :after_each,  -> * { '' }
         
     | 
| 
      
 44 
     | 
    
         
            +
                  self.buffer      = Parser::Source::Buffer.new('program-without-annotations')
         
     | 
| 
      
 45 
     | 
    
         
            +
                  buffer.source    = program
         
     | 
| 
      
 46 
     | 
    
         
            +
                  self.root        = Parser::CurrentRuby.new.parse buffer
         
     | 
| 
      
 47 
     | 
    
         
            +
                  self.rewriter    = Parser::Source::Rewriter.new buffer
         
     | 
| 
      
 48 
     | 
    
         
            +
                  self.wrappings   = {}
         
     | 
| 
      
 49 
     | 
    
         
            +
                rescue Parser::SyntaxError => e
         
     | 
| 
      
 50 
     | 
    
         
            +
                  raise ::SyntaxError, e.message
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def call
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @called ||= begin
         
     | 
| 
      
 55 
     | 
    
         
            +
                    find_wrappings
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    if root # file may be empty
         
     | 
| 
      
 58 
     | 
    
         
            +
                      rewriter.insert_before root.location.expression, before_all
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                      wrappings.each do |line_num, (range, last_col)|
         
     | 
| 
      
 61 
     | 
    
         
            +
                        rewriter.insert_before range, before_each.call(line_num)
         
     | 
| 
      
 62 
     | 
    
         
            +
                        rewriter.insert_after  range, after_each.call(line_num)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                      rewriter.insert_after root.location.expression, after_all
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    rewriter.process
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                private
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                attr_accessor :program, :before_all, :after_all, :before_each, :after_each, :buffer, :root, :rewriter, :wrappings
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def add_to_wrappings(range_or_ast)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  range = range_or_ast
         
     | 
| 
      
 78 
     | 
    
         
            +
                  range = range_or_ast.location.expression if range.kind_of? ::AST::Node
         
     | 
| 
      
 79 
     | 
    
         
            +
                  line, col = buffer.decompose_position range.end_pos
         
     | 
| 
      
 80 
     | 
    
         
            +
                  _, prev_col = wrappings[line]
         
     | 
| 
      
 81 
     | 
    
         
            +
                  wrappings[line] = (!wrappings[line] || prev_col < col ? [range, col] : wrappings[line] )
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def add_children(ast, omit_first = false)
         
     | 
| 
      
 85 
     | 
    
         
            +
                  (omit_first ? ast.children.drop(1) : ast.children)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    .each { |child| find_wrappings child }
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                def find_wrappings(ast=root)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  return wrappings unless ast.kind_of? ::AST::Node
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  case ast.type
         
     | 
| 
      
 93 
     | 
    
         
            +
                  when :args, :redo, :retry, :alias, :undef, :splat, :match_current_line
         
     | 
| 
      
 94 
     | 
    
         
            +
                    # no op
         
     | 
| 
      
 95 
     | 
    
         
            +
                  when :rescue, :ensure, :def, :return, :break, :next
         
     | 
| 
      
 96 
     | 
    
         
            +
                    add_children ast
         
     | 
| 
      
 97 
     | 
    
         
            +
                  when :if
         
     | 
| 
      
 98 
     | 
    
         
            +
                    if ast.location.kind_of? Parser::Source::Map::Ternary
         
     | 
| 
      
 99 
     | 
    
         
            +
                      add_to_wrappings ast unless ast.children.any? { |child| void_value? child }
         
     | 
| 
      
 100 
     | 
    
         
            +
                      add_children ast
         
     | 
| 
      
 101 
     | 
    
         
            +
                    else
         
     | 
| 
      
 102 
     | 
    
         
            +
                      keyword = ast.location.keyword.source
         
     | 
| 
      
 103 
     | 
    
         
            +
                      if (keyword == 'if' || keyword == 'unless') && ast.children.none? { |child| void_value? child }
         
     | 
| 
      
 104 
     | 
    
         
            +
                        add_to_wrappings ast
         
     | 
| 
      
 105 
     | 
    
         
            +
                      end
         
     | 
| 
      
 106 
     | 
    
         
            +
                      add_children ast
         
     | 
| 
      
 107 
     | 
    
         
            +
                    end
         
     | 
| 
      
 108 
     | 
    
         
            +
                  when :when, :pair, :defs, :class, :module, :sclass
         
     | 
| 
      
 109 
     | 
    
         
            +
                    find_wrappings ast.children.last
         
     | 
| 
      
 110 
     | 
    
         
            +
                  when :resbody
         
     | 
| 
      
 111 
     | 
    
         
            +
                    exception_type, variable_name, body = ast.children
         
     | 
| 
      
 112 
     | 
    
         
            +
                    find_wrappings body
         
     | 
| 
      
 113 
     | 
    
         
            +
                  when :block
         
     | 
| 
      
 114 
     | 
    
         
            +
                    add_to_wrappings ast
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                    # a {} comes in as
         
     | 
| 
      
 117 
     | 
    
         
            +
                    #   (block
         
     | 
| 
      
 118 
     | 
    
         
            +
                    #     (send nil :a)
         
     | 
| 
      
 119 
     | 
    
         
            +
                    #     (args) nil)
         
     | 
| 
      
 120 
     | 
    
         
            +
                    #
         
     | 
| 
      
 121 
     | 
    
         
            +
                    # a.b {} comes in as
         
     | 
| 
      
 122 
     | 
    
         
            +
                    #   (block
         
     | 
| 
      
 123 
     | 
    
         
            +
                    #     (send
         
     | 
| 
      
 124 
     | 
    
         
            +
                    #       (send nil :a) :b)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    #     (args) nil)
         
     | 
| 
      
 126 
     | 
    
         
            +
                    #
         
     | 
| 
      
 127 
     | 
    
         
            +
                    # we don't want to wrap the send itself, otherwise could come in as <a>{}
         
     | 
| 
      
 128 
     | 
    
         
            +
                    # but we do want ot wrap its first child so that we can get <<a>\n.b{}>
         
     | 
| 
      
 129 
     | 
    
         
            +
                    #
         
     | 
| 
      
 130 
     | 
    
         
            +
                    # I can't think of anything other than a :send that could be the first child
         
     | 
| 
      
 131 
     | 
    
         
            +
                    # but I'll check for it anyway.
         
     | 
| 
      
 132 
     | 
    
         
            +
                    the_send = ast.children[0]
         
     | 
| 
      
 133 
     | 
    
         
            +
                    find_wrappings the_send.children.first if the_send.type == :send
         
     | 
| 
      
 134 
     | 
    
         
            +
                    add_children ast, true
         
     | 
| 
      
 135 
     | 
    
         
            +
                  when :masgn
         
     | 
| 
      
 136 
     | 
    
         
            +
                    # we must look at RHS because [1,<<A] and 1,<<A are both allowed
         
     | 
| 
      
 137 
     | 
    
         
            +
                    #
         
     | 
| 
      
 138 
     | 
    
         
            +
                    # in the first case, we must take the end_pos of the array, or we'll insert the after_each in the wrong location
         
     | 
| 
      
 139 
     | 
    
         
            +
                    #
         
     | 
| 
      
 140 
     | 
    
         
            +
                    # in the second, there is an implicit Array wrapped around it, with the wrong end_pos,
         
     | 
| 
      
 141 
     | 
    
         
            +
                    # so we must take the end_pos of the last arg
         
     | 
| 
      
 142 
     | 
    
         
            +
                    array = ast.children.last
         
     | 
| 
      
 143 
     | 
    
         
            +
                    if array.location.expression.source.start_with? '['
         
     | 
| 
      
 144 
     | 
    
         
            +
                      add_to_wrappings ast
         
     | 
| 
      
 145 
     | 
    
         
            +
                      find_wrappings array
         
     | 
| 
      
 146 
     | 
    
         
            +
                    else
         
     | 
| 
      
 147 
     | 
    
         
            +
                      begin_pos = ast.location.expression.begin_pos
         
     | 
| 
      
 148 
     | 
    
         
            +
                      end_pos   = heredoc_hack(ast.children.last.children.last).location.expression.end_pos
         
     | 
| 
      
 149 
     | 
    
         
            +
                      range     = Parser::Source::Range.new buffer, begin_pos, end_pos
         
     | 
| 
      
 150 
     | 
    
         
            +
                      add_to_wrappings range
         
     | 
| 
      
 151 
     | 
    
         
            +
                      add_children ast.children.last
         
     | 
| 
      
 152 
     | 
    
         
            +
                    end
         
     | 
| 
      
 153 
     | 
    
         
            +
                  when :lvasgn
         
     | 
| 
      
 154 
     | 
    
         
            +
                    # because the RHS can be a heredoc, and parser currently handles heredocs locations incorrectly
         
     | 
| 
      
 155 
     | 
    
         
            +
                    # we must hack around this
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                    # can have one or two children:
         
     | 
| 
      
 158 
     | 
    
         
            +
                    #   in a=1 (has children :a, and 1)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    #   in a,b=1,2 it comes out like:
         
     | 
| 
      
 160 
     | 
    
         
            +
                    #     (masgn
         
     | 
| 
      
 161 
     | 
    
         
            +
                    #       (mlhs
         
     | 
| 
      
 162 
     | 
    
         
            +
                    #         (lvasgn :a) <-- one child
         
     | 
| 
      
 163 
     | 
    
         
            +
                    #
         
     | 
| 
      
 164 
     | 
    
         
            +
                    #         (lvasgn :b))
         
     | 
| 
      
 165 
     | 
    
         
            +
                    #       (array
         
     | 
| 
      
 166 
     | 
    
         
            +
                    #         (int 1)
         
     | 
| 
      
 167 
     | 
    
         
            +
                    #         (int 2)))
         
     | 
| 
      
 168 
     | 
    
         
            +
                    if ast.children.size == 2
         
     | 
| 
      
 169 
     | 
    
         
            +
                      begin_pos = ast.location.expression.begin_pos
         
     | 
| 
      
 170 
     | 
    
         
            +
                      end_pos   = heredoc_hack(ast.children.last).location.expression.end_pos
         
     | 
| 
      
 171 
     | 
    
         
            +
                      range     = Parser::Source::Range.new buffer, begin_pos, end_pos
         
     | 
| 
      
 172 
     | 
    
         
            +
                      add_to_wrappings range
         
     | 
| 
      
 173 
     | 
    
         
            +
                      add_children ast
         
     | 
| 
      
 174 
     | 
    
         
            +
                    end
         
     | 
| 
      
 175 
     | 
    
         
            +
                  when :send
         
     | 
| 
      
 176 
     | 
    
         
            +
                    # because the target and the last child can be heredocs
         
     | 
| 
      
 177 
     | 
    
         
            +
                    # and the method may or may not have parens,
         
     | 
| 
      
 178 
     | 
    
         
            +
                    # it can inadvertently inherit the incorrect location of the heredocs
         
     | 
| 
      
 179 
     | 
    
         
            +
                    # so we check for this case, that way we can construct the correct range instead
         
     | 
| 
      
 180 
     | 
    
         
            +
                    range = ast.location.expression
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                    # first two children: target, message, so we want the last child only if it is an argument
         
     | 
| 
      
 183 
     | 
    
         
            +
                    target, message, *, last_arg = ast.children
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                    # last arg is a heredoc, use the closing paren, or the end of the first line of the heredoc
         
     | 
| 
      
 186 
     | 
    
         
            +
                    if heredoc? last_arg
         
     | 
| 
      
 187 
     | 
    
         
            +
                      end_pos = heredoc_hack(last_arg).location.expression.end_pos
         
     | 
| 
      
 188 
     | 
    
         
            +
                      if buffer.source[ast.location.selector.end_pos] == '('
         
     | 
| 
      
 189 
     | 
    
         
            +
                        end_pos += 1 until buffer.source[end_pos] == ')'
         
     | 
| 
      
 190 
     | 
    
         
            +
                        end_pos += 1
         
     | 
| 
      
 191 
     | 
    
         
            +
                      end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                    # the last arg is not a heredoc, the range of the expression can be trusted
         
     | 
| 
      
 194 
     | 
    
         
            +
                    elsif last_arg
         
     | 
| 
      
 195 
     | 
    
         
            +
                      end_pos = ast.location.expression.end_pos
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                    # there is no last arg, but there are parens, find the closing paren
         
     | 
| 
      
 198 
     | 
    
         
            +
                    # we can't trust the expression range because the *target* could be a heredoc
         
     | 
| 
      
 199 
     | 
    
         
            +
                    # FIXME: This blows up on 2.0 with ->{}.() because it has no selector, so in this case
         
     | 
| 
      
 200 
     | 
    
         
            +
                    #        we would want to use the expression, but I'm ignoring that for now, because
         
     | 
| 
      
 201 
     | 
    
         
            +
                    #        we would have to check the target to see whether to use the selector or the expression
         
     | 
| 
      
 202 
     | 
    
         
            +
                    elsif buffer.source[ast.location.selector.end_pos] == '('
         
     | 
| 
      
 203 
     | 
    
         
            +
                      closing_paren_index = ast.location.selector.end_pos + 1
         
     | 
| 
      
 204 
     | 
    
         
            +
                      closing_paren_index += 1 until buffer.source[closing_paren_index] == ')'
         
     | 
| 
      
 205 
     | 
    
         
            +
                      end_pos = closing_paren_index + 1
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
                    # use the selector because we can't trust expression since target can be a heredoc
         
     | 
| 
      
 208 
     | 
    
         
            +
                    elsif heredoc? target
         
     | 
| 
      
 209 
     | 
    
         
            +
                      end_pos = ast.location.selector.end_pos
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                    # use the expression because it could be something like !1, in which case the selector would return the rhs of the !
         
     | 
| 
      
 212 
     | 
    
         
            +
                    else
         
     | 
| 
      
 213 
     | 
    
         
            +
                      end_pos = ast.location.expression.end_pos
         
     | 
| 
      
 214 
     | 
    
         
            +
                    end
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
                    begin_pos = ast.location.expression.begin_pos
         
     | 
| 
      
 217 
     | 
    
         
            +
                    range     = Parser::Source::Range.new(buffer, begin_pos, end_pos)
         
     | 
| 
      
 218 
     | 
    
         
            +
                    add_to_wrappings range
         
     | 
| 
      
 219 
     | 
    
         
            +
                    add_children ast
         
     | 
| 
      
 220 
     | 
    
         
            +
                  when :begin
         
     | 
| 
      
 221 
     | 
    
         
            +
                    last_child = ast.children.last
         
     | 
| 
      
 222 
     | 
    
         
            +
                    if heredoc? last_child
         
     | 
| 
      
 223 
     | 
    
         
            +
                      range = Parser::Source::Range.new buffer,
         
     | 
| 
      
 224 
     | 
    
         
            +
                                                        ast.location.expression.begin_pos,
         
     | 
| 
      
 225 
     | 
    
         
            +
                                                        heredoc_hack(last_child).location.expression.end_pos
         
     | 
| 
      
 226 
     | 
    
         
            +
                      add_to_wrappings range unless void_value? ast.children.last
         
     | 
| 
      
 227 
     | 
    
         
            +
                    end
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                    add_children ast
         
     | 
| 
      
 230 
     | 
    
         
            +
                  when :str, :dstr, :xstr, :regexp
         
     | 
| 
      
 231 
     | 
    
         
            +
                    add_to_wrappings heredoc_hack ast
         
     | 
| 
      
 232 
     | 
    
         
            +
                  else
         
     | 
| 
      
 233 
     | 
    
         
            +
                    add_to_wrappings ast
         
     | 
| 
      
 234 
     | 
    
         
            +
                    add_children ast
         
     | 
| 
      
 235 
     | 
    
         
            +
                  end
         
     | 
| 
      
 236 
     | 
    
         
            +
                rescue
         
     | 
| 
      
 237 
     | 
    
         
            +
                  # TODO: delete this rescue block once things get stabler
         
     | 
| 
      
 238 
     | 
    
         
            +
                  puts ast.type
         
     | 
| 
      
 239 
     | 
    
         
            +
                  puts $!
         
     | 
| 
      
 240 
     | 
    
         
            +
                  require "pry"
         
     | 
| 
      
 241 
     | 
    
         
            +
                  binding.pry
         
     | 
| 
      
 242 
     | 
    
         
            +
                end
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
                def heredoc_hack(ast)
         
     | 
| 
      
 245 
     | 
    
         
            +
                  return ast unless heredoc? ast
         
     | 
| 
      
 246 
     | 
    
         
            +
                  Parser::AST::Node.new :str,
         
     | 
| 
      
 247 
     | 
    
         
            +
                                        [],
         
     | 
| 
      
 248 
     | 
    
         
            +
                                        location: Parser::Source::Map.new(ast.location.begin)
         
     | 
| 
      
 249 
     | 
    
         
            +
                end
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
                # this is the scardest fucking method I think I've ever written.
         
     | 
| 
      
 252 
     | 
    
         
            +
                # *anything* can go wrong!
         
     | 
| 
      
 253 
     | 
    
         
            +
                def heredoc?(ast)
         
     | 
| 
      
 254 
     | 
    
         
            +
                  # some strings are fucking weird.
         
     | 
| 
      
 255 
     | 
    
         
            +
                  # e.g. the "1" in `%w[1]` returns nil for ast.location.begin
         
     | 
| 
      
 256 
     | 
    
         
            +
                  # and `__FILE__` is a string whose location is a Parser::Source::Map instead of a Parser::Source::Map::Collection, so it has no #begin
         
     | 
| 
      
 257 
     | 
    
         
            +
                  ast.kind_of?(Parser::AST::Node)           &&
         
     | 
| 
      
 258 
     | 
    
         
            +
                    (ast.type == :dstr || ast.type == :str) &&
         
     | 
| 
      
 259 
     | 
    
         
            +
                    (location  = ast.location)              &&
         
     | 
| 
      
 260 
     | 
    
         
            +
                    (location.respond_to?(:begin))          &&
         
     | 
| 
      
 261 
     | 
    
         
            +
                    (the_begin = location.begin)            &&
         
     | 
| 
      
 262 
     | 
    
         
            +
                    (the_begin.source =~ /^\<\<-?/)
         
     | 
| 
      
 263 
     | 
    
         
            +
                end
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                def void_value?(ast)
         
     | 
| 
      
 266 
     | 
    
         
            +
                  case ast && ast.type
         
     | 
| 
      
 267 
     | 
    
         
            +
                  when :begin, :kwbegin, :resbody
         
     | 
| 
      
 268 
     | 
    
         
            +
                    void_value?(ast.children[-1])
         
     | 
| 
      
 269 
     | 
    
         
            +
                  when :rescue, :ensure
         
     | 
| 
      
 270 
     | 
    
         
            +
                    ast.children.any? { |child| void_value? child }
         
     | 
| 
      
 271 
     | 
    
         
            +
                  when :if
         
     | 
| 
      
 272 
     | 
    
         
            +
                    void_value?(ast.children[1]) || void_value?(ast.children[2])
         
     | 
| 
      
 273 
     | 
    
         
            +
                  when :return, :next, :redo, :retry, :break
         
     | 
| 
      
 274 
     | 
    
         
            +
                    true
         
     | 
| 
      
 275 
     | 
    
         
            +
                  else
         
     | 
| 
      
 276 
     | 
    
         
            +
                    false
         
     | 
| 
      
 277 
     | 
    
         
            +
                  end
         
     | 
| 
      
 278 
     | 
    
         
            +
                end
         
     | 
| 
      
 279 
     | 
    
         
            +
              end
         
     | 
| 
      
 280 
     | 
    
         
            +
            end
         
     |