tickly 1.0.0 → 2.0.0

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.
data/Gemfile CHANGED
@@ -5,9 +5,9 @@ gem "bychar", "~> 1.2"
5
5
  # Add dependencies to develop your gem here.
6
6
  # Include everything needed to run rake, tests, features, etc.
7
7
  group :development do
8
+ gem "rake"
8
9
  gem "shoulda", ">= 0"
9
10
  gem "rdoc", "~> 3.12"
10
- gem "bundler"
11
11
  gem "jeweler", "~> 1.8.3"
12
12
  gem "ruby-prof"
13
13
  end
data/Rakefile CHANGED
@@ -46,3 +46,21 @@ Rake::RDocTask.new do |rdoc|
46
46
  rdoc.rdoc_files.include('README*')
47
47
  rdoc.rdoc_files.include('lib/**/*.rb')
48
48
  end
49
+
50
+ desc "Profiles the parser"
51
+ task :profile do
52
+ require 'ruby-prof'
53
+ p = Tickly::NodeProcessor.new
54
+ f = File.open(File.dirname(__FILE__) + "/test/test-data/huge_nuke_tcl.tcl")
55
+
56
+ RubyProf.start
57
+ p.parse(f) {|_| }
58
+ result = RubyProf.stop
59
+
60
+ # Print a call graph
61
+ File.open("profiler_calls.html", "w") do | f |
62
+ RubyProf::GraphHtmlPrinter.new(result).print(f)
63
+ end
64
+ `open profiler_calls.html`
65
+ end
66
+
@@ -49,7 +49,7 @@ module Tickly
49
49
  # at the end of the method. This method evaluates one expression at a time
50
50
  # (it's more of a pattern matcher really)
51
51
  def evaluate(expr)
52
- if multiple_atoms?(expr) && has_subcommand?(expr) && has_handler?(expr)
52
+ if will_capture?(expr)
53
53
  handler_class = @node_handlers.find{|e| unconst_name(e) == expr[0]}
54
54
  handler_arguments = expr[1]
55
55
  hash_of_args = {}
@@ -68,6 +68,12 @@ module Tickly
68
68
  end
69
69
  end
70
70
 
71
+ # Tells whether this Evaluator will actually instantiate
72
+ # anything from the passed expression
73
+ def will_capture?(expr)
74
+ multiple_atoms?(expr) && has_subcommand?(expr) && has_handler?(expr)
75
+ end
76
+
71
77
  private
72
78
 
73
79
  def multiple_atoms?(expr)
