sbyc 0.1.3 → 0.1.4
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/lib/sbyc.rb +1 -5
- data/lib/sbyc/codetree.rb +79 -77
- data/lib/sbyc/codetree/ast_node.rb +117 -115
- data/lib/sbyc/codetree/eval/ast_node_ext.rb +33 -31
- data/lib/sbyc/codetree/eval/functional_eval.rb +33 -31
- data/lib/sbyc/codetree/eval/object_eval.rb +33 -31
- data/lib/sbyc/codetree/matching/ast_node_ext.rb +13 -11
- data/lib/sbyc/codetree/matching/match_data.rb +25 -23
- data/lib/sbyc/codetree/matching/matcher.rb +73 -71
- data/lib/sbyc/codetree/name2x/delegate.rb +56 -54
- data/lib/sbyc/codetree/name2x/module_delegate.rb +23 -22
- data/lib/sbyc/codetree/proc_parser.rb +101 -99
- data/lib/sbyc/codetree/producing/producer.rb +69 -67
- data/lib/sbyc/codetree/producing/tracing_methods.rb +66 -64
- data/lib/sbyc/codetree/rewriting/class_methods.rb +15 -13
- data/lib/sbyc/codetree/rewriting/compiler.rb +27 -25
- data/lib/sbyc/codetree/rewriting/instance_methods.rb +80 -78
- data/lib/sbyc/codetree/rewriting/match.rb +51 -49
- data/lib/sbyc/shortspaces.rb +3 -0
- data/lib/sbyc/type_system.rb +4 -2
- data/lib/sbyc/type_system/contract.rb +38 -36
- data/lib/sbyc/type_system/errors.rb +8 -6
- data/lib/sbyc/type_system/ruby.rb +137 -121
- data/test/spec/spec_helper.rb +1 -1
- data/test/spec/unit/sbyc/type_system/ruby/boolean.spec +14 -0
- data/test/spec/unit/sbyc/type_system/ruby/coerce.spec +10 -1
- metadata +6 -4
@@ -1,25 +1,26 @@
|
|
1
|
-
module
|
2
|
-
module
|
3
|
-
|
1
|
+
module SByC
|
2
|
+
module CodeTree
|
3
|
+
module Name2X
|
4
|
+
class ModuleDelegate < Delegate
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
# Overrides the default behavior to use const_get instead.
|
7
|
+
def fetch(name)
|
8
|
+
name = name2name(name)
|
9
|
+
delegate.const_defined?(name) ? delegate.const_get(name) : nil
|
10
|
+
rescue NameError
|
11
|
+
nil
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
# Capitalize a name
|
15
|
+
def name2name(name)
|
16
|
+
src = name.to_s
|
17
|
+
src.gsub!(/[^a-zA-Z\s]/," ")
|
18
|
+
src = " " + src.split.join(" ")
|
19
|
+
src.gsub!(/ (.)/) { $1.upcase }
|
20
|
+
src.to_sym
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
end # module CodeTree
|
25
|
-
|
23
|
+
end # class ModuleDelegate
|
24
|
+
end # module Name2X
|
25
|
+
end # module CodeTree
|
26
|
+
end # module SByC
|
@@ -1,124 +1,126 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module SByC
|
2
|
+
module CodeTree
|
3
|
+
class ProcParser
|
4
|
+
#
|
5
|
+
# Collector when :multiline option is set to true.
|
6
|
+
#
|
7
|
+
class Collector
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
# Which nodes are kept?
|
10
|
+
attr_reader :kept
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
# Creates a collector instance
|
13
|
+
def initialize
|
14
|
+
@kept = []
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
# Callback when a new Expr is created
|
18
|
+
def built(who, children)
|
19
|
+
@kept << who
|
20
|
+
children.each{|c| @kept.delete_if{|k| k.__id__ == c.__id__}}
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
# Returns expressions as an array of AstNodes
|
24
|
+
def to_a
|
25
|
+
kept.collect{|c| c.__to_functional_code}
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
+
end # class Collector
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
# An expression
|
31
|
+
class Expr
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
# Methods that we keep
|
34
|
+
KEPT_METHODS = [ "__send__", "__id__", "instance_eval", "initialize", "object_id",
|
35
|
+
"singleton_method_added", "singleton_method_undefined", "method_missing",
|
36
|
+
"__evaluate__", "coerce", "kind_of?"]
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
class << self
|
39
|
+
def __clean_scope__
|
40
|
+
# Removes all methods that are not needed to the class
|
41
|
+
(instance_methods + private_instance_methods).each do |m|
|
42
|
+
m_to_s = m.to_s
|
43
|
+
undef_method(m_to_s.to_sym) unless ('__' == m_to_s[0..1]) or KEPT_METHODS.include?(m_to_s)
|
44
|
+
end
|
43
45
|
end
|
44
46
|
end
|
45
|
-
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
# Creates an expression instance
|
49
|
+
def initialize(name, children, collector)
|
50
|
+
class << self; __clean_scope__; end
|
51
|
+
@name, @children, @collector = name, children, collector
|
52
|
+
if @collector and @name
|
53
|
+
@collector.built(self, children)
|
54
|
+
end
|
53
55
|
end
|
54
|
-
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
57
|
+
# Returns the associated functional code
|
58
|
+
def __to_functional_code
|
59
|
+
children = (@children || []).collect{|c|
|
60
|
+
c.kind_of?(Expr) ? c.__to_functional_code : AstNode.coerce(c)
|
61
|
+
}
|
62
|
+
CodeTree::AstNode.coerce([@name, children])
|
63
|
+
end
|
64
|
+
alias :inspect :__to_functional_code
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
66
|
+
# Called when a method is missing
|
67
|
+
def method_missing(name, *args)
|
68
|
+
if @name.nil?
|
69
|
+
if name == :[]
|
70
|
+
Expr.new(:'?', args, @collector)
|
71
|
+
elsif args.empty?
|
72
|
+
Expr.new(:'?', [name], @collector)
|
73
|
+
else
|
74
|
+
Expr.new(name, args, @collector)
|
75
|
+
end
|
72
76
|
else
|
77
|
+
args.unshift(self)
|
73
78
|
Expr.new(name, args, @collector)
|
74
79
|
end
|
75
|
-
else
|
76
|
-
args.unshift(self)
|
77
|
-
Expr.new(name, args, @collector)
|
78
80
|
end
|
79
|
-
end
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
def coerce(other)
|
83
|
+
[self, other]
|
84
|
+
end
|
84
85
|
|
85
|
-
|
86
|
+
end # class Expr
|
86
87
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
88
|
+
# Parses a Proc object
|
89
|
+
def self.parse_proc(block, options = {})
|
90
|
+
collector = options[:multiline] ? Collector.new : nil
|
91
|
+
e = case block.arity
|
92
|
+
when -1, 0
|
93
|
+
expr = Expr.new(nil, nil, collector)
|
94
|
+
expr.instance_eval(&block)
|
95
|
+
when 1
|
96
|
+
block.call(Expr.new(nil, nil, collector))
|
97
|
+
else
|
98
|
+
raise ArgumentError, "Unexpected block arity #{block.arity}"
|
99
|
+
end
|
100
|
+
return collector.to_a if collector
|
101
|
+
case e
|
102
|
+
when Expr
|
103
|
+
e.__to_functional_code
|
104
|
+
else
|
105
|
+
CodeTree::AstNode.coerce(e)
|
106
|
+
end
|
105
107
|
end
|
106
|
-
end
|
107
108
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
109
|
+
# Parses a block
|
110
|
+
def self.parse(code = nil, options = {}, &block)
|
111
|
+
block = code || block
|
112
|
+
case block
|
113
|
+
when Proc
|
114
|
+
parse_proc(block, options)
|
115
|
+
when AstNode
|
116
|
+
block
|
117
|
+
when String
|
118
|
+
parse(Kernel.eval("proc{ #{block} }"), options)
|
119
|
+
else
|
120
|
+
raise ArgumentError, "Unable to parse #{block}"
|
121
|
+
end
|
120
122
|
end
|
121
|
-
end
|
122
123
|
|
123
|
-
|
124
|
-
end # module CodeTree
|
124
|
+
end # class ProcParser
|
125
|
+
end # module CodeTree
|
126
|
+
end # module SByC
|
@@ -1,80 +1,82 @@
|
|
1
|
-
module
|
2
|
-
module
|
3
|
-
|
1
|
+
module SByC
|
2
|
+
module CodeTree
|
3
|
+
module Producing
|
4
|
+
class Producer
|
4
5
|
|
5
|
-
|
6
|
-
|
6
|
+
# Rules kepts by node function
|
7
|
+
attr_reader :rules
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
# Extension options.
|
10
|
+
attr_reader :extension_options
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
# Creates a producer instance
|
13
|
+
def initialize(default_rules = true)
|
14
|
+
@rules = {}
|
15
|
+
@extension_options = {}
|
16
|
+
if default_rules
|
17
|
+
rule(:_) {|r,node| node.literal}
|
18
|
+
rule("*"){|r,node| r.apply(node.children)}
|
19
|
+
end
|
20
|
+
yield(self) if block_given?
|
18
21
|
end
|
19
|
-
yield(self) if block_given?
|
20
|
-
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
# Adds an extension module
|
24
|
+
def add_extension(mod, options = {})
|
25
|
+
self.extend(mod)
|
26
|
+
options = mod.send(:prepare_options, options || {}) if mod.respond_to?(:prepare_options)
|
27
|
+
extension_options[mod] = options
|
28
|
+
self
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
# Adds a rule
|
32
|
+
def rule(function_name, &block)
|
33
|
+
rules[function_name] = block
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
# Applies on some arguments
|
37
|
+
def apply(*args)
|
38
|
+
case args = apply_args_conventions(*args)
|
39
|
+
when CodeTree::AstNode
|
40
|
+
apply_on_node(args)
|
41
|
+
when Array
|
42
|
+
args.collect{|c| apply(c)}
|
43
|
+
else
|
44
|
+
args
|
45
|
+
end
|
44
46
|
end
|
45
|
-
end
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
# Applies on a given node
|
49
|
+
def apply_on_node(node)
|
50
|
+
func = node.function
|
51
|
+
if rules.key?(func)
|
52
|
+
@rules[func].call(self, node)
|
53
|
+
elsif rules.key?("*")
|
54
|
+
@rules["*"].call(self, node)
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
56
58
|
end
|
57
|
-
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
60
|
+
# Applies arument conventions allowed by _apply_
|
61
|
+
def apply_args_conventions(*args)
|
62
|
+
if args.size == 1 and args[0].kind_of?(CodeTree::AstNode)
|
63
|
+
args[0]
|
64
|
+
elsif args.size > 1 and args[0].kind_of?(Symbol)
|
65
|
+
function = args.shift
|
66
|
+
children = args.collect{|c| apply_args_conventions(c)}.flatten
|
67
|
+
CodeTree::AstNode.coerce([function, children])
|
68
|
+
elsif args.all?{|a| a.kind_of?(CodeTree::AstNode)}
|
69
|
+
args
|
70
|
+
elsif args.size == 1
|
71
|
+
args[0]
|
72
|
+
else
|
73
|
+
raise ArgumentError, "Unable to apply on #{args.inspect} (#{args.size})", caller
|
74
|
+
end
|
73
75
|
end
|
74
|
-
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end # module CodeTree
|
77
|
+
private :apply_on_node
|
78
|
+
private :apply_args_conventions
|
79
|
+
end # class Producer
|
80
|
+
end # module Producing
|
81
|
+
end # module CodeTree
|
82
|
+
end # module SByC
|
@@ -1,77 +1,79 @@
|
|
1
|
-
module
|
2
|
-
module
|
3
|
-
module
|
1
|
+
module SByC
|
2
|
+
module CodeTree
|
3
|
+
module Producing
|
4
|
+
module TracingMethods
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
# Returns the default options
|
7
|
+
def self.default_options
|
8
|
+
{:indent_support => true,
|
9
|
+
:indent_string => " ",
|
10
|
+
:newline_support => true,
|
11
|
+
:io => STDOUT}
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
# Merge the default options and the options
|
15
|
+
def self.prepare_options(options)
|
16
|
+
default_options.merge(options)
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
# Returns tracing extension options
|
20
|
+
def tracing_options
|
21
|
+
extension_options[TracingMethods] ||= {}
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
# Is indentation support installed?
|
25
|
+
def tracing_indent_support?
|
26
|
+
!!tracing_options[:indent_support]
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
# Is new line support installed?
|
30
|
+
def tracing_newline_support?
|
31
|
+
!!tracing_options[:newline_support]
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
# Returns the string to use for indentation
|
35
|
+
def tracing_indent_string
|
36
|
+
tracing_options[:indent_string] ||= " "
|
37
|
+
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
# Returns the current indentation level
|
40
|
+
def tracing_indent_level
|
41
|
+
tracing_options[:indent_level] ||= 0
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
# Returns the IO object (actually, any object supporting the << operator)
|
45
|
+
# that must be used for tracing the Producer.
|
46
|
+
def tracing_io
|
47
|
+
tracing_options[:io] ||= STDOUT
|
48
|
+
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
50
|
+
# Logs a message on the IO object, without any other side effect (no
|
51
|
+
# depth increment, for instance).
|
52
|
+
def trace(message)
|
53
|
+
message = (tracing_indent_string*tracing_indent_level + message.to_s) if tracing_indent_support? and tracing_indent_level>0
|
54
|
+
message = "#{message}\n" if tracing_newline_support?
|
55
|
+
if tracing_io.kind_of?(IO)
|
56
|
+
tracing_io << message
|
57
|
+
else
|
58
|
+
tracing_io << message
|
59
|
+
end
|
58
60
|
end
|
59
|
-
end
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
# Logs that a rule has been entered and increment the depth
|
63
|
+
# level
|
64
|
+
def trace_rule_entered(message)
|
65
|
+
trace(message)
|
66
|
+
tracing_options[:indent_level] = tracing_indent_level+1
|
67
|
+
end
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
# Logs that a rule has been exited and decrements the depth
|
70
|
+
# level
|
71
|
+
def trace_rule_exited(message)
|
72
|
+
tracing_options[:indent_level] = tracing_indent_level-1
|
73
|
+
trace(message)
|
74
|
+
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
end # module CodeTree
|
76
|
+
end # module TracingMethods
|
77
|
+
end # module Producing
|
78
|
+
end # module CodeTree
|
79
|
+
end # module SByC
|