sbyc 0.1.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.
Files changed (59) hide show
  1. data/LICENCE.textile +12 -0
  2. data/README.textile +44 -0
  3. data/lib/sbyc.rb +14 -0
  4. data/lib/sbyc/codetree.rb +82 -0
  5. data/lib/sbyc/codetree/ast_node.rb +101 -0
  6. data/lib/sbyc/codetree/eval.rb +3 -0
  7. data/lib/sbyc/codetree/eval/ast_node_ext.rb +38 -0
  8. data/lib/sbyc/codetree/eval/functional_eval.rb +36 -0
  9. data/lib/sbyc/codetree/eval/object_eval.rb +36 -0
  10. data/lib/sbyc/codetree/matching.rb +3 -0
  11. data/lib/sbyc/codetree/matching/ast_node_ext.rb +14 -0
  12. data/lib/sbyc/codetree/matching/match_data.rb +30 -0
  13. data/lib/sbyc/codetree/matching/matcher.rb +83 -0
  14. data/lib/sbyc/codetree/proc_parser.rb +91 -0
  15. data/lib/sbyc/codetree/producing.rb +1 -0
  16. data/lib/sbyc/codetree/producing/producer.rb +68 -0
  17. data/lib/sbyc/codetree/rewriting.rb +4 -0
  18. data/lib/sbyc/codetree/rewriting/class_methods.rb +15 -0
  19. data/lib/sbyc/codetree/rewriting/compiler.rb +28 -0
  20. data/lib/sbyc/codetree/rewriting/instance_methods.rb +92 -0
  21. data/lib/sbyc/codetree/rewriting/match.rb +59 -0
  22. data/test/spec/documentation/codetree/production.spec +59 -0
  23. data/test/spec/documentation/readme/assumptions.spec +33 -0
  24. data/test/spec/documentation/readme/functional_evaluation.spec +29 -0
  25. data/test/spec/documentation/readme/object_evaluation.spec +17 -0
  26. data/test/spec/documentation/readme/rewriting.spec +60 -0
  27. data/test/spec/documentation/readme/semantics.spec +21 -0
  28. data/test/spec/documentation/readme/synopsis.spec +27 -0
  29. data/test/spec/documentation/readme/syntax.spec +26 -0
  30. data/test/spec/spec_helper.rb +13 -0
  31. data/test/spec/test_all.rb +6 -0
  32. data/test/spec/unit/sbyc/codetree/ast_node/coerce.spec +60 -0
  33. data/test/spec/unit/sbyc/codetree/ast_node/equality.spec +75 -0
  34. data/test/spec/unit/sbyc/codetree/ast_node/inspect.spec +23 -0
  35. data/test/spec/unit/sbyc/codetree/ast_node/literal.spec +15 -0
  36. data/test/spec/unit/sbyc/codetree/ast_node/to_a.spec +27 -0
  37. data/test/spec/unit/sbyc/codetree/ast_node/to_s.spec +23 -0
  38. data/test/spec/unit/sbyc/codetree/ast_node/visit.spec +34 -0
  39. data/test/spec/unit/sbyc/codetree/eval/functional_compile.spec +30 -0
  40. data/test/spec/unit/sbyc/codetree/eval/functional_eval.spec +34 -0
  41. data/test/spec/unit/sbyc/codetree/eval/functional_proc.spec +36 -0
  42. data/test/spec/unit/sbyc/codetree/eval/object_compile.spec +36 -0
  43. data/test/spec/unit/sbyc/codetree/eval/object_eval.spec +38 -0
  44. data/test/spec/unit/sbyc/codetree/eval/object_proc.spec +36 -0
  45. data/test/spec/unit/sbyc/codetree/matching/matcher/args_match.spec +91 -0
  46. data/test/spec/unit/sbyc/codetree/matching/matcher/do_match.spec +39 -0
  47. data/test/spec/unit/sbyc/codetree/matching/matcher/function_match.spec +45 -0
  48. data/test/spec/unit/sbyc/codetree/matching/matcher/match.spec +64 -0
  49. data/test/spec/unit/sbyc/codetree/proc_parser/expr.spec +20 -0
  50. data/test/spec/unit/sbyc/codetree/proc_parser/parse.spec +83 -0
  51. data/test/spec/unit/sbyc/codetree/producing/producer.spec +31 -0
  52. data/test/spec/unit/sbyc/codetree/producing/producer/apply_args_conventions.spec +60 -0
  53. data/test/spec/unit/sbyc/codetree/rewriting/instance_methods/apply_args_conventions.spec +60 -0
  54. data/test/spec/unit/sbyc/codetree/rewriting/instance_methods/node.spec +19 -0
  55. data/test/spec/unit/sbyc/codetree/rewriting/instance_methods/rewrite.spec +76 -0
  56. data/test/spec/unit/sbyc/codetree/rewriting/match/apply.spec +23 -0
  57. data/test/spec/unit/sbyc/codetree/rewriting/match/coerce.spec +48 -0
  58. data/test/spec/unit/sbyc/codetree/rewriting/match/matches.spec +27 -0
  59. metadata +129 -0
