1"""@package grass.temporal
2
3Temporal operator evaluation with PLY
4
5(C) 2013 by the GRASS Development Team
6This program is free software under the GNU General Public
7License (>=v2). Read the file COPYING that comes with GRASS
8for details.
9
10:authors: Thomas Leppelt and Soeren Gebbert
11
12.. code-block:: python
13
14    >>> p = TemporalOperatorParser()
15    >>> expression =  "{equal|equivalent|cover|in|meet|contain|overlap}"
16    >>> p.parse(expression, optype = 'relation')
17    >>> print((p.relations, p.temporal, p.function))
18    (['equal', 'equivalent', 'cover', 'in', 'meet', 'contain', 'overlap'], None, None)
19
20    >>> p = TemporalOperatorParser()
21    >>> expression =  "{equal| during}"
22    >>> p.parse(expression, optype = 'relation')
23    >>> print((p.relations, p.temporal, p.function))
24    (['equal', 'during'], None, None)
25    >>> p = TemporalOperatorParser()
26    >>> expression =  "{contains | starts}"
27    >>> p.parse(expression)
28    >>> print((p.relations, p.temporal, p.function))
29    (['contains', 'starts'], None, None)
30    >>> p = TemporalOperatorParser()
31    >>> expression =  "{&&, during}"
32    >>> p.parse(expression, optype = 'boolean')
33    >>> print((p.relations, p.temporal, p.function, p.aggregate))
34    (['during'], 'l', '&&', '&')
35    >>> p = TemporalOperatorParser()
36    >>> expression =  "{||, equal | during}"
37    >>> p.parse(expression, optype = 'boolean')
38    >>> print((p.relations, p.temporal, p.function, p.aggregate))
39    (['equal', 'during'], 'l', '||', '|')
40    >>> p = TemporalOperatorParser()
41    >>> expression =  "{||, equal | during, &}"
42    >>> p.parse(expression, optype = 'boolean')
43    >>> print((p.relations, p.temporal, p.function, p.aggregate))
44    (['equal', 'during'], 'l', '||', '&')
45    >>> p = TemporalOperatorParser()
46    >>> expression =  "{&&, during, |}"
47    >>> p.parse(expression, optype = 'boolean')
48    >>> print((p.relations, p.temporal, p.function, p.aggregate))
49    (['during'], 'l', '&&', '|')
50    >>> p = TemporalOperatorParser()
51    >>> expression =  "{&&, during, |, r}"
52    >>> p.parse(expression, optype = 'boolean')
53    >>> print((p.relations, p.temporal, p.function, p.aggregate))
54    (['during'], 'r', '&&', '|')
55    >>> p = TemporalOperatorParser()
56    >>> expression =  "{&&, during, u}"
57    >>> p.parse(expression, optype = 'boolean')
58    >>> print((p.relations, p.temporal, p.function, p.aggregate))
59    (['during'], 'u', '&&', '&')
60    >>> p = TemporalOperatorParser()
61    >>> expression =  "{:, during, r}"
62    >>> p.parse(expression, optype = 'select')
63    >>> print((p.relations, p.temporal, p.function))
64    (['during'], 'r', ':')
65    >>> p = TemporalOperatorParser()
66    >>> expression =  "{!:, equal | contains, d}"
67    >>> p.parse(expression, optype = 'select')
68    >>> print((p.relations, p.temporal, p.function))
69    (['equal', 'contains'], 'd', '!:')
70    >>> p = TemporalOperatorParser()
71    >>> expression =  "{#, during, r}"
72    >>> p.parse(expression, optype = 'hash')
73    >>> print((p.relations, p.temporal, p.function))
74    (['during'], 'r', '#')
75    >>> p = TemporalOperatorParser()
76    >>> expression =  "{#, equal | contains}"
77    >>> p.parse(expression, optype = 'hash')
78    >>> print((p.relations, p.temporal, p.function))
79    (['equal', 'contains'], 'l', '#')
80    >>> p = TemporalOperatorParser()
81    >>> expression =  "{+, during, r}"
82    >>> p.parse(expression, optype = 'raster')
83    >>> print((p.relations, p.temporal, p.function))
84    (['during'], 'r', '+')
85    >>> p = TemporalOperatorParser()
86    >>> expression =  "{/, equal | contains}"
87    >>> p.parse(expression, optype = 'raster')
88    >>> print((p.relations, p.temporal, p.function))
89    (['equal', 'contains'], 'l', '/')
90    >>> p = TemporalOperatorParser()
91    >>> expression =  "{+, equal | contains,intersect}"
92    >>> p.parse(expression, optype = 'raster')
93    >>> print((p.relations, p.temporal, p.function))
94    (['equal', 'contains'], 'i', '+')
95    >>> p = TemporalOperatorParser()
96    >>> expression =  "{*, contains,disjoint}"
97    >>> p.parse(expression, optype = 'raster')
98    >>> print((p.relations, p.temporal, p.function))
99    (['contains'], 'd', '*')
100    >>> p = TemporalOperatorParser()
101    >>> expression =  "{~, equal,left}"
102    >>> p.parse(expression, optype = 'overlay')
103    >>> print((p.relations, p.temporal, p.function))
104    (['equal'], 'l', '~')
105    >>> p = TemporalOperatorParser()
106    >>> expression =  "{^, over,right}"
107    >>> p.parse(expression, optype = 'overlay')
108    >>> print((p.relations, p.temporal, p.function))
109    (['overlaps', 'overlapped'], 'r', '^')
110    >>> p = TemporalOperatorParser()
111    >>> expression =  "{&&, equal | during | contains | starts, &}"
112    >>> p.parse(expression, optype = 'boolean')
113    >>> print((p.relations, p.temporal, p.function, p.aggregate))
114    (['equal', 'during', 'contains', 'starts'], 'l', '&&', '&')
115    >>> p = TemporalOperatorParser()
116    >>> expression =  "{&&, equal | during | contains | starts, &&&&&}"
117    >>> p.parse(expression, optype = 'boolean')
118    Traceback (most recent call last):
119    SyntaxError: Unexpected syntax error in expression "{&&, equal | during | contains | starts, &&&&&}" at position 42 near &
120    >>> p = TemporalOperatorParser()
121    >>> expression =  "{+, starting}"
122    >>> p.parse(expression)
123    Traceback (most recent call last):
124    SyntaxError: syntax error on line 1 position 4 near 'starting'
125    >>> p = TemporalOperatorParser()
126    >>> expression =  "{nope, start, |, l}"
127    >>> p.parse(expression)
128    Traceback (most recent call last):
129    SyntaxError: syntax error on line 1 position 1 near 'nope'
130    >>> p = TemporalOperatorParser()
131    >>> expression =  "{++, start, |, l}"
132    >>> p.parse(expression)
133    Traceback (most recent call last):
134    SyntaxError: Unexpected syntax error in expression "{++, start, |, l}" at position 2 near +
135    >>> p = TemporalOperatorParser()
136    >>> expression =  "{^, over, right}"
137    >>> p.parse(expression, optype='rter')
138    Traceback (most recent call last):
139    SyntaxError: Unknown optype rter, must be one of ['select', 'boolean', 'raster', 'hash', 'relation', 'overlay']
140
141"""
142from __future__ import print_function
143
144try:
145    import ply.lex as lex
146    import ply.yacc as yacc
147except:
148    pass
149
150class TemporalOperatorLexer(object):
151    """Lexical analyzer for the GRASS GIS temporal operator"""
152
153    # Functions that defines topological relations.
154    relations = {
155        # temporal relations
156        'equal'      : "EQUAL",
157        'follows'    : "FOLLOWS",
158        'precedes'   : "PRECEDES",
159        'overlaps'   : "OVERLAPS",
160        'overlapped' : "OVERLAPPED",
161        'during'     : "DURING",
162        'starts'     : "STARTS",
163        'finishes'   : "FINISHES",
164        'contains'   : "CONTAINS",
165        'started'    : "STARTED",
166        'finished'   : "FINISHED",
167        'over'       : "OVER",
168        # spatial relations
169        'equivalent' : "EQUIVALENT",
170        'cover'      : "COVER",
171        'overlap'    : "OVERLAP",
172        'in'         : "IN",
173        'contain'    : "CONTAIN",
174        'meet'       : "MEET"
175        }
176
177    # This is the list of token names.
178    tokens = (
179        'COMMA',
180        'LEFTREF',
181        'RIGHTREF',
182        'UNION',
183        'DISJOINT',
184        'INTERSECT',
185        'HASH',
186        'OR',
187        'AND',
188        'DISOR',
189        'XOR',
190        'NOT',
191        'MOD',
192        'DIV',
193        'MULT',
194        'ADD',
195        'SUB',
196        'T_SELECT',
197        'T_NOT_SELECT',
198        'CLPAREN',
199        'CRPAREN',
200    )
201
202    # Build the token list
203    tokens = tokens + tuple(relations.values())
204
205    # Regular expression rules for simple tokens
206    t_T_SELECT       = r':'
207    t_T_NOT_SELECT   = r'!:'
208    t_COMMA          = r','
209    t_LEFTREF        = '^[l|left]'
210    t_RIGHTREF       = '^[r|right]'
211    t_UNION          = '^[u|union]'
212    t_DISJOINT       = '^[d|disjoint]'
213    t_INTERSECT      = '^[i|intersect]'
214    t_HASH           = r'\#'
215    t_OR             = r'[\|]'
216    t_AND            = r'[&]'
217    t_DISOR          = r'\+'
218    t_XOR            = r'\^'
219    t_NOT            = r'\~'
220    t_MOD            = r'[\%]'
221    t_DIV            = r'[\/]'
222    t_MULT           = r'[\*]'
223    t_ADD            = r'[\+]'
224    t_SUB            = r'[-]'
225    t_CLPAREN        = r'\{'
226    t_CRPAREN        = r'\}'
227
228    # These are the things that should be ignored.
229    t_ignore = ' \t\n'
230
231    # Track line numbers.
232    def t_newline(self, t):
233        r'\n+'
234        t.lineno += len(t.value)
235
236    def t_NAME(self, t):
237        r'[a-zA-Z_][a-zA-Z_0-9]*'
238        return self.temporal_symbol(t)
239
240    # Parse symbols
241    def temporal_symbol(self, t):
242        # Check for reserved words
243        if t.value in TemporalOperatorLexer.relations.keys():
244            t.type = TemporalOperatorLexer.relations.get(t.value)
245        elif t.value == 'l' or t.value == 'left':
246            t.value = 'l'
247            t.type = 'LEFTREF'
248        elif t.value == 'r' or t.value == 'right':
249            t.value = 'r'
250            t.type = 'RIGHTREF'
251        elif t.value == 'u' or t.value == 'union':
252            t.value = 'u'
253            t.type = 'UNION'
254        elif t.value == 'd' or t.value == 'disjoint':
255            t.value = 'd'
256            t.type = 'DISJOINT'
257        elif t.value == 'i' or t.value == 'intersect':
258            t.value = 'i'
259            t.type = 'INTERSECT'
260        else:
261            self.t_error(t)
262        return(t)
263
264    # Handle errors.
265    def t_error(self, t):
266        raise SyntaxError("syntax error on line %d position %i near '%s'" %
267                          (t.lineno, t.lexpos, t.value))
268
269    # Build the lexer
270    def build(self,**kwargs):
271        self.lexer = lex.lex(module=self, optimize=False,
272                             nowarn=True, debug=0, **kwargs)
273
274    # Just for testing
275    def test(self,data):
276        self.name_list = {}
277        print(data)
278        self.lexer.input(data)
279        while True:
280             tok = self.lexer.token()
281             if not tok: break
282             print(tok)
283
284###############################################################################
285
286class TemporalOperatorParser(object):
287    """The temporal operator class"""
288
289    def __init__(self):
290        self.lexer = TemporalOperatorLexer()
291        self.lexer.build()
292        self.parser = yacc.yacc(module=self, debug=0)
293        self.relations = None   # Temporal relations (equals, contain, during, ...)
294        self.temporal  = None   # Temporal operation (intersect, left, right, ...)
295        self.function  = None   # Actual operation (+, -, /, *, ... )
296        self.aggregate = None   # Aggregation function (|, &)
297
298        self.optype_list = ["select", "boolean", "raster", "hash", "relation", "overlay"]
299
300    def parse(self, expression, optype='relation'):
301        """Parse the expression and fill the object variables
302
303        :param expression:
304        :param optype: The parameter optype can be of type:
305                       - select   { :, during,   r}
306                       - boolean  {&&, contains, |}
307                       - raster   { *, equal,    |}
308                       - overlay  { |, starts,   &}
309                       - hash     { #, during,   l}
310                       - relation {during}
311        :return:
312        """
313        self.optype = optype
314
315        if optype not in self.optype_list:
316            raise SyntaxError("Unknown optype %s, must be one of %s"%(self.optype, str(self.optype_list)))
317        self.expression = expression
318        self.parser.parse(expression)
319
320    # Error rule for syntax errors.
321    def p_error(self, t):
322        raise SyntaxError("Unexpected syntax error in expression"
323                          " \"%s\" at position %i near %s"%(self.expression,
324                                                            t.lexpos,
325                                                            t.value))
326
327    # Get the tokens from the lexer class
328    tokens = TemporalOperatorLexer.tokens
329
330    def p_relation_operator(self, t):
331        # {during}
332        # {during | equal | starts}
333        """
334        operator : CLPAREN relation CRPAREN
335                 | CLPAREN relationlist CRPAREN
336        """
337        # Check for correct type.
338        if not self.optype == 'relation':
339            raise SyntaxError("Wrong optype \"%s\" must be \"relation\""%self.optype)
340        else:
341            # Set three operator components.
342            if isinstance(t[2], list):
343                self.relations = t[2]
344            else:
345                self.relations = [t[2]]
346            self.temporal  = None
347            self.function  = None
348
349            t[0] = t[2]
350
351    def p_relation_bool_operator(self, t):
352        # {||, during}
353        # {&&, during | equal | starts}
354        """
355        operator : CLPAREN OR  OR  COMMA relation     CRPAREN
356                 | CLPAREN AND AND COMMA relation     CRPAREN
357                 | CLPAREN OR  OR  COMMA relationlist CRPAREN
358                 | CLPAREN AND AND COMMA relationlist CRPAREN
359        """
360        if not self.optype == 'boolean':
361            raise SyntaxError("Wrong optype \"%s\" must be \"boolean\""%self.optype)
362        else:
363            # Set three operator components.
364            if isinstance(t[5], list):
365                self.relations = t[5]
366            else:
367                self.relations = [t[5]]
368            self.temporal  = "l"
369            self.function  = t[2] + t[3]
370            self.aggregate = t[2]
371
372            t[0] = t[2]
373
374    def p_relation_bool_combi_operator(self, t):
375        # {||, during, &}
376        # {&&, during | equal | starts, |}
377        """
378        operator : CLPAREN OR  OR  COMMA relation     COMMA OR  CRPAREN
379                 | CLPAREN OR  OR  COMMA relation     COMMA AND CRPAREN
380                 | CLPAREN AND AND COMMA relation     COMMA OR  CRPAREN
381                 | CLPAREN AND AND COMMA relation     COMMA AND CRPAREN
382                 | CLPAREN OR  OR  COMMA relationlist COMMA OR  CRPAREN
383                 | CLPAREN OR  OR  COMMA relationlist COMMA AND CRPAREN
384                 | CLPAREN AND AND COMMA relationlist COMMA OR  CRPAREN
385                 | CLPAREN AND AND COMMA relationlist COMMA AND CRPAREN
386        """
387        if not self.optype == 'boolean':
388            raise SyntaxError("Wrong optype \"%s\" must be \"boolean\""%self.optype)
389        else:
390            # Set three operator components.
391            if isinstance(t[5], list):
392                self.relations = t[5]
393            else:
394                self.relations = [t[5]]
395            self.temporal  = "l"
396            self.function  = t[2] + t[3]
397            self.aggregate = t[7]
398
399            t[0] = t[2]
400
401    def p_relation_bool_combi_operator2(self, t):
402        # {||, during, left}
403        # {&&, during | equal | starts, union}
404        """
405        operator : CLPAREN OR  OR  COMMA relation     COMMA temporal CRPAREN
406                 | CLPAREN AND AND COMMA relation     COMMA temporal CRPAREN
407                 | CLPAREN OR  OR  COMMA relationlist COMMA temporal CRPAREN
408                 | CLPAREN AND AND COMMA relationlist COMMA temporal CRPAREN
409        """
410        if not self.optype == 'boolean':
411            raise SyntaxError("Wrong optype \"%s\" must be \"boolean\""%self.optype)
412        else:
413            # Set three operator components.
414            if isinstance(t[5], list):
415                self.relations = t[5]
416            else:
417                self.relations = [t[5]]
418            self.temporal  = t[7]
419            self.function  = t[2] + t[3]
420            self.aggregate = t[2]
421
422            t[0] = t[2]
423
424    def p_relation_bool_combi_operator3(self, t):
425        # {||, during, |, left}
426        # {&&, during | equal | starts, &, union}
427        """
428        operator : CLPAREN OR  OR  COMMA relation     COMMA OR  COMMA temporal CRPAREN
429                 | CLPAREN OR  OR  COMMA relation     COMMA AND COMMA temporal CRPAREN
430                 | CLPAREN AND AND COMMA relation     COMMA OR  COMMA temporal CRPAREN
431                 | CLPAREN AND AND COMMA relation     COMMA AND COMMA temporal CRPAREN
432                 | CLPAREN OR  OR  COMMA relationlist COMMA OR  COMMA temporal CRPAREN
433                 | CLPAREN OR  OR  COMMA relationlist COMMA AND COMMA temporal CRPAREN
434                 | CLPAREN AND AND COMMA relationlist COMMA OR  COMMA temporal CRPAREN
435                 | CLPAREN AND AND COMMA relationlist COMMA AND COMMA temporal CRPAREN
436        """
437        if not self.optype == 'boolean':
438            raise SyntaxError("Wrong optype \"%s\" must be \"relation\""%self.optype)
439        else:
440            # Set three operator components.
441            if isinstance(t[5], list):
442                self.relations = t[5]
443            else:
444                self.relations = [t[5]]
445            self.temporal  = t[9]
446            self.function  = t[2] + t[3]
447            self.aggregate = t[7]
448
449            t[0] = t[2]
450
451    def p_select_relation_operator(self, t):
452        # {!:}
453        # { :, during}
454        # {!:, during | equal | starts}
455        # { :, during | equal | starts, l}
456        """
457        operator : CLPAREN select CRPAREN
458                 | CLPAREN select COMMA relation     CRPAREN
459                 | CLPAREN select COMMA relationlist CRPAREN
460                 | CLPAREN select COMMA relation     COMMA temporal CRPAREN
461                 | CLPAREN select COMMA relationlist COMMA temporal CRPAREN
462        """
463        if not self.optype == 'select':
464            raise SyntaxError("Wrong optype \"%s\" must be \"select\""%self.optype)
465        else:
466            if len(t) == 4:
467                # Set three operator components.
468                self.relations = ['equal', 'equivalent']
469                self.temporal  = "l"
470                self.function  = t[2]
471            elif len(t) == 6:
472                if isinstance(t[4], list):
473                    self.relations = t[4]
474                else:
475                    self.relations = [t[4]]
476                self.temporal  = "l"
477                self.function  = t[2]
478            elif len(t) == 8:
479                if isinstance(t[4], list):
480                    self.relations = t[4]
481                else:
482                    self.relations = [t[4]]
483                self.temporal  = t[6]
484                self.function  = t[2]
485            t[0] = t[2]
486
487    def p_hash_relation_operator(self, t):
488        # {#}
489        # {#, during}
490        # {#, during | equal | starts}
491        # {#, during | equal | starts, l}
492        """
493        operator : CLPAREN HASH CRPAREN
494                 | CLPAREN HASH COMMA relation     CRPAREN
495                 | CLPAREN HASH COMMA relationlist CRPAREN
496                 | CLPAREN HASH COMMA relation     COMMA temporal CRPAREN
497                 | CLPAREN HASH COMMA relationlist COMMA temporal CRPAREN
498        """
499        if not self.optype == 'hash':
500            raise SyntaxError("Wrong optype \"%s\" must be \"hash\""%self.optype)
501        else:
502            if len(t) == 4:
503                # Set three operator components.
504                self.relations = ['equal']
505                self.temporal  = "l"
506                self.function  = t[2]
507            elif len(t) == 6:
508                if isinstance(t[4], list):
509                    self.relations = t[4]
510                else:
511                    self.relations = [t[4]]
512                self.temporal  = "l"
513                self.function  = t[2]
514            elif len(t) == 8:
515                if isinstance(t[4], list):
516                    self.relations = t[4]
517                else:
518                    self.relations = [t[4]]
519                self.temporal  = t[6]
520                self.function  = t[2]
521            t[0] = t[2]
522
523    def p_raster_relation_operator(self, t):
524        # {+}
525        # {-, during}
526        # {*, during | equal | starts}
527        # {/, during | equal | starts, l}
528        """
529        operator : CLPAREN arithmetic CRPAREN
530                 | CLPAREN arithmetic COMMA relation     CRPAREN
531                 | CLPAREN arithmetic COMMA relationlist CRPAREN
532                 | CLPAREN arithmetic COMMA relation     COMMA temporal CRPAREN
533                 | CLPAREN arithmetic COMMA relationlist COMMA temporal CRPAREN
534        """
535        if not self.optype == 'raster':
536            raise SyntaxError("Wrong optype \"%s\" must be \"raster\""%self.optype)
537        else:
538            if len(t) == 4:
539                # Set three operator components.
540                self.relations = ['equal']
541                self.temporal  = "l"
542                self.function  = t[2]
543            elif len(t) == 6:
544                if isinstance(t[4], list):
545                    self.relations = t[4]
546                else:
547                    self.relations = [t[4]]
548                self.temporal  = "l"
549                self.function  = t[2]
550            elif len(t) == 8:
551                if isinstance(t[4], list):
552                    self.relations = t[4]
553                else:
554                    self.relations = [t[4]]
555                self.temporal  = t[6]
556                self.function  = t[2]
557            t[0] = t[2]
558
559    def p_overlay_relation_operator(self, t):
560        # {+}
561        # {-, during}
562        # {~, during | equal | starts}
563        # {^, during | equal | starts, l}
564        """
565        operator : CLPAREN overlay CRPAREN
566                 | CLPAREN overlay COMMA relation     CRPAREN
567                 | CLPAREN overlay COMMA relationlist CRPAREN
568                 | CLPAREN overlay COMMA relation     COMMA temporal CRPAREN
569                 | CLPAREN overlay COMMA relationlist COMMA temporal CRPAREN
570        """
571        if not self.optype == 'overlay':
572            raise SyntaxError("Wrong optype \"%s\" must be \"overlay\""%self.optype)
573        else:
574            if len(t) == 4:
575                # Set three operator components.
576                self.relations = ['equal']
577                self.temporal  = "l"
578                self.function  = t[2]
579            elif len(t) == 6:
580                if isinstance(t[4], list):
581                    self.relations = t[4]
582                else:
583                    self.relations = [t[4]]
584                self.temporal  = "l"
585                self.function  = t[2]
586            elif len(t) == 8:
587                if isinstance(t[4], list):
588                    self.relations = t[4]
589                else:
590                    self.relations = [t[4]]
591                self.temporal  = t[6]
592                self.function  = t[2]
593            t[0] = t[2]
594
595    def p_relation(self, t):
596        # The list of relations. Temporal and spatial relations are supported
597        """
598        relation : EQUAL
599                 | FOLLOWS
600                 | PRECEDES
601                 | OVERLAPS
602                 | OVERLAPPED
603                 | DURING
604                 | STARTS
605                 | FINISHES
606                 | CONTAINS
607                 | STARTED
608                 | FINISHED
609                 | EQUIVALENT
610                 | COVER
611                 | OVERLAP
612                 | IN
613                 | CONTAIN
614                 | MEET
615        """
616        t[0] = t[1]
617
618    def p_over(self, t):
619        # The the over keyword
620        """
621        relation : OVER
622        """
623        over_list = ["overlaps", "overlapped"]
624        t[0] = over_list
625
626    def p_relationlist(self, t):
627        # The list of relations.
628        """
629        relationlist : relation OR relation
630                     | relation OR relationlist
631        """
632        rel_list = []
633        rel_list.append(t[1])
634        if isinstance(t[3], list):
635            rel_list = rel_list + t[3]
636        else:
637            rel_list.append(t[3])
638        t[0] =  rel_list
639
640    def p_temporal_operator(self, t):
641        # The list of relations.
642        """
643        temporal : LEFTREF
644                 | RIGHTREF
645                 | UNION
646                 | DISJOINT
647                 | INTERSECT
648        """
649        t[0] = t[1]
650
651    def p_select_operator(self, t):
652        # The list of relations.
653        """
654        select : T_SELECT
655               | T_NOT_SELECT
656        """
657        t[0] = t[1]
658
659    def p_arithmetic_operator(self, t):
660        # The list of relations.
661        """
662        arithmetic : MOD
663                   | DIV
664                   | MULT
665                   | ADD
666                   | SUB
667        """
668        t[0] = t[1]
669
670    def p_overlay_operator(self, t):
671        # The list of relations.
672        """
673        overlay : AND
674                | OR
675                | XOR
676                | DISOR
677                | NOT
678        """
679        t[0] = t[1]
680
681###############################################################################
682
683if __name__ == "__main__":
684    import doctest
685    doctest.testmod()
686