surpass 0.0.7 → 0.0.9
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.
- data/History.txt +2 -2
- data/Rakefile +14 -16
- data/debug-examples/formula-cell.bin +0 -0
- data/debug-examples/formula-cell.rb +14 -0
- data/debug-examples/formula-record.bin +0 -0
- data/debug-examples/formula-record.rb +15 -0
- data/lib/surpass/ExcelFormula.g +82 -84
- data/lib/surpass/ExcelFormula.tokens +29 -29
- data/lib/surpass/ExcelFormulaLexer.rb +1283 -842
- data/lib/surpass/ExcelFormulaParser.rb +1654 -491
- data/lib/surpass/biff_record.rb +2 -2
- data/lib/surpass/formula.rb +25 -0
- data/lib/surpass/row.rb +1 -1
- data/lib/surpass/utilities.rb +32 -0
- data/lib/surpass/workbook.rb +1 -1
- data/lib/surpass/worksheet.rb +4 -4
- data/lib/surpass.rb +12 -2
- data/surpass.gemspec +8 -9
- metadata +26 -25
- data/lib/surpass/excel_formula.rb +0 -23
- data/out.bin +0 -0
- data/tasks/ann.rake +0 -80
- data/tasks/bones.rake +0 -20
- data/tasks/gem.rake +0 -201
- data/tasks/git.rake +0 -40
- data/tasks/notes.rake +0 -27
- data/tasks/post_load.rake +0 -34
- data/tasks/rdoc.rake +0 -51
- data/tasks/rubyforge.rake +0 -55
- data/tasks/setup.rb +0 -292
- data/tasks/spec.rake +0 -54
- data/tasks/svn.rake +0 -47
- data/tasks/test.rake +0 -40
- data/tasks/zentest.rake +0 -36
data/History.txt
CHANGED
data/Rakefile
CHANGED
@@ -4,27 +4,25 @@
|
|
4
4
|
|
5
5
|
begin
|
6
6
|
require 'bones'
|
7
|
-
Bones.setup
|
8
7
|
rescue LoadError
|
9
|
-
|
10
|
-
load 'tasks/setup.rb'
|
11
|
-
rescue LoadError
|
12
|
-
raise RuntimeError, '### please install the "bones" gem ###'
|
13
|
-
end
|
8
|
+
abort '### Please install the "bones" gem ###'
|
14
9
|
end
|
15
10
|
|
16
11
|
ensure_in_path 'lib'
|
17
12
|
require 'surpass'
|
18
13
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
PROJ.rubyforge.name = 'surpass'
|
14
|
+
Bones {
|
15
|
+
name 'surpass'
|
16
|
+
authors 'Ana Nelson'
|
17
|
+
email 'ana@ananelson.com'
|
18
|
+
url 'http://surpass.rubyforge.org'
|
19
|
+
version Surpass::VERSION
|
20
|
+
}
|
27
21
|
|
28
|
-
|
22
|
+
task :default => 'spec:run'
|
23
|
+
task 'gem:release' => 'test:run'
|
29
24
|
|
30
|
-
|
25
|
+
desc "run antlr compiler"
|
26
|
+
task :antlr do
|
27
|
+
`cd lib/surpass; antlr4ruby ExcelFormula.g`
|
28
|
+
end
|
Binary file
|
Binary file
|
data/lib/surpass/ExcelFormula.g
CHANGED
@@ -7,6 +7,8 @@ options {
|
|
7
7
|
|
8
8
|
@header {
|
9
9
|
RVA_DELTA = {"R" => 0, "V" => 0x20, "A" => 0x40}
|
10
|
+
RVA_DELTA_REF = {"R" => 0, "V" => 0x20, "A" => 0x40, "D" => 0x20}
|
11
|
+
RVA_DELTA_AREA = {"R" => 0, "V" => 0x20, "A" => 0x40, "D" => 0}
|
10
12
|
}
|
11
13
|
|
12
14
|
@init {
|
@@ -28,11 +30,11 @@ expr[arg_type]
|
|
28
30
|
(
|
29
31
|
(
|
30
32
|
EQ { op = [PTGEQ].pack('C') }
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
| NE { op = [PTGNE].pack('C') }
|
34
|
+
| GT { op = [PTGGT].pack('C') }
|
35
|
+
| LT { op = [PTGLT].pack('C') }
|
36
|
+
| GE { op = [PTGGE].pack('C') }
|
37
|
+
| LE { op = [PTGLE].pack('C') }
|
36
38
|
|
37
39
|
)
|
38
40
|
prec0_expr[arg_type] { @rpn += op }
|
@@ -105,7 +107,8 @@ primary[arg_type]
|
|
105
107
|
}
|
106
108
|
| str_tok = STR_CONST
|
107
109
|
{
|
108
|
-
|
110
|
+
s = str_tok.text.gsub("\"", "")
|
111
|
+
@rpn += [PTGSTR].pack("C") + [s.length].pack('v') + s
|
109
112
|
}
|
110
113
|
| int_tok = INT_CONST
|
111
114
|
{
|
@@ -118,22 +121,23 @@ primary[arg_type]
|
|
118
121
|
}
|
119
122
|
| num_tok = NUM_CONST
|
120
123
|
{
|
121
|
-
@rpn += [
|
124
|
+
@rpn += [PTGNUM, num_tok.text.to_f].pack("CE")
|
122
125
|
}
|
123
126
|
| ref2d_tok = REF2D
|
124
127
|
{
|
125
|
-
r, c =
|
128
|
+
r, c = Utilities.cell_to_packed_rowcol(ref2d_tok.text)
|
126
129
|
ptg = PTGREFR + RVA_DELTA[arg_type]
|
127
130
|
@rpn += [ptg, r, c].pack("Cv2")
|
128
131
|
}
|
129
132
|
| ref2d1_tok = REF2D COLON ref2d2_tok = REF2D
|
130
133
|
{
|
131
|
-
r1, c1 =
|
132
|
-
r2, c2 =
|
134
|
+
r1, c1 = Utilities.cell_to_packed_rowcol(ref2d1_tok.text)
|
135
|
+
r2, c2 = Utilities.cell_to_packed_rowcol(ref2d2_tok.text)
|
133
136
|
ptg = PTGAREAR + RVA_DELTA[arg_type]
|
134
|
-
|
137
|
+
@rpn += [ptg, r1, r2, c1, c2].pack("Cv4")
|
135
138
|
}
|
136
|
-
|
139
|
+
|
140
|
+
| sheet1 = sheet
|
137
141
|
{
|
138
142
|
sheet2 = sheet1
|
139
143
|
}
|
@@ -141,13 +145,13 @@ primary[arg_type]
|
|
141
145
|
{
|
142
146
|
ptg = PTGREF3DR + RVA_DELTA[arg_type]
|
143
147
|
rpn_ref2d = ""
|
144
|
-
r1, c1 =
|
148
|
+
r1, c1 = Utilities.cell_to_packed_rowcol(ref3d_ref2d.text)
|
145
149
|
rpn_ref2d = [0x0000, r1, c1].pack("v3")
|
146
150
|
}
|
147
151
|
( COLON ref3d_ref2d2= REF2D
|
148
152
|
{
|
149
153
|
ptg = PTGAREA3DR + RVA_DELTA[arg_type]
|
150
|
-
r2, c2 =
|
154
|
+
r2, c2 = Utilities.cell_to_packed_rowcol(ref3d_ref2d2.text)
|
151
155
|
rpn_ref2d = [0x0000, r1, r2, c1, c2].pack("v5")
|
152
156
|
}
|
153
157
|
)?
|
@@ -156,7 +160,7 @@ primary[arg_type]
|
|
156
160
|
@sheet_references << [sheet1, sheet2, @rpn.size]
|
157
161
|
@rpn += rpn_ref2d
|
158
162
|
}
|
159
|
-
|
163
|
+
| FUNC_IF
|
160
164
|
LP expr["V"] (SEMICOLON | COMMA)
|
161
165
|
{
|
162
166
|
@rpn += [PTGATTR, 0x02, 0].pack("C2v") # tAttrIf
|
@@ -165,44 +169,48 @@ primary[arg_type]
|
|
165
169
|
expr[arg_type] (SEMICOLON | COMMA)
|
166
170
|
{
|
167
171
|
@rpn += [PTGATTR, 0x08, 0].pack("C2v") # tAttrSkip
|
168
|
-
pos1 = @rpn.
|
169
|
-
|
172
|
+
pos1 = @rpn.length - 2
|
173
|
+
|
174
|
+
rem = @rpn.length - (pos0 + 2)
|
175
|
+
@rpn = @rpn[0..pos0] + [pos1-pos0].pack("v") + @rpn[pos0+2, rem] # TODO Check for OBO
|
170
176
|
}
|
171
177
|
expr[arg_type] RP
|
172
178
|
{
|
173
179
|
@rpn += [PTGATTR, 0x08, 3].pack("C2v") # tAttrSkip
|
174
180
|
@rpn += [PTGFUNCVARR, 3, 1].pack("C2v") # 3 = nargs, 1 = IF func
|
175
|
-
pos2 = @rpn.
|
176
|
-
|
181
|
+
pos2 = @rpn.length
|
182
|
+
|
183
|
+
rem = @rpn.length - (pos1 + 2)
|
184
|
+
@rpn = @rpn[0..pos1] + [pos2-(pos1+2)-1].pack("v") + @rpn[pos1+2, rem] # TODO Check for OBO
|
177
185
|
}
|
178
|
-
|
186
|
+
| FUNC_CHOOSE
|
179
187
|
{
|
180
188
|
arg_type = "R"
|
181
189
|
rpn_chunks = []
|
182
190
|
}
|
183
|
-
LP expr["V"]
|
191
|
+
LP expr["V"]
|
184
192
|
{
|
185
|
-
rpn_start = @rpn.
|
186
|
-
ref_markers = [@sheet_references.
|
193
|
+
rpn_start = @rpn.length
|
194
|
+
ref_markers = [@sheet_references.length]
|
187
195
|
}
|
188
196
|
(
|
189
197
|
(SEMICOLON | COMMA)
|
190
|
-
{ mark = @rpn.
|
198
|
+
{ mark = @rpn.length }
|
191
199
|
(
|
192
200
|
expr[arg_type]
|
193
201
|
| { @rpn += [PTGMISSARG].pack("C") }
|
194
202
|
)
|
195
203
|
{
|
196
|
-
|
197
|
-
|
204
|
+
rem = @rpn.length - mark
|
205
|
+
rpn_chunks << @rpn[mark, rem]
|
206
|
+
ref_markers << @sheet_references.size
|
198
207
|
}
|
199
208
|
)*
|
200
209
|
RP
|
201
210
|
{
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
chunklens = rpn_chunks.collect {|c| c.size}
|
211
|
+
@rpn = @rpn[0..rpn_start]
|
212
|
+
nc = rpn_chunks.length
|
213
|
+
chunklens = rpn_chunks.collect {|c| c.length}
|
206
214
|
skiplens = [0] * nc
|
207
215
|
skiplens[-1] = 3
|
208
216
|
|
@@ -219,80 +227,73 @@ primary[arg_type]
|
|
219
227
|
(0...nc).each do |i|
|
220
228
|
(ref_markers[i]...ref_markers[i+1]).each do |r|
|
221
229
|
ref = @sheet_references[r]
|
222
|
-
@sheet_references[r] = [
|
230
|
+
@sheet_references[r] = [ref[0], ref[1], ref[2] + chunk_shift]
|
223
231
|
end
|
224
232
|
chunk_shift += 4 # size of tAttrSkip
|
225
233
|
end
|
226
234
|
|
227
235
|
choose_rpn = []
|
228
|
-
choose_rpn
|
229
|
-
choose_rpn
|
236
|
+
choose_rpn << [PTGATTR, 0x04, nc].pack("CCv") # 0x04 is tAttrChoose
|
237
|
+
choose_rpn << jump_pos.pack("v*")
|
230
238
|
|
231
239
|
(0...nc).each do |i|
|
232
240
|
choose_rpn << rpn_chunks[i]
|
233
241
|
choose_rpn << [PTGATTR, 0x08, skiplens[i]].pack("CCv") # 0x08 is tAttrSkip
|
234
242
|
end
|
235
|
-
choose_rpn
|
243
|
+
choose_rpn << [PTGFUNCVARV, nc+1, 100].pack("CCv") # 100 is CHOOSE fn
|
236
244
|
@rpn += choose_rpn.join
|
237
245
|
}
|
238
|
-
|
239
246
|
| name_tok = NAME
|
240
247
|
{
|
241
|
-
|
242
|
-
# TODO: handle references to defined names here
|
248
|
+
raise "[formula] found unexpected NAME token #{name_tok.text}"
|
243
249
|
}
|
244
250
|
| func_tok = NAME
|
245
251
|
{
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
# # The name of the add-in function is passed as the 1st arg
|
260
|
-
# # of the hidden XCALL function
|
261
|
-
# self.xcall_references.append((func_toku, len(self.rpn) + 1))
|
262
|
-
# self.rpn += struct.pack("<BHHH",
|
263
|
-
# ptgNameXR,
|
264
|
-
# 0xadde, # ##PATCHME## index to REF entry in EXTERNSHEET record
|
265
|
-
# 0xefbe, # ##PATCHME## one-based index to EXTERNNAME record
|
266
|
-
# 0x0000) # unused
|
252
|
+
func_toku = func_tok.text.upcase
|
253
|
+
if STD_FUNC_BY_NAME.has_key?(func_toku)
|
254
|
+
opcode, min_argc, max_argc, func_type, arg_type_str = STD_FUNC_BY_NAME[func_toku]
|
255
|
+
arg_type_list = arg_type_str.split
|
256
|
+
else
|
257
|
+
raise "[formula] unknown function #{func_tok.text}"
|
258
|
+
end
|
259
|
+
xcall = (opcode < 0)
|
260
|
+
|
261
|
+
if xcall
|
262
|
+
@xcall_references << [func_toku, @rpn.size + 1]
|
263
|
+
@rpn += [PTGNAMEXR, 0xadde, 0xefbe, 0x0000].pack("Cvvv")
|
264
|
+
end
|
267
265
|
}
|
268
266
|
LP arg_count = expr_list[arg_type_list, min_argc, max_argc] RP
|
269
267
|
{
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
268
|
+
if (arg_count > max_argc) || (arg_count < min_argc)
|
269
|
+
raise "#{arg_count} parameters for function: #{func_tok.text}"
|
270
|
+
end
|
271
|
+
|
272
|
+
if xcall
|
273
|
+
func_ptg = PTGFUNCVARR + RVA_DELTA[func_type]
|
274
|
+
@rpn += [func_ptg, arg_count + 1, 255].pack("CCv") # 255 is magic XCALL function
|
275
|
+
elsif (min_argc == max_argc)
|
276
|
+
func_ptg = PTGFUNCR + RVA_DELTA[func_type]
|
277
|
+
@rpn += [func_ptg, opcode].pack("Cv")
|
278
|
+
elsif (arg_count == 1) && (func_tok.text.upcase == "SUM")
|
279
|
+
@rpn += [PTGATTR, 0x10, 0].pack("CCv") # tAttrSum
|
280
|
+
else
|
281
|
+
func_ptg = PTGFUNCVARR + RVA_DELTA[func_type]
|
282
|
+
@rpn += [func_ptg, arg_count, opcode].pack("CCv")
|
283
|
+
end
|
284
284
|
}
|
285
285
|
| LP expr[arg_type] RP
|
286
286
|
{
|
287
287
|
@rpn += [PTGPAREN].pack('C')
|
288
288
|
}
|
289
289
|
;
|
290
|
+
|
290
291
|
|
291
292
|
expr_list[arg_type_list, min_argc, max_argc] returns [arg_cnt]
|
292
293
|
@members{
|
293
|
-
|
294
|
-
|
295
|
-
|
294
|
+
arg_cnt = 0
|
295
|
+
arg_type = arg_type_list[arg_cnt]
|
296
|
+
}
|
296
297
|
: expr[arg_type] { arg_cnt += 1 }
|
297
298
|
(
|
298
299
|
{
|
@@ -321,14 +322,10 @@ sheet returns[ref]
|
|
321
322
|
| sheet_ref_int = INT_CONST
|
322
323
|
{ ref = sheet_ref_int.text }
|
323
324
|
| sheet_ref_quote = QUOTENAME
|
324
|
-
{ ref = sheet_ref_quote.text[1
|
325
|
+
{ ref = sheet_ref_quote.text[1, len(sheet_ref_quote.text) - 1].replace("''", "'") }
|
325
326
|
;
|
326
327
|
|
327
|
-
|
328
|
-
|
329
|
-
;
|
330
|
-
|
331
|
-
fragment
|
328
|
+
|
332
329
|
EQ: '=';
|
333
330
|
LT: '<';
|
334
331
|
GT: '>';
|
@@ -352,11 +349,11 @@ PERCENT: '%';
|
|
352
349
|
POWER: '^';
|
353
350
|
BANG: '!';
|
354
351
|
|
355
|
-
DIGIT: '0'..'9';
|
352
|
+
fragment DIGIT: '0'..'9';
|
356
353
|
|
357
354
|
INT_CONST: DIGIT+ ;
|
358
|
-
NUM_CONST: DIGIT* '.' DIGIT+ (('E'|'e') ('+'|'-')? DIGIT+)?;
|
359
|
-
STR_CONST: '"' ~'"' '"';
|
355
|
+
NUM_CONST: DIGIT* '.' DIGIT+ (('E'|'e') ('+'|'-')? DIGIT+)?;
|
356
|
+
STR_CONST: '"' (~'"')+ '"';
|
360
357
|
REF2D: '$'? ('A'..'I')? ('A'..'Z') '$'? DIGIT+;
|
361
358
|
TRUE_CONST: ('T'|'t') ('R'|'r') ('U'|'u') ('E'|'e') ;
|
362
359
|
FALSE_CONST: ('F'|'f') ('A'|'a') ('L'|'l') ('S'|'s') ('E'|'e') ;
|
@@ -364,3 +361,4 @@ NAME: '\w[\.\w]*' ;
|
|
364
361
|
QUOTENAME: '\'(?:[^\']|\'\')*\'';
|
365
362
|
FUNC_IF: 'IF';
|
366
363
|
FUNC_CHOOSE: 'CHOOSE';
|
364
|
+
|
@@ -1,30 +1,30 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
QUOTENAME=31
|
9
|
-
LT=19
|
10
|
-
NE=21
|
11
|
-
GT=20
|
12
|
-
FUNC_IF=32
|
13
|
-
RP=27
|
14
|
-
FALSE_CONST=13
|
1
|
+
GE=8
|
2
|
+
LT=7
|
3
|
+
NUM_CONST=21
|
4
|
+
PERCENT=16
|
5
|
+
REF2D=22
|
6
|
+
CONCAT=10
|
7
|
+
RP=29
|
15
8
|
LP=26
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
COLON=
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
9
|
+
INT_CONST=20
|
10
|
+
STR_CONST=19
|
11
|
+
POWER=15
|
12
|
+
SUB=12
|
13
|
+
FUNC_CHOOSE=30
|
14
|
+
SEMICOLON=27
|
15
|
+
BANG=24
|
16
|
+
TRUE_CONST=17
|
17
|
+
MUL=13
|
18
|
+
COLON=23
|
19
|
+
NAME=31
|
20
|
+
FALSE_CONST=18
|
21
|
+
COMMA=28
|
22
|
+
GT=6
|
23
|
+
DIGIT=33
|
24
|
+
EQ=4
|
25
|
+
DIV=14
|
26
|
+
FUNC_IF=25
|
27
|
+
QUOTENAME=32
|
28
|
+
LE=9
|
29
|
+
NE=5
|
30
|
+
ADD=11
|