rVM 0.0.3 → 0.0.4

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 CHANGED
@@ -0,0 +1,38 @@
1
+ = rVM
2
+
3
+ rVM is the implementation of a VirtualMachine in pure Ruby. To clarify, no virtual machine as in VMWare that lets you virtualize a computer. But rather like ruby 1.9 or JRuby that both implement a virtual machine (written in C or Java) to execute Ruby code.
4
+ Just that rVM takes it the other way around, it is written in ruby and lets you execute code within ruby.
5
+
6
+ = So what is so cool about that?
7
+
8
+ For once, the challenge to pull this off ;P. Then it has on huge advantage. It makes it pretty easy to give ruby programs a interface for scripting.
9
+
10
+ Now you might think 'wow cool but I could just use ruby!' that is right, and wrong. Imagine just short code snippets to make a query, perhaps you want to implement a SQL based language to let a user query data in your program? Also consider you want your website to allow user side scripts. I would not want to have ruby code executed for random visitors. Then again I might be more tempted to let them execute code within a VM that has no access to anything outside it's VM neither variables nor hardware nor memory.
11
+
12
+ = Example
13
+
14
+
15
+ The following example implements a pretty easy calculator using the the math language shipped with rVM. Set variables and are not used outside one line
16
+
17
+ require 'rubygems'
18
+ require 'rvm' # This loads the rVM gem
19
+ require 'rvm/languages/math' #This loads the math plugin as well as the math function library
20
+ s =''
21
+ compiler = RVM::Languages[:math] #Lets get us the compiler
22
+ while s!= 'QUIT'
23
+ s=gets.chomp
24
+ puts compiler.compile(s).execute(RVM::Interpreter.env) # We pass an fresh enviroment as we don't need an environment.
25
+ end
26
+
27
+ Here one example that keeps set variables due to using one enviroment for all the calls.
28
+
29
+ require 'rubygems'
30
+ require 'rvm' # This loads the rVM gem
31
+ require 'rvm/languages/math' #This loads the math plugin as well as the math function library
32
+ s =''
33
+ compiler = RVM::Languages[:math] #Lets get us the compiler
34
+ env = RVM::Interpreter.env
35
+ while s!= 'QUIT'
36
+ s=gets.chomp
37
+ puts compiler.compile(s).execute(env) # We pass an fresh enviroment as we don't need an environment.
38
+ end
@@ -1,6 +1,6 @@
1
1
  module RVM
2
2
  module Classes
3
- class String <String
3
+ class String < ::String
4
4
  extend Plugin
5
5
  plugin_host Classes
6
6
  register_for :string
@@ -39,12 +39,13 @@ module RVM
39
39
  RVM::debug "data: #{data}\noldenv:#{oldenv}"
40
40
  @data = {
41
41
  :locals => {},
42
+ :functions => {},
42
43
  :caller => nil,
43
44
  :self => nil,
44
45
  :evaldeepth => 0,
45
46
  :params => []
46
47
  }.merge(data)
47
- @prev = oldenv || {}
48
+ @prev = oldenv || {}
48
49
  RVM::debug "Creating new enviroment with: #{@data.inspect} as child of #{@prev}"
49
50
 
50
51
  end
@@ -86,7 +87,7 @@ module RVM
86
87
  # the given name it tries the privious enviroments.
87
88
  def [] k
88
89
  r = @data[:locals][k] || @prev[k]
89
- RVM::debug "Getting ariable #{k} (#{r})"
90
+ RVM::debug "Getting variable #{k} (#{r})"
90
91
  r
91
92
  end
92
93
 
@@ -97,6 +98,38 @@ module RVM
97
98
  @data[:locals][k] = v
98
99
  end
99
100
 
