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,12 @@
1
+ h1. The MIT License
2
+
3
+ Copyright (c) 2009 Bernard Lambeau and the University of Louvain (Universite Catholique de Louvain, Louvain-la-Neuve, Belgium)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
11
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12
+
@@ -0,0 +1,44 @@
1
+ h1. SByC - Investigating Specialization by Constraint
2
+
3
+ h2. SYNOPSIS
4
+
5
+ SByC is an investigation about Specialization by Constraint as an alternative to common type systems that we found in (object-oriented) programming languages. The library is written as a collection of Ruby tools described below. Those tools are in fact sub-gems of SByC that can often be used in isolation.
6
+
7
+ h2. CodeTree
8
+
9
+ This part of SByC provides a safe, reusable, extensible, mashallable, and non-intrusive (no monkey patching of Ruby core classes) implementation of block expressions. Block expressions are parsed using a generic DSL and converted to a parse tree, which may be analyzed, rewrited, compiled, and so on. The example below illustrates typical usage of CodeTree.
10
+
11
+ <pre>
12
+ code = CodeTree::parse{ x > 10 & y < 20 }
13
+ # => (& (> x, 10), (< y, 20))
14
+
15
+ # See the evaluation section for details
16
+ code.eval{:x => 5, :y => 5}
17
+ # => true
18
+
19
+ # See the code-generation section for details
20
+ code.object_compile('scope', :[])
21
+ # => "scope[:x].>(10) & scope[:y].<(20)"
22
+
23
+ # CodeTree expressions may be marshaled
24
+ Marshal.dump(code)
25
+ </pre>
26
+
27
+ "Learn more about CodeTree":http://blambeau.github.com/sbyc/codetree.html
28
+
29
+ h2. INSTALL
30
+
31
+ bc. gem install sbyc
32
+
33
+ h2. DOCUMENTATION
34
+
35
+ * The official documentation can be found on "GitHub pages":http://blambeau.github.com/sbyc/
36
+ * The api documentation can be found on "rdoc.info":http://rdoc.info/projects/blambeau/sbyc
37
+
38
+ h2. CONTRIBUTE
39
+
40
+ Fork the project on "github":http://github.com/blambeau/sbyc, make a contrib, and send me a pull request
41
+
42
+ h2. CREDITS
43
+
44
+ SByC (c) 2010 by Bernard Lambeau. SByC is distributed under the MIT licence. Please see the LICENCE.md document for details.
@@ -0,0 +1,14 @@
1
+ module SByC
2
+
3
+ # Version
4
+ VERSION = "0.1.0".freeze
5
+
6
+ # Provides tools around functional source code trees.
7
+ module CodeTree
8
+
9
+ end # module CodeTree
10
+
11
+ end # module SByC
12
+
13
+ CodeTree = ::SByC::CodeTree
14
+ require 'sbyc/codetree'
@@ -0,0 +1,82 @@
1
+ module CodeTree
2
+
3
+ # Operator names
4
+ OPERATOR_NAMES = {
5
+ :[] => :get,
6
+ :[]= => :set,
7
+ :** => :exponentiation,
8
+ :~ => :complement,
9
+ :== => :eq,
10
+ #:!= => :neq,
11
+ :-@ => :unary_minus,
12
+ :+@ => :unary_plus,
13
+ :- => :minus,
14
+ :+ => :plus,
15
+ :* => :times,
16
+ :/ => :divide,
17
+ :% => :modulo,
18
+ :=~ => :match,
19
+ :=== => :matches?,
20
+ :& => :bool_and,
21
+ :| => :bool_or,
22
+ :> => :gt,
23
+ :>= => :gte,
24
+ :<= => :lte,
25
+ :< => :lt,
26
+ :>> => :right_shift,
27
+ :<< => :left_shift,
28
+ :'^' => :exclusive,
29
+ :<=> => :compare
30
+ }
31
+ REVERSE_OPERATOR_NAMES = Hash[*OPERATOR_NAMES.to_a.collect{|c| c.reverse}.flatten]
32
+
33
+ # Parses some code or block
34
+ def parse(code = nil, &block)
35
+ ProcParser::parse(code, &block)
36
+ end
37
+ module_function :parse
38
+
39
+ # Alias for _parse_
40
+ def expr(code = nil, &block)
41
+ ProcParser::parse(code, &block)
42
+ end
43
+ module_function :expr
44
+
45
+ # Factors a CodeTree::Matcher instance
46
+ def matcher(code = nil, &block)
47
+ CodeTree::Matcher.new(parse(code, &block))
48
+ end
49
+ module_function :matcher
50
+
51
+ # Factors a rewriter instance
52
+ def rewriter(&definition)
53
+ CodeTree::Rewriting::Rewriter.new(&definition)
54
+ end
55
+ module_function :rewriter
56
+
57
+ # Factors a producer instance
58
+ def producer(default_rules = true, &definition)
59
+ CodeTree::Producing::Producer.new(default_rules, &definition)
60
+ end
61
+ module_function :producer
62
+
63
+ # Converts an argument to an parse tree (an ASTNode instance)
64
+ def coerce(arg)
65
+ case arg
66
+ when AstNode
67
+ arg
68
+ when Proc, String
69
+ CodeTree::parse(arg)
70
+ else
71
+ raise "Unable to coerce #{arg.inspect} to a CodeTree::AstNode"
72
+ end
73
+ end
74
+ module_function :coerce
75
+
76
+ end # module CodeTree
77
+ require 'sbyc/codetree/ast_node'
78
+ require 'sbyc/codetree/proc_parser'
79
+ require 'sbyc/codetree/eval'
80
+ require 'sbyc/codetree/producing'
81
+ require 'sbyc/codetree/matching'
82
+ require 'sbyc/codetree/rewriting'
@@ -0,0 +1,101 @@
1
+ module CodeTree
2
+ class AstNode
3
+ include Enumerable
4
+
5
+ # Name of the method call
6
+ attr_reader :name
7
+ alias :function :name
8
+
9
+ # Children nodes
10
+ attr_reader :children
11
+ alias :args :children
12
+
13
+ # Operator found by type checking
14
+ attr_reader :operator
15
+
16
+ # Creates an ASTNode instance
17
+ def initialize(name, children)
18
+ @name, @children = name, children
19
+ end
20
+
21
+ # Delegated to the children array
22
+ def [](*args, &block)
23
+ children.[](*args, &block)
24
+ end
25
+
26
+ # Recursively finds the first literal.
27
+ def literal
28
+ leaf? ? children[0] : children[0].literal
29
+ end
30
+
31
+ # Returns false
32
+ def leaf?
33
+ (name == :_)
34
+ end
35
+
36
+ # Negation of leaf?
37
+ def branch?
38
+ not(leaf?)
39
+ end
40
+
41
+ # Yields block with each child in turn
42
+ def each(&block)
43
+ children.each(&block)
44
+ end
45
+
46
+ # Makes a depth-first-search visit of the AST
47
+ def visit(&block)
48
+ yield(self, leaf? ? children : children.collect{|c| c.visit(&block)})
49
+ end
50
+ alias :produce :visit
51
+
52
+ # Inspection
53
+ def inspect
54
+ "(#{name} #{children.collect{|c| c.inspect}.join(', ')})"
55
+ end
56
+
57
+ # Returns a short string representation
58
+ def to_s
59
+ case function
60
+ when :'_'
61
+ literal.inspect
62
+ when :'?'
63
+ literal.to_s
64
+ else
65
+ "(#{name} #{children.collect{|c| c.to_s}.join(', ')})"
66
+ end
67
+ end
68
+
69
+ # Returns an array version of this ast
70
+ def to_a
71
+ visit{|node, collected| [node.name, collected]}
72
+ end
73
+
74
+ # Checks tree equality with another node
75
+ def ==(other)
76
+ return false unless other.kind_of?(AstNode)
77
+ return false unless (function == other.function)
78
+ return false unless args.size == other.args.size
79
+ return false unless args.zip(other.args).all?{|v1, v2| v1 == v2}
80
+ true
81
+ end
82
+
83
+ # Coercion
84
+ def self.coerce(arg)
85
+ case arg
86
+ when AstNode
87
+ arg
88
+ when Array
89
+ name, children = arg
90
+ if name == :_ and children.size == 1
91
+ AstNode.new(:_, children)
92
+ else
93
+ AstNode.new(name, children.collect{|c| AstNode.coerce(c)})
94
+ end
95
+ else
96
+ AstNode.new(:_, [ arg ])
97
+ end
98
+ end
99
+
100
+ end # module AstNode
101
+ end # module CodeTree
@@ -0,0 +1,3 @@
1
+ require 'sbyc/codetree/eval/object_eval'
2
+ require 'sbyc/codetree/eval/functional_eval'
3
+ require 'sbyc/codetree/eval/ast_node_ext'
@@ -0,0 +1,38 @@
1
+ module CodeTree
2
+ class AstNode
3
+
4
+ # Generates the code of an object evaluation
5
+ def object_compile(scope_object = "scope", scope_method = :[])
6
+ CodeTree::ObjectEval.object_compile(self, scope_object, scope_method)
7
+ end
8
+
9
+ # Generates a lambda function for object evaluation
10
+ def object_proc(scope_method = :[])
11
+ CodeTree::ObjectEval.object_proc(self, scope_method)
12
+ end
13
+
14
+ # Executes object evaluation of this ast
15
+ def object_eval(scope = {}, scope_method = :[])
16
+ CodeTree::ObjectEval.object_eval(self, scope)
17
+ end
18
+ alias :call :object_eval
19
+ alias :eval :object_eval
20
+
21
+ # Generates the code of an functional evaluation
22
+ def functional_compile(receiver_object = "receiver", scope_object = "scope", scope_method = :[])
23
+ CodeTree::FunctionalEval.functional_compile(self, receiver_object, scope_object, scope_method)
24
+ end
25
+
26
+ # Generates a lambda function for functional evaluation
27
+ def functional_proc(scope_method = :[])
28
+ CodeTree::FunctionalEval.functional_proc(self, scope_method)
29
+ end
30
+
31
+ # Executes functional evaluation of this ast
32
+ def functional_eval(master_object, scope = {}, scope_method = :[])
33
+ CodeTree::FunctionalEval.functional_eval(self, master_object, scope)
34
+ end
35
+ alias :apply :functional_eval
36
+
37
+ end # class AstNode
38
+ end # module CodeTree
@@ -0,0 +1,36 @@
1
+ module CodeTree
2
+ module FunctionalEval
3
+
4
+ # Generates code for a functional evaluation
5
+ def functional_compile(ast, receiver_object = "receiver", scope_object = "scope", scope_method = :[])
6
+ ast.produce{|node, collected|
7
+ case func = node.function
8
+ when :_
9
+ collected.first.inspect
10
+ when :'?'
11
+ if scope_method == :[]
12
+ "#{scope_object}[#{collected.join(', ')}]"
13
+ else
14
+ "#{scope_object}.#{scope_method}(#{collected.join(', ')})"
15
+ end
16
+ else
17
+ "#{receiver_object}.#{func}(#{collected.join(', ')})"
18
+ end
19
+ }
20
+ end
21
+ module_function :functional_compile
22
+
23
+ # Generates a lambda function for functional evaluation
24
+ def functional_proc(ast, scope_method = :[])
25
+ ::Kernel.eval("::Kernel.lambda{|receiver, scope| #{functional_compile(ast, 'receiver', 'scope', scope_method)}}")
26
+ end
27
+ module_function :functional_proc
28
+
29
+ # Evaluates this AST with an object style.
30
+ def functional_eval(ast, receiver, scope = {}, scope_method = :[])
31
+ functional_proc(ast, scope_method).call(receiver, scope)
32
+ end
33
+ module_function :functional_eval
34
+
35
+ end # module FunctionalEval
36
+ end # module CodeTree
@@ -0,0 +1,36 @@
1
+ module CodeTree
2
+ module ObjectEval
3
+
4
+ # Generates code for an object evaluation
5
+ def object_compile(ast, scope_object = "scope", scope_method = :[])
6
+ ast.produce{|node, collected|
7
+ case func = node.function
8
+ when :'_'
9
+ collected.first.inspect
10
+ when :'?'
11
+ if scope_method == :[]
12
+ "#{scope_object}[#{collected.join(', ')}]"
13
+ else
14
+ "#{scope_object}.#{scope_method}(#{collected.join(', ')})"
15
+ end
16
+ else
17
+ "#{collected[0]}.#{func}(#{collected[1..-1].join(', ')})"
18
+ end
19
+ }
20
+ end
21
+ module_function :object_compile
22
+
23
+ # Generates a lambda function for object evaluation
24
+ def object_proc(ast, scope_method = :[])
25
+ ::Kernel.eval "::Kernel.lambda{|scope| #{object_compile(ast, 'scope', scope_method)}}"
26
+ end
27
+ module_function :object_proc
28
+
29
+ # Evaluates this AST with an object style.
30
+ def object_eval(ast, scope = {}, scope_method = :[])
31
+ object_proc(ast, scope_method).call(scope)
32
+ end
33
+ module_function :object_eval
34
+
35
+ end # module ObjectEval
36
+ end # module CodeTree
@@ -0,0 +1,3 @@
1
+ require 'sbyc/codetree/matching/matcher'
2
+ require 'sbyc/codetree/matching/match_data'
3
+ require 'sbyc/codetree/matching/ast_node_ext'
@@ -0,0 +1,14 @@
1
+ module CodeTree
2
+ class AstNode
3
+
4
+ # Applies ast node matching
5
+ def ===(other)
6
+ if other.kind_of?(CodeTree::AstNode)
7
+ CodeTree::Matcher.new(self) === other
8
+ else
9
+ super
10
+ end
11
+ end
12
+
13
+ end # class AstNode
14
+ end # module CodeTree
@@ -0,0 +1,30 @@
1
+ module CodeTree
2
+ class MatchData
3
+
4
+ # Source AstNode of the matching
5
+ attr_reader :source
6
+
7
+ # Matched node
8
+ attr_reader :matched_node
9
+
10
+ # Captured variables
11
+ attr_reader :captures
12
+
13
+ # Creates a MatchData instance
14
+ def initialize(source, matched_node, captures)
15
+ @source, @matched_node, @captures = source, matched_node, captures
16
+ end
17
+
18
+ # Returns node matched under a given variable name
19
+ def [](varname)
20
+ captures[varname]
21
+ end
22
+
23
+ # Inspects this MatchData
24
+ def inspect
25
+ "MatchData#{captures.inspect}"
26
+ end
27
+ alias :to_s :inspect
28
+
29
+ end # class MatchData
30
+ end # module CodeTree
@@ -0,0 +1,83 @@
1
+ module CodeTree
2
+ class Matcher
3
+
4
+ # Match Abstract Syntax Tree
5
+ attr_reader :match_ast
6
+
7
+ # Creates an IMatch instance
8
+ def initialize(match_ast)
9
+ @match_ast = match_ast
10
+ end
11
+
12
+ # Looks for a match against some ast node
13
+ def =~(ast_node)
14
+ if captures = do_match(match_ast, ast_node)
15
+ CodeTree::MatchData.new(ast_node, ast_node, captures)
16
+ else
17
+ nil
18
+ end
19
+ end
20
+ alias :match :=~
21
+ alias :call :=~
22
+
23
+ # Returns true if _ast_node_ is matched by this matcher,
24
+ # false otherwise
25
+ def ===(ast_node)
26
+ not((self =~ ast_node).nil?)
27
+ end
28
+
29
+ # Lookups for a match between _matcher_ and _matched_.
30
+ def do_match(matcher, matched, match_data = {})
31
+ return nil unless matched.kind_of?(CodeTree::AstNode)
32
+ return nil unless function_match(matcher, matched, match_data)
33
+ return nil unless args_match(matcher.args[1..-1], matched.args.dup, match_data)
34
+ return match_data
35
+ end
36
+
37
+ #
38
+ # Applies function name matching, returning true if _ast_node_'s function name
39
+ # is matched by first argument of _match_ast_.
40
+ #
41
+ # Cases that may arise:
42
+ # (match (_ :fname), ...) # true iif ast_node.function == fname
43
+ # (match (? (_ :x)), ...) # true and match_data[x] = ast_node.function
44
+ # false otherwise
45
+ #
46
+ def function_match(match_ast, ast_node, match_data = {})
47
+ case fname = match_ast[0].function
48
+ when :'_'
49
+ return (match_ast[0].literal == ast_node.function ? true : false)
50
+ when :'?'
51
+ match_data[match_ast[0].literal] = ast_node.function
52
+ return true
53
+ end
54
+ false
55
+ end
56
+
57
+ #
58
+ # Applies arguments matching, return true if there is a match,
59
+ # false otherwise.
60
+ #
61
+ def args_match(matchers, candidates, match_data = {})
62
+ matchers.each do |m|
63
+ case m.function
64
+ when :'?'
65
+ return false if candidates.empty?
66
+ match_data[m.literal] = candidates.shift
67
+ when :'_'
68
+ return false if candidates.empty?
69
+ return false unless candidates.shift.literal == m.literal
70
+ when :'[]'
71
+ return false if candidates.empty?
72
+ match_data[m.literal] = candidates
73
+ candidates = []
74
+ else
75
+ return false if candidates.empty?
76
+ return false unless do_match(m, candidates.shift, match_data)
77
+ end
78
+ end
79
+ candidates.empty?
80
+ end
81
+
82
+ end # class Matcher
83
+ end # module CodeTree