@@ -0,0 +1,81 @@
1
+ module Tickly
2
+ # A combination of a Parser and an Evaluator
3
+ # Evaluates a passed Nuke script without expanding it's inner arguments.
4
+ # The TCL should look like Nuke's node commands:
5
+ #
6
+ # NodeClass {
7
+ # foo bar
8
+ # baz bad
9
+ # }
10
+ #
11
+ # You have to add the Classes that you want to instantiate for nodes using add_node_handler_class
12
+ # and every time the parser encounters that node the node will be instantiated
13
+ # and the node options (actually TCL commands) will be passed to the constructor,
14
+ # as a Ruby Hash with string keys.
15
+ # Every value of the knobs hash will be the AST as returned by the Parser.
16
+ #
17
+ # class Blur
18
+ # def initialize(knobs_hash)
19
+ # puts knobs_hash.inspect
20
+ # end
21
+ # end
22
+ #
23
+ # e = Tickly::NodeProcessor.new
24
+ # e.add_node_handler_class Blur
25
+ # e.parse(File.open("/path/to/script.nk")) do | blur_node |
26
+ # # do whatever you want to the node instance
27
+ # end
28
+ # end
29
+ class NodeProcessor
30
+ def initialize
31
+ @evaluator = Tickly::Evaluator.new
32
+ @parser = Ratchet.new
33
+ @parser.expr_callback = method(:filter_expression)
34
+ end
35
+
36
+ # Add a Class object that can instantiate node handlers. The last part of the class name
37
+ # has to match the name of the Nuke node that you want to capture.
38
+ # For example, to capture Tracker3 nodes a name like this will do:
39
+ # Whatever::YourModule::Better::Tracker3
40
+ def add_node_handler_class(class_object)
41
+ @evaluator.add_node_handler_class(class_object)
42
+ end
43
+
44
+ # Parses from the passed IO or string and yields every node
45
+ # that has been instantiated
46
+ def parse(io_or_str, &nuke_node_callback)
47
+ raise LocalJumpError, "NodeProcesssor#parse totally requires a block" unless block_given?
48
+ @node_handler = nuke_node_callback
49
+ @parser.parse(io_or_str)
50
+ end
51
+
52
+ private
53
+
54
+ class Ratchet < Parser #:nodoc: :all
55
+ attr_accessor :expr_callback
56
+ def compact_subexpr(expr, at_depth)
57
+ expr_callback.call(expr, at_depth)
58
+ end
59
+ end
60
+
61
+ def filter_expression(expression, at_depth)
62
+ # Leave all expressions which are deeper than 1
63
+ # intact
64
+ return expression if at_depth > 1
65
+
66
+ # Skip all nodes which are not interesting for
67
+ # the evaluator to do
68
+ unless @evaluator.will_capture?(expression)
69
+ return nil # Do not even keep it in memory
70
+ end
71
+
72
+ # And immediately evaluate
73
+ # TODO: also yield it!
74
+ node_instance = @evaluator.evaluate(expression)
75
+ @node_handler.call(node_instance)
76
+
77
+ # Still return nil
78
+ return nil
79
+ end
80
+ end
81
+ end
data/lib/tickly/parser.rb CHANGED
@@ -25,9 +25,11 @@ module Tickly
25
25
  sub_parse(reader, stop_char = nil, stack_depth = 0, multiple_expressions = true)
26
26
  end
27
27
 
28
- # Override this to remove any unneeded subexpressions Modify the passed expr
29
- # array in-place.
30
- def expand_subexpr!(expr, at_depth)
28
+ # Override this to remove any unneeded subexpressions.
29
+ # Return the modified expression. If you return nil, the result
30
+ # will not be added to the expression list
31
+ def compact_subexpr(expr, at_depth)
32
+ expr
31
33
  end
32
34
 
33
35
  private
@@ -44,7 +46,6 @@ module Tickly
44
46
  return stack unless multiple_expressions
45
47
 
46
48
  expressions << stack if stack.any?
47
- expressions.each { |expr| expand_subexpr!(expr, stack_depth + 1) }
48
49
 
49
50
  return expressions
50
51
  end
@@ -73,8 +74,13 @@ module Tickly
73
74
  end
74
75
  if TERMINATORS.include?(char) && stack.any? && !last_char_was_linebreak # Introduce a stack separator! This is a new line
75
76
  stack << buf if buf.length > 0
76
- expressions << stack
77
+ # Immediately run this expression through the filter
78
+ filtered_expr = compact_subexpr(stack, stack_depth + 1)
77
79
  stack = []
80
+
81
+ # Only preserve the parsed expression if it's not nil
82
+ expressions << filtered_expr unless filtered_expr.nil?
83
+
78
84
  last_char_was_linebreak = true
79
85
  multiple_expressions = true
80
86
  #puts "Next expression! #{expressions.inspect} #{stack.inspect} #{buf.inspect}"
data/lib/tickly.rb CHANGED
@@ -1,11 +1,10 @@
1
1
  require File.dirname(__FILE__) + "/tickly/parser"
2
- require File.dirname(__FILE__) + "/tickly/node_extractor"
3
2
  require File.dirname(__FILE__) + "/tickly/evaluator"
4
3
  require File.dirname(__FILE__) + "/tickly/curve"
5
- require 'forwardable'
4
+ require File.dirname(__FILE__) + "/tickly/node_processor"
6
5
 
7
6
  module Tickly
8
- VERSION = '1.0.0'
7
+ VERSION = '2.0.0'
9
8
 
