line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Spreadsheet::Engine; |
2
|
|
|
|
|
|
|
|
3
|
31
|
|
|
31
|
|
101449
|
use strict; |
|
31
|
|
|
|
|
71
|
|
|
31
|
|
|
|
|
1104
|
|
4
|
31
|
|
|
31
|
|
160
|
use warnings; |
|
31
|
|
|
|
|
55
|
|
|
31
|
|
|
|
|
944
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
use Spreadsheet::Engine::Sheet ( |
7
|
31
|
|
|
31
|
|
59139
|
qw/parse_sheet_save execute_sheet_command recalc_sheet/); |
|
31
|
|
|
|
|
78
|
|
|
31
|
|
|
|
|
13395
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.14'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Spreadsheet::Engine - Core calculation engine for a spreadsheet |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use Spreadsheet::Engine; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $sheet = Spreadsheet::Engine->new; |
20
|
|
|
|
|
|
|
my $sheet = Spreadsheet::Engine->load_data([@data]); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$sheet->execute('set A1 value n 2'); |
23
|
|
|
|
|
|
|
$sheet->execute('set A2 value n 4'); |
24
|
|
|
|
|
|
|
$sheet->execute('set A3 formula SUM(A1:A2)'); |
25
|
|
|
|
|
|
|
$sheet->recalc; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my $data = $sheet->raw; |
28
|
|
|
|
|
|
|
print $data->{datavalues}{A3}; # 6 |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 DESCRIPTION |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
This provides back-end spreadsheet functionality for creating a |
33
|
|
|
|
|
|
|
sheet, setting cells to have values or formulae, and performing all |
34
|
|
|
|
|
|
|
necessary calculations. There is no front-end UI provided - this |
35
|
|
|
|
|
|
|
is purely the calculation engine. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Over 110 spreadsheet functions are provided: see |
38
|
|
|
|
|
|
|
Spreadsheet::Engine::Function::* and L |
39
|
|
|
|
|
|
|
for the full list. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head1 METHODS |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=head2 new |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $sheet = Spreadsheet::Engine->new; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Instantiate a new blank spreadsheet. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=cut |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub _new { |
52
|
31
|
|
|
31
|
|
78
|
my $class = shift; |
53
|
31
|
|
|
|
|
190
|
bless { _sheet => {} } => $class; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub new { |
57
|
10
|
|
|
10
|
1
|
363
|
my $class = shift; |
58
|
10
|
|
|
|
|
65
|
return $class->load_data([]); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head2 load_data |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
my $sheet = Spreadsheet::Engine->load_data([@data]); |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
Instantiate a sheet from lines of data in the saved file format (see |
66
|
|
|
|
|
|
|
L for documentation) |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=cut |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub load_data { |
71
|
31
|
|
|
31
|
1
|
100
|
my ($class, $data) = @_; |
72
|
31
|
|
|
|
|
197
|
my $self = $class->_new; |
73
|
31
|
|
|
|
|
381
|
parse_sheet_save($data => $self->{_sheet}); |
74
|
31
|
|
|
|
|
1350
|
return $self; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head2 execute |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
$sheet->execute('set A1 value n 2'); |
80
|
|
|
|
|
|
|
$sheet->execute('set A2 value n 4'); |
81
|
|
|
|
|
|
|
$sheet->execute('set A3 formula SUM(A1:A2)'); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Execute the given command against the sheet. See |
84
|
|
|
|
|
|
|
L for documentation of commands. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=cut |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub execute { |
89
|
679
|
|
|
679
|
1
|
612817
|
my ($self, $command) = @_; |
90
|
679
|
|
|
|
|
4322
|
execute_sheet_command($self->{_sheet} => $command); |
91
|
679
|
|
|
|
|
1567
|
return $self; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head2 recalc |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
$sheet->recalc; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Recalculate the values for all formulae in the sheet. This never happens |
99
|
|
|
|
|
|
|
automatically - it must be explicitly called. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=cut |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub recalc { |
104
|
680
|
|
|
680
|
1
|
3301
|
my $self = shift; |
105
|
680
|
|
|
|
|
2821
|
recalc_sheet($self->{_sheet}); |
106
|
680
|
|
|
|
|
8955
|
return $self; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head2 raw |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
my $data = $sheet->raw; |
112
|
|
|
|
|
|
|
print $data->{datavalues}{A3}; # 6 |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Access the raw datastructure for the sheet. This is a temporary method |
115
|
|
|
|
|
|
|
until we provide proper accessors to the underlying data. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=cut |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub raw { |
120
|
1193
|
|
|
1193
|
1
|
59249
|
my $self = shift; |
121
|
1193
|
|
|
|
|
5511
|
return $self->{_sheet}; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
1; |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head1 WARNING |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
Although the core underlying code is relatively mature and featureful, |
129
|
|
|
|
|
|
|
there will be significant interface changes and refactoring going |
130
|
|
|
|
|
|
|
forward with this version. As well as any bugs in the original |
131
|
|
|
|
|
|
|
SocialCalc code, the process of rearranging the code is likely to |
132
|
|
|
|
|
|
|
introduce more. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
There is a reasonably complete test suite for most of the functions, |
135
|
|
|
|
|
|
|
but much of the other code is as yet untested. (See 'coverage.txt' |
136
|
|
|
|
|
|
|
in the root directory of the distribution for a little more detail). |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Please pay close attention to the CHANGES file if you upgrade this |
139
|
|
|
|
|
|
|
package. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=head1 OPEN FORMULA SPECIFICATION |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Spreadsheet::Engine attemps to conform as closely as possible to the |
144
|
|
|
|
|
|
|
Open Formula specification and provide all the features of the "Small" |
145
|
|
|
|
|
|
|
group as defined there. Divergences from that are detailed below. It is |
146
|
|
|
|
|
|
|
hoped to add the extra functionality of the "Medium" and "Large" groups |
147
|
|
|
|
|
|
|
eventually, but the initial work is on refactoring the code base to make |
148
|
|
|
|
|
|
|
it easier for users to plug in their own extensions to provide more of |
149
|
|
|
|
|
|
|
that functionality. |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
The latest version of the specification can be found at |
152
|
|
|
|
|
|
|
L |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head1 EXTRAS |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
On top of the "Small" group functionality the following features are |
157
|
|
|
|
|
|
|
provided: |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=over 4 |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=item * "Year 1583": Dates between 1583 and 1900 are handled correctly. |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=item * Text is automatically converted to numbers in some (but not all) |
164
|
|
|
|
|
|
|
circumstances (see t/of-autonum.t gives examples) |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=item * PLAINTEXT() and HTML() functions |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=back |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head1 KNOWN BUGS AND SHORTCOMINGS |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
We do not meet the Open Formula specification at the following sections: |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=over 4 |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=item * (4.8.7) Whitespace is significant in database tests: '> 2006-01-01 |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=item * (5.6) Empty parameters cannot be omitted: IF(FALSE(),7,) |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item * (5.8) Semicolon is sometimes not recognised as a separator in |
181
|
|
|
|
|
|
|
function calls, cell/range lists, etc.: SUM(A1;B2;B3), IF(FALSE();7;8) |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=item * (5.8) Cell references of the form [.B1] are not supported |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=item * (5.8) References to other sheets are not supported |
186
|
|
|
|
|
|
|
(this feature is in SocialCalc, but for now is out of scope for this |
187
|
|
|
|
|
|
|
engine) |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=item * (5.10.1) Range names cannot contain unicode characters |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=item * (5.10.1) Range names do not support $$ markers |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=item * (5.11) Errors cannot be as strings (#N/A, #DIV/0! etc) in formulae |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=item * (6.2.4) Empty cell in numeric context is not treated as 0 |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item * (6.2.4) Reference to TRUE in numeric context is not treated as 0 |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=item * (6.3.10) Whitespace is significant in string-concatenation using & |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item * (6.3.2) Whitespace is significant in unary minus: (5 - - 2) vs (5--2) |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=item * (6.3.6) Different types can be equal: 5 = "5" |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item * (6.3.9) Range extensions are not implemented: B4:C4:C5 |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item * (6.3.11) Range intersections are not implemented: B3:B5!B5:B6 |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item * (6.3.14) Strings are converted to number with prefix +: +"Hello" |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=item * (6.9.1) Roll-over dates may not be correct (DATE(2006,25,34) = |
212
|
|
|
|
|
|
|
2008-02-05 vs 2008-02-03) |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=item * (6.12.5) COUNT() includes TRUE/FALSE values |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=item * (6.12.32) VALUE("-1 1/2") is interpreted (-1)+(1/2) not -1.5 |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=item * (6.12.32) VALUE("3/32/2006") is interpreted as 1st April 2006 |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=item * (6.12.32) VALUE does not raise error for false leap years |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=item * (6.12.32) Cannot take VALUE() of a datetime (with space or T) |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=item * (6.12.32) Cannot enter dates with alphabetic month names |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=item * (6.14.3) IF() does not have default ifTrue/ifFalse values |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=item * (6.15.29) FACT() operates on negative numbers |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=item * (6.15.44) PRODUCT() with no paramters is an error |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=item * (6.17.45) MIN("a") returns 0 rather than an error |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=item * (6.19.11) LEFT() does not have a default length |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item * (6.19.14) MID() with a start beyond string returns undef |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=item * (6.19.18) RIGHT() does not have a default length |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=back |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head2 FIXED |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=over |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=item * (6.13.11) VLOOKUP for an integer must be exact, not <= |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=item * (6.19.14) MID() does not accept a zero length |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=item * (6.15.37) LOG() does not default to base 10 |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=back |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 HISTORY |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
The original Spreadsheet::Engine code was taken from SocialCalc version |
257
|
|
|
|
|
|
|
1.1.0, which in turn originated as wikiCalc(R) version 1.0. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head1 AUTHORS |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Spreadsheet::Engine is developed and maintained by Tony Bowden |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
SocialCalc 1.1.0 was developed by Dan Bricklin, Casey West, and Tony |
265
|
|
|
|
|
|
|
Bowden, at Socialtext, Inc. |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
wikiCalc was developed by Dan Bricklin, at Software Garden, Inc. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=head1 COPYRIGHT |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
Portions (c) Copyright 2005, 2006, 2007 Software Garden, Inc. |
272
|
|
|
|
|
|
|
All Rights Reserved. |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
Portions (c) Copyright 2007 Socialtext, Inc. |
275
|
|
|
|
|
|
|
All Rights Reserved. |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
Portions (c) Copyright 2007, 2008 Tony Bowden. Some Rights Reserved. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=head1 LICENCE |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
The contents of this file are subject to the Artistic License 2.0; |
282
|
|
|
|
|
|
|
you may not use this file except in compliance with the License. |
283
|
|
|
|
|
|
|
You may obtain a copy of the License at |
284
|
|
|
|
|
|
|
http://www.perlfoundation.org/artistic_license_2_0 |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
=cut |
287
|
|
|
|
|
|
|
|