rVM 0.0.10 → 0.0.11
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/lib/rake/helpers/code_statistics.rb +167 -0
- data/lib/rvm/classes.rb +4 -0
- data/lib/rvm/classes/string.rb +1 -0
- data/lib/rvm/functions/array.rb +3 -0
- data/lib/rvm/functions/array/set_at.rb +0 -4
- data/lib/rvm/functions/collection/size.rb +2 -2
- data/lib/rvm/functions/logic/and.rb +1 -1
- data/lib/rvm/functions/logic/or.rb +2 -2
- data/lib/rvm/interpreter.rb +84 -44
- data/lib/rvm/languages/ecma.rb +581 -501
- data/spec/base/class_spec.rb +27 -0
- data/spec/base/function_spec.rb +25 -0
- data/spec/base/interpreter/assignment_spec.rb +22 -0
- data/spec/base/interpreter/condition_spec.rb +47 -0
- data/spec/base/interpreter/constant_spec.rb +31 -0
- data/spec/base/interpreter/enviroment_spec.rb +51 -0
- data/spec/base/interpreter/function_call_spec.rb +72 -0
- data/spec/base/interpreter/interpreter_spec.rb +11 -0
- data/spec/base/interpreter/parameter_spec.rb +24 -0
- data/spec/base/interpreter/sequence_spec.rb +20 -0
- data/spec/base/interpreter/variable_spec.rb +24 -0
- data/spec/base/plugin_spec.rb +10 -0
- data/spec/classes/atom/association_spec.rb +39 -0
- data/spec/classes/atom/block_spec.rb +27 -0
- data/spec/classes/atom/boolean_spec.rb +67 -0
- data/spec/classes/atom/error_spec.rb +43 -0
- data/spec/classes/atom/list_spec.rb +68 -0
- data/spec/classes/atom/number_spec.rb +132 -0
- data/spec/classes/atom/string_spec.rb +175 -0
- data/spec/functions/association/assoc_get_spec.rb +41 -0
- data/spec/functions/association/assoc_set_spec.rb +43 -0
- data/spec/functions/collection/get_spec.rb +12 -0
- data/spec/functions/collection/set_spec.rb +10 -0
- data/spec/functions/collection/size_spec.rb +10 -0
- data/spec/functions/list/split_spec.rb +47 -0
- data/spec/functions/string/ansi_spec.rb +44 -0
- data/spec/functions/string/capstr_spec.rb +42 -0
- data/spec/functions/string/center_spec.rb +49 -0
- data/spec/functions/string/ljust_spec.rb +49 -0
- data/spec/functions/string/regmatch_spec.rb +52 -0
- data/spec/functions/string/rjust_spec.rb +49 -0
- data/spec/languages/ecma_spec.rb +337 -0
- data/spec/languages/math/compiler_spec.rb +49 -0
- data/spec/languages/math/tokenizer_spec.rb +73 -0
- data/spec/languages/math/tree_spec.rb +153 -0
- metadata +42 -5
@@ -0,0 +1,337 @@
|
|
1
|
+
require File.dirname(__FILE__) +'/../../lib/rvm'
|
2
|
+
require File.dirname(__FILE__) +'/../../lib/rvm/languages'
|
3
|
+
require File.dirname(__FILE__) +'/../../lib/rvm/languages/ecma'
|
4
|
+
|
5
|
+
describe RVM::Languages::ECMA do
|
6
|
+
def exec code
|
7
|
+
@compiler.compile(code).execute(@env)
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@env = RVM::Interpreter.env
|
12
|
+
@compiler = RVM::Languages::ECMA.new
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "literals" do
|
16
|
+
describe "strings" do
|
17
|
+
it "should handle string literals" do
|
18
|
+
res = exec <<-EOC
|
19
|
+
"Hallo!"
|
20
|
+
EOC
|
21
|
+
res.should == "Hallo!"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should handle newline replacements" do
|
25
|
+
res = exec <<-EOC
|
26
|
+
"\n"
|
27
|
+
EOC
|
28
|
+
res.should == "\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should handle newline replacements" do
|
32
|
+
res = exec <<-EOC
|
33
|
+
"\t"
|
34
|
+
EOC
|
35
|
+
res.should == "\t"
|
36
|
+
end
|
37
|
+
it "should handle newline replacements" do
|
38
|
+
res = exec <<-EOC
|
39
|
+
"\n"
|
40
|
+
EOC
|
41
|
+
res.should == "\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should handle double escapes" do
|
45
|
+
res = exec <<-EOC
|
46
|
+
"\\t"
|
47
|
+
EOC
|
48
|
+
res.should == "\\t"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should handle double escapes followed by single escapes" do
|
52
|
+
res = exec <<-EOC
|
53
|
+
"\\\t"
|
54
|
+
EOC
|
55
|
+
res.should == "\\\t"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should handle number literals" do
|
60
|
+
res = exec <<-EOC
|
61
|
+
42
|
62
|
+
EOC
|
63
|
+
res.should == 42
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should handle array literals" do
|
67
|
+
res = exec <<-EOC
|
68
|
+
[42, "Kekse!"]
|
69
|
+
EOC
|
70
|
+
res.should == [42, "Kekse!"]
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should handle true literals" do
|
74
|
+
res = exec <<-EOC
|
75
|
+
true
|
76
|
+
EOC
|
77
|
+
res.should be_is_true
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should handle false literals" do
|
81
|
+
res = exec <<-EOC
|
82
|
+
false
|
83
|
+
EOC
|
84
|
+
res.should_not be_is_true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "array" do
|
89
|
+
|
90
|
+
it "should allow accessing elements" do
|
91
|
+
@env['a'] = RVM::Classes::List.new([1,2,3])
|
92
|
+
res = exec <<-EOC
|
93
|
+
a[0]
|
94
|
+
EOC
|
95
|
+
res.should == 1
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should allow accessing the length" do
|
99
|
+
@env['a'] = RVM::Classes::List.new([1,2,3])
|
100
|
+
res = exec <<-EOC
|
101
|
+
a.length()
|
102
|
+
EOC
|
103
|
+
res.should == 3
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "variables" do
|
109
|
+
it "should recognize simple assignments" do
|
110
|
+
res = exec <<-EOC
|
111
|
+
variable = 'value';
|
112
|
+
EOC
|
113
|
+
res.should == 'value'
|
114
|
+
@env['variable'].val.should == 'value'
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should recognize local assignments" do
|
118
|
+
res = exec <<-EOC
|
119
|
+
var variable = 'value';
|
120
|
+
EOC
|
121
|
+
res.should == 'value'
|
122
|
+
@env['variable'].val.should == 'value'
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should scope local assignments correctly" do
|
126
|
+
res = exec <<-EOC
|
127
|
+
variable = 'outer scope';
|
128
|
+
{
|
129
|
+
var variable = 'inner scope';
|
130
|
+
}
|
131
|
+
EOC
|
132
|
+
res.should == 'inner scope'
|
133
|
+
@env['variable'].val.should == 'outer scope'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe "conditions" do
|
138
|
+
describe "true condition" do
|
139
|
+
describe "without blocks" do
|
140
|
+
|
141
|
+
it "should handle loops without else" do
|
142
|
+
res = exec <<-EOC
|
143
|
+
if (true)
|
144
|
+
42
|
145
|
+
EOC
|
146
|
+
res.should == 42
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should handle loops with else" do
|
150
|
+
res = exec <<-EOC
|
151
|
+
if (true)
|
152
|
+
42
|
153
|
+
else
|
154
|
+
23
|
155
|
+
EOC
|
156
|
+
res.should == 42
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "with blocks" do
|
162
|
+
it "should handle loops without else" do
|
163
|
+
res = exec <<-EOC
|
164
|
+
if (true) {
|
165
|
+
42
|
166
|
+
}
|
167
|
+
EOC
|
168
|
+
res.should == 42
|
169
|
+
end
|
170
|
+
it "should handle loops with else" do
|
171
|
+
res = exec <<-EOC
|
172
|
+
if (true) {
|
173
|
+
42
|
174
|
+
} else {
|
175
|
+
23
|
176
|
+
}
|
177
|
+
EOC
|
178
|
+
res.should == 42
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
describe "false condition" do
|
183
|
+
describe "without blocks" do
|
184
|
+
it "should handle loops without else" do
|
185
|
+
res = exec <<-EOC
|
186
|
+
if (false)
|
187
|
+
42
|
188
|
+
EOC
|
189
|
+
res.should be_nil
|
190
|
+
end
|
191
|
+
it "should handle loops with else" do
|
192
|
+
res = exec <<-EOC
|
193
|
+
if (false)
|
194
|
+
42
|
195
|
+
else
|
196
|
+
23
|
197
|
+
EOC
|
198
|
+
res.should == 23
|
199
|
+
end
|
200
|
+
end
|
201
|
+
describe "with blocks" do
|
202
|
+
it "should handle loops without else" do
|
203
|
+
res = exec <<-EOC
|
204
|
+
if (false) {
|
205
|
+
42
|
206
|
+
}
|
207
|
+
EOC
|
208
|
+
res.should be_nil
|
209
|
+
end
|
210
|
+
it "should handle loops with else" do
|
211
|
+
res = exec <<-EOC
|
212
|
+
if (false) {
|
213
|
+
42
|
214
|
+
} else {
|
215
|
+
23
|
216
|
+
}
|
217
|
+
EOC
|
218
|
+
res.should == 23
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "loops" do
|
225
|
+
it "should handle for loops" do
|
226
|
+
res = exec <<-EOC
|
227
|
+
variable = "";
|
228
|
+
for (i = 0; i < 3; i = i + 1) {
|
229
|
+
variable = variable + i;
|
230
|
+
}
|
231
|
+
EOC
|
232
|
+
res.should == 3
|
233
|
+
@env['variable'].val.should == '012'
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should handle while loops" do
|
237
|
+
res = exec <<-EOC
|
238
|
+
variable = "";
|
239
|
+
i = 0;
|
240
|
+
while (i < 3) {
|
241
|
+
i = i + 1;
|
242
|
+
variable = variable + i;
|
243
|
+
}
|
244
|
+
EOC
|
245
|
+
res.should == '123'
|
246
|
+
@env['variable'].val.should == '123'
|
247
|
+
end
|
248
|
+
|
249
|
+
it "should handle do while loops" do
|
250
|
+
res = exec <<-EOC
|
251
|
+
variable = "";
|
252
|
+
i = 0;
|
253
|
+
do {
|
254
|
+
i = i + 1;
|
255
|
+
variable = variable + i;
|
256
|
+
} while (i < 3)
|
257
|
+
EOC
|
258
|
+
res.should == '123'
|
259
|
+
@env['variable'].val.should == '123'
|
260
|
+
end
|
261
|
+
end
|
262
|
+
it "should execute do while loops at least once" do
|
263
|
+
res = exec <<-EOC
|
264
|
+
variable = "";
|
265
|
+
i = 0;
|
266
|
+
do {
|
267
|
+
i = i + 1;
|
268
|
+
variable = variable + i;
|
269
|
+
} while (i < 0)
|
270
|
+
EOC
|
271
|
+
res.should be_nil
|
272
|
+
@env['variable'].val.should == '1'
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
describe "functions" do
|
278
|
+
it "should handle function definitions without variables" do
|
279
|
+
res = exec <<-EOC
|
280
|
+
function f() {
|
281
|
+
42;
|
282
|
+
}
|
283
|
+
EOC
|
284
|
+
res.class.should == RVM::Classes::Block
|
285
|
+
@env.function('f').should be
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should handle function definitions with variables" do
|
289
|
+
res = exec <<-EOC
|
290
|
+
function g(x) {
|
291
|
+
42 + x;
|
292
|
+
}
|
293
|
+
EOC
|
294
|
+
res.class.should == RVM::Classes::Block
|
295
|
+
@env.function('g').should be
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "objects" do
|
300
|
+
it "should handle object variables" do
|
301
|
+
obj = mock('object mock')
|
302
|
+
varialbes = mock('variables')
|
303
|
+
obj.should_receive(:variables).once.and_return(varialbes)
|
304
|
+
obj.should_receive(:functions).once.and_return({})
|
305
|
+
varialbes.should_receive(:each)
|
306
|
+
varialbes.should_receive(:[]=)
|
307
|
+
varialbes.should_receive(:[]).with('test').and_return(RVM::Interpreter::VariableStorage.new('ok'))
|
308
|
+
@env['obj'] = obj
|
309
|
+
res = exec <<-EOC
|
310
|
+
obj.test;
|
311
|
+
EOC
|
312
|
+
|
313
|
+
res.should == 'ok'
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should handle object functions" do
|
317
|
+
obj = mock('object mock')
|
318
|
+
functions = mock('variables mock')
|
319
|
+
function = mock('function mock')
|
320
|
+
obj.should_receive(:variables).once.and_return({})
|
321
|
+
obj.should_receive(:functions).once.and_return(functions)
|
322
|
+
|
323
|
+
functions.should_receive(:[]).with('test').and_return(function)
|
324
|
+
function.should_receive(:call).and_return 'ok'
|
325
|
+
|
326
|
+
@env['obj'] = obj
|
327
|
+
res = exec <<-EOC
|
328
|
+
obj.test();
|
329
|
+
EOC
|
330
|
+
|
331
|
+
res.should == 'ok'
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe "condition"
|
336
|
+
|
337
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/../../../lib/rvm/interpreter')
|
2
|
+
require File.join(File.dirname(__FILE__), '/../../../lib/rvm/classes/number')
|
3
|
+
require File.join(File.dirname(__FILE__), '/../../../lib/rvm/languages/math/compiler')
|
4
|
+
describe RVM::Languages::Math::Compiler do
|
5
|
+
before do
|
6
|
+
@c = RVM::Languages::Math::Compiler
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should compile numbers to constants" do
|
10
|
+
@c.compile({:type=>:number, :number=>"5"}).should be_instance_of(RVM::Interpreter::Constant);
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should ignore arrays" do
|
14
|
+
@c.compile([{:type=>:number, :number=>"5"}]).should be_instance_of(RVM::Interpreter::Constant);
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should compile it ident to variables" do
|
18
|
+
@c.compile({:type => :ident, :ident => "x"}).should be_instance_of(RVM::Interpreter::Variable);
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should compile functions" do
|
22
|
+
@c.compile({:type => :function, :op => "x", :params => []}).should be_instance_of(RVM::Interpreter::FunctionCall);
|
23
|
+
@c.compile({:type => :function, :op => "-", :params => []}).should be_instance_of(RVM::Interpreter::FunctionCall);
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should compile mathematical ooperators to function calls" do
|
27
|
+
@c.compile({:type => :op, :op => "+", :left => {:type=>:number, :number=>"5"}, :right => {:type=>:number, :number=>"5"}}).should be_instance_of(RVM::Interpreter::FunctionCall);
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should compile = operators to assignments if left side is a ident" do
|
31
|
+
@c.compile({:type => :op, :op => "=", :left => {:type => :ident, :ident=>"x"}, :right => {:type=>:number, :number=>"5"}}).should be_instance_of(RVM::Interpreter::Assignment);
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should compile = operators to function declaratins if left side is a function" do
|
35
|
+
@c.compile({:type => :op, :op => "=", :left => {:type => :function, :op => "x", :params => []}, :right => {:type=>:number, :number=>"5"}}).should be_instance_of(RVM::Interpreter::FunctionDefinition);
|
36
|
+
@c.compile({:type => :op, :op => "=", :left => {:type => :function, :op => "x", :params => [{:type => :ident, :ident=>"x"}]}, :right => {:type=>:number, :number=>"5"}}).should be_instance_of(RVM::Interpreter::FunctionDefinition);
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise an error of the = operators is used with a number on the left" do
|
40
|
+
lambda {
|
41
|
+
@c.compile({:type => :op, :op => "=", :left => {:type => :number, :ident=>"5"}, :right => {:type=>:number, :number=>"5"}})
|
42
|
+
}.should raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should compile ; to sepperators of sequences" do
|
46
|
+
@c.compile({:type => :op, :op => ";", :left => {:type => :ident, :ident=>"x"}, :right => {:type=>:number, :number=>"5"}}).should be_instance_of(RVM::Interpreter::Sequence);
|
47
|
+
@c.compile({:type => :op, :op => ";", :left => {:type => :op, :op => ";", :left => {:type => :ident, :ident=>"x"}, :right => {:type=>:number, :number=>"5"}}, :right => {:type => :op, :op => ";", :left => {:type => :ident, :ident=>"x"}, :right => {:type=>:number, :number=>"5"}}}).should be_instance_of(RVM::Interpreter::Sequence);
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/../../../lib/rvm/languages/math/tokenizer')
|
2
|
+
describe RVM::Languages::Math::Tokenizer do
|
3
|
+
before do
|
4
|
+
@t = RVM::Languages::Math::Tokenizer
|
5
|
+
end
|
6
|
+
|
7
|
+
it "should tokenize numbers in it's forms" do
|
8
|
+
@t.tokenize("5").should == [['5', :number]]
|
9
|
+
@t.tokenize("42").should == [['42', :number]]
|
10
|
+
@t.tokenize("4.2").should == [['4.2', :number]]
|
11
|
+
@t.tokenize(".2").should == [['.2', :number]]
|
12
|
+
@t.tokenize("4.2e10").should == [['4.2e10', :number]]
|
13
|
+
@t.tokenize("4.2e-10").should == [['4.2e-10', :number]]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should tokenize idents" do
|
17
|
+
@t.tokenize("x").should == [['x', :ident]]
|
18
|
+
@t.tokenize("xy").should == [['xy', :ident]]
|
19
|
+
@t.tokenize("x42").should == [['x42', :ident]]
|
20
|
+
@t.tokenize("_x").should == [['_x', :ident]]
|
21
|
+
@t.tokenize("_x_").should == [['_x_', :ident]]
|
22
|
+
@t.tokenize("really_freaking_long_IdentWithA11KindOfOddStuff").should == [['really_freaking_long_IdentWithA11KindOfOddStuff', :ident]]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should ignore spaces between tokens" do
|
26
|
+
@t.tokenize("23 + 19").should == [['23', :number],['+', :opperator],['19', :number]]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should recognize mathematical opperators (+,-,*,/,^)" do
|
30
|
+
@t.tokenize("23+19").should == [['23', :number], ['+', :opperator], ['19', :number]]
|
31
|
+
@t.tokenize("43-1").should == [['43', :number], ['-', :opperator], ['1', :number]]
|
32
|
+
@t.tokenize("21*2").should == [['21', :number], ['*', :opperator], ['2', :number]]
|
33
|
+
@t.tokenize("84/2").should == [['84', :number], ['/', :opperator], ['2', :number]]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "recognizes unary - and ü" do
|
37
|
+
@t.tokenize("+5").should == [['5', :number]]
|
38
|
+
@t.tokenize("++5").should == [['5', :number]]
|
39
|
+
@t.tokenize("-5").should == [['-', :function], ['5', :number]]
|
40
|
+
@t.tokenize("- 5").should == [['-', :function], ['5', :number]]
|
41
|
+
@t.tokenize("- - 5").should == [['-', :function], ['-', :function], ['5', :number]]
|
42
|
+
@t.tokenize("3 + - - 5").should == [['3', :number], ['+', :opperator], ['-', :function], ['-', :function], ['5', :number]]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should recognize functions" do
|
46
|
+
@t.tokenize("f(x)").should == [['f', :function], ['x', :ident], [')', :paren_close]]
|
47
|
+
@t.tokenize("f(x, y)").should == [['f', :function], ['x', :ident], [',', :function_sep], ['y', :ident], [')', :paren_close]]
|
48
|
+
@t.tokenize("f()").should == [['f', :function], [")", :paren_close]]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should recognize parentheethes" do
|
52
|
+
@t.tokenize("(42)").should == [["(", :paren_open], ["42", :number], [")", :paren_close]]
|
53
|
+
@t.tokenize("(f(x))").should == [["(", :paren_open], ['f', :function], ['x', :ident], [')', :paren_close], [")", :paren_close]]
|
54
|
+
@t.tokenize("(19+23)").should == [["(", :paren_open], ['19', :number], ['+', :opperator], ['23', :number], [")", :paren_close]]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should recognize semicolons" do
|
58
|
+
@t.tokenize("42;42").should == [["42", :number], [";", :opperator], ["42", :number]]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not alow semicolons within brackets" do
|
62
|
+
lambda{@t.tokenize("(42;42)")}.should raise_error
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should throw exeptions on unknown literals where numbers are expected" do
|
66
|
+
lambda{@t.tokenize("42that_is_not_valid!")}.should raise_error
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should throw exeptions on unknown literals" do
|
70
|
+
lambda{@t.tokenize("42that_is_not_valid!")}.should raise_error
|
71
|
+
lambda{@t.tokenize("?")}.should raise_error
|
72
|
+
end
|
73
|
+
end
|