rVM 0.0.4 → 0.0.5
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/rvm/interpreter.rb +0 -23
- data/lib/rvm/languages/math/compiler.rb +1 -1
- data/lib/rvm/languages/math/tokenizer.rb +7 -3
- data/lib/rvm/languages/math/tree.rb +19 -18
- data/lib/rvm/languages/simple.rb +15 -0
- data/lib/rvm/languages/simple/compiler.rb +73 -0
- data/lib/rvm/languages/simple/tokenizer.rb +75 -0
- data/lib/rvm/languages/simple/tree.rb +144 -0
- metadata +8 -3
data/lib/rvm/interpreter.rb
CHANGED
@@ -40,8 +40,6 @@ module RVM
|
|
40
40
|
@data = {
|
41
41
|
:locals => {},
|
42
42
|
:functions => {},
|
43
|
-
:caller => nil,
|
44
|
-
:self => nil,
|
45
43
|
:evaldeepth => 0,
|
46
44
|
:params => []
|
47
45
|
}.merge(data)
|
@@ -50,27 +48,6 @@ module RVM
|
|
50
48
|
|
51
49
|
end
|
52
50
|
|
53
|
-
# returns the object that issued the VM call - so to say it's owner.
|
54
|
-
#
|
55
|
-
# If the current enviroment has no caller it tries it's 'oldenv' attrib
|
56
|
-
# to see if that had a owner.
|
57
|
-
def caller
|
58
|
-
r = @data[:caller] || (@prev.is_a?(Enviroment) ? @prev.caller : nil)
|
59
|
-
RVM::debug "Getting caller: #{r}"
|
60
|
-
r
|
61
|
-
end
|
62
|
-
|
63
|
-
# returns the object that the code is run as, so it has the rights of
|
64
|
-
# this object.
|
65
|
-
#
|
66
|
-
# If the current enviroment has no object it tries it's 'oldenv' attrib
|
67
|
-
# to see if that had a object.
|
68
|
-
def object
|
69
|
-
r = @data[:self] || (@prev.is_a?(Enviroment) ? @prev.object : nil)
|
70
|
-
RVM::debug "Getting object: #{r}"
|
71
|
-
r
|
72
|
-
end
|
73
|
-
|
74
51
|
# returns a parameter that was passed to the call. For function calls
|
75
52
|
# this is especially important, it beginns with 0.
|
76
53
|
#
|
@@ -51,10 +51,10 @@ module RVM
|
|
51
51
|
tree[:left][:params].each do |p|
|
52
52
|
raise ArgumentError, "Bad ype for a function parameter: #{p[:type]} " if p[:type] != :ident
|
53
53
|
body << Interpreter::Assignment.new(Interpreter.const(:string, p[:ident]), Interpreter::Parameter.new(Interpreter.const(:number, i)))
|
54
|
+
i += 1
|
54
55
|
end
|
55
56
|
body << compile(tree[:right])
|
56
57
|
Interpreter::FunctionDefinition.new(Interpreter.const(:string, tree[:left][:op]), Interpreter.const(:block,body))
|
57
|
-
|
58
58
|
else
|
59
59
|
raise ArgumentError, "Bad name for a variable!"
|
60
60
|
end
|
@@ -16,16 +16,20 @@ module RVM
|
|
16
16
|
if t == '-'
|
17
17
|
tokens << [t, :function]
|
18
18
|
end
|
19
|
-
elsif s.scan(/[
|
19
|
+
elsif s.scan(/[_a-z][_a-z0-9]*/i)
|
20
20
|
r = s.matched
|
21
21
|
if s.scan(/\(/)
|
22
22
|
tokens << [r, :function]
|
23
|
-
|
23
|
+
if s.scan(/\)/)
|
24
|
+
tokens << [s.matched, :paren_close]
|
25
|
+
else
|
26
|
+
paren_deepth += 1
|
27
|
+
end
|
24
28
|
else
|
25
29
|
tokens << [r, :ident]
|
26
30
|
state = :opperator
|
27
31
|
end
|
28
|
-
elsif s.scan(/
|
32
|
+
elsif s.scan(/((\d+(\.\d+)?)|(\.\d+))(e-?\d+)?/i)
|
29
33
|
tokens << [s.matched, :number]
|
30
34
|
state = :opperator
|
31
35
|
elsif s.scan(/\(/)
|
@@ -3,17 +3,17 @@ module RVM
|
|
3
3
|
module Math
|
4
4
|
class Tree
|
5
5
|
PRIORITIES = {
|
6
|
-
';' => -
|
7
|
-
'=' =>
|
6
|
+
';' => -100,
|
7
|
+
'=' => -50,
|
8
8
|
'(' => 0,
|
9
|
-
'+' =>
|
10
|
-
'-' =>
|
11
|
-
'*' =>
|
12
|
-
'/' =>
|
13
|
-
'^' =>
|
9
|
+
'+' => 10,
|
10
|
+
'-' => 10,
|
11
|
+
'*' => 20,
|
12
|
+
'/' => 20,
|
13
|
+
'^' => 30,
|
14
14
|
}
|
15
15
|
ASSOSICATIONS = {
|
16
|
-
';' => :right,
|
16
|
+
';' => :right,
|
17
17
|
'+' => :left,
|
18
18
|
'-' => :left,
|
19
19
|
'*' => :left,
|
@@ -36,7 +36,7 @@ module RVM
|
|
36
36
|
dbgoutput << ')'
|
37
37
|
dbgstack << fun
|
38
38
|
when :function_sep
|
39
|
-
while stack.last && (stack.last[:
|
39
|
+
while stack.last && (stack.last[:type] != :function)
|
40
40
|
output << stack.pop
|
41
41
|
dbgoutput << dbgstack.pop
|
42
42
|
end
|
@@ -51,10 +51,6 @@ module RVM
|
|
51
51
|
end
|
52
52
|
stack.pop
|
53
53
|
dbgstack.pop
|
54
|
-
if stack.last && (stack.last[:type] == :function)
|
55
|
-
output << stack.pop
|
56
|
-
dbgoutput << dbgstack.pop
|
57
|
-
end
|
58
54
|
when :number
|
59
55
|
output << {:type => :number, :number => token[0]}
|
60
56
|
dbgoutput << token[0]
|
@@ -65,7 +61,7 @@ module RVM
|
|
65
61
|
op = token[0]
|
66
62
|
ass = ASSOSICATIONS[op]
|
67
63
|
o1p = PRIORITIES[op]
|
68
|
-
while stack.last &&
|
64
|
+
while stack.last && (stack.last[:type] != :function) &&
|
69
65
|
(((ass == :left) && (o1p <= PRIORITIES[stack.last[:op]])) ||
|
70
66
|
((ass == :right) && (o1p < PRIORITIES[stack.last[:op]])))
|
71
67
|
output << stack.pop
|
@@ -74,25 +70,30 @@ module RVM
|
|
74
70
|
stack << {:type => :op, :op => op}
|
75
71
|
dbgstack << op
|
76
72
|
else
|
73
|
+
raise "Unknown element in parser tree: #{token.inspect}"
|
77
74
|
end
|
75
|
+
p(dbgstack) if $DEBUG
|
76
|
+
p(dbgoutput) if $DEBUG
|
78
77
|
end
|
79
78
|
while not stack.empty?
|
80
79
|
output << stack.pop
|
81
80
|
end
|
82
|
-
p output
|
83
81
|
gen_tree output
|
84
82
|
end
|
85
83
|
|
86
84
|
private
|
87
85
|
def Tree.gen_tree data
|
88
|
-
p data
|
89
86
|
item = data.pop
|
90
87
|
case item[:type]
|
91
88
|
when :number, :ident
|
92
89
|
item
|
93
|
-
|
90
|
+
when :op
|
94
91
|
item[:right] = gen_tree(data)
|
95
|
-
item[:
|
92
|
+
if item[:op] == ';' and data.empty?
|
93
|
+
item = item[:right]
|
94
|
+
else
|
95
|
+
item[:left] = gen_tree(data)
|
96
|
+
end
|
96
97
|
item
|
97
98
|
when :function
|
98
99
|
item[:params] = []
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/simple/tokenizer'
|
2
|
+
require File.dirname(__FILE__) + '/simple/tree'
|
3
|
+
require File.dirname(__FILE__) + '/simple/compiler'
|
4
|
+
require 'rvm/functions/math'
|
5
|
+
module RVM
|
6
|
+
module Languages
|
7
|
+
class SimpleLanguage < Language
|
8
|
+
include Simple
|
9
|
+
def compile text
|
10
|
+
Compiler.compile(Tree.generate(Tokenizer.tokenize(text)))
|
11
|
+
end
|
12
|
+
register_for :simple
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module RVM
|
2
|
+
module Languages
|
3
|
+
module Simple
|
4
|
+
class Compiler
|
5
|
+
include RVM::Interpreter
|
6
|
+
FUNCTION_MAP = {
|
7
|
+
'+' => :add,
|
8
|
+
'-' => :sub,
|
9
|
+
'/' => :div,
|
10
|
+
'*' => :mul,
|
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 :paren
|
19
|
+
compile tree[:content]
|
20
|
+
when :number
|
21
|
+
Interpreter.const(:number, tree[:number])
|
22
|
+
when :function
|
23
|
+
params = tree[:params].map {|p| compile(p)}
|
24
|
+
case tree[:op]
|
25
|
+
when '-'
|
26
|
+
FunctionCall.new :neg, params
|
27
|
+
else
|
28
|
+
FunctionCall.new tree[:op], params
|
29
|
+
end
|
30
|
+
when :ident
|
31
|
+
Interpreter::Variable.new(Interpreter.const(:string, tree[:ident]))
|
32
|
+
when :op
|
33
|
+
case tree[:op]
|
34
|
+
when ';'
|
35
|
+
s = Interpreter::Sequence.new()
|
36
|
+
if (tree[:left][:type] == :op) and (tree[:left][:op] == ';')
|
37
|
+
s += compile(tree[:left])
|
38
|
+
else
|
39
|
+
s << compile(tree[:left])
|
40
|
+
end
|
41
|
+
if (tree[:right][:type] == :op) and (tree[:right][:op] == ';')
|
42
|
+
s += compile(tree[:right])
|
43
|
+
else
|
44
|
+
s << compile(tree[:right])
|
45
|
+
end
|
46
|
+
when '='
|
47
|
+
case tree[:left][:type]
|
48
|
+
when :ident
|
49
|
+
Interpreter::Assignment.new(Interpreter.const(:string, tree[:left][:ident]),compile(tree[:right]))
|
50
|
+
when :function
|
51
|
+
body = Interpreter::Sequence.new()
|
52
|
+
i = 0
|
53
|
+
tree[:left][:params].each do |p|
|
54
|
+
raise ArgumentError, "Bad ype for a function parameter: #{p[:type]} " if p[:type] != :ident
|
55
|
+
body << Interpreter::Assignment.new(Interpreter.const(:string, p[:ident]), Interpreter::Parameter.new(Interpreter.const(:number, i)))
|
56
|
+
i += 1
|
57
|
+
end
|
58
|
+
body << compile(tree[:right])
|
59
|
+
Interpreter::FunctionDefinition.new(Interpreter.const(:string, tree[:left][:op]), Interpreter.const(:block,body))
|
60
|
+
|
61
|
+
else
|
62
|
+
raise ArgumentError, "Bad name for a variable!"
|
63
|
+
end
|
64
|
+
else
|
65
|
+
FunctionCall.new(FUNCTION_MAP[tree[:op]], [compile(tree[:left]),compile(tree[:right])])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
module RVM
|
3
|
+
module Languages
|
4
|
+
module Simple
|
5
|
+
class Tokenizer
|
6
|
+
|
7
|
+
def Tokenizer.tokenize str
|
8
|
+
s = StringScanner.new str
|
9
|
+
tokens = []
|
10
|
+
state = :number
|
11
|
+
paren_deepth = 0
|
12
|
+
block_deepth = 0
|
13
|
+
while not s.eos?
|
14
|
+
s.skip(/\s*/)
|
15
|
+
if state == :number
|
16
|
+
if s.scan(/[+-]+/)
|
17
|
+
t = s.matched.gsub('+','').gsub('--','')
|
18
|
+
if t == '-'
|
19
|
+
tokens << [t, :function]
|
20
|
+
end
|
21
|
+
elsif s.scan(/[a-z][a-z0-9]*/i)
|
22
|
+
r = s.matched
|
23
|
+
if s.scan(/[(]/)
|
24
|
+
tokens << [r, :function]
|
25
|
+
paren_deepth += 1
|
26
|
+
else
|
27
|
+
tokens << [r, :ident]
|
28
|
+
state = :opperator
|
29
|
+
end
|
30
|
+
elsif s.scan(/[\d]+(\.\d+)?/i)
|
31
|
+
tokens << [s.matched, :number]
|
32
|
+
state = :opperator
|
33
|
+
elsif s.scan(/[(]/)
|
34
|
+
paren_deepth += 1
|
35
|
+
tokens << [s.matched, :paren_open]
|
36
|
+
elsif s.scan(/[{]/)
|
37
|
+
block_deepth += 1
|
38
|
+
tokens << [s.matched, :block_close]
|
39
|
+
else
|
40
|
+
raise "Unknown literal in term at positin #{s.pos}."
|
41
|
+
end
|
42
|
+
else
|
43
|
+
if s.scan(/[)]/)
|
44
|
+
paren_deepth -= 1
|
45
|
+
raise "Unmatched ')' parenthes at #{s.pos}" if paren_deepth < 0
|
46
|
+
tokens << [s.matched, :paren_close]
|
47
|
+
elsif s.scan(/[}]/)
|
48
|
+
block_deepth -= 1
|
49
|
+
raise "Unmatched '}' parenthes at #{s.pos}" if paren_deepth < 0
|
50
|
+
tokens << [s.matched, :block_close]
|
51
|
+
elsif s.scan(/,/)
|
52
|
+
tokens << [s.matched, :function_sep]
|
53
|
+
state = :number
|
54
|
+
elsif s.scan(/[*+\/^=-]/)
|
55
|
+
tokens << [s.matched, :opperator]
|
56
|
+
state = :number
|
57
|
+
elsif s.scan(/;/)
|
58
|
+
if paren_deepth == 0
|
59
|
+
tokens << [s.matched, :opperator]
|
60
|
+
state = :number
|
61
|
+
else
|
62
|
+
raise "Expression sepperator enclosed in parenthes at #{s.pos}"
|
63
|
+
end
|
64
|
+
else
|
65
|
+
raise "Unknown literal in term at positin #{s.pos}."
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
tokens
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
module RVM
|
2
|
+
module Languages
|
3
|
+
module Simple
|
4
|
+
class Tree
|
5
|
+
PRIORITIES = {
|
6
|
+
|
7
|
+
';' => -100,
|
8
|
+
'{' => -50,
|
9
|
+
'=' => -50,
|
10
|
+
'(' => 0,
|
11
|
+
'+' => 10,
|
12
|
+
'-' => 10,
|
13
|
+
'*' => 20,
|
14
|
+
'/' => 20,
|
15
|
+
'^' => 30,
|
16
|
+
}
|
17
|
+
ASSOSICATIONS = {
|
18
|
+
'{' => :left,
|
19
|
+
'+' => :left,
|
20
|
+
'-' => :left,
|
21
|
+
'*' => :left,
|
22
|
+
'/' => :left,
|
23
|
+
';' => :right,
|
24
|
+
'^' => :right,
|
25
|
+
'=' => :right
|
26
|
+
}
|
27
|
+
PARENS = {
|
28
|
+
'(' => ')',
|
29
|
+
'{' => '}'
|
30
|
+
}
|
31
|
+
def Tree.generate tokens
|
32
|
+
output = []
|
33
|
+
dbgoutput = []
|
34
|
+
pstack = []
|
35
|
+
stack = []
|
36
|
+
dbgstack = []
|
37
|
+
|
38
|
+
while not tokens.empty?
|
39
|
+
token = tokens.shift
|
40
|
+
case token[1]
|
41
|
+
when :function
|
42
|
+
fun = token[0]
|
43
|
+
stack << {:type => :function, :op => fun}
|
44
|
+
output << {:type => :function_end}
|
45
|
+
dbgoutput << ')'
|
46
|
+
dbgstack << fun
|
47
|
+
when :function_sep
|
48
|
+
while stack.last && (stack.last[:type] != :function)
|
49
|
+
output << stack.pop
|
50
|
+
dbgoutput << dbgstack.pop
|
51
|
+
end
|
52
|
+
#Parenthetes
|
53
|
+
when :paren_open
|
54
|
+
op = token[0]
|
55
|
+
pstack << op
|
56
|
+
stack << {:type => :paren, :op => op}
|
57
|
+
dbgstack << op
|
58
|
+
when :paren_close
|
59
|
+
while stack.last && (stack.last[:op] != ')')
|
60
|
+
output << stack.pop
|
61
|
+
dbgoutput << dbgstack.pop
|
62
|
+
end
|
63
|
+
stack.pop
|
64
|
+
dbgstack.pop
|
65
|
+
if stack.last && (stack.last[:type] == :function)
|
66
|
+
output << stack.pop
|
67
|
+
dbgoutput << dbgstack.pop
|
68
|
+
end
|
69
|
+
#Blocks
|
70
|
+
when :block_open
|
71
|
+
op = token[0]
|
72
|
+
pstack << op
|
73
|
+
stack << {:type => :block, :op => op}
|
74
|
+
dbgstack << op
|
75
|
+
when :block_close
|
76
|
+
while stack.last && (stack.last[:op] != '}')
|
77
|
+
output << stack.pop
|
78
|
+
dbgoutput << dbgstack.pop
|
79
|
+
end
|
80
|
+
stack.pop
|
81
|
+
dbgstack.pop
|
82
|
+
#numbers
|
83
|
+
when :number
|
84
|
+
output << {:type => :number, :number => token[0]}
|
85
|
+
dbgoutput << token[0]
|
86
|
+
#idents
|
87
|
+
when :ident
|
88
|
+
output << {:type => :ident, :ident => token[0]}
|
89
|
+
dbgoutput << token[0]
|
90
|
+
# opperators
|
91
|
+
when :opperator
|
92
|
+
op = token[0]
|
93
|
+
ass = ASSOSICATIONS[op]
|
94
|
+
o1p = PRIORITIES[op]
|
95
|
+
while stack.last &&
|
96
|
+
(((ass == :left) && (o1p <= PRIORITIES[stack.last[:op]])) ||
|
97
|
+
((ass == :right) && (o1p < PRIORITIES[stack.last[:op]])))
|
98
|
+
output << stack.pop
|
99
|
+
dbgoutput << dbgstack.pop
|
100
|
+
end
|
101
|
+
stack << {:type => :op, :op => op}
|
102
|
+
dbgstack << op
|
103
|
+
else
|
104
|
+
end
|
105
|
+
if $DEBUG
|
106
|
+
p dbgstack
|
107
|
+
p dbgoutput
|
108
|
+
end
|
109
|
+
end
|
110
|
+
while not stack.empty?
|
111
|
+
output << stack.pop
|
112
|
+
end
|
113
|
+
p output if $DEBUG
|
114
|
+
gen_tree output
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def Tree.gen_tree data
|
119
|
+
p data if $DEBUG
|
120
|
+
item = data.pop
|
121
|
+
p item
|
122
|
+
case item[:type]
|
123
|
+
when :number, :ident
|
124
|
+
item
|
125
|
+
when :op
|
126
|
+
item[:right] = gen_tree(data)
|
127
|
+
item[:left] = gen_tree(data)
|
128
|
+
item
|
129
|
+
when :paren, :block
|
130
|
+
item[:content] = gen_tree(data)
|
131
|
+
item
|
132
|
+
when :function
|
133
|
+
item[:params] = []
|
134
|
+
while data.last && data.last[:type] != :function_end
|
135
|
+
item[:params] << gen_tree(data)
|
136
|
+
end
|
137
|
+
data.pop
|
138
|
+
item
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rVM
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Heinz N. Gies
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-03-
|
12
|
+
date: 2008-03-06 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -73,6 +73,11 @@ files:
|
|
73
73
|
- lib/rvm/languages/math/tokenizer.rb
|
74
74
|
- lib/rvm/languages/math/tree.rb
|
75
75
|
- lib/rvm/languages/math.rb
|
76
|
+
- lib/rvm/languages/simple
|
77
|
+
- lib/rvm/languages/simple/compiler.rb
|
78
|
+
- lib/rvm/languages/simple/tokenizer.rb
|
79
|
+
- lib/rvm/languages/simple/tree.rb
|
80
|
+
- lib/rvm/languages/simple.rb
|
76
81
|
- lib/rvm/languages.rb
|
77
82
|
- lib/rvm/plugin.rb
|
78
83
|
- lib/rvm.rb
|