Source code for temporal.temporal_operator

"""@package grass.temporal

Temporal operator evaluation with PLY

(C) 2013 by the GRASS Development Team
This program is free software under the GNU General Public
License (>=v2). Read the file COPYING that comes with GRASS
for details.

:authors: Thomas Leppelt and Soeren Gebbert

.. code-block:: python

    >>> p = TemporalOperatorParser()
    >>> expression =  "{equal|equivalent|cover|in|meet|contain|overlap}"
    >>> p.parse(expression, optype = 'relation')
    >>> print((p.relations, p.temporal, p.function))
    (['equal', 'equivalent', 'cover', 'in', 'meet', 'contain', 'overlap'], None, None)

    >>> p = TemporalOperatorParser()
    >>> expression =  "{equal| during}"
    >>> p.parse(expression, optype = 'relation')
    >>> print((p.relations, p.temporal, p.function))
    (['equal', 'during'], None, None)
    >>> p = TemporalOperatorParser()
    >>> expression =  "{contains | starts}"
    >>> p.parse(expression)
    >>> print((p.relations, p.temporal, p.function))
    (['contains', 'starts'], None, None)
    >>> p = TemporalOperatorParser()
    >>> expression =  "{&&, during}"
    >>> p.parse(expression, optype = 'boolean')
    >>> print((p.relations, p.temporal, p.function, p.aggregate))
    (['during'], 'l', '&&', '&')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{||, equal | during}"
    >>> p.parse(expression, optype = 'boolean')
    >>> print((p.relations, p.temporal, p.function, p.aggregate))
    (['equal', 'during'], 'l', '||', '|')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{||, equal | during, &}"
    >>> p.parse(expression, optype = 'boolean')
    >>> print((p.relations, p.temporal, p.function, p.aggregate))
    (['equal', 'during'], 'l', '||', '&')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{&&, during, |}"
    >>> p.parse(expression, optype = 'boolean')
    >>> print((p.relations, p.temporal, p.function, p.aggregate))
    (['during'], 'l', '&&', '|')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{&&, during, |, r}"
    >>> p.parse(expression, optype = 'boolean')
    >>> print((p.relations, p.temporal, p.function, p.aggregate))
    (['during'], 'r', '&&', '|')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{&&, during, u}"
    >>> p.parse(expression, optype = 'boolean')
    >>> print((p.relations, p.temporal, p.function, p.aggregate))
    (['during'], 'u', '&&', '&')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{:, during, r}"
    >>> p.parse(expression, optype = 'select')
    >>> print((p.relations, p.temporal, p.function))
    (['during'], 'r', ':')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{!:, equal | contains, d}"
    >>> p.parse(expression, optype = 'select')
    >>> print((p.relations, p.temporal, p.function))
    (['equal', 'contains'], 'd', '!:')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{#, during, r}"
    >>> p.parse(expression, optype = 'hash')
    >>> print((p.relations, p.temporal, p.function))
    (['during'], 'r', '#')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{#, equal | contains}"
    >>> p.parse(expression, optype = 'hash')
    >>> print((p.relations, p.temporal, p.function))
    (['equal', 'contains'], 'l', '#')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{+, during, r}"
    >>> p.parse(expression, optype = 'raster')
    >>> print((p.relations, p.temporal, p.function))
    (['during'], 'r', '+')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{/, equal | contains}"
    >>> p.parse(expression, optype = 'raster')
    >>> print((p.relations, p.temporal, p.function))
    (['equal', 'contains'], 'l', '/')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{+, equal | contains,intersect}"
    >>> p.parse(expression, optype = 'raster')
    >>> print((p.relations, p.temporal, p.function))
    (['equal', 'contains'], 'i', '+')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{*, contains,disjoint}"
    >>> p.parse(expression, optype = 'raster')
    >>> print((p.relations, p.temporal, p.function))
    (['contains'], 'd', '*')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{~, equal,left}"
    >>> p.parse(expression, optype = 'overlay')
    >>> print((p.relations, p.temporal, p.function))
    (['equal'], 'l', '~')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{^, over,right}"
    >>> p.parse(expression, optype = 'overlay')
    >>> print((p.relations, p.temporal, p.function))
    (['overlaps', 'overlapped'], 'r', '^')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{&&, equal | during | contains | starts, &}"
    >>> p.parse(expression, optype = 'boolean')
    >>> print((p.relations, p.temporal, p.function, p.aggregate))
    (['equal', 'during', 'contains', 'starts'], 'l', '&&', '&')
    >>> p = TemporalOperatorParser()
    >>> expression =  "{&&, equal | during | contains | starts, &&&&&}"
    >>> p.parse(expression, optype = 'boolean')
    Traceback (most recent call last):
    SyntaxError: Unexpected syntax error in expression "{&&, equal | during | contains | starts, &&&&&}" at position 42 near &
    >>> p = TemporalOperatorParser()
    >>> expression =  "{+, starting}"
    >>> p.parse(expression)
    Traceback (most recent call last):
    SyntaxError: syntax error on line 1 position 4 near 'starting'
    >>> p = TemporalOperatorParser()
    >>> expression =  "{nope, start, |, l}"
    >>> p.parse(expression)
    Traceback (most recent call last):
    SyntaxError: syntax error on line 1 position 1 near 'nope'
    >>> p = TemporalOperatorParser()
    >>> expression =  "{++, start, |, l}"
    >>> p.parse(expression)
    Traceback (most recent call last):
    SyntaxError: Unexpected syntax error in expression "{++, start, |, l}" at position 2 near +
    >>> p = TemporalOperatorParser()
    >>> expression =  "{^, over, right}"
    >>> p.parse(expression, optype='rter')
    Traceback (most recent call last):
    SyntaxError: Unknown optype rter, must be one of ['select', 'boolean', 'raster', 'hash', 'relation', 'overlay']

"""
from __future__ import print_function

