slidefield 0.1

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +674 -0
  5. data/README.md +254 -0
  6. data/Rakefile +7 -0
  7. data/bin/slidefield +89 -0
  8. data/examples/complete/assets/K2.jpg +0 -0
  9. data/examples/complete/assets/gobi.jpg +0 -0
  10. data/examples/complete/assets/mount_everest.jpg +0 -0
  11. data/examples/complete/assets/sahara.jpg +0 -0
  12. data/examples/complete/main.sfp +7 -0
  13. data/examples/complete/slides/deserts.sfi +19 -0
  14. data/examples/complete/slides/mountains.sfi +25 -0
  15. data/examples/complete/templates.sfi +95 -0
  16. data/examples/complete/variables.sfi +6 -0
  17. data/examples/minimal/main.sfp +43 -0
  18. data/examples/minimal/ruby-logo.png +0 -0
  19. data/lib/slidefield/animator.rb +104 -0
  20. data/lib/slidefield/errors.rb +6 -0
  21. data/lib/slidefield/interpreter.rb +414 -0
  22. data/lib/slidefield/object_data.rb +78 -0
  23. data/lib/slidefield/object_manager.rb +29 -0
  24. data/lib/slidefield/object_rules.rb +79 -0
  25. data/lib/slidefield/objects/_base.rb +29 -0
  26. data/lib/slidefield/objects/_root.rb +10 -0
  27. data/lib/slidefield/objects/animation.rb +10 -0
  28. data/lib/slidefield/objects/debug.rb +18 -0
  29. data/lib/slidefield/objects/image.rb +47 -0
  30. data/lib/slidefield/objects/include.rb +9 -0
  31. data/lib/slidefield/objects/layout.rb +10 -0
  32. data/lib/slidefield/objects/rect.rb +44 -0
  33. data/lib/slidefield/objects/slide.rb +43 -0
  34. data/lib/slidefield/objects/song.rb +31 -0
  35. data/lib/slidefield/objects/text.rb +57 -0
  36. data/lib/slidefield/parser.rb +99 -0
  37. data/lib/slidefield/version.rb +3 -0
  38. data/lib/slidefield/viewer.rb +89 -0
  39. data/lib/slidefield.rb +27 -0
  40. data/slidefield.gemspec +27 -0
  41. data/test/helper.rb +11 -0
  42. data/test/resources/include_sub.sfp +1 -0
  43. data/test/resources/parse_error.sfp +1 -0
  44. data/test/resources/recursive_include.sfp +1 -0
  45. data/test/resources/sub/include_parent.sfp +1 -0
  46. data/test/resources/unclosed_object.sfp +2 -0
  47. data/test/resources/unknown_object.sfp +1 -0
  48. data/test/resources/wrong_template.sfp +4 -0
  49. data/test/test_animator.rb +244 -0
  50. data/test/test_examples.rb +29 -0
  51. data/test/test_interpreter.rb +1766 -0
  52. data/test/test_object_data.rb +108 -0
  53. data/test/test_object_manager.rb +48 -0
  54. data/test/test_object_rules.rb +87 -0
  55. data/test/test_parser.rb +408 -0
  56. 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,10 @@
1
+ module SlideField::ObjectRules
2
+ class ROOT < Base
3
+ def rules
4
+ child :layout, 1, 1
5
+ child :slide, 1
6
+
7
+ super
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module SlideField::ObjectRules
2
+ class Animation < SBase
3
+ def rules
4
+ property :name, :string
5
+ property :duration, :integer, 400
6
+
7
+ super
8
+ end
9
+ end
10
+ 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