101
+ #Returns a function for the current enviroment
102
+ def function k
103
+ r = @data[:functions][k]
104
+ if not r and @prev.is_a? Enviroment
105
+ r = @prev.function(k)
106
+ end
107
+ RVM::debug "Getting functon #{k} (#{r})"
108
+ r
109
+ end
110
+
111
+ #Returns a function for the current enviroment
112
+ def def_function k, b
113
+ @data[:functions][k] = b
114
+ RVM::debug "Setting functon #{k} (#{b})"
115
+ nil
116
+ end
117
+ end
118
+
119
+ class FunctionDefinition
120
+ def initialize name, body, override = true
121
+ @name = name
122
+ @body = body
123
+ @override = override
124
+ end
125
+
126
+ def execute env
127
+ if (not @override) and env.function(@name)
128
+ raise "Function #{@name} already defined!"
129
+ end
130
+ env.def_function(@name.execute(env),@body.execute(env))
131
+ nil
132
+ end
100
133
  end
101
134
 
102
135
  class Loop
@@ -284,7 +317,7 @@ module RVM
284
317
  def execute env
285
318
  RVM::debug "Executing Parameter..."
286
319
  r = env.param(@num.execute(env).to_i)
287
- @type = r.data_type
320
+ @type = r.data_type if r
288
321
  r
289
322
  end
290
323
  end
@@ -303,6 +336,10 @@ module RVM
303
336
  r
304
337
  end
305
338
 
339
+ def + v
340
+ Sequence.new(super)
341
+ end
342
+
306
343
  def data_type
307
344
  :any
308
345
  end
@@ -322,11 +359,7 @@ module RVM
322
359
  #
323
360
  # Arguments is a list of the arguments to the function.
324
361
  def initialize function, arguments
325
- if function.is_a? RVM::Classes[:block]
326
- @function = function
327
- else
328
- @function = RVM::Functions[function]
329
- end
362
+ @function = function
330
363
  @arguments = arguments
331
364
  end
332
365
 
@@ -345,12 +378,25 @@ module RVM
345
378
  def execute env
346
379
  RVM::debug "Executing FunctionCall..."
347
380
  args = @arguments
348
- if @function.execargs
381
+ if @function.is_a? RVM::Classes[:block]
382
+ args = @arguments.map do |arg|
383
+ arg.execute env
384
+ end
385
+ @function.call(args, env)
386
+ elsif (fun = env.function(@function))
349
387
  args = @arguments.map do |arg|
350
388
  arg.execute env
351
389
  end
390
+ fun.call(args, env)
391
+ else
392
+ fun = RVM::Functions[@function]
393
+ if fun.execargs
394
+ args = @arguments.map do |arg|
395
+ arg.execute env
396
+ end
397
+ end
398
+ fun.call(args, env)
352
399
  end
353
- @function.call(args, env)
354
400
  end
355
401
  end
356
402
  end
@@ -1,36 +1,70 @@
1
1
  module RVM
