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