1### $ANTLR 2.7.7 (20060930): "xlwt/excel-formula.g" -> "ExcelFormulaParser.py"$
2### import antlr and other modules ..
3from . import antlr
4
5### header action >>>
6import struct
7from . import Utils
8from .UnicodeUtils import upack1
9from .ExcelMagic import *
10
11_RVAdelta =     {"R": 0, "V": 0x20, "A": 0x40}
12_RVAdeltaRef =  {"R": 0, "V": 0x20, "A": 0x40, "D": 0x20}
13_RVAdeltaArea = {"R": 0, "V": 0x20, "A": 0x40, "D": 0}
14
15
16class FormulaParseException(Exception):
17   """
18   An exception indicating that a Formula could not be successfully parsed.
19   """
20### header action <<<
21### preamble action>>>
22
23### preamble action <<<
24
25### >>>The Known Token Types <<<
26SKIP                = antlr.SKIP
27INVALID_TYPE        = antlr.INVALID_TYPE
28EOF_TYPE            = antlr.EOF_TYPE
29EOF                 = antlr.EOF
30NULL_TREE_LOOKAHEAD = antlr.NULL_TREE_LOOKAHEAD
31MIN_USER_TYPE       = antlr.MIN_USER_TYPE
32TRUE_CONST = 4
33FALSE_CONST = 5
34STR_CONST = 6
35NUM_CONST = 7
36INT_CONST = 8
37FUNC_IF = 9
38FUNC_CHOOSE = 10
39NAME = 11
40QUOTENAME = 12
41EQ = 13
42NE = 14
43GT = 15
44LT = 16
45GE = 17
46LE = 18
47ADD = 19
48SUB = 20
49MUL = 21
50DIV = 22
51POWER = 23
52PERCENT = 24
53LP = 25
54RP = 26
55LB = 27
56RB = 28
57COLON = 29
58COMMA = 30
59SEMICOLON = 31
60REF2D = 32
61REF2D_R1C1 = 33
62BANG = 34
63CONCAT = 35
64
65class Parser(antlr.LLkParser):
66    ### user action >>>
67    ### user action <<<
68
69    def __init__(self, *args, **kwargs):
70        antlr.LLkParser.__init__(self, *args, **kwargs)
71        self.tokenNames = _tokenNames
72        ### __init__ header action >>>
73        self.rpn = b""
74        self.sheet_references = []
75        self.xcall_references = []
76        ### __init__ header action <<<
77
78    def formula(self):
79
80        pass
81        self.expr("V")
82
83    def expr(self,
84        arg_type
85    ):
86
87        pass
88        self.prec0_expr(arg_type)
89        while True:
90            if ((self.LA(1) >= EQ and self.LA(1) <= LE)):
91                pass
92                la1 = self.LA(1)
93                if False:
94                    pass
95                elif la1 and la1 in [EQ]:
96                    pass
97                    self.match(EQ)
98                    op = struct.pack('B', ptgEQ)
99                elif la1 and la1 in [NE]:
100                    pass
101                    self.match(NE)
102                    op = struct.pack('B', ptgNE)
103                elif la1 and la1 in [GT]:
104                    pass
105                    self.match(GT)
106                    op = struct.pack('B', ptgGT)
107                elif la1 and la1 in [LT]:
108                    pass
109                    self.match(LT)
110                    op = struct.pack('B', ptgLT)
111                elif la1 and la1 in [GE]:
112                    pass
113                    self.match(GE)
114                    op = struct.pack('B', ptgGE)
115                elif la1 and la1 in [LE]:
116                    pass
117                    self.match(LE)
118                    op = struct.pack('B', ptgLE)
119                else:
120                        raise antlr.NoViableAltException(self.LT(1), self.getFilename())
121
122                self.prec0_expr(arg_type)
123                self.rpn += op
124            else:
125                break
126
127
128    def prec0_expr(self,
129        arg_type
130    ):
131
132        pass
133        self.prec1_expr(arg_type)
134        while True:
135            if (self.LA(1)==CONCAT):
136                pass
137                pass
138                self.match(CONCAT)
139                op = struct.pack('B', ptgConcat)
140                self.prec1_expr(arg_type)
141                self.rpn += op
142            else:
143                break
144
145
146    def prec1_expr(self,
147        arg_type
148    ):
149
150        pass
151        self.prec2_expr(arg_type)
152        while True:
153            if (self.LA(1)==ADD or self.LA(1)==SUB):
154                pass
155                la1 = self.LA(1)
156                if False:
157                    pass
158                elif la1 and la1 in [ADD]:
159                    pass
160                    self.match(ADD)
161                    op = struct.pack('B', ptgAdd)
162                elif la1 and la1 in [SUB]:
163                    pass
164                    self.match(SUB)
165                    op = struct.pack('B', ptgSub)
166                else:
167                        raise antlr.NoViableAltException(self.LT(1), self.getFilename())
168
169                self.prec2_expr(arg_type)
170                self.rpn += op;
171                          # print "**prec1_expr4 %s" % arg_type
172            else:
173                break
174
175
176    def prec2_expr(self,
177        arg_type
178    ):
179
180        pass
181        self.prec3_expr(arg_type)
182        while True:
183            if (self.LA(1)==MUL or self.LA(1)==DIV):
184                pass
185                la1 = self.LA(1)
186                if False:
187                    pass
188                elif la1 and la1 in [MUL]:
189                    pass
190                    self.match(MUL)
191                    op = struct.pack('B', ptgMul)
192                elif la1 and la1 in [DIV]:
193                    pass
194                    self.match(DIV)
195                    op = struct.pack('B', ptgDiv)
196                else:
197                        raise antlr.NoViableAltException(self.LT(1), self.getFilename())
198
199                self.prec3_expr(arg_type)
200                self.rpn += op
201            else:
202                break
203
204
205    def prec3_expr(self,
206        arg_type
207    ):
208
209        pass
210        self.prec4_expr(arg_type)
211        while True:
212            if (self.LA(1)==POWER):
213                pass
214                pass
215                self.match(POWER)
216                op = struct.pack('B', ptgPower)
217                self.prec4_expr(arg_type)
218                self.rpn += op
219            else:
220                break
221
222
223    def prec4_expr(self,
224        arg_type
225    ):
226
227        pass
228        self.prec5_expr(arg_type)
229        la1 = self.LA(1)
230        if False:
231            pass
232        elif la1 and la1 in [PERCENT]:
233            pass
234            self.match(PERCENT)
235            self.rpn += struct.pack('B', ptgPercent)
236        elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,RP,COMMA,SEMICOLON,CONCAT]:
237            pass
238        else:
239                raise antlr.NoViableAltException(self.LT(1), self.getFilename())
240
241
242    def prec5_expr(self,
243        arg_type
244    ):
245
246        la1 = self.LA(1)
247        if False:
248            pass
249        elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,LP,REF2D]:
250            pass
251            self.primary(arg_type)
252        elif la1 and la1 in [SUB]:
253            pass
254            self.match(SUB)
255            self.primary(arg_type)
256            self.rpn += struct.pack('B', ptgUminus)
257        else:
258                raise antlr.NoViableAltException(self.LT(1), self.getFilename())
259
260
261    def primary(self,
262        arg_type
263    ):
264
265        str_tok = None
266        int_tok = None
267        num_tok = None
268        ref2d_tok = None
269        ref2d1_tok = None
270        ref2d2_tok = None
271        ref3d_ref2d = None
272        ref3d_ref2d2 = None
273        name_tok = None
274        func_tok = None
275        la1 = self.LA(1)
276        if False:
277            pass
278        elif la1 and la1 in [TRUE_CONST]:
279            pass
280            self.match(TRUE_CONST)
281            self.rpn += struct.pack("2B", ptgBool, 1)
282        elif la1 and la1 in [FALSE_CONST]:
283            pass
284            self.match(FALSE_CONST)
285            self.rpn += struct.pack("2B", ptgBool, 0)
286        elif la1 and la1 in [STR_CONST]:
287            pass
288            str_tok = self.LT(1)
289            self.match(STR_CONST)
290            self.rpn += struct.pack("B", ptgStr) + upack1(str_tok.text[1:-1].replace("\"\"", "\""))
291        elif la1 and la1 in [NUM_CONST]:
292            pass
293            num_tok = self.LT(1)
294            self.match(NUM_CONST)
295            self.rpn += struct.pack("<Bd", ptgNum, float(num_tok.text))
296        elif la1 and la1 in [FUNC_IF]:
297            pass
298            self.match(FUNC_IF)
299            self.match(LP)
300            self.expr("V")
301            la1 = self.LA(1)
302            if False:
303                pass
304            elif la1 and la1 in [SEMICOLON]:
305                pass
306                self.match(SEMICOLON)
307            elif la1 and la1 in [COMMA]:
308                pass
309                self.match(COMMA)
310            else:
311                    raise antlr.NoViableAltException(self.LT(1), self.getFilename())
312
313            self.rpn += struct.pack("<BBH", ptgAttr, 0x02, 0) # tAttrIf
314            pos0 = len(self.rpn) - 2
315            self.expr(arg_type)
316            la1 = self.LA(1)
317            if False:
318                pass
319            elif la1 and la1 in [SEMICOLON]:
320                pass
321                self.match(SEMICOLON)
322            elif la1 and la1 in [COMMA]:
323                pass
324                self.match(COMMA)
325            else:
326                    raise antlr.NoViableAltException(self.LT(1), self.getFilename())
327
328            self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 0) # tAttrSkip
329            pos1 = len(self.rpn) - 2
330            self.rpn = self.rpn[:pos0] + struct.pack("<H", pos1-pos0) + self.rpn[pos0+2:]
331            self.expr(arg_type)
332            self.match(RP)
333            self.rpn += struct.pack("<BBH", ptgAttr, 0x08, 3) # tAttrSkip
334            self.rpn += struct.pack("<BBH", ptgFuncVarR, 3, 1) # 3 = nargs, 1 = IF func
335            pos2 = len(self.rpn)
336            self.rpn = self.rpn[:pos1] + struct.pack("<H", pos2-(pos1+2)-1) + self.rpn[pos1+2:]
337        elif la1 and la1 in [FUNC_CHOOSE]:
338            pass
339            self.match(FUNC_CHOOSE)
340            arg_type = "R"
341            rpn_chunks = []
342            self.match(LP)
343            self.expr("V")
344            rpn_start = len(self.rpn)
345            ref_markers = [len(self.sheet_references)]
346            while True:
347                if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON):
348                    pass
349                    la1 = self.LA(1)
350                    if False:
351                        pass
352                    elif la1 and la1 in [SEMICOLON]:
353                        pass
354                        self.match(SEMICOLON)
355                    elif la1 and la1 in [COMMA]:
356                        pass
357                        self.match(COMMA)
358                    else:
359                            raise antlr.NoViableAltException(self.LT(1), self.getFilename())
360
361                    mark = len(self.rpn)
362                    la1 = self.LA(1)
363                    if False:
364                        pass
365                    elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
366                        pass
367                        self.expr(arg_type)
368                    elif la1 and la1 in [RP,COMMA,SEMICOLON]:
369                        pass
370                        self.rpn += struct.pack("B", ptgMissArg)
371                    else:
372                            raise antlr.NoViableAltException(self.LT(1), self.getFilename())
373
374                    rpn_chunks.append(self.rpn[mark:])
375                    ref_markers.append(len(self.sheet_references))
376                else:
377                    break
378
379            self.match(RP)
380            self.rpn = self.rpn[:rpn_start]
381            nc = len(rpn_chunks)
382            chunklens = [len(chunk) for chunk in rpn_chunks]
383            skiplens = [0] * nc
384            skiplens[-1] = 3
385            for ic in xrange(nc-1, 0, -1):
386               skiplens[ic-1] = skiplens[ic] + chunklens[ic] + 4
387            jump_pos = [2 * nc + 2]
388            for ic in xrange(nc):
389               jump_pos.append(jump_pos[-1] + chunklens[ic] + 4)
390            chunk_shift = 2 * nc + 6 # size of tAttrChoose
391            for ic in xrange(nc):
392               for refx in xrange(ref_markers[ic], ref_markers[ic+1]):
393                   ref = self.sheet_references[refx]
394                   self.sheet_references[refx] = (ref[0], ref[1], ref[2] + chunk_shift)
395               chunk_shift += 4 # size of tAttrSkip
396            choose_rpn = []
397            choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x04, nc)) # 0x04 is tAttrChoose
398            choose_rpn.append(struct.pack("<%dH" % (nc+1), *jump_pos))
399            for ic in xrange(nc):
400               choose_rpn.append(rpn_chunks[ic])
401               choose_rpn.append(struct.pack("<BBH", ptgAttr, 0x08, skiplens[ic])) # 0x08 is tAttrSkip
402            choose_rpn.append(struct.pack("<BBH", ptgFuncVarV, nc+1, 100)) # 100 is CHOOSE fn
403            self.rpn += "".join(choose_rpn)
404        elif la1 and la1 in [LP]:
405            pass
406            self.match(LP)
407            self.expr(arg_type)
408            self.match(RP)
409            self.rpn += struct.pack("B", ptgParen)
410        else:
411            if (self.LA(1)==INT_CONST) and (_tokenSet_0.member(self.LA(2))):
412                pass
413                int_tok = self.LT(1)
414                self.match(INT_CONST)
415                # print "**int_const", int_tok.text
416                int_value = int(int_tok.text)
417                if int_value <= 65535:
418                   self.rpn += struct.pack("<BH", ptgInt, int_value)
419                else:
420                   self.rpn += struct.pack("<Bd", ptgNum, float(int_value))
421            elif (self.LA(1)==REF2D) and (_tokenSet_0.member(self.LA(2))):
422                pass
423                ref2d_tok = self.LT(1)
424                self.match(REF2D)
425                # print "**ref2d %s %s" % (ref2d_tok.text, arg_type)
426                r, c = Utils.cell_to_packed_rowcol(ref2d_tok.text)
427                ptg = ptgRefR + _RVAdeltaRef[arg_type]
428                self.rpn += struct.pack("<B2H", ptg, r, c)
429            elif (self.LA(1)==REF2D) and (self.LA(2)==COLON):
430                pass
431                ref2d1_tok = self.LT(1)
432                self.match(REF2D)
433                self.match(COLON)
434                ref2d2_tok = self.LT(1)
435                self.match(REF2D)
436                r1, c1 = Utils.cell_to_packed_rowcol(ref2d1_tok.text)
437                r2, c2 = Utils.cell_to_packed_rowcol(ref2d2_tok.text)
438                ptg = ptgAreaR + _RVAdeltaArea[arg_type]
439                self.rpn += struct.pack("<B4H", ptg, r1, r2, c1, c2)
440            elif (self.LA(1)==INT_CONST or self.LA(1)==NAME or self.LA(1)==QUOTENAME) and (self.LA(2)==COLON or self.LA(2)==BANG):
441                pass
442                sheet1=self.sheet()
443                sheet2 = sheet1
444                la1 = self.LA(1)
445                if False:
446                    pass
447                elif la1 and la1 in [COLON]:
448                    pass
449                    self.match(COLON)
450                    sheet2=self.sheet()
451                elif la1 and la1 in [BANG]:
452                    pass
453                else:
454                        raise antlr.NoViableAltException(self.LT(1), self.getFilename())
455
456                self.match(BANG)
457                ref3d_ref2d = self.LT(1)
458                self.match(REF2D)
459                ptg = ptgRef3dR + _RVAdeltaRef[arg_type]
460                rpn_ref2d = ""
461                r1, c1 = Utils.cell_to_packed_rowcol(ref3d_ref2d.text)
462                rpn_ref2d = struct.pack("<3H", 0x0000, r1, c1)
463                la1 = self.LA(1)
464                if False:
465                    pass
466                elif la1 and la1 in [COLON]:
467                    pass
468                    self.match(COLON)
469                    ref3d_ref2d2 = self.LT(1)
470                    self.match(REF2D)
471                    ptg = ptgArea3dR + _RVAdeltaArea[arg_type]
472                    r2, c2 = Utils.cell_to_packed_rowcol(ref3d_ref2d2.text)
473                    rpn_ref2d = struct.pack("<5H", 0x0000, r1, r2, c1, c2)
474                elif la1 and la1 in [EOF,EQ,NE,GT,LT,GE,LE,ADD,SUB,MUL,DIV,POWER,PERCENT,RP,COMMA,SEMICOLON,CONCAT]:
475                    pass
476                else:
477                        raise antlr.NoViableAltException(self.LT(1), self.getFilename())
478
479                self.rpn += struct.pack("<B", ptg)
480                self.sheet_references.append((sheet1, sheet2, len(self.rpn)))
481                self.rpn += rpn_ref2d
482            elif (self.LA(1)==NAME) and (_tokenSet_0.member(self.LA(2))):
483                pass
484                name_tok = self.LT(1)
485                self.match(NAME)
486                raise Exception("[formula] found unexpected NAME token (%r)" % name_tok.txt)
487                # #### TODO: handle references to defined names here
488            elif (self.LA(1)==NAME) and (self.LA(2)==LP):
489                pass
490                func_tok = self.LT(1)
491                self.match(NAME)
492                func_toku = func_tok.text.upper()
493                if func_toku in all_funcs_by_name:
494                   (opcode,
495                   min_argc,
496                   max_argc,
497                   func_type,
498                   arg_type_str) = all_funcs_by_name[func_toku]
499                   arg_type_list = list(arg_type_str)
500                else:
501                   raise Exception("[formula] unknown function (%s)" % func_tok.text)
502                # print "**func_tok1 %s %s" % (func_toku, func_type)
503                xcall = opcode < 0
504                if xcall:
505                   # The name of the add-in function is passed as the 1st arg
506                   # of the hidden XCALL function
507                   self.xcall_references.append((func_toku, len(self.rpn) + 1))
508                   self.rpn += struct.pack("<BHHH",
509                       ptgNameXR,
510                       0xadde, # ##PATCHME## index to REF entry in EXTERNSHEET record
511                       0xefbe, # ##PATCHME## one-based index to EXTERNNAME record
512                       0x0000) # unused
513                self.match(LP)
514                arg_count=self.expr_list(arg_type_list, min_argc, max_argc)
515                self.match(RP)
516                if arg_count > max_argc or arg_count < min_argc:
517                   raise Exception("%d parameters for function: %s" % (arg_count, func_tok.text))
518                if xcall:
519                   func_ptg = ptgFuncVarR + _RVAdelta[func_type]
520                   self.rpn += struct.pack("<2BH", func_ptg, arg_count + 1, 255) # 255 is magic XCALL function
521                elif min_argc == max_argc:
522                   func_ptg = ptgFuncR + _RVAdelta[func_type]
523                   self.rpn += struct.pack("<BH", func_ptg, opcode)
524                elif arg_count == 1 and func_tok.text.upper() == "SUM":
525                   self.rpn += struct.pack("<BBH", ptgAttr, 0x10, 0) # tAttrSum
526                else:
527                   func_ptg = ptgFuncVarR + _RVAdelta[func_type]
528                   self.rpn += struct.pack("<2BH", func_ptg, arg_count, opcode)
529            else:
530                raise antlr.NoViableAltException(self.LT(1), self.getFilename())
531
532
533    def sheet(self):
534        ref = None
535
536        sheet_ref_name = None
537        sheet_ref_int = None
538        sheet_ref_quote = None
539        la1 = self.LA(1)
540        if False:
541            pass
542        elif la1 and la1 in [NAME]:
543            pass
544            sheet_ref_name = self.LT(1)
545            self.match(NAME)
546            ref = sheet_ref_name.text
547        elif la1 and la1 in [INT_CONST]:
548            pass
549            sheet_ref_int = self.LT(1)
550            self.match(INT_CONST)
551            ref = sheet_ref_int.text
552        elif la1 and la1 in [QUOTENAME]:
553            pass
554            sheet_ref_quote = self.LT(1)
555            self.match(QUOTENAME)
556            ref = sheet_ref_quote.text[1:-1].replace("''", "'")
557        else:
558                raise antlr.NoViableAltException(self.LT(1), self.getFilename())
559
560        return ref
561
562    def expr_list(self,
563        arg_type_list, min_argc, max_argc
564    ):
565        arg_cnt = None
566
567        arg_cnt = 0
568        arg_type = arg_type_list[arg_cnt]
569        # print "**expr_list1[%d] req=%s" % (arg_cnt, arg_type)
570        la1 = self.LA(1)
571        if False:
572            pass
573        elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
574            pass
575            self.expr(arg_type)
576            arg_cnt += 1
577            while True:
578                if (self.LA(1)==COMMA or self.LA(1)==SEMICOLON):
579                    pass
580                    if arg_cnt < len(arg_type_list):
581                       arg_type = arg_type_list[arg_cnt]
582                    else:
583                       arg_type = arg_type_list[-1]
584                    if arg_type == "+":
585                       arg_type = arg_type_list[-2]
586                    # print "**expr_list2[%d] req=%s" % (arg_cnt, arg_type)
587                    la1 = self.LA(1)
588                    if False:
589                        pass
590                    elif la1 and la1 in [SEMICOLON]:
591                        pass
592                        self.match(SEMICOLON)
593                    elif la1 and la1 in [COMMA]:
594                        pass
595                        self.match(COMMA)
596                    else:
597                            raise antlr.NoViableAltException(self.LT(1), self.getFilename())
598
599                    la1 = self.LA(1)
600                    if False:
601                        pass
602                    elif la1 and la1 in [TRUE_CONST,FALSE_CONST,STR_CONST,NUM_CONST,INT_CONST,FUNC_IF,FUNC_CHOOSE,NAME,QUOTENAME,SUB,LP,REF2D]:
603                        pass
604                        self.expr(arg_type)
605                    elif la1 and la1 in [RP,COMMA,SEMICOLON]:
606                        pass
607                        self.rpn += struct.pack("B", ptgMissArg)
608                    else:
609                            raise antlr.NoViableAltException(self.LT(1), self.getFilename())
610
611                    arg_cnt += 1
612                else:
613                    break
614
615        elif la1 and la1 in [RP]:
616            pass
617        else:
618                raise antlr.NoViableAltException(self.LT(1), self.getFilename())
619
620        return arg_cnt
621
622
623_tokenNames = [
624    "<0>",
625    "EOF",
626    "<2>",
627    "NULL_TREE_LOOKAHEAD",
628    "TRUE_CONST",
629    "FALSE_CONST",
630    "STR_CONST",
631    "NUM_CONST",
632    "INT_CONST",
633    "FUNC_IF",
634    "FUNC_CHOOSE",
635    "NAME",
636    "QUOTENAME",
637    "EQ",
638    "NE",
639    "GT",
640    "LT",
641    "GE",
642    "LE",
643    "ADD",
644    "SUB",
645    "MUL",
646    "DIV",
647    "POWER",
648    "PERCENT",
649    "LP",
650    "RP",
651    "LB",
652    "RB",
653    "COLON",
654    "COMMA",
655    "SEMICOLON",
656    "REF2D",
657    "REF2D_R1C1",
658    "BANG",
659    "CONCAT"
660]
661
662
663### generate bit set
664def mk_tokenSet_0():
665    ### var1
666    data = [ 37681618946, 0]
667    return data
668_tokenSet_0 = antlr.BitSet(mk_tokenSet_0())
669
670