1
"""@package grass.temporal
3
Temporal operator evaluation with PLY
5
(C) 2013 by the GRASS Development Team
6
This program is free software under the GNU General Public
7
License (>=v2). Read the file COPYING that comes with GRASS
10
:authors: Thomas Leppelt and Soeren Gebbert
12
.. code-block:: python
14
>>> p = TemporalOperatorParser()
15
>>> expression = "{equal| during}"
16
>>> p.parse(expression, optype = 'relation')
17
>>> print(p.relations, p.temporal, p.function)
18
(['equal', 'during'], None, None)
19
>>> p = TemporalOperatorParser()
20
>>> expression = "{contains | starts}"
21
>>> p.parse(expression)
22
>>> print(p.relations, p.temporal, p.function)
23
(['contains', 'starts'], None, None)
24
>>> p = TemporalOperatorParser()
25
>>> expression = "{&&, during}"
26
>>> p.parse(expression, optype = 'boolean')
27
>>> print(p.relations, p.temporal, p.function,p.aggregate)
28
(['during'], 'l', '&&', '&')
29
>>> p = TemporalOperatorParser()
30
>>> expression = "{||, equal | during}"
31
>>> p.parse(expression, optype = 'boolean')
32
>>> print(p.relations, p.temporal, p.function,p.aggregate)
33
(['equal', 'during'], 'l', '||', '|')
34
>>> p = TemporalOperatorParser()
35
>>> expression = "{||, equal | during, &}"
36
>>> p.parse(expression, optype = 'boolean')
37
>>> print(p.relations, p.temporal, p.function,p.aggregate)
38
(['equal', 'during'], 'l', '||', '&')
39
>>> p = TemporalOperatorParser()
40
>>> expression = "{&&, during, |}"
41
>>> p.parse(expression, optype = 'boolean')
42
>>> print(p.relations, p.temporal, p.function,p.aggregate)
43
(['during'], 'l', '&&', '|')
44
>>> p = TemporalOperatorParser()
45
>>> expression = "{&&, during, |, r}"
46
>>> p.parse(expression, optype = 'boolean')
47
>>> print(p.relations, p.temporal, p.function,p.aggregate)
48
(['during'], 'r', '&&', '|')
49
>>> p = TemporalOperatorParser()
50
>>> expression = "{&&, during, u}"
51
>>> p.parse(expression, optype = 'boolean')
52
>>> print(p.relations, p.temporal, p.function,p.aggregate)
53
(['during'], 'u', '&&', '&')
54
>>> p = TemporalOperatorParser()
55
>>> expression = "{:, during, r}"
56
>>> p.parse(expression, optype = 'select')
57
>>> print(p.relations, p.temporal, p.function)
58
(['during'], 'r', ':')
59
>>> p = TemporalOperatorParser()
60
>>> expression = "{!:, equal | contains, d}"
61
>>> p.parse(expression, optype = 'select')
62
>>> print(p.relations, p.temporal, p.function)
63
(['equal', 'contains'], 'd', '!:')
64
>>> p = TemporalOperatorParser()
65
>>> expression = "{#, during, r}"
66
>>> p.parse(expression, optype = 'hash')
67
>>> print(p.relations, p.temporal, p.function)
68
(['during'], 'r', '#')
69
>>> p = TemporalOperatorParser()
70
>>> expression = "{#, equal | contains}"
71
>>> p.parse(expression, optype = 'hash')
72
>>> print(p.relations, p.temporal, p.function)
73
(['equal', 'contains'], 'l', '#')
74
>>> p = TemporalOperatorParser()
75
>>> expression = "{+, during, r}"
76
>>> p.parse(expression, optype = 'raster')
77
>>> print(p.relations, p.temporal, p.function)
78
(['during'], 'r', '+')
79
>>> p = TemporalOperatorParser()
80
>>> expression = "{/, equal | contains}"
81
>>> p.parse(expression, optype = 'raster')
82
>>> print(p.relations, p.temporal, p.function)
83
(['equal', 'contains'], 'l', '/')
84
>>> p = TemporalOperatorParser()
85
>>> expression = "{+, equal | contains,intersect}"
86
>>> p.parse(expression, optype = 'raster')
87
>>> print(p.relations, p.temporal, p.function)
88
(['equal', 'contains'], 'i', '+')
89
>>> p = TemporalOperatorParser()
90
>>> expression = "{*, contains,disjoint}"
91
>>> p.parse(expression, optype = 'raster')
92
>>> print(p.relations, p.temporal, p.function)
93
(['contains'], 'd', '*')
94
>>> p = TemporalOperatorParser()
95
>>> expression = "{~, equal,left}"
96
>>> p.parse(expression, optype = 'overlay')
97
>>> print(p.relations, p.temporal, p.function)
99
>>> p = TemporalOperatorParser()
100
>>> expression = "{^, over,right}"
101
>>> p.parse(expression, optype = 'overlay')
102
>>> print(p.relations, p.temporal, p.function)
103
(['overlaps', 'overlapped'], 'r', '^')
108
import ply.lex as lex
109
import ply.yacc as yacc
113
class TemporalOperatorLexer(object):
114
"""Lexical analyzer for the GRASS GIS temporal operator"""
116
# Functions that defines topological relations.
119
'follows' : "FOLLOWS",
120
'precedes' : "PRECEDES",
121
'overlaps' : "OVERLAPS",
122
'overlapped' : "OVERLAPPED",
125
'finishes' : "FINISHES",
126
'contains' : "CONTAINS",
127
'started' : "STARTED",
128
'finished' : "FINISHED",
132
# This is the list of token names.
157
# Build the token list
158
tokens = tokens + tuple(relations.values())
160
# Regular expression rules for simple tokens
162
t_T_NOT_SELECT = r'!:'
164
t_LEFTREF = '^[l|left]'
165
t_RIGHTREF ='^[r|right]'
166
t_UNION = '^[u|union]'
167
t_DISJOINT = '^[d|disjoint]'
168
t_INTERSECT = '^[i|intersect]'
183
# These are the things that should be ignored.
186
# Track line numbers.
187
def t_newline(self, t):
189
t.lineno += len(t.value)
192
r'[a-zA-Z_][a-zA-Z_0-9]*'
193
self.temporal_symbol(t)
197
def temporal_symbol(self, t):
198
# Check for reserved words
199
if t.value in TemporalOperatorLexer.relations.keys():
200
t.type = TemporalOperatorLexer.relations.get(t.value)
201
elif t.value == 'l' or t.value == 'left':
204
elif t.value == 'r' or t.value == 'right':
207
elif t.value == 'u' or t.value == 'union':
210
elif t.value == 'd' or t.value == 'disjoint':
213
elif t.value == 'i' or t.value == 'intersect':
221
def t_error(self, t):
222
raise SyntaxError("syntax error on line %d near '%s'" %
226
def build(self,**kwargs):
227
self.lexer = lex.lex(module=self, **kwargs)
233
self.lexer.input(data)
235
tok = self.lexer.token()
239
###############################################################################
241
class TemporalOperatorParser(object):
242
"""The temporal operator class"""
245
self.lexer = TemporalOperatorLexer()
247
self.parser = yacc.yacc(module=self)
248
self.relations = None
251
self.aggregate = None
253
def parse(self, expression, optype = 'relation'):
255
self.parser.parse(expression)
256
# The parameter optype can be of type: select {:, during, r}, boolean{&&, contains, |},
257
# raster{*, equal, |}, vector {|, starts, &},
258
# hash{#, during, l} or relation {during}.
260
# Error rule for syntax errors.
261
def p_error(self, t):
262
raise SyntaxError("Unexpected syntax error")
264
# Get the tokens from the lexer class
265
tokens = TemporalOperatorLexer.tokens
267
def p_relation_operator(self, t):
268
# The expression should always return a list of maps.
270
operator : CLPAREN relation CRPAREN
271
| CLPAREN relationlist CRPAREN
273
# Check for correct type.
274
if not self.optype == 'relation':
275
raise SyntaxError("invalid syntax")
277
# Set three operator components.
278
if isinstance(t[2], list):
279
self.relations = t[2]
281
self.relations = [t[2]]
287
def p_relation_bool_operator(self, t):
288
# The expression should always return a list of maps.
290
operator : CLPAREN OR OR COMMA relation CRPAREN
291
| CLPAREN AND AND COMMA relation CRPAREN
292
| CLPAREN OR OR COMMA relationlist CRPAREN
293
| CLPAREN AND AND COMMA relationlist CRPAREN
295
if not self.optype == 'boolean':
296
raise SyntaxError("invalid syntax")
298
# Set three operator components.
299
if isinstance(t[5], list):
300
self.relations = t[5]
302
self.relations = [t[5]]
304
self.function = t[2] + t[3]
305
self.aggregate = t[2]
309
def p_relation_bool_combi_operator(self, t):
310
# The expression should always return a list of maps.
312
operator : CLPAREN OR OR COMMA relation COMMA OR CRPAREN
313
| CLPAREN OR OR COMMA relation COMMA AND CRPAREN
314
| CLPAREN AND AND COMMA relation COMMA OR CRPAREN
315
| CLPAREN AND AND COMMA relation COMMA AND CRPAREN
316
| CLPAREN OR OR COMMA relationlist COMMA OR CRPAREN
317
| CLPAREN OR OR COMMA relationlist COMMA AND CRPAREN
318
| CLPAREN AND AND COMMA relationlist COMMA OR CRPAREN
319
| CLPAREN AND AND COMMA relationlist COMMA AND CRPAREN
321
if not self.optype == 'boolean':
322
raise SyntaxError("invalid syntax")
324
# Set three operator components.
325
if isinstance(t[5], list):
326
self.relations = t[5]
328
self.relations = [t[5]]
330
self.function = t[2] + t[3]
331
self.aggregate = t[7]
335
def p_relation_bool_combi_operator2(self, t):
336
# The expression should always return a list of maps.
338
operator : CLPAREN OR OR COMMA relation COMMA temporal CRPAREN
339
| CLPAREN AND AND COMMA relation COMMA temporal CRPAREN
340
| CLPAREN OR OR COMMA relationlist COMMA temporal CRPAREN
341
| CLPAREN AND AND COMMA relationlist COMMA temporal CRPAREN
343
if not self.optype == 'boolean':
344
raise SyntaxError("invalid syntax")
346
# Set three operator components.
347
if isinstance(t[5], list):
348
self.relations = t[5]
350
self.relations = [t[5]]
352
self.function = t[2] + t[3]
353
self.aggregate = t[2]
357
def p_relation_bool_combi_operator3(self, t):
358
# The expression should always return a list of maps.
360
operator : CLPAREN OR OR COMMA relation COMMA OR COMMA temporal CRPAREN
361
| CLPAREN OR OR COMMA relation COMMA AND COMMA temporal CRPAREN
362
| CLPAREN AND AND COMMA relation COMMA OR COMMA temporal CRPAREN
363
| CLPAREN AND AND COMMA relation COMMA AND COMMA temporal CRPAREN
364
| CLPAREN OR OR COMMA relationlist COMMA OR COMMA temporal CRPAREN
365
| CLPAREN OR OR COMMA relationlist COMMA AND COMMA temporal CRPAREN
366
| CLPAREN AND AND COMMA relationlist COMMA OR COMMA temporal CRPAREN
367
| CLPAREN AND AND COMMA relationlist COMMA AND COMMA temporal CRPAREN
369
if not self.optype == 'boolean':
370
raise SyntaxError("invalid syntax")
372
# Set three operator components.
373
if isinstance(t[5], list):
374
self.relations = t[5]
376
self.relations = [t[5]]
378
self.function = t[2] + t[3]
379
self.aggregate = t[7]
383
def p_select_relation_operator(self, t):
384
# The expression should always return a list of maps.
386
operator : CLPAREN select CRPAREN
387
| CLPAREN select COMMA relation CRPAREN
388
| CLPAREN select COMMA relationlist CRPAREN
389
| CLPAREN select COMMA relation COMMA temporal CRPAREN
390
| CLPAREN select COMMA relationlist COMMA temporal CRPAREN
392
if not self.optype == 'select':
393
raise SyntaxError("invalid syntax")
396
# Set three operator components.
397
self.relations = ['equal']
401
self.relations = ['equal']
405
if isinstance(t[4], list):
406
self.relations = t[4]
408
self.relations = [t[4]]
412
if isinstance(t[4], list):
413
self.relations = t[4]
415
self.relations = [t[4]]
420
def p_hash_relation_operator(self, t):
421
# The expression should always return a list of maps.
423
operator : CLPAREN HASH CRPAREN
424
| CLPAREN HASH COMMA relation CRPAREN
425
| CLPAREN HASH COMMA relationlist CRPAREN
426
| CLPAREN HASH COMMA relation COMMA temporal CRPAREN
427
| CLPAREN HASH COMMA relationlist COMMA temporal CRPAREN
429
if not self.optype == 'hash':
430
raise SyntaxError("invalid syntax")
433
# Set three operator components.
434
self.relations = ['equal']
438
self.relations = ['equal']
442
if isinstance(t[4], list):
443
self.relations = t[4]
445
self.relations = [t[4]]
449
if isinstance(t[4], list):
450
self.relations = t[4]
452
self.relations = [t[4]]
457
def p_raster_relation_operator(self, t):
458
# The expression should always return a list of maps.
460
operator : CLPAREN arithmetic CRPAREN
461
| CLPAREN arithmetic COMMA relation CRPAREN
462
| CLPAREN arithmetic COMMA relationlist CRPAREN
463
| CLPAREN arithmetic COMMA relation COMMA temporal CRPAREN
464
| CLPAREN arithmetic COMMA relationlist COMMA temporal CRPAREN
466
if not self.optype == 'raster':
467
raise SyntaxError("invalid syntax")
470
# Set three operator components.
471
self.relations = ['equal']
475
self.relations = ['equal']
479
if isinstance(t[4], list):
480
self.relations = t[4]
482
self.relations = [t[4]]
486
if isinstance(t[4], list):
487
self.relations = t[4]
489
self.relations = [t[4]]
494
def p_overlay_relation_operator(self, t):
495
# The expression should always return a list of maps.
497
operator : CLPAREN overlay CRPAREN
498
| CLPAREN overlay COMMA relation CRPAREN
499
| CLPAREN overlay COMMA relationlist CRPAREN
500
| CLPAREN overlay COMMA relation COMMA temporal CRPAREN
501
| CLPAREN overlay COMMA relationlist COMMA temporal CRPAREN
503
if not self.optype == 'overlay':
504
raise SyntaxError("invalid syntax")
507
# Set three operator components.
508
self.relations = ['equal']
512
self.relations = ['equal']
516
if isinstance(t[4], list):
517
self.relations = t[4]
519
self.relations = [t[4]]
523
if isinstance(t[4], list):
524
self.relations = t[4]
526
self.relations = [t[4]]
531
def p_relation(self, t):
532
# The list of relations.
549
# The list of relations.
553
over_list = ["overlaps", "overlapped"]
556
def p_relationlist(self, t):
557
# The list of relations.
559
relationlist : relation OR relation
560
| relation OR relationlist
563
rel_list.append(t[1])
564
if isinstance(t[3], list):
565
rel_list = rel_list + t[3]
567
rel_list.append(t[3])
570
def p_temporal_operator(self, t):
571
# The list of relations.
581
def p_select_operator(self, t):
582
# The list of relations.
589
def p_arithmetic_operator(self, t):
590
# The list of relations.
600
def p_overlay_operator(self, t):
601
# The list of relations.
610
###############################################################################
612
if __name__ == "__main__":