slidefield 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +674 -0
- data/README.md +254 -0
- data/Rakefile +7 -0
- data/bin/slidefield +89 -0
- data/examples/complete/assets/K2.jpg +0 -0
- data/examples/complete/assets/gobi.jpg +0 -0
- data/examples/complete/assets/mount_everest.jpg +0 -0
- data/examples/complete/assets/sahara.jpg +0 -0
- data/examples/complete/main.sfp +7 -0
- data/examples/complete/slides/deserts.sfi +19 -0
- data/examples/complete/slides/mountains.sfi +25 -0
- data/examples/complete/templates.sfi +95 -0
- data/examples/complete/variables.sfi +6 -0
- data/examples/minimal/main.sfp +43 -0
- data/examples/minimal/ruby-logo.png +0 -0
- data/lib/slidefield/animator.rb +104 -0
- data/lib/slidefield/errors.rb +6 -0
- data/lib/slidefield/interpreter.rb +414 -0
- data/lib/slidefield/object_data.rb +78 -0
- data/lib/slidefield/object_manager.rb +29 -0
- data/lib/slidefield/object_rules.rb +79 -0
- data/lib/slidefield/objects/_base.rb +29 -0
- data/lib/slidefield/objects/_root.rb +10 -0
- data/lib/slidefield/objects/animation.rb +10 -0
- data/lib/slidefield/objects/debug.rb +18 -0
- data/lib/slidefield/objects/image.rb +47 -0
- data/lib/slidefield/objects/include.rb +9 -0
- data/lib/slidefield/objects/layout.rb +10 -0
- data/lib/slidefield/objects/rect.rb +44 -0
- data/lib/slidefield/objects/slide.rb +43 -0
- data/lib/slidefield/objects/song.rb +31 -0
- data/lib/slidefield/objects/text.rb +57 -0
- data/lib/slidefield/parser.rb +99 -0
- data/lib/slidefield/version.rb +3 -0
- data/lib/slidefield/viewer.rb +89 -0
- data/lib/slidefield.rb +27 -0
- data/slidefield.gemspec +27 -0
- data/test/helper.rb +11 -0
- data/test/resources/include_sub.sfp +1 -0
- data/test/resources/parse_error.sfp +1 -0
- data/test/resources/recursive_include.sfp +1 -0
- data/test/resources/sub/include_parent.sfp +1 -0
- data/test/resources/unclosed_object.sfp +2 -0
- data/test/resources/unknown_object.sfp +1 -0
- data/test/resources/wrong_template.sfp +4 -0
- data/test/test_animator.rb +244 -0
- data/test/test_examples.rb +29 -0
- data/test/test_interpreter.rb +1766 -0
- data/test/test_object_data.rb +108 -0
- data/test/test_object_manager.rb +48 -0
- data/test/test_object_rules.rb +87 -0
- data/test/test_parser.rb +408 -0
- metadata +199 -0
@@ -0,0 +1,414 @@
|
|
1
|
+
class SlideField::Interpreter
|
2
|
+
attr_accessor :root
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@parser = SlideField::Parser.new
|
6
|
+
@files = []
|
7
|
+
@root = SlideField::ObjectData.new(:ROOT, 'line 0 char 0')
|
8
|
+
end
|
9
|
+
|
10
|
+
def run_file(path, parent_obj = nil)
|
11
|
+
if @files.include? path
|
12
|
+
raise SlideField::InterpreterError,
|
13
|
+
"File already interpreted: '#{path}'"
|
14
|
+
else
|
15
|
+
@files << path
|
16
|
+
end
|
17
|
+
|
18
|
+
file = Pathname.new path
|
19
|
+
|
20
|
+
begin
|
21
|
+
input = file.read
|
22
|
+
rescue => e
|
23
|
+
raise SlideField::InterpreterError, e.message
|
24
|
+
end
|
25
|
+
|
26
|
+
include_path = file.dirname.to_s
|
27
|
+
@rootpath = Pathname.new(include_path) if parent_obj.nil? || @rootpath.nil?
|
28
|
+
context = file.relative_path_from(@rootpath).to_s
|
29
|
+
|
30
|
+
run_string input, include_path, context, parent_obj
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_string(input, include_path = '.', context = 'input', parent_obj = nil)
|
34
|
+
include_path = File.absolute_path(include_path) + File::SEPARATOR
|
35
|
+
|
36
|
+
object = parent_obj || @root
|
37
|
+
object.include_path = include_path unless object.include_path
|
38
|
+
object.context = context unless object.context
|
39
|
+
|
40
|
+
close = parent_obj.nil?
|
41
|
+
|
42
|
+
begin
|
43
|
+
tree = @parser.parse input, reporter: Parslet::ErrorReporter::Deepest.new
|
44
|
+
rescue Parslet::ParseFailed => error
|
45
|
+
cause = error.cause
|
46
|
+
reason = nil
|
47
|
+
|
48
|
+
while cause
|
49
|
+
reason = cause.to_s
|
50
|
+
cause = cause.children.last
|
51
|
+
end
|
52
|
+
|
53
|
+
raise SlideField::ParseError, reason
|
54
|
+
end
|
55
|
+
|
56
|
+
interpret_tree tree, object, include_path, context, close
|
57
|
+
rescue SlideField::Error => error
|
58
|
+
message = error.message
|
59
|
+
|
60
|
+
if !message.start_with?('[') && message =~ /line (\d+) char (\d+)/
|
61
|
+
line = $1.to_i - 1
|
62
|
+
column = $2.to_i - 1
|
63
|
+
|
64
|
+
if line > -1 && source = input.lines[line]
|
65
|
+
excerpt = source.strip
|
66
|
+
column -= source.index excerpt
|
67
|
+
arrow = "#{"\x20" * column}^"
|
68
|
+
|
69
|
+
message += "\n\t#{excerpt}\n\t#{arrow}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
raise error.class, "[#{context}] #{message}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def interpret_tree(tree, object, child_path = nil, child_context = nil, close_object = true)
|
77
|
+
tree.respond_to? :each and tree.each {|stmt|
|
78
|
+
if stmt_data = stmt[:assignment]
|
79
|
+
interpret_assignment stmt_data, object
|
80
|
+
elsif stmt_data = stmt[:object]
|
81
|
+
interpret_object stmt_data, object, child_path, child_context
|
82
|
+
else
|
83
|
+
# we got strange data from the parser?!
|
84
|
+
raise "Unsupported statement '#{stmt.keys.first}'"
|
85
|
+
end
|
86
|
+
}
|
87
|
+
|
88
|
+
if close_object
|
89
|
+
# finalize the object once all its content has been processed
|
90
|
+
|
91
|
+
rules = object.rules
|
92
|
+
rules.required_properties.each {|name|
|
93
|
+
unless object.get name
|
94
|
+
raise SlideField::InterpreterError,
|
95
|
+
"Missing property '#{name}' for object '#{object.type}' at #{object.loc}"
|
96
|
+
end
|
97
|
+
}
|
98
|
+
|
99
|
+
rules.optional_properties.each {|name|
|
100
|
+
next unless object.get(name).nil?
|
101
|
+
|
102
|
+
default = rules.default_value name
|
103
|
+
type = rules.type_of_property name
|
104
|
+
|
105
|
+
object.set name, default, 'default', type
|
106
|
+
}
|
107
|
+
|
108
|
+
rules.accepted_children.each {|type|
|
109
|
+
min, max = rules.requirements_of_child type
|
110
|
+
count = object[type].count
|
111
|
+
|
112
|
+
if count < min
|
113
|
+
raise SlideField::InterpreterError,
|
114
|
+
"Object '#{object.type}' must have at least #{min} '#{type}', #{count} found at #{object.loc}"
|
115
|
+
end
|
116
|
+
|
117
|
+
if max > 0 && count > max
|
118
|
+
raise SlideField::InterpreterError,
|
119
|
+
"Object '#{object.type}' can not have more than #{max} '#{type}', #{count} found at #{object.loc}"
|
120
|
+
end
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
object
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
def interpret_assignment(stmt_data, object)
|
129
|
+
var_name_t = stmt_data[:variable]
|
130
|
+
var_name = var_name_t.to_sym
|
131
|
+
|
132
|
+
operator_t = stmt_data[:operator]
|
133
|
+
operator = operator_t.to_s
|
134
|
+
|
135
|
+
var_type, var_value_t, var_value = extract_value stmt_data[:value], object
|
136
|
+
|
137
|
+
case operator
|
138
|
+
when '='
|
139
|
+
if object.has? var_name
|
140
|
+
raise SlideField::InterpreterError,
|
141
|
+
"Variable '#{var_name}' is already defined at #{get_loc var_name_t}"
|
142
|
+
end
|
143
|
+
|
144
|
+
if valid_type = object.rules.type_of_property(var_name)
|
145
|
+
if var_type != valid_type
|
146
|
+
raise SlideField::InterpreterError,
|
147
|
+
"Unexpected '#{var_type}', expecting '#{valid_type}' for property '#{var_name}' at #{get_loc var_value_t}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
object.set var_name, var_value, get_loc(var_value_t), var_type
|
152
|
+
when '+=', '-=', '*=', '/='
|
153
|
+
origin_val = object.get var_name
|
154
|
+
unless origin_val
|
155
|
+
raise SlideField::InterpreterError,
|
156
|
+
"Undefined variable '#{var_name}' at #{get_loc var_name_t}"
|
157
|
+
end
|
158
|
+
origin_type = object.var_type var_name
|
159
|
+
|
160
|
+
method = operator[0]
|
161
|
+
|
162
|
+
if var_type != origin_type
|
163
|
+
raise SlideField::InterpreterError,
|
164
|
+
"Unexpected '#{var_type}', expecting '#{origin_type}' for variable or property '#{var_name}' at #{get_loc var_value_t}"
|
165
|
+
end
|
166
|
+
|
167
|
+
value = nil
|
168
|
+
|
169
|
+
case origin_type
|
170
|
+
when :integer
|
171
|
+
value = origin_val.send method, var_value
|
172
|
+
when :point, :color
|
173
|
+
if origin_type != :color || ['+=', '-='].include?(operator)
|
174
|
+
value = origin_val.collect.with_index {|v, i| v.send method, var_value[i] }
|
175
|
+
|
176
|
+
if origin_type == :color
|
177
|
+
# normalize
|
178
|
+
value.collect! {|v|
|
179
|
+
v = 0 if v < 0
|
180
|
+
v = 255 if v > 255
|
181
|
+
v
|
182
|
+
}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
when :string
|
186
|
+
case operator
|
187
|
+
when '+='
|
188
|
+
value = origin_val + var_value
|
189
|
+
when '-='
|
190
|
+
copy = origin_val.dup
|
191
|
+
copy[var_value] = '' while copy.include? var_value
|
192
|
+
value = copy
|
193
|
+
when '*='
|
194
|
+
multiplier = var_value.to_i
|
195
|
+
if multiplier < 1
|
196
|
+
raise SlideField::InterpreterError,
|
197
|
+
"Invalid string multiplier '#{var_value}', integer > 0 required at #{get_loc var_value_t}"
|
198
|
+
end
|
199
|
+
value = origin_val * multiplier
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
unless value
|
204
|
+
raise SlideField::InterpreterError,
|
205
|
+
"Invalid operator '#{operator}' for type '#{origin_type}' at #{get_loc operator_t}"
|
206
|
+
end
|
207
|
+
|
208
|
+
object.set var_name, value, get_loc(var_value_t)
|
209
|
+
else
|
210
|
+
# the parser gave us strange data?!
|
211
|
+
raise "Unsupported operator '#{operator}' at #{get_loc operator_t}"
|
212
|
+
end
|
213
|
+
rescue ZeroDivisionError
|
214
|
+
raise SlideField::InterpreterError,
|
215
|
+
"divided by zero at #{get_loc var_value_t}"
|
216
|
+
end
|
217
|
+
|
218
|
+
def interpret_object(stmt_data, object, include_path, context)
|
219
|
+
type_t = stmt_data[:type]
|
220
|
+
type = type_t.to_sym
|
221
|
+
value_data = stmt_data[:value]
|
222
|
+
tpl_value_data = nil
|
223
|
+
body = stmt_data[:body] || []
|
224
|
+
|
225
|
+
if stmt_data[:template]
|
226
|
+
template = object.get type
|
227
|
+
unless template
|
228
|
+
raise SlideField::InterpreterError,
|
229
|
+
"Undefined variable '#{type}' at #{get_loc type_t}"
|
230
|
+
end
|
231
|
+
|
232
|
+
unless :object == tpl_type = object.var_type(type)
|
233
|
+
raise SlideField::InterpreterError,
|
234
|
+
"Unexpected '#{tpl_type}', expecting 'object' at #{get_loc type_t}"
|
235
|
+
end
|
236
|
+
|
237
|
+
type = template[:type].to_sym
|
238
|
+
|
239
|
+
if template[:value]
|
240
|
+
tpl_value_data = rebind_tokens template[:value], stmt_data[:template]
|
241
|
+
end
|
242
|
+
|
243
|
+
if template[:body]
|
244
|
+
tpl_body = rebind_tokens template[:body], stmt_data[:template]
|
245
|
+
body += tpl_body
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
unless object.rules.accepted_children.include?(type)
|
250
|
+
raise SlideField::InterpreterError,
|
251
|
+
"Unexpected object '#{type}', expecting one of #{object.rules.accepted_children.sort} at #{get_loc type_t}"
|
252
|
+
end
|
253
|
+
|
254
|
+
child = SlideField::ObjectData.new type, get_loc(type_t)
|
255
|
+
child.include_path = include_path
|
256
|
+
child.context = context
|
257
|
+
child.parent = object # enable variable inheritance
|
258
|
+
|
259
|
+
unless child.rules
|
260
|
+
# the object was allowed but we don't know anything about it?!
|
261
|
+
raise "Unsupported object '#{child.type}'"
|
262
|
+
end
|
263
|
+
|
264
|
+
interpret_anon_value tpl_value_data, child if tpl_value_data
|
265
|
+
interpret_anon_value value_data, child if value_data
|
266
|
+
interpret_tree body, child || [], include_path, context
|
267
|
+
|
268
|
+
# process special objects
|
269
|
+
case child.type
|
270
|
+
when :include
|
271
|
+
source = File.expand_path child.get(:source), include_path
|
272
|
+
run_file source, object
|
273
|
+
when :debug
|
274
|
+
debug_infos = {
|
275
|
+
:type=>child.var_type(:thing),
|
276
|
+
:value=>child.get(:thing)
|
277
|
+
}
|
278
|
+
|
279
|
+
puts "DEBUG in #{child.context} at #{child.loc}:"
|
280
|
+
ap debug_infos
|
281
|
+
puts
|
282
|
+
else
|
283
|
+
object << child
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def interpret_anon_value(value_data, object)
|
288
|
+
val_type, value_t, value = extract_value value_data, object
|
289
|
+
var_name = object.rules.matching_properties(val_type).first # guess variable name
|
290
|
+
|
291
|
+
unless var_name
|
292
|
+
raise SlideField::InterpreterError,
|
293
|
+
"Unexpected '#{val_type}', expecting one of #{object.rules.properties_types} at #{get_loc value_t}"
|
294
|
+
end
|
295
|
+
|
296
|
+
if object.has? var_name
|
297
|
+
raise SlideField::InterpreterError,
|
298
|
+
"Variable '#{var_name}' is already defined at #{get_loc value_t}"
|
299
|
+
end
|
300
|
+
|
301
|
+
object.set var_name, value, get_loc(value_t), val_type
|
302
|
+
end
|
303
|
+
|
304
|
+
def get_loc(token)
|
305
|
+
pos = token.line_and_column
|
306
|
+
"line #{pos.first} char #{pos.last}"
|
307
|
+
end
|
308
|
+
|
309
|
+
def extract_value(data, object)
|
310
|
+
filters = data.delete :filters
|
311
|
+
value_data = data.first
|
312
|
+
type = value_data[0]
|
313
|
+
token = value_data[1]
|
314
|
+
value = convert type, token
|
315
|
+
|
316
|
+
if type == :identifier
|
317
|
+
if id_value = object.get(value.to_sym)
|
318
|
+
type = object.var_type value.to_sym
|
319
|
+
value = id_value
|
320
|
+
else
|
321
|
+
raise SlideField::InterpreterError,
|
322
|
+
"Undefined variable '#{value}' at #{get_loc token}"
|
323
|
+
end
|
324
|
+
elsif type == :object
|
325
|
+
token = token[:type]
|
326
|
+
|
327
|
+
if value[:template]
|
328
|
+
# we got something like `alias = \&template`
|
329
|
+
# a template must be copied using the standard syntax `alias = template`
|
330
|
+
# otherwise the reference would not be resolved
|
331
|
+
raise SlideField::InterpreterError,
|
332
|
+
"Unexpected template reference at #{get_loc token}"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
filters.reverse_each {|filter_token|
|
337
|
+
type, value = filter filter_token, type, value
|
338
|
+
}
|
339
|
+
|
340
|
+
return type, token, value
|
341
|
+
end
|
342
|
+
|
343
|
+
def convert(type, token)
|
344
|
+
case type
|
345
|
+
when :identifier, :object
|
346
|
+
token
|
347
|
+
when :integer
|
348
|
+
token.to_i
|
349
|
+
when :point
|
350
|
+
token.to_s.split('x').collect &:to_i
|
351
|
+
when :string
|
352
|
+
escape_sequences = {
|
353
|
+
'n'=>"\n"
|
354
|
+
}
|
355
|
+
|
356
|
+
token.to_s[1..-2].gsub(/\\(.)/) {
|
357
|
+
escape_sequences[$1] || $1
|
358
|
+
}
|
359
|
+
when :color
|
360
|
+
int = token.to_s[1..-1].hex
|
361
|
+
|
362
|
+
r = (int >> 24) & 255
|
363
|
+
g = (int >> 16) & 255
|
364
|
+
b = (int >> 8) & 255
|
365
|
+
a = (int) & 255
|
366
|
+
[r, g, b, a]
|
367
|
+
when :boolean
|
368
|
+
token == ':true'
|
369
|
+
else
|
370
|
+
# the parser gave us strange data?!
|
371
|
+
raise "Unsupported type '#{type}' at #{get_loc token}"
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def filter(token, type, value)
|
376
|
+
name_t = token[:name]
|
377
|
+
name = name_t.to_sym
|
378
|
+
|
379
|
+
case [type, name]
|
380
|
+
when [:point, :x]
|
381
|
+
type = :integer
|
382
|
+
value = value[0]
|
383
|
+
when [:point, :y]
|
384
|
+
type = :integer
|
385
|
+
value = value[1]
|
386
|
+
when [:integer, :x]
|
387
|
+
type = :point
|
388
|
+
value = [value, 0]
|
389
|
+
when [:integer, :y]
|
390
|
+
type = :point
|
391
|
+
value = [0, value]
|
392
|
+
when [:string, :lines]
|
393
|
+
type = :integer
|
394
|
+
value = value.lines.count
|
395
|
+
else
|
396
|
+
raise SlideField::InterpreterError,
|
397
|
+
"Invalid filter '#{name}' for type '#{type}' at #{get_loc name_t}"
|
398
|
+
end
|
399
|
+
|
400
|
+
return type, value
|
401
|
+
end
|
402
|
+
|
403
|
+
def rebind_tokens(tree, dest)
|
404
|
+
case tree
|
405
|
+
when Array
|
406
|
+
tree.collect {|h| rebind_tokens h, dest }
|
407
|
+
when Hash
|
408
|
+
tree = tree.dup
|
409
|
+
tree.each {|k, v| tree[k] = rebind_tokens v, dest }
|
410
|
+
when Parslet::Slice
|
411
|
+
Parslet::Slice.new dest.position, tree.str, dest.line_cache
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class SlideField::ObjectData
|
2
|
+
attr_reader :type, :loc, :children
|
3
|
+
attr_accessor :context, :include_path, :parent
|
4
|
+
|
5
|
+
def initialize(type, loc)
|
6
|
+
@type = type
|
7
|
+
@loc = loc
|
8
|
+
@variables = {}
|
9
|
+
@children = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def has?(var)
|
13
|
+
@variables.has_key? var
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(var, val, loc = nil, type = nil)
|
17
|
+
loc ||= var_loc var
|
18
|
+
type ||= var_type var
|
19
|
+
|
20
|
+
@variables[var] = [type, val, loc]
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(var)
|
24
|
+
if has? var
|
25
|
+
@variables[var][1]
|
26
|
+
elsif parent
|
27
|
+
parent.get var
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def var_type(var)
|
32
|
+
if has? var
|
33
|
+
@variables[var][0]
|
34
|
+
elsif parent
|
35
|
+
parent.var_type var
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def var_loc(var)
|
40
|
+
if has? var
|
41
|
+
@variables[var][2]
|
42
|
+
elsif parent
|
43
|
+
parent.var_loc var
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def <<(child)
|
48
|
+
child.parent = self
|
49
|
+
@children << child
|
50
|
+
end
|
51
|
+
|
52
|
+
def [](selector)
|
53
|
+
@children.select {|o| o.type == selector }
|
54
|
+
end
|
55
|
+
|
56
|
+
def ancestor(selector)
|
57
|
+
p = @parent
|
58
|
+
while p
|
59
|
+
return p if p.type == selector
|
60
|
+
p = p.parent
|
61
|
+
end
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def context_string
|
66
|
+
array = [@context]
|
67
|
+
parent = @parent
|
68
|
+
while parent
|
69
|
+
array.unshift parent.context unless array.first == parent.context
|
70
|
+
parent = parent.parent
|
71
|
+
end
|
72
|
+
"[#{array.join '] ['}]"
|
73
|
+
end
|
74
|
+
|
75
|
+
def rules
|
76
|
+
SlideField::ObjectRules[@type]
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SlideField::ObjectManager
|
2
|
+
def self.new(obj, window)
|
3
|
+
type = obj.type.to_s
|
4
|
+
type[0] = type[0].upcase
|
5
|
+
const_get(type).new obj, window
|
6
|
+
rescue NameError
|
7
|
+
end
|
8
|
+
|
9
|
+
class Base
|
10
|
+
def initialize(obj, window)
|
11
|
+
@obj = obj
|
12
|
+
@window = window
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(event, *args)
|
16
|
+
send event, *args
|
17
|
+
rescue => e
|
18
|
+
raise SlideField::RuntimeError,
|
19
|
+
"#{@obj.context_string} An error occured while executing the '#{event}' event on the object '#{@obj.type}' at #{@obj.loc}:\n" +
|
20
|
+
"\t(#{e.class}) #{e.message}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def load; end
|
24
|
+
def activate; end
|
25
|
+
def draw(animator); end
|
26
|
+
def deactivate; end
|
27
|
+
def unload; end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module SlideField::ObjectRules
|
2
|
+
def self.[](type)
|
3
|
+
type = type.to_s
|
4
|
+
type[0] = type[0].upcase
|
5
|
+
|
6
|
+
SlideField::ObjectRules.const_get(type).get
|
7
|
+
rescue NameError
|
8
|
+
end
|
9
|
+
|
10
|
+
class Base
|
11
|
+
@@cache = {}
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@properties = []
|
15
|
+
@children = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def properties_names
|
19
|
+
@properties.collect {|hash| hash[:name] }
|
20
|
+
end
|
21
|
+
|
22
|
+
def properties_types
|
23
|
+
@properties.collect {|hash| hash[:type] }.uniq
|
24
|
+
end
|
25
|
+
|
26
|
+
def required_properties
|
27
|
+
required = @properties.select {|hash| hash[:default].nil? }
|
28
|
+
required.collect {|hash| hash[:name] }
|
29
|
+
end
|
30
|
+
|
31
|
+
def optional_properties
|
32
|
+
required = @properties.select {|hash| !hash[:default].nil? }
|
33
|
+
required.collect {|hash| hash[:name] }
|
34
|
+
end
|
35
|
+
|
36
|
+
def type_of_property(name)
|
37
|
+
rule = @properties.select {|hash| hash[:name] == name }.first
|
38
|
+
rule[:type] if rule
|
39
|
+
end
|
40
|
+
|
41
|
+
def matching_properties(type)
|
42
|
+
matches = @properties.select {|hash| hash[:type] == type }
|
43
|
+
matches.collect {|hash| hash[:name] }
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_value(name)
|
47
|
+
rule = @properties.select {|hash| hash[:name] == name }.first
|
48
|
+
rule[:default] if rule
|
49
|
+
end
|
50
|
+
|
51
|
+
def accepted_children
|
52
|
+
@children.collect {|hash| hash[:type] }
|
53
|
+
end
|
54
|
+
|
55
|
+
def requirements_of_child(type)
|
56
|
+
rule = @children.select {|hash| hash[:type] == type }.first
|
57
|
+
rule[:requirements] if rule
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.get
|
61
|
+
if instance = @@cache[self]
|
62
|
+
instance
|
63
|
+
else
|
64
|
+
instance = self.new
|
65
|
+
instance.rules
|
66
|
+
@@cache[self] = instance
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
def property(name, type, default = nil)
|
72
|
+
@properties << {:name=>name, :type=>type, :default=>default}
|
73
|
+
end
|
74
|
+
|
75
|
+
def child(type, min = 0, max = 0)
|
76
|
+
@children << {:type=>type, :requirements=>[min, max]}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SlideField::ObjectRules
|
2
|
+
class Base
|
3
|
+
def rules
|
4
|
+
child :include
|
5
|
+
child :debug
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class SBase < Base
|
10
|
+
def rules
|
11
|
+
child :animation
|
12
|
+
child :image
|
13
|
+
child :rect
|
14
|
+
child :song
|
15
|
+
child :text
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class GBase < SBase
|
22
|
+
def rules
|
23
|
+
property :position, :point, [0,0]
|
24
|
+
property :z_order, :integer, 0
|
25
|
+
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SlideField::ObjectRules
|
2
|
+
class Debug < Base
|
3
|
+
def rules
|
4
|
+
# hack
|
5
|
+
# `thing = 10x10` or anything but an integer will raise an error
|
6
|
+
# however `\debug 10x10` works as expected
|
7
|
+
|
8
|
+
property :thing, :integer
|
9
|
+
property :thing, :string
|
10
|
+
property :thing, :point
|
11
|
+
property :thing, :color
|
12
|
+
property :thing, :boolean
|
13
|
+
property :thing, :object
|
14
|
+
|
15
|
+
# don't call super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|