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.
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'