@@ -0,0 +1,20 @@
1
+ require File.expand_path('../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::ProcParser::Expr" do
4
+
5
+ let(:the_expr) { CodeTree::ProcParser::Expr.new }
6
+
7
+ describe("should not have access to Kernel methods") do
8
+ specify{
9
+ x = the_expr.instance_eval{ puts "hello" }
10
+ lambda{ x.__to_functional_code }.should_not raise_error
11
+ x.__to_functional_code.should be_kind_of(CodeTree::AstNode)
12
+ }
13
+ end
14
+
15
+ describe "should not have a to_s method" do
16
+ subject { the_expr.to_s.__to_functional_code }
17
+ it { should be_kind_of(CodeTree::AstNode) }
18
+ end
19
+
20
+ end
@@ -0,0 +1,83 @@
1
+ require File.expand_path('../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::ProcParser#parse" do
4
+
5
+ subject { CodeTree::ProcParser::parse(code) }
6
+
7
+ context "when called with an argument" do
8
+ let(:expected) { "(+ (? (_ :a)), (_ 12))" }
9
+
10
+ context("with a simple literal") do
11
+ let(:code) { proc {|t| 12 } }
12
+ specify { subject.should be_kind_of(CodeTree::AstNode) }
13
+ specify { subject.inspect.should == CodeTree::parse{ 12 }.inspect }
14
+ end
15
+
16
+ context("with a simple operator call") do
17
+ let(:code) { proc {|t| t[:a] + 12 } }
18
+ specify { subject.inspect.should == expected }
19
+ end
20
+
21
+ context("with a simple operator call and dot heuristic") do
22
+ let(:code) { proc {|t| t.a + 12 } }
23
+ specify { subject.inspect.should == expected }
24
+ end
25
+
26
+ context("with left literal at left") do
27
+ let(:code) { proc {|t| 12 + t[:a] } }
28
+ specify { subject.inspect.should == expected }
29
+ end
30
+
31
+ context("with left literal at left and dot heuristic") do
32
+ let(:code) { proc {|t| 12 + t.a } }
33
+ specify { subject.inspect.should == expected }
34
+ end
35
+ end
36
+
37
+ context "when called without argument" do
38
+ let(:expected) { "(+ (? (_ :a)), (_ 12))" }
39
+
40
+ context("with a simple literal") do
41
+ let(:code) { proc { 12 } }
42
+ specify { subject.should be_kind_of(CodeTree::AstNode) }
43
+ specify { subject.inspect.should == CodeTree::parse{ 12 }.inspect }
44
+ end
45
+
46
+ context("with a simple operator call") do
47
+ let(:code) { proc { a + 12 } }
48
+ specify { subject.inspect.should == expected }
49
+ end
50
+
51
+ context("with left literal at left") do
52
+ let(:code) { proc { 12 + a } }
53
+ specify { subject.inspect.should == expected }
54
+ end
55
+ end
56
+
57
+ context "when called and used with method calls" do
58
+ let(:expected) { "(plus (? (_ :a)), (_ 12))" }
59
+
60
+ context("with a simple method call") do
61
+ let(:code) { proc {|t| t[:a].plus(12) } }
62
+ specify { subject.inspect.should == expected }
63
+ end
64
+
65
+ context("with a simple method call and dot heuristic") do
66
+ let(:code) { proc {|t| t.a.plus(12) } }
67
+ specify { subject.inspect.should == expected }
68
+ end
69
+ end
70
+
71
+ context "when call on expressions that refer to ruby Kernel methods" do
72
+ let(:expected) { "(puts (to_s (? (_ :x))))"}
73
+ let(:code) { lambda { (puts (to_s x)) } }
74
+ specify{ subject.inspect.should == expected }
75
+ end
76
+
77
+ context "when call on expressions that refer to typical inherited methods/operators" do
78
+ let(:expected) { "(== (hash (? (_ :x))), (_ 12))"}
79
+ let(:code) { lambda { x.hash == 12 } }
80
+ specify{ subject.inspect.should == expected }
81
+ end
82
+
83
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path('../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Producing::Producer" do
4
+
5
+ let(:expr){ CodeTree::parse{ ~(a & b) } }
6
+
7
+ context 'when called with default rules' do
8
+ let(:producer) {
9
+ CodeTree::producer{|p|
10
+ p.rule(:~){|r, n| "not(#{r.apply(n[0])})" }
11
+ p.rule(:&){|r, n| "(#{r.apply(n.children).join(' and ')})" }
12
+ p.rule(:|){|r, n| "(#{r.apply(n.children).join(' or ')})" }
13
+ }
14
+ }
15
+ subject{ producer.apply(expr) }
16
+ it{ should == "not((a and b))" }
17
+ end
18
+
19
+ context 'when called without default rules' do
20
+ let(:producer) {
21
+ CodeTree::producer(false){|p|
22
+ p.rule("*"){|r,n| [n.function] + r.apply(n.children)}
23
+ }
24
+ }
25
+ subject{ producer.apply(expr) }
26
+ specify{ producer.rules.size.should == 1 }
27
+ it{ should == [:~, [:&, [:'?', [:_, :a]], [:'?', [:_, :b]]]] }
28
+ end
29
+
30
+ end
31
+
@@ -0,0 +1,60 @@
1
+ require File.expand_path('../../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Producing::Producer#apply_args_conventions" do
4
+
5
+ let(:producer) { ::CodeTree::producer }
6
+
7
+ context "when called with a literal" do
8
+ let(:node) { 12 }
9
+ subject { producer.send(:apply_args_conventions,node) }
10
+ it { should == node }
11
+ end
12
+
13
+ context "when called with a single ASTNode instance" do
14
+ let(:node) { ::CodeTree::parse{ 12 } }
15
+ subject { producer.send(:apply_args_conventions,node) }
16
+ it { should == node }
17
+ end
18
+
19
+ context "when called with multiple ASTNode instances" do
20
+ let(:child1) { ::CodeTree::parse{ "hello" } }
21
+ let(:child2) { ::CodeTree::parse{ "world" } }
22
+ subject { producer.send(:apply_args_conventions,child1, child2) }
23
+ it { should == [child1, child2] }
24
+ end
25
+
26
+ context "when called with an array of ASTNode instances" do
27
+ let(:child1) { ::CodeTree::parse{ "hello" } }
28
+ let(:child2) { ::CodeTree::parse{ "world" } }
29
+ subject { producer.send(:apply_args_conventions,[child1, child2]) }
30
+ it { should == [child1, child2] }
31
+ end
32
+
33
+ context "when called with a function and a single ASTNode child" do
34
+ let(:function) { :testfunc }
35
+ let(:child) { ::CodeTree::parse{ 12 } }
36
+ let(:expected) { ::CodeTree::parse{ (testfunc 12) } }
37
+ subject{ producer.send(:apply_args_conventions,function, child) }
38
+ it { should == expected }
39
+ end
40
+
41
+ context "when called with a function and two ASTNode children" do
42
+ let(:function) { :say }
43
+ let(:child1) { ::CodeTree::parse{ "hello" } }
44
+ let(:child2) { ::CodeTree::parse{ "world" } }
45
+ let(:expected) { ::CodeTree::parse{ (say "hello", "world") } }
46
+ subject{ producer.send(:apply_args_conventions,function, child1, child2) }
47
+ it { should == expected }
48
+ end
49
+
50
+ context "when called with a function and mix of ASTNode children" do
51
+ let(:function) { :say }
52
+ let(:child1) { ::CodeTree::parse{ "hello" } }
53
+ let(:child2) { ::CodeTree::parse{ "you" } }
54
+ let(:child3) { ::CodeTree::parse{ "world" } }
55
+ let(:expected) { ::CodeTree::parse{ (say "hello", "you", "world") } }
56
+ subject{ producer.send(:apply_args_conventions,function, child1, [child2, child3]) }
57
+ it { should == expected }
58
+ end
59
+
60
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path('../../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Rewriting::Rewriter#apply_args_conventions" do
4
+
5
+ let(:rewriter) { ::CodeTree::rewriter }
6
+
7
+ context "when called with a literal" do
8
+ let(:node) { 12 }
9
+ subject { rewriter.apply_args_conventions(node) }
10
+ it { should == node }
11
+ end
12
+
13
+ context "when called with a single ASTNode instance" do
14
+ let(:node) { ::CodeTree::parse{ 12 } }
15
+ subject { rewriter.apply_args_conventions(node) }
16
+ it { should == node }
17
+ end
18
+
19
+ context "when called with multiple ASTNode instances" do
20
+ let(:child1) { ::CodeTree::parse{ "hello" } }
21
+ let(:child2) { ::CodeTree::parse{ "world" } }
22
+ subject { rewriter.apply_args_conventions(child1, child2) }
23
+ it { should == [child1, child2] }
24
+ end
25
+
26
+ context "when called with an array of ASTNode instances" do
27
+ let(:child1) { ::CodeTree::parse{ "hello" } }
28
+ let(:child2) { ::CodeTree::parse{ "world" } }
29
+ subject { rewriter.apply_args_conventions([child1, child2]) }
30
+ it { should == [child1, child2] }
31
+ end
32
+
33
+ context "when called with a function and a single ASTNode child" do
34
+ let(:function) { :testfunc }
35
+ let(:child) { ::CodeTree::parse{ 12 } }
36
+ let(:expected) { ::CodeTree::parse{ (testfunc 12) } }
37
+ subject{ rewriter.apply_args_conventions(function, child) }
38
+ it { should == expected }
39
+ end
40
+
41
+ context "when called with a function and two ASTNode children" do
42
+ let(:function) { :say }
43
+ let(:child1) { ::CodeTree::parse{ "hello" } }
44
+ let(:child2) { ::CodeTree::parse{ "world" } }
45
+ let(:expected) { ::CodeTree::parse{ (say "hello", "world") } }
46
+ subject{ rewriter.apply_args_conventions(function, child1, child2) }
47
+ it { should == expected }
48
+ end
49
+
50
+ context "when called with a function and mix of ASTNode children" do
51
+ let(:function) { :say }
52
+ let(:child1) { ::CodeTree::parse{ "hello" } }
53
+ let(:child2) { ::CodeTree::parse{ "you" } }
54
+ let(:child3) { ::CodeTree::parse{ "world" } }
55
+ let(:expected) { ::CodeTree::parse{ (say "hello", "you", "world") } }
56
+ subject{ rewriter.apply_args_conventions(function, child1, [child2, child3]) }
57
+ it { should == expected }
58
+ end
59
+
60
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Rewriting::Rewriter#copy" do
4
+
5
+ let(:engine) { CodeTree::rewriter }
6
+
7
+ context "when called with a literal" do
8
+ subject{ engine.node(:test, [ 12 ]) }
9
+ it { should == CodeTree::parse{ (test 12) } }
10
+ end
11
+
12
+ context "when called with copy in mind" do
13
+ let(:node) { CodeTree::parse{ 12 } }
14
+ subject{ engine.node(node.function, node.children) }
15
+ it { should == CodeTree::parse{ 12 } }
16
+ end
17
+
18
+ end
19
+
@@ -0,0 +1,76 @@
1
+ require File.expand_path('../../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Rewriting::Rewriter" do
4
+
5
+ describe "when called without scope" do
6
+ subject{
7
+ CodeTree::rewriter {|r|
8
+ r.rule(:concat) {|r, node, *args| args.collect{|c| r.apply(c)}.join }
9
+ r.rule(:_) {|r, node| node.literal }
10
+ }
11
+ }
12
+
13
+ it { should be_kind_of(CodeTree::Rewriting::Rewriter) }
14
+
15
+ context("when applied on an ast string") do
16
+ specify { subject.rewrite("(concat 1, 2, 3)").should == "123" }
17
+ end
18
+
19
+ context("when applied with a proc argument") do
20
+ let(:code){ lambda{ (concat 1, 2, 3) } }
21
+ specify { subject.rewrite(code).should == "123" }
22
+ end
23
+
24
+ context("when applied with a block") do
25
+ specify { subject.rewrite{ (concat 1, 2, 3) }.should == "123" }
26
+ end
27
+
28
+ end
29
+
30
+ describe "when called with a scope object" do
31
+ subject{
32
+ CodeTree::rewriter {|r|
33
+ r.rule(:concat) {|r, node, *args| args.collect{|c| r.apply(c)}.join }
34
+ r.rule(:get) {|r, node, name| r.scope[r.apply(name)] }
35
+ r.rule(:_) {|r, node| node.literal }
36
+ }
37
+ }
38
+
39
+ it { should be_kind_of(CodeTree::Rewriting::Rewriter) }
40
+
41
+ context("when applied on an ast string") do
42
+ specify { subject.rewrite("(get :hello)", :hello => "world").should == "world" }
43
+ end
44
+
45
+ context("when applied with a proc argument") do
46
+ let(:code){ lambda{ (get :hello) } }
47
+ specify { subject.rewrite(code, :hello => "world").should == "world" }
48
+ end
49
+
50
+ context("when applied with a block") do
51
+ specify { subject.rewrite(:hello => "world"){ (get :hello) }.should == "world" }
52
+ end
53
+ end
54
+
55
+ describe "when only an ANY match is installed" do
56
+ subject{ CodeTree::rewriter{|r|
57
+ r.rule(CodeTree::Rewriting::Rewriter::Match::ANY){|r, node, arg| node.leaf? ? arg : r.apply(arg) }
58
+ }}
59
+
60
+ it "should return inner-most argument" do
61
+ subject.rewrite{ (call (other (one :hello))) }.should == :hello
62
+ end
63
+ end
64
+
65
+ describe "when only an LEAF/BRANCH are used" do
66
+ subject{ CodeTree::rewriter{|r|
67
+ r.rule(CodeTree::Rewriting::Rewriter::Match::BRANCH){|r, node, child| r.apply(child)}
68
+ r.rule(CodeTree::Rewriting::Rewriter::Match::LEAF){|r, node| node.literal}
69
+ }}
70
+
71
+ it "should return inner-most argument" do
72
+ subject.rewrite{ (call (other (one :hello))) }.should == :hello
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Rewriting::Rewriter::Match#apply" do
4
+
5
+ let(:predicate) { true }
6
+ let(:block) { lambda{ |r, node, *args| [r, node, args] } }
7
+ subject { CodeTree::Rewriting::Rewriter::Match.new(predicate, block) }
8
+
9
+ context "when called on a leaf node" do
10
+ let(:node) { CodeTree::AstNode.coerce(:literal) }
11
+ specify {
12
+ subject.apply("rewriter", node).should == ["rewriter", node, [ :literal ]]
13
+ }
14
+ end
15
+
16
+ context "when called on a branch node" do
17
+ let(:node) { CodeTree::AstNode.new(:branch, [1, 2, 3]) }
18
+ specify {
19
+ subject.apply("rewriter", node).should == ["rewriter", node, [1, 2, 3]]
20
+ }
21
+ end
22
+
23
+ end
@@ -0,0 +1,48 @@
1
+ require File.expand_path('../../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Rewriting::Rewriter::Match.coerce?" do
4
+
5
+ let(:branch) { CodeTree::parse{ (branch :hello) } }
6
+ let(:leaf) { CodeTree::parse{ :hello } }
7
+
8
+ context("when called with a symbol") do
9
+ subject{ CodeTree::Rewriting::Rewriter::Match.coerce(:branch, nil) }
10
+
11
+ it { should === branch }
12
+ it { should_not === leaf }
13
+ end
14
+
15
+ context("when called with a proc") do
16
+ let(:predicate) { lambda{|node| node.leaf? }}
17
+ subject{ CodeTree::Rewriting::Rewriter::Match.coerce(predicate, nil) }
18
+
19
+ it { should_not === branch }
20
+ it { should === leaf }
21
+ end
22
+
23
+ context("when called with '.'") do
24
+ subject{ CodeTree::Rewriting::Rewriter::Match.coerce(".", nil) }
25
+
26
+ it { should === 12 }
27
+ it { should === branch }
28
+ it { should === leaf }
29
+ end
30
+
31
+ context("when called with '*'") do
32
+ subject{ CodeTree::Rewriting::Rewriter::Match.coerce("*", nil) }
33
+
34
+ it { should_not === 12 }
35
+ it { should === branch }
36
+ it { should_not === leaf }
37
+ end
38
+
39
+ context("when called with '@*'") do
40
+ subject{ CodeTree::Rewriting::Rewriter::Match.coerce("@*", nil) }
41
+
42
+ it { should_not === 12 }
43
+ it { should_not === branch }
44
+ it { should === leaf }
45
+ end
46
+
47
+ end
48
+
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../../../../../../spec_helper', __FILE__)
2
+
3
+ describe "CodeTree::Rewriting::Rewriter::Match#matches?" do
4
+
5
+ let(:branch) { CodeTree::AstNode.new(:branch, [1, 2, 3]) }
6
+ let(:literal) { CodeTree::AstNode.coerce(12) }
7
+
8
+ context "when built with a symbol" do
9
+ subject { CodeTree::Rewriting::Rewriter::Match.coerce(:_, nil) }
10
+ specify {
11
+ (subject.matches? branch).should be_false
12
+ (subject.matches? literal).should be_true
13
+ (subject === branch).should be_false
14
+ (subject === literal).should be_true
15
+ }
16
+ end
17
+
18
+ context "when built with a proc" do
19
+ let(:proc) { lambda {|node| node.name == :branch } }
20
+ subject { CodeTree::Rewriting::Rewriter::Match.coerce(proc, nil) }
21
+ specify {
22
+ (subject.matches? branch).should be_true
23
+ (subject.matches? literal).should be_false
24
+ }
25
+ end
26
+
27
+ end