rVM 0.0.14 → 0.0.17
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 +4 -4
- data/bin/rvm +229 -0
- data/lib/rvm.rb +6 -6
- data/lib/rvm/acts_as_rvm_type.rb +26 -3
- data/lib/rvm/classes.rb +9 -15
- data/lib/rvm/classes/block.rb +8 -3
- data/lib/rvm/classes/class.rb +2 -2
- data/lib/rvm/classes/list.rb +1 -1
- data/lib/rvm/classes/null.rb +31 -0
- data/lib/rvm/classes/number.rb +4 -0
- data/lib/rvm/classes/object.rb +1 -1
- data/lib/rvm/classes/string.rb +6 -1
- data/lib/rvm/environment.rb +256 -0
- data/lib/rvm/functions.rb +9 -4
- data/lib/rvm/functions/array.rb +26 -2
- data/lib/rvm/functions/array/append.rb +31 -1
- data/lib/rvm/functions/array/at.rb +29 -1
- data/lib/rvm/functions/array/set_at.rb +29 -0
- data/lib/rvm/functions/association/assoc_get.rb +34 -0
- data/lib/rvm/functions/association/assoc_set.rb +32 -0
- data/lib/rvm/functions/bitwise.rb +3 -0
- data/lib/rvm/functions/bitwise/bitwise_and.rb +41 -0
- data/lib/rvm/functions/bitwise/bitwise_or.rb +41 -0
- data/lib/rvm/functions/bitwise/bitwise_xor.rb +41 -0
- data/lib/rvm/functions/collection/get.rb +37 -4
- data/lib/rvm/functions/collection/set.rb +37 -3
- data/lib/rvm/functions/collection/size.rb +33 -1
- data/lib/rvm/functions/general/cmp.rb +35 -7
- data/lib/rvm/functions/general/eq.rb +29 -0
- data/lib/rvm/functions/general/gt.rb +29 -0
- data/lib/rvm/functions/general/gte.rb +29 -0
- data/lib/rvm/functions/general/lt.rb +29 -0
- data/lib/rvm/functions/general/lte.rb +29 -0
- data/lib/rvm/functions/general/neq.rb +5 -0
- data/lib/rvm/functions/io/print.rb +38 -8
- data/lib/rvm/functions/list/align.rb +25 -1
- data/lib/rvm/functions/list/join.rb +27 -0
- data/lib/rvm/functions/list/map.rb +34 -0
- data/lib/rvm/functions/list/split.rb +31 -0
- data/lib/rvm/functions/logic/and.rb +36 -2
- data/lib/rvm/functions/logic/not.rb +27 -0
- data/lib/rvm/functions/logic/or.rb +32 -2
- data/lib/rvm/functions/math/add.rb +25 -0
- data/lib/rvm/functions/math/cos.rb +39 -0
- data/lib/rvm/functions/math/div.rb +25 -0
- data/lib/rvm/functions/math/mod.rb +41 -0
- data/lib/rvm/functions/math/mul.rb +25 -0
- data/lib/rvm/functions/math/neg.rb +25 -0
- data/lib/rvm/functions/math/power.rb +25 -0
- data/lib/rvm/functions/math/shl.rb +41 -0
- data/lib/rvm/functions/math/shr.rb +41 -0
- data/lib/rvm/functions/math/sin.rb +39 -0
- data/lib/rvm/functions/math/sub.rb +25 -0
- data/lib/rvm/functions/math/tan.rb +39 -0
- data/lib/rvm/functions/rails/print.rb +33 -3
- data/lib/rvm/interpreter.rb +405 -272
- data/lib/rvm/languages.rb +45 -11
- data/lib/rvm/languages/brainfuck.rb +15 -16
- data/lib/rvm/languages/ecma.rb +4 -1257
- data/lib/rvm/languages/ecma/compiler.rb +1353 -0
- data/lib/rvm/languages/ecma/core-math.js +84 -0
- data/lib/rvm/languages/math.rb +9 -16
- data/lib/rvm/languages/math/compiler.rb +9 -9
- data/lib/rvm/languages/math/tokenizer.rb +1 -1
- data/lib/rvm/languages/math/tree.rb +14 -14
- data/lib/rvm/library.rb +26 -18
- data/lib/rvm/optimisation.rb +109 -0
- data/lib/rvm/plugin.rb +109 -45
- data/lib/rvm/rails.rb +79 -54
- data/spec/classes/atom/association_spec.rb +8 -8
- data/spec/classes/atom/block_spec.rb +8 -5
- data/spec/classes/atom/boolean_spec.rb +1 -1
- data/spec/classes/atom/error_spec.rb +1 -1
- data/spec/classes/atom/list_spec.rb +1 -1
- data/spec/classes/atom/number_spec.rb +2 -2
- data/spec/classes/atom/string_spec.rb +1 -1
- data/spec/languages/ecma/ecma_spec.rb +94 -38
- data/spec/languages/ecma/json_spec.rb +4 -4
- data/spec/languages/math/compiler_spec.rb +5 -5
- data/spec/languages/math/tokenizer_spec.rb +1 -1
- data/spec/languages/math/tree_spec.rb +1 -1
- data/spec/{base → rvm}/class_spec.rb +2 -2
- data/spec/{base/interpreter → rvm}/enviroment_spec.rb +19 -9
- data/spec/{base → rvm}/function_spec.rb +2 -2
- data/spec/{functions → rvm/functions}/association/assoc_get_spec.rb +2 -2
- data/spec/{functions → rvm/functions}/association/assoc_set_spec.rb +2 -2
- data/spec/rvm/functions/collection/get_spec.rb +12 -0
- data/spec/rvm/functions/collection/set_spec.rb +10 -0
- data/spec/rvm/functions/collection/size_spec.rb +10 -0
- data/spec/{functions → rvm/functions}/list/split_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/ansi_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/capstr_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/center_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/ljust_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/regmatch_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/rjust_spec.rb +3 -3
- data/spec/{base → rvm}/interpreter/assignment_spec.rb +1 -1
- data/spec/rvm/interpreter/condition_spec.rb +103 -0
- data/spec/{base → rvm}/interpreter/constant_spec.rb +1 -1
- data/spec/rvm/interpreter/core_call_spec.rb +72 -0
- data/spec/{base → rvm}/interpreter/interpreter_spec.rb +1 -1
- data/spec/{base → rvm}/interpreter/parameter_spec.rb +1 -1
- data/spec/rvm/interpreter/sequence_spec.rb +47 -0
- data/spec/{base → rvm}/interpreter/variable_spec.rb +1 -1
- data/spec/{base → rvm}/plugin_spec.rb +2 -2
- metadata +66 -35
- data/lib/rake/helpers/code_statistics.rb +0 -167
- data/spec/base/interpreter/condition_spec.rb +0 -47
- data/spec/base/interpreter/function_call_spec.rb +0 -72
- data/spec/base/interpreter/sequence_spec.rb +0 -20
- data/spec/functions/collection/get_spec.rb +0 -12
- data/spec/functions/collection/set_spec.rb +0 -10
- data/spec/functions/collection/size_spec.rb +0 -10
data/lib/rvm/interpreter.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'rvm/functions'
|
2
|
+
require 'rvm/classes'
|
3
|
+
require 'rvm/environment'
|
3
4
|
module RVM
|
4
5
|
# This Module hold all the VM classes that are used to execute and evaluate
|
5
6
|
# code for the VM, so it is quite important to read and understand if you
|
@@ -17,6 +18,35 @@ module RVM
|
|
17
18
|
module Interpreter
|
18
19
|
|
19
20
|
|
21
|
+
class RuntimeError < Exception
|
22
|
+
attr_accessor :total, :line, :char, :error_message
|
23
|
+
def initialize message, total = nil, line = nil, char = nil
|
24
|
+
super()
|
25
|
+
@line = line
|
26
|
+
@char = char
|
27
|
+
@total = total
|
28
|
+
@error_message = message
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
res = "Runtime error"
|
33
|
+
if @total or @line
|
34
|
+
res << " at"
|
35
|
+
end
|
36
|
+
if @total
|
37
|
+
res << " character #{@total}"
|
38
|
+
end
|
39
|
+
if @line
|
40
|
+
res << " line #{@line}"
|
41
|
+
end
|
42
|
+
if @char
|
43
|
+
res << ":#{@char}"
|
44
|
+
end
|
45
|
+
res << ": #{@error_message}"
|
46
|
+
res
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
20
50
|
# Helper class to be parented to other Interpreter calsses for checks and
|
21
51
|
# including general behavior.
|
22
52
|
class Element
|
@@ -28,6 +58,12 @@ module RVM
|
|
28
58
|
def initialize pos
|
29
59
|
@pos = pos
|
30
60
|
end
|
61
|
+
|
62
|
+
#Placeholder for optimization
|
63
|
+
def optimize
|
64
|
+
RVM::debug "Optimizung #{self}" if $DEBUG
|
65
|
+
self
|
66
|
+
end
|
31
67
|
end
|
32
68
|
|
33
69
|
# This is a helper fuctio to quickly generate a empty enviorment.
|
@@ -37,7 +73,7 @@ module RVM
|
|
37
73
|
# :functions can be a hash to hold functions defined in the the scope
|
38
74
|
def Interpreter.env aenv = {}
|
39
75
|
RVM::debug "Interpreter.env" if $DEBUG
|
40
|
-
|
76
|
+
Environment.new aenv
|
41
77
|
end
|
42
78
|
|
43
79
|
# This is a helper function that generates a constat of a simple constant,
|
@@ -47,234 +83,6 @@ module RVM
|
|
47
83
|
RVM::Interpreter::Constant.new(RVM::Classes[type].new(value), pos)
|
48
84
|
end
|
49
85
|
|
50
|
-
# This class is used to store the variable content to allow chross
|
51
|
-
# refferencing and passing variables upwards through different scopes.
|
52
|
-
class VariableStorage
|
53
|
-
|
54
|
-
# Lets the script read the value of the variable.
|
55
|
-
attr_reader :val
|
56
|
-
|
57
|
-
# The storage is initialized with two optional parameters:
|
58
|
-
#
|
59
|
-
# +val+:: which is the value to be stores.
|
60
|
-
# +writable+:: which when set to false prevents the variable to be
|
61
|
-
# written, which might be helpfull when trying to publish
|
62
|
-
# some data to a script that is not supposed to be changed.
|
63
|
-
def initialize val = nil, writable = true
|
64
|
-
@writable = writable
|
65
|
-
@val = val
|
66
|
-
end
|
67
|
-
|
68
|
-
# Used to write to the variable, if +writable+ was set to false this
|
69
|
-
# will have no effect.
|
70
|
-
#
|
71
|
-
# It returns the new value set or, if +writable+ was false the old value.
|
72
|
-
def val=v
|
73
|
-
@val = v if @writable
|
74
|
-
@val
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# The callback storang is used to help publishing variables from a ruby
|
79
|
-
# class to the rVM, it will get and set the variables during runtime.
|
80
|
-
#
|
81
|
-
# It will link getter and setter methods and not the instance variable
|
82
|
-
# itself, which means also computed attributes can be handled by this.
|
83
|
-
#
|
84
|
-
# This class is made if it is nessessary that a script always has access
|
85
|
-
# to constantly chaning data within a object and not only deal with static
|
86
|
-
# values set once.
|
87
|
-
class VariableStorageCallback < VariableStorage
|
88
|
-
# The construtor takes 3 parameters to specifie the object it shold
|
89
|
-
# be linked with, the attribute to handle and +writable+ that will define
|
90
|
-
# if the VariableStorage can be written too by the script.
|
91
|
-
#
|
92
|
-
# var is expected to by a Symbol equal the the function, the getter will
|
93
|
-
# call obj.var and the setter will call obj.var=.
|
94
|
-
#
|
95
|
-
def initialize obj, var, writable = true
|
96
|
-
super(nil, writable)
|
97
|
-
@obj = obj
|
98
|
-
@get = var.to_sym
|
99
|
-
@set = "#{var}=".to_sym
|
100
|
-
end
|
101
|
-
|
102
|
-
# This methods sets the variable passed to the object by sending it to
|
103
|
-
# the setter method.
|
104
|
-
def val= v
|
105
|
-
@obj.send(@set,v) if @writable
|
106
|
-
@obj.send(@get)
|
107
|
-
end
|
108
|
-
|
109
|
-
# The getter will call the getter of the object to handle to get the
|
110
|
-
# current value.
|
111
|
-
def val
|
112
|
-
@obj.send(@get)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# This class represents the enviroment, the memory so to say for the VM.
|
117
|
-
# The enviroment holds enviromental variables like who executes the
|
118
|
-
# code, on what object it runs, what parameters where passed to the call
|
119
|
-
# and as well, and perhaps most importantly the local variables.
|
120
|
-
#
|
121
|
-
# Some of the functions called will initialize new enviroments, as
|
122
|
-
# for example function calls.
|
123
|
-
class Enviroment
|
124
|
-
# This creates a new enviroment enviroment, it generates a new env
|
125
|
-
# default variables are set if not defined in +data+.
|
126
|
-
#
|
127
|
-
# If +oldenv+ is provided it will also fall back to see if not
|
128
|
-
# existing variables
|
129
|
-
#
|
130
|
-
# +init_data+ is a hash that can be passed the following values:
|
131
|
-
# :locals:: A hash with local variables mapped name => value
|
132
|
-
# :functions:: A hash with functions for the scope.
|
133
|
-
# :evaldeepth:: A fixnum that indicates how deep the evaluation is.
|
134
|
-
# :params:: A array that holds local parameters for example for functions
|
135
|
-
# or blocks.
|
136
|
-
def initialize init_data = {}, oldenv=nil
|
137
|
-
|
138
|
-
@data = {
|
139
|
-
:locals => {},
|
140
|
-
:functions => {},
|
141
|
-
:evaldeepth => 0,
|
142
|
-
:params => []
|
143
|
-
}.merge(init_data)
|
144
|
-
|
145
|
-
|
146
|
-
# Make sure that all locals that are passed, are actually in a variable
|
147
|
-
# storage.
|
148
|
-
if init_data[:locals]
|
149
|
-
# For easy access we get us a link to the locals
|
150
|
-
locals = @data[:locals]
|
151
|
-
# Now we itterate through them
|
152
|
-
init_data[:locals].each do |k,v|
|
153
|
-
#For every variable that isn't already in a storage
|
154
|
-
if not v.is_a?(VariableStorage)
|
155
|
-
# We put it in a storage to assure the enviroment is propper.
|
156
|
-
locals[k] = VariableStorage.new(v)
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
|
161
|
-
# We store the priviouse enviroment to look upwards through the scopes
|
162
|
-
# for priviouse defined variables, if no old enviroment was given we
|
163
|
-
# set the priviose scope to an empty hash.
|
164
|
-
@prev = oldenv || {}
|
165
|
-
RVM::debug "data: #{data}\noldenv:#{oldenv}" if $DEBUG
|
166
|
-
RVM::debug "Creating new enviroment with: #{@data.inspect} as child" +
|
167
|
-
" of #{@prev}" if $DEBUG
|
168
|
-
end
|
169
|
-
|
170
|
-
# Allows raw access to the data, it should not be used unless
|
171
|
-
# somoene knows pretty exactly what they are doing.
|
172
|
-
def data
|
173
|
-
@data
|
174
|
-
end
|
175
|
-
|
176
|
-
# returns a parameter that was passed to the call. For function calls
|
177
|
-
# this is especially important, it beginns with 0.
|
178
|
-
#
|
179
|
-
# If the current enviroment does not have the given paramenter it tries
|
180
|
-
# it's 'oldenv' attrib to see if that had a object.
|
181
|
-
def param i
|
182
|
-
RVM::debug "Getting param #{i} (#{@data[:params][i]})" if $DEBUG
|
183
|
-
@data[:params][i] || @prev.param(i)
|
184
|
-
end
|
185
|
-
|
186
|
-
# Returns a local variable with the given name.
|
187
|
-
#
|
188
|
-
# If the current enviroment does not have any local variables with
|
189
|
-
# the given name it tries the privious enviroments.
|
190
|
-
#
|
191
|
-
# This returns the +VariableData+ object NOT the value of the variable.
|
192
|
-
# Use +read_var_val+ if you want to read the value of a variable.
|
193
|
-
def [] k
|
194
|
-
r = @data[:locals][k] || @prev[k]
|
195
|
-
RVM::debug "Getting variable #{k} (#{r})" if $DEBUG
|
196
|
-
r
|
197
|
-
end
|
198
|
-
|
199
|
-
# Sets a local variable witin the enviroment.
|
200
|
-
#
|
201
|
-
# If the variable exists in a 'higher' enviroment the value is changed.
|
202
|
-
#
|
203
|
-
# If not it is newly created in the current enviroment and thus not
|
204
|
-
# visible in upper enviroments.
|
205
|
-
def []= name, value
|
206
|
-
RVM::debug "Setting variable #{name} to #{value}" if $DEBUG
|
207
|
-
# First we try to get the variable storage of the variable requested.
|
208
|
-
# This will work if it was defined in this or a priviouse scope.
|
209
|
-
if (res = self[name])
|
210
|
-
# If we found the storage we'll change it's value and not define it
|
211
|
-
# again.
|
212
|
-
res.val = value
|
213
|
-
else
|
214
|
-
# If we didn't found a Variable storage, the variable wasn't defined
|
215
|
-
# before so we make a new VariableStorage and save the variable in
|
216
|
-
# it.
|
217
|
-
@data[:locals][name] = VariableStorage.new(value)
|
218
|
-
end
|
219
|
-
value
|
220
|
-
end
|
221
|
-
|
222
|
-
# Defines a varialbe in the current scope, priviose variables are not
|
223
|
-
# changed.
|
224
|
-
# This is needed for local varialbe declarations that overwrite priviouse
|
225
|
-
# globals.
|
226
|
-
def declare name, value
|
227
|
-
@data[:locals][name] = VariableStorage.new(value)
|
228
|
-
end
|
229
|
-
|
230
|
-
# Returns a function for the enviroment. If the current enviroment does
|
231
|
-
# not hold a function with the requested name it checks through the
|
232
|
-
# entire tree if a function can be found in the outer scopes.
|
233
|
-
#
|
234
|
-
# The result is to be execpted to be of the +RVM::Classes::Block+ class.
|
235
|
-
def function name
|
236
|
-
# Try to get the function in the local defintions.
|
237
|
-
fun = @data[:functions][name]
|
238
|
-
# if that fails and we have a previous enviroment check that
|
239
|
-
fun = @prev.function(name) if fun.nil? and @prev.is_a? Enviroment
|
240
|
-
RVM::debug "Getting functon #{name} (#{fun})" if $DEBUG
|
241
|
-
# return what was found
|
242
|
-
fun
|
243
|
-
end
|
244
|
-
|
245
|
-
# This defines a function within the enviroment and lets you call it later
|
246
|
-
# on.
|
247
|
-
#
|
248
|
-
# It expects the body to be a RVM::Classes::Block so it can be executed.
|
249
|
-
# The return value is the given block
|
250
|
-
def def_function name, body
|
251
|
-
if not body.is_a?(RVM::Classes::Block)
|
252
|
-
raise(ArgumentError, "Function definitions must be of block class.")
|
253
|
-
end
|
254
|
-
@data[:functions][name] = body
|
255
|
-
RVM::debug "Setting functon #{name} (#{body})" if $DEBUG
|
256
|
-
body
|
257
|
-
end
|
258
|
-
|
259
|
-
# This functin is closely related to +[]+ with the difference that it
|
260
|
-
# returns the value and not the VariableStorage object.
|
261
|
-
# It is equivalent to [].val just that it also deals with nil ojects and
|
262
|
-
# and returns a RVM::Classes::Error if the requested variable wasn't
|
263
|
-
# found.
|
264
|
-
def read_var_val name
|
265
|
-
|
266
|
-
r = nil
|
267
|
-
if (v = self[name])
|
268
|
-
r = v.val
|
269
|
-
else
|
270
|
-
r = RVM::Classes[:error].new(0, "Variable #{name} is not defined.")
|
271
|
-
end
|
272
|
-
RVM::debug "Getting variable value #{name} (#{r})" if $DEBUG
|
273
|
-
r
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
|
278
86
|
# Jumps into object context (anther way beside using send).
|
279
87
|
#
|
280
88
|
# The special variable :self (attention, not to be mixed wiht 'self')
|
@@ -290,7 +98,6 @@ module RVM
|
|
290
98
|
# This will execute my_code in a way pretty much equal to instance_eval in
|
291
99
|
# ruby would do.
|
292
100
|
class ObjectContext < Element
|
293
|
-
|
294
101
|
# The constructor takes 3 arguments, the first beeing the object of which
|
295
102
|
# the scope is taken, the second is the code to be executed in the objects
|
296
103
|
# scope and the third is the position, to be set by the compiler.
|
@@ -303,17 +110,29 @@ module RVM
|
|
303
110
|
@code = code
|
304
111
|
end
|
305
112
|
|
306
|
-
|
113
|
+
def pretty_print(q)
|
114
|
+
q.group 1, "#{@object}.{", "}" do
|
115
|
+
q.pp @code
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Execute for this Interpreter element by creating a new environment ,
|
307
120
|
# setting it's variables and functions to those definded by the object's
|
308
121
|
# +functions+ and +variables+ methods. It also sets the :self variable in
|
309
|
-
# the new
|
122
|
+
# the new environment to the object.
|
310
123
|
def execute env
|
311
124
|
#The new
|
312
125
|
obj = @object.execute(env)
|
313
|
-
new_env = Interpreter::
|
126
|
+
new_env = Interpreter::Environment.new({:functions => obj.functions, :locals => obj.variables}, env)
|
314
127
|
new_env.declare(:self, obj)
|
315
128
|
@code.execute(new_env)
|
316
129
|
end
|
130
|
+
|
131
|
+
def optimize
|
132
|
+
@object = @object.optimize
|
133
|
+
@code = @code.optimize
|
134
|
+
super
|
135
|
+
end
|
317
136
|
end
|
318
137
|
|
319
138
|
|
@@ -337,6 +156,12 @@ module RVM
|
|
337
156
|
@object.object_functions[@name.execute(env)] = @function
|
338
157
|
@function
|
339
158
|
end
|
159
|
+
|
160
|
+
def optimize
|
161
|
+
@object = @object.optimize
|
162
|
+
@function = @function.optimize
|
163
|
+
super
|
164
|
+
end
|
340
165
|
end
|
341
166
|
|
342
167
|
# A block localizes variables, do not mix this up with the
|
@@ -351,13 +176,25 @@ module RVM
|
|
351
176
|
@content = content
|
352
177
|
end
|
353
178
|
|
354
|
-
|
179
|
+
def pretty_print(q)
|
180
|
+
first = true
|
181
|
+
q.group 1, "{", "}" do
|
182
|
+
q.pp @content
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# When executed a temporary environment is created with the passed
|
355
187
|
# Enviroement as a parent to it.
|
356
|
-
# This new
|
188
|
+
# This new environment is discaded after the execution.
|
357
189
|
def execute env
|
358
|
-
tenv =
|
190
|
+
tenv = Environment.new({}, env)
|
359
191
|
@content.execute tenv
|
360
192
|
end
|
193
|
+
|
194
|
+
def optimize
|
195
|
+
@content = @content.optimize
|
196
|
+
super
|
197
|
+
end
|
361
198
|
end
|
362
199
|
|
363
200
|
|
@@ -388,6 +225,18 @@ module RVM
|
|
388
225
|
@override = override
|
389
226
|
end
|
390
227
|
|
228
|
+
def pretty_print(q)
|
229
|
+
first = true
|
230
|
+
q.text "function "
|
231
|
+
if @name.is_a? Constant
|
232
|
+
q.text @name.value
|
233
|
+
else
|
234
|
+
q.pp @name
|
235
|
+
end
|
236
|
+
q.text "()"
|
237
|
+
q.pp @body
|
238
|
+
end
|
239
|
+
|
391
240
|
# When executed the FunctionDefinition first checks if a function with the
|
392
241
|
# same name is already defined. If it is and +override+ wasn't set to ture
|
393
242
|
# it trows a Exception. Otherwise it defines the function, deleting the
|
@@ -396,11 +245,17 @@ module RVM
|
|
396
245
|
# It returns the body of the newly defined function.
|
397
246
|
def execute env
|
398
247
|
if (not @override) and env.function(@name)
|
399
|
-
raise "Function #{@name} already defined
|
248
|
+
raise RuntimeError.new("Function #{@name} already defined", @pos[0], @pos[1], @pos[2])
|
400
249
|
end
|
401
250
|
env.def_function(@name.execute(env),@body)
|
402
251
|
@body
|
403
252
|
end
|
253
|
+
|
254
|
+
def optimize
|
255
|
+
@body = @body.optimize
|
256
|
+
@name = @name.optimize
|
257
|
+
super
|
258
|
+
end
|
404
259
|
end
|
405
260
|
|
406
261
|
|
@@ -427,6 +282,14 @@ module RVM
|
|
427
282
|
@body = body
|
428
283
|
end
|
429
284
|
|
285
|
+
def pretty_print(q)
|
286
|
+
first = true
|
287
|
+
q.text "while ("
|
288
|
+
q.pp @condition
|
289
|
+
q.text ")"
|
290
|
+
q.pp @body
|
291
|
+
end
|
292
|
+
|
430
293
|
# The loop will execute as long as the code passed as condition evaluates
|
431
294
|
# to is_true?.
|
432
295
|
# Once the loop stops executing the return value is the result of the last
|
@@ -440,6 +303,13 @@ module RVM
|
|
440
303
|
end
|
441
304
|
end
|
442
305
|
|
306
|
+
# Optimization of the loop
|
307
|
+
def optimize
|
308
|
+
@condition = @condition.optimize
|
309
|
+
@body = @body.optimize
|
310
|
+
super
|
311
|
+
end
|
312
|
+
|
443
313
|
|
444
314
|
# A constant, it evaluates to thevalue given and end the evaluation.
|
445
315
|
# Meaning that no further execution is done in this tree branch, so the
|
@@ -456,6 +326,10 @@ module RVM
|
|
456
326
|
@value.data_type
|
457
327
|
end
|
458
328
|
|
329
|
+
def pretty_print(q)
|
330
|
+
q.pp @value
|
331
|
+
end
|
332
|
+
|
459
333
|
# Comparing a constant with something has two ways to go, if the object
|
460
334
|
# it is compared to is a Constant the two values are compared. If not the
|
461
335
|
# Constant compares the value with the passed object.
|
@@ -464,7 +338,6 @@ module RVM
|
|
464
338
|
@value == v.value
|
465
339
|
else
|
466
340
|
@value == v
|
467
|
-
false
|
468
341
|
end
|
469
342
|
end
|
470
343
|
|
@@ -517,6 +390,17 @@ module RVM
|
|
517
390
|
@false_case = false_case
|
518
391
|
end
|
519
392
|
|
393
|
+
def pretty_print(q)
|
394
|
+
first = true
|
395
|
+
q.text "if ("
|
396
|
+
q.pp @value
|
397
|
+
q.text ")"
|
398
|
+
q.pp @true_case
|
399
|
+
if (@false_case)
|
400
|
+
q.text "else"
|
401
|
+
q.pp @false_case
|
402
|
+
end
|
403
|
+
end
|
520
404
|
|
521
405
|
# The data type of a condition is tried to evaluate by checking if the
|
522
406
|
# type is the same for both conditions, if so the common type is returned,
|
@@ -546,6 +430,25 @@ module RVM
|
|
546
430
|
@false_case.execute env
|
547
431
|
end
|
548
432
|
end
|
433
|
+
|
434
|
+
#Optimizes the condition (checks for trivial cases)
|
435
|
+
def optimize
|
436
|
+
@value = @value.optimize
|
437
|
+
@true_case = @true_case.optimize
|
438
|
+
@false_case = @false_case.optimize if @false_case
|
439
|
+
if @value.is_a? RVM::Interpreter::Constant
|
440
|
+
RVM::debug "Optimizing #{self}, with shortcuting a condition." if $DEBUG
|
441
|
+
if @value.value.is_true?
|
442
|
+
@true_case
|
443
|
+
elsif @false_case
|
444
|
+
@false_case
|
445
|
+
else
|
446
|
+
RVM::Interpreter::Sequence.new
|
447
|
+
end
|
448
|
+
else
|
449
|
+
super
|
450
|
+
end
|
451
|
+
end
|
549
452
|
end
|
550
453
|
|
551
454
|
|
@@ -575,6 +478,16 @@ module RVM
|
|
575
478
|
@value = value
|
576
479
|
end
|
577
480
|
|
481
|
+
def pretty_print(q)
|
482
|
+
if @name.is_a? Constant
|
483
|
+
q.text @name.value
|
484
|
+
else
|
485
|
+
q.pp @name
|
486
|
+
end
|
487
|
+
q.text " = "
|
488
|
+
q.pp @value
|
489
|
+
end
|
490
|
+
|
578
491
|
# The data type of a Assignment is the data type of it's value.
|
579
492
|
def data_type
|
580
493
|
@value.data_type
|
@@ -582,7 +495,7 @@ module RVM
|
|
582
495
|
|
583
496
|
# When executed the assignment first evaluates the name of the assignment
|
584
497
|
# then the value and stores the result of the executed value in the
|
585
|
-
#
|
498
|
+
# environment under the name of the executed name.
|
586
499
|
#
|
587
500
|
# The return value of the execution is the value that is assigned to the
|
588
501
|
# variable.
|
@@ -590,13 +503,23 @@ module RVM
|
|
590
503
|
RVM::debug "Executing Assignment at #{@pos}..." if $DEBUG
|
591
504
|
env[@name.execute(env).to_s] = @value.execute env
|
592
505
|
end
|
506
|
+
def optimize
|
507
|
+
@name = @name.optimize
|
508
|
+
@value = @value.optimize
|
509
|
+
if @name.is_a? RVM::Interpreter::Constant
|
510
|
+
RVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
511
|
+
RVM::Interpreter::SimpleAssignment.new(@name.value, @value, @pos)
|
512
|
+
else
|
513
|
+
super
|
514
|
+
end
|
515
|
+
end
|
593
516
|
end
|
594
517
|
|
595
518
|
# A variable declarion that sets a local variable, it will redelcare
|
596
519
|
# the variable if declared in a privouse scope.
|
597
520
|
#
|
598
521
|
# It is very closely related to the Assignment as it acts exactly alike if
|
599
|
-
# the variable is not yet existing in the
|
522
|
+
# the variable is not yet existing in the Environment .
|
600
523
|
#
|
601
524
|
# Both the +name+ and the #value# are evaluated before the assignment
|
602
525
|
# is done.
|
@@ -619,6 +542,16 @@ module RVM
|
|
619
542
|
@value = value
|
620
543
|
end
|
621
544
|
|
545
|
+
def pretty_print(q)
|
546
|
+
if @name.is_a? Constant
|
547
|
+
q.text @name.value
|
548
|
+
else
|
549
|
+
q.pp @name
|
550
|
+
end
|
551
|
+
q.text " !=! "
|
552
|
+
q.pp @value
|
553
|
+
end
|
554
|
+
|
622
555
|
# The data type of a Assignment is the data type of it's value.
|
623
556
|
def data_type
|
624
557
|
@value.data_type
|
@@ -626,9 +559,9 @@ module RVM
|
|
626
559
|
|
627
560
|
# When executed the assignment first evaluates the name of the assignment
|
628
561
|
# then the value and stores the result of the executed value in the
|
629
|
-
#
|
562
|
+
# environment under the name of the executed name.
|
630
563
|
#
|
631
|
-
# If the variable was priviosely in a
|
564
|
+
# If the variable was priviosely in a environment that lays above the
|
632
565
|
# current one in the hearachy the old value will not be altered in any
|
633
566
|
# way but a new variable declared.
|
634
567
|
#
|
@@ -638,9 +571,20 @@ module RVM
|
|
638
571
|
RVM::debug "Executing Assignment at #{@pos}..." if $DEBUG
|
639
572
|
env.declare(@name.execute(env).to_s,@value.execute(env)).val
|
640
573
|
end
|
641
|
-
|
574
|
+
|
575
|
+
def optimize
|
576
|
+
@name = @name.optimize
|
577
|
+
@value = @value.optimize
|
578
|
+
if @name.is_a? RVM::Interpreter::Constant
|
579
|
+
RVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
580
|
+
RVM::Interpreter::SimpleDeclaration.new(@name.value, @value, @pos)
|
581
|
+
else
|
582
|
+
super
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
642
586
|
|
643
|
-
# Reads the value of a variable in the
|
587
|
+
# Reads the value of a variable in the environment .
|
644
588
|
#
|
645
589
|
# The +name+ is evaluated before the variable is retrieved.
|
646
590
|
class Variable < Element
|
@@ -657,6 +601,14 @@ module RVM
|
|
657
601
|
@type = :any
|
658
602
|
end
|
659
603
|
|
604
|
+
def pretty_print(q)
|
605
|
+
if @name.is_a? Constant
|
606
|
+
q.text @name.value
|
607
|
+
else
|
608
|
+
q.pp @name
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
660
612
|
# The type can only be tretrieved when the name is aconstant
|
661
613
|
# as it can be evaluated without sideffect.
|
662
614
|
def data_type
|
@@ -666,16 +618,33 @@ module RVM
|
|
666
618
|
# When the name is a symbol, the name isn't executed and treated as a
|
667
619
|
# special variable.
|
668
620
|
# Otherwise the name is executed and converted into a string to be passed
|
669
|
-
# to the
|
621
|
+
# to the environment so it can go and collect the value.
|
670
622
|
def execute env
|
671
623
|
RVM::debug "Executing Variable at #{@pos}..." if $DEBUG
|
672
|
-
|
673
|
-
|
674
|
-
|
624
|
+
begin
|
625
|
+
n = @name
|
626
|
+
if not @name.is_a?(Symbol)
|
627
|
+
n = n.execute(env).to_s
|
628
|
+
end
|
629
|
+
r = env.read_var_val(n)
|
630
|
+
@type = r.data_type if r.respond_to?(:data_type)
|
631
|
+
r
|
632
|
+
rescue Exception => e
|
633
|
+
raise RuntimeError.new("Failed to get Varialbe #{e}", @pos[0], @pos[1], @pos[2])
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
def optimize
|
638
|
+
@name = @name.optimize if not @name.is_a?(Symbol)
|
639
|
+
if @name.is_a?(Symbol)
|
640
|
+
RVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
641
|
+
RVM::Interpreter::SimpleVariable.new(@name, @pos)
|
642
|
+
elsif @name.is_a?(RVM::Interpreter::Constant)
|
643
|
+
RVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
644
|
+
RVM::Interpreter::SimpleVariable.new(@name.value, @pos)
|
645
|
+
else
|
646
|
+
super
|
675
647
|
end
|
676
|
-
r = env.read_var_val(n)
|
677
|
-
@type = r.data_type if r.respond_to?(:data_type)
|
678
|
-
r
|
679
648
|
end
|
680
649
|
end
|
681
650
|
|
@@ -696,6 +665,12 @@ module RVM
|
|
696
665
|
@type = :any
|
697
666
|
end
|
698
667
|
|
668
|
+
def pretty_print(q)
|
669
|
+
q.text "!!PARAMS["
|
670
|
+
q.pp @num
|
671
|
+
q.text "]"
|
672
|
+
end
|
673
|
+
|
699
674
|
# The type can only be tretrieved when the num is aconstant
|
700
675
|
# as it can be evaluated without sideffect.
|
701
676
|
def data_type
|
@@ -703,23 +678,29 @@ module RVM
|
|
703
678
|
end
|
704
679
|
|
705
680
|
# When executed the Parameter evaluates the number, of the parameter and
|
706
|
-
# then queries the
|
681
|
+
# then queries the environment to get the function parameter requested.
|
707
682
|
#
|
708
683
|
# After the first execution the parameter remembers the type of the value
|
709
684
|
# it returns.
|
710
685
|
def execute env
|
711
686
|
RVM::debug "Executing Parameter at #{@pos}..." if $DEBUG
|
712
687
|
r = env.param(@num.execute(env).to_i)
|
713
|
-
@type = r.data_type if r
|
688
|
+
@type = r.data_type if r && r.respond_to?(:data_type)
|
714
689
|
r
|
715
690
|
end
|
691
|
+
|
692
|
+
def optimize
|
693
|
+
@num = @num.optimize
|
694
|
+
super
|
695
|
+
end
|
716
696
|
end
|
717
697
|
|
718
698
|
# A sequence is a list of commands that are executed one after
|
719
699
|
# another.
|
720
700
|
# The type of the squence is equal to the last element of the sequence.
|
721
|
-
class Sequence <
|
701
|
+
class Sequence < Element
|
722
702
|
attr_accessor :pos
|
703
|
+
attr_accessor :data
|
723
704
|
|
724
705
|
# The Sequence is initialized wiht 1 to 2 parameters.
|
725
706
|
#
|
@@ -729,27 +710,73 @@ module RVM
|
|
729
710
|
# pos:: The position within the source code of the definition - for
|
730
711
|
# deugging purpose.
|
731
712
|
def initialize src=[], pos = nil
|
732
|
-
|
713
|
+
@data = src
|
733
714
|
@pos = pos
|
734
715
|
end
|
735
716
|
|
717
|
+
def pretty_print(q)
|
718
|
+
first = true
|
719
|
+
q.group 1, "{", "}" do
|
720
|
+
@data.each do |c|
|
721
|
+
if first
|
722
|
+
first = false
|
723
|
+
else
|
724
|
+
q.text ";"
|
725
|
+
end
|
726
|
+
q.breakable
|
727
|
+
q.pp c
|
728
|
+
end
|
729
|
+
q.breakable
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
736
733
|
# When exeuted a sequence starts to execute every element in it starting
|
737
734
|
# with the first element in the array.
|
738
735
|
#
|
739
736
|
# The result is the last element of the array executed.
|
740
737
|
def execute env
|
741
738
|
RVM::debug "Executing Sequence... #{inspect}" if $DEBUG
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
}
|
739
|
+
for item in @data
|
740
|
+
r = item.execute env
|
741
|
+
end
|
746
742
|
r
|
747
743
|
end
|
748
744
|
|
749
745
|
# When adding something to the Sequence a new Sequence will be created
|
750
746
|
# with the result of the joined arrays.
|
751
747
|
def + v
|
752
|
-
Sequence
|
748
|
+
v = v.data if v.is_a? RVM::Interpreter::Sequence
|
749
|
+
Sequence.new(@data + v)
|
750
|
+
end
|
751
|
+
|
752
|
+
def << v
|
753
|
+
@data << v
|
754
|
+
self
|
755
|
+
end
|
756
|
+
|
757
|
+
def unshift v
|
758
|
+
@data.unshift v
|
759
|
+
self
|
760
|
+
end
|
761
|
+
|
762
|
+
# Optimization for sequences
|
763
|
+
def optimize
|
764
|
+
|
765
|
+
if @data.size == 1
|
766
|
+
return @data.first.optimize
|
767
|
+
else
|
768
|
+
newdata = []
|
769
|
+
@data.each do |d|
|
770
|
+
d = d.optimize
|
771
|
+
if d.is_a? RVM::Interpreter::Sequence
|
772
|
+
newdata.concat(d.data)
|
773
|
+
else
|
774
|
+
newdata << d
|
775
|
+
end
|
776
|
+
end
|
777
|
+
@data = newdata
|
778
|
+
end
|
779
|
+
super
|
753
780
|
end
|
754
781
|
|
755
782
|
# The data type of the list is :any as it is unknown where the the
|
@@ -796,6 +823,11 @@ module RVM
|
|
796
823
|
@val = val
|
797
824
|
end
|
798
825
|
|
826
|
+
def pretty_print(q)
|
827
|
+
q.text "return "
|
828
|
+
q.pp @val
|
829
|
+
end
|
830
|
+
|
799
831
|
# The data type of a return statement is any, as it does not return
|
800
832
|
# anything at all, after all it jumps out of a block.
|
801
833
|
def data_type
|
@@ -807,10 +839,15 @@ module RVM
|
|
807
839
|
def execute env
|
808
840
|
raise ReturnException.new(@val.execute(env))
|
809
841
|
end
|
842
|
+
|
843
|
+
def optimize
|
844
|
+
@val = @val.optimize
|
845
|
+
super
|
846
|
+
end
|
810
847
|
end
|
811
848
|
|
812
849
|
# A function call or a function or a block. The initialization of a new
|
813
|
-
#
|
850
|
+
# environment is done by the function Class as it also sorts the arguments
|
814
851
|
# into it.
|
815
852
|
#
|
816
853
|
# Arguments are only executed when the function does return true for
|
@@ -822,12 +859,27 @@ module RVM
|
|
822
859
|
# class.
|
823
860
|
#
|
824
861
|
# Arguments is a list of the arguments to the function.
|
825
|
-
def initialize function, arguments, pos =
|
862
|
+
def initialize function, arguments, pos = [0,0,0]
|
826
863
|
super(pos)
|
827
864
|
@function = function
|
828
865
|
@arguments = arguments
|
829
866
|
end
|
830
867
|
|
868
|
+
def pretty_print(q)
|
869
|
+
first = true
|
870
|
+
q.pp @function
|
871
|
+
q.text "("
|
872
|
+
@arguments.each do |a|
|
873
|
+
if first
|
874
|
+
first = false
|
875
|
+
else
|
876
|
+
q.text ', '
|
877
|
+
end
|
878
|
+
q.pp a
|
879
|
+
end
|
880
|
+
q.text ")"
|
881
|
+
end
|
882
|
+
|
831
883
|
# The data type of the FunctionCall is the return value of the function
|
832
884
|
# that it calls.
|
833
885
|
def data_type
|
@@ -844,6 +896,12 @@ module RVM
|
|
844
896
|
end
|
845
897
|
end
|
846
898
|
|
899
|
+
def optimize
|
900
|
+
@arguments.map!{ |a| a.optimize}
|
901
|
+
@function.optimize if @function.respond_to?(:optimize)
|
902
|
+
super
|
903
|
+
end
|
904
|
+
|
847
905
|
# When executed the FunctionCall has four possible behaviours.
|
848
906
|
#
|
849
907
|
# 1) If the function is a block, so an anonymous function, the arguments
|
@@ -880,19 +938,94 @@ module RVM
|
|
880
938
|
fun.call(args, env, @pos)
|
881
939
|
# If nothing of the above the function must be a global function.
|
882
940
|
else
|
883
|
-
#
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
941
|
+
raise RuntimeError.new("Function '#{@function}' Not found!", @pos[0], @pos[1], @pos[2])
|
942
|
+
end
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
946
|
+
|
947
|
+
# A core call is a call to the direct core functions and libraries RVM proides.
|
948
|
+
# this is used to write core libraries for different languages.
|
949
|
+
class CoreCall < Element
|
950
|
+
attr_reader :arguments
|
951
|
+
attr_reader :function
|
952
|
+
# The constructor. +function+ can either be a block object or a function
|
953
|
+
# class.
|
954
|
+
#
|
955
|
+
# Arguments is a list of the arguments to the function.
|
956
|
+
def initialize function, arguments, pos = nil
|
957
|
+
super(pos)
|
958
|
+
@function = function
|
959
|
+
@arguments = arguments
|
960
|
+
end
|
961
|
+
|
962
|
+
def pretty_print(q)
|
963
|
+
binary = {:sub => '-', :add => '+', :mul => '*', :div => '/', :mod => '%', :shl => '<<', :shr => '>>',
|
964
|
+
:cmp => '<=>', :eq => '==', :gt => '>', :gte => '>=', :lt => '<', :lte => '<=',
|
965
|
+
:bitwise_and => '&', :bitwise_or => '|', :bitwise_xor => '^',
|
966
|
+
:and => '&&', :or => '||', }
|
967
|
+
if binary.keys.include?(@function)
|
968
|
+
first = true
|
969
|
+
@arguments.each do |a|
|
970
|
+
if first
|
971
|
+
first = false
|
972
|
+
else
|
973
|
+
q.text " #{binary[@function]} "
|
890
974
|
end
|
975
|
+
q.pp a
|
891
976
|
end
|
892
|
-
|
977
|
+
else
|
978
|
+
first = true
|
979
|
+
q.pp @function
|
980
|
+
q.text "!("
|
981
|
+
@arguments.each do |a|
|
982
|
+
if first
|
983
|
+
first = false
|
984
|
+
else
|
985
|
+
q.text ', '
|
986
|
+
end
|
987
|
+
q.pp a
|
988
|
+
end
|
989
|
+
q.text ")"
|
990
|
+
end
|
991
|
+
end
|
992
|
+
|
993
|
+
def optimize
|
994
|
+
@arguments.map!{ |a| a.optimize}
|
995
|
+
super
|
996
|
+
end
|
997
|
+
|
998
|
+
def data_type
|
999
|
+
RVM::Functions[@function] ? RVM::Functions[@function].data_type : :any
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
# When executed the CoreCall it will call one of the RVM's library functions
|
1003
|
+
def execute env
|
1004
|
+
RVM::debug "Executing FunctionCall..." if $DEBUG
|
1005
|
+
args = @arguments.dup
|
1006
|
+
# The function is a anonymous function
|
1007
|
+
|
1008
|
+
# Get the function from he globals
|
1009
|
+
|
1010
|
+
if not fun = RVM::Functions[@function]
|
1011
|
+
raise RuntimeError.new("Function Not found!", @pos[0], @pos[1], @pos[2])
|
1012
|
+
end
|
1013
|
+
# Test if the arguments should be executed
|
1014
|
+
if fun.execargs
|
1015
|
+
# The arges get executed
|
1016
|
+
args.map! do |arg|
|
1017
|
+
arg.execute env
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
# Call the function
|
1021
|
+
begin
|
893
1022
|
fun.call(args, env, @pos)
|
1023
|
+
rescue Exception => e
|
1024
|
+
raise e
|
1025
|
+
raise RuntimeError.new("Function failed to execute: #{e}", @pos[0], @pos[1], @pos[2])
|
894
1026
|
end
|
895
1027
|
end
|
896
1028
|
end
|
897
1029
|
end
|
898
1030
|
end
|
1031
|
+
require 'rvm/optimisation'
|