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,299 @@
1
+ module AmberVM
2
+ module Interpreter
3
+
4
+ # Version of the AmberVM::Interpreter::Variable that handles static variable names
5
+ class SimpleVariable < Variable
6
+ # A SimpleVariable is initialized wiht 1 to 2 parameters.
7
+ #
8
+ # name:: The name of the variable to get, it will be executed as long as
9
+ # it is no Sybol in which case it is treated as a special variable.
10
+ #
11
+ # pos:: The position within the source code of the definition - for
12
+ # deugging purpose.
13
+ def initialize name, pos = ['-', '-', '-'], require_declaration = false
14
+ super(name.to_s,pos, require_declaration)
15
+ end
16
+
17
+ # The name is a sting and we simple get what is written in the env
18
+ def execute env
19
+ AmberVM::debug "Executing SimpleVariable at #{@pos}..." if $DEBUG
20
+ begin
21
+ r = env[@name]
22
+ if not r
23
+ if @require_declaration
24
+ raise RuntimeError.new("Variable #{@name} was not declared.")
25
+ else
26
+ r = env.declare(@name, AmberVM::Classes::Null.new(nil))
27
+ end
28
+ end
29
+ @type = r.val.data_type if r.val.respond_to?(:data_type)
30
+ r
31
+ rescue Exception => e
32
+ raise RuntimeError.new("Failed to get Varialbe #{e}", @pos[0], @pos[1], @pos[2])
33
+ end
34
+ end
35
+
36
+ def optimize variables = {}
37
+ variables[@name] ||= VariableStorage.new(AmberVM::Classes[:null].new(nil))
38
+ StaticVariable.new(variables[@name], pos, @require_declaration)
39
+ end
40
+ end
41
+
42
+ # Version of the AmberVM::Interpreter::Variable that handles static variable names
43
+ class StaticVariable < Variable
44
+ # A SimpleVariable is initialized wiht 1 to 2 parameters.
45
+ #
46
+ # name:: The name of the variable to get, it will be executed as long as
47
+ # it is no Sybol in which case it is treated as a special variable.
48
+ #
49
+ # pos:: The position within the source code of the definition - for
50
+ # deugging purpose.
51
+ def initialize var, pos = ['-', '-', '-'], require_declaration = false
52
+ var = AmberVM::Interpreter::makevs(var)
53
+ super(var, pos, require_declaration)
54
+ end
55
+
56
+ def value
57
+ @name
58
+ end
59
+ # The name is a sting and we simple get what is written in the env
60
+ def execute env
61
+ AmberVM::debug "Executing Static at #{@pos}..." if $DEBUG
62
+ @name
63
+ end
64
+
65
+ def optimize variables = {}
66
+ StaticVariable.new(@name, @pos, @require_declaration)
67
+ end
68
+ end
69
+
70
+ # class SimpleFunctionDefinition < FunctionDefinition
71
+ #
72
+ # # Initializes a new function definition
73
+ # #
74
+ # # name:: is the name of the function to define, if it is not
75
+ # # unique it will overwrite earlyer definitions
76
+ # # (unless override is false)
77
+ # #
78
+ # # body:: is the body of the cuntion. this will be executed
79
+ # # when the defined function is called.
80
+ # #
81
+ # # override:: when true (default) earlyer definitions are
82
+ # # replaced when it is called a second time.
83
+ # # When false a exception is thrown
84
+ # #
85
+ # # pos:: The position within the source code of the definition - for
86
+ # # deugging purpose.
87
+ # def initialize name, body, override = true, pos = nil
88
+ # super(name, body, override, pos)
89
+ #
90
+ # end
91
+ #
92
+ # # When executed the FunctionDefinition first checks if a function with the
93
+ # # same name is already defined. If it is and +override+ wasn't set to ture
94
+ # # it trows a Exception. Otherwise it defines the function, deleting the
95
+ # # old definition when still repsent.
96
+ # #
97
+ # # It returns the body of the newly defined function.
98
+ # def execute env
99
+ # if (not @override) and env.function(@name)
100
+ # raise RuntimeError.new("Function #{@name} already defined", @pos[0], @pos[1], @pos[2])
101
+ # end
102
+ # if not env
103
+ # raise RuntimeError.new("No env in #{@name}", @pos[0], @pos[1], @pos[2])
104
+ # end
105
+ # env.def_function(@name,@body)
106
+ # @body
107
+ # end
108
+ #
109
+ # def optimize variables = {}
110
+ # local_vars = {}
111
+ # @body = @body.optimize local_vars
112
+ # self
113
+ # end
114
+ # end
115
+
116
+ class SimpleBlock < Block
117
+
118
+ def execute env
119
+ @content.execute tenv
120
+ end
121
+
122
+ def optimize variables = {}
123
+ SimpleBlock.new(@content.optimize(variables))
124
+ end
125
+ end
126
+
127
+ class SimpleCoreCall < CoreCall
128
+
129
+ def initialize function, arguments, pos = nil
130
+ @pos = pos
131
+ @function = function
132
+ @arguments = arguments
133
+ end
134
+ # When executed the CoreCall it will call one of the AmberVM's library functions
135
+ def execute env
136
+ AmberVM::debug "Executing SimpleCoreCall... args: #{@arguments.inspect}" if $DEBUG
137
+ args = nil
138
+ # The function is a anonymous function
139
+
140
+ # Get the function from he globals
141
+ # Test if the arguments should be executed
142
+ if @function.execargs
143
+ # The arges get executed
144
+ args = @arguments.map do |arg|
145
+ a = arg.execute env
146
+ end
147
+ else
148
+ args = @arguments
149
+ end
150
+ args = args.map do |a|
151
+ a.is_a?(AmberVM::Interpreter::VariableStorage) ? a.val : a
152
+ end
153
+ # Call the function
154
+ begin
155
+ f = @function.call(args, env, @pos)
156
+ f
157
+ rescue Exception => e
158
+ pp self
159
+ raise e
160
+ raise RuntimeError.new("Function failed to execute: #{e}", @pos[0], @pos[1], @pos[2])
161
+ end
162
+ end
163
+
164
+ def optimize env
165
+ optimized = true
166
+ @arguments.each do |a|
167
+ if optimized
168
+ if a.is_a?(AmberVM::Interpreter::Constant)
169
+ elsif a.is_a?(AmberVM::Interpreter::VariableValue)
170
+ if a.variable.is_a?(AmberVM::Interpreter::StaticVariable)
171
+ else
172
+ optimized = false
173
+ end
174
+ else
175
+ optimized = false
176
+ end
177
+ end
178
+ end
179
+ if optimized and @function.execargs
180
+ newargs = arguments.map! do |a|
181
+ a = if a.is_a?(AmberVM::Interpreter::VariableValue)
182
+ a.variable.value
183
+ else
184
+ AmberVM::Interpreter::VariableStorage.new(a.execute(nil))
185
+ end
186
+ end
187
+ AmberVM::Interpreter::TrivialCoreCall.new(@function, @arguments, @pos)
188
+ else
189
+ self
190
+ end
191
+ end
192
+ end
193
+
194
+ class TrivialCoreCall < SimpleCoreCall
195
+ def initialize function, arguments, pos = nil
196
+ super
197
+ end
198
+
199
+ def execute env
200
+ AmberVM::debug "Executing TrivialCoreCall... args: #{@arguments.inspect}" if $DEBUG
201
+ args = nil
202
+ # The function is a anonymous function
203
+
204
+ args = @arguments.map do |a|
205
+ a.val
206
+ end
207
+ # Call the function
208
+ begin
209
+ f = @function.call(args, env, @pos)
210
+ f
211
+ rescue Exception => e
212
+ pp self
213
+ raise e
214
+ raise RuntimeError.new("Function failed to execute: #{e}", @pos[0], @pos[1], @pos[2])
215
+ end
216
+ end
217
+
218
+ def optimize variables
219
+ self
220
+ end
221
+ end
222
+
223
+
224
+ # For details see AmberVM::Interpreter::Declration, this is a
225
+ # optimized version vor simple cases.
226
+ #
227
+ # Only #value# is evaluated before the assignment is done.
228
+
229
+ class SimpleDeclaration < Declaration
230
+ # A Declaration is initialized wiht 2 to 3 parameters.
231
+ #
232
+ # name:: The name of the variable to store, it will be executed, usually
233
+ # this will be a Constant, unless dynamic naming is needed by the
234
+ # implemented language.
235
+ #
236
+ # value:: The value that will be assigned to the variable, for a = 1 + 1
237
+ # '1+1' would be the value to assign, so as this already suggests
238
+ # the value will be executed.
239
+ #
240
+ # pos:: The position within the source code of the definition - for
241
+ # deugging purpose.
242
+ def initialize name, value, pos = nil
243
+ @pos = pos
244
+ @name = name.to_s
245
+ @value = value
246
+ end
247
+
248
+ # Only the value is evaluated before assigning it, the name is taken
249
+ # to be a string.
250
+ #
251
+ # For details see AmberVM::Interpreter::Declration
252
+ def execute env
253
+ AmberVM::debug "Executing SimpleAssignment at #{@pos}..." if $DEBUG
254
+ env.declare(@name,@value.execute(env)).val
255
+ end
256
+
257
+ def optimize variables = {}
258
+ variables[@name] = VariableStorage.new(AmberVM::Classes[:null].new(nil))
259
+ value = @value.optimize variables
260
+ Assignment.new(StaticVariable.new(variables[@name]), value)
261
+ end
262
+ end
263
+
264
+ class OptimizedClosures < Closures
265
+
266
+ class OptimizedBinding < Binding
267
+
268
+ def initialize content, vars
269
+ @content = content
270
+ @vars = vars
271
+ end
272
+
273
+ def call params, env, pos = nil
274
+ @vars.each do |var,value|
275
+ var.val = value
276
+ end
277
+ @content.call(params, env, pos)
278
+ end
279
+ end
280
+
281
+ def initialize content, variables
282
+ @content = content
283
+ @variables = variables
284
+ end
285
+
286
+ def optimize variables = {}
287
+ OptimizedClosures.new(@content, @var)
288
+ end
289
+
290
+ def execute env
291
+ local_variables = {}
292
+ new_content=@content.optimize(local_variables)
293
+ vars_to_copy=Set.new(@variables.keys).intersection(Set.new(local_variables.keys)).to_a.map{|k| [local_variables[k],@variables[k]]}
294
+ OptimizedBinding.new(new_content, vars_to_copy.map{|inner,outer| [inner,outer.val]})
295
+ end
296
+
297
+ end
298
+ end
299
+ end
@@ -0,0 +1,337 @@
1
+ module AmberVM
2
+
3
+ # = PluginHost
4
+ #
5
+ # $Id: plugin.rb 255 2008-09-21 16:25:44Z murphy $
6
+ #
7
+ # A simple subclass plugin system.
8
+ #
9
+ # Example:
10
+ # class Generators < PluginHost
11
+ # plugin_path 'app/generators'
12
+ # end
13
+ #
14
+ # class Generator
15
+ # extend Plugin
16
+ # PLUGIN_HOST = Generators
17
+ # end
18
+ #
19
+ # class FancyGenerator < Generator
20
+ # register_for :fancy
21
+ # end
22
+ #
23
+ # Generators[:fancy] #-> FancyGenerator
24
+ # # or
25
+ # AmberVM.require_plugin 'Generators/fancy'
26
+ module PluginHost
27
+
28
+ # Raised if Encoders::[] fails because:
29
+ # * a file could not be found
30
+ # * the requested Encoder is not registered
31
+ PluginNotFound = Class.new Exception
32
+ HostNotFound = Class.new Exception
33
+
34
+ PLUGIN_HOSTS = []
35
+ PLUGIN_HOSTS_BY_ID = {} # dummy hash
36
+
37
+ # Loads all plugins using list and load.
38
+ def load_all
39
+ for plugin in list
40
+ load plugin
41
+ end
42
+ end
43
+
44
+ # Returns the Plugin for +id+.
45
+ #
46
+ # Example:
47
+ # yaml_plugin = MyPluginHost[:yaml]
48
+ def [] id, *args, &blk
49
+ plugin = validate_id(id)
50
+ begin
51
+ plugin = plugin_hash.[] plugin, *args, &blk
52
+ end while plugin.is_a? Symbol
53
+ plugin
54
+ end
55
+
56
+ # Alias for +[]+.
57
+ alias load []
58
+
59
+ def require_helper plugin_id, helper_name
60
+ path = path_to File.join(plugin_id, helper_name)
61
+ require path
62
+ end
63
+
64
+ def has? id
65
+ plugin_hash.has_key? validate_id(id)
66
+ end
67
+
68
+ class << self
69
+
70
+ # Adds the module/class to the PLUGIN_HOSTS list.
71
+ def extended mod
72
+ PLUGIN_HOSTS << mod
73
+ end
74
+
75
+ # Warns you that you should not #include this module.
76
+ def included mod
77
+ warn "#{name} should not be included. Use extend."
78
+ end
79
+
80
+ # Find the PluginHost for host_id.
81
+ def host_by_id host_id
82
+ unless PLUGIN_HOSTS_BY_ID.default_proc
83
+ ph = Hash.new do |h, a_host_id|
84
+ for host in PLUGIN_HOSTS
85
+ h[host.host_id] = host
86
+ end
87
+ h.fetch a_host_id, nil
88
+ end
89
+ PLUGIN_HOSTS_BY_ID.replace ph
90
+ end
91
+ PLUGIN_HOSTS_BY_ID[host_id]
92
+ end
93
+
94
+ end
95
+
96
+ # The path where the plugins can be found.
97
+ def plugin_path *args
98
+ unless args.empty?
99
+ @plugin_path = File.expand_path File.join(*args)
100
+ load_map
101
+ end
102
+ @plugin_path or raise 'No plugin_path given for %p %p' % [args, self]
103
+ end
104
+
105
+ # The host's ID.
106
+ #
107
+ # If PLUGIN_HOST_ID is not set, it is simply the class name.
108
+ def host_id
109
+ if self.const_defined? :PLUGIN_HOST_ID
110
+ self::PLUGIN_HOST_ID
111
+ else
112
+ name
113
+ end
114
+ end
115
+
116
+ # Map a plugin_id to another.
117
+ #
118
+ # Usage: Put this in a file plugin_path/_map.rb.
119
+ #
120
+ # class MyColorHost < PluginHost
121
+ # map :navy => :dark_blue,
122
+ # :maroon => :brown,
123
+ # :luna => :moon
124
+ # end
125
+ def map hash
126
+ for from, to in hash
127
+ from = validate_id from
128
+ to = validate_id to
129
+ plugin_hash[from] = to unless plugin_hash.has_key? from
130
+ end
131
+ end
132
+
133
+ # Define the default plugin to use when no plugin is found
134
+ # for a given id.
135
+ #
136
+ # See also map.
137
+ #
138
+ # class MyColorHost < PluginHost
139
+ # map :navy => :dark_blue
140
+ # default :gray
141
+ # end
142
+ def default id = nil
143
+ if id
144
+ id = validate_id id
145
+ plugin_hash[nil] = id
146
+ else
147
+ plugin_hash[nil]
148
+ end
149
+ end
150
+
151
+ # Every plugin must register itself for one or more
152
+ # +ids+ by calling register_for, which calls this method.
153
+ #
154
+ # See Plugin#register_for.
155
+ def register plugin, *ids
156
+ for id in ids
157
+ unless id.is_a? Symbol
158
+ raise ArgumentError,
159
+ "id must be a Symbol, but it was a #{id.class}"
160
+ end
161
+ plugin_hash[validate_id(id)] = plugin
162
+ end
163
+ end
164
+
165
+ # A Hash of plugion_id => Plugin pairs.
166
+ def plugin_hash
167
+ @plugin_hash ||= create_plugin_hash
168
+ end
169
+
170
+ # Returns an array of all .rb files in the plugin path.
171
+ #
172
+ # The extension .rb is not included.
173
+ def list
174
+ Dir[path_to('*')].select do |file|
175
+ File.basename(file)[/^(?!_)\w+\.rb$/]
176
+ end.map do |file|
177
+ File.basename file, '.rb'
178
+ end
179
+ end
180
+
181
+ # Makes a map of all loaded plugins.
182
+ def inspect
183
+ map = plugin_hash.dup
184
+ map.each do |id, plugin|
185
+ map[id] = plugin.to_s[/(?>\w+)$/]
186
+ end
187
+ "#{name}[#{host_id}]#{map.inspect}"
188
+ end
189
+
190
+ protected
191
+ # Created a new plugin list and stores it to @plugin_hash.
192
+ def create_plugin_hash
193
+ @plugin_hash =
194
+ Hash.new do |h, plugin_id|
195
+ id = validate_id(plugin_id)
196
+ path = path_to id
197
+ begin
198
+ require path
199
+ rescue LoadError => boom
200
+ if h.has_key? nil # default plugin
201
+ h[id] = h[nil]
202
+ else
203
+ raise PluginNotFound, 'Could not load plugin %p: %s' % [id, boom]
204
+ end
205
+ else
206
+ # Plugin should have registered by now
207
+ unless h.has_key? id
208
+ raise PluginNotFound,
209
+ "No #{self.name} plugin for #{id.inspect} found in #{path}."
210
+ end
211
+ end
212
+ h[id]
213
+ end
214
+ end
215
+
216
+ # Loads the map file (see map).
217
+ #
218
+ # This is done automatically when plugin_path is called.
219
+ def load_map
220
+ mapfile = path_to '_map'
221
+ if File.exist? mapfile
222
+ require mapfile
223
+ elsif $DEBUG
224
+ warn 'no _map.rb found for %s' % name
225
+ end
226
+ end
227
+
228
+ # Returns the Plugin for +id+.
229
+ # Use it like Hash#fetch.
230
+ #
231
+ # Example:
232
+ # yaml_plugin = MyPluginHost[:yaml, :default]
233
+ def fetch id, *args, &blk
234
+ plugin_hash.fetch validate_id(id), *args, &blk
235
+ end
236
+
237
+ # Returns the expected path to the plugin file for the given id.
238
+ def path_to plugin_id
239
+ File.join plugin_path, "#{plugin_id}.rb"
240
+ end
241
+
242
+ # Converts +id+ to a Symbol if it is a String,
243
+ # or returns +id+ if it already is a Symbol.
244
+ #
245
+ # Raises +ArgumentError+ for all other objects, or if the
246
+ # given String includes non-alphanumeric characters (\W).
247
+ def validate_id id
248
+ if id.is_a? Symbol or id.nil?
249
+ id
250
+ elsif id.is_a? String
251
+ if id[/\w+/] == id
252
+ id.to_sym
253
+ else
254
+ raise ArgumentError, "Invalid id: '#{id}' given."
255
+ end
256
+ else
257
+ raise ArgumentError,
258
+ "String or Symbol expected, but #{id.class} given."
259
+ end
260
+ end
261
+
262
+ end
263
+
264
+
265
+ # = Plugin
266
+ #
267
+ # Plugins have to include this module.
268
+ #
269
+ # IMPORTANT: use extend for this module.
270
+ #
271
+ # Example: see PluginHost.
272
+ module Plugin
273
+
274
+ def included mod
275
+ warn "#{name} should not be included. Use extend."
276
+ end
277
+
278
+ # Register this class for the given langs.
279
+ # Example:
280
+ # class MyPlugin < PluginHost::BaseClass
281
+ # register_for :my_id
282
+ # ...
283
+ # end
284
+ #
285
+ # See PluginHost.register.
286
+ def register_for *ids
287
+ plugin_host.register self, *ids
288
+ end
289
+
290
+ # The host for this Plugin class.
291
+ def plugin_host host = nil
292
+ if host and not host.is_a? PluginHost
293
+ raise ArgumentError,
294
+ "PluginHost expected, but #{host.class} given."
295
+ end
296
+ self.const_set :PLUGIN_HOST, host if host
297
+ self::PLUGIN_HOST
298
+ end
299
+
300
+ # Require some helper files.
301
+ #
302
+ # Example:
303
+ #
304
+ # class MyPlugin < PluginHost::BaseClass
305
+ # register_for :my_id
306
+ # helper :my_helper
307
+ #
308
+ # The above example loads the file myplugin/my_helper.rb relative to the
309
+ # file in which MyPlugin was defined.
310
+ def helper *helpers
311
+ for helper in helpers
312
+ self::PLUGIN_HOST.require_helper plugin_id, helper.to_s
313
+ end
314
+ end
315
+
316
+ # Returns the pulgin id used by the engine.
317
+ def plugin_id
318
+ name[/\w+$/].downcase
319
+ end
320
+
321
+ end
322
+
323
+ # Convenience method for plugin loading.
324
+ # The syntax used is:
325
+ #
326
+ # AmberVM.require_plugin '<Host ID>/<Plugin ID>'
327
+ #
328
+ # Returns the loaded plugin.
329
+ def self.require_plugin path
330
+ host_id, plugin_id = path.split '/', 2
331
+ host = PluginHost.host_by_id(host_id)
332
+ raise PluginHost::HostNotFound,
333
+ "No host for #{host_id.inspect} found." unless host
334
+ host.load plugin_id
335
+ end
336
+
337
+ end