10
9
  # Provides the methods for quickly emitting the expression arrays,
11
10
  # is used in tests
@@ -15,7 +15,7 @@ class TestEvaluator < Test::Unit::TestCase
15
15
  end
16
16
  end
17
17
 
18
- should "not send anything to the handler when the expr does not conform to the standard" do
18
+ def test_does_not_send_anything_when_the_expression_passed_does_not_match_pattern
19
19
  stack = e("ShouldNotBeInstantiated")
20
20
  e = Tickly::Evaluator.new
21
21
  e.add_node_handler_class(ShouldNotBeInstantiated)
@@ -56,5 +56,15 @@ class TestEvaluator < Test::Unit::TestCase
56
56
  end
57
57
  end
58
58
  end
59
+
60
+ def test_will_capture
61
+ e = Tickly::Evaluator.new
62
+ e.add_node_handler_class(SomeNode)
63
+
64
+ valid = e("SomeNode", le(e("foo", "bar"), e("baz", "bad")))
65
+ assert e.will_capture?(valid)
66
+ assert !e.will_capture?([])
67
+ assert !e.will_capture?(e("SomeNode"))
68
+ end
59
69
 
60
70
  end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ class TestParserEvaluator < Test::Unit::TestCase
4
+ include Tickly::Emitter
5
+
6
+ NUKE7_SCRIPT = File.open(File.dirname(__FILE__) + "/test-data/nuke7_tracker_2tracks.nk")
7
+
8
+ class Tracker4
9
+ attr_reader :knobs
10
+ def initialize(knobs)
11
+ @knobs = knobs
12
+ end
13
+ end
14
+
15
+ class NodeCaptured < RuntimeError; end
16
+
17
+ def test_processes_nodes
18
+ pe = Tickly::NodeProcessor.new
19
+ pe.add_node_handler_class(Tracker4)
20
+
21
+ assert_raise(NodeCaptured) do
22
+ pe.parse(NUKE7_SCRIPT) do | node |
23
+
24
+ assert_kind_of Tracker4, node
25
+ assert_equal "Tracker1", node.knobs["name"]
26
+
27
+ raise NodeCaptured
28
+ end
29
+ end
30
+ end
31
+
32
+ def test_raises_without_a_block
33
+ pe = Tickly::NodeProcessor.new
34
+ assert_raise(LocalJumpError) { pe.parse(NUKE7_SCRIPT) }
35
+ end
36
+ end
data/test/test_parser.rb CHANGED
@@ -91,6 +91,31 @@ class TestParser < Test::Unit::TestCase
91
91
  assert_equal blur, p[4]
92
92
  end
93
93
 
94
+ class Discarder < Tickly::Parser
95
+ def compact_subexpr(expr, depth)
96
+ return :discarded
97
+ end
98
+ end
99
+
100
+ class Eater < Tickly::Parser
101
+ def compact_subexpr(e, d)
102
+ nil
103
+ end
104
+ end
105
+
106
+ def test_passes_expressions_via_compact_subexpr
107
+ f = File.open(File.dirname(__FILE__) + "/test-data/three_nodes_and_roto.txt")
108
+ p = Discarder.new.parse(f)
109
+ assert_equal [:discarded, :discarded, :discarded, :discarded, :discarded], p
110
+ end
111
+
112
+ def test_removes_all_the_expressions_compacted_into_nil
113
+ f = File.open(File.dirname(__FILE__) + "/test-data/three_nodes_and_roto.txt")
114
+ p = Eater.new.parse(f)
115
+ assert_equal [], p
116
+ end
117
+
118
+
94
119
  def test_parsing_nuke_script_with_indentations
95
120
  f = File.open(File.dirname(__FILE__) + "/test-data/nuke_group.txt")
96
121
  p = P.parse(f)
data/tickly.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "tickly"
8
- s.version = "1.0.0"
8
+ s.version = "2.0.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Julik Tarkhanov"]
12
- s.date = "2013-03-17"
12
+ s.date = "2013-03-21"
13
13
  s.description = "Parses the subset of the TCL grammar needed for Nuke scripts"
