slidefield 0.1

Sign up to get free protection for your applications and to get access to all the features.
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