seeing_is_believing 1.0.1 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|