Soroban 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.yardopts +6 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +44 -0
- data/LICENSE.txt +20 -0
- data/README.md +115 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/lib/soroban.rb +3 -0
- data/lib/soroban/cell.rb +47 -0
- data/lib/soroban/error.rb +20 -0
- data/lib/soroban/functions.rb +31 -0
- data/lib/soroban/functions/and.rb +4 -0
- data/lib/soroban/functions/average.rb +5 -0
- data/lib/soroban/functions/if.rb +4 -0
- data/lib/soroban/functions/max.rb +4 -0
- data/lib/soroban/functions/min.rb +4 -0
- data/lib/soroban/functions/not.rb +4 -0
- data/lib/soroban/functions/or.rb +4 -0
- data/lib/soroban/functions/sum.rb +4 -0
- data/lib/soroban/functions/vlookup.rb +10 -0
- data/lib/soroban/helpers.rb +45 -0
- data/lib/soroban/parser.rb +15 -0
- data/lib/soroban/parser/grammar.rb +1814 -0
- data/lib/soroban/parser/grammar.treetop +68 -0
- data/lib/soroban/parser/nodes.rb +65 -0
- data/lib/soroban/parser/rewrite.rb +36 -0
- data/lib/soroban/sheet.rb +119 -0
- data/lib/soroban/walker.rb +26 -0
- data/spec/soroban_spec.rb +97 -0
- data/spec/spec_helper.rb +12 -0
- metadata +234 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
grammar Soroban
|
2
|
+
rule formula
|
3
|
+
'=' space? logical <Formula> / string / number / boolean
|
4
|
+
end
|
5
|
+
rule logical
|
6
|
+
and ( space? 'or' space? and )*
|
7
|
+
end
|
8
|
+
rule and
|
9
|
+
truthval ( space? 'and' space? truthval )*
|
10
|
+
end
|
11
|
+
rule truthval
|
12
|
+
comparison / '(' space? logical space? ')' / boolean
|
13
|
+
end
|
14
|
+
rule boolean
|
15
|
+
'true' / 'false' / 'TRUE' / 'FALSE'
|
16
|
+
end
|
17
|
+
rule comparison
|
18
|
+
expression ( space? comparator space? expression )*
|
19
|
+
end
|
20
|
+
rule comparator
|
21
|
+
'=' <Equal> / '<>' <NotEqual> / '>=' / '<=' / '>' / '<'
|
22
|
+
end
|
23
|
+
rule expression
|
24
|
+
multiplicative ( space? additive_operator space? multiplicative )*
|
25
|
+
end
|
26
|
+
rule additive_operator
|
27
|
+
'+' / '-'
|
28
|
+
end
|
29
|
+
rule multiplicative
|
30
|
+
value ( space? multiplicative_operator space? value )*
|
31
|
+
end
|
32
|
+
rule multiplicative_operator
|
33
|
+
'^' <Pow> / '*' / '/'
|
34
|
+
end
|
35
|
+
rule value
|
36
|
+
( function / '(' space? expression space? ')' / range / number / boolean / identifier / string / '-' value )
|
37
|
+
end
|
38
|
+
rule function
|
39
|
+
[a-zA-Z]+ '(' space? arguments? space? ')' <Function>
|
40
|
+
end
|
41
|
+
rule arguments
|
42
|
+
logical ( space? ',' space? logical )*
|
43
|
+
end
|
44
|
+
rule number
|
45
|
+
float / integer
|
46
|
+
end
|
47
|
+
rule float
|
48
|
+
[0-9]* '.' [0-9]+
|
49
|
+
end
|
50
|
+
rule integer
|
51
|
+
[0-9]+
|
52
|
+
end
|
53
|
+
rule identifier
|
54
|
+
[a-zA-Z] [a-zA-Z0-9]* <Identifier>
|
55
|
+
end
|
56
|
+
rule label
|
57
|
+
[A-Za-z]+ [1-9] [0-9]* <Label> / '$' [A-Za-z]+ '$' [1-9] [0-9]* <Label>
|
58
|
+
end
|
59
|
+
rule string
|
60
|
+
'"' ('\"' / !'"' .)* '"' / "'" [^']* "'"
|
61
|
+
end
|
62
|
+
rule range
|
63
|
+
label ':' label <Range>
|
64
|
+
end
|
65
|
+
rule space
|
66
|
+
[\s]+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Soroban
|
2
|
+
|
3
|
+
class Formula < Treetop::Runtime::SyntaxNode
|
4
|
+
def rewrite(value)
|
5
|
+
value.gsub(/^= */, '')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Identifier < Treetop::Runtime::SyntaxNode
|
10
|
+
def rewrite(value)
|
11
|
+
"@#{value}.get"
|
12
|
+
end
|
13
|
+
def extract(value)
|
14
|
+
value.to_sym
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Function < Treetop::Runtime::SyntaxNode
|
19
|
+
def rewrite(value)
|
20
|
+
match = /^([^(]*)(.*)$/.match(value)
|
21
|
+
"func_#{match[1].downcase}#{match[2]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Pow < Treetop::Runtime::SyntaxNode
|
26
|
+
def rewrite(value)
|
27
|
+
"**"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Equal < Treetop::Runtime::SyntaxNode
|
32
|
+
def rewrite(value)
|
33
|
+
"=="
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class NotEqual < Treetop::Runtime::SyntaxNode
|
38
|
+
def rewrite(value)
|
39
|
+
"!="
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Label < Treetop::Runtime::SyntaxNode
|
44
|
+
def rewrite(value)
|
45
|
+
value.gsub('$', '')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Range < Treetop::Runtime::SyntaxNode
|
50
|
+
def rewrite(value)
|
51
|
+
"'#{value}'"
|
52
|
+
end
|
53
|
+
def extract(value)
|
54
|
+
fc, fr, tc, tr = Soroban::getRange(value)
|
55
|
+
retval = []
|
56
|
+
(fc..tc).each do |cc|
|
57
|
+
(fr..tr).each do |cr|
|
58
|
+
retval << "#{cc}#{cr}".to_sym
|
59
|
+
end
|
60
|
+
end
|
61
|
+
retval
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Treetop
|
2
|
+
module Runtime
|
3
|
+
class SyntaxNode
|
4
|
+
|
5
|
+
def convert(dependencies)
|
6
|
+
if nonterminal?
|
7
|
+
value = ""
|
8
|
+
elements.each { |element| value << element.convert(dependencies) }
|
9
|
+
_add_dependency(dependencies, extract(value))
|
10
|
+
rewrite(value)
|
11
|
+
else
|
12
|
+
_add_dependency(dependencies, extract(text_value))
|
13
|
+
rewrite(text_value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def rewrite(value)
|
18
|
+
value
|
19
|
+
end
|
20
|
+
|
21
|
+
def extract(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def _add_dependency(dependencies, value)
|
27
|
+
return if value.nil?
|
28
|
+
dependencies << value
|
29
|
+
dependencies.flatten!
|
30
|
+
dependencies.compact!
|
31
|
+
dependencies.uniq!
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'soroban/helpers'
|
2
|
+
require 'soroban/functions'
|
3
|
+
require 'soroban/walker'
|
4
|
+
require 'soroban/cell'
|
5
|
+
|
6
|
+
module Soroban
|
7
|
+
|
8
|
+
# A container for cells.
|
9
|
+
class Sheet
|
10
|
+
attr_reader :bindings
|
11
|
+
|
12
|
+
# Creates a new sheet.
|
13
|
+
def initialize
|
14
|
+
@cells = {}
|
15
|
+
@bindings = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Used for calling dynamically defined functions, and for creating new
|
19
|
+
# cells (via `label=`).
|
20
|
+
def method_missing(method, *args, &block)
|
21
|
+
if match = /^func_(.*)$/i.match(method.to_s)
|
22
|
+
return Soroban::call(self, match[1], *args)
|
23
|
+
elsif match = /^([a-z][\w]*)=$/i.match(method.to_s)
|
24
|
+
return _add(match[1], args[0])
|
25
|
+
end
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set the contents of one or more cells or ranges.
|
30
|
+
def set(label_or_range, contents)
|
31
|
+
unless range = Soroban::getRange(label_or_range)
|
32
|
+
return _add(label_or_range, contents)
|
33
|
+
end
|
34
|
+
fc, fr, tc, tr = range
|
35
|
+
if fc == tc || fr == tr
|
36
|
+
raise ArgumentError, "Expecting an array when setting #{label_or_range}" unless contents.kind_of? Array
|
37
|
+
cc, cr = fc, fr
|
38
|
+
contents.each do |item|
|
39
|
+
set("#{cc}#{cr}", item)
|
40
|
+
cc.next! if fr == tr
|
41
|
+
cr.next! if fc == tc
|
42
|
+
end
|
43
|
+
raise Soroban::RangeError, "Supplied array doesn't match range length" if cc != tc && cr != tr
|
44
|
+
else
|
45
|
+
raise ArgumentError, "Can only set cells or 1-dimensional ranges of cells"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Retrieve the contents of a cell.
|
50
|
+
def get(label_or_name)
|
51
|
+
_get(label_or_name, eval("@#{label_or_name}", binding))
|
52
|
+
end
|
53
|
+
|
54
|
+
# Bind one or more named variables to a cell.
|
55
|
+
def bind(name, label)
|
56
|
+
unless @cells.keys.include?(label.to_sym)
|
57
|
+
raise Soroban::UndefinedError, "Cannot bind '#{name}' to non-existent cell '#{label}'"
|
58
|
+
end
|
59
|
+
_bind(name, label)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Visit each cell in the supplied range, yielding its value.
|
63
|
+
def walk(range)
|
64
|
+
Walker.new(range, binding)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return a hash of `label => contents` for each cell in the sheet.
|
68
|
+
def cells
|
69
|
+
Hash[@cells.keys.map { |label| label.to_s }.zip( @cells.keys.map { |label| eval("@#{label}.excel") } )]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Return a list of referenced but undefined cells.
|
73
|
+
def missing
|
74
|
+
@cells.values.map.flatten.uniq - @cells.keys
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def _add(label, contents)
|
80
|
+
internal = "@#{label}"
|
81
|
+
_expose(internal, label)
|
82
|
+
cell = Cell.new(binding)
|
83
|
+
_set(label, cell, contents)
|
84
|
+
instance_variable_set(internal, cell)
|
85
|
+
end
|
86
|
+
|
87
|
+
def _set(label, cell, contents)
|
88
|
+
cell.set(contents)
|
89
|
+
@cells[label.to_sym] = cell.dependencies
|
90
|
+
end
|
91
|
+
|
92
|
+
def _get(label_or_name, cell)
|
93
|
+
label = label_or_name.to_sym
|
94
|
+
name = @cells[label] ? label : @bindings[label]
|
95
|
+
badref = @cells[name] & missing
|
96
|
+
raise Soroban::UndefinedError, "Unmet dependencies #{badref.join(', ')} for #{label}" if badref.length > 0
|
97
|
+
cell.get
|
98
|
+
end
|
99
|
+
|
100
|
+
def _bind(name, label)
|
101
|
+
@bindings[name.to_sym] = label.to_sym
|
102
|
+
internal = "@#{label}"
|
103
|
+
_expose(internal, name)
|
104
|
+
end
|
105
|
+
|
106
|
+
def _expose(internal, name)
|
107
|
+
instance_eval <<-EOV, __FILE__, __LINE__ + 1
|
108
|
+
def #{name}
|
109
|
+
_get("#{name}", #{internal})
|
110
|
+
end
|
111
|
+
def #{name}=(contents)
|
112
|
+
_set("#{name}", #{internal}, contents)
|
113
|
+
end
|
114
|
+
EOV
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Soroban
|
2
|
+
|
3
|
+
# An enumerable that allows cells in a range to be visited.
|
4
|
+
class Walker
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Create a new walker from a supplied range and binding. The binding is
|
9
|
+
# required when calculating the value of each visited cell.
|
10
|
+
def initialize(range, binding)
|
11
|
+
@binding = binding
|
12
|
+
@fc, @fr, @tc, @tr = Soroban::getRange(range)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Yield the value of each cell referenced by the supplied range.
|
16
|
+
def each
|
17
|
+
(@fc..@tc).each do |col|
|
18
|
+
(@fr..@tr).each do |row|
|
19
|
+
yield eval("@#{col}#{row}.get", @binding)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Soroban" do
|
4
|
+
|
5
|
+
let(:sheet) { Soroban::Sheet.new }
|
6
|
+
|
7
|
+
it "can add two numbers" do
|
8
|
+
sheet.x = 2
|
9
|
+
sheet.y = 3
|
10
|
+
sheet.f = "=x+y"
|
11
|
+
sheet.f.should eq(5)
|
12
|
+
sheet.x -= 1
|
13
|
+
sheet.f.should eq(4)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can iterate over a collection of cells" do
|
17
|
+
sheet.A1 = 'a'
|
18
|
+
sheet.B1 = 'b'
|
19
|
+
sheet.A2 = 'c'
|
20
|
+
sheet.B2 = 'd'
|
21
|
+
data = sheet.walk('A1:B2').to_a
|
22
|
+
data.sort.join.should eq('abcd')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can set a value" do
|
26
|
+
sheet.set(:foo, 'hello')
|
27
|
+
sheet.foo.should eq('hello')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can get a value" do
|
31
|
+
sheet.foo = 'hello'
|
32
|
+
sheet.get(:foo).should eq('hello')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can set an array" do
|
36
|
+
sheet.set("A1:A5", [ 1, 2, 3, 4, 5 ])
|
37
|
+
sheet.B1 = '=SUM(A1:A5)'
|
38
|
+
sheet.B1.should eq(15)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can set a hash" do
|
42
|
+
sheet.set("A1:A3", [ 'one', 'two', 'three' ])
|
43
|
+
sheet.set("B1:B3", [ 'mop', 'hai', 'bah' ])
|
44
|
+
sheet.C1 = '=VLOOKUP("two", A1:B3, 2, 0)'
|
45
|
+
sheet.C1.should eq('hai')
|
46
|
+
end
|
47
|
+
|
48
|
+
it "can iterate over all cells" do
|
49
|
+
sheet.set("A1:A3", [ 1, 2, 3 ])
|
50
|
+
sheet.set("B1:B3", [ 4, 5, 6 ])
|
51
|
+
sheet.set("C1:C3", [ 7, 8, 9 ])
|
52
|
+
sheet.cells.map { |label, contents| contents.to_i }.sort.should eq [1,2,3,4,5,6,7,8,9]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "can bind variables to cells" do
|
56
|
+
sheet.A1 = 0
|
57
|
+
sheet.A2 = "=A1^2"
|
58
|
+
sheet.bind(:input, :A1)
|
59
|
+
sheet.bind(:output, :A2)
|
60
|
+
sheet.input = 5
|
61
|
+
sheet.output.should eq(25)
|
62
|
+
sheet.bindings.keys.should include :input
|
63
|
+
sheet.bindings.keys.should include :output
|
64
|
+
sheet.bindings.values.should include :A1
|
65
|
+
sheet.bindings.values.should include :A2
|
66
|
+
end
|
67
|
+
|
68
|
+
it "can define new functions" do
|
69
|
+
Soroban::define :FOO => lambda { |a, b| 2 * a + b / 2 }
|
70
|
+
sheet.A1 = 7
|
71
|
+
sheet.A2 = 8
|
72
|
+
sheet.A3 = "=foo(A1, A2)"
|
73
|
+
sheet.A3.should eq(18)
|
74
|
+
Soroban::functions.should include 'FOO'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "can report on missing cells" do
|
78
|
+
sheet.A3 = "=A2+foo(A3:B4)"
|
79
|
+
expected = [:A2, :A4, :B3, :B4 ]
|
80
|
+
sheet.missing.should =~ expected
|
81
|
+
end
|
82
|
+
|
83
|
+
it "can detect loops when running formulas" do
|
84
|
+
lambda {
|
85
|
+
sheet.A1 = "=A2"
|
86
|
+
sheet.A2 = "=A1"
|
87
|
+
sheet.A2
|
88
|
+
}.should raise_error(Soroban::RecursionError)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "can reject valid ruby code in formulas" do
|
92
|
+
lambda {
|
93
|
+
sheet.set(:A1, "=3**2")
|
94
|
+
}.should raise_error(Soroban::ParseError)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'soroban'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Soroban
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jason Hutchens
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-04-23 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 19
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 4
|
32
|
+
- 10
|
33
|
+
version: 1.4.10
|
34
|
+
version_requirements: *id001
|
35
|
+
name: treetop
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 47
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 8
|
48
|
+
- 0
|
49
|
+
version: 2.8.0
|
50
|
+
version_requirements: *id002
|
51
|
+
name: rspec
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
type: :development
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 5
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
- 7
|
64
|
+
version: "0.7"
|
65
|
+
version_requirements: *id003
|
66
|
+
name: yard
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 31
|
76
|
+
segments:
|
77
|
+
- 3
|
78
|
+
- 12
|
79
|
+
version: "3.12"
|
80
|
+
version_requirements: *id004
|
81
|
+
name: rdoc
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ~>
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 21
|
91
|
+
segments:
|
92
|
+
- 1
|
93
|
+
- 1
|
94
|
+
- 3
|
95
|
+
version: 1.1.3
|
96
|
+
version_requirements: *id005
|
97
|
+
name: bundler
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ~>
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 49
|
107
|
+
segments:
|
108
|
+
- 1
|
109
|
+
- 8
|
110
|
+
- 3
|
111
|
+
version: 1.8.3
|
112
|
+
version_requirements: *id006
|
113
|
+
name: jeweler
|
114
|
+
- !ruby/object:Gem::Dependency
|
115
|
+
type: :development
|
116
|
+
prerelease: false
|
117
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
version: "0"
|
126
|
+
version_requirements: *id007
|
127
|
+
name: rcov
|
128
|
+
- !ruby/object:Gem::Dependency
|
129
|
+
type: :development
|
130
|
+
prerelease: false
|
131
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ~>
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
hash: 17
|
137
|
+
segments:
|
138
|
+
- 1
|
139
|
+
- 2
|
140
|
+
- 7
|
141
|
+
version: 1.2.7
|
142
|
+
version_requirements: *id008
|
143
|
+
name: rubyXL
|
144
|
+
- !ruby/object:Gem::Dependency
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
hash: 3
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
version: "0"
|
156
|
+
version_requirements: *id009
|
157
|
+
name: redcarpet
|
158
|
+
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.
|
159
|
+
email: jason.hutchens@agworld.com.au
|
160
|
+
executables: []
|
161
|
+
|
162
|
+
extensions: []
|
163
|
+
|
164
|
+
extra_rdoc_files:
|
165
|
+
- LICENSE.txt
|
166
|
+
- README.md
|
167
|
+
files:
|
168
|
+
- .document
|
169
|
+
- .rspec
|
170
|
+
- .yardopts
|
171
|
+
- Gemfile
|
172
|
+
- Gemfile.lock
|
173
|
+
- LICENSE.txt
|
174
|
+
- README.md
|
175
|
+
- Rakefile
|
176
|
+
- VERSION
|
177
|
+
- lib/soroban.rb
|
178
|
+
- lib/soroban/cell.rb
|
179
|
+
- lib/soroban/error.rb
|
180
|
+
- lib/soroban/functions.rb
|
181
|
+
- lib/soroban/functions/and.rb
|
182
|
+
- lib/soroban/functions/average.rb
|
183
|
+
- lib/soroban/functions/if.rb
|
184
|
+
- lib/soroban/functions/max.rb
|
185
|
+
- lib/soroban/functions/min.rb
|
186
|
+
- lib/soroban/functions/not.rb
|
187
|
+
- lib/soroban/functions/or.rb
|
188
|
+
- lib/soroban/functions/sum.rb
|
189
|
+
- lib/soroban/functions/vlookup.rb
|
190
|
+
- lib/soroban/helpers.rb
|
191
|
+
- lib/soroban/parser.rb
|
192
|
+
- lib/soroban/parser/grammar.rb
|
193
|
+
- lib/soroban/parser/grammar.treetop
|
194
|
+
- lib/soroban/parser/nodes.rb
|
195
|
+
- lib/soroban/parser/rewrite.rb
|
196
|
+
- lib/soroban/sheet.rb
|
197
|
+
- lib/soroban/walker.rb
|
198
|
+
- spec/soroban_spec.rb
|
199
|
+
- spec/spec_helper.rb
|
200
|
+
homepage: http://github.com/jasonhutchens/soroban
|
201
|
+
licenses:
|
202
|
+
- MIT
|
203
|
+
post_install_message:
|
204
|
+
rdoc_options: []
|
205
|
+
|
206
|
+
require_paths:
|
207
|
+
- lib
|
208
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
209
|
+
none: false
|
210
|
+
requirements:
|
211
|
+
- - ">="
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
hash: 3
|
214
|
+
segments:
|
215
|
+
- 0
|
216
|
+
version: "0"
|
217
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
218
|
+
none: false
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
hash: 3
|
223
|
+
segments:
|
224
|
+
- 0
|
225
|
+
version: "0"
|
226
|
+
requirements: []
|
227
|
+
|
228
|
+
rubyforge_project:
|
229
|
+
rubygems_version: 1.8.10
|
230
|
+
signing_key:
|
231
|
+
specification_version: 3
|
232
|
+
summary: Soroban is a calculating engine that understands Excel formulas.
|
233
|
+
test_files: []
|
234
|
+
|