AmberVM 0.0.19
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 +38 -0
- data/bin/ambervm +278 -0
- data/lib/amber/acts_as_rvm_type.rb +157 -0
- data/lib/amber/classes/association.rb +36 -0
- data/lib/amber/classes/block.rb +52 -0
- data/lib/amber/classes/boolean.rb +40 -0
- data/lib/amber/classes/class.rb +50 -0
- data/lib/amber/classes/error.rb +22 -0
- data/lib/amber/classes/list.rb +96 -0
- data/lib/amber/classes/null.rb +35 -0
- data/lib/amber/classes/number.rb +95 -0
- data/lib/amber/classes/object.rb +56 -0
- data/lib/amber/classes/string.rb +79 -0
- data/lib/amber/classes.rb +113 -0
- data/lib/amber/environment.rb +251 -0
- data/lib/amber/fukubukuro/ecma_core.rb +409 -0
- data/lib/amber/fukubukuro.rb +866 -0
- data/lib/amber/functions/all.rb +3 -0
- data/lib/amber/functions/array/append.rb +50 -0
- data/lib/amber/functions/array/at.rb +50 -0
- data/lib/amber/functions/array/set_at.rb +50 -0
- data/lib/amber/functions/array.rb +30 -0
- data/lib/amber/functions/association/assoc_get.rb +55 -0
- data/lib/amber/functions/association/assoc_set.rb +56 -0
- data/lib/amber/functions/bitwise/bitwise_and.rb +41 -0
- data/lib/amber/functions/bitwise/bitwise_not.rb +41 -0
- data/lib/amber/functions/bitwise/bitwise_or.rb +41 -0
- data/lib/amber/functions/bitwise/bitwise_xor.rb +41 -0
- data/lib/amber/functions/bitwise.rb +3 -0
- data/lib/amber/functions/collection/get.rb +66 -0
- data/lib/amber/functions/collection/set.rb +67 -0
- data/lib/amber/functions/collection/size.rb +54 -0
- data/lib/amber/functions/general/cmp.rb +43 -0
- data/lib/amber/functions/general/eq.rb +45 -0
- data/lib/amber/functions/general/gt.rb +45 -0
- data/lib/amber/functions/general/gte.rb +45 -0
- data/lib/amber/functions/general/lt.rb +45 -0
- data/lib/amber/functions/general/lte.rb +45 -0
- data/lib/amber/functions/general/neq.rb +45 -0
- data/lib/amber/functions/general/type.rb +43 -0
- data/lib/amber/functions/general.rb +3 -0
- data/lib/amber/functions/io/print.rb +45 -0
- data/lib/amber/functions/io.rb +3 -0
- data/lib/amber/functions/list/align.rb +73 -0
- data/lib/amber/functions/list/join.rb +45 -0
- data/lib/amber/functions/list/map.rb +58 -0
- data/lib/amber/functions/list/split.rb +55 -0
- data/lib/amber/functions/list.rb +3 -0
- data/lib/amber/functions/logic/and.rb +55 -0
- data/lib/amber/functions/logic/not.rb +40 -0
- data/lib/amber/functions/logic/or.rb +50 -0
- data/lib/amber/functions/logic.rb +3 -0
- data/lib/amber/functions/math/abs.rb +39 -0
- data/lib/amber/functions/math/acos.rb +39 -0
- data/lib/amber/functions/math/add.rb +40 -0
- data/lib/amber/functions/math/asin.rb +39 -0
- data/lib/amber/functions/math/atan.rb +39 -0
- data/lib/amber/functions/math/ceil.rb +39 -0
- data/lib/amber/functions/math/cos.rb +39 -0
- data/lib/amber/functions/math/dec.rb +39 -0
- data/lib/amber/functions/math/div.rb +44 -0
- data/lib/amber/functions/math/exp.rb +39 -0
- data/lib/amber/functions/math/floor.rb +39 -0
- data/lib/amber/functions/math/inc.rb +39 -0
- data/lib/amber/functions/math/log.rb +39 -0
- data/lib/amber/functions/math/mod.rb +41 -0
- data/lib/amber/functions/math/mul.rb +43 -0
- data/lib/amber/functions/math/neg.rb +43 -0
- data/lib/amber/functions/math/power.rb +43 -0
- data/lib/amber/functions/math/rand.rb +36 -0
- data/lib/amber/functions/math/round.rb +39 -0
- data/lib/amber/functions/math/shl.rb +41 -0
- data/lib/amber/functions/math/shr.rb +41 -0
- data/lib/amber/functions/math/sin.rb +39 -0
- data/lib/amber/functions/math/sub.rb +43 -0
- data/lib/amber/functions/math/tan.rb +39 -0
- data/lib/amber/functions/math.rb +3 -0
- data/lib/amber/functions/objects/send.rb +22 -0
- data/lib/amber/functions/rails/print.rb +44 -0
- data/lib/amber/functions/rails.rb +3 -0
- data/lib/amber/functions/string/ansi.rb +24 -0
- data/lib/amber/functions/string/capstr.rb +23 -0
- data/lib/amber/functions/string/center.rb +25 -0
- data/lib/amber/functions/string/chr.rb +16 -0
- data/lib/amber/functions/string/ljust.rb +26 -0
- data/lib/amber/functions/string/regmatch.rb +34 -0
- data/lib/amber/functions/string/rjust.rb +26 -0
- data/lib/amber/functions/string.rb +3 -0
- data/lib/amber/functions.rb +103 -0
- data/lib/amber/interpreter.rb +1380 -0
- data/lib/amber/languages/brainfuck.rb +153 -0
- data/lib/amber/languages/ecma/compiler.rb +1661 -0
- data/lib/amber/languages/ecma/core-math.js +67 -0
- data/lib/amber/languages/ecma/core-objects.js +57 -0
- data/lib/amber/languages/ecma.rb +9 -0
- data/lib/amber/languages/ecma_fuku/compiler.rb +1622 -0
- data/lib/amber/languages/ecma_fuku/core-math.js +67 -0
- data/lib/amber/languages/ecma_fuku/core-objects.js +56 -0
- data/lib/amber/languages/ecma_fuku.rb +13 -0
- data/lib/amber/languages/math/compiler.rb +70 -0
- data/lib/amber/languages/math/tokenizer.rb +69 -0
- data/lib/amber/languages/math/tree.rb +110 -0
- data/lib/amber/languages/math.rb +26 -0
- data/lib/amber/languages.rb +99 -0
- data/lib/amber/library.rb +79 -0
- data/lib/amber/optimisation.rb +299 -0
- data/lib/amber/plugin.rb +337 -0
- data/lib/amber/rails.rb +90 -0
- data/lib/amber.rb +106 -0
- data/spec/amber/class_spec.rb +27 -0
- data/spec/amber/enviroment_spec.rb +61 -0
- data/spec/amber/function_spec.rb +25 -0
- data/spec/amber/functions/association/assoc_get_spec.rb +41 -0
- data/spec/amber/functions/association/assoc_set_spec.rb +43 -0
- data/spec/amber/functions/collection/get_spec.rb +12 -0
- data/spec/amber/functions/collection/set_spec.rb +10 -0
- data/spec/amber/functions/collection/size_spec.rb +10 -0
- data/spec/amber/functions/list/split_spec.rb +47 -0
- data/spec/amber/functions/string/ansi_spec.rb +44 -0
- data/spec/amber/functions/string/capstr_spec.rb +42 -0
- data/spec/amber/functions/string/center_spec.rb +49 -0
- data/spec/amber/functions/string/ljust_spec.rb +49 -0
- data/spec/amber/functions/string/regmatch_spec.rb +52 -0
- data/spec/amber/functions/string/rjust_spec.rb +49 -0
- data/spec/amber/interpreter/assignment_spec.rb +22 -0
- data/spec/amber/interpreter/condition_spec.rb +103 -0
- data/spec/amber/interpreter/constant_spec.rb +31 -0
- data/spec/amber/interpreter/core_call_spec.rb +72 -0
- data/spec/amber/interpreter/interpreter_spec.rb +11 -0
- data/spec/amber/interpreter/parameter_spec.rb +24 -0
- data/spec/amber/interpreter/sequence_spec.rb +47 -0
- data/spec/amber/interpreter/variable_spec.rb +24 -0
- data/spec/amber/plugin_spec.rb +10 -0
- data/spec/classes/atom/association_spec.rb +39 -0
- data/spec/classes/atom/block_spec.rb +25 -0
- data/spec/classes/atom/boolean_spec.rb +67 -0
- data/spec/classes/atom/error_spec.rb +43 -0
- data/spec/classes/atom/list_spec.rb +68 -0
- data/spec/classes/atom/number_spec.rb +132 -0
- data/spec/classes/atom/string_spec.rb +175 -0
- data/spec/languages/ecma/ecma_array_spec.rb +79 -0
- data/spec/languages/ecma/ecma_closure_spec.rb +38 -0
- data/spec/languages/ecma/ecma_literals_spec.rb +71 -0
- data/spec/languages/ecma/ecma_objects_spec.rb +165 -0
- data/spec/languages/ecma/ecma_old_spec.rb +540 -0
- data/spec/languages/ecma/ecma_spec.rb +64 -0
- data/spec/languages/ecma_fuku/ecma_array_spec.rb +61 -0
- data/spec/languages/ecma_fuku/ecma_closure_spec.rb +33 -0
- data/spec/languages/ecma_fuku/ecma_function_spec.rb +84 -0
- data/spec/languages/ecma_fuku/ecma_literals_spec.rb +55 -0
- data/spec/languages/ecma_fuku/ecma_objects_spec.rb +133 -0
- data/spec/languages/ecma_fuku/ecma_old_spec.rb +415 -0
- data/spec/languages/ecma_fuku/ecma_operator_spec.rb +33 -0
- data/spec/languages/ecma_fuku/ecma_spec.rb +52 -0
- data/spec/languages/math/compiler_spec.rb +49 -0
- data/spec/languages/math/tokenizer_spec.rb +73 -0
- data/spec/languages/math/tree_spec.rb +153 -0
- metadata +225 -0
@@ -0,0 +1,1380 @@
|
|
1
|
+
require 'amber/functions'
|
2
|
+
require 'amber/classes'
|
3
|
+
require 'amber/environment'
|
4
|
+
require 'set'
|
5
|
+
module AmberVM
|
6
|
+
# This Module hold all the VM classes that are used to execute and evaluate
|
7
|
+
# code for the VM, so it is quite important to read and understand if you
|
8
|
+
# feel like making a own compiler.
|
9
|
+
#
|
10
|
+
# For someone not interested in going as deep into building a own compiler
|
11
|
+
# it still is a good start for understanding how rVM works, yet it is not
|
12
|
+
# mandatory to read.
|
13
|
+
#
|
14
|
+
# The basic idea of this objects is that the compiler translates the source
|
15
|
+
# code into a tree of AmberVM::Interpreter objects to build the behavior that is
|
16
|
+
# expected. Once that is done the code can be executed by calling #execute
|
17
|
+
# for the root of the object tree.
|
18
|
+
#
|
19
|
+
module Interpreter
|
20
|
+
|
21
|
+
# This is a helper fuctio to quickly generate a empty enviorment.
|
22
|
+
# A hash can be passed to store variables.
|
23
|
+
#
|
24
|
+
# :locals can be a hash holding local variables
|
25
|
+
# :functions can be a hash to hold functions defined in the the scope
|
26
|
+
def Interpreter.env aenv = {}
|
27
|
+
AmberVM::debug "Interpreter.env" if $DEBUG
|
28
|
+
Environment.new aenv
|
29
|
+
end
|
30
|
+
|
31
|
+
# This is a helper function that generates a constat of a simple constant,
|
32
|
+
# for example if you've a lot of string constants it is way quickly to call
|
33
|
+
# this then writing it out the whole time.
|
34
|
+
def Interpreter.const type, value, pos = nil
|
35
|
+
AmberVM::Interpreter::Constant.new(AmberVM::Classes[type].new(value), pos)
|
36
|
+
end
|
37
|
+
|
38
|
+
class RuntimeError < Exception
|
39
|
+
attr_accessor :total, :line, :char, :error_message
|
40
|
+
def initialize message, total = nil, line = nil, char = nil
|
41
|
+
super()
|
42
|
+
@line = line
|
43
|
+
@char = char
|
44
|
+
@total = total
|
45
|
+
@error_message = message
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
res = "Runtime error"
|
50
|
+
if @total or @line
|
51
|
+
res << " at"
|
52
|
+
end
|
53
|
+
if @total
|
54
|
+
res << " character #{@total}"
|
55
|
+
end
|
56
|
+
if @line
|
57
|
+
res << " line #{@line}"
|
58
|
+
end
|
59
|
+
if @char
|
60
|
+
res << ":#{@char}"
|
61
|
+
end
|
62
|
+
res << ": #{@error_message}"
|
63
|
+
res
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Helper class to be parented to other Interpreter calsses for checks and
|
68
|
+
# including general behavior.
|
69
|
+
class Element
|
70
|
+
# The position in the soruce code, added to help debugging scripts with
|
71
|
+
# problems.
|
72
|
+
attr_accessor :pos
|
73
|
+
|
74
|
+
# Initializes the interpreter element with a position.
|
75
|
+
def initialize pos
|
76
|
+
@pos = pos
|
77
|
+
end
|
78
|
+
|
79
|
+
#Placeholder for execute
|
80
|
+
def execute env
|
81
|
+
raise "Execute not yet implemented for #{self.class}."
|
82
|
+
end
|
83
|
+
|
84
|
+
#Placeholder for optimization
|
85
|
+
def optimize variables = {}
|
86
|
+
AmberVM::debug "Optimizung #{self}" if $DEBUG
|
87
|
+
slef.class.new(@pos)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
class Program < Element
|
93
|
+
def initialize code
|
94
|
+
@code = code
|
95
|
+
@variables = {}
|
96
|
+
end
|
97
|
+
|
98
|
+
def optimize variables = {}
|
99
|
+
@code = @code.optimize(variables)
|
100
|
+
@variables = variables
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
def execute env
|
105
|
+
@variables.each do |name, var|
|
106
|
+
var.val = env[name].val if env[name]
|
107
|
+
end
|
108
|
+
res = @code.execute(env)
|
109
|
+
@variables.each do |name, var|
|
110
|
+
env[name] = var.val
|
111
|
+
end
|
112
|
+
res
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Version of the AmberVM::Interpreter::Variable that is made to use with objects
|
117
|
+
# Jumps into object context (anther way beside using send).
|
118
|
+
#
|
119
|
+
# The special variable :self (attention, not to be mixed wiht 'self')
|
120
|
+
# is set to the object in which context we are!
|
121
|
+
#
|
122
|
+
# This is one of the few AmberVM::Interpreter calsses that is likely to be used
|
123
|
+
# outside a compiler as it will allow to execute a set of code within the
|
124
|
+
# scope of a specific object:
|
125
|
+
#
|
126
|
+
# =Example
|
127
|
+
# AmberVM::Interpreter::ObjectContext.new(my_object, my_code).execute(env)
|
128
|
+
#
|
129
|
+
# This will execute my_code in a way pretty much equal to instance_eval in
|
130
|
+
# ruby would do.
|
131
|
+
class ObjectContext < Element
|
132
|
+
# The constructor takes 3 arguments, the first beeing the object of which
|
133
|
+
# the scope is taken, the second is the code to be executed in the objects
|
134
|
+
# scope and the third is the position, to be set by the compiler.
|
135
|
+
#
|
136
|
+
# The passed object is executed once when the code is ran so it can be
|
137
|
+
# passed something that evaluates like a variable, or constant.
|
138
|
+
def initialize object, code, pos = nil
|
139
|
+
super(pos)
|
140
|
+
@object = object
|
141
|
+
@code = code
|
142
|
+
end
|
143
|
+
|
144
|
+
def pretty_print(q)
|
145
|
+
q.group 1, "#{@object}.{", "}" do
|
146
|
+
q.pp @code
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Execute for this Interpreter element by creating a new environment ,
|
151
|
+
# setting it's variables and functions to those definded by the object's
|
152
|
+
# +functions+ and +variables+ methods. It also sets the :self variable in
|
153
|
+
# the new environment to the object.
|
154
|
+
def execute env
|
155
|
+
#The new
|
156
|
+
obj = @object.execute(env)
|
157
|
+
o bj.env.prev = env if obj.env.prev == {}
|
158
|
+
|
159
|
+
@code.execute(obj.env)
|
160
|
+
end
|
161
|
+
|
162
|
+
def optimize variables = {}
|
163
|
+
object = @object.optimize variables
|
164
|
+
code = @code.optimize variables
|
165
|
+
ObjectContext.new(object, code)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class NewClassInstance < Element
|
170
|
+
def initialize object_class, params
|
171
|
+
@class = object_class
|
172
|
+
@params = params
|
173
|
+
end
|
174
|
+
|
175
|
+
def execute env
|
176
|
+
AmberVM::debug "Creating class instance..." if $DEBUG
|
177
|
+
params = @params.map{|p| p.execute(env)}
|
178
|
+
@class.instance(@params, env)
|
179
|
+
end
|
180
|
+
|
181
|
+
def optimize variables = {}
|
182
|
+
p = params.map{|p| p.optimize variables}
|
183
|
+
NewClassInstance.new(@class, p)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
# This sets a funciton on a Class (to be included in its objects)
|
187
|
+
#
|
188
|
+
# To define class functions use ObjectContext and define the function
|
189
|
+
# normaly, nifty isn't it?
|
190
|
+
#
|
191
|
+
# This object may be subject to removal or change, don't use it yet.
|
192
|
+
#---
|
193
|
+
# TODO: Work this over.
|
194
|
+
class SetClassFunction < Element
|
195
|
+
def initialize obj, name, function, pos = nil
|
196
|
+
super(pos)
|
197
|
+
@object = obj
|
198
|
+
@name = name
|
199
|
+
@function = function
|
200
|
+
end
|
201
|
+
|
202
|
+
def execute env
|
203
|
+
@object.object_functions[@name.execute(env)] = @function
|
204
|
+
@function
|
205
|
+
end
|
206
|
+
|
207
|
+
def optimize variables = {}
|
208
|
+
object = @object.optimize variables
|
209
|
+
function = @function.optimize variables
|
210
|
+
SetClassFunction.new(object, function)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# A block localizes variables, do not mix this up with the
|
215
|
+
# +AmberVM::Classes::Block+ class!
|
216
|
+
#
|
217
|
+
# Blocks are mostly used to handle tasks as not interfeeing
|
218
|
+
# With outer code.
|
219
|
+
class Block < Element
|
220
|
+
attr_reader :content
|
221
|
+
def initialize content, pos = nil
|
222
|
+
super(pos)
|
223
|
+
@content = content
|
224
|
+
end
|
225
|
+
|
226
|
+
def pretty_print(q)
|
227
|
+
first = true
|
228
|
+
q.group 1, "{", "}" do
|
229
|
+
q.pp @content
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# When executed a temporary environment is created with the passed
|
234
|
+
# Enviroement as a parent to it.
|
235
|
+
# This new environment is discaded after the execution.
|
236
|
+
def execute env
|
237
|
+
tenv = Environment.new({}, env)
|
238
|
+
@content.execute tenv
|
239
|
+
end
|
240
|
+
|
241
|
+
def optimize variables = {}
|
242
|
+
content = @content.optimize variables
|
243
|
+
Block.new(content, @pos)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# The FunctionDefinition can be used to define functions in the current
|
248
|
+
# scope, it can either be methods belonging to a object when called within
|
249
|
+
# object scope or good old functions.
|
250
|
+
class FunctionDefinition < Element
|
251
|
+
|
252
|
+
# Initializes a new function definition
|
253
|
+
#
|
254
|
+
# name:: is the name of the function to define, if it is not
|
255
|
+
# unique it will overwrite earlyer definitions
|
256
|
+
# (unless override is false)
|
257
|
+
#
|
258
|
+
# body:: is the body of the cuntion. this will be executed
|
259
|
+
# when the defined function is called.
|
260
|
+
#
|
261
|
+
# override:: when true (default) earlyer definitions are
|
262
|
+
# replaced when it is called a second time.
|
263
|
+
# When false a exception is thrown
|
264
|
+
#
|
265
|
+
# pos:: The position within the source code of the definition - for
|
266
|
+
# deugging purpose.
|
267
|
+
def initialize name, body, override = true, pos = nil
|
268
|
+
super(pos)
|
269
|
+
@name = name
|
270
|
+
@body = body
|
271
|
+
@override = override
|
272
|
+
end
|
273
|
+
|
274
|
+
def pretty_print(q)
|
275
|
+
first = true
|
276
|
+
q.text "function "
|
277
|
+
if @name.is_a? Constant
|
278
|
+
q.text @name.value
|
279
|
+
else
|
280
|
+
q.pp @name
|
281
|
+
end
|
282
|
+
q.text "()"
|
283
|
+
q.pp @body
|
284
|
+
end
|
285
|
+
|
286
|
+
# When executed the FunctionDefinition first checks if a function with the
|
287
|
+
# same name is already defined. If it is and +override+ wasn't set to ture
|
288
|
+
# it trows a Exception. Otherwise it defines the function, deleting the
|
289
|
+
# old definition when still repsent.
|
290
|
+
#
|
291
|
+
# It returns the body of the newly defined function.
|
292
|
+
def execute env
|
293
|
+
if (not @override) and env.function(@name)
|
294
|
+
raise RuntimeError.new("Function #{@name} already defined", @pos[0], @pos[1], @pos[2])
|
295
|
+
end
|
296
|
+
env.def_function(@name.execute(env),@body)
|
297
|
+
@body
|
298
|
+
end
|
299
|
+
|
300
|
+
def optimize variables = {}
|
301
|
+
local_variables = {}
|
302
|
+
body = @body.optimize local_variables
|
303
|
+
name = @name.optimize variables
|
304
|
+
FunctionDefinition.new(body, name, @pos)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
class Closures < Element
|
309
|
+
|
310
|
+
class Binding
|
311
|
+
|
312
|
+
def initialize content, vars
|
313
|
+
@content = content
|
314
|
+
@vars = vars
|
315
|
+
end
|
316
|
+
|
317
|
+
def call params, env, pos = nil
|
318
|
+
env = AmberVM::Interpreter::Environment.new({:locals => @vars}, env)
|
319
|
+
@content.call(params, env, pos)
|
320
|
+
end
|
321
|
+
|
322
|
+
def inspect
|
323
|
+
"<#{self.class}:#{self.object_id}>"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
attr_accessor :content
|
328
|
+
def initialize content
|
329
|
+
@content = content
|
330
|
+
end
|
331
|
+
|
332
|
+
def optimize variables = {}
|
333
|
+
OptimizedClosures.new(@content, variables)
|
334
|
+
end
|
335
|
+
|
336
|
+
def execute env
|
337
|
+
Binding.new(@content, env.data[:locals].dup)
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
# This is a loop. It is executed over and over again as long as
|
343
|
+
# the passed condition evaluates to a value that matches is_true?.
|
344
|
+
#
|
345
|
+
#---
|
346
|
+
#TODO: Add BreakException to stop execution of loops.
|
347
|
+
#TODO: Add NextExceeption to skip rest of a evaluation on loop code.
|
348
|
+
class Loop < Element
|
349
|
+
# Initializes a new loop.
|
350
|
+
#
|
351
|
+
# condition:: is executed before each run of the loop. If it evaluates
|
352
|
+
# to true the loop is executed another time otherwise the
|
353
|
+
# exection ends.
|
354
|
+
#
|
355
|
+
# body:: For each itteration of the loop this is executed once.
|
356
|
+
#
|
357
|
+
# pos:: The position within the source code of the definition - for
|
358
|
+
# deugging purpose.
|
359
|
+
def initialize(condition, body, pos = nil)
|
360
|
+
super(pos)
|
361
|
+
@condition = condition
|
362
|
+
@body = body
|
363
|
+
end
|
364
|
+
|
365
|
+
def pretty_print(q)
|
366
|
+
first = true
|
367
|
+
q.text "while ("
|
368
|
+
q.pp @condition
|
369
|
+
q.text ")"
|
370
|
+
q.pp @body
|
371
|
+
end
|
372
|
+
|
373
|
+
# The loop will execute as long as the code passed as condition evaluates
|
374
|
+
# to is_true?.
|
375
|
+
# Once the loop stops executing the return value is the result of the last
|
376
|
+
# body execution.
|
377
|
+
def execute env
|
378
|
+
r = nil
|
379
|
+
while @condition.execute(env).is_true?
|
380
|
+
r = @body.execute(env)
|
381
|
+
end
|
382
|
+
r
|
383
|
+
end
|
384
|
+
|
385
|
+
# Optimization of the loop
|
386
|
+
def optimize variables = {}
|
387
|
+
condition = @condition.optimize variables
|
388
|
+
body = @body.optimize variables
|
389
|
+
Loop.new(condition, body, @pos)
|
390
|
+
end
|
391
|
+
|
392
|
+
end
|
393
|
+
|
394
|
+
# A constant, it evaluates to thevalue given and end the evaluation.
|
395
|
+
# Meaning that no further execution is done in this tree branch, so the
|
396
|
+
# value isn't evaluated.
|
397
|
+
class Constant < Element
|
398
|
+
attr_reader :value
|
399
|
+
def initialize value, pos = nil
|
400
|
+
super(pos)
|
401
|
+
@value = value
|
402
|
+
end
|
403
|
+
|
404
|
+
# A constat returns the data type of the value that is stored in it.
|
405
|
+
def data_type
|
406
|
+
@value.data_type
|
407
|
+
end
|
408
|
+
|
409
|
+
def pretty_print(q)
|
410
|
+
q.pp @value
|
411
|
+
end
|
412
|
+
|
413
|
+
# Comparing a constant with something has two ways to go, if the object
|
414
|
+
# it is compared to is a Constant the two values are compared. If not the
|
415
|
+
# Constant compares the value with the passed object.
|
416
|
+
def == v
|
417
|
+
if v.is_a? Constant
|
418
|
+
@value == v.value
|
419
|
+
else
|
420
|
+
@value == v
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def optimize variables={}
|
425
|
+
value = @value
|
426
|
+
if @value.respond_to?(:optimize)
|
427
|
+
value = @value.optimize(variables)
|
428
|
+
end
|
429
|
+
Constant.new(value)
|
430
|
+
end
|
431
|
+
|
432
|
+
# When executed the constant returns the value stored in it, without
|
433
|
+
# evaluating it.
|
434
|
+
def execute env
|
435
|
+
AmberVM::debug "Executing Constant at #{@pos}: #{@value}" if $DEBUG
|
436
|
+
@value
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
# The condition is most widely known as 'if' statement, also the tertier
|
441
|
+
# opperator is a condition.
|
442
|
+
#
|
443
|
+
# Conditions are quite important, elseif can be implemented by chaining
|
444
|
+
# if statements.
|
445
|
+
#
|
446
|
+
# == Example
|
447
|
+
#
|
448
|
+
# === TRANSLATING IF
|
449
|
+
#
|
450
|
+
# cond = Interpreter::Condition.new(
|
451
|
+
# <... thing for the condition ...>,
|
452
|
+
# <... what to do if condition is true ...>
|
453
|
+
# [<... what to do if condition is false ...>])
|
454
|
+
#
|
455
|
+
# if now cond.execute is called the following happens:
|
456
|
+
# * execute is called for the condition
|
457
|
+
# * if the result of it true and is_true? is also true:
|
458
|
+
# * execute for the true case is called and it's result returned
|
459
|
+
# * if the result is false or is_true? is false:
|
460
|
+
# * execute is called for the false case and it's result is returned
|
461
|
+
class Condition < Element
|
462
|
+
# Creates a new condition with the given parameters. The false_case can
|
463
|
+
# be ommitted.
|
464
|
+
#
|
465
|
+
# value:: is executed, and depandant of the result either true_case or
|
466
|
+
# false casae s executed.
|
467
|
+
#
|
468
|
+
# true_case:: is only executed if value evalutes to true (is_true?)
|
469
|
+
#
|
470
|
+
# false_case:: is only executed if value evalutes to false (!is_true?)
|
471
|
+
#
|
472
|
+
# pos:: The position within the source code of the definition - for
|
473
|
+
# deugging purpose.
|
474
|
+
def initialize value, true_case, false_case = nil, pos = nil
|
475
|
+
super(pos)
|
476
|
+
@value = value
|
477
|
+
@true_case = true_case
|
478
|
+
@false_case = false_case
|
479
|
+
end
|
480
|
+
|
481
|
+
def pretty_print(q)
|
482
|
+
first = true
|
483
|
+
q.text "if ("
|
484
|
+
q.pp @value
|
485
|
+
q.text ")"
|
486
|
+
q.pp @true_case
|
487
|
+
if (@false_case)
|
488
|
+
q.text "else"
|
489
|
+
q.pp @false_case
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
# The data type of a condition is tried to evaluate by checking if the
|
494
|
+
# type is the same for both conditions, if so the common type is returned,
|
495
|
+
# if not :any is returend as it can not be determined what type the
|
496
|
+
# Condition will have.
|
497
|
+
def data_type
|
498
|
+
if @true_case.data_type == @false_case.data_type
|
499
|
+
@false_case.data_type
|
500
|
+
else
|
501
|
+
:any
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
# When executed the condition first executes the condition, if it
|
506
|
+
# evaluates to a value that .is_true? the true case is executed if not,
|
507
|
+
# and a false case is given it will be executed.
|
508
|
+
#
|
509
|
+
# The return value is the value of the executed condition, so either the
|
510
|
+
# ture_case or the false_case.
|
511
|
+
def execute env
|
512
|
+
v = @value.execute(env)
|
513
|
+
if v and v.is_true?
|
514
|
+
AmberVM::debug "Executing Condition... (true)" if $DEBUG
|
515
|
+
@true_case.execute env
|
516
|
+
elsif @false_case
|
517
|
+
AmberVM::debug "Executing Condition... (false)" if $DEBUG
|
518
|
+
@false_case.execute env
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
#Optimizes the condition (checks for trivial cases)
|
523
|
+
def optimize variables = {}
|
524
|
+
value = @value.optimize variables
|
525
|
+
true_case = @true_case.optimize variables
|
526
|
+
false_case = @false_case.optimize variables if @false_case
|
527
|
+
if value.is_a? AmberVM::Interpreter::Constant
|
528
|
+
AmberVM::debug "Optimizing #{self}, with shortcuting a condition." if $DEBUG
|
529
|
+
if value.value.is_true?
|
530
|
+
true_case
|
531
|
+
elsif false_case
|
532
|
+
false_case
|
533
|
+
else
|
534
|
+
AmberVM::Interpreter::Sequence.new
|
535
|
+
end
|
536
|
+
else
|
537
|
+
Condition.new(value, true_case, false_case, @pos)
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
|
543
|
+
# A variable assignment that sets a local variable. A declaration
|
544
|
+
# is not required before the assignment can be done, yet it can be used to
|
545
|
+
# force a laready declaed variale into the local scope.
|
546
|
+
#
|
547
|
+
# Both the +name+ and the +value+ are evaluated before the assignment
|
548
|
+
# is done.
|
549
|
+
class Assignment < Element
|
550
|
+
|
551
|
+
# A Assignment is initialized wiht 2 to 3 parameters.
|
552
|
+
#
|
553
|
+
# name:: The name of the variable to store, it will be executed, usually
|
554
|
+
# this will be a Constant, unless dynamic naming is needed by the
|
555
|
+
# implemented language.
|
556
|
+
#
|
557
|
+
# value:: The value that will be assigned to the variable, for a = 1 + 1
|
558
|
+
# '1+1' would be the value to assign, so as this already suggests
|
559
|
+
# the value will be executed.
|
560
|
+
#
|
561
|
+
# pos:: The position within the source code of the definition - for
|
562
|
+
# deugging purpose.
|
563
|
+
def initialize variable, value, pos = nil
|
564
|
+
super(pos)
|
565
|
+
@variable = variable
|
566
|
+
@value = value
|
567
|
+
end
|
568
|
+
|
569
|
+
def pretty_print(q)
|
570
|
+
q.pp @variable
|
571
|
+
q.text " = "
|
572
|
+
q.pp @value
|
573
|
+
end
|
574
|
+
|
575
|
+
# The data type of a Assignment is the data type of it's value.
|
576
|
+
def data_type
|
577
|
+
@value.data_type
|
578
|
+
end
|
579
|
+
|
580
|
+
# When executed the assignment first evaluates the name of the assignment
|
581
|
+
# then the value and stores the result of the executed value in the
|
582
|
+
# environment under the name of the executed name.
|
583
|
+
#
|
584
|
+
# The return value of the execution is the value that is assigned to the
|
585
|
+
# variable.
|
586
|
+
def execute env
|
587
|
+
new_val = @value.execute env
|
588
|
+
var = @variable.execute env
|
589
|
+
AmberVM::debug "Executing Assignment at #{@pos}... #{var} = #{new_val}" if $DEBUG
|
590
|
+
var.val = new_val
|
591
|
+
end
|
592
|
+
|
593
|
+
def optimize variables = {}
|
594
|
+
variable = @variable.optimize(variables)
|
595
|
+
value = @value.optimize variables
|
596
|
+
Assignment.new(variable, value, @pos);
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
# # A variable assignment that sets a local variable. A declaration
|
601
|
+
# # is not required before the assignment can be done, yet it can be used to
|
602
|
+
# # force a laready declaed variale into the local scope.
|
603
|
+
# #
|
604
|
+
# # Both the +name+ and the +value+ are evaluated before the assignment
|
605
|
+
# # is done.
|
606
|
+
# class ObjectAssignment < Element
|
607
|
+
#
|
608
|
+
# # A Assignment is initialized wiht 2 to 3 parameters.
|
609
|
+
# #
|
610
|
+
# # name:: The name of the variable to store, it will be executed, usually
|
611
|
+
# # this will be a Constant, unless dynamic naming is needed by the
|
612
|
+
# # implemented language.
|
613
|
+
# #
|
614
|
+
# # value:: The value that will be assigned to the variable, for a = 1 + 1
|
615
|
+
# # '1+1' would be the value to assign, so as this already suggests
|
616
|
+
# # the value will be executed.
|
617
|
+
# #
|
618
|
+
# # pos:: The position within the source code of the definition - for
|
619
|
+
# # deugging purpose.
|
620
|
+
# def initialize object, name, value, pos = nil
|
621
|
+
# super(pos)
|
622
|
+
# @object = object
|
623
|
+
# @name = name
|
624
|
+
# @value = value
|
625
|
+
# end
|
626
|
+
#
|
627
|
+
# def pretty_print(q)
|
628
|
+
# if @object.is_a? Constant
|
629
|
+
# q.text @object.value
|
630
|
+
# else
|
631
|
+
# q.pp @object
|
632
|
+
# end
|
633
|
+
# q.text '.'
|
634
|
+
# if @name.is_a? Constant
|
635
|
+
# q.text @name.value
|
636
|
+
# else
|
637
|
+
# q.pp @name
|
638
|
+
# end
|
639
|
+
# q.text " = "
|
640
|
+
# q.pp @value
|
641
|
+
# end
|
642
|
+
#
|
643
|
+
# # The data type of a Assignment is the data type of it's value.
|
644
|
+
# def data_type
|
645
|
+
# @value.data_type
|
646
|
+
# end
|
647
|
+
#
|
648
|
+
# # When executed the assignment first evaluates the name of the assignment
|
649
|
+
# # then the value and stores the result of the executed value in the
|
650
|
+
# # environment under the name of the executed name.
|
651
|
+
# #
|
652
|
+
# # The return value of the execution is the value that is assigned to the
|
653
|
+
# # variable.
|
654
|
+
# def execute env
|
655
|
+
# AmberVM::debug "Executing Assignment at #{@pos}..." if $DEBUG
|
656
|
+
# @object.execute(env).variables[@name.execute(env).to_s] = @value.execute env
|
657
|
+
# end
|
658
|
+
#
|
659
|
+
# def optimize variables = {}
|
660
|
+
# @name = @name.optimize variables
|
661
|
+
# @value = @value.optimize variables
|
662
|
+
# if @name.is_a? AmberVM::Interpreter::Constant
|
663
|
+
# AmberVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
664
|
+
# AmberVM::Interpreter::SimpleAssignment.new(@name.value, @value, @pos)
|
665
|
+
# else
|
666
|
+
# super
|
667
|
+
# end
|
668
|
+
# end
|
669
|
+
# end
|
670
|
+
|
671
|
+
# A variable declarion that sets a local variable, it will redelcare
|
672
|
+
# the variable if declared in a privouse scope.
|
673
|
+
#
|
674
|
+
# It is very closely related to the Assignment as it acts exactly alike if
|
675
|
+
# the variable is not yet existing in the Environment .
|
676
|
+
#
|
677
|
+
# Both the +name+ and the #value# are evaluated before the assignment
|
678
|
+
# is done.
|
679
|
+
class Declaration < Element
|
680
|
+
# A Declaration is initialized wiht 2 to 3 parameters.
|
681
|
+
#
|
682
|
+
# name:: The name of the variable to store, it will be executed, usually
|
683
|
+
# this will be a Constant, unless dynamic naming is needed by the
|
684
|
+
# implemented language.
|
685
|
+
#
|
686
|
+
# value:: The value that will be assigned to the variable, for a = 1 + 1
|
687
|
+
# '1+1' would be the value to assign, so as this already suggests
|
688
|
+
# the value will be executed.
|
689
|
+
#
|
690
|
+
# pos:: The position within the source code of the definition - for
|
691
|
+
# deugging purpose.
|
692
|
+
def initialize name, value, pos = nil
|
693
|
+
super(pos)
|
694
|
+
@name = name
|
695
|
+
@value = value
|
696
|
+
end
|
697
|
+
|
698
|
+
def pretty_print(q)
|
699
|
+
if @name.is_a? Constant
|
700
|
+
q.text @name.value
|
701
|
+
else
|
702
|
+
q.pp @name
|
703
|
+
end
|
704
|
+
q.text " !=! "
|
705
|
+
q.pp @value
|
706
|
+
end
|
707
|
+
|
708
|
+
# The data type of a Assignment is the data type of it's value.
|
709
|
+
def data_type
|
710
|
+
@value.data_type
|
711
|
+
end
|
712
|
+
|
713
|
+
# When executed the assignment first evaluates the name of the assignment
|
714
|
+
# then the value and stores the result of the executed value in the
|
715
|
+
# environment under the name of the executed name.
|
716
|
+
#
|
717
|
+
# If the variable was priviosely in a environment that lays above the
|
718
|
+
# current one in the hearachy the old value will not be altered in any
|
719
|
+
# way but a new variable declared.
|
720
|
+
#
|
721
|
+
# The return value of the execution is the value that is assigned to the
|
722
|
+
# variable.
|
723
|
+
def execute env
|
724
|
+
AmberVM::debug "Executing Assignment at #{@pos}..." if $DEBUG
|
725
|
+
env.declare(@name.execute(env).to_s,@value.execute(env)).val
|
726
|
+
end
|
727
|
+
|
728
|
+
def optimize variables = {}
|
729
|
+
name = @name.optimize variables
|
730
|
+
value = @value.optimize variables
|
731
|
+
if name.is_a? AmberVM::Interpreter::Constant
|
732
|
+
AmberVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
733
|
+
AmberVM::Interpreter::SimpleDeclaration.new(name.value, value, @pos)
|
734
|
+
else
|
735
|
+
Declaration.new(name, value, @pos)
|
736
|
+
super
|
737
|
+
end
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
# Reads the value of a variable in the environment .
|
742
|
+
#
|
743
|
+
# The +name+ is evaluated before the variable is retrieved.
|
744
|
+
class Variable < Element
|
745
|
+
# A Variable is initialized wiht 1 to 2 parameters.
|
746
|
+
#
|
747
|
+
# name:: The name of the variable to get, it will be executed as long as
|
748
|
+
# it is no Sybol in which case it is treated as a special variable.
|
749
|
+
#
|
750
|
+
# pos:: The position within the source code of the definition - for
|
751
|
+
# deugging purpose.
|
752
|
+
def initialize name, pos = ['-','-','-'], require_declaration = false
|
753
|
+
super(pos)
|
754
|
+
@name = name
|
755
|
+
@type = :any
|
756
|
+
@require_declaration = require_declaration
|
757
|
+
end
|
758
|
+
|
759
|
+
def pretty_print(q)
|
760
|
+
if @name.is_a? Constant
|
761
|
+
q.text @name.value
|
762
|
+
else
|
763
|
+
q.pp @name
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
# The type can only be tretrieved when the name is aconstant
|
768
|
+
# as it can be evaluated without sideffect.
|
769
|
+
def data_type
|
770
|
+
@type
|
771
|
+
end
|
772
|
+
|
773
|
+
# When the name is a symbol, the name isn't executed and treated as a
|
774
|
+
# special variable.
|
775
|
+
# Otherwise the name is executed and converted into a string to be passed
|
776
|
+
# to the environment so it can go and collect the value.
|
777
|
+
def execute env
|
778
|
+
AmberVM::debug "Executing Variable at #{@pos}..." if $DEBUG
|
779
|
+
begin
|
780
|
+
n = @name
|
781
|
+
if not @name.is_a?(Symbol)
|
782
|
+
n = n.execute(env).to_s
|
783
|
+
end
|
784
|
+
r = env[n]
|
785
|
+
if not r
|
786
|
+
if @require_declaration
|
787
|
+
raise RuntimeError.new("Variable #{n} was not declared.")
|
788
|
+
else
|
789
|
+
r = env.declare(n, AmberVM::Classes::Null.new(nil))
|
790
|
+
end
|
791
|
+
end
|
792
|
+
AmberVM::debug "Gor variable: #{r}" if $DEBUG
|
793
|
+
@type = r.val.data_type if r.val.respond_to?(:data_type)
|
794
|
+
r
|
795
|
+
rescue Exception => e
|
796
|
+
raise RuntimeError.new("Failed to get Variable #{e}", @pos[0], @pos[1], @pos[2])
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
def optimize variables = {}
|
801
|
+
name = @name
|
802
|
+
name = name.optimize variables if not name.is_a?(Symbol)
|
803
|
+
if name.is_a?(Symbol)
|
804
|
+
AmberVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
805
|
+
AmberVM::Interpreter::SimpleVariable.new(name, @pos)
|
806
|
+
elsif name.is_a?(AmberVM::Interpreter::Constant)
|
807
|
+
AmberVM::debug "Optimizing #{self}, by making it simple" if $DEBUG
|
808
|
+
AmberVM::Interpreter::SimpleVariable.new(name.value, @pos)
|
809
|
+
else
|
810
|
+
Variable.new(name, @pos, @require_declaration)
|
811
|
+
end
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
class VariableValue < Element
|
816
|
+
attr_accessor :variable
|
817
|
+
def initialize variable, pos = ['-', '-', '-']
|
818
|
+
super(pos)
|
819
|
+
@variable = variable
|
820
|
+
end
|
821
|
+
|
822
|
+
def execute env
|
823
|
+
@variable.execute(env).val
|
824
|
+
end
|
825
|
+
|
826
|
+
def pretty_print(q)
|
827
|
+
q.pp @variable
|
828
|
+
q.text ".value"
|
829
|
+
end
|
830
|
+
|
831
|
+
def optimize variables = {}
|
832
|
+
variable = @variable.optimize variables
|
833
|
+
VariableValue.new(variable)
|
834
|
+
end
|
835
|
+
end
|
836
|
+
|
837
|
+
class ObjectVariable < AmberVM::Interpreter::Variable
|
838
|
+
# A SimpleVariable is initialized wiht 1 to 2 parameters.
|
839
|
+
#
|
840
|
+
# name:: The name of the variable to get, it will be executed as long as
|
841
|
+
# it is no Sybol in which case it is treated as a special variable.
|
842
|
+
#
|
843
|
+
# pos:: The position within the source code of the definition - for
|
844
|
+
# deugging purpose.
|
845
|
+
|
846
|
+
attr_reader :object, :name
|
847
|
+
def initialize object, name, pos = ['-', '-', '-'], require_declaration = false
|
848
|
+
super(name.to_s, pos, require_declaration)
|
849
|
+
@object = object
|
850
|
+
end
|
851
|
+
|
852
|
+
def pretty_print q
|
853
|
+
q.pp @object
|
854
|
+
q.text '.'
|
855
|
+
q.pp @name
|
856
|
+
end
|
857
|
+
|
858
|
+
# The name is a sting and we simple get what is written in the env
|
859
|
+
def execute env
|
860
|
+
AmberVM::debug "Executing SimpleVariable at #{@pos}..." if $DEBUG
|
861
|
+
begin
|
862
|
+
obj = @object.execute(env) #.val
|
863
|
+
r = obj.variables[@name]
|
864
|
+
if not r
|
865
|
+
if @require_declaration
|
866
|
+
raise RuntimeError.new("Variable #{@name} was not declared for thie Object.")
|
867
|
+
else
|
868
|
+
r = obj.variables[@name] = AmberVM::Interpreter::VariableStorage.new(AmberVM::Classes::Null.new(nil))
|
869
|
+
end
|
870
|
+
end
|
871
|
+
@type = r.val.data_type if r.val.respond_to?(:data_type)
|
872
|
+
r
|
873
|
+
rescue Exception => e
|
874
|
+
raise RuntimeError.new("Failed to get object variable Variable #{$!}", @pos[0], @pos[1], @pos[2])
|
875
|
+
end
|
876
|
+
end
|
877
|
+
|
878
|
+
def optimize variables = {}
|
879
|
+
object = @object.optimize(variables)
|
880
|
+
ObjectVariable.new(object, @name, @pos)
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
884
|
+
# This evauates to the parameter passed to the function call.
|
885
|
+
# The number of it is evaluated and typecasted to an interger.
|
886
|
+
class Parameter < Element
|
887
|
+
|
888
|
+
# A Parameter is initialized wiht 1 to 2 parameters.
|
889
|
+
#
|
890
|
+
# num:: The number if the parameter to get, 0 is the first parameter
|
891
|
+
# passed, 1 the second and so on.
|
892
|
+
#
|
893
|
+
# pos:: The position within the source code of the definition - for
|
894
|
+
# deugging purpose.
|
895
|
+
def initialize num, pos = nil
|
896
|
+
super(pos)
|
897
|
+
@num = num
|
898
|
+
@type = :any
|
899
|
+
end
|
900
|
+
|
901
|
+
def pretty_print(q)
|
902
|
+
q.text "!!PARAMS["
|
903
|
+
q.pp @num
|
904
|
+
q.text "]"
|
905
|
+
end
|
906
|
+
|
907
|
+
# The type can only be tretrieved when the num is aconstant
|
908
|
+
# as it can be evaluated without sideffect.
|
909
|
+
def data_type
|
910
|
+
@type
|
911
|
+
end
|
912
|
+
|
913
|
+
# When executed the Parameter evaluates the number, of the parameter and
|
914
|
+
# then queries the environment to get the function parameter requested.
|
915
|
+
#
|
916
|
+
# After the first execution the parameter remembers the type of the value
|
917
|
+
# it returns.
|
918
|
+
def execute env
|
919
|
+
AmberVM::debug "Executing Parameter at #{@pos}..." if $DEBUG
|
920
|
+
r = env.param(@num.execute(env).to_i)
|
921
|
+
@type = r.data_type if r && r.respond_to?(:data_type)
|
922
|
+
r
|
923
|
+
end
|
924
|
+
|
925
|
+
def optimize variables = {}
|
926
|
+
num = @num.optimize variables
|
927
|
+
Parameter.new(num, @pos)
|
928
|
+
end
|
929
|
+
end
|
930
|
+
|
931
|
+
# A sequence is a list of commands that are executed one after
|
932
|
+
# another.
|
933
|
+
# The type of the squence is equal to the last element of the sequence.
|
934
|
+
class Sequence < Element
|
935
|
+
attr_accessor :pos
|
936
|
+
attr_accessor :data
|
937
|
+
|
938
|
+
# The Sequence is initialized wiht 1 to 2 parameters.
|
939
|
+
#
|
940
|
+
# src:: The source is an array that holds the inital list of commands that
|
941
|
+
# are supposed to be executed.
|
942
|
+
#
|
943
|
+
# pos:: The position within the source code of the definition - for
|
944
|
+
# deugging purpose.
|
945
|
+
def initialize src=[], pos = nil
|
946
|
+
@data = src
|
947
|
+
@pos = pos
|
948
|
+
end
|
949
|
+
|
950
|
+
def pretty_print(q)
|
951
|
+
first = true
|
952
|
+
q.group 1, "{", "}" do
|
953
|
+
@data.each do |c|
|
954
|
+
if first
|
955
|
+
first = false
|
956
|
+
else
|
957
|
+
q.text ";"
|
958
|
+
end
|
959
|
+
q.breakable
|
960
|
+
q.pp c
|
961
|
+
end
|
962
|
+
q.breakable
|
963
|
+
end
|
964
|
+
end
|
965
|
+
|
966
|
+
# When exeuted a sequence starts to execute every element in it starting
|
967
|
+
# with the first element in the array.
|
968
|
+
#
|
969
|
+
# The result is the last element of the array executed.
|
970
|
+
def execute env
|
971
|
+
AmberVM::debug "Executing Sequence... #{inspect}" if $DEBUG
|
972
|
+
for item in @data
|
973
|
+
r = item.execute env
|
974
|
+
end
|
975
|
+
r
|
976
|
+
end
|
977
|
+
|
978
|
+
# When adding something to the Sequence a new Sequence will be created
|
979
|
+
# with the result of the joined arrays.
|
980
|
+
def + v
|
981
|
+
v = v.data if v.is_a? AmberVM::Interpreter::Sequence
|
982
|
+
Sequence.new(@data + v)
|
983
|
+
end
|
984
|
+
|
985
|
+
def << v
|
986
|
+
@data << v
|
987
|
+
self
|
988
|
+
end
|
989
|
+
|
990
|
+
def unshift v
|
991
|
+
@data.unshift v
|
992
|
+
self
|
993
|
+
end
|
994
|
+
|
995
|
+
# Optimization for sequences
|
996
|
+
def optimize variables = {}
|
997
|
+
|
998
|
+
if @data.size == 1
|
999
|
+
return @data.first.optimize(variables)
|
1000
|
+
else
|
1001
|
+
newdata = []
|
1002
|
+
@data.each do |d|
|
1003
|
+
d = d.optimize variables
|
1004
|
+
if d.is_a? AmberVM::Interpreter::Sequence
|
1005
|
+
newdata.concat(d.data)
|
1006
|
+
else
|
1007
|
+
newdata << d
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
Sequence.new(newdata, @pos)
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# The data type of the list is :any as it is unknown where the the
|
1015
|
+
# sequence exits.
|
1016
|
+
def data_type
|
1017
|
+
:any
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
# This is an exception designed to handle the return statement.
|
1022
|
+
# It is thrown for for the return and the value can be evaluated.
|
1023
|
+
#
|
1024
|
+
# The catching is handled bythe +AmberVM::Classes::Block+ class.
|
1025
|
+
class ReturnData < Exception
|
1026
|
+
attr_reader :val
|
1027
|
+
attr_reader :pos
|
1028
|
+
|
1029
|
+
# The ReturnException is initialized wiht 1 to 2 parameters.
|
1030
|
+
#
|
1031
|
+
# val:: The value that will be returned, aka the part after 'return'.
|
1032
|
+
#
|
1033
|
+
# pos:: The position within the source code of the definition - for
|
1034
|
+
# deugging purpose.
|
1035
|
+
def initialize val, pos = nil
|
1036
|
+
super()
|
1037
|
+
@val = val
|
1038
|
+
@pos = pos
|
1039
|
+
end
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
# Represents the return statement, it throws a
|
1043
|
+
# +ReturnException+ which can be caught to have the function
|
1044
|
+
# or block return what they wish.
|
1045
|
+
class Return < Element
|
1046
|
+
|
1047
|
+
# The Return is initialized wiht 1 to 2 parameters.
|
1048
|
+
#
|
1049
|
+
# val:: The value that will be returned, aka the part after 'return'.
|
1050
|
+
#
|
1051
|
+
# pos:: The position within the source code of the definition - for
|
1052
|
+
# deugging purpose.
|
1053
|
+
def initialize val, pos = nil
|
1054
|
+
super(pos)
|
1055
|
+
@val = val
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
def pretty_print(q)
|
1059
|
+
q.text "return "
|
1060
|
+
q.pp @val
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
# The data type of a return statement is any, as it does not return
|
1064
|
+
# anything at all, after all it jumps out of a block.
|
1065
|
+
def data_type
|
1066
|
+
:any
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
# When executed the Return executed the value and then raises a
|
1070
|
+
# ReturnException.
|
1071
|
+
def execute env
|
1072
|
+
v = @val.execute(env)
|
1073
|
+
AmberVM::debug "Return reached, returning: #{v}" if $DEBUG
|
1074
|
+
throw :return, ReturnData.new(v)
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def optimize variables = {}
|
1078
|
+
val = @val.optimize variables
|
1079
|
+
Return.new(val, @pos)
|
1080
|
+
end
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
# A function call or a function or a block. The initialization of a new
|
1084
|
+
# environment is done by the function Class as it also sorts the arguments
|
1085
|
+
# into it.
|
1086
|
+
#
|
1087
|
+
# Arguments are only executed when the function does return true for
|
1088
|
+
# execargs.
|
1089
|
+
class FunctionCall < Element
|
1090
|
+
attr_reader :arguments
|
1091
|
+
attr_reader :function
|
1092
|
+
# The constructor. +function+ can either be a block object or a function
|
1093
|
+
# class.
|
1094
|
+
#
|
1095
|
+
# Arguments is a list of the arguments to the function.
|
1096
|
+
def initialize function, arguments, pos = [0,0,0]
|
1097
|
+
super(pos)
|
1098
|
+
@function = function
|
1099
|
+
@arguments = arguments
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
def pretty_print(q)
|
1103
|
+
first = true
|
1104
|
+
q.pp @function
|
1105
|
+
q.text "("
|
1106
|
+
@arguments.each do |a|
|
1107
|
+
if first
|
1108
|
+
first = false
|
1109
|
+
else
|
1110
|
+
q.text ', '
|
1111
|
+
end
|
1112
|
+
q.pp a
|
1113
|
+
end
|
1114
|
+
q.text ")"
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
# The data type of the FunctionCall is the return value of the function
|
1118
|
+
# that it calls.
|
1119
|
+
def data_type
|
1120
|
+
@function.data_type
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
# Comparing two function calls will result in a match when the passed
|
1124
|
+
# arguments are the same and the function to call the same
|
1125
|
+
def == v
|
1126
|
+
if v.is_a? FunctionCall
|
1127
|
+
(@arguments == v.arguments) && (@function == v.function)
|
1128
|
+
else
|
1129
|
+
false
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def optimize variables = {}
|
1134
|
+
arguments = @arguments.map{ |a| a.optimize variables}
|
1135
|
+
function = @function
|
1136
|
+
function = @function.optimize variables if function.respond_to?(:optimize)
|
1137
|
+
FunctionCall.new(function, arguments, @pos)
|
1138
|
+
end
|
1139
|
+
|
1140
|
+
# When executed the FunctionCall has four possible behaviours.
|
1141
|
+
#
|
1142
|
+
# 1) If the function is a block, so an anonymous function, the arguments
|
1143
|
+
# will be executed and then the block is called.
|
1144
|
+
#
|
1145
|
+
# 2) The function is a locally defined function and then the arguments are
|
1146
|
+
# executed and passed to the locally defined function.
|
1147
|
+
#
|
1148
|
+
# 3) The function is a AmberVM::Functions::Function and execargs returns true,
|
1149
|
+
# the arguments are executed and the function called.
|
1150
|
+
#
|
1151
|
+
# 4) The function is a AmberVM::Functions::Function and execargs returns
|
1152
|
+
# false, the arguments are passed along unexecuted and the function has
|
1153
|
+
# to take care of that itself. This is important for logical functions
|
1154
|
+
# as and and or which execute only some of the arguments
|
1155
|
+
def execute env
|
1156
|
+
AmberVM::debug "Executing FunctionCall..." if $DEBUG
|
1157
|
+
# The function is a anonymous function
|
1158
|
+
if @function.is_a? AmberVM::Classes[:block] or @function.is_a? AmberVM::Interpreter::Closures::Binding
|
1159
|
+
# The arguments are executed.
|
1160
|
+
args = @arguments.map do |arg|
|
1161
|
+
arg.execute env
|
1162
|
+
end
|
1163
|
+
# Call the function
|
1164
|
+
@function.call(args, env, @pos)
|
1165
|
+
# The function is a selfdefined function (or object function)
|
1166
|
+
elsif @function.is_a? AmberVM::Interpreter::Element
|
1167
|
+
fun = @function.execute(env)
|
1168
|
+
# The arguments are executed.
|
1169
|
+
args = @arguments.map do |arg|
|
1170
|
+
arg.execute env
|
1171
|
+
end
|
1172
|
+
# Call the function
|
1173
|
+
begin
|
1174
|
+
fun.call(args, env, @pos)
|
1175
|
+
rescue Exception => e
|
1176
|
+
exit
|
1177
|
+
end
|
1178
|
+
# The function is a selfdefined function (or object function)
|
1179
|
+
elsif fun = env.function(@function)
|
1180
|
+
# The arguments are executed.
|
1181
|
+
args = @arguments.map do |arg|
|
1182
|
+
arg.execute env
|
1183
|
+
end
|
1184
|
+
# Call the function
|
1185
|
+
|
1186
|
+
fun.call(args, env, @pos)
|
1187
|
+
# If nothing of the above the function must be a global function.
|
1188
|
+
else
|
1189
|
+
raise RuntimeError.new("Function '#{@function}' Not found!", @pos[0], @pos[1], @pos[2])
|
1190
|
+
end
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
|
1194
|
+
# # A function call or a function or a block. The initialization of a new
|
1195
|
+
# # environment is done by the function Class as it also sorts the arguments
|
1196
|
+
# # into it.
|
1197
|
+
# #
|
1198
|
+
# # Arguments are only executed when the function does return true for
|
1199
|
+
# # execargs.
|
1200
|
+
# class ObjectFunctionCall < Element
|
1201
|
+
# attr_reader :arguments
|
1202
|
+
# attr_reader :function
|
1203
|
+
# # The constructor. +function+ can either be a block object or a function
|
1204
|
+
# # class.
|
1205
|
+
# #
|
1206
|
+
# # Arguments is a list of the arguments to the function.
|
1207
|
+
# def initialize object, function, arguments, pos = [0,0,0]
|
1208
|
+
# super(pos)
|
1209
|
+
# @object = object
|
1210
|
+
# @function = function
|
1211
|
+
# @arguments = arguments
|
1212
|
+
# end
|
1213
|
+
#
|
1214
|
+
# def pretty_print(q)
|
1215
|
+
# first = true
|
1216
|
+
# q.pp @object
|
1217
|
+
# q.text '.'
|
1218
|
+
# q.pp @function
|
1219
|
+
# q.text "("
|
1220
|
+
# @arguments.each do |a|
|
1221
|
+
# if first
|
1222
|
+
# first = false
|
1223
|
+
# else
|
1224
|
+
# q.text ', '
|
1225
|
+
# end
|
1226
|
+
# q.pp a
|
1227
|
+
# end
|
1228
|
+
# q.text ")"
|
1229
|
+
# end
|
1230
|
+
#
|
1231
|
+
# # The data type of the FunctionCall is the return value of the function
|
1232
|
+
# # that it calls.
|
1233
|
+
# def data_type
|
1234
|
+
# @function.data_type
|
1235
|
+
# end
|
1236
|
+
#
|
1237
|
+
# # Comparing two function calls will result in a match when the passed
|
1238
|
+
# # arguments are the same and the function to call the same
|
1239
|
+
# def == v
|
1240
|
+
# if v.is_a? FunctionCall
|
1241
|
+
# (@arguments == v.arguments) && (@function == v.function)
|
1242
|
+
# else
|
1243
|
+
# false
|
1244
|
+
# end
|
1245
|
+
# end
|
1246
|
+
#
|
1247
|
+
# def optimize variables = {}
|
1248
|
+
# @arguments.map!{ |a| a.optimize variables}
|
1249
|
+
# @function.optimize variables if @function.respond_to?(:optimize)
|
1250
|
+
# super
|
1251
|
+
# end
|
1252
|
+
#
|
1253
|
+
# # When executed the FunctionCall has four possible behaviours.
|
1254
|
+
# #
|
1255
|
+
# # 1) If the function is a block, so an anonymous function, the arguments
|
1256
|
+
# # will be executed and then the block is called.
|
1257
|
+
# #
|
1258
|
+
# # 2) The function is a locally defined function and then the arguments are
|
1259
|
+
# # executed and passed to the locally defined function.
|
1260
|
+
# #
|
1261
|
+
# # 3) The function is a AmberVM::Functions::Function and execargs returns true,
|
1262
|
+
# # the arguments are executed and the function called.
|
1263
|
+
# #
|
1264
|
+
# # 4) The function is a AmberVM::Functions::Function and execargs returns
|
1265
|
+
# # false, the arguments are passed along unexecuted and the function has
|
1266
|
+
# # to take care of that itself. This is important for logical functions
|
1267
|
+
# # as and and or which execute only some of the arguments
|
1268
|
+
# def execute env
|
1269
|
+
# AmberVM::debug "Executing Object FunctionCall..." if $DEBUG
|
1270
|
+
# obj = @object.execute(env).val
|
1271
|
+
# # The function is a anonymous function
|
1272
|
+
# if fun = obj.functions[@function]
|
1273
|
+
# # The arguments are executed.
|
1274
|
+
# args = @arguments.map do |arg|
|
1275
|
+
# arg.execute env
|
1276
|
+
# end
|
1277
|
+
# # Call the function
|
1278
|
+
# obj.obj_send(@function, args, env)
|
1279
|
+
# # If nothing of the above the function must be a global function.
|
1280
|
+
# else
|
1281
|
+
# raise RuntimeError.new("Function '#{@function}' for object #{obj} not found!", @pos[0], @pos[1], @pos[2])
|
1282
|
+
# end
|
1283
|
+
# end
|
1284
|
+
# end
|
1285
|
+
|
1286
|
+
# A core call is a call to the direct core functions and libraries AmberVM proides.
|
1287
|
+
# this is used to write core libraries for different languages.
|
1288
|
+
class CoreCall < Element
|
1289
|
+
attr_reader :arguments
|
1290
|
+
attr_reader :function
|
1291
|
+
# The constructor. +function+ can either be a block object or a function
|
1292
|
+
# class.
|
1293
|
+
#
|
1294
|
+
# Arguments is a list of the arguments to the function.
|
1295
|
+
def initialize function, arguments, pos = nil
|
1296
|
+
super(pos)
|
1297
|
+
@function = function
|
1298
|
+
@arguments = arguments
|
1299
|
+
end
|
1300
|
+
|
1301
|
+
def pretty_print(q)
|
1302
|
+
binary = {:sub => '-', :add => '+', :mul => '*', :div => '/', :mod => '%', :shl => '<<', :shr => '>>',
|
1303
|
+
:cmp => '<=>', :eq => '==', :gt => '>', :gte => '>=', :lt => '<', :lte => '<=',
|
1304
|
+
:bitwise_and => '&', :bitwise_or => '|', :bitwise_xor => '^',
|
1305
|
+
:and => '&&', :or => '||', }
|
1306
|
+
if binary.keys.include?(@function)
|
1307
|
+
first = true
|
1308
|
+
@arguments.each do |a|
|
1309
|
+
if first
|
1310
|
+
first = false
|
1311
|
+
else
|
1312
|
+
q.text " #{binary[@function]} "
|
1313
|
+
end
|
1314
|
+
q.pp a
|
1315
|
+
end
|
1316
|
+
else
|
1317
|
+
first = true
|
1318
|
+
q.pp @function
|
1319
|
+
q.text "!("
|
1320
|
+
@arguments.each do |a|
|
1321
|
+
if first
|
1322
|
+
first = false
|
1323
|
+
else
|
1324
|
+
q.text ', '
|
1325
|
+
end
|
1326
|
+
q.pp a
|
1327
|
+
end
|
1328
|
+
q.text ")"
|
1329
|
+
end
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
def optimize variables = {}
|
1333
|
+
arguments = @arguments.map!{ |a| a.optimize variables}
|
1334
|
+
if fun = AmberVM::Functions[@function]
|
1335
|
+
AmberVM::Interpreter::SimpleCoreCall.new(fun, arguments, @pos)
|
1336
|
+
else
|
1337
|
+
CoreCall.new(@function, arguments, @pos)
|
1338
|
+
end
|
1339
|
+
end
|
1340
|
+
|
1341
|
+
def data_type
|
1342
|
+
AmberVM::Functions[@function] ? AmberVM::Functions[@function].data_type : :any
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
# When executed the CoreCall it will call one of the AmberVM's library functions
|
1346
|
+
def execute env
|
1347
|
+
AmberVM::debug "Executing CoreCall... args: #{@arguments.inspect}" if $DEBUG
|
1348
|
+
args = nil
|
1349
|
+
# The function is a anonymous function
|
1350
|
+
|
1351
|
+
# Get the function from he globals
|
1352
|
+
if not fun = AmberVM::Functions[@function]
|
1353
|
+
raise RuntimeError.new("Function Not found!", @pos[0], @pos[1], @pos[2])
|
1354
|
+
end
|
1355
|
+
# Test if the arguments should be executed
|
1356
|
+
if fun.execargs
|
1357
|
+
# The arges get executed
|
1358
|
+
args = @arguments.map do |arg|
|
1359
|
+
a = arg.execute env
|
1360
|
+
end
|
1361
|
+
else
|
1362
|
+
args = @arguments
|
1363
|
+
end
|
1364
|
+
args.map! do |a|
|
1365
|
+
a = a.is_a?(AmberVM::Interpreter::VariableStorage) ? a.val : a
|
1366
|
+
a
|
1367
|
+
end
|
1368
|
+
# Call the function
|
1369
|
+
begin
|
1370
|
+
f = fun.call(args, env, @pos)
|
1371
|
+
f
|
1372
|
+
rescue Exception => e
|
1373
|
+
raise e
|
1374
|
+
raise RuntimeError.new("Function failed to execute: #{e}", @pos[0], @pos[1], @pos[2])
|
1375
|
+
end
|
1376
|
+
end
|
1377
|
+
end
|
1378
|
+
end
|
1379
|
+
end
|
1380
|
+
require 'amber/optimisation'
|