maroon 0.6.1 → 0.6.5
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.
- 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
|