rmasalov-surpass 0.1.0

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.
@@ -0,0 +1,4 @@
1
+ == 0.0.9 / 2010-05-03
2
+
3
+ * 1 major enhancement
4
+ * Some formula support enabled, see examples/formulas.rb to see what is supported.
@@ -0,0 +1,110 @@
1
+ Portions Copyright (c) 2008-9, Ana Nelson
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
18
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
19
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
+
27
+
28
+
29
+ Surpass is a Ruby port of Python projects xlwt and pyExcelerator, whose
30
+ licenses are below.
31
+
32
+
33
+
34
+ Portions copyright © 2007, Stephen John Machin, Lingfo Pty Ltd
35
+ All rights reserved.
36
+
37
+ Redistribution and use in source and binary forms, with or without
38
+ modification, are permitted provided that the following conditions are met:
39
+
40
+ 1. Redistributions of source code must retain the above copyright notice,
41
+ this list of conditions and the following disclaimer.
42
+
43
+ 2. Redistributions in binary form must reproduce the above copyright notice,
44
+ this list of conditions and the following disclaimer in the documentation
45
+ and/or other materials provided with the distribution.
46
+
47
+ 3. None of the names of Stephen John Machin, Lingfo Pty Ltd and any
48
+ contributors may be used to endorse or promote products derived from this
49
+ software without specific prior written permission.
50
+
51
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
52
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
53
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
54
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
55
+ BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
61
+ THE POSSIBILITY OF SUCH DAMAGE.
62
+
63
+
64
+ Copyright (C) 2005 Roman V. Kiseliov
65
+ All rights reserved.
66
+
67
+ Redistribution and use in source and binary forms, with or without
68
+ modification, are permitted provided that the following conditions
69
+ are met:
70
+
71
+ 1. Redistributions of source code must retain the above copyright
72
+ notice, this list of conditions and the following disclaimer.
73
+
74
+ 2. Redistributions in binary form must reproduce the above copyright
75
+ notice, this list of conditions and the following disclaimer in
76
+ the documentation and/or other materials provided with the
77
+ distribution.
78
+
79
+ 3. All advertising materials mentioning features or use of this
80
+ software must display the following acknowledgment:
81
+ "This product includes software developed by
82
+ Roman V. Kiseliov <roman@kiseliov.ru>."
83
+
84
+ 4. Redistributions of any form whatsoever must retain the following
85
+ acknowledgment:
86
+ "This product includes software developed by
87
+ Roman V. Kiseliov <roman@kiseliov.ru>."
88
+
89
+ THIS SOFTWARE IS PROVIDED BY Roman V. Kiseliov ``AS IS'' AND ANY
90
+ EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
91
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
92
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Roman V. Kiseliov OR
93
+ ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
94
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
95
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
96
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
97
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
98
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
99
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
100
+ OF THE POSSIBILITY OF SUCH DAMAGE.
101
+
102
+ Roman V. Kiseliov
103
+ Russia
104
+ Kursk
105
+ Libknecht St., 4
106
+
107
+ +7(0712)56-09-83
108
+
109
+ <roman@kiseliov.ru>
110
+ Subject: pyExcelerator
@@ -0,0 +1,26 @@
1
+ surpass
2
+ by Ana Nelson
3
+ http://ananelson.com
4
+ Based on original Python code by Roman V. Kiseliov
5
+
6
+ == DESCRIPTION:
7
+
8
+ Surpass is writing (and eventually reading) excel workbooks in pure Ruby. Surpass is based on xlwt (and pyExcelerator).
9
+
10
+ For comprehensive documentation, please refer to the PDF manual which is available from http://surpass.rubyforge.org or in the root directory of the source code repository.
11
+
12
+ If you like to learn from playing with working examples, then there are plenty in the examples/ and webby/examples directories of the source code.
13
+
14
+ == REQUIREMENTS:
15
+
16
+ Tested with Ruby 1.8.6 (MRI or JRuby)
17
+
18
+ This is just standard Ruby, so please feel free to try on other Ruby platforms and let me know how it goes.
19
+
20
+ == INSTALL:
21
+
22
+ sudo gem install surpass
23
+
24
+ == LICENSE:
25
+
26
+ Please see LICENSE.txt in this directory.
@@ -0,0 +1,36 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ rescue LoadError
8
+ abort '### Please install the "bones" gem ###'
9
+ end
10
+
11
+ require 'lib/surpass'
12
+
13
+ Bones {
14
+ name 'rmasalov-surpass'
15
+ authors 'Ana Nelson'
16
+ email 'ana@ananelson.com'
17
+ url 'http://surpass.rubyforge.org'
18
+ version Surpass::VERSION
19
+ }
20
+
21
+ task :default => 'spec:run'
22
+ task 'gem:release' => 'test:run'
23
+
24
+ desc "run antlr compiler"
25
+ task :antlr do
26
+ `cd lib/surpass; antlr4ruby ExcelFormula.g`
27
+ end
28
+
29
+ desc "run examples"
30
+ task :examples do
31
+ `rm examples/*.xls`
32
+ `cd examples; ls *.rb`.chomp.split.each do |f|
33
+ next if f =~ /big/
34
+ `jruby #{File.expand_path(f, "examples")}`
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib surpass]))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
@@ -0,0 +1,64 @@
1
+ module Surpass
2
+
3
+ # :stopdoc:
4
+ VERSION = '0.1.0'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args )
20
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ end
22
+
23
+ # Returns the lpath for the module. If any arguments are given,
24
+ # they will be joined to the end of the path using
25
+ # <tt>File.join</tt>.
26
+ #
27
+ def self.path( *args )
28
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
29
+ end
30
+
31
+ # Utility method used to require all files ending in .rb that lie in the
32
+ # directory below this file that has the same name as the filename passed
33
+ # in. Optionally, a specific _directory_ name can be passed in such that
34
+ # the _filename_ does not have to be equivalent to the directory.
35
+ #
36
+ def self.require_all_libs_relative_to( fname, dir = "." )
37
+ dir ||= ::File.basename(fname, '.*')
38
+ search_me = ::File.expand_path(
39
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
40
+
41
+ Dir.glob(search_me).sort.each do |rb|
42
+ next if File.basename(rb) === File.basename(__FILE__) # skip surpass.rb
43
+ next if File.basename(rb) =~ /^ExcelFormula/ unless FORMULAS_AVAILABLE
44
+ next if File.basename(rb) =~ /^excel_magic/ # already loaded this
45
+ require rb
46
+ end
47
+ end
48
+
49
+ end # module Surpass
50
+
51
+ begin
52
+ require 'rubygems'
53
+ require 'antlr3'
54
+ FORMULAS_AVAILABLE = true
55
+ rescue Exception => e
56
+ puts "antlr3 gem not found, formulas not available. Install antlr3 to enable formulas."
57
+ FORMULAS_AVAILABLE = false
58
+ end
59
+
60
+ require File.join(File.dirname(__FILE__), 'surpass', 'excel_magic')
61
+ include ExcelMagic
62
+ Surpass.require_all_libs_relative_to(__FILE__)
63
+ require 'date'
64
+
@@ -0,0 +1,393 @@
1
+ grammar ExcelFormula;
2
+
3
+ options {
4
+ language = Ruby;
5
+ k = 2;
6
+ }
7
+
8
+ @header {
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}
12
+ }
13
+
14
+ @init {
15
+ @rpn = ''
16
+ @sheet_references = []
17
+ @xcall_references = []
18
+ }
19
+
20
+ @members {
21
+ attr_accessor :rpn
22
+ }
23
+
24
+ /// @export "formula"
25
+ formula
26
+ : expr["V"]
27
+ ;
28
+
29
+ /// @export "expr"
30
+ expr[arg_type]
31
+ : prec0_expr[arg_type]
32
+ (
33
+ (
34
+ EQ { op = [PTGEQ].pack('C') }
35
+ | NE { op = [PTGNE].pack('C') }
36
+ | GT { op = [PTGGT].pack('C') }
37
+ | LT { op = [PTGLT].pack('C') }
38
+ | GE { op = [PTGGE].pack('C') }
39
+ | LE { op = [PTGLE].pack('C') }
40
+
41
+ )
42
+ prec0_expr[arg_type] { @rpn += op }
43
+ )*
44
+ ;
45
+ /// @export "prec0_expr"
46
+ prec0_expr[arg_type]
47
+ : prec1_expr[arg_type]
48
+ (
49
+ (
50
+ CONCAT { op = [PTGCONCAT].pack('C') }
51
+ )
52
+ prec1_expr[arg_type] { @rpn += op }
53
+ )*
54
+ ;
55
+ /// @export "prec1_expr"
56
+ prec1_expr[arg_type]
57
+ : prec2_expr[arg_type]
58
+ (
59
+ (
60
+ ADD { op = [PTGADD].pack('C') }
61
+ | SUB { op = [PTGSUB].pack('C') }
62
+ )
63
+ prec2_expr[arg_type] { @rpn += op }
64
+ )*
65
+ ;
66
+ /// @export "prec2_expr"
67
+ prec2_expr[arg_type]
68
+ : prec3_expr[arg_type]
69
+ (
70
+ (
71
+ MUL { op = [PTGMUL].pack('C') }
72
+ | DIV { op = [PTGDIV].pack('C') }
73
+ )
74
+ prec3_expr[arg_type] { @rpn += op }
75
+ )*
76
+ ;
77
+
78
+ /// @export "prec3_expr"
79
+ prec3_expr[arg_type]
80
+ : prec4_expr[arg_type]
81
+ (
82
+ (
83
+ POWER { op = [PTGPOWER].pack('C') }
84
+ )
85
+ prec4_expr[arg_type] { @rpn += op }
86
+ )*
87
+ ;
88
+ /// @export "prec4_expr"
89
+ prec4_expr[arg_type]
90
+ : prec5_expr[arg_type]
91
+ (
92
+ PERCENT { @rpn += [PTGPERCENT].pack('C') }
93
+ )?
94
+ ;
95
+ /// @export "prec5_expr"
96
+ prec5_expr[arg_type]
97
+ : primary[arg_type]
98
+ | SUB primary[arg_type] { @rpn += [PTGUMINUS].pack('C') }
99
+ ;
100
+ /// @end
101
+ primary[arg_type]
102
+ /// @export "boolean-string"
103
+ : TRUE_CONST
104
+ {
105
+ @rpn += [PTGBOOL, 1].pack("C2")
106
+ }
107
+ | FALSE_CONST
108
+ {
109
+ @rpn += [PTGBOOL, 0].pack("C2")
110
+ }
111
+ | str_tok = STR_CONST
112
+ {
113
+ s = str_tok.text.gsub("\"", "")
114
+ @rpn += [PTGSTR].pack("C") + [s.length].pack('v') + s
115
+ }
116
+ /// @export "numeric"
117
+ | int_tok = INT_CONST
118
+ {
119
+ int_value = int_tok.text.to_i
120
+ if int_value <= 65535
121
+ @rpn += [PTGINT, int_value].pack("Cv")
122
+ else
123
+ @rpn += [PTGNUM, int_value.to_f].pack("CE")
124
+ end
125
+ }
126
+ | num_tok = NUM_CONST
127
+ {
128
+ @rpn += [PTGNUM, num_tok.text.to_f].pack("CE")
129
+ }
130
+ /// @end
131
+ | ref2d_tok = REF2D
132
+ {
133
+ r, c = Utilities.cell_to_packed_rowcol(ref2d_tok.text)
134
+ ptg = PTGREFR + RVA_DELTA_REF[arg_type]
135
+ @rpn += [ptg, r, c].pack("Cv2")
136
+ }
137
+ | ref2d1_tok = REF2D COLON ref2d2_tok = REF2D
138
+ {
139
+ r1, c1 = Utilities.cell_to_packed_rowcol(ref2d1_tok.text)
140
+ r2, c2 = Utilities.cell_to_packed_rowcol(ref2d2_tok.text)
141
+ ptg = PTGAREAR + RVA_DELTA_AREA[arg_type]
142
+ @rpn += [ptg, r1, r2, c1, c2].pack("Cv4")
143
+ }
144
+ /// @export "parens"
145
+ | LP expr[arg_type] RP
146
+ {
147
+ @rpn += [PTGPAREN].pack('C')
148
+ }
149
+ /// @end
150
+ | sheet1 = sheet
151
+ {
152
+ sheet2 = sheet1
153
+ }
154
+ ( COLON sheet2 = sheet )? BANG ref3d_ref2d=REF2D
155
+ {
156
+ ptg = PTGREF3DR + RVA_DELTA_REF[arg_type]
157
+ r1, c1 = Utilities.cell_to_packed_rowcol(ref3d_ref2d.text)
158
+ rpn_ref2d = [0x0000, r1, c1].pack("v3")
159
+ }
160
+ ( COLON ref3d_ref2d2= REF2D
161
+ {
162
+ ptg = PTGAREA3DR + RVA_DELTA_AREA[arg_type]
163
+ r2, c2 = Utilities.cell_to_packed_rowcol(ref3d_ref2d2.text)
164
+ rpn_ref2d = [0x0000, r1, r2, c1, c2].pack("v5")
165
+ }
166
+ )?
167
+ {
168
+ @rpn += [ptg].pack("C")
169
+ @sheet_references << [sheet1, sheet2, @rpn.size]
170
+ @rpn += rpn_ref2d
171
+ }
172
+ /// @export "if-fn"
173
+ | FUNC_IF
174
+ LP expr["V"] (SEMICOLON | COMMA)
175
+ {
176
+ @rpn += [PTGATTR, 0x02, 0].pack("C2v") # tAttrIf
177
+ pos0 = @rpn.size - 2
178
+ }
179
+ expr[arg_type] (SEMICOLON | COMMA)
180
+ {
181
+ @rpn += [PTGATTR, 0x08, 0].pack("C2v") # tAttrSkip
182
+ pos1 = @rpn.size - 2
183
+
184
+ @rpn = @rpn[0...pos0] + [pos1-pos0].pack("v") + @rpn[(pos0+2)...(@rpn.size)]
185
+ }
186
+ expr[arg_type] RP
187
+ {
188
+ @rpn += [PTGATTR, 0x08, 3].pack("C2v") # tAttrSkip
189
+ @rpn += [PTGFUNCVARR, 3, 1].pack("C2v") # 3 = nargs, 1 = IF func
190
+ pos2 = @rpn.size
191
+
192
+ @rpn = @rpn[0...pos1] + [pos2-(pos1+2)-1].pack("v") + @rpn[(pos1+2)...(@rpn.size)]
193
+ }
194
+ /// @end
195
+ | FUNC_CHOOSE
196
+ {
197
+ arg_type = "R"
198
+ rpn_chunks = []
199
+ }
200
+ LP expr["V"]
201
+ {
202
+ rpn_start = @rpn.size
203
+ ref_markers = [@sheet_references.size]
204
+ }
205
+ (
206
+ (SEMICOLON | COMMA)
207
+ { mark = @rpn.size }
208
+ (
209
+ expr[arg_type]
210
+ | { @rpn += [PTGMISSARG].pack("C") }
211
+ )
212
+ {
213
+ rem = @rpn.size - mark
214
+ rpn_chunks << @rpn[mark, rem]
215
+ ref_markers << @sheet_references.size
216
+ }
217
+ )*
218
+ RP
219
+ {
220
+ @rpn = @rpn[0...rpn_start]
221
+ nc = rpn_chunks.length
222
+ chunklens = rpn_chunks.collect {|c| c.length}
223
+ skiplens = [0] * nc
224
+ skiplens[nc-1] = 3
225
+
226
+ (nc-1).downto(1) do |i|
227
+ skiplens[i-1] = skiplens[i] + chunklens[i] + 4
228
+ end
229
+ jump_pos = [2*nc + 2]
230
+
231
+ (0...nc).each do |i|
232
+ jump_pos << (jump_pos.last + chunklens[i] + 4)
233
+ end
234
+ chunk_shift = 2*nc + 6 # size of tAttrChoose
235
+
236
+ (0...nc).each do |i|
237
+ (ref_markers[i]...ref_markers[i+1]).each do |r|
238
+ ref = @sheet_references[r]
239
+ @sheet_references[r] = [ref[0], ref[1], ref[2] + chunk_shift]
240
+ end
241
+ chunk_shift += 4 # size of tAttrSkip
242
+ end
243
+
244
+ choose_rpn = []
245
+ choose_rpn << [PTGATTR, 0x04, nc].pack("CCv") # 0x04 is tAttrChoose
246
+ choose_rpn << jump_pos.pack("v*")
247
+
248
+ (0...nc).each do |i|
249
+ choose_rpn << rpn_chunks[i]
250
+ choose_rpn << [PTGATTR, 0x08, skiplens[i]].pack("CCv") # 0x08 is tAttrSkip
251
+ end
252
+ choose_rpn << [PTGFUNCVARV, nc+1, 100].pack("CCv") # 100 is CHOOSE fn
253
+ @rpn += choose_rpn.join
254
+ }
255
+ | name_tok = NAME
256
+ {
257
+ raise "[formula] found unexpected NAME token #{name_tok.text}"
258
+ }
259
+ | func_tok = NAME
260
+ {
261
+ func_toku = func_tok.text.upcase
262
+ if STD_FUNC_BY_NAME.has_key?(func_toku)
263
+ opcode, min_argc, max_argc, func_type, arg_type_str = STD_FUNC_BY_NAME[func_toku]
264
+ arg_type_list = arg_type_str.split
265
+ else
266
+ raise "[formula] unknown function #{func_tok.text}"
267
+ end
268
+ xcall = (opcode < 0)
269
+
270
+ if xcall
271
+ @xcall_references << [func_toku, @rpn.size + 1]
272
+ @rpn += [PTGNAMEXR, 0xadde, 0xefbe, 0x0000].pack("Cvvv")
273
+ end
274
+ }
275
+ LP arg_count = expr_list[arg_type_list, min_argc, max_argc] RP
276
+ {
277
+ if (arg_count > max_argc) || (arg_count < min_argc)
278
+ raise "incorrect number #{arg_count} of parameters for function: #{func_tok.text}"
279
+ end
280
+
281
+ if xcall
282
+ func_ptg = PTGFUNCVARR + RVA_DELTA[func_type]
283
+ @rpn += [func_ptg, arg_count + 1, 255].pack("CCv") # 255 is magic XCALL function
284
+ elsif (min_argc == max_argc)
285
+ func_ptg = PTGFUNCR + RVA_DELTA[func_type]
286
+ @rpn += [func_ptg, opcode].pack("Cv")
287
+ elsif (arg_count == 1) && (func_tok.text.upcase === "SUM")
288
+ @rpn += [PTGATTR, 0x10, 0].pack("CCv") # tAttrSum
289
+ else
290
+ func_ptg = PTGFUNCVARR + RVA_DELTA[func_type]
291
+ @rpn += [func_ptg, arg_count, opcode].pack("CCv")
292
+ end
293
+ }
294
+ ;
295
+
296
+ // Process arguments to a function.
297
+ /// @export "expr_list"
298
+ expr_list[arg_type_list, min_argc, max_argc] returns [arg_cnt]
299
+ @init
300
+ {
301
+ arg_cnt = 0
302
+
303
+ # Set these for processing first argument,
304
+ # it's simpler because first argument type can't be '...'
305
+ arg_type = arg_type_list.first
306
+
307
+ # need to check for '-' for a fn with no arguments
308
+ arg_cnt += 1 unless arg_type === '-'
309
+ }
310
+ :
311
+ (expr[arg_type]
312
+ (
313
+ (SEMICOLON | COMMA) { arg_cnt += 1 }
314
+ (
315
+ // *either* we find an argument after the comma/semicolon in which case process it...
316
+ expr[arg_type]
317
+ {
318
+ if arg_cnt - 2 < arg_type_list.size
319
+ arg_type = arg_type_list[arg_cnt - 2]
320
+ else
321
+ if arg_type_list.last === "..."
322
+ # e.g. "V R ..." arbitrary number of args of type R
323
+ # this will always be last element in arg_type_list
324
+ # 2nd to last element will provide type
325
+ arg_type = arg_type_list[arg_type_list.size - 2]
326
+ else
327
+ # Just read last element normally.
328
+ arg_type = arg_type_list[arg_cnt - 2]
329
+ end
330
+ end
331
+ }
332
+ // ... *or* we have a missing argument and need to insert a placeholder
333
+ | { @rpn += [PTGMISSARG].pack("C") }
334
+ )
335
+ )*
336
+ )
337
+ | // Some functions have no arguments e.g. pi()
338
+ ;
339
+ /// @end
340
+ sheet returns[ref]
341
+ : sheet_ref_name = NAME
342
+ { ref = sheet_ref_name.text }
343
+ | sheet_ref_int = INT_CONST
344
+ { ref = sheet_ref_int.text }
345
+ | sheet_ref_quote = QUOTENAME
346
+ { ref = sheet_ref_quote.text[1, len(sheet_ref_quote.text) - 1].replace("''", "'") }
347
+ ;
348
+
349
+ /// @export "expr-tokens"
350
+ EQ: '=';
351
+ LT: '<';
352
+ GT: '>';
353
+ NE: '<>';
354
+ LE: '<=';
355
+ GE: '>=';
356
+ /// @export "prec0_expr-tokens"
357
+ CONCAT: '&';
358
+ /// @export "prec1_expr-tokens"
359
+ ADD: '+';
360
+ SUB: '-';
361
+ /// @export "prec2_expr-tokens"
362
+ MUL: '*';
363
+ DIV: '/';
364
+ /// @export "prec3_expr-tokens"
365
+ POWER: '^';
366
+ /// @export "prec4_expr-tokens"
367
+ PERCENT: '\%';
368
+ /// @end
369
+
370
+ COLON: ':';
371
+ SEMICOLON: ';';
372
+ COMMA: ',';
373
+
374
+ LP: '(';
375
+ RP: ')';
376
+ BANG: '!';
377
+
378
+ fragment DIGIT: '0'..'9';
379
+
380
+ INT_CONST: DIGIT+ ;
381
+ NUM_CONST: DIGIT* '.' DIGIT+ (('E'|'e') ('+'|'-')? DIGIT+)?;
382
+ STR_CONST: '"' (~'"')+ '"';
383
+ REF2D: '$'? ('A'..'I')? ('A'..'Z') '$'? DIGIT+;
384
+ TRUE_CONST: ('T'|'t') ('R'|'r') ('U'|'u') ('E'|'e') ;
385
+ FALSE_CONST: ('F'|'f') ('A'|'a') ('L'|'l') ('S'|'s') ('E'|'e') ;
386
+ QUOTENAME: '\'(?:[^\']|\'\')*\'';
387
+ FUNC_IF: 'IF';
388
+ FUNC_CHOOSE: 'CHOOSE';
389
+
390
+ fragment ALPHA: 'a'..'z' | 'A'..'Z';
391
+ NAME: ALPHA+ ;
392
+
393
+ WS: (' ')+ {skip()};