partialruby 0.1.0

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 ADDED
@@ -0,0 +1,89 @@
1
+ = partialruby - Ruby partial interpreter written in pure ruby
2
+
3
+ What I trying to say with "partial" ? Well, the ruby interpreter is composed by a few main conceptual "components": parser, api, VM and execution environment. A partial interpreter will be a library implementing only one or even only part of one of these basic components.
4
+ In this case, partialruby only implements part of the VM (e.g. partialruby DOESN'T implement a GC), use the excenllent ruby_parser gem (https://github.com/seattlerb/ruby_parser) to parse ruby code and use the real interpreter to the rest (api, environment, etc...).
5
+
6
+ == Goals:
7
+
8
+ === Implement a alternative VM which export services that is not exported by the original VM without implementing a entire ruby interpreter or fork an existing ruby interpreter
9
+ - Control of execution flow via hooking for sandboxing (see evalhook and shikashi projects)
10
+ - Fast DSL (over ruby, and faster than ruby itself!)
11
+
12
+ === Proof of Concept of interpreter and partial interpreter development
13
+
14
+ - Hyper-portability: microruby
15
+ - Search for a faster ruby
16
+
17
+ == Installation
18
+
19
+ === Gem Installation (pending)
20
+
21
+ sudo gem install partialruby
22
+
23
+ OR
24
+
25
+ * Download the last version of the gem from http://github.com/tario/partialruby/downloads
26
+ * Install the gem with the following;
27
+
28
+ sudo gem install partialruby-X.X.X.gem.
29
+
30
+ == Documentation (pending)
31
+
32
+ Full API documentation can be found on:
33
+ http://tario.github.com/partialruby/doc/
34
+
35
+ == Usage (pending)
36
+
37
+ Basically, partialruby export two APIs:
38
+
39
+ * A alernative eval function with context. Example:
40
+
41
+ require "partialruby"
42
+
43
+ context = PartialRuby::Context.new
44
+ context.eval('print "hello world\n"')
45
+
46
+ Or
47
+
48
+ require "partialruby"
49
+
50
+ PartialRuby.eval('print "hello world\n"')
51
+
52
+ * A flexible node handling. Example
53
+
54
+ require "partialruby"
55
+
56
+ class MyPartialRubyContext < PartialRuby::Context
57
+
58
+ def ruby_emul_call(tree)
59
+ object_tree = tree[1]
60
+ method_name = tree[2]
61
+
62
+ arglist = tree[3]
63
+
64
+ argsstr = arglist[1..-1].
65
+ map{|subtree| "(" + emul(subtree, frame) + ")" }.
66
+ join(",")
67
+
68
+
69
+ if (object_tree)
70
+ "((#{emul(object_tree)}).#{method_name}(#{argsstr})"
71
+ else
72
+ if arglist.count == 0
73
+ "#{method_name}(#{argsstr})"
74
+ else
75
+ "#{method_name}"
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+
83
+ context = MyPartialRubyContext.new
84
+ context.eval('print "hello world\n"')
85
+
86
+ == Copying
87
+
88
+ Copyright (c) 2010-2011 Dario Seminara, released under the GPL License (see LICENSE)
89
+
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = 'partialruby'
9
+ s.version = '0.1.0'
10
+ s.author = 'Dario Seminara'
11
+ s.email = 'robertodarioseminara@gmail.com'
12
+ s.platform = Gem::Platform::RUBY
13
+ s.summary = 'Ruby partial interpreter written in pure-ruby '
14
+ s.homepage = "http://github.com/tario/partialruby"
15
+ s.add_dependency "ruby_parser", ">= 2.0.6"
16
+ s.has_rdoc = true
17
+ s.extra_rdoc_files = [ 'README' ]
18
+ s.rdoc_options << '--main' << 'README'
19
+ s.files = Dir.glob("{examples,lib,spec}/**/*.rb") + [ 'LICENSE', 'AUTHORS', 'CHANGELOG', 'README', 'Rakefile', 'TODO' ]
20
+ end
21
+
22
+ desc 'Run tests'
23
+ task :default => [ :test ]
24
+
25
+ Rake::TestTask.new('test') do |t|
26
+ t.libs << 'test'
27
+ t.pattern = '{test}/**/test_*.rb'
28
+ t.verbose = true
29
+ end
30
+
31
+ desc 'Generate RDoc'
32
+ Rake::RDocTask.new :rdoc do |rd|
33
+ rd.rdoc_dir = 'doc'
34
+ rd.rdoc_files.add 'lib', 'ext', 'README'
35
+ rd.main = 'README'
36
+ end
37
+
38
+ desc 'Build Gem'
39
+ Rake::GemPackageTask.new spec do |pkg|
40
+ pkg.need_tar = true
41
+ end
42
+
43
+ desc 'Clean up'
44
+ task :clean => [ :clobber_rdoc, :clobber_package ]
45
+
46
+ desc 'Clean up'
47
+ task :clobber => [ :clean ]
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * change interface to eval only ASTs (remove unnecessary ruby_parser dependency)
2
+ * remove obsolete code of node evaluation (only keep emulation code)
3
+ * Ruby 1.9 compatibility
@@ -0,0 +1,468 @@
1
+ =begin
2
+
3
+ This file is part of the partialruby project, http://github.com/tario/partialruby
4
+
5
+ Copyright (c) 2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ partialruby is free software: you can redistribute it and/or modify
8
+ it under the terms of the gnu general public license as published by
9
+ the free software foundation, either version 3 of the license, or
10
+ (at your option) any later version.
11
+
12
+ partialruby is distributed in the hope that it will be useful,
13
+ but without any warranty; without even the implied warranty of
14
+ merchantability or fitness for a particular purpose. see the
15
+ gnu general public license for more details.
16
+
17
+ you should have received a copy of the gnu general public license
18
+ along with partialruby. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+ require "rubygems"
22
+ require "ruby_parser"
23
+
24
+ module PartialRuby
25
+
26
+ def self.eval(code, b_)
27
+ c = PureRubyContext.new
28
+
29
+ parser = RubyParser.new
30
+ c.run(parser.parse(code), Frame.new(b_, b_.eval("self")) )
31
+ end
32
+
33
+ class Frame
34
+ attr_reader :_binding
35
+ attr_reader :_self
36
+ attr_reader :locals
37
+
38
+ def initialize(b, _self)
39
+ @_binding = b
40
+ @_self = _self
41
+ @locals = Hash.new
42
+ end
43
+ end
44
+
45
+ class Context
46
+ def object_ref(obj)
47
+ "ObjectSpace._id2ref(#{obj.object_id})"
48
+ end
49
+
50
+ def ruby_emul(tree)
51
+ nodetype = tree.first
52
+ send("ruby_emul_"+nodetype.to_s, tree)
53
+ end
54
+
55
+ def emul(tree)
56
+ begin
57
+ # first, try to emul the node
58
+ return ruby_emul(tree)
59
+ rescue NoMethodError => e
60
+ if tree
61
+ "#{object_ref self}.run(#{object_ref tree}, PartialRuby::Frame.new(binding,self) )"
62
+ else
63
+ "nil; "
64
+ end
65
+ end
66
+ end
67
+
68
+ def run(tree, frame)
69
+ return nil unless tree
70
+
71
+ nodetype = tree.first
72
+
73
+ code = nil
74
+ begin
75
+ # first, try to emul the node
76
+ code = ruby_emul(tree)
77
+ rescue NoMethodError => e
78
+ end
79
+
80
+ if code then
81
+ eval(code, frame._binding)
82
+ else
83
+ send("handle_node_"+nodetype.to_s, tree, frame)
84
+ end
85
+ end
86
+ end
87
+
88
+ class PureRubyContext < Context
89
+
90
+ # def handle_node_scope(tree, frame)
91
+ # run(tree[1], frame)
92
+ # end
93
+
94
+ def ruby_emul_nil(tree)
95
+ "(nil)"
96
+ end
97
+
98
+ def ruby_emul_scope(tree)
99
+ emul tree[1]
100
+ end
101
+
102
+ def ruby_emul_block(tree)
103
+ last = nil
104
+
105
+ code = ""
106
+ tree[1..-1].each do |subtree|
107
+ code << emul(subtree) << "\n"
108
+ end
109
+
110
+ code
111
+ end
112
+
113
+ def ruby_emul_hash(tree)
114
+ pairs = Array.new
115
+ (0..((tree.size - 1) / 2)-1).each do |i|
116
+ pairs << [ tree[i*2+1], tree[i*2+2] ]
117
+ end
118
+
119
+ "{" + pairs.map{|pair| "(#{emul pair.first})=>(#{emul pair.last} )" }.join(",") + "}"
120
+ end
121
+
122
+ # def handle_node_block(tree, frame)
123
+ # last = nil
124
+ # tree[1..-1].each do |subtree|
125
+ # last = run(subtree, frame)
126
+ # end
127
+
128
+ # last
129
+ #end
130
+
131
+ def ruby_emul_lasgn(tree)
132
+ varname = tree[1]
133
+ "#{varname} = ( #{emul(tree[2])} );"
134
+ end
135
+
136
+ def ruby_emul_lvar(tree)
137
+ varname = tree[1]
138
+ varname.to_s
139
+ end
140
+
141
+ def ruby_emul_const(tree)
142
+ tree[1].to_s
143
+ end
144
+
145
+ def ruby_emul_colon3(tree)
146
+ "::" + tree[1].to_s
147
+ end
148
+
149
+ def ruby_emul_colon2(tree)
150
+ "#{emul tree[1]}::#{tree[2]}"
151
+ end
152
+
153
+ def ruby_emul_class(tree)
154
+ classtree = tree[1]
155
+ subtree = tree[3]
156
+
157
+ classname = ""
158
+ if classtree.instance_of? Symbol then
159
+ classname = classtree
160
+ else
161
+ classname = emul classtree
162
+ end
163
+
164
+ return ("
165
+ class #{classname}
166
+ #{emul subtree}
167
+ end
168
+ ")
169
+ end
170
+
171
+ def ruby_emul_defs(tree)
172
+ methodname = tree[2]
173
+ args = tree[3]
174
+ "def (#{emul tree[1]}).#{methodname}(#{args[1..-1].map(&:to_s).join(",")})
175
+ #{emul tree[4]}
176
+ end
177
+ "
178
+ end
179
+
180
+ def ruby_emul_defn(tree)
181
+ method_name = tree[1]
182
+ args = tree[2]
183
+ impl = tree[3][1]
184
+
185
+ "def #{method_name}(#{args[1..-1].map(&:to_s).join(",")})
186
+ #{emul impl}
187
+ end
188
+ "
189
+ end
190
+
191
+ def ruby_emul_lit(tree)
192
+ obj = tree[1]
193
+ if obj.instance_of? Symbol or obj.instance_of? Fixnum or obj == nil or obj == true or obj == false
194
+ "(#{obj.inspect})"
195
+ else
196
+ "(#{object_ref tree[1]})"
197
+ end
198
+ end
199
+
200
+ def ruby_emul_str(tree)
201
+ "(#{tree[1].inspect})"
202
+ end
203
+
204
+ def ruby_emul_cdecl(tree)
205
+
206
+ if tree[1].instance_of? Sexp
207
+ "(#{emul tree[1]} = #{emul tree[2]})"
208
+ else
209
+ "(#{tree[1]} = #{emul tree[2]})"
210
+ end
211
+ end
212
+
213
+ def ruby_emul_dstr(tree)
214
+ firststr = tree[1]
215
+ retstr = firststr
216
+ tree[2..-1].each do |subtree|
217
+
218
+ subtreetype = subtree[0]
219
+
220
+ if subtreetype == :evstr
221
+ retstr << "\#{(#{emul subtree[1]})}"
222
+ else
223
+ retstr << "\#{(#{emul subtree})}"
224
+ end
225
+ end
226
+
227
+ '"' + retstr + '"'
228
+ end
229
+
230
+ def ruby_emul_array(tree)
231
+ "[" + tree[1..-1].map{ |subtree| "(" + emul(subtree) + ")" }.join(",") + "]"
232
+ end
233
+
234
+ def ruby_emul_self(tree)
235
+ "(self)"
236
+ end
237
+
238
+ def ruby_emul_true(tree)
239
+ "true"
240
+ end
241
+
242
+ def ruby_emul_false(tree)
243
+ "false"
244
+ end
245
+
246
+ def ruby_emul_if(tree)
247
+ "if (#{emul tree[1]}); (#{emul tree[2]}) else (#{emul tree[3]}) end"
248
+ end
249
+
250
+ def ruby_emul_while(tree)
251
+ "while (#{emul tree[1]}); (#{emul tree[2]}); end "
252
+ end
253
+
254
+ def process_args(tree)
255
+ nodetype = tree[0]
256
+ if nodetype == :lasgn
257
+ tree.last.to_s
258
+ elsif nodetype == :masgn
259
+ tree[1][1..-1].map {|subtree|
260
+ process_args(subtree)
261
+ }.join(",")
262
+ elsif nodetype == :splat
263
+ "*" + process_args(tree.last)
264
+ end
265
+
266
+ end
267
+
268
+ def ruby_emul_iter(tree)
269
+ callnode = tree[1]
270
+ innernode = tree[3]
271
+
272
+ arguments = tree[2]
273
+ argumentsstr = ""
274
+
275
+ if arguments
276
+ argumentsstr = "|" + process_args(arguments) + "|"
277
+ end
278
+ "#{emul callnode} { #{argumentsstr} #{emul innernode} }"
279
+ end
280
+
281
+ def ruby_emul_until(tree)
282
+ "until (#{emul tree[1]}); (#{emul tree[2]}); end "
283
+ end
284
+
285
+ def ruby_emul_case(tree)
286
+ str = "case #{emul tree[1]}; "
287
+
288
+ tree[2..-2].each do |subtree|
289
+ matches = subtree[1][1..-1].map{|subsubtree| "(" + emul(subsubtree) + ")" }.join(",")
290
+
291
+ str << "when #{matches}; #{emul subtree[2]};"
292
+ end
293
+
294
+ if tree[-1]
295
+ str << "else; #{emul tree[-1]}; "
296
+ end
297
+
298
+ str << "end; "
299
+ str
300
+ end
301
+
302
+ def ruby_emul_splat(tree)
303
+ "*(#{emul(tree[1])})"
304
+ end
305
+
306
+ def ruby_emul_ensure(tree)
307
+ "begin;
308
+ #{emul tree[1]}
309
+ ensure;
310
+ #{emul tree[2] }
311
+ end;
312
+ "
313
+ end
314
+
315
+ def ruby_emul_ivar(tree)
316
+ "(" + tree[1].to_s + ")"
317
+ end
318
+
319
+ def ruby_emul_iasgn(tree)
320
+ "(#{tree[1].to_s} = (#{emul tree[2]}))"
321
+ end
322
+
323
+ def ruby_emul_gvar(tree)
324
+ "(" + tree[1].to_s + ")"
325
+ end
326
+
327
+ def ruby_emul_gasgn(tree)
328
+ "(#{tree[1].to_s} = (#{emul tree[2]}))"
329
+ end
330
+
331
+ def ruby_emul_xstr(tree)
332
+ "`#{tree[1].gsub("`","")}`"
333
+ end
334
+
335
+ def ruby_emul_dxstr(tree)
336
+ firststr = tree[1]
337
+ retstr = firststr
338
+ tree[2..-1].each do |subtree|
339
+
340
+ subtreetype = subtree[0]
341
+
342
+ if subtreetype == :evstr
343
+ retstr << "\#{(#{emul subtree[1]})}"
344
+ else
345
+ retstr << "\#{(#{emul subtree})}"
346
+ end
347
+ end
348
+
349
+ '`' + retstr + '`'
350
+ end
351
+
352
+ def ruby_emul_yield(tree)
353
+
354
+ strargs = tree[1..-1].map{ |subtree|
355
+ if subtree[0] == :splat
356
+ "*(" + emul(subtree[1]) + ")"
357
+ else
358
+ "(" + emul(subtree) + ")"
359
+ end
360
+ }.join(",")
361
+
362
+ "(yield(#{strargs}))"
363
+ end
364
+
365
+ def ruby_emul_module(tree)
366
+
367
+ if tree[1].instance_of? Symbol
368
+ modulename = tree[1].to_s
369
+ else
370
+ modulename = emul tree[1]
371
+ end
372
+
373
+ "module #{modulename}
374
+ #{emul tree[2]}
375
+ end
376
+ "
377
+ end
378
+
379
+ def ruby_emul_rescue(tree)
380
+
381
+ resbody = tree[2][2]
382
+
383
+ strresbody = ""
384
+ if resbody
385
+ strresbody = emul resbody
386
+ else
387
+ strresbody = ""
388
+ end
389
+
390
+ exceptionarray = tree[2][1][1..-1]
391
+
392
+ exceptionstrarray = []
393
+
394
+ i = 0
395
+ while i < exceptionarray.size
396
+ if exceptionarray[i+1]
397
+ if exceptionarray[i+1][0] == :lasgn
398
+ exceptionstrarray << "(" + (emul(exceptionarray[i]) + ") => " + exceptionarray[i+1][1].to_s)
399
+ i = i + 1
400
+ else
401
+ exceptionstrarray << "(" + (emul(exceptionarray[i])) + ")"
402
+ end
403
+ else
404
+ exceptionstrarray << "(" + (emul(exceptionarray[i])) + ")"
405
+ end
406
+ i = i + 1
407
+ end
408
+
409
+ "begin;
410
+ #{emul tree[1]}
411
+ rescue #{exceptionstrarray.join(",")};
412
+ #{strresbody}
413
+ end;
414
+ "
415
+ end
416
+
417
+ def ruby_emul_return(tree)
418
+ "; return #{emul tree[1]}; "
419
+ end
420
+
421
+ def ruby_emul_and(tree)
422
+ "(#{emul tree[1]}) and (#{emul tree[2]})"
423
+ end
424
+
425
+ def ruby_emul_or(tree)
426
+ "(#{emul tree[1]}) or (#{emul tree[2]})"
427
+ end
428
+
429
+ def ruby_emul_not(tree)
430
+ "not (#{emul tree[1]})"
431
+ end
432
+
433
+ def ruby_emul_call(tree)
434
+ object_tree = tree[1]
435
+ method_name = tree[2]
436
+
437
+ arglisttree = tree[3]
438
+ arglist = arglisttree[1..-1]
439
+
440
+ argsstr = arglist.
441
+ map{|subtree|
442
+ if subtree[0] == :splat
443
+ emul(subtree)
444
+ else
445
+ "(" + emul(subtree) + ")"
446
+ end
447
+ }.
448
+ join(",")
449
+
450
+ if (object_tree)
451
+ if arglist.count == 0
452
+ "(#{emul(object_tree)}).#{method_name}"
453
+ else
454
+ "(#{emul(object_tree)}).#{method_name}(#{argsstr})"
455
+ end
456
+ else
457
+ if arglist.count == 0
458
+ "#{method_name}"
459
+ else
460
+ "#{method_name}(#{argsstr})"
461
+ end
462
+ end
463
+
464
+ end
465
+ end
466
+
467
+ #X.new.foo
468
+ end