maroon 0.6.1 → 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Examples/Dijkstra/CalculateShortestDistance.rb +16 -12
- data/Examples/Dijkstra/calculate_shortest_path.rb +47 -27
- data/Examples/Dijkstra/data.rb +41 -14
- data/Examples/Dijkstra/dijkstra.rb +4 -3
- data/Examples/MoneyTransfer.rb +61 -60
- data/Examples/greeter.rb +8 -7
- data/Examples/meter.rb +35 -29
- data/Gemfile +9 -4
- data/LICENSE.txt +21 -21
- data/README.md +13 -0
- data/Rakefile +41 -1
- data/Test/Generate/method_info_test.rb +12 -0
- data/Test/{Greeter_test.rb → Greeter_test_disabled.rb} +24 -20
- data/Test/ImmutableQueue_test.rb +18 -0
- data/Test/MethodInfo_test.rb +65 -0
- data/Test/alltests.rb +1 -0
- data/Test/{source_assertions.rb → assertions.rb} +15 -7
- data/Test/bind_test.rb +13 -0
- data/Test/expression_test.rb +105 -0
- data/Test/method_call_test.rb +83 -0
- data/Test/self_test.rb +46 -0
- data/Test/stack_test.rb +17 -0
- data/Test/test_helper.rb +14 -0
- data/base/ImmutableStack.rb +32 -0
- data/base/MethodDefinition.rb +124 -0
- data/base/bind_rewriter.rb +58 -0
- data/base/immutable_queue.rb +50 -0
- data/base/maroon_base.rb +267 -0
- data/base/method_call.rb +78 -0
- data/base/method_info.rb +86 -0
- data/base/self.rb +60 -0
- data/generated/build.rb +13 -0
- data/generated/interpretation_context.rb +29 -0
- data/lib/Bind.rb +65 -0
- data/lib/Context.rb +187 -0
- data/lib/ImmutableQueue.rb +50 -0
- data/lib/ImmutableStack.rb +38 -0
- data/lib/MethodCall.rb +91 -0
- data/lib/MethodDefinition.rb +114 -0
- data/lib/MethodInfo.rb +78 -0
- data/lib/Self.rb +71 -0
- data/lib/build.rb +14 -0
- data/lib/interpretation_context.rb +30 -0
- data/lib/maroon/contracts.rb +43 -0
- data/lib/maroon/kernel.rb +2 -0
- data/lib/maroon/version.rb +3 -3
- data/maroon.gemspec +26 -26
- metadata +49 -31
- data/lib/Source_cleaner.rb +0 -34
- data/lib/maroon.rb +0 -165
- data/lib/rewriter.rb +0 -185
data/lib/maroon/kernel.rb
CHANGED
data/lib/maroon/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module Maroon
|
2
|
-
VERSION = '0.6.
|
3
|
-
end
|
1
|
+
module Maroon
|
2
|
+
VERSION = '0.6.5'
|
3
|
+
end
|
data/maroon.gemspec
CHANGED
@@ -1,26 +1,26 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'maroon/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |gem|
|
7
|
-
gem.name
|
8
|
-
gem.version
|
9
|
-
gem.authors
|
10
|
-
gem.email
|
11
|
-
gem.description
|
12
|
-
the first language to support injectionless DCI.
|
13
|
-
|
14
|
-
The performance of code written using maroon is on par with code using regular method invocation.
|
15
|
-
|
16
|
-
For examples on how to use maroon look at the examples found at the home page}
|
17
|
-
gem.summary
|
18
|
-
gem.homepage
|
19
|
-
|
20
|
-
gem.files
|
21
|
-
gem.executables
|
22
|
-
gem.test_files
|
23
|
-
gem.require_paths = ["lib"]
|
24
|
-
gem.add_runtime_dependency 'sourcify', '~>0.3', '>=0.3.10'
|
25
|
-
gem.add_runtime_dependency 'sorcerer'
|
26
|
-
end
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'maroon/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'maroon'
|
8
|
+
gem.version = Maroon::VERSION
|
9
|
+
gem.authors = ['Rune Funch Søltoft']
|
10
|
+
gem.email = %w(funchsoltoft@gmail.com)
|
11
|
+
gem.description = %q{maroon makes DCI a DSL for Ruby it's mainly based on the work gone into Marvin,
|
12
|
+
the first language to support injectionless DCI.
|
13
|
+
|
14
|
+
The performance of code written using maroon is on par with code using regular method invocation.
|
15
|
+
|
16
|
+
For examples on how to use maroon look at the examples found at the home page}
|
17
|
+
gem.summary = %q{maroon}
|
18
|
+
gem.homepage = 'https://github.com/runefs/Moby'
|
19
|
+
|
20
|
+
gem.files = `git ls-files`.split($/)
|
21
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
22
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
23
|
+
gem.require_paths = ["lib"]
|
24
|
+
gem.add_runtime_dependency 'sourcify', '~>0.3', '>=0.3.10'
|
25
|
+
gem.add_runtime_dependency 'sorcerer'
|
26
|
+
end
|
metadata
CHANGED
@@ -1,65 +1,56 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maroon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
5
|
-
prerelease:
|
4
|
+
version: 0.6.5
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Rune Funch Søltoft
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-04-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: sourcify
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0.3'
|
22
|
-
- -
|
20
|
+
- - '>='
|
23
21
|
- !ruby/object:Gem::Version
|
24
22
|
version: 0.3.10
|
25
23
|
type: :runtime
|
26
24
|
prerelease: false
|
27
25
|
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
26
|
requirements:
|
30
27
|
- - ~>
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '0.3'
|
33
|
-
- -
|
30
|
+
- - '>='
|
34
31
|
- !ruby/object:Gem::Version
|
35
32
|
version: 0.3.10
|
36
33
|
- !ruby/object:Gem::Dependency
|
37
34
|
name: sorcerer
|
38
35
|
requirement: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
36
|
requirements:
|
41
|
-
- -
|
37
|
+
- - '>='
|
42
38
|
- !ruby/object:Gem::Version
|
43
39
|
version: '0'
|
44
40
|
type: :runtime
|
45
41
|
prerelease: false
|
46
42
|
version_requirements: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
43
|
requirements:
|
49
|
-
- -
|
44
|
+
- - '>='
|
50
45
|
- !ruby/object:Gem::Version
|
51
46
|
version: '0'
|
52
|
-
description:
|
53
|
-
into Marvin,
|
54
|
-
|
47
|
+
description: |-
|
48
|
+
maroon makes DCI a DSL for Ruby it's mainly based on the work gone into Marvin,
|
55
49
|
the first language to support injectionless DCI.
|
56
50
|
|
51
|
+
The performance of code written using maroon is on par with code using regular method invocation.
|
57
52
|
|
58
|
-
|
59
|
-
invocation.
|
60
|
-
|
61
|
-
|
62
|
-
For examples on how to use maroon look at the examples found at the home page'
|
53
|
+
For examples on how to use maroon look at the examples found at the home page
|
63
54
|
email:
|
64
55
|
- funchsoltoft@gmail.com
|
65
56
|
executables: []
|
@@ -78,36 +69,63 @@ files:
|
|
78
69
|
- LICENSE.txt
|
79
70
|
- README.md
|
80
71
|
- Rakefile
|
81
|
-
- Test/
|
82
|
-
- Test/
|
83
|
-
-
|
84
|
-
-
|
72
|
+
- Test/Generate/method_info_test.rb
|
73
|
+
- Test/Greeter_test_disabled.rb
|
74
|
+
- Test/ImmutableQueue_test.rb
|
75
|
+
- Test/MethodInfo_test.rb
|
76
|
+
- Test/alltests.rb
|
77
|
+
- Test/assertions.rb
|
78
|
+
- Test/bind_test.rb
|
79
|
+
- Test/expression_test.rb
|
80
|
+
- Test/method_call_test.rb
|
81
|
+
- Test/self_test.rb
|
82
|
+
- Test/stack_test.rb
|
83
|
+
- Test/test_helper.rb
|
84
|
+
- base/ImmutableStack.rb
|
85
|
+
- base/MethodDefinition.rb
|
86
|
+
- base/bind_rewriter.rb
|
87
|
+
- base/immutable_queue.rb
|
88
|
+
- base/maroon_base.rb
|
89
|
+
- base/method_call.rb
|
90
|
+
- base/method_info.rb
|
91
|
+
- base/self.rb
|
92
|
+
- generated/build.rb
|
93
|
+
- generated/interpretation_context.rb
|
94
|
+
- lib/Bind.rb
|
95
|
+
- lib/Context.rb
|
96
|
+
- lib/ImmutableQueue.rb
|
97
|
+
- lib/ImmutableStack.rb
|
98
|
+
- lib/MethodCall.rb
|
99
|
+
- lib/MethodDefinition.rb
|
100
|
+
- lib/MethodInfo.rb
|
101
|
+
- lib/Self.rb
|
102
|
+
- lib/build.rb
|
103
|
+
- lib/interpretation_context.rb
|
104
|
+
- lib/maroon/contracts.rb
|
85
105
|
- lib/maroon/kernel.rb
|
86
106
|
- lib/maroon/version.rb
|
87
|
-
- lib/rewriter.rb
|
88
107
|
- maroon.gemspec
|
89
|
-
homepage: https://github.com/runefs/
|
108
|
+
homepage: https://github.com/runefs/Moby
|
90
109
|
licenses: []
|
110
|
+
metadata: {}
|
91
111
|
post_install_message:
|
92
112
|
rdoc_options: []
|
93
113
|
require_paths:
|
94
114
|
- lib
|
95
115
|
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
-
none: false
|
97
116
|
requirements:
|
98
|
-
- -
|
117
|
+
- - '>='
|
99
118
|
- !ruby/object:Gem::Version
|
100
119
|
version: '0'
|
101
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
-
none: false
|
103
121
|
requirements:
|
104
|
-
- -
|
122
|
+
- - '>='
|
105
123
|
- !ruby/object:Gem::Version
|
106
124
|
version: '0'
|
107
125
|
requirements: []
|
108
126
|
rubyforge_project:
|
109
|
-
rubygems_version:
|
127
|
+
rubygems_version: 2.0.0
|
110
128
|
signing_key:
|
111
|
-
specification_version:
|
129
|
+
specification_version: 4
|
112
130
|
summary: maroon
|
113
131
|
test_files: []
|
data/lib/Source_cleaner.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'sourcify'
|
2
|
-
require 'sorcerer'
|
3
|
-
|
4
|
-
module Source_cleaner
|
5
|
-
private
|
6
|
-
|
7
|
-
#cleans up the string for further processing and separates arguments from body
|
8
|
-
def block2source(method_name, &block)
|
9
|
-
source = block.to_sexp
|
10
|
-
raise 'unknown format' unless source[0] == :iter or source.length != 4
|
11
|
-
args = get_args source[2]
|
12
|
-
body = source[3]
|
13
|
-
return args, body
|
14
|
-
end
|
15
|
-
|
16
|
-
def get_args(sexp)
|
17
|
-
return nil unless sexp
|
18
|
-
return sexp[1] if sexp[0] == :lasgn
|
19
|
-
sexp = sexp[1][1..-1] # array or arguments
|
20
|
-
args = []
|
21
|
-
sexp.each { |e|
|
22
|
-
args << e[1]
|
23
|
-
}
|
24
|
-
args.join(',')
|
25
|
-
end
|
26
|
-
|
27
|
-
def lambda2method (method_name, method)
|
28
|
-
arguments, body = method.arguments, method.body
|
29
|
-
transform_ast body
|
30
|
-
block = Ruby2Ruby.new.process(body)
|
31
|
-
args = "(#{arguments})" if arguments
|
32
|
-
"\ndef #{method_name} #{args}\n#{block} end\n"
|
33
|
-
end
|
34
|
-
end
|
data/lib/maroon.rb
DELETED
@@ -1,165 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
require './lib/Source_cleaner.rb'
|
3
|
-
require './lib/rewriter.rb'
|
4
|
-
|
5
|
-
|
6
|
-
class Method_info
|
7
|
-
def initialize(arguments,body)
|
8
|
-
@arguments = arguments
|
9
|
-
@body = body
|
10
|
-
end
|
11
|
-
attr_reader :arguments
|
12
|
-
attr_reader :body
|
13
|
-
end
|
14
|
-
|
15
|
-
##
|
16
|
-
# The Context class is used to define a DCI context with roles and their role methods
|
17
|
-
# to define a context call define with the name of the context (this name will become the name of the class that defines the context)
|
18
|
-
# the name should be a symbol and since it's going to be used as a class name, use class naming conventions
|
19
|
-
# follow the name with a block. With in this block you can define roles and interactions
|
20
|
-
# and interaction is defined by write the name of the interaction (hello in the below example) followed by a block
|
21
|
-
# the block will become the method body
|
22
|
-
# a role can be defined much like a context. instead of calling the define method call the role method followed by the role name (as a symbol)
|
23
|
-
# the role will be used for a private instance variable and the naming convention should match this
|
24
|
-
# With in the block supplied to the role method you can define role methods the same way as you define interactions. See the method who
|
25
|
-
# in the below example
|
26
|
-
# = Example
|
27
|
-
# Context::define :Greeter do
|
28
|
-
# role :who do
|
29
|
-
# say do
|
30
|
-
# @who #could be self as well to refer to the current role player of the 'who' role
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
# greeting do
|
34
|
-
# p "Hello #{who.say}!"
|
35
|
-
# end
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# class Greeter
|
39
|
-
# def initialize(player)
|
40
|
-
# #@who = player
|
41
|
-
# end
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# Greeter.new('world').greeting #Will print "Hello world!"
|
45
|
-
#maroon is base on Marvin which was the first injectionless language for DCI
|
46
|
-
#being injectionless there's no runtime extend or anything else impacting the performance. There' only regular method invocation even when using role methods
|
47
|
-
#Author:: Rune Funch Søltoft (funchsoltoft@gmail.com)
|
48
|
-
#License:: Same as for Ruby
|
49
|
-
##
|
50
|
-
class Context
|
51
|
-
include Rewriter,Source_cleaner
|
52
|
-
@roles
|
53
|
-
@interactions
|
54
|
-
@defining_role
|
55
|
-
@role_alias
|
56
|
-
@alias_list
|
57
|
-
@cached_roles_and_alias_list
|
58
|
-
|
59
|
-
#define is the only exposed method and can be used to define a context (class)
|
60
|
-
#if maroon/kernel is required calling context of Context::define are equivalent
|
61
|
-
#params
|
62
|
-
#name:: the name of the context. Since this is used as the name of a class, class naming convetions should be used
|
63
|
-
#block:: the body of the context. Can include definitions of roles (through the role method) or definitions of interactions
|
64
|
-
#by simply calling a method with the name of the interaction and passing a block as the body of the interaction
|
65
|
-
def self.define(*args, &block)
|
66
|
-
name,base_class,default_interaction = *args
|
67
|
-
#if there's two arguments and the second is not a class it must be an interaction
|
68
|
-
if default_interaction && (!base_class.instance_of? Class) then base_class = eval(base_class.to_s) end
|
69
|
-
base_class,default_interaction = default_interaction, base_class if base_class and !default_interaction and !base_class.instance_of? Class
|
70
|
-
ctx = Context.new
|
71
|
-
ctx.instance_eval &block
|
72
|
-
return ctx.send(:finalize, name,base_class,default_interaction)
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
##
|
77
|
-
#Defines a role with the given name
|
78
|
-
#role methods can be defined inside a block passed to this method
|
79
|
-
# = Example
|
80
|
-
# role :who do
|
81
|
-
# say do
|
82
|
-
# p @who
|
83
|
-
# end
|
84
|
-
# end
|
85
|
-
#The above code defines a role called 'who' with a role method called say
|
86
|
-
##
|
87
|
-
def role(role_name)
|
88
|
-
raise 'Argument role_name must be a symbol' unless role_name.instance_of? Symbol
|
89
|
-
|
90
|
-
@defining_role = role_name
|
91
|
-
@roles[role_name] = Hash.new
|
92
|
-
yield if block_given?
|
93
|
-
@defining_role = nil
|
94
|
-
end
|
95
|
-
|
96
|
-
def initialize
|
97
|
-
@roles = Hash.new
|
98
|
-
@interactions = Hash.new
|
99
|
-
@role_alias = Array.new
|
100
|
-
end
|
101
|
-
|
102
|
-
def methods
|
103
|
-
(@defining_role ? @roles[@defining_role] : @interactions)
|
104
|
-
end
|
105
|
-
|
106
|
-
def finalize(name, base_class, default)
|
107
|
-
c = base_class ? (Class.new base_class) : Class.new
|
108
|
-
Kernel.const_set name, c
|
109
|
-
code = ''
|
110
|
-
fields = ''
|
111
|
-
getters = ''
|
112
|
-
impl = ''
|
113
|
-
interactions = ''
|
114
|
-
@interactions.each do |method_name, method|
|
115
|
-
@defining_role = nil
|
116
|
-
interactions << " #{lambda2method(method_name, method)}"
|
117
|
-
end
|
118
|
-
if default
|
119
|
-
interactions <<"
|
120
|
-
def self.call(*args)
|
121
|
-
arity =#{name}.method(:new).arity
|
122
|
-
newArgs = args[0..arity-1]
|
123
|
-
p \"new \#{newArgs}\"
|
124
|
-
obj = #{name}.new *newArgs
|
125
|
-
if arity < args.length
|
126
|
-
methodArgs = args[arity..-1]
|
127
|
-
p \"method \#{methodArgs}\"
|
128
|
-
obj.#{default} *methodArgs
|
129
|
-
else
|
130
|
-
obj.#{default}
|
131
|
-
end
|
132
|
-
end
|
133
|
-
"
|
134
|
-
interactions <<"\ndef call(*args);#{default} *args; end\n"
|
135
|
-
end
|
136
|
-
|
137
|
-
@roles.each do |role, methods|
|
138
|
-
fields << "@#{role}\n"
|
139
|
-
getters << "def #{role};@#{role} end\n"
|
140
|
-
|
141
|
-
methods.each do |method_name, method_source|
|
142
|
-
@defining_role = role
|
143
|
-
rewritten_method_name = "self_#{role}_#{method_name}"
|
144
|
-
definition = lambda2method rewritten_method_name, method_source
|
145
|
-
impl << " #{definition}" if definition
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
code << "#{interactions}\n#{fields}\n private\n#{getters}\n#{impl}\n"
|
150
|
-
|
151
|
-
complete = "class #{name}\r\n#{code}\r\nend"
|
152
|
-
#File.open("#{name}_generated.rb", 'w') {|f| f.write(complete) }
|
153
|
-
temp = c.class_eval(code)
|
154
|
-
return (temp ||c),complete
|
155
|
-
end
|
156
|
-
|
157
|
-
def role_or_interaction_method(method_name,*args, &b)
|
158
|
-
raise "method with out block #{method_name}" unless b
|
159
|
-
|
160
|
-
args, body = block2source method_name, &b
|
161
|
-
methods[method_name] = Method_info.new args,body
|
162
|
-
end
|
163
|
-
|
164
|
-
alias method_missing role_or_interaction_method
|
165
|
-
end
|
data/lib/rewriter.rb
DELETED
@@ -1,185 +0,0 @@
|
|
1
|
-
require 'Ripper'
|
2
|
-
|
3
|
-
module Rewriter
|
4
|
-
private
|
5
|
-
def role_aliases
|
6
|
-
@alias_list if @alias_list
|
7
|
-
@alias_list = Hash.new
|
8
|
-
@role_alias.each { |aliases|
|
9
|
-
aliases.each { |k, v|
|
10
|
-
@alias_list[k] = v
|
11
|
-
}
|
12
|
-
}
|
13
|
-
@alias_list
|
14
|
-
end
|
15
|
-
|
16
|
-
def roles
|
17
|
-
@cached_roles_and_alias_list if @cached_roles_and_alias_list
|
18
|
-
@roles unless @role_alias and @role_alias.length
|
19
|
-
@cached_roles_and_alias_list = Hash.new
|
20
|
-
@roles.each { |k, v|
|
21
|
-
@cached_roles_and_alias_list[k] = v
|
22
|
-
}
|
23
|
-
role_aliases.each { |k, v|
|
24
|
-
@cached_roles_and_alias_list[k] = @roles[v]
|
25
|
-
}
|
26
|
-
@cached_roles_and_alias_list
|
27
|
-
end
|
28
|
-
|
29
|
-
def add_alias (a, role_name)
|
30
|
-
@cached_roles_and_alias_list, @alias_list = nil
|
31
|
-
@role_alias.last()[a] = role_name
|
32
|
-
end
|
33
|
-
|
34
|
-
def role_method_call(ast, method)
|
35
|
-
is_call_expression = ast && ast[0] == :call
|
36
|
-
self_is_instance_expression = is_call_expression && (!ast[1]) #implicit self
|
37
|
-
is_in_block = ast && ast[0] == :lvar
|
38
|
-
role_name_index = self_is_instance_expression ? 2 : 1
|
39
|
-
role = (self_is_instance_expression || is_in_block) ? roles[ast[role_name_index]] : nil #is it a call to a role getter
|
40
|
-
is_role_method = role && role.has_key?(method)
|
41
|
-
role_name = is_in_block ? role_aliases[ast[1]] : (ast[2] if self_is_instance_expression)
|
42
|
-
role_name if is_role_method #return role name
|
43
|
-
end
|
44
|
-
|
45
|
-
##
|
46
|
-
#Test if there's a block that needs to potentially be transformed
|
47
|
-
##
|
48
|
-
def transform_block(exp)
|
49
|
-
if exp && exp[0] == :iter
|
50
|
-
(exp.length-1).times do |i|
|
51
|
-
expr = exp[i+1]
|
52
|
-
#find the block
|
53
|
-
if expr && expr.length && expr[0] == :block
|
54
|
-
transform_ast exp if rewrite_bind? expr, expr[1]
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
##
|
61
|
-
#Calls rewrite_block if needed and will return true if the AST was changed otherwise false
|
62
|
-
##
|
63
|
-
def rewrite_bind?(block, expr)
|
64
|
-
changed = false
|
65
|
-
#check if the first call is a bind call
|
66
|
-
if expr && expr.length && (expr[0] == :call && expr[1] == nil && expr[2] == :bind)
|
67
|
-
argument_list = expr[3]
|
68
|
-
if argument_list && argument_list[0] == :arglist
|
69
|
-
arguments = argument_list[1]
|
70
|
-
if arguments && arguments[0] == :hash
|
71
|
-
block.delete_at 1
|
72
|
-
count = (arguments.length-1) / 2
|
73
|
-
(1..count).each do |j|
|
74
|
-
temp = j * 2
|
75
|
-
local = arguments[temp-1][1]
|
76
|
-
if local.instance_of? Sexp
|
77
|
-
local = local[1]
|
78
|
-
end
|
79
|
-
raise 'invalid value for role alias' unless local.instance_of? Symbol
|
80
|
-
#find the name of the role being bound to
|
81
|
-
aliased_role = arguments[temp][1]
|
82
|
-
if aliased_role.instance_of? Sexp
|
83
|
-
aliased_role = aliased_role[1]
|
84
|
-
end
|
85
|
-
raise "#{aliased_role} used in binding is an unknown role #{roles}" unless aliased_role.instance_of? Symbol and @roles.has_key? aliased_role
|
86
|
-
add_alias local, aliased_role
|
87
|
-
#replace bind call with assignment of iteration variable to role field
|
88
|
-
do_rewrite_bind(aliased_role, local, block)
|
89
|
-
changed = true
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
changed
|
95
|
-
end
|
96
|
-
|
97
|
-
##
|
98
|
-
#removes call to bind in a block
|
99
|
-
#and replaces it with assignment to the proper role player local variables
|
100
|
-
#in the end of the block the local variables have their original values reassigned
|
101
|
-
def do_rewrite_bind(aliased_role, local, block)
|
102
|
-
raise 'aliased_role must be a Symbol' unless aliased_role.instance_of? Symbol
|
103
|
-
raise 'local must be a Symbol' unless local.instance_of? Symbol
|
104
|
-
aliased_field = "@#{aliased_role}".to_sym
|
105
|
-
assignment = Sexp.new
|
106
|
-
assignment[0] = :iasgn
|
107
|
-
assignment[1] = aliased_field
|
108
|
-
load_arg = Sexp.new
|
109
|
-
load_arg[0] = :lvar
|
110
|
-
load_arg[1] = local
|
111
|
-
assignment[2] = load_arg
|
112
|
-
block.insert 1, assignment
|
113
|
-
|
114
|
-
# assign role player to temp
|
115
|
-
temp_symbol = "temp____#{aliased_role}".to_sym
|
116
|
-
assignment = Sexp.new
|
117
|
-
assignment[0] = :lasgn
|
118
|
-
assignment[1] = temp_symbol
|
119
|
-
load_field = Sexp.new
|
120
|
-
load_field[0] = :ivar
|
121
|
-
load_field[1] = aliased_field
|
122
|
-
assignment[2] = load_field
|
123
|
-
block.insert 1, assignment
|
124
|
-
|
125
|
-
# reassign original player
|
126
|
-
assignment = Sexp.new
|
127
|
-
assignment[0] = :iasgn
|
128
|
-
assignment[1] = aliased_field
|
129
|
-
load_temp = Sexp.new
|
130
|
-
load_temp[0] = :lvar
|
131
|
-
load_temp[1] = temp_symbol
|
132
|
-
assignment[2] = load_temp
|
133
|
-
block[block.length] = assignment
|
134
|
-
end
|
135
|
-
|
136
|
-
# rewrites a call to self in a role method to a call to the role player accessor
|
137
|
-
# which is subsequently rewritten to a call to the instance variable itself
|
138
|
-
# in the case where no role method is called on the role player
|
139
|
-
# It's rewritten to an instance call on the context object if a role method is called
|
140
|
-
def rewrite_self (ast)
|
141
|
-
ast.length.times do |i|
|
142
|
-
raise 'Invalid argument. must be an expression' unless ast.instance_of? Sexp
|
143
|
-
exp = ast[i]
|
144
|
-
if exp == :self
|
145
|
-
ast[0] = :call
|
146
|
-
ast[1] = nil
|
147
|
-
ast[2] = @defining_role
|
148
|
-
arglist = Sexp.new
|
149
|
-
ast[3] = arglist
|
150
|
-
arglist[0] = :arglist
|
151
|
-
elsif exp.instance_of? Sexp
|
152
|
-
rewrite_self exp
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
#rewrites the ast so that role method calls are rewritten to a method invocation on the context object rather than the role player
|
158
|
-
#also does rewriting of binds in blocks
|
159
|
-
def transform_ast(ast)
|
160
|
-
if ast
|
161
|
-
if @defining_role
|
162
|
-
rewrite_self ast
|
163
|
-
end
|
164
|
-
ast.length.times do |k|
|
165
|
-
exp = ast[k]
|
166
|
-
if exp
|
167
|
-
method_name = exp[2]
|
168
|
-
role = role_method_call exp[1], exp[2]
|
169
|
-
if exp[0] == :iter
|
170
|
-
@role_alias.push Hash.new
|
171
|
-
transform_block exp
|
172
|
-
@role_alias.pop()
|
173
|
-
end
|
174
|
-
if exp[0] == :call && role
|
175
|
-
exp[1] = nil #remove call to attribute
|
176
|
-
exp[2] = "self_#{role}_#{method_name}".to_sym
|
177
|
-
end
|
178
|
-
if exp.instance_of? Sexp
|
179
|
-
transform_ast exp
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|