2
- module Languages
3
- module Math
4
- class Compiler
5
- include RVM::Interpreter
6
- FUNCTION_MAP = {
2
+ module Languages
3
+ module Math
4
+ class Compiler
5
+ include RVM::Interpreter
6
+ FUNCTION_MAP = {
7
7
  '+' => :add,
8
8
  '-' => :sub,
9
9
  '/' => :div,
10
10
  '*' => :mul,
11
11
  '^' => :power
12
- }
13
- def Compiler.compile tree
14
- if tree.is_a? Array
15
- compile tree.first
16
- elsif tree.is_a? Hash
17
- case tree[:type]
18
- when :number
19
- Interpreter.const(:number, tree[:number])
20
- when :function
21
- params = tree[:params].map {|p| compile p}
22
- case tree[:op]
23
- when '-'
24
- FunctionCall.new :neg, params
25
- else
26
- FunctionCall.new tree[:op], params
27
- end
28
- when :op
29
- FunctionCall.new(FUNCTION_MAP[tree[:op]], [compile(tree[:left]),compile(tree[:right])])
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
12
+ }
13
+ def Compiler.compile tree
14
+ if tree.is_a? Array
15
+ compile(tree.first)
16
+ elsif tree.is_a? Hash
17
+ case tree[:type]
18
+ when :number
19
+ Interpreter.const(:number, tree[:number])
20
+ when :function
21
+ params = tree[:params].map {|p| compile(p)}
22
+ case tree[:op]
23
+ when '-'
24
+ FunctionCall.new :neg, params
25
+ else
26
+ FunctionCall.new tree[:op], params
27
+ end
28
+ when :ident
29
+ Interpreter::Variable.new(Interpreter.const(:string, tree[:ident]))
30
+ when :op
31
+ case tree[:op]
32
+ when ';'
33
+ s = Interpreter::Sequence.new()
34
+ if (tree[:left][:type] == :op) and (tree[:left][:op] == ';')
35
+ s += compile(tree[:left])
36
+ else
37
+ s << compile(tree[:left])
38
+ end
39
+ if (tree[:right][:type] == :op) and (tree[:right][:op] == ';')
40
+ s += compile(tree[:right])
41
+ else
42
+ s << compile(tree[:right])
43
+ end
44
+ when '='
45
+ case tree[:left][:type]
46
+ when :ident
47
+ Interpreter::Assignment.new(Interpreter.const(:string, tree[:left][:ident]),compile(tree[:right]))
48
+ when :function
49
+ body = Interpreter::Sequence.new()
50
+ i = 0
51
+ tree[:left][:params].each do |p|
52
+ raise ArgumentError, "Bad ype for a function parameter: #{p[:type]} " if p[:type] != :ident
53
+ body << Interpreter::Assignment.new(Interpreter.const(:string, p[:ident]), Interpreter::Parameter.new(Interpreter.const(:number, i)))
54
+ end
55
+ body << compile(tree[:right])
56
+ Interpreter::FunctionDefinition.new(Interpreter.const(:string, tree[:left][:op]), Interpreter.const(:block,body))
57
+
58
+ else
59
+ raise ArgumentError, "Bad name for a variable!"
60
+ end
61
+ else
62
+ FunctionCall.new(FUNCTION_MAP[tree[:op]], [compile(tree[:left]),compile(tree[:right])])
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
36
70
  end
@@ -1,47 +1,65 @@
1
1
  require 'strscan'
2
2
  module RVM