try:
    import ply.lex as lex
    import ply.yacc as yacc
except:
    pass

[docs]class TemporalOperatorLexer(object): """Lexical analyzer for the GRASS GIS temporal operator""" # Functions that defines topological relations. relations = { # temporal relations 'equal' : "EQUAL", 'follows' : "FOLLOWS", 'precedes' : "PRECEDES", 'overlaps' : "OVERLAPS", 'overlapped' : "OVERLAPPED", 'during' : "DURING", 'starts' : "STARTS", 'finishes' : "FINISHES", 'contains' : "CONTAINS", 'started' : "STARTED", 'finished' : "FINISHED", 'over' : "OVER", # spatial relations 'equivalent' : "EQUIVALENT", 'cover' : "COVER", 'overlap' : "OVERLAP", 'in' : "IN", 'contain' : "CONTAIN", 'meet' : "MEET" } # This is the list of token names. tokens = ( 'COMMA', 'LEFTREF', 'RIGHTREF', 'UNION', 'DISJOINT', 'INTERSECT', 'HASH', 'OR', 'AND', 'DISOR', 'XOR', 'NOT', 'MOD', 'DIV', 'MULT', 'ADD', 'SUB', 'T_SELECT', 'T_NOT_SELECT', 'CLPAREN', 'CRPAREN', ) # Build the token list tokens = tokens + tuple(relations.values()) # Regular expression rules for simple tokens t_T_SELECT = r':' t_T_NOT_SELECT = r'!:' t_COMMA = r',' t_LEFTREF = '^[l|left]' t_RIGHTREF = '^[r|right]' t_UNION = '^[u|union]' t_DISJOINT = '^[d|disjoint]' t_INTERSECT = '^[i|intersect]' t_HASH = r'\#' t_OR = r'[\|]' t_AND = r'[&]' t_DISOR = r'\+' t_XOR = r'\^' t_NOT = r'\~' t_MOD = r'[\%]' t_DIV = r'[\/]' t_MULT = r'[\*]' t_ADD = r'[\+]' t_SUB = r'[-]' t_CLPAREN = r'\{' t_CRPAREN = r'\}' # These are the things that should be ignored. t_ignore = ' \t\n' # Track line numbers.
[docs] def t_newline(self, t): r'\n+' t.lineno += len(t.value)
[docs] def t_NAME(self, t): r'[a-zA-Z_][a-zA-Z_0-9]*' return self.temporal_symbol(t)
# Parse symbols
[docs] def temporal_symbol(self, t): # Check for reserved words if t.value in TemporalOperatorLexer.relations.keys(): t.type = TemporalOperatorLexer.relations.get(t.value) elif t.value == 'l' or t.value == 'left': t.value = 'l' t.type = 'LEFTREF' elif t.value == 'r' or t.value == 'right': t.value = 'r' t.type = 'RIGHTREF' elif t.value == 'u' or t.value == 'union': t.value = 'u' t.type = 'UNION' elif t.value == 'd' or t.value == 'disjoint': t.value = 'd' t.type = 'DISJOINT' elif t.value == 'i' or t.value == 'intersect': t.value = 'i' t.type = 'INTERSECT' else: self.t_error(t) return(t)
# Handle errors.
[docs] def t_error(self, t): raise SyntaxError("syntax error on line %d position %i near '%s'" % (t.lineno, t.lexpos, t.value))
# Build the lexer
[docs] def build(self,**kwargs): self.lexer = lex.lex(module=self, optimize=False, nowarn=True, debug=0, **kwargs)
# Just for testing
[docs] def test(self,data): self.name_list = {} print(data) self.lexer.input(data) while True: tok = self.lexer.token() if not tok: break print(tok)
###############################################################################
[docs]class TemporalOperatorParser(object): """The temporal operator class""" def __init__(self): self.lexer = TemporalOperatorLexer() self.lexer.build() self.parser = yacc.yacc(module=self, debug=0) self.relations = None # Temporal relations (equals, contain, during, ...) self.temporal = None # Temporal operation (intersect, left, right, ...) self.function = None # Actual operation (+, -, /, *, ... ) self.aggregate = None # Aggregation function (|, &) self.optype_list = ["select", "boolean", "raster", "hash", "relation", "overlay"]
[docs] def parse(self, expression, optype='relation'): """Parse the expression and fill the object variables :param expression: :param optype: The parameter optype can be of type: - select { :, during, r} - boolean {&&, contains, |} - raster { *, equal, |} - overlay { |, starts, &} - hash { #, during, l} - relation {during} :return: """ self.optype = optype if optype not in self.optype_list: raise SyntaxError("Unknown optype %s, must be one of %s"%(self.optype, str(self.optype_list))) self.expression = expression self.parser.parse(expression)
# Error rule for syntax errors.
[docs] def p_error(self, t): raise SyntaxError("Unexpected syntax error in expression" " \"%s\" at position %i near %s"%(self.expression, t.lexpos, t.value))
# Get the tokens from the lexer class tokens = TemporalOperatorLexer.tokens
[docs] def p_relation_operator(self, t): # {during} # {during | equal | starts} """ operator : CLPAREN relation CRPAREN | CLPAREN relationlist CRPAREN """ # Check for correct type. if not self.optype == 'relation': raise SyntaxError("Wrong optype \"%s\" must be \"relation\""%self.optype) else: # Set three operator components. if isinstance(t[2], list): self.relations = t[2] else: self.relations = [t[2]] self.temporal = None self.function = None t[0] = t[2]
[docs] def p_relation_bool_operator(self, t): # {||, during} # {&&, during | equal | starts} """ operator : CLPAREN OR OR COMMA relation CRPAREN | CLPAREN AND AND COMMA relation CRPAREN | CLPAREN OR OR COMMA relationlist CRPAREN | CLPAREN AND AND COMMA relationlist CRPAREN """ if not self.optype == 'boolean': raise SyntaxError("Wrong optype \"%s\" must be \"boolean\""%self.optype) else: # Set three operator components. if isinstance(t[5], list): self.relations = t[5] else: self.relations = [t[5]] self.temporal = "l" self.function = t[2] + t[3] self.aggregate = t[2] t[0] = t[2]
[docs] def p_relation_bool_combi_operator(self, t): # {||, during, &} # {&&, during | equal | starts, |} """ operator : CLPAREN OR OR COMMA relation COMMA OR CRPAREN | CLPAREN OR OR COMMA relation COMMA AND CRPAREN | CLPAREN AND AND COMMA relation COMMA OR CRPAREN | CLPAREN AND AND COMMA relation COMMA AND CRPAREN | CLPAREN OR OR COMMA relationlist COMMA OR CRPAREN | CLPAREN OR OR COMMA relationlist COMMA AND CRPAREN | CLPAREN AND AND COMMA relationlist COMMA OR CRPAREN | CLPAREN AND AND COMMA relationlist COMMA AND CRPAREN """ if not self.optype == 'boolean': raise SyntaxError("Wrong optype \"%s\" must be \"boolean\""%self.optype) else: # Set three operator components. if isinstance(t[5], list): self.relations = t[5] else: self.relations = [t[5]] self.temporal = "l" self.function = t[2] + t[3] self.aggregate = t[7] t[0] = t[2]
[docs] def p_relation_bool_combi_operator2(self, t): # {||, during, left} # {&&, during | equal | starts, union} """ operator : CLPAREN OR OR COMMA relation COMMA temporal CRPAREN | CLPAREN AND AND COMMA relation COMMA temporal CRPAREN | CLPAREN OR OR COMMA relationlist COMMA temporal CRPAREN | CLPAREN AND AND COMMA relationlist COMMA temporal CRPAREN """ if not self.optype == 'boolean': raise SyntaxError("Wrong optype \"%s\" must be \"boolean\""%self.optype) else: # Set three operator components. if isinstance(t[5], list): self.relations = t[5] else: self.relations = [t[5]] self.temporal = t[7] self.function = t[2] + t[3] self.aggregate = t[2] t[0] = t[2]
[docs] def p_relation_bool_combi_operator3(self, t): # {||, during, |, left} # {&&, during | equal | starts, &, union} """ operator : CLPAREN OR OR COMMA relation COMMA OR COMMA temporal CRPAREN | CLPAREN OR OR COMMA relation COMMA AND COMMA temporal CRPAREN | CLPAREN AND AND COMMA relation COMMA OR COMMA temporal CRPAREN | CLPAREN AND AND COMMA relation COMMA AND COMMA temporal CRPAREN | CLPAREN OR OR COMMA relationlist COMMA OR COMMA temporal CRPAREN | CLPAREN OR OR COMMA relationlist COMMA AND COMMA temporal CRPAREN | CLPAREN AND AND COMMA relationlist COMMA OR COMMA temporal CRPAREN | CLPAREN AND AND COMMA relationlist COMMA AND COMMA temporal CRPAREN """ if not self.optype == 'boolean': raise SyntaxError("Wrong optype \"%s\" must be \"relation\""%self.optype) else: # Set three operator components. if isinstance(t[5], list): self.relations = t[5] else: self.relations = [t[5]] self.temporal = t[9] self.function = t[2] + t[3] self.aggregate = t[7] t[0] = t[2]
[docs] def p_select_relation_operator(self, t): # {!:} # { :, during} # {!:, during | equal | starts} # { :, during | equal | starts, l} """ operator : CLPAREN select CRPAREN | CLPAREN select COMMA relation CRPAREN | CLPAREN select COMMA relationlist CRPAREN | CLPAREN select COMMA relation COMMA temporal CRPAREN | CLPAREN select COMMA relationlist COMMA temporal CRPAREN """ if not self.optype == 'select': raise SyntaxError("Wrong optype \"%s\" must be \"select\""%self.optype) else: if len(t) == 4: # Set three operator components. self.relations = ['equal', 'equivalent'] self.temporal = "l" self.function = t[2] elif len(t) == 6: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = "l" self.function = t[2] elif len(t) == 8: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = t[6] self.function = t[2] t[0] = t[2]
[docs] def p_hash_relation_operator(self, t): # {#} # {#, during} # {#, during | equal | starts} # {#, during | equal | starts, l} """ operator : CLPAREN HASH CRPAREN | CLPAREN HASH COMMA relation CRPAREN | CLPAREN HASH COMMA relationlist CRPAREN | CLPAREN HASH COMMA relation COMMA temporal CRPAREN | CLPAREN HASH COMMA relationlist COMMA temporal CRPAREN """ if not self.optype == 'hash': raise SyntaxError("Wrong optype \"%s\" must be \"hash\""%self.optype) else: if len(t) == 4: # Set three operator components. self.relations = ['equal'] self.temporal = "l" self.function = t[2] elif len(t) == 6: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = "l" self.function = t[2] elif len(t) == 8: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = t[6] self.function = t[2] t[0] = t[2]
[docs] def p_raster_relation_operator(self, t): # {+} # {-, during} # {*, during | equal | starts} # {/, during | equal | starts, l} """ operator : CLPAREN arithmetic CRPAREN | CLPAREN arithmetic COMMA relation CRPAREN | CLPAREN arithmetic COMMA relationlist CRPAREN | CLPAREN arithmetic COMMA relation COMMA temporal CRPAREN | CLPAREN arithmetic COMMA relationlist COMMA temporal CRPAREN """ if not self.optype == 'raster': raise SyntaxError("Wrong optype \"%s\" must be \"raster\""%self.optype) else: if len(t) == 4: # Set three operator components. self.relations = ['equal'] self.temporal = "l" self.function = t[2] elif len(t) == 6: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = "l" self.function = t[2] elif len(t) == 8: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = t[6] self.function = t[2] t[0] = t[2]
[docs] def p_overlay_relation_operator(self, t): # {+} # {-, during} # {~, during | equal | starts} # {^, during | equal | starts, l} """ operator : CLPAREN overlay CRPAREN | CLPAREN overlay COMMA relation CRPAREN | CLPAREN overlay COMMA relationlist CRPAREN | CLPAREN overlay COMMA relation COMMA temporal CRPAREN | CLPAREN overlay COMMA relationlist COMMA temporal CRPAREN """ if not self.optype == 'overlay': raise SyntaxError("Wrong optype \"%s\" must be \"overlay\""%self.optype) else: if len(t) == 4: # Set three operator components. self.relations = ['equal'] self.temporal = "l" self.function = t[2] elif len(t) == 6: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = "l" self.function = t[2] elif len(t) == 8: if isinstance(t[4], list): self.relations = t[4] else: self.relations = [t[4]] self.temporal = t[6] self.function = t[2] t[0] = t[2]
[docs] def p_relation(self, t): # The list of relations. Temporal and spatial relations are supported """ relation : EQUAL | FOLLOWS | PRECEDES | OVERLAPS | OVERLAPPED | DURING | STARTS | FINISHES | CONTAINS | STARTED | FINISHED | EQUIVALENT | COVER | OVERLAP | IN | CONTAIN | MEET """ t[0] = t[1]
[docs] def p_over(self, t): # The the over keyword """ relation : OVER """ over_list = ["overlaps", "overlapped"] t[0] = over_list
[docs] def p_relationlist(self, t): # The list of relations. """ relationlist : relation OR relation | relation OR relationlist """ rel_list = [] rel_list.append(t[1]) if isinstance(t[3], list): rel_list = rel_list + t[3] else: rel_list.append(t[3]) t[0] = rel_list
[docs] def p_temporal_operator(self, t): # The list of relations. """ temporal : LEFTREF | RIGHTREF | UNION | DISJOINT | INTERSECT """ t[0] = t[1]
[docs] def p_select_operator(self, t): # The list of relations. """ select : T_SELECT | T_NOT_SELECT """ t[0] = t[1]
[docs] def p_arithmetic_operator(self, t): # The list of relations. """ arithmetic : MOD | DIV | MULT | ADD | SUB """ t[0] = t[1]
[docs] def p_overlay_operator(self, t): # The list of relations. """ overlay : AND | OR | XOR | DISOR | NOT """ t[0] = t[1]
############################################################################### if __name__ == "__main__": import doctest doctest.testmod()