sbyc 0.1.0 → 0.1.1
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 -1
- data/lib/sbyc/codetree.rb +9 -6
- data/lib/sbyc/codetree/ast_node.rb +39 -4
- data/lib/sbyc/codetree/eval/functional_eval.rb +1 -1
- data/lib/sbyc/codetree/eval/object_eval.rb +1 -1
- data/lib/sbyc/codetree/name2x.rb +2 -0
- data/lib/sbyc/codetree/name2x/delegate.rb +57 -0
- data/lib/sbyc/codetree/name2x/module_delegate.rb +25 -0
- data/lib/sbyc/codetree/proc_parser.rb +46 -14
- data/lib/sbyc/codetree/producing.rb +2 -1
- data/lib/sbyc/codetree/producing/producer.rb +12 -0
- data/lib/sbyc/codetree/producing/tracing_methods.rb +77 -0
- data/test/spec/unit/sbyc/codetree/ast_node/code_inject.spec +57 -0
- data/test/spec/unit/sbyc/codetree/ast_node/digest.spec +83 -0
- data/test/spec/unit/sbyc/codetree/ast_node/rename.spec +36 -0
- data/test/spec/unit/sbyc/codetree/ast_node/to_s.spec +1 -1
- data/test/spec/unit/sbyc/codetree/ast_node/visit.spec +2 -2
- data/test/spec/unit/sbyc/codetree/name2x/delegate/coerce.spec +38 -0
- data/test/spec/unit/sbyc/codetree/name2x/delegate/fetch.spec +15 -0
- data/test/spec/unit/sbyc/codetree/name2x/delegate/name2class.spec +31 -0
- data/test/spec/unit/sbyc/codetree/name2x/delegate/name2module.spec +31 -0
- data/test/spec/unit/sbyc/codetree/name2x/delegate/name2name.spec +31 -0
- data/test/spec/unit/sbyc/codetree/name2x/module_delegate/name2class.spec +27 -0
- data/test/spec/unit/sbyc/codetree/name2x/module_delegate/name2module.spec +22 -0
- data/test/spec/unit/sbyc/codetree/name2x/module_delegate/name2name.spec +18 -0
- data/test/spec/unit/sbyc/codetree/parse.spec +32 -0
- data/test/spec/unit/sbyc/codetree/proc_parser/expr.spec +1 -1
- data/test/spec/unit/sbyc/codetree/proc_parser/parse.spec +34 -1
- data/test/spec/unit/sbyc/codetree/producing/producer/add_extension.spec +62 -0
- data/test/spec/unit/sbyc/codetree/producing/tracing_methods.spec +36 -0
- data/test/spec/unit/sbyc/codetree/producing/tracing_methods/default_options.spec +10 -0
- data/test/spec/unit/sbyc/codetree/producing/tracing_methods/prepare_options.spec +18 -0
- data/test/spec/unit/sbyc/codetree/producing/tracing_methods/trace.spec +59 -0
- data/test/spec/unit/sbyc/codetree/producing/tracing_methods/trace_rule_entered.spec +25 -0
- data/test/spec/unit/sbyc/codetree/producing/tracing_methods/trace_rule_exited.spec +25 -0
- metadata +27 -4
data/lib/sbyc.rb
CHANGED
data/lib/sbyc/codetree.rb
CHANGED
@@ -2,6 +2,7 @@ module CodeTree
|
|
2
2
|
|
3
3
|
# Operator names
|
4
4
|
OPERATOR_NAMES = {
|
5
|
+
:'?' => :varref,
|
5
6
|
:[] => :get,
|
6
7
|
:[]= => :set,
|
7
8
|
:** => :exponentiation,
|
@@ -17,8 +18,8 @@ module CodeTree
|
|
17
18
|
:% => :modulo,
|
18
19
|
:=~ => :match,
|
19
20
|
:=== => :matches?,
|
20
|
-
:& => :
|
21
|
-
:| => :
|
21
|
+
:& => :and,
|
22
|
+
:| => :or,
|
22
23
|
:> => :gt,
|
23
24
|
:>= => :gte,
|
24
25
|
:<= => :lte,
|
@@ -31,14 +32,15 @@ module CodeTree
|
|
31
32
|
REVERSE_OPERATOR_NAMES = Hash[*OPERATOR_NAMES.to_a.collect{|c| c.reverse}.flatten]
|
32
33
|
|
33
34
|
# Parses some code or block
|
34
|
-
def parse(code = nil, &block)
|
35
|
-
|
35
|
+
def parse(code = nil, options = nil, &block)
|
36
|
+
code, options = nil, code if code.kind_of?(Hash) and options.nil?
|
37
|
+
ProcParser::parse(code, options || {}, &block)
|
36
38
|
end
|
37
39
|
module_function :parse
|
38
40
|
|
39
41
|
# Alias for _parse_
|
40
|
-
def expr(code = nil, &block)
|
41
|
-
|
42
|
+
def expr(code = nil, options = nil, &block)
|
43
|
+
parse(code, options || {}, &block)
|
42
44
|
end
|
43
45
|
module_function :expr
|
44
46
|
|
@@ -80,3 +82,4 @@ require 'sbyc/codetree/eval'
|
|
80
82
|
require 'sbyc/codetree/producing'
|
81
83
|
require 'sbyc/codetree/matching'
|
82
84
|
require 'sbyc/codetree/rewriting'
|
85
|
+
require 'sbyc/codetree/name2x'
|
@@ -1,9 +1,8 @@
|
|
1
1
|
module CodeTree
|
2
2
|
class AstNode
|
3
|
-
include Enumerable
|
4
3
|
|
5
4
|
# Name of the method call
|
6
|
-
|
5
|
+
attr_accessor :name
|
7
6
|
alias :function :name
|
8
7
|
|
9
8
|
# Children nodes
|
@@ -45,9 +44,44 @@ module CodeTree
|
|
45
44
|
|
46
45
|
# Makes a depth-first-search visit of the AST
|
47
46
|
def visit(&block)
|
48
|
-
yield(self, leaf? ? children : children.collect{|c| c.visit(&block)})
|
47
|
+
yield(self, leaf? ? children : children.collect{|c| c.respond_to?(:visit) ? c.visit(&block) : c})
|
48
|
+
end
|
49
|
+
|
50
|
+
# Renames some nodes, given a name2name map.
|
51
|
+
def rename!(map = nil, &block)
|
52
|
+
map = CodeTree::Name2X::Delegate.coerce(map || block)
|
53
|
+
visit{|node, collected|
|
54
|
+
newname = map.name2name(node.name)
|
55
|
+
node.send(:name=, newname) if newname
|
56
|
+
nil
|
57
|
+
}
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
# Inject code through module mapped to function names
|
62
|
+
def code_inject!(map = nil, &block)
|
63
|
+
map = CodeTree::Name2X::Delegate.coerce(map || block)
|
64
|
+
visit{|node, collected|
|
65
|
+
ext = map.name2module(node.function)
|
66
|
+
node.extend(ext) if ext
|
67
|
+
nil
|
68
|
+
}
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
# Create class instances
|
73
|
+
def digest(map = nil, &block)
|
74
|
+
map = CodeTree::Name2X::Delegate.coerce(map || block)
|
75
|
+
visit{|node, collected|
|
76
|
+
if node.leaf?
|
77
|
+
node.literal
|
78
|
+
else
|
79
|
+
ext = map.name2class(node.function)
|
80
|
+
raise "Unexpected node function: #{node.function}" unless ext
|
81
|
+
ext.new(*collected)
|
82
|
+
end
|
83
|
+
}
|
49
84
|
end
|
50
|
-
alias :produce :visit
|
51
85
|
|
52
86
|
# Inspection
|
53
87
|
def inspect
|
@@ -97,5 +131,6 @@ module CodeTree
|
|
97
131
|
end
|
98
132
|
end
|
99
133
|
|
134
|
+
private :name=
|
100
135
|
end # module AstNode
|
101
136
|
end # module CodeTree
|
@@ -3,7 +3,7 @@ module CodeTree
|
|
3
3
|
|
4
4
|
# Generates code for a functional evaluation
|
5
5
|
def functional_compile(ast, receiver_object = "receiver", scope_object = "scope", scope_method = :[])
|
6
|
-
ast.
|
6
|
+
ast.visit{|node, collected|
|
7
7
|
case func = node.function
|
8
8
|
when :_
|
9
9
|
collected.first.inspect
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module CodeTree
|
2
|
+
module Name2X
|
3
|
+
class Delegate
|
4
|
+
|
5
|
+
# Hash delegate
|
6
|
+
attr_reader :delegate
|
7
|
+
|
8
|
+
# Creates FromHash instance
|
9
|
+
def initialize(delegate)
|
10
|
+
@delegate = delegate
|
11
|
+
end
|
12
|
+
|
13
|
+
# Converts an argument to a sub-class of this
|
14
|
+
# delegate implementation
|
15
|
+
def self.coerce(arg)
|
16
|
+
case arg
|
17
|
+
when Delegate
|
18
|
+
arg
|
19
|
+
when Hash, Proc
|
20
|
+
Delegate.new(arg)
|
21
|
+
when Module
|
22
|
+
ModuleDelegate.new(arg)
|
23
|
+
else
|
24
|
+
if arg.respond_to?(:[])
|
25
|
+
Delegate.new(arg)
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Unable to convert #{arg} to a Name2X::Delegate", caller
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Make a fetch on from_hash
|
33
|
+
def fetch(name)
|
34
|
+
delegate[name]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Convert a name to a module
|
38
|
+
def name2module(name)
|
39
|
+
result = fetch(name)
|
40
|
+
result.kind_of?(Module) ? result : nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Convert a name to a class
|
44
|
+
def name2class(name)
|
45
|
+
result = fetch(name)
|
46
|
+
result.kind_of?(Class) ? result : nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# Convert a name to another name
|
50
|
+
def name2name(name)
|
51
|
+
result = fetch(name)
|
52
|
+
result.kind_of?(Symbol) ? result : nil
|
53
|
+
end
|
54
|
+
|
55
|
+
end # class FromHash
|
56
|
+
end # module Name2X
|
57
|
+
end # module CodeTree
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CodeTree
|
2
|
+
module Name2X
|
3
|
+
class ModuleDelegate < Delegate
|
4
|
+
|
5
|
+
# Overrides the default behavior to use const_get instead.
|
6
|
+
def fetch(name)
|
7
|
+
name = name2name(name)
|
8
|
+
delegate.const_defined?(name) ? delegate.const_get(name) : nil
|
9
|
+
rescue NameError
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
# Capitalize a name
|
14
|
+
def name2name(name)
|
15
|
+
src = name.to_s
|
16
|
+
src.gsub!(/[^a-zA-Z\s]/," ")
|
17
|
+
src = " " + src.split.join(" ")
|
18
|
+
src.gsub!(/ (.)/) { $1.upcase }
|
19
|
+
src.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class ModuleDelegate
|
23
|
+
end # module Name2X
|
24
|
+
end # module CodeTree
|
25
|
+
|
@@ -1,11 +1,38 @@
|
|
1
1
|
module CodeTree
|
2
2
|
class ProcParser
|
3
|
+
#
|
4
|
+
# Collector when :multiline option is set to true.
|
5
|
+
#
|
6
|
+
class Collector
|
7
|
+
|
8
|
+
# Which nodes are kept?
|
9
|
+
attr_reader :kept
|
10
|
+
|
11
|
+
# Creates a collector instance
|
12
|
+
def initialize
|
13
|
+
@kept = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Callback when a new Expr is created
|
17
|
+
def built(who, children)
|
18
|
+
@kept << who
|
19
|
+
children.each{|c| @kept.delete_if{|k| k.__id__ == c.__id__}}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns expressions as an array of AstNodes
|
23
|
+
def to_a
|
24
|
+
kept.collect{|c| c.__to_functional_code}
|
25
|
+
end
|
26
|
+
|
27
|
+
end # class Collector
|
28
|
+
|
29
|
+
# An expression
|
3
30
|
class Expr
|
4
31
|
|
5
32
|
# Methods that we keep
|
6
33
|
KEPT_METHODS = [ "__send__", "__id__", "instance_eval", "initialize", "object_id",
|
7
34
|
"singleton_method_added", "singleton_method_undefined", "method_missing",
|
8
|
-
"__evaluate__", "coerce"]
|
35
|
+
"__evaluate__", "coerce", "kind_of?"]
|
9
36
|
|
10
37
|
class << self
|
11
38
|
def __clean_scope__
|
@@ -18,14 +45,17 @@ module CodeTree
|
|
18
45
|
end
|
19
46
|
|
20
47
|
# Creates an expression instance
|
21
|
-
def initialize(name
|
48
|
+
def initialize(name, children, collector)
|
22
49
|
class << self; __clean_scope__; end
|
23
|
-
@name, @children = name, children
|
50
|
+
@name, @children, @collector = name, children, collector
|
51
|
+
if @collector and @name
|
52
|
+
@collector.built(self, children)
|
53
|
+
end
|
24
54
|
end
|
25
55
|
|
26
56
|
# Returns the associated functional code
|
27
57
|
def __to_functional_code
|
28
|
-
children = @children.collect{|c|
|
58
|
+
children = (@children || []).collect{|c|
|
29
59
|
c.kind_of?(Expr) ? c.__to_functional_code : AstNode.coerce(c)
|
30
60
|
}
|
31
61
|
CodeTree::AstNode.coerce([@name, children])
|
@@ -36,15 +66,15 @@ module CodeTree
|
|
36
66
|
def method_missing(name, *args)
|
37
67
|
if @name.nil?
|
38
68
|
if name == :[]
|
39
|
-
Expr.new(:'?', args)
|
69
|
+
Expr.new(:'?', args, @collector)
|
40
70
|
elsif args.empty?
|
41
|
-
Expr.new(:'?', [name])
|
71
|
+
Expr.new(:'?', [name], @collector)
|
42
72
|
else
|
43
|
-
Expr.new(name, args)
|
73
|
+
Expr.new(name, args, @collector)
|
44
74
|
end
|
45
75
|
else
|
46
76
|
args.unshift(self)
|
47
|
-
Expr.new(name, args)
|
77
|
+
Expr.new(name, args, @collector)
|
48
78
|
end
|
49
79
|
end
|
50
80
|
|
@@ -55,15 +85,17 @@ module CodeTree
|
|
55
85
|
end # class Expr
|
56
86
|
|
57
87
|
# Parses a Proc object
|
58
|
-
def self.parse_proc(block)
|
88
|
+
def self.parse_proc(block, options = {})
|
89
|
+
collector = options[:multiline] ? Collector.new : nil
|
59
90
|
e = case block.arity
|
60
91
|
when -1, 0
|
61
|
-
Expr.new.instance_eval(&block)
|
92
|
+
Expr.new(nil, nil, collector).instance_eval(&block)
|
62
93
|
when 1
|
63
|
-
block.call(Expr.new)
|
94
|
+
block.call(Expr.new(nil, nil, collector))
|
64
95
|
else
|
65
96
|
raise ArgumentError, "Unexpected block arity #{block.arity}"
|
66
97
|
end
|
98
|
+
return collector.to_a if collector
|
67
99
|
case e
|
68
100
|
when Expr
|
69
101
|
e.__to_functional_code
|
@@ -73,15 +105,15 @@ module CodeTree
|
|
73
105
|
end
|
74
106
|
|
75
107
|
# Parses a block
|
76
|
-
def self.parse(code = nil, &block)
|
108
|
+
def self.parse(code = nil, options = {}, &block)
|
77
109
|
block = code || block
|
78
110
|
case block
|
79
111
|
when Proc
|
80
|
-
parse_proc(block)
|
112
|
+
parse_proc(block, options)
|
81
113
|
when AstNode
|
82
114
|
block
|
83
115
|
when String
|
84
|
-
parse(Kernel.eval("proc{ #{block} }"))
|
116
|
+
parse(Kernel.eval("proc{ #{block} }"), options)
|
85
117
|
else
|
86
118
|
raise ArgumentError, "Unable to parse #{block}"
|
87
119
|
end
|
@@ -1 +1,2 @@
|
|
1
|
-
require 'sbyc/codetree/producing/producer'
|
1
|
+
require 'sbyc/codetree/producing/producer'
|
2
|
+
require 'sbyc/codetree/producing/tracing_methods'
|
@@ -5,9 +5,13 @@ module CodeTree
|
|
5
5
|
# Rules kepts by node function
|
6
6
|
attr_reader :rules
|
7
7
|
|
8
|
+
# Extension options.
|
9
|
+
attr_reader :extension_options
|
10
|
+
|
8
11
|
# Creates a producer instance
|
9
12
|
def initialize(default_rules = true)
|
10
13
|
@rules = {}
|
14
|
+
@extension_options = {}
|
11
15
|
if default_rules
|
12
16
|
rule(:_) {|r,node| node.literal}
|
13
17
|
rule("*"){|r,node| r.apply(node.children)}
|
@@ -15,6 +19,14 @@ module CodeTree
|
|
15
19
|
yield(self) if block_given?
|
16
20
|
end
|
17
21
|
|
22
|
+
# Adds an extension module
|
23
|
+
def add_extension(mod, options = {})
|
24
|
+
self.extend(mod)
|
25
|
+
options = mod.send(:prepare_options, options || {}) if mod.respond_to?(:prepare_options)
|
26
|
+
extension_options[mod] = options
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
18
30
|
# Adds a rule
|
19
31
|
def rule(function_name, &block)
|
20
32
|
rules[function_name] = block
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module CodeTree
|
2
|
+
module Producing
|
3
|
+
module TracingMethods
|
4
|
+
|
5
|
+
# Returns the default options
|
6
|
+
def self.default_options
|
7
|
+
{:indent_support => true,
|
8
|
+
:indent_string => " ",
|
9
|
+
:newline_support => true,
|
10
|
+
:io => STDOUT}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Merge the default options and the options
|
14
|
+
def self.prepare_options(options)
|
15
|
+
default_options.merge(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns tracing extension options
|
19
|
+
def tracing_options
|
20
|
+
extension_options[TracingMethods] ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Is indentation support installed?
|
24
|
+
def tracing_indent_support?
|
25
|
+
!!tracing_options[:indent_support]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Is new line support installed?
|
29
|
+
def tracing_newline_support?
|
30
|
+
!!tracing_options[:newline_support]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the string to use for indentation
|
34
|
+
def tracing_indent_string
|
35
|
+
tracing_options[:indent_string] ||= " "
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the current indentation level
|
39
|
+
def tracing_indent_level
|
40
|
+
tracing_options[:indent_level] ||= 0
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the IO object (actually, any object supporting the << operator)
|
44
|
+
# that must be used for tracing the Producer.
|
45
|
+
def tracing_io
|
46
|
+
tracing_options[:io] ||= STDOUT
|
47
|
+
end
|
48
|
+
|
49
|
+
# Logs a message on the IO object, without any other side effect (no
|
50
|
+
# depth increment, for instance).
|
51
|
+
def trace(message)
|
52
|
+
message = (tracing_indent_string*tracing_indent_level + message.to_s) if tracing_indent_support? and tracing_indent_level>0
|
53
|
+
message = "#{message}\n" if tracing_newline_support?
|
54
|
+
if tracing_io.kind_of?(IO)
|
55
|
+
tracing_io << message
|
56
|
+
else
|
57
|
+
tracing_io << message
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Logs that a rule has been entered and increment the depth
|
62
|
+
# level
|
63
|
+
def trace_rule_entered(message)
|
64
|
+
trace(message)
|
65
|
+
tracing_options[:indent_level] = tracing_indent_level+1
|
66
|
+
end
|
67
|
+
|
68
|
+
# Logs that a rule has been exited and decrements the depth
|
69
|
+
# level
|
70
|
+
def trace_rule_exited(message)
|
71
|
+
tracing_options[:indent_level] = tracing_indent_level-1
|
72
|
+
trace(message)
|
73
|
+
end
|
74
|
+
|
75
|
+
end # module TracingMethods
|
76
|
+
end # module Producing
|
77
|
+
end # module CodeTree
|