rVM 0.0.3 → 0.0.4

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