maroon 0.7.1 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|