3
- module Languages
4
- module Math
5
- class Tokenizer
6
- def Tokenizer.tokenize str
7
- s = StringScanner.new str
8
- tokens = []
9
- state = :number
10
- while not s.eos?
11
- s.skip(/\s*/)
12
- if state == :number
13
- if s.scan(/[+-]+/)
14
- t = s.matched.gsub('+','').gsub('--','')
15
- if t == '-'
16
- tokens << [t, :function]
17
- end
18
- elsif s.scan(/[a-z][a-z0-9]*/i)
19
- tokens << [s.matched, :function]
20
- elsif s.scan(/[\d]+(\.\d+)?/i)
21
- tokens << [s.matched, :number]
22
- state = :opperator
23
- elsif s.scan(/\(/)
24
- tokens << [s.matched, :paren_open]
25
- else
26
- return []
27
- end
28
- else
29
- if s.scan(/\)/)
30
- tokens << [s.matched, :paren_close]
31
- elsif s.scan(/,/)
32
- tokens << [s.matched, :function_sep]
33
- state = :number
34
- elsif s.scan(/[*+\/^-]/)
35
- tokens << [s.matched, :opperator]
36
- state = :number
37
- else
38
- return []
39
- end
40
- end
41
- end
42
- tokens
43
- end
44
- end
45
- end
46
- end
3
+ module Languages
4
+ module Math
5
+ class Tokenizer
6
+ def Tokenizer.tokenize str
7
+ s = StringScanner.new str
8
+ tokens = []
9
+ state = :number
10
+ paren_deepth = 0
11
+ while not s.eos?
12
+ s.skip(/\s*/)
13
+ if state == :number
14
+ if s.scan(/[+-]+/)
15
+ t = s.matched.gsub('+','').gsub('--','')
16
+ if t == '-'
17
+ tokens << [t, :function]
18
+ end
19
+ elsif s.scan(/[a-z][a-z0-9]*/i)
20
+ r = s.matched
21
+ if s.scan(/\(/)
22
+ tokens << [r, :function]
23
+ paren_deepth += 1
24
+ else
25
+ tokens << [r, :ident]
26
+ state = :opperator
27
+ end
28
+ elsif s.scan(/[\d]+(\.\d+)?/i)
29
+ tokens << [s.matched, :number]
30
+ state = :opperator
31
+ elsif s.scan(/\(/)
32
+ paren_deepth += 1
33
+ tokens << [s.matched, :paren_open]
34
+ else
35
+ raise "Unknown literal in term at positin #{s.pos}."
36
+ end
37
+ else
38
+ if s.scan(/\)/)
39
+ paren_deepth -= 1
40
+ raise "Unmatched parenthes at #{s.pos}" if paren_deepth < 0
41
+ tokens << [s.matched, :paren_close]
42
+ elsif s.scan(/,/)
43
+ tokens << [s.matched, :function_sep]
44
+ state = :number
45
+ elsif s.scan(/[*+\/^=-]/)
46
+ tokens << [s.matched, :opperator]
47
+ state = :number
48
+ elsif s.scan(/;/)
49
+ if paren_deepth == 0
50
+ tokens << [s.matched, :opperator]
51
+ state = :number
52
+ else
53
+ raise "Expression sepperator enclosed in parenthes at #{s.pos}"
54
+ end
55
+ else
56
+ raise "Unknown literal in term at positin #{s.pos}."
57
+ end
58
+ end
59
+ end
60
+ tokens
61
+ end
62
+ end
63
+ end
64
+ end
47
65
  end
@@ -1,100 +1,109 @@
1
1
  module RVM
2
- module Languages
3
- module Math
4
- class Tree
5
- PRIORITIES = {
2
+ module Languages
3
+ module Math
4
+ class Tree
5
+ PRIORITIES = {
6
+ ';' => -1,
7
+ '=' => 0,
6
8
  '(' => 0,
7
9
  '+' => 1,
8
10
  '-' => 1,
9
11
  '*' => 2,
10
12
  '/' => 2,
11
13
  '^' => 3,
12
- }
13
- ASSOSICATIONS = {
14
+ }
15
+ ASSOSICATIONS = {
16
+ ';' => :right,
14
17
  '+' => :left,
15
18
  '-' => :left,
16
19
  '*' => :left,
17
20
  '/' => :left,
18
- '^' => :right
19
- }
20
- def Tree.generate tokens
21
- output = []
22
- dbgoutput = []
23
- stack = []
24
- dbgstack = []
25
- while not tokens.empty?
26
- token = tokens.shift
27
- case token[1]
28
- when :function
29
- fun = token[0]
30
- stack << {:type => :function, :op => fun}
31
- output << {:type => :function_end}
32
- dbgoutput << ')'
33
- dbgstack << fun
34
- when :function_sep
35
- while stack.last && (stack.last[:op] != '(')
36
- output << stack.pop
37
- dbgoutput << dbgstack.pop
38
- end
39
- when :paren_open
40
- op = token[0]
41
- stack << {:type => :op, :op => op}
42
- dbgstack << op
43
- when :paren_close
44
- while stack.last && (stack.last[:op] != '(')
45
- output << stack.pop
46
- dbgoutput << dbgstack.pop
47
- end
48
- stack.pop
49
- dbgstack.pop
50
- if stack.last && (stack.last[:type] == :function)
51
- output << stack.pop
52
- dbgoutput << dbgstack.pop
53
- end
54
- when :number
55
- output << {:type => :number, :number => token[0]}
56
- dbgoutput << token[0]
57
- when :opperator
58
- op = token[0]
59
- ass = ASSOSICATIONS[op]
60
- o1p = PRIORITIES[op]
61
- while stack.last &&
62
- (((ass == :left) && (o1p <= PRIORITIES[stack.last[:op]])) ||
63
- ((ass == :right) && (o1p < PRIORITIES[stack.last[:op]])))
64
- output << stack.pop
65
- dbgoutput << dbgstack.pop
66
- end
67
- stack << {:type => :op, :op => op}
68
- dbgstack << op
69
- else
70
- end
71
- end
72
- while not stack.empty?
73
- output << stack.pop
74
- end
75
- gen_tree output
76
- end
77
-
78
- private
79
- def Tree.gen_tree data
80
- item = data.pop
81
- case item[:type]
82
- when :number
83
- item
84
- when :op
85
- item[:right] = gen_tree(data)
86
- item[:left] = gen_tree(data)
87
- item
88
- when :function
89
- item[:params] = []
90
- while data.last && data.last[:type] != :function_end
91
- item[:params] << gen_tree(data)
92
- end
93
- data.pop
94
- item
95
- end
96
- end
97
- end
98
- end
99
- end
21
+ '^' => :right,
22
+ '=' => :right
23
+ }
24
+ def Tree.generate tokens
25
+ output = []
26
+ dbgoutput = []
27
+ stack = []
28
+ dbgstack = []
29
+ while not tokens.empty?
30
+ token = tokens.shift
31
+ case token[1]
32
+ when :function
33
+ fun = token[0]
34
+ stack << {:type => :function, :op => fun}
35
+ output << {:type => :function_end}
36
+ dbgoutput << ')'
37
+ dbgstack << fun
38
+ when :function_sep
39
+ while stack.last && (stack.last[:op] != '(')
40
+ output << stack.pop
41
+ dbgoutput << dbgstack.pop
42
+ end
43
+ when :paren_open
44
+ op = token[0]
45
+ stack << {:type => :op, :op => op}
46
+ dbgstack << op
47
+ when :paren_close
48
+ while stack.last && (stack.last[:op] != '(')
49
+ output << stack.pop
50
+ dbgoutput << dbgstack.pop
51
+ end
52
+ stack.pop
53
+ dbgstack.pop
54
+ if stack.last && (stack.last[:type] == :function)
55
+ output << stack.pop
56
+ dbgoutput << dbgstack.pop
57
+ end
58
+ when :number
59
+ output << {:type => :number, :number => token[0]}
60
+ dbgoutput << token[0]
61
+ when :ident
62
+ output << {:type => :ident, :ident => token[0]}
63
+ dbgoutput << token[0]
64
+ when :opperator
65
+ op = token[0]
66
+ ass = ASSOSICATIONS[op]
67
+ o1p = PRIORITIES[op]
68
+ while stack.last &&
69
+ (((ass == :left) && (o1p <= PRIORITIES[stack.last[:op]])) ||
70
+ ((ass == :right) && (o1p < PRIORITIES[stack.last[:op]])))
71
+ output << stack.pop
72
+ dbgoutput << dbgstack.pop
73
+ end
74
+ stack << {:type => :op, :op => op}
75
+ dbgstack << op
76
+ else
77
+ end
78
+ end
79
+ while not stack.empty?
80
+ output << stack.pop
81
+ end
82
+ p output
83
+ gen_tree output
84
+ end
85
+
86
+ private
87
+ def Tree.gen_tree data
88
+ p data
89
+ item = data.pop
90
+ case item[:type]
91
+ when :number, :ident
92
+ item
93
+ when :op
94
+ item[:right] = gen_tree(data)
95
+ item[:left] = gen_tree(data)
96
+ item
97
+ when :function
98
+ item[:params] = []
99
+ while data.last && data.last[:type] != :function_end
100
+ item[:params] << gen_tree(data)
101
+ end
102
+ data.pop
103
+ item
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
100
109
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rVM
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Heinz N. Gies
@@ -9,7 +9,7 @@ autorequire: ""
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-03-04 00:00:00 +01:00
12
+ date: 2008-03-05 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15