maroon 0.7.1 → 0.8.0
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 +4 -4
- data/README.md +26 -6
- data/Rakefile +3 -3
- data/Test/Context_test.rb +16 -16
- data/{Examples/Dijkstra → Test/Examples}/CalculateShortestDistance.rb +11 -13
- data/Test/Examples/MoneyTransfer_test.rb +76 -0
- data/{Examples/Dijkstra → Test/Examples}/calculate_shortest_path.rb +77 -89
- data/{Examples/Dijkstra → Test/Examples}/data.rb +0 -0
- data/{Examples/Dijkstra/dijkstra.rb → Test/Examples/dijkstra_test.rb} +34 -24
- data/Test/Examples/greeter_test.rb +48 -0
- data/{Examples/meter.rb → Test/Examples/meter_test.rb} +44 -30
- data/Test/abstract_syntax_tree_test.rb +17 -26
- data/Test/alltests.rb +1 -1
- data/Test/test_helper.rb +2 -0
- data/base/AbstractSyntaxTree.rb +24 -3
- data/base/ImmutableStack.rb +1 -1
- data/base/dependency_graph.rb +94 -0
- data/base/immutable_queue.rb +1 -1
- data/base/maroon_base.rb +50 -11
- data/base/transfomer.rb +196 -197
- data/generated/Tokens.rb +64 -2
- data/generated/build.rb +1 -3
- data/generated/maroon/kernel.rb +7 -0
- data/lib/AbstractSyntaxTree.rb +120 -0
- data/lib/AstRewritter.rb +53 -58
- data/lib/Context.rb +104 -126
- data/lib/DependencyGraph.rb +76 -0
- data/lib/ImmutableQueue.rb +28 -39
- data/lib/ImmutableStack.rb +20 -34
- data/lib/Tokens.rb +64 -2
- data/lib/Transformer.rb +125 -165
- data/lib/build.rb +2 -4
- data/lib/maroon/kernel.rb +1 -1
- data/lib/maroon/version.rb +1 -1
- metadata +13 -11
- data/Examples/MoneyTransfer.rb +0 -62
- data/Examples/greeter.rb +0 -46
- data/Test/Greeter_test_disabled.rb +0 -203
- data/lib/Production.rb +0 -149
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f663cc4db7c5aafc0525f99404cdeb2afe53f17b
|
4
|
+
data.tar.gz: b52e10404b2bcaafcc8fea74744d134235901aef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d01a342d33a30bea615cc7584868963db58b5637f7adbb82168dd0bcbaa5d7c67f335b1e9f6822215d20a82a61234978e3a9572b58784adf9f1a253c48cdfa9
|
7
|
+
data.tar.gz: 84b7561a6183480bf571a5f7027be75cde74274c11752b9f9975fb418992b97f962ca288a730f591edba16f88ba744504dae2ff27f8f8cfb57559afad885ceb2
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ See the examples for detailed information on how to use maroon.
|
|
22
22
|
|
23
23
|
Essentially you can define a context by using
|
24
24
|
|
25
|
-
Context
|
25
|
+
Context.define :context_name do
|
26
26
|
role :role_name do
|
27
27
|
print_self do |x| #notice no symbol
|
28
28
|
p "#{role_name} #use role_name to refer to the role of said name
|
@@ -34,7 +34,7 @@ end
|
|
34
34
|
|
35
35
|
If you're using Bundler, run `bundle install` to setup your environment.
|
36
36
|
|
37
|
-
Run `rake
|
37
|
+
Run `rake default` or just `rake` to make the tests run.
|
38
38
|
|
39
39
|
|
40
40
|
## Contributing
|
@@ -47,10 +47,30 @@ Run `rake test` or just `rake` to make the tests run.
|
|
47
47
|
6. Push to the branch (`git push origin my-new-feature`)
|
48
48
|
7. Create new Pull Request
|
49
49
|
|
50
|
+
All changes should be done to the code in the base folder or the test folder.
|
51
|
+
The code in the base folder is the implementation of maroon. When the default rake task is executed code will be generated
|
52
|
+
in the 'generated' folder. The code in generated will be used when running the tests.
|
53
|
+
If all tests pass
|
54
|
+
1. Copy the generated files from 'generated' to 'lib'
|
55
|
+
2. Rerun the the default rake task
|
56
|
+
3. If all tests pass copy the generated file from 'generated' to 'lib'
|
57
|
+
4. commit and create a pull request
|
58
|
+
|
59
|
+
There's a rake task (build_lib) that will do the above if you are courageous enough to potentially loose your changes.
|
50
60
|
|
51
61
|
Known bugs
|
52
|
-
There are a few known bugs. The two major once are that
|
53
|
-
|
54
|
-
|
55
|
-
|
62
|
+
1. There are a few known bugs. The two major once are that double quotes can't be used. This is due to
|
63
|
+
limitation/bug in the current version of sourcify.
|
64
|
+
2. Using 'self' in a role method points to the context itself where it should be the role player
|
65
|
+
|
66
|
+
|
56
67
|
|
68
|
+
Short description of the flow
|
69
|
+
The class named Context (defined in maroon_base.rb) will read and parse the block passed to the Context.define method
|
70
|
+
When the parsing is complete each method will be represented by an AST (using S-expressions). The transformer context
|
71
|
+
will take over from this point. In time it runs through the definition of all roles (including there methods) and interactions.
|
72
|
+
For each method it will use the AstRewritter context to rewrite the methods (e.g. call the correct method on the context
|
73
|
+
object when a role method is called). The AstRewritter is build on another context namely the AbstractSyntaxTree that is used to represent
|
74
|
+
and semantics to the abstract syntax tree (S-expressions) that represents each method.
|
75
|
+
When all methods have been rewritten the transformer will either write the corresponding class definition to file (if so specified)
|
76
|
+
or create a class in memory.
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'rake/testtask'
|
|
3
3
|
|
4
4
|
Rake::TestTask.new do |t|
|
5
5
|
t.libs << 'test'
|
6
|
-
t.test_files = FileList['test
|
6
|
+
t.test_files = FileList['test/alltests.rb']
|
7
7
|
t.verbose = true
|
8
8
|
end
|
9
9
|
|
@@ -12,8 +12,8 @@ task :generate do |t|
|
|
12
12
|
require_relative './lib/Context'
|
13
13
|
require_relative './lib/maroon/kernel'
|
14
14
|
require_relative './lib/build' #use the one in lib. That should be the stable one
|
15
|
-
Context::generate_files_in
|
16
|
-
`git ls-files ./base/`.split($/).grep(%r{(.)*.rb}).select {|f|
|
15
|
+
Context::generate_files_in=:generated #generate files not just in memory classes
|
16
|
+
`git ls-files ./base/`.split($/).grep(%r{(.)*.rb}).select {|f| require_relative("#{f}")}
|
17
17
|
end
|
18
18
|
|
19
19
|
#execute as with command line to make memory spaces independent
|
data/Test/Context_test.rb
CHANGED
@@ -11,7 +11,7 @@ class ContextTest < Test::Unit::TestCase
|
|
11
11
|
name = :MyContextRoleMethodCall
|
12
12
|
role_name = :rol
|
13
13
|
|
14
|
-
|
14
|
+
Context.define name do
|
15
15
|
role role_name do
|
16
16
|
def rolem(x, y)
|
17
17
|
x+y
|
@@ -26,20 +26,20 @@ class ContextTest < Test::Unit::TestCase
|
|
26
26
|
assert_equal(7, MyContextRoleMethodCall.new.add(3, 4))
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
29
|
+
def test_simple
|
30
30
|
name = :MyContextSimple
|
31
31
|
role_name = :r
|
32
|
-
Context
|
32
|
+
Context.define name do
|
33
33
|
role role_name do
|
34
34
|
end
|
35
35
|
end
|
36
36
|
assert(Kernel::const_defined? name)
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
39
|
+
def test_bind
|
40
40
|
name = :MyContextBind
|
41
41
|
|
42
|
-
c= Context
|
42
|
+
c= Context.define name do
|
43
43
|
role :role_name do
|
44
44
|
def sum
|
45
45
|
@sum += role_name
|
@@ -58,10 +58,10 @@ class ContextTest < Test::Unit::TestCase
|
|
58
58
|
assert_equal(3, MyContextBind.new.inter)
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
61
|
+
def test_role_method
|
62
62
|
name = :MyContext
|
63
63
|
role_name = :rol
|
64
|
-
Context
|
64
|
+
Context.define name do
|
65
65
|
role role_name do
|
66
66
|
def rolem
|
67
67
|
0+1
|
@@ -71,10 +71,10 @@ class ContextTest < Test::Unit::TestCase
|
|
71
71
|
assert_equal(1, MyContext.new.send(:self_rol_rolem))
|
72
72
|
end
|
73
73
|
|
74
|
-
def
|
74
|
+
def test_role_method_args
|
75
75
|
name = :MyContextArgs
|
76
76
|
role_name = :rol
|
77
|
-
Context
|
77
|
+
Context.define name do
|
78
78
|
role role_name do
|
79
79
|
def rolem(x, y)
|
80
80
|
x+y
|
@@ -84,10 +84,10 @@ class ContextTest < Test::Unit::TestCase
|
|
84
84
|
assert_equal(7, MyContextArgs.new.send(:self_rol_rolem, 3, 4))
|
85
85
|
end
|
86
86
|
|
87
|
-
def
|
87
|
+
def test_role_method_splat
|
88
88
|
name = :MyContextSplat
|
89
89
|
role_name = :rol
|
90
|
-
Context
|
90
|
+
Context.define name do
|
91
91
|
role role_name do
|
92
92
|
def rolem(x, *args)
|
93
93
|
x+(args[0])
|
@@ -97,10 +97,10 @@ class ContextTest < Test::Unit::TestCase
|
|
97
97
|
assert_equal(7, MyContextSplat.new.send(:self_rol_rolem, 3, 4))
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
100
|
+
def test_role_method_block
|
101
101
|
name = :MyContextBlock
|
102
102
|
role_name = :rol
|
103
|
-
c= Context
|
103
|
+
c= Context.define name do
|
104
104
|
role :num do
|
105
105
|
def next
|
106
106
|
num + 3
|
@@ -118,13 +118,13 @@ class ContextTest < Test::Unit::TestCase
|
|
118
118
|
end
|
119
119
|
end
|
120
120
|
end
|
121
|
-
assert_equal(
|
121
|
+
assert_equal(13, MyContextBlock.new.send(:self_rol_rolem, 3, 4) { |x, res| res + x })
|
122
122
|
end
|
123
123
|
|
124
|
-
def
|
124
|
+
def test_class_method_block
|
125
125
|
name = :MyContextClass
|
126
126
|
role_name = :rol
|
127
|
-
Context
|
127
|
+
Context.define name do
|
128
128
|
role :dummy do end
|
129
129
|
role role_name do
|
130
130
|
def rolem(*args, &b)
|
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
context :CalculateShortestDistance do
|
2
3
|
|
3
4
|
role :tentative_distance_values do
|
4
5
|
end
|
@@ -12,40 +13,40 @@ Context::define :CalculateShortestDistance do
|
|
12
13
|
|
13
14
|
|
14
15
|
role :map do
|
15
|
-
distance_between
|
16
|
+
def distance_between(a, b)
|
16
17
|
map.distances[Edge.new(a, b)]
|
17
18
|
end
|
18
19
|
|
19
20
|
# These two functions presume always travelling
|
20
21
|
# in a southern or easterly direction
|
21
|
-
next_down_the_street_from
|
22
|
+
def next_down_the_street_from(x)
|
22
23
|
map.east_neighbor_of x
|
23
24
|
end
|
24
25
|
|
25
|
-
next_along_the_avenue_from
|
26
|
+
def next_along_the_avenue_from(x)
|
26
27
|
map.south_neighbor_of x
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
31
|
role :current do
|
31
|
-
tentative_distance
|
32
|
+
def tentative_distance
|
32
33
|
tentative_distance_values[current]
|
33
34
|
end
|
34
|
-
set_tentative_distance_to
|
35
|
+
def set_tentative_distance_to(x)
|
35
36
|
tentative_distance_values[current] = x
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
40
|
|
40
|
-
rebind
|
41
|
+
def rebind(origin_node, geometries)
|
41
42
|
@current = origin_node
|
42
43
|
@destination = geometries.destination
|
43
44
|
@map = geometries
|
44
45
|
end
|
45
46
|
|
46
|
-
distance
|
47
|
-
current.set_tentative_distance_to
|
48
|
-
@path = CalculateShortestPath.new(current, destination, map).path
|
47
|
+
def distance
|
48
|
+
current.set_tentative_distance_to(0)
|
49
|
+
@path = CalculateShortestPath.new(current, destination, map,nil,nil,nil,nil).path
|
49
50
|
retval = 0
|
50
51
|
previous_node = nil
|
51
52
|
path.reverse_each { |node|
|
@@ -59,9 +60,6 @@ Context::define :CalculateShortestDistance do
|
|
59
60
|
retval
|
60
61
|
end
|
61
62
|
|
62
|
-
end
|
63
|
-
|
64
|
-
class CalculateShortestDistance
|
65
63
|
def initialize(origin_node, geometries)
|
66
64
|
rebind(origin_node, geometries)
|
67
65
|
@tentative_distance_values = Hash.new
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require_relative '../test_helper'
|
3
|
+
|
4
|
+
Context.define :MoneyTransfer do
|
5
|
+
def initialize(source, destination, amount)
|
6
|
+
@source = source
|
7
|
+
@destination = destination
|
8
|
+
@amount = amount
|
9
|
+
@log = []
|
10
|
+
end
|
11
|
+
|
12
|
+
role :source do
|
13
|
+
def withdraw(amount)
|
14
|
+
source.movement(amount)
|
15
|
+
source.log 'withdrawal ' + amount.to_s
|
16
|
+
end
|
17
|
+
def log(message)
|
18
|
+
@log << (@source.to_s + ' source ' + message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
role :destination do
|
23
|
+
def deposit(amount)
|
24
|
+
@destination.movement(amount)
|
25
|
+
@destination.log 'deposit ' + amount.to_s
|
26
|
+
end
|
27
|
+
def logger(message)
|
28
|
+
@log << @destination.to_s + ' destination ' + message
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
role :amount do
|
33
|
+
end
|
34
|
+
|
35
|
+
def transfer
|
36
|
+
source.withdraw -amount
|
37
|
+
destination.deposit amount
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Account
|
42
|
+
def initialize (amount, id)
|
43
|
+
@balance = amount
|
44
|
+
@account_id = id
|
45
|
+
@log = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def movement(amount)
|
49
|
+
log "Amount #{amount}"
|
50
|
+
@balance+=amount
|
51
|
+
end
|
52
|
+
|
53
|
+
def log(message)
|
54
|
+
@log << message
|
55
|
+
end
|
56
|
+
|
57
|
+
def balance
|
58
|
+
@balance
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
"balance of #{@account_id}: #{@balance}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
class MoneyTransferTest < Test::Unit::TestCase
|
68
|
+
def test_transfer
|
69
|
+
source = Account.new 1000, "source"
|
70
|
+
destination = Account.new 0, "destination"
|
71
|
+
ctx = MoneyTransfer.new source, destination, 100
|
72
|
+
ctx.transfer
|
73
|
+
assert_equal(900,source.balance)
|
74
|
+
assert_equal(100,destination.balance)
|
75
|
+
end
|
76
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Consider street corners on a Manhattan grid. We want to find the
|
4
4
|
# minimal path from the most northeast city to the most
|
5
|
-
# southeast city. Use
|
5
|
+
# southeast city. Use DijkstraTest's algorithm
|
6
6
|
#
|
7
7
|
|
8
8
|
|
@@ -29,80 +29,91 @@
|
|
29
29
|
#
|
30
30
|
# The algorithm is straight from Wikipedia:
|
31
31
|
#
|
32
|
-
# http://en.wikipedia.org/wiki/
|
32
|
+
# http://en.wikipedia.org/wiki/DijkstraTest's_algorithm
|
33
33
|
#
|
34
34
|
# and reads directly from the distance method, below
|
35
35
|
|
36
36
|
|
37
|
-
#
|
37
|
+
# Map as in cartography rather than Computer Science...
|
38
38
|
#
|
39
39
|
# Map is a DCI role. The role in this example is played by an
|
40
40
|
# object representing a particular Manhattan geometry
|
41
|
-
|
41
|
+
#Context::generate_files_in('.')
|
42
|
+
c = context :CalculateShortestPath do
|
43
|
+
# public initialize. It's overloaded so that the public version doesn't
|
44
|
+
# have to pass a lot of crap; the initialize method takes care of
|
45
|
+
# setting up internal data structures on the first invocation. On
|
46
|
+
# recursion we override the defaults
|
47
|
+
|
48
|
+
def initialize(origin_node, target_node, geometries,
|
49
|
+
path_vector, unvisited_hash, pathto_hash,
|
50
|
+
tentative_distance_values_hash)
|
51
|
+
@destination = target_node
|
52
|
+
|
53
|
+
rebind(origin_node, geometries)
|
54
|
+
|
55
|
+
execute(path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash)
|
56
|
+
end
|
57
|
+
|
58
|
+
|
42
59
|
role :distance_labeled_graph_node do
|
43
60
|
# Access to roles and other Context data
|
44
|
-
tentative_distance_values
|
61
|
+
def tentative_distance_values
|
45
62
|
tentative_distance_values
|
46
63
|
end
|
47
64
|
# Role Methods
|
48
|
-
tentative_distance
|
65
|
+
def tentative_distance
|
49
66
|
tentative_distance_values[@distance_labeled_graph_node]
|
50
67
|
end
|
51
|
-
set_tentative_distance_to
|
68
|
+
def set_tentative_distance_to(x)
|
52
69
|
tentative_distance_values[@distance_labeled_graph_node] = x
|
53
70
|
end
|
54
71
|
end
|
55
72
|
|
56
73
|
# These are handles to to the roles
|
57
74
|
role :map do
|
58
|
-
distance_between
|
59
|
-
|
60
|
-
# p "distance between #{a.name} and #{b.name} is #{dist}"
|
61
|
-
dist
|
75
|
+
def distance_between(a, b)
|
76
|
+
@map.distances[Edge.new(a, b)]
|
62
77
|
end
|
63
|
-
next_down_the_street_from
|
64
|
-
|
65
|
-
# p "next down the street from #{x.name} is #{n.name}"
|
66
|
-
n
|
78
|
+
def next_down_the_street_from(x)
|
79
|
+
east_neighbor_of x
|
67
80
|
end
|
68
|
-
next_along_the_avenue_from
|
69
|
-
|
70
|
-
# p "next along the avenue from #{x.name} is #{n.name}"
|
71
|
-
n
|
81
|
+
def next_along_the_avenue_from(x)
|
82
|
+
south_neighbor_of x
|
72
83
|
end
|
73
|
-
origin
|
84
|
+
def origin
|
74
85
|
map.root
|
75
86
|
end
|
76
|
-
nearest_unvisited_node_to_target
|
87
|
+
def nearest_unvisited_node_to_target
|
77
88
|
min = infinity
|
78
89
|
selection = nil
|
79
90
|
@unvisited.each_key {
|
80
91
|
|intersection|
|
81
92
|
bind :intersection => :distance_labeled_graph_node
|
82
|
-
if @unvisited[
|
83
|
-
tentative_distance =
|
93
|
+
if @unvisited[distance_labeled_graph_node]
|
94
|
+
tentative_distance = distance_labeled_graph_node.tentative_distance
|
84
95
|
if tentative_distance < min
|
85
|
-
|
96
|
+
|
86
97
|
min = tentative_distance
|
87
|
-
selection =
|
98
|
+
selection = distance_labeled_graph_node
|
88
99
|
end
|
89
100
|
end
|
90
101
|
}
|
91
102
|
selection
|
92
103
|
end
|
93
|
-
unvisited
|
104
|
+
def unvisited
|
94
105
|
@unvisited
|
95
106
|
end
|
96
107
|
end
|
97
108
|
|
98
109
|
role :current do
|
99
110
|
# Access to roles and other Context data
|
100
|
-
unvisited
|
111
|
+
def unvisited
|
101
112
|
map.unvisited
|
102
113
|
end
|
103
114
|
|
104
115
|
# Role Methods
|
105
|
-
unvisited_neighbors
|
116
|
+
def unvisited_neighbors
|
106
117
|
retval = Array.new
|
107
118
|
if @south_neighbor != nil
|
108
119
|
if unvisited[@south_neighbor] then
|
@@ -114,11 +125,10 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
114
125
|
retval << @east_neighbor
|
115
126
|
end
|
116
127
|
end
|
117
|
-
|
128
|
+
|
118
129
|
retval
|
119
130
|
end
|
120
|
-
tentative_distance
|
121
|
-
raise "key (#{current}) not found in #{@tentative_distance_values}" unless @tentative_distance_values && (@tentative_distance_values.has_key? current)
|
131
|
+
def tentative_distance
|
122
132
|
@tentative_distance_values[current]
|
123
133
|
end
|
124
134
|
end
|
@@ -130,34 +140,32 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
130
140
|
# east_neighbor and south_neighbor roles
|
131
141
|
|
132
142
|
role :neighbor_node do
|
133
|
-
relable_node_as
|
134
|
-
raise
|
135
|
-
raise
|
143
|
+
def relable_node_as(x)
|
144
|
+
raise 'Argument cannot be nil' unless x
|
145
|
+
raise 'self cannot be nil' unless @neighbor_node
|
136
146
|
|
137
147
|
if x < neighbor_node.tentative_distance
|
138
|
-
# p "updated tentative distance from #{neighbor_node.tentative_distance} to #{x}"
|
139
148
|
neighbor_node.set_tentative_distance_to x
|
140
149
|
:distance_was_udated
|
141
150
|
else
|
142
|
-
# p "left tentative distance at #{neighbor_node.tentative_distance} instead of #{x}"
|
143
151
|
:distance_was_not_udated
|
144
152
|
end
|
145
153
|
end
|
146
154
|
|
147
155
|
# Role Methods
|
148
|
-
tentative_distance
|
149
|
-
raise
|
156
|
+
def tentative_distance
|
157
|
+
raise 'self cannot be nil' unless @neighbor_node
|
150
158
|
tentative_distance_values[@neighbor_node]
|
151
159
|
end
|
152
|
-
set_tentative_distance_to
|
153
|
-
raise
|
154
|
-
raise
|
160
|
+
def set_tentative_distance_to(x)
|
161
|
+
raise 'Argument cannot be nil' unless x
|
162
|
+
raise 'self cannot be nil' unless @neighbor_node
|
155
163
|
tentative_distance_values[@neighbor_node] = x
|
156
164
|
end
|
157
165
|
end
|
158
166
|
# This is the method that starts the work. Called from initialize.
|
159
167
|
|
160
|
-
execute
|
168
|
+
def execute (path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash)
|
161
169
|
do_inits(path_vector, unvisited_hash, pathto_hash,
|
162
170
|
tentative_distance_values_hash)
|
163
171
|
|
@@ -165,21 +173,19 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
165
173
|
# Calculate tentative distances of unvisited neighbors
|
166
174
|
|
167
175
|
unvisited_neighbors = current.unvisited_neighbors
|
168
|
-
|
176
|
+
|
169
177
|
if unvisited_neighbors != nil
|
170
178
|
unvisited_neighbors.each {
|
171
179
|
|neighbor|
|
172
180
|
bind :neighbor => :neighbor_node
|
173
181
|
tentative_distance = current.tentative_distance
|
174
|
-
raise
|
182
|
+
raise 'tentative distance cannot be nil' if tentative_distance == nil
|
175
183
|
distance_between = map.distance_between(current, neighbor)
|
176
|
-
raise
|
184
|
+
raise 'distance between cannot be nil' if distance_between == nil
|
177
185
|
net_distance = tentative_distance + distance_between
|
178
186
|
|
179
|
-
if
|
180
|
-
# p "set path"
|
187
|
+
if neighbor_node.relable_node_as(net_distance) == :distance_was_udated
|
181
188
|
pathTo[neighbor] = @current
|
182
|
-
# p "path #{@pathTo}"
|
183
189
|
end
|
184
190
|
}
|
185
191
|
end
|
@@ -200,7 +206,7 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
200
206
|
end
|
201
207
|
end
|
202
208
|
|
203
|
-
do_inits
|
209
|
+
def do_inits(path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash)
|
204
210
|
|
205
211
|
# The conditional switches between the first and subsequent instances of the
|
206
212
|
# recursion (the algorithm is recursive in graph contexts)
|
@@ -213,8 +219,8 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
213
219
|
# Since path_vector isn't set up, this is the first iteration of the recursion
|
214
220
|
@tentative_distance_values = Hash.new
|
215
221
|
|
216
|
-
# This is the fundamental data structure for
|
217
|
-
#
|
222
|
+
# This is the fundamental data structure for DijkstraTest's algorithm, called
|
223
|
+
# Q in the Wikipedia description. It is a boolean hash that maps a
|
218
224
|
# node onto false or true according to whether it has been visited
|
219
225
|
|
220
226
|
@unvisited = Hash.new
|
@@ -222,13 +228,13 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
222
228
|
# These initializations are directly from the description of the algorithm
|
223
229
|
map.nodes.each { |node| @unvisited[node] = true }
|
224
230
|
@unvisited.delete(map.origin)
|
225
|
-
map.nodes.each { |node| bind :node => :distance_labeled_graph_node;
|
231
|
+
map.nodes.each { |node| bind :node => :distance_labeled_graph_node; distance_labeled_graph_node.set_tentative_distance_to(infinity) }
|
226
232
|
tentative_distance_values[map.origin] = 0
|
227
233
|
|
228
234
|
# The path array is kept in the outermost context and serves to store the
|
229
235
|
# return path. Each recurring context may add something to the array along
|
230
236
|
# the way. However, because of the nature of the algorithm, individual
|
231
|
-
# Context instances don't deliver
|
237
|
+
# Context instances don't deliver partial paths as partial answers.
|
232
238
|
@path = Array.new
|
233
239
|
|
234
240
|
# The pathTo map is a local associative array that remembers the
|
@@ -253,8 +259,8 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
253
259
|
|
254
260
|
@tentative_distance_values = Hash.new
|
255
261
|
|
256
|
-
# This is the fundamental data structure for
|
257
|
-
#
|
262
|
+
# This is the fundamental data structure for DijkstraTest's algorithm, called
|
263
|
+
# Q in the Wikipedia description. It is a boolean hash that maps a
|
258
264
|
# node onto false or true according to whether it has been visited
|
259
265
|
|
260
266
|
@unvisited = Hash.new
|
@@ -262,11 +268,10 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
262
268
|
# These initializations are directly from the description of the algorithm
|
263
269
|
map.nodes.each { |node| @unvisited[node] = true }
|
264
270
|
@unvisited.delete(map.origin)
|
265
|
-
|
271
|
+
|
266
272
|
map.nodes.each { |node|
|
267
|
-
bind :node => :distance_labeled_graph_node
|
268
|
-
|
269
|
-
# p "initialized node #{node.name}"
|
273
|
+
bind :node => :distance_labeled_graph_node
|
274
|
+
distance_labeled_graph_node.set_tentative_distance_to(infinity)
|
270
275
|
}
|
271
276
|
tentative_distance_values[map.origin] = 0
|
272
277
|
|
@@ -274,7 +279,7 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
274
279
|
# The path array is kept in the outermost context and serves to store the
|
275
280
|
# return path. Each recurring context may add something to the array along
|
276
281
|
# the way. However, because of the nature of the algorithm, individual
|
277
|
-
# Context instances don't deliver
|
282
|
+
# Context instances don't deliver partial paths as partial answers.
|
278
283
|
|
279
284
|
@path = Array.new
|
280
285
|
|
@@ -295,9 +300,18 @@ ctx, source = Context::define :CalculateShortestPath do
|
|
295
300
|
@pathTo = pathto_hash
|
296
301
|
end
|
297
302
|
end
|
298
|
-
end
|
299
303
|
|
300
|
-
|
304
|
+
|
305
|
+
def each
|
306
|
+
path.each { |node| yield node }
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
def path
|
311
|
+
@path
|
312
|
+
end
|
313
|
+
|
314
|
+
private
|
301
315
|
|
302
316
|
def pathTo
|
303
317
|
@pathTo
|
@@ -311,10 +325,6 @@ class CalculateShortestPath
|
|
311
325
|
@south_neighbor
|
312
326
|
end
|
313
327
|
|
314
|
-
def path;
|
315
|
-
@path
|
316
|
-
end
|
317
|
-
|
318
328
|
def destination;
|
319
329
|
@destination
|
320
330
|
end
|
@@ -337,27 +347,6 @@ class CalculateShortestPath
|
|
337
347
|
@south_neighbor = map.south_neighbor_of(origin_node)
|
338
348
|
end
|
339
349
|
|
340
|
-
|
341
|
-
# public initialize. It's overloaded so that the public version doesn't
|
342
|
-
# have to pass a lot of crap; the initialize method takes care of
|
343
|
-
# setting up internal data structures on the first invocation. On
|
344
|
-
# recursion we override the defaults
|
345
|
-
|
346
|
-
def initialize(origin_node, target_node, geometries,
|
347
|
-
path_vector = nil, unvisited_hash = nil, pathto_hash = nil,
|
348
|
-
tentative_distance_values_hash = nil)
|
349
|
-
@destination = target_node
|
350
|
-
|
351
|
-
rebind(origin_node, geometries)
|
352
|
-
|
353
|
-
execute(path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash)
|
354
|
-
end
|
355
|
-
|
356
|
-
def each
|
357
|
-
path.each { |node| yield node }
|
358
|
-
end
|
359
|
-
|
360
|
-
|
361
350
|
# This method does a simple traversal of the data structures (following pathTo)
|
362
351
|
# to build the directed traversal vector for the minimum path
|
363
352
|
|
@@ -370,5 +359,4 @@ class CalculateShortestPath
|
|
370
359
|
end
|
371
360
|
end
|
372
361
|
|
373
|
-
|
374
|
-
|
362
|
+
p c
|