AmberVM 0.0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. data/README +38 -0
  2. data/bin/ambervm +278 -0
  3. data/lib/amber/acts_as_rvm_type.rb +157 -0
  4. data/lib/amber/classes/association.rb +36 -0
  5. data/lib/amber/classes/block.rb +52 -0
  6. data/lib/amber/classes/boolean.rb +40 -0
  7. data/lib/amber/classes/class.rb +50 -0
  8. data/lib/amber/classes/error.rb +22 -0
  9. data/lib/amber/classes/list.rb +96 -0
  10. data/lib/amber/classes/null.rb +35 -0
  11. data/lib/amber/classes/number.rb +95 -0
  12. data/lib/amber/classes/object.rb +56 -0
  13. data/lib/amber/classes/string.rb +79 -0
  14. data/lib/amber/classes.rb +113 -0
  15. data/lib/amber/environment.rb +251 -0
  16. data/lib/amber/fukubukuro/ecma_core.rb +409 -0
  17. data/lib/amber/fukubukuro.rb +866 -0
  18. data/lib/amber/functions/all.rb +3 -0
  19. data/lib/amber/functions/array/append.rb +50 -0
  20. data/lib/amber/functions/array/at.rb +50 -0
  21. data/lib/amber/functions/array/set_at.rb +50 -0
  22. data/lib/amber/functions/array.rb +30 -0
  23. data/lib/amber/functions/association/assoc_get.rb +55 -0
  24. data/lib/amber/functions/association/assoc_set.rb +56 -0
  25. data/lib/amber/functions/bitwise/bitwise_and.rb +41 -0
  26. data/lib/amber/functions/bitwise/bitwise_not.rb +41 -0
  27. data/lib/amber/functions/bitwise/bitwise_or.rb +41 -0
  28. data/lib/amber/functions/bitwise/bitwise_xor.rb +41 -0
  29. data/lib/amber/functions/bitwise.rb +3 -0
  30. data/lib/amber/functions/collection/get.rb +66 -0
  31. data/lib/amber/functions/collection/set.rb +67 -0
  32. data/lib/amber/functions/collection/size.rb +54 -0
  33. data/lib/amber/functions/general/cmp.rb +43 -0
  34. data/lib/amber/functions/general/eq.rb +45 -0
  35. data/lib/amber/functions/general/gt.rb +45 -0
  36. data/lib/amber/functions/general/gte.rb +45 -0
  37. data/lib/amber/functions/general/lt.rb +45 -0
  38. data/lib/amber/functions/general/lte.rb +45 -0
  39. data/lib/amber/functions/general/neq.rb +45 -0
  40. data/lib/amber/functions/general/type.rb +43 -0
  41. data/lib/amber/functions/general.rb +3 -0
  42. data/lib/amber/functions/io/print.rb +45 -0
  43. data/lib/amber/functions/io.rb +3 -0
  44. data/lib/amber/functions/list/align.rb +73 -0
  45. data/lib/amber/functions/list/join.rb +45 -0
  46. data/lib/amber/functions/list/map.rb +58 -0
  47. data/lib/amber/functions/list/split.rb +55 -0
  48. data/lib/amber/functions/list.rb +3 -0
  49. data/lib/amber/functions/logic/and.rb +55 -0
  50. data/lib/amber/functions/logic/not.rb +40 -0
  51. data/lib/amber/functions/logic/or.rb +50 -0
  52. data/lib/amber/functions/logic.rb +3 -0
  53. data/lib/amber/functions/math/abs.rb +39 -0
  54. data/lib/amber/functions/math/acos.rb +39 -0
  55. data/lib/amber/functions/math/add.rb +40 -0
  56. data/lib/amber/functions/math/asin.rb +39 -0
  57. data/lib/amber/functions/math/atan.rb +39 -0
  58. data/lib/amber/functions/math/ceil.rb +39 -0
  59. data/lib/amber/functions/math/cos.rb +39 -0
  60. data/lib/amber/functions/math/dec.rb +39 -0
  61. data/lib/amber/functions/math/div.rb +44 -0
  62. data/lib/amber/functions/math/exp.rb +39 -0
  63. data/lib/amber/functions/math/floor.rb +39 -0
  64. data/lib/amber/functions/math/inc.rb +39 -0
  65. data/lib/amber/functions/math/log.rb +39 -0
  66. data/lib/amber/functions/math/mod.rb +41 -0
  67. data/lib/amber/functions/math/mul.rb +43 -0
  68. data/lib/amber/functions/math/neg.rb +43 -0
  69. data/lib/amber/functions/math/power.rb +43 -0
  70. data/lib/amber/functions/math/rand.rb +36 -0
  71. data/lib/amber/functions/math/round.rb +39 -0
  72. data/lib/amber/functions/math/shl.rb +41 -0
  73. data/lib/amber/functions/math/shr.rb +41 -0
  74. data/lib/amber/functions/math/sin.rb +39 -0
  75. data/lib/amber/functions/math/sub.rb +43 -0
  76. data/lib/amber/functions/math/tan.rb +39 -0
  77. data/lib/amber/functions/math.rb +3 -0
  78. data/lib/amber/functions/objects/send.rb +22 -0
  79. data/lib/amber/functions/rails/print.rb +44 -0
  80. data/lib/amber/functions/rails.rb +3 -0
  81. data/lib/amber/functions/string/ansi.rb +24 -0
  82. data/lib/amber/functions/string/capstr.rb +23 -0
  83. data/lib/amber/functions/string/center.rb +25 -0
  84. data/lib/amber/functions/string/chr.rb +16 -0
  85. data/lib/amber/functions/string/ljust.rb +26 -0
  86. data/lib/amber/functions/string/regmatch.rb +34 -0
  87. data/lib/amber/functions/string/rjust.rb +26 -0
  88. data/lib/amber/functions/string.rb +3 -0
  89. data/lib/amber/functions.rb +103 -0
  90. data/lib/amber/interpreter.rb +1380 -0
  91. data/lib/amber/languages/brainfuck.rb +153 -0
  92. data/lib/amber/languages/ecma/compiler.rb +1661 -0
  93. data/lib/amber/languages/ecma/core-math.js +67 -0
  94. data/lib/amber/languages/ecma/core-objects.js +57 -0
  95. data/lib/amber/languages/ecma.rb +9 -0
  96. data/lib/amber/languages/ecma_fuku/compiler.rb +1622 -0
  97. data/lib/amber/languages/ecma_fuku/core-math.js +67 -0
  98. data/lib/amber/languages/ecma_fuku/core-objects.js +56 -0
  99. data/lib/amber/languages/ecma_fuku.rb +13 -0
  100. data/lib/amber/languages/math/compiler.rb +70 -0
  101. data/lib/amber/languages/math/tokenizer.rb +69 -0
  102. data/lib/amber/languages/math/tree.rb +110 -0
  103. data/lib/amber/languages/math.rb +26 -0
  104. data/lib/amber/languages.rb +99 -0
  105. data/lib/amber/library.rb +79 -0
  106. data/lib/amber/optimisation.rb +299 -0
  107. data/lib/amber/plugin.rb +337 -0
  108. data/lib/amber/rails.rb +90 -0
  109. data/lib/amber.rb +106 -0
  110. data/spec/amber/class_spec.rb +27 -0
  111. data/spec/amber/enviroment_spec.rb +61 -0
  112. data/spec/amber/function_spec.rb +25 -0
  113. data/spec/amber/functions/association/assoc_get_spec.rb +41 -0
  114. data/spec/amber/functions/association/assoc_set_spec.rb +43 -0
  115. data/spec/amber/functions/collection/get_spec.rb +12 -0
  116. data/spec/amber/functions/collection/set_spec.rb +10 -0
  117. data/spec/amber/functions/collection/size_spec.rb +10 -0
  118. data/spec/amber/functions/list/split_spec.rb +47 -0
  119. data/spec/amber/functions/string/ansi_spec.rb +44 -0
  120. data/spec/amber/functions/string/capstr_spec.rb +42 -0
  121. data/spec/amber/functions/string/center_spec.rb +49 -0
  122. data/spec/amber/functions/string/ljust_spec.rb +49 -0
  123. data/spec/amber/functions/string/regmatch_spec.rb +52 -0
  124. data/spec/amber/functions/string/rjust_spec.rb +49 -0
  125. data/spec/amber/interpreter/assignment_spec.rb +22 -0
  126. data/spec/amber/interpreter/condition_spec.rb +103 -0
  127. data/spec/amber/interpreter/constant_spec.rb +31 -0
  128. data/spec/amber/interpreter/core_call_spec.rb +72 -0
  129. data/spec/amber/interpreter/interpreter_spec.rb +11 -0
  130. data/spec/amber/interpreter/parameter_spec.rb +24 -0
  131. data/spec/amber/interpreter/sequence_spec.rb +47 -0
  132. data/spec/amber/interpreter/variable_spec.rb +24 -0
  133. data/spec/amber/plugin_spec.rb +10 -0
  134. data/spec/classes/atom/association_spec.rb +39 -0
  135. data/spec/classes/atom/block_spec.rb +25 -0
  136. data/spec/classes/atom/boolean_spec.rb +67 -0
  137. data/spec/classes/atom/error_spec.rb +43 -0
  138. data/spec/classes/atom/list_spec.rb +68 -0
  139. data/spec/classes/atom/number_spec.rb +132 -0
  140. data/spec/classes/atom/string_spec.rb +175 -0
  141. data/spec/languages/ecma/ecma_array_spec.rb +79 -0
  142. data/spec/languages/ecma/ecma_closure_spec.rb +38 -0
  143. data/spec/languages/ecma/ecma_literals_spec.rb +71 -0
  144. data/spec/languages/ecma/ecma_objects_spec.rb +165 -0
  145. data/spec/languages/ecma/ecma_old_spec.rb +540 -0
  146. data/spec/languages/ecma/ecma_spec.rb +64 -0
  147. data/spec/languages/ecma_fuku/ecma_array_spec.rb +61 -0
  148. data/spec/languages/ecma_fuku/ecma_closure_spec.rb +33 -0
  149. data/spec/languages/ecma_fuku/ecma_function_spec.rb +84 -0
  150. data/spec/languages/ecma_fuku/ecma_literals_spec.rb +55 -0
  151. data/spec/languages/ecma_fuku/ecma_objects_spec.rb +133 -0
  152. data/spec/languages/ecma_fuku/ecma_old_spec.rb +415 -0
  153. data/spec/languages/ecma_fuku/ecma_operator_spec.rb +33 -0
  154. data/spec/languages/ecma_fuku/ecma_spec.rb +52 -0
  155. data/spec/languages/math/compiler_spec.rb +49 -0
  156. data/spec/languages/math/tokenizer_spec.rb +73 -0
  157. data/spec/languages/math/tree_spec.rb +153 -0
  158. 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'