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
metadata
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seeing_is_believing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version:
|
4
|
+
prerelease: 6
|
5
|
+
version: 2.0.0.beta1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Josh Cheek
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-08-
|
12
|
+
date: 2013-08-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
version_requirements: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 2.0.0.pre6
|
20
20
|
none: false
|
@@ -23,7 +23,7 @@ dependencies:
|
|
23
23
|
prerelease: false
|
24
24
|
requirement: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ~>
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: 2.0.0.pre6
|
29
29
|
none: false
|
@@ -140,10 +140,10 @@ files:
|
|
140
140
|
- lib/seeing_is_believing/debugger.rb
|
141
141
|
- lib/seeing_is_believing/error.rb
|
142
142
|
- lib/seeing_is_believing/evaluate_by_moving_files.rb
|
143
|
-
- lib/seeing_is_believing/expression_list.rb
|
144
143
|
- lib/seeing_is_believing/hard_core_ensure.rb
|
145
144
|
- lib/seeing_is_believing/has_exception.rb
|
146
145
|
- lib/seeing_is_believing/line.rb
|
146
|
+
- lib/seeing_is_believing/program_rewriter.rb
|
147
147
|
- lib/seeing_is_believing/queue.rb
|
148
148
|
- lib/seeing_is_believing/remove_inline_comments.rb
|
149
149
|
- lib/seeing_is_believing/result.rb
|
@@ -157,9 +157,9 @@ files:
|
|
157
157
|
- spec/binary/remove_previous_annotations_spec.rb
|
158
158
|
- spec/debugger_spec.rb
|
159
159
|
- spec/evaluate_by_moving_files_spec.rb
|
160
|
-
- spec/expression_list_spec.rb
|
161
160
|
- spec/hard_core_ensure_spec.rb
|
162
161
|
- spec/line_spec.rb
|
162
|
+
- spec/program_rewriter_spec.rb
|
163
163
|
- spec/queue_spec.rb
|
164
164
|
- spec/seeing_is_believing_spec.rb
|
165
165
|
- spec/syntax_analyzer_spec.rb
|
@@ -179,9 +179,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
179
179
|
none: false
|
180
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
181
|
requirements:
|
182
|
-
- - ! '
|
182
|
+
- - ! '>'
|
183
183
|
- !ruby/object:Gem::Version
|
184
|
-
version:
|
184
|
+
version: 1.3.1
|
185
185
|
none: false
|
186
186
|
requirements: []
|
187
187
|
rubyforge_project: seeing_is_believing
|
@@ -200,9 +200,9 @@ test_files:
|
|
200
200
|
- spec/binary/remove_previous_annotations_spec.rb
|
201
201
|
- spec/debugger_spec.rb
|
202
202
|
- spec/evaluate_by_moving_files_spec.rb
|
203
|
-
- spec/expression_list_spec.rb
|
204
203
|
- spec/hard_core_ensure_spec.rb
|
205
204
|
- spec/line_spec.rb
|
205
|
+
- spec/program_rewriter_spec.rb
|
206
206
|
- spec/queue_spec.rb
|
207
207
|
- spec/seeing_is_believing_spec.rb
|
208
208
|
- spec/syntax_analyzer_spec.rb
|
@@ -1,101 +0,0 @@
|
|
1
|
-
require 'open3'
|
2
|
-
require 'seeing_is_believing/debugger'
|
3
|
-
require 'seeing_is_believing/syntax_analyzer'
|
4
|
-
|
5
|
-
# can we get better debugging support so that we don't need to drop ANSI escape sequences in the middle of strings?
|
6
|
-
class SeeingIsBelieving
|
7
|
-
class ExpressionList
|
8
|
-
PendingExpression = Struct.new :expression, :children do
|
9
|
-
# coloured debug because there's so much syntax that I get lost trying to parse the output
|
10
|
-
def inspect(debug=false)
|
11
|
-
colour1 = colour2 = lambda { |s| s }
|
12
|
-
colour1 = lambda { |s| "\e[30;46m#{s}\e[0m" } if debug
|
13
|
-
colour2 = lambda { |s| "\e[37;46m#{s}\e[0m" } if debug
|
14
|
-
"#{colour1['PE(']}#{colour2[expression.inspect]}#{colour1[',' ]}#{colour2[children.inspect]}#{colour1[')']}"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def initialize(options)
|
19
|
-
self.debugger = options.fetch :debugger, Debugger.new(enabled: false)
|
20
|
-
self.get_next_line = options.fetch :get_next_line
|
21
|
-
self.peek_next_line = options.fetch :peek_next_line
|
22
|
-
self.on_complete = options.fetch :on_complete
|
23
|
-
end
|
24
|
-
|
25
|
-
def call
|
26
|
-
offset, expressions, expression = 0, [], nil
|
27
|
-
begin
|
28
|
-
pending_expression = generate(expressions)
|
29
|
-
|
30
|
-
debugger.context debugger_context do
|
31
|
-
"GENERATED: #{pending_expression.expression.inspect}, ADDING IT TO #{inspected_expressions expressions}"
|
32
|
-
end
|
33
|
-
|
34
|
-
expression = reduce expressions, offset unless next_line_modifies_current?
|
35
|
-
|
36
|
-
offset += 1
|
37
|
-
end until expressions.empty?
|
38
|
-
return expression, offset
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
attr_accessor :debugger, :get_next_line, :peek_next_line, :on_complete, :expressions
|
44
|
-
|
45
|
-
def debugger_context
|
46
|
-
"EXPRESSION EVALUATION"
|
47
|
-
end
|
48
|
-
|
49
|
-
def generate(expressions)
|
50
|
-
expression = get_next_line.call
|
51
|
-
raise SyntaxError unless expression
|
52
|
-
if expressions.last && SyntaxAnalyzer.unfinished_here_doc?(expressions.last.expression)
|
53
|
-
expressions.last.expression << "\n" << expression # more stupid \n -.-
|
54
|
-
else
|
55
|
-
expressions << PendingExpression.new(expression, [])
|
56
|
-
end
|
57
|
-
expressions.last
|
58
|
-
end
|
59
|
-
|
60
|
-
def next_line_modifies_current?
|
61
|
-
# method invocations can be put on the next line, and begin with a dot.
|
62
|
-
# I think that's the only case we need to worry about.
|
63
|
-
# e.g: `3\n.times { |i| p i }`
|
64
|
-
peek_next_line.call && SyntaxAnalyzer.next_line_modifies_current?(peek_next_line.call)
|
65
|
-
end
|
66
|
-
|
67
|
-
def inspected_expressions(expressions)
|
68
|
-
"[#{expressions.map { |pe| pe.inspect debugger.enabled? }.join(', ')}]"
|
69
|
-
end
|
70
|
-
|
71
|
-
def reduce(expressions, offset)
|
72
|
-
expressions.size.times do |i|
|
73
|
-
expression = expressions[i..-1].map { |e| [e.expression, *e.children] }
|
74
|
-
.flatten
|
75
|
-
.join("\n") # must use newline otherwise can get expressions like `a\\+b` that should be `a\\\n+b`, former is invalid
|
76
|
-
return if children_will_never_be_valid? expression
|
77
|
-
next unless valid_ruby? expression
|
78
|
-
result = on_complete.call(expressions[i].expression,
|
79
|
-
expressions[i].children,
|
80
|
-
expressions[i+1..-1].map { |pe| [pe.expression, pe.children] }.flatten, # hmmm, not sure this is really correct, but it allows it to work for my use cases
|
81
|
-
offset)
|
82
|
-
expressions.replace expressions[0, i]
|
83
|
-
expressions[i-1].children << result unless expressions.empty?
|
84
|
-
debugger.context(debugger_context) { "REDUCED: #{result.inspect}, LIST: #{inspected_expressions expressions}" }
|
85
|
-
return result
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def valid_ruby?(expression)
|
90
|
-
valid = SyntaxAnalyzer.valid_ruby? expression
|
91
|
-
debugger.context(debugger_context) { "#{valid ? "\e[32mIS VALID:" : "\e[31mIS NOT VALID:"}: #{expression.inspect}\e[0m" }
|
92
|
-
valid
|
93
|
-
end
|
94
|
-
|
95
|
-
def children_will_never_be_valid?(expression)
|
96
|
-
analyzer = SyntaxAnalyzer.new(expression)
|
97
|
-
analyzer.parse
|
98
|
-
analyzer.unclosed_string? || analyzer.unclosed_regexp? || SyntaxAnalyzer.unclosed_comment?(expression)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
@@ -1,277 +0,0 @@
|
|
1
|
-
require 'seeing_is_believing/expression_list'
|
2
|
-
|
3
|
-
describe SeeingIsBelieving::ExpressionList do
|
4
|
-
|
5
|
-
def list_for(generations, options={}, &block)
|
6
|
-
described_class.new({
|
7
|
-
on_complete: block,
|
8
|
-
get_next_line: -> { generations.shift || raise("EMPTY!") },
|
9
|
-
peek_next_line: -> { generations.first },
|
10
|
-
}.merge(options))
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(generations, options={}, &block)
|
14
|
-
list_for(generations, options, &block).call
|
15
|
-
end
|
16
|
-
|
17
|
-
example 'example: multiple children' do
|
18
|
-
block_invocations = 0
|
19
|
-
result, size = call %w[begin b+ c x\\ + y end] do |line, children, completions, offset|
|
20
|
-
case offset
|
21
|
-
when 2
|
22
|
-
line.should == 'b+'
|
23
|
-
children.should == []
|
24
|
-
completions.should == ['c']
|
25
|
-
block_invocations += 1
|
26
|
-
'b+c'
|
27
|
-
when 5
|
28
|
-
line.should == 'x\\'
|
29
|
-
children.should == []
|
30
|
-
completions.should == ['+', 'y']
|
31
|
-
block_invocations += 10
|
32
|
-
"x+y"
|
33
|
-
when 6
|
34
|
-
line.should == 'begin'
|
35
|
-
children.should == ['b+c', 'x+y']
|
36
|
-
completions.should == ['end']
|
37
|
-
block_invocations += 100
|
38
|
-
'ALL DONE!'
|
39
|
-
else
|
40
|
-
raise "offset: #{offset.inspect}"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
result.should == 'ALL DONE!'
|
44
|
-
size.should == 7
|
45
|
-
block_invocations.should == 111
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
example 'example: nested children' do
|
50
|
-
block_invocations = 0
|
51
|
-
expressions = [ '[1].map do |n1|',
|
52
|
-
' [2].map do |n2|',
|
53
|
-
' n1 + n2',
|
54
|
-
' end',
|
55
|
-
'end',
|
56
|
-
]
|
57
|
-
result, size = call expressions do |line, children, completions, offset|
|
58
|
-
case offset
|
59
|
-
when 2
|
60
|
-
[line, children, completions].should == [' n1 + n2', [], []]
|
61
|
-
block_invocations += 1
|
62
|
-
when 3
|
63
|
-
[line, children, completions].should == [' [2].map do |n2|', [' n1 + n2'], [' end']]
|
64
|
-
block_invocations += 10
|
65
|
-
when 4
|
66
|
-
[line, children, completions].should == ['[1].map do |n1|',
|
67
|
-
[" [2].map do |n2|\n n1 + n2\n end"],
|
68
|
-
['end']]
|
69
|
-
block_invocations += 100
|
70
|
-
else
|
71
|
-
raise "line_number: #{line_number.inspect}"
|
72
|
-
end
|
73
|
-
[line, *children, *completions].join("\n")
|
74
|
-
end
|
75
|
-
block_invocations.should == 111
|
76
|
-
result.should == "[1].map do |n1|\n"\
|
77
|
-
" [2].map do |n2|\n"\
|
78
|
-
" n1 + n2\n"\
|
79
|
-
" end\n"\
|
80
|
-
"end"
|
81
|
-
size.should == 5
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
|
-
example 'example: completions that have children' do
|
86
|
-
block_invocations = 0
|
87
|
-
expressions = [ "[1].map do |n1|",
|
88
|
-
"[2].map do |n2|",
|
89
|
-
"n1 + n2",
|
90
|
-
"end end",
|
91
|
-
]
|
92
|
-
result, size = call expressions do |line, children, completions, offset|
|
93
|
-
case offset
|
94
|
-
when 2
|
95
|
-
[line, children, completions].should == ["n1 + n2", [], []]
|
96
|
-
block_invocations += 1
|
97
|
-
when 3
|
98
|
-
# not really sure what this *should* be like, but if this is the result,
|
99
|
-
# then it will work for the use cases I need it for
|
100
|
-
[line, *children, *completions].should == ["[1].map do |n1|",
|
101
|
-
"[2].map do |n2|",
|
102
|
-
"n1 + n2",
|
103
|
-
'end end']
|
104
|
-
block_invocations += 10
|
105
|
-
else
|
106
|
-
raise "offset: #{offset.inspect}"
|
107
|
-
end
|
108
|
-
[line, *children, *completions].join("\n")
|
109
|
-
end
|
110
|
-
block_invocations.should == 11
|
111
|
-
result.should == "[1].map do |n1|\n"\
|
112
|
-
"[2].map do |n2|\n"\
|
113
|
-
"n1 + n2\n"\
|
114
|
-
"end end"
|
115
|
-
size.should == 4
|
116
|
-
end
|
117
|
-
|
118
|
-
example 'example: completions who requires its children to be considered for the expression to be valid' do
|
119
|
-
block_invocations = 0
|
120
|
-
result, size = call ["if true &&", "true", "1", "end"] do |line, children, completions, offset|
|
121
|
-
case offset
|
122
|
-
when 1
|
123
|
-
[line, children, completions].should == ['true', [], []]
|
124
|
-
block_invocations += 1
|
125
|
-
when 2
|
126
|
-
[line, children, completions].should == ['1', [], []]
|
127
|
-
block_invocations += 10
|
128
|
-
when 3
|
129
|
-
[line, children, completions].should == ['if true &&', ['true', '1'], ['end']]
|
130
|
-
block_invocations += 100
|
131
|
-
end
|
132
|
-
[line, *children, *completions].join("\n")
|
133
|
-
end
|
134
|
-
block_invocations.should == 111
|
135
|
-
result.should == "if true &&\ntrue\n1\nend"
|
136
|
-
size.should == 4
|
137
|
-
end
|
138
|
-
|
139
|
-
example 'example: multiline strings with valid code in them' do
|
140
|
-
block_invocations = 0
|
141
|
-
call ["'", "1", "'"] do |*expressions, offset|
|
142
|
-
expressions.join('').should == "'1'"
|
143
|
-
offset.should == 2
|
144
|
-
block_invocations += 1
|
145
|
-
end
|
146
|
-
block_invocations.should == 1
|
147
|
-
end
|
148
|
-
|
149
|
-
example 'example: multiline regexps with valid code in them' do
|
150
|
-
block_invocations = 0
|
151
|
-
call ['/', '1', '/'] do |*expressions, offset|
|
152
|
-
expressions.join('').should == "/1/"
|
153
|
-
offset.should == 2
|
154
|
-
block_invocations += 1
|
155
|
-
end
|
156
|
-
block_invocations.should == 1
|
157
|
-
end
|
158
|
-
|
159
|
-
example "example: =begin/=end comments" do
|
160
|
-
block_invocations = 0
|
161
|
-
call ['=begin', '1', '=end'] do |*expressions, offset|
|
162
|
-
expressions.join('').should == "=begin1=end"
|
163
|
-
offset.should == 2
|
164
|
-
block_invocations += 1
|
165
|
-
end
|
166
|
-
block_invocations.should == 1
|
167
|
-
end
|
168
|
-
|
169
|
-
example "example: heredoc" do
|
170
|
-
pending 'Not sure how to do this, for now just catch it at a higher level' do
|
171
|
-
result, size = call ['strings = [<<A, <<-B]', '1', 'A', '2', ' B'] do |*expressions, offset|
|
172
|
-
offset.should == 1
|
173
|
-
expressions.should == ['strings = [<<A, <<B]']
|
174
|
-
'zomg!'
|
175
|
-
end
|
176
|
-
result.should == "zomg!\n1\nA\n2\n B"
|
177
|
-
size.should == 5
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
example "example: method invocations on next line", not_implemented: true do
|
182
|
-
pending 'I need to *actually* parse the Ruby in order to be able to identify where to split the expressions :('
|
183
|
-
# example 1: consume the expression with lines after
|
184
|
-
list = list_for ['a', '.b', ' .c', 'irrelevant'] do |*expressions, offset|
|
185
|
-
flat_expressions = expressions.flatten.join('')
|
186
|
-
case offset
|
187
|
-
when 0
|
188
|
-
flat_expressions.should == 'a'
|
189
|
-
'A'
|
190
|
-
when 1
|
191
|
-
flat_expressions.should == 'A.b'
|
192
|
-
'A.B'
|
193
|
-
when 2
|
194
|
-
flat_expressions.should == 'A.B .c'
|
195
|
-
'A.B.C'
|
196
|
-
else
|
197
|
-
raise "O.o"
|
198
|
-
end
|
199
|
-
end
|
200
|
-
list.call.should == ['A.B.C', 3]
|
201
|
-
|
202
|
-
# example 2: consume the expression with no lines after
|
203
|
-
list = list_for ['a', '.b'] do |*expressions, offset|
|
204
|
-
flat_expressions = expressions.flatten.join('')
|
205
|
-
case offset
|
206
|
-
when 0
|
207
|
-
flat_expressions.should == 'a'
|
208
|
-
'A'
|
209
|
-
when 1
|
210
|
-
flat_expressions.should == 'A.b'
|
211
|
-
'A.B'
|
212
|
-
else
|
213
|
-
raise "O.o"
|
214
|
-
end
|
215
|
-
end
|
216
|
-
list.call.should == ['A.B', 2]
|
217
|
-
end
|
218
|
-
|
219
|
-
example "example: method invocations on next line with the first line not being its own expression", not_implemented: true do
|
220
|
-
pending 'I need to *actually* parse the Ruby in order to be able to identify where to split the expressions :('
|
221
|
-
list = list_for ['x = a', '.b', ' .c', 'irrelevant'] do |*expressions, offset|
|
222
|
-
flat_expressions = expressions.flatten.join('')
|
223
|
-
case offset
|
224
|
-
when 0
|
225
|
-
flat_expressions.should == 'a'
|
226
|
-
'A'
|
227
|
-
when 1
|
228
|
-
flat_expressions.should == 'A.b'
|
229
|
-
'A.B'
|
230
|
-
when 2
|
231
|
-
flat_expressions.should == 'x = A.B .c'
|
232
|
-
'X=A.B.C'
|
233
|
-
else
|
234
|
-
raise "O.o"
|
235
|
-
end
|
236
|
-
end
|
237
|
-
list.call.should == ['X=A.B.C', 3]
|
238
|
-
|
239
|
-
list = list_for ['puts a', '.b', ' .c', 'irrelevant'] do |*expressions, offset|
|
240
|
-
flat_expressions = expressions.flatten.join('')
|
241
|
-
case offset
|
242
|
-
when 0
|
243
|
-
flat_expressions.should == 'a'
|
244
|
-
'A'
|
245
|
-
when 1
|
246
|
-
flat_expressions.should == 'A.b'
|
247
|
-
'A.B'
|
248
|
-
when 2
|
249
|
-
flat_expressions.should == 'puts A.B .c'
|
250
|
-
'PUTS A.B.C'
|
251
|
-
else
|
252
|
-
raise "O.o"
|
253
|
-
end
|
254
|
-
end
|
255
|
-
list.call.should == ['PUTS A.B.C', 3]
|
256
|
-
end
|
257
|
-
|
258
|
-
example "example: smoke test debug option" do
|
259
|
-
debugger = SeeingIsBelieving::Debugger.new
|
260
|
-
call(%w[a+ b], debugger: debugger) { |*expressions, _| expressions.join("\n") }
|
261
|
-
debugger.to_s.should include "GENERATED"
|
262
|
-
debugger.to_s.should include "REDUCED"
|
263
|
-
end
|
264
|
-
|
265
|
-
# in reality, the problem may just lie with our lib
|
266
|
-
# but it should be correct in most cases
|
267
|
-
it 'Raises a syntax error if it cannot generate the expression' do
|
268
|
-
generations = ["'"]
|
269
|
-
expect do
|
270
|
-
described_class.new(
|
271
|
-
on_complete: -> { "" },
|
272
|
-
get_next_line: -> { generations.shift },
|
273
|
-
peek_next_line: -> { generations.first }
|
274
|
-
).call
|
275
|
-
end.to raise_error SyntaxError
|
276
|
-
end
|
277
|
-
end
|