surpass 0.0.7 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,4 +1,4 @@
1
- == 1.0.0 / 2009-10-27
1
+ == 0.0.9 / 2010-05-03
2
2
 
3
3
  * 1 major enhancement
4
- * Birthday!
4
+ * Some formula support enabled, see examples/formulas.rb to see what is supported.
data/Rakefile CHANGED
@@ -4,27 +4,25 @@
4
4
 
5
5
  begin
6
6
  require 'bones'
7
- Bones.setup
8
7
  rescue LoadError
9
- begin
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
- task :default => 'spec:run'
20
-
21
- PROJ.name = 'surpass'
22
- PROJ.authors = 'Ana Nelson'
23
- PROJ.email = 'ana@ananelson.com'
24
- PROJ.url = 'http://surpass.rubyforge.org'
25
- PROJ.version = Surpass::VERSION
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
- PROJ.spec.opts << '--color'
22
+ task :default => 'spec:run'
23
+ task 'gem:release' => 'test:run'
29
24
 
30
- # EOF
25
+ desc "run antlr compiler"
26
+ task :antlr do
27
+ `cd lib/surpass; antlr4ruby ExcelFormula.g`
28
+ end
Binary file
@@ -0,0 +1,14 @@
1
+ require "lib/surpass"
2
+
3
+ class Parent
4
+ def index
5
+ 0
6
+ end
7
+ end
8
+
9
+ cell = FormulaCell.new(Parent.new, 0, 0, ExcelFormula.new("1"))
10
+
11
+ File.open("formula-cell.bin", "w") do |f|
12
+ f.write cell.to_biff
13
+ end
14
+
Binary file
@@ -0,0 +1,15 @@
1
+ require "lib/surpass"
2
+
3
+ class Parent
4
+ def index
5
+ 0
6
+ end
7
+ end
8
+
9
+ rpn = ExcelFormula.new("1").to_biff
10
+ record = FormulaRecord.new(0, 0, 0, rpn)
11
+
12
+ File.open("formula-record.bin", "w") do |f|
13
+ f.write record.to_biff
14
+ end
15
+
@@ -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
- /* | NE { op = [PTGNE].pack('C') } */
32
- /* | GT { op = [PTGGT].pack('C') } */
33
- /* | LT { op = [PTGLT].pack('C') } */
34
- /* | GE { op = [PTGGE].pack('C') } */
35
- /* | LE { op = [PTGLE].pack('C') } */
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
- @rpn += [PTGSTR].pack('C') + upack1(str_tok.text[1, -1]) #TODO
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 += [ptgNum, num_tok.text.to_f].pack("CE")
124
+ @rpn += [PTGNUM, num_tok.text.to_f].pack("CE")
122
125
  }
123
126
  | ref2d_tok = REF2D
124
127
  {
125
- r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text) # TODO
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 = Utils.cell_to_packed_rowcol(ref2d1_tok.text) #TODO
132
- r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text) #TODO
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
- self.rpn += struct.pack("Cv4", ptg, r1, r2, c1, c2)
137
+ @rpn += [ptg, r1, r2, c1, c2].pack("Cv4")
135
138
  }
136
- /* | sheet1 = sheet
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 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text) #TODO
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 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text) #TODO
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
- | FUNC_IF
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.size - 2
169
- @rpn = @rpn[0...pos0] + [pos1-pos0].pack("v") + @rpn[pos0+2...-1] # TODO
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.size
176
- @rpn = @rpn[0...pos1] + [pos2-(pos1+2)-1].pack("v") + @rpn[pos1+2...-1] # TODO
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
- | FUNC_CHOOSE
186
+ | FUNC_CHOOSE
179
187
  {
180
188
  arg_type = "R"
181
189
  rpn_chunks = []
182
190
  }
183
- LP expr["V"] // first argument (the selector)
191
+ LP expr["V"]
184
192
  {
185
- rpn_start = @rpn.size
186
- ref_markers = [@sheet_references.size]
193
+ rpn_start = @rpn.length
194
+ ref_markers = [@sheet_references.length]
187
195
  }
188
196
  (
189
197
  (SEMICOLON | COMMA)
190
- { mark = @rpn.size }
198
+ { mark = @rpn.length }
191
199
  (
192
200
  expr[arg_type]
193
201
  | { @rpn += [PTGMISSARG].pack("C") }
194
202
  )
195
203
  {
196
- rpn_chunks.append(@rpn[mark...-1])
197
- ref_markers.append(@sheet_references.size)
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
- # TODO test this, no idea if it works, just blindly translated
203
- @rpn = @rpn[0...rpn_start]
204
- nc = rpn_chunks.size
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] = [r[0], r[1], r[2] + chunk_shift]
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.append([PTGATTR, 0x04, nc].pack("CCv")) # 0x04 is tAttrChoose
229
- choose_rpn.append(jump_pos.pack("v*"))
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.append([PTGFUNCVARV, nc+1, 100].pack("CCv")) # 100 is CHOOSE fn
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
- raise "[formula] found unexpected NAME token #{name_tok.text}"
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
- raise "not implemented"
247
- # func_toku = func_tok.text.upper()
248
- # if func_toku in all_funcs_by_name:
249
- # (opcode,
250
- # min_argc,
251
- # max_argc,
252
- # func_type,
253
- # arg_type_str) = all_funcs_by_name[func_toku]
254
- # arg_type_list = list(arg_type_str)
255
- # else:
256
- # raise Exception("[formula] unknown function #{func_tok.text}")
257
- # xcall = opcode < 0
258
- # if xcall:
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
- raise "not implemented"
271
- # if arg_count > max_argc or arg_count < min_argc:
272
- # raise Exception, "#{arg_count} parameters for function: #{func_tok.text}"
273
- # if xcall:
274
- # func_ptg = ptgFuncVarR + _RVAdelta[func_type]
275
- # self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) # 255 is magic XCALL function
276
- # elif min_argc == max_argc:
277
- # func_ptg = ptgFuncR + _RVAdelta[func_type]
278
- # self.rpn += struct.pack("<BH", func_ptg, opcode)
279
- # elif arg_count == 1 and func_tok.text.upper() == "SUM":
280
- # self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) # tAttrSum
281
- # else:
282
- # func_ptg = ptgFuncVarR + _RVAdelta[func_type]
283
- # self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode)
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
- arg_cnt = 0
294
- arg_type = arg_type_list[arg_cnt]
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..-1].replace("''", "'") # TODO }
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+)?; // \d*\.\d+(?:[Ee][+-]?\d+)?
359
- STR_CONST: '"' ~'"' '"'; // TODO add escape recognition
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
- COMMA=25
2
- TRUE_CONST=12
3
- PERCENT=11
4
- POWER=10
5
- FUNC_CHOOSE=33
6
- BANG=28
7
- EQ=4
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
- GE=23
17
- MUL=8
18
- NUM_CONST=16
19
- REF2D=17
20
- SEMICOLON=24
21
- CONCAT=5
22
- LE=22
23
- STR_CONST=14
24
- INT_CONST=15
25
- COLON=18
26
- DIV=9
27
- DIGIT=29
28
- NAME=30
29
- SUB=7
30
- ADD=6
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