sbyc 0.1.0

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