14
14
  s.email = "me@julik.nl"
15
15
  s.extra_rdoc_files = [
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
26
26
  "lib/tickly.rb",
27
27
  "lib/tickly/curve.rb",
28
28
  "lib/tickly/evaluator.rb",
29
- "lib/tickly/node_extractor.rb",
29
+ "lib/tickly/node_processor.rb",
30
30
  "lib/tickly/parser.rb",
31
31
  "test/helper.rb",
32
32
  "test/test-data/huge_nuke_tcl.tcl",
@@ -41,9 +41,8 @@ Gem::Specification.new do |s|
41
41
  "test/test-data/tracker_with_repeating_gaps.nk",
42
42
  "test/test_curve.rb",
43
43
  "test/test_evaluator.rb",
44
- "test/test_node_extractor.rb",
44
+ "test/test_node_processor.rb",
45
45
  "test/test_parser.rb",
46
- "test/test_profile.rb",
47
46
  "tickly.gemspec"
48
47
  ]
49
48
  s.homepage = "http://github.com/julik/tickly"
@@ -57,24 +56,24 @@ Gem::Specification.new do |s|
57
56
 
58
57
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
59
58
  s.add_runtime_dependency(%q<bychar>, ["~> 1.2"])
59
+ s.add_development_dependency(%q<rake>, [">= 0"])
60
60
  s.add_development_dependency(%q<shoulda>, [">= 0"])
61
61
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
62
- s.add_development_dependency(%q<bundler>, [">= 0"])
63
62
  s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
64
63
  s.add_development_dependency(%q<ruby-prof>, [">= 0"])
65
64
  else
66
65
  s.add_dependency(%q<bychar>, ["~> 1.2"])
66
+ s.add_dependency(%q<rake>, [">= 0"])
67
67
  s.add_dependency(%q<shoulda>, [">= 0"])
68
68
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
69
- s.add_dependency(%q<bundler>, [">= 0"])
70
69
  s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
71
70
  s.add_dependency(%q<ruby-prof>, [">= 0"])
72
71
  end
73
72
  else
74
73
  s.add_dependency(%q<bychar>, ["~> 1.2"])
74
+ s.add_dependency(%q<rake>, [">= 0"])
75
75
  s.add_dependency(%q<shoulda>, [">= 0"])
76
76
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
77
- s.add_dependency(%q<bundler>, [">= 0"])
78
77
  s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
79
78
  s.add_dependency(%q<ruby-prof>, [">= 0"])
80
79
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tickly
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-17 00:00:00.000000000 Z
12
+ date: 2013-03-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bychar
@@ -28,7 +28,7 @@ dependencies:
28
28
  - !ruby/object:Gem::Version
29
29
  version: '1.2'
30
30
  - !ruby/object:Gem::Dependency
31
- name: shoulda
31
+ name: rake
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
@@ -44,37 +44,37 @@ dependencies:
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
46
  - !ruby/object:Gem::Dependency
47
- name: rdoc
47
+ name: shoulda
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  none: false
50
50
  requirements:
51
- - - ~>
51
+ - - ! '>='
52
52
  - !ruby/object:Gem::Version
53
- version: '3.12'
53
+ version: '0'
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  none: false
58
58
  requirements:
59
- - - ~>
59
+ - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
- version: '3.12'
61
+ version: '0'
62
62
  - !ruby/object:Gem::Dependency
63
- name: bundler
63
+ name: rdoc
64
64
  requirement: !ruby/object:Gem::Requirement
65
65
  none: false
66
66
  requirements:
67
- - - ! '>='
67
+ - - ~>
68
68
  - !ruby/object:Gem::Version
69
- version: '0'
69
+ version: '3.12'
70
70
  type: :development
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
- - - ! '>='
75
+ - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: '0'
77
+ version: '3.12'
78
78
  - !ruby/object:Gem::Dependency
79
79
  name: jeweler
