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 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