soroban 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +0 -1
- data/Gemfile.lock +0 -2
- data/LICENSE.txt +1 -1
- data/README.md +1 -8
- data/Soroban.gemspec +2 -6
- data/VERSION +1 -1
- data/lib/soroban/cell.rb +2 -17
- data/lib/soroban/parser/nodes.rb +0 -36
- data/lib/soroban/parser/rewrite.rb +0 -30
- data/lib/soroban/sheet.rb +0 -54
- data/spec/import_spec.rb +0 -41
- data/spec/soroban_spec.rb +0 -6
- metadata +19 -34
- data/lib/soroban/tabulator.rb +0 -34
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -147,13 +147,6 @@ s.g = "=FOO(10, 20)"
|
|
147
147
|
puts s.g # => 17
|
148
148
|
```
|
149
149
|
|
150
|
-
Compilation
|
151
|
-
-----------
|
152
|
-
|
153
|
-
Rather than interact with a `Soroban::Sheet` object at runtime, you can compile
|
154
|
-
the sheet into a Ruby or JavaScript class which you can then either save out to
|
155
|
-
a file or evaluate directly. This is slightly less flexible, but more efficient.
|
156
|
-
|
157
150
|
Contributing to Soroban
|
158
151
|
-----------------------
|
159
152
|
|
@@ -168,4 +161,4 @@ Contributing to Soroban
|
|
168
161
|
Copyright
|
169
162
|
---------
|
170
163
|
|
171
|
-
Copyright (c)
|
164
|
+
Copyright (c) 2014 Agworld Pty. Ltd. See LICENSE.txt for further details.
|
data/Soroban.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "soroban"
|
8
|
-
s.version = "0.7.
|
8
|
+
s.version = "0.7.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jason Hutchens"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2014-03-20"
|
13
13
|
s.description = "Soroban makes it easy to extract and execute formulas from Excel spreadsheets. It rewrites Excel formulas as Ruby expressions, and allows you to bind named variables to spreadsheet cells to easily manipulate inputs and capture outputs."
|
14
14
|
s.email = "jason.hutchens@agworld.com.au"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -55,7 +55,6 @@ Gem::Specification.new do |s|
|
|
55
55
|
"lib/soroban/parser/nodes.rb",
|
56
56
|
"lib/soroban/parser/rewrite.rb",
|
57
57
|
"lib/soroban/sheet.rb",
|
58
|
-
"lib/soroban/tabulator.rb",
|
59
58
|
"lib/soroban/value_walker.rb",
|
60
59
|
"spec/documentation_spec.rb",
|
61
60
|
"spec/import_spec.rb",
|
@@ -76,20 +75,17 @@ Gem::Specification.new do |s|
|
|
76
75
|
s.add_development_dependency(%q<rubyXL>, ["~> 1.2.7"])
|
77
76
|
s.add_development_dependency(%q<nokogiri>, [">= 1.4.4"])
|
78
77
|
s.add_development_dependency(%q<rubyzip>, [">= 0.9.4"])
|
79
|
-
s.add_development_dependency(%q<awesome_print>, [">= 0"])
|
80
78
|
else
|
81
79
|
s.add_dependency(%q<treetop>, ["~> 1.4.10"])
|
82
80
|
s.add_dependency(%q<rubyXL>, ["~> 1.2.7"])
|
83
81
|
s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
|
84
82
|
s.add_dependency(%q<rubyzip>, [">= 0.9.4"])
|
85
|
-
s.add_dependency(%q<awesome_print>, [">= 0"])
|
86
83
|
end
|
87
84
|
else
|
88
85
|
s.add_dependency(%q<treetop>, ["~> 1.4.10"])
|
89
86
|
s.add_dependency(%q<rubyXL>, ["~> 1.2.7"])
|
90
87
|
s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
|
91
88
|
s.add_dependency(%q<rubyzip>, [">= 0.9.4"])
|
92
|
-
s.add_dependency(%q<awesome_print>, [">= 0"])
|
93
89
|
end
|
94
90
|
end
|
95
91
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.3
|
data/lib/soroban/cell.rb
CHANGED
@@ -7,7 +7,7 @@ module Soroban
|
|
7
7
|
# representation of its contents, and the executable Ruby version of same, as
|
8
8
|
# generated via a rewrite grammar. Cells also store their dependencies.
|
9
9
|
class Cell
|
10
|
-
attr_reader :excel, :
|
10
|
+
attr_reader :excel, :dependencies
|
11
11
|
|
12
12
|
# Cells are initialised with a binding to allow formulas to be executed
|
13
13
|
# within the context of the sheet which owns the cell.
|
@@ -18,10 +18,6 @@ module Soroban
|
|
18
18
|
@value = nil
|
19
19
|
end
|
20
20
|
|
21
|
-
def to_compiled_ruby
|
22
|
-
@tree.to_compiled_ruby
|
23
|
-
end
|
24
|
-
|
25
21
|
# Set the contents of a cell, and store the executable Ruby version.
|
26
22
|
def set(contents)
|
27
23
|
contents = contents.to_s
|
@@ -30,7 +26,7 @@ module Soroban
|
|
30
26
|
@excel = contents
|
31
27
|
@tree = Soroban::parser.parse(@excel)
|
32
28
|
raise Soroban::ParseError, Soroban::parser.failure_reason if @tree.nil?
|
33
|
-
@ruby =
|
29
|
+
@ruby = @tree.to_ruby(@dependencies.clear)
|
34
30
|
end
|
35
31
|
|
36
32
|
# Clear the cached value of a cell to force it to be recalculated
|
@@ -49,17 +45,6 @@ module Soroban
|
|
49
45
|
ensure
|
50
46
|
@touched = false
|
51
47
|
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def _to_ruby
|
56
|
-
@tree.to_ruby(@dependencies.clear)
|
57
|
-
end
|
58
|
-
|
59
|
-
def _to_javascript
|
60
|
-
@tree.to_javascript(@dependencies.clear)
|
61
|
-
end
|
62
|
-
|
63
48
|
end
|
64
49
|
|
65
50
|
end
|
data/lib/soroban/parser/nodes.rb
CHANGED
@@ -4,16 +4,12 @@ module Soroban
|
|
4
4
|
def rewrite_ruby(value)
|
5
5
|
value.gsub(/^= */, '')
|
6
6
|
end
|
7
|
-
alias :compile_ruby :rewrite_ruby
|
8
7
|
end
|
9
8
|
|
10
9
|
class Identifier < Treetop::Runtime::SyntaxNode
|
11
10
|
def rewrite_ruby(value)
|
12
11
|
"@#{value}.get"
|
13
12
|
end
|
14
|
-
def compile_ruby(value)
|
15
|
-
"@cells[:#{value}].call"
|
16
|
-
end
|
17
13
|
def extract(value)
|
18
14
|
value.to_sym
|
19
15
|
end
|
@@ -23,14 +19,12 @@ module Soroban
|
|
23
19
|
def rewrite_ruby(value)
|
24
20
|
"#{value.to_f}"
|
25
21
|
end
|
26
|
-
alias :compile_ruby :rewrite_ruby
|
27
22
|
end
|
28
23
|
|
29
24
|
class FloatValue < Treetop::Runtime::SyntaxNode
|
30
25
|
def rewrite_ruby(value)
|
31
26
|
"#{value.to_f}"
|
32
27
|
end
|
33
|
-
alias :compile_ruby :rewrite_ruby
|
34
28
|
end
|
35
29
|
|
36
30
|
class Function < Treetop::Runtime::SyntaxNode
|
@@ -38,66 +32,36 @@ module Soroban
|
|
38
32
|
match = /^([^(]*)(.*)$/.match(value)
|
39
33
|
"func_#{match[1].downcase}#{match[2]}"
|
40
34
|
end
|
41
|
-
def compile_ruby(value)
|
42
|
-
match = /^([A-Z]+)\((.*)\)$/.match(value)
|
43
|
-
name, args = match[1], match[2].split(',')
|
44
|
-
case name
|
45
|
-
when 'VLOOKUP'
|
46
|
-
find, table, column, _ = args
|
47
|
-
table = table[1...-1]
|
48
|
-
column = column.to_i
|
49
|
-
table_key = "'#{table}_#{column}'"
|
50
|
-
code = []
|
51
|
-
code << "begin"
|
52
|
-
code << " @cache[#{table_key}] ||= {"
|
53
|
-
cols = Tabulator.new(table).get
|
54
|
-
lookup = Hash[cols[0].zip(cols[column-1])]
|
55
|
-
code << lookup.map do |key, val|
|
56
|
-
" @cells[:#{key}].call => @cells[:#{val}].call"
|
57
|
-
end.join(",\n")
|
58
|
-
code << " }"
|
59
|
-
code << " @cache[#{table_key}][#{find}] || 0.0"
|
60
|
-
code << " end"
|
61
|
-
code.join("\n")
|
62
|
-
else
|
63
|
-
value
|
64
|
-
end
|
65
|
-
end
|
66
35
|
end
|
67
36
|
|
68
37
|
class Pow < Treetop::Runtime::SyntaxNode
|
69
38
|
def rewrite_ruby(value)
|
70
39
|
"**"
|
71
40
|
end
|
72
|
-
alias :compile_ruby :rewrite_ruby
|
73
41
|
end
|
74
42
|
|
75
43
|
class Equal < Treetop::Runtime::SyntaxNode
|
76
44
|
def rewrite_ruby(value)
|
77
45
|
"=="
|
78
46
|
end
|
79
|
-
alias :compile_ruby :rewrite_ruby
|
80
47
|
end
|
81
48
|
|
82
49
|
class NotEqual < Treetop::Runtime::SyntaxNode
|
83
50
|
def rewrite_ruby(value)
|
84
51
|
"!="
|
85
52
|
end
|
86
|
-
alias :compile_ruby :rewrite_ruby
|
87
53
|
end
|
88
54
|
|
89
55
|
class Label < Treetop::Runtime::SyntaxNode
|
90
56
|
def rewrite_ruby(value)
|
91
57
|
value.gsub('$', '')
|
92
58
|
end
|
93
|
-
alias :compile_ruby :rewrite_ruby
|
94
59
|
end
|
95
60
|
|
96
61
|
class Range < Treetop::Runtime::SyntaxNode
|
97
62
|
def rewrite_ruby(value)
|
98
63
|
"'#{value}'"
|
99
64
|
end
|
100
|
-
alias :compile_ruby :rewrite_ruby
|
101
65
|
def extract(value)
|
102
66
|
LabelWalker.new(value).map { |label| "#{label}".to_sym }
|
103
67
|
end
|
@@ -14,40 +14,10 @@ module Treetop
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def to_compiled_ruby
|
18
|
-
if nonterminal?
|
19
|
-
value = ""
|
20
|
-
elements.each { |element| value << element.to_compiled_ruby }
|
21
|
-
compile_ruby(value)
|
22
|
-
else
|
23
|
-
compile_ruby(text_value)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_javascript(dependencies)
|
28
|
-
if nonterminal?
|
29
|
-
value = ""
|
30
|
-
elements.each { |element| value << element.to_javascript(dependencies) }
|
31
|
-
_add_dependency(dependencies, extract(value))
|
32
|
-
rewrite_javascript(value)
|
33
|
-
else
|
34
|
-
_add_dependency(dependencies, extract(text_value))
|
35
|
-
rewrite_javascript(text_value)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def compile_ruby(value)
|
40
|
-
value
|
41
|
-
end
|
42
|
-
|
43
17
|
def rewrite_ruby(value)
|
44
18
|
value
|
45
19
|
end
|
46
20
|
|
47
|
-
def rewrite_javascript(value)
|
48
|
-
value
|
49
|
-
end
|
50
|
-
|
51
21
|
def extract(value)
|
52
22
|
end
|
53
23
|
|
data/lib/soroban/sheet.rb
CHANGED
@@ -2,7 +2,6 @@ require 'soroban/helpers'
|
|
2
2
|
require 'soroban/functions'
|
3
3
|
require 'soroban/label_walker'
|
4
4
|
require 'soroban/value_walker'
|
5
|
-
require 'soroban/tabulator'
|
6
5
|
require 'soroban/cell'
|
7
6
|
|
8
7
|
require 'set'
|
@@ -17,62 +16,10 @@ module Soroban
|
|
17
16
|
def initialize(logger=nil)
|
18
17
|
@logger = logger
|
19
18
|
@cells = {}
|
20
|
-
@compiled = {}
|
21
19
|
@changes = Hash.new{ |h, k| h[k] = Set.new }
|
22
20
|
@bindings = {}
|
23
21
|
end
|
24
22
|
|
25
|
-
def factory(name)
|
26
|
-
eval(self.to_ruby(name), TOPLEVEL_BINDING)
|
27
|
-
Object::const_get('Soroban').const_get('Model').const_get(name).new
|
28
|
-
end
|
29
|
-
|
30
|
-
# Return a string containing a ruby class that implements the sheet. You can
|
31
|
-
# call eval() on this string to create the class, which you can then
|
32
|
-
# instantiate. Set inputs on the instance and read outputs off.
|
33
|
-
def to_ruby(class_name)
|
34
|
-
data = []
|
35
|
-
data << "module Soroban"
|
36
|
-
data << "module Model"
|
37
|
-
data << "class #{class_name}"
|
38
|
-
data << " def initialize"
|
39
|
-
data << " @binds = {"
|
40
|
-
data << bindings.map do |name, cell|
|
41
|
-
" '#{name}' => :#{cell}"
|
42
|
-
end.join(",\n")
|
43
|
-
data << " }"
|
44
|
-
data << " @cache = {}"
|
45
|
-
data << " @cells = {"
|
46
|
-
data << @compiled.map do |label, cell|
|
47
|
-
" :#{label} => lambda { @cache[:#{label}] ||= #{cell.to_compiled_ruby} }"
|
48
|
-
end.join(",\n")
|
49
|
-
data << " }"
|
50
|
-
data << " end"
|
51
|
-
data << " def clear"
|
52
|
-
data << " @cache.clear"
|
53
|
-
data << " end"
|
54
|
-
data << " def get(name)"
|
55
|
-
data << " @cells[@binds[name]].call"
|
56
|
-
data << " end"
|
57
|
-
data << " def set(name, value)"
|
58
|
-
data << " self.clear"
|
59
|
-
data << " @cells[@binds[name]] = lambda { @cache[@binds[name]] ||= value }"
|
60
|
-
data << " end"
|
61
|
-
bindings.each do |name, cell|
|
62
|
-
data << " def #{name}"
|
63
|
-
data << " get('#{name}')"
|
64
|
-
data << " end"
|
65
|
-
data << " def #{name}=(value)"
|
66
|
-
data << " set('#{name}', value)"
|
67
|
-
data << " end"
|
68
|
-
end
|
69
|
-
data << "end"
|
70
|
-
data << "end"
|
71
|
-
data << "end"
|
72
|
-
puts data.join("\n")
|
73
|
-
data.join("\n")
|
74
|
-
end
|
75
|
-
|
76
23
|
# Used for calling dynamically defined functions, and for creating new
|
77
24
|
# cells (via `label=`).
|
78
25
|
def method_missing(method, *args, &block)
|
@@ -180,7 +127,6 @@ module Soroban
|
|
180
127
|
internal = "@#{label}"
|
181
128
|
_expose(internal, label)
|
182
129
|
cell = Cell.new(binding)
|
183
|
-
@compiled[label] = cell
|
184
130
|
_set(label, cell, contents)
|
185
131
|
instance_variable_set(internal, cell)
|
186
132
|
end
|
data/spec/import_spec.rb
CHANGED
@@ -28,47 +28,6 @@ describe "Documentation", :if => has_rubyxl do
|
|
28
28
|
puts s.force # => 710.044826106394
|
29
29
|
s.force.should be_within(0.01).of(710.04)
|
30
30
|
|
31
|
-
require 'benchmark'
|
32
|
-
|
33
|
-
i_time = Benchmark.realtime do
|
34
|
-
1000.times do
|
35
|
-
s.planet = 'Earth'
|
36
|
-
s.mass = 80
|
37
|
-
s.force
|
38
|
-
s.planet = 'Venus'
|
39
|
-
s.mass = 80
|
40
|
-
s.force
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
puts "Interpreted Time: #{i_time}"
|
45
|
-
|
46
|
-
eval(s.to_ruby("Test"))
|
47
|
-
model = Soroban::Model::Test.new
|
48
|
-
|
49
|
-
model.planet = 'Earth'
|
50
|
-
model.mass = 80
|
51
|
-
model.force.should be_within(0.01).of(783.46)
|
52
|
-
|
53
|
-
model.planet = 'Venus'
|
54
|
-
model.mass = 80
|
55
|
-
model.force.should be_within(0.01).of(710.04)
|
56
|
-
|
57
|
-
c_time = Benchmark.realtime do
|
58
|
-
1000.times do
|
59
|
-
model.planet = 'Earth'
|
60
|
-
model.mass = 80
|
61
|
-
model.force
|
62
|
-
model.planet = 'Venus'
|
63
|
-
model.mass = 80
|
64
|
-
model.force
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
puts "Compiled Time: #{c_time}"
|
69
|
-
|
70
|
-
(10.0 * c_time).should be < i_time
|
71
|
-
|
72
31
|
end
|
73
32
|
|
74
33
|
end
|
data/spec/soroban_spec.rb
CHANGED
@@ -61,12 +61,6 @@ describe "Soroban" do
|
|
61
61
|
sheet.bindings.keys.should include :output
|
62
62
|
sheet.bindings.values.should include :A1
|
63
63
|
sheet.bindings.values.should include :A2
|
64
|
-
|
65
|
-
model = sheet.factory('Test')
|
66
|
-
model.input = 5
|
67
|
-
model.output.should eq(25)
|
68
|
-
model.input = 4
|
69
|
-
model.output.should eq(16)
|
70
64
|
end
|
71
65
|
|
72
66
|
it "can bind variables to ranges" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soroban
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 0.7.
|
9
|
+
- 3
|
10
|
+
version: 0.7.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jason Hutchens
|
@@ -15,10 +15,11 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2014-03-20 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
|
21
|
+
name: treetop
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
22
23
|
none: false
|
23
24
|
requirements:
|
24
25
|
- - ~>
|
@@ -29,12 +30,12 @@ dependencies:
|
|
29
30
|
- 4
|
30
31
|
- 10
|
31
32
|
version: 1.4.10
|
32
|
-
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
name: treetop
|
35
33
|
prerelease: false
|
34
|
+
type: :runtime
|
35
|
+
requirement: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
|
37
|
+
name: rubyXL
|
38
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
38
39
|
none: false
|
39
40
|
requirements:
|
40
41
|
- - ~>
|
@@ -45,12 +46,12 @@ dependencies:
|
|
45
46
|
- 2
|
46
47
|
- 7
|
47
48
|
version: 1.2.7
|
48
|
-
type: :development
|
49
|
-
version_requirements: *id002
|
50
|
-
name: rubyXL
|
51
49
|
prerelease: false
|
50
|
+
type: :development
|
51
|
+
requirement: *id002
|
52
52
|
- !ruby/object:Gem::Dependency
|
53
|
-
|
53
|
+
name: nokogiri
|
54
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
55
|
none: false
|
55
56
|
requirements:
|
56
57
|
- - ">="
|
@@ -61,12 +62,12 @@ dependencies:
|
|
61
62
|
- 4
|
62
63
|
- 4
|
63
64
|
version: 1.4.4
|
64
|
-
type: :development
|
65
|
-
version_requirements: *id003
|
66
|
-
name: nokogiri
|
67
65
|
prerelease: false
|
66
|
+
type: :development
|
67
|
+
requirement: *id003
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
|
-
|
69
|
+
name: rubyzip
|
70
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
70
71
|
none: false
|
71
72
|
requirements:
|
72
73
|
- - ">="
|
@@ -77,24 +78,9 @@ dependencies:
|
|
77
78
|
- 9
|
78
79
|
- 4
|
79
80
|
version: 0.9.4
|
80
|
-
type: :development
|
81
|
-
version_requirements: *id004
|
82
|
-
name: rubyzip
|
83
81
|
prerelease: false
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
-
none: false
|
87
|
-
requirements:
|
88
|
-
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
hash: 3
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
version: "0"
|
94
82
|
type: :development
|
95
|
-
|
96
|
-
name: awesome_print
|
97
|
-
prerelease: false
|
83
|
+
requirement: *id004
|
98
84
|
description: Soroban makes it easy to extract and execute formulas from Excel spreadsheets. It rewrites Excel formulas as Ruby expressions, and allows you to bind named variables to spreadsheet cells to easily manipulate inputs and capture outputs.
|
99
85
|
email: jason.hutchens@agworld.com.au
|
100
86
|
executables: []
|
@@ -143,7 +129,6 @@ files:
|
|
143
129
|
- lib/soroban/parser/nodes.rb
|
144
130
|
- lib/soroban/parser/rewrite.rb
|
145
131
|
- lib/soroban/sheet.rb
|
146
|
-
- lib/soroban/tabulator.rb
|
147
132
|
- lib/soroban/value_walker.rb
|
148
133
|
- spec/documentation_spec.rb
|
149
134
|
- spec/import_spec.rb
|
data/lib/soroban/tabulator.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
module Soroban
|
2
|
-
|
3
|
-
# An enumerable that splits a range of cells into an nxm array
|
4
|
-
class Tabulator
|
5
|
-
|
6
|
-
include Enumerable
|
7
|
-
|
8
|
-
# Create a new walker from a supplied range.
|
9
|
-
def initialize(range)
|
10
|
-
@fc, @fr, @tc, @tr = Soroban::getRange(range)
|
11
|
-
end
|
12
|
-
|
13
|
-
def get
|
14
|
-
row = []
|
15
|
-
cols = [row]
|
16
|
-
col_ref, row_ref = @fc, @fr
|
17
|
-
while true do
|
18
|
-
row << "#{col_ref}#{row_ref}".to_sym
|
19
|
-
break if row_ref == @tr && col_ref == @tc
|
20
|
-
if row_ref == @tr
|
21
|
-
row = []
|
22
|
-
cols << row
|
23
|
-
row_ref = @fr
|
24
|
-
col_ref = col_ref.next
|
25
|
-
else
|
26
|
-
row_ref = row_ref.next
|
27
|
-
end
|
28
|
-
end
|
29
|
-
cols
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|