80
80
  requirement: !ruby/object:Gem::Requirement
@@ -124,7 +124,7 @@ files:
124
124
  - lib/tickly.rb
125
125
  - lib/tickly/curve.rb
126
126
  - lib/tickly/evaluator.rb
127
- - lib/tickly/node_extractor.rb
127
+ - lib/tickly/node_processor.rb
128
128
  - lib/tickly/parser.rb
129
129
  - test/helper.rb
130
130
  - test/test-data/huge_nuke_tcl.tcl
@@ -139,9 +139,8 @@ files:
139
139
  - test/test-data/tracker_with_repeating_gaps.nk
140
140
  - test/test_curve.rb
141
141
  - test/test_evaluator.rb
142
- - test/test_node_extractor.rb
142
+ - test/test_node_processor.rb
143
143
  - test/test_parser.rb
144
- - test/test_profile.rb
145
144
  - tickly.gemspec
146
145
  homepage: http://github.com/julik/tickly
147
146
  licenses:
@@ -158,7 +157,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
158
157
  version: '0'
159
158
  segments:
160
159
  - 0
161
- hash: 748629881843892716
160
+ hash: 4279008670880210764
162
161
  required_rubygems_version: !ruby/object:Gem::Requirement
163
162
  none: false
164
163
  requirements:
@@ -1,22 +0,0 @@
1
- module Tickly
2
- # A more refined version of the Parser class that can scope itself to the passed Nuke node
3
- # classnames. It will toss all of the node data that is not relevant, keeping only nodes that are
4
- # required.
5
- class NodeExtractor < Parser
6
- def initialize(*interesting_node_class_names)
7
- @nodes = interesting_node_class_names
8
- end
9
-
10
- # Override this to remove any unneeded subexpressions
11
- def expand_subexpr!(expr, at_depth)
12
- if is_node_constructor?(expr, at_depth)
13
- node_class_name = expr[0]
14
- expr.replace([:discarded]) unless @nodes.include?(node_class_name)
15
- end
16
- end
17
-
18
- def is_node_constructor?(expr, depth)
19
- depth == 1 && expr[0].is_a?(String) && expr.length == 2 && expr[1][0] == :c
20
- end
21
- end
22
- end
@@ -1,30 +0,0 @@
1
- require 'helper'
2
-
3
- class TestNodeExtractor < Test::Unit::TestCase
4
-
5
- include Tickly::Emitter
6
-
7
- def test_parsing_nuke_script_with_indentations
8
- f = File.open(File.dirname(__FILE__) + "/test-data/nuke_group.txt")
9
- x = Tickly::NodeExtractor.new("Group")
10
-
11
- p = x.parse(f)
12
- grp = e(
13
- e("set", "cut_paste_input", se("stack", "0")),
14
- e("version", "6.3", "v4"),
15
- e("Group",
16
- le(
17
- e("inputs", "0"),
18
- e("name", "Group1"),
19
- e("selected", "true")
20
- )
21
- ),
22
- e(:discarded),
23
- e(:discarded),
24
- e(:discarded),
25
- e("end_group")
26
- )
27
- assert_equal grp, p
28
- end
29
-
30
- end
data/test/test_profile.rb DELETED
@@ -1,27 +0,0 @@
1
- require 'helper'
2
-
3
- if ENV['USER'] == 'julik'
4
-
5
- require 'ruby-prof'
6
-
7
- class TestProfile < Test::Unit::TestCase
8
- P = Tickly::Parser.new
9
-
10
-
11
- def test_huge_tcl
12
- f = File.open(File.dirname(__FILE__) + "/test-data/huge_nuke_tcl.tcl")
13
-
14
- RubyProf.start
15
- P.parse(f)
16
- result = RubyProf.stop
17
-
18
- # Print a call graph
19
- File.open("profiler_calls.html", "w") do | f |
20
- RubyProf::GraphHtmlPrinter.new(result).print(f)
21
- end
22
- `open profiler_calls.html`
23
- end
24
-
25
- end
26
-
27
- end