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,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