def visit(node: Node, data: dict, context: Context) -> EvalObject: if isinstance(node, BinaryNode): if node.op in ['=', '<', '>', '>=', '<=']: if node.visited: previous = context.previous[context.get_current_index()] res = Table(table_copy(previous.name, context), previous.fv, previous.is_negative) else: res = visit_condition(node, context) node.visited = True else: left = visit(node.left, data, context) right = visit(node.right, data, context) if node.op in ['*', '/', 'MOD', '+', '-']: raise RuntimeError( "Arithmetic operator without condition operator found in" + node.to_str()) elif node.op == 'OR': res = union(left, right, context) elif node.op == 'AND': res = intersection(left, right, context) elif node.op == 'IMPLIES': res = union(negation(left, context), right, context) elif node.op == 'EQUIV': if isinstance(left, Table) and isinstance(right, Table): l_tuples = get_tuples(left.name, left.fv, context) r_tuples = get_tuples(right.name, right.fv, context) if l_tuples == r_tuples: res = Bool(True) else: res = Bool(False) else: raise RuntimeError( "EQUIV must contain tables on both sides") elif node.op == 'SINCE': if node.sub: start, end = node.sub ts_diff = context.current_ts - context.previous_ts res = None for to in range(0, end + 1): frm = max(0, to - (end - start)) if frm > 0: if ts_diff <= to: res = context.previous[ context.get_current_index() - ts_diff] else: res = Bool(False) else: if frm <= ts_diff <= to: previous = context.previous[ context.get_current_index() - ts_diff] else: previous = Bool(False) res = union(right, intersection(left, previous, context), context) if to < end: context.current.append(res) else: previous = context.previous[context.get_current_index()] res = union(right, intersection(left, previous, context), context) else: raise RuntimeError("Unhandled operator " + node.op) elif isinstance(node, UnaryNode): child = visit(node.child, data, context) if node.op == 'NOT': res = negation(child, context) elif node.op == 'EXISTS': if isinstance(child, Table): if set(node.sub).issubset([e[0] for e in child.fv]): fv = set() for e in child.fv: if not e[0] in node.sub: fv.add(e) if fv == set(): if child.is_negative: res = Bool(False) else: if get_tuples(child.name, child.fv, context): res = Bool(True) else: res = Bool(False) else: if child.is_negative: res = Table(empty_table(fv, context), fv, child.is_negative) else: res = Table(projection(child.name, fv, context), fv, child.is_negative) else: raise RuntimeError("EXISTS: %s has to be subset of %s" % (node.sub, child.fv)) else: raise RuntimeError("EXISTS of non-Table not supported") elif node.op == 'FORALL': if isinstance(child, Table): if set(node.sub).issubset(child.fv): fv = child.fv.difference(set(node.sub)) if fv == set(): if child.is_negative: if get_tuples(child.name, child.fv, context): res = Bool(True) else: res = Bool(False) else: res = Bool(False) else: if child.is_negative: res = Table(projection(child.name, fv, context), fv, child.is_negative) else: res = Table(empty_table(fv, context), fv, child.is_negative) else: raise RuntimeError( "FORALL: %s has to be subset of %s" % node.sub, child.fv) else: raise RuntimeError("FORALL of non-Table not supported") elif node.op == 'PREVIOUS': previous = context.previous[context.get_current_index() - 1] if node.sub: start, end = node.sub ts_diff = context.current_ts - context.previous_ts if start <= ts_diff <= end: if isinstance(previous, Table): res = Table(table_copy(previous.name, context), previous.fv, previous.is_negative) else: res = previous else: res = Table(empty_table(previous.fv, context), previous.fv, False) else: if isinstance(previous, Table): res = Table(table_copy(previous.name, context), previous.fv, previous.is_negative) else: res = previous elif node.op == 'ONCE': if node.sub: start, end = node.sub ts_diff = context.current_ts - context.previous_ts res = None for to in range(0, end + 1): frm = max(0, to - (end - start)) if frm > 0: if ts_diff <= to: res = context.previous[context.get_current_index() - ts_diff] else: res = Bool(False) else: if frm <= ts_diff <= to: previous = context.previous[ context.get_current_index() - ts_diff] else: previous = Bool(False) res = union(child, previous, context) if to < end: context.current.append(res) else: previous = context.previous[context.get_current_index()] res = union(child, previous, context) elif node.op in ['SUM', 'MIN', 'MAX', 'CNT', 'AVG']: if len(node.sub) == 2: out, aggreg = node.sub res = aggregation(child, node.op, out, aggreg, context) elif len(node.sub) == 3: out, aggreg, group_by = node.sub res = aggregation(child, node.op, out, aggreg, context, group_by) else: raise RuntimeError("Error parsing aggregation") else: raise RuntimeError("Unhandled operator: " + node.op) elif isinstance(node, Leaf): res = parse_leaf(node, data, context) else: raise RuntimeError("Error while parsing node: " + node.to_str()) context.current.append(res) # if isinstance(res, Table): # if isinstance(node, BinaryNode): # print(res.name, res.fv, node.op, end=" ") # print(get_tuples(res.name, res.fv, context)) # elif isinstance(node, UnaryNode): # print(res.name, res.fv, node.op, end=" ") # print(get_tuples(res.name, res.fv, context)) # elif isinstance(node, Leaf): # print(res.name, res.fv, node.typ, node.value, end=" ") # print(get_tuples(res.name, res.fv, context)) # elif isinstance(res, Bool): # if isinstance(node, BinaryNode): # print(res.v, node.op) # elif isinstance(node, UnaryNode): # print(res.v, node.op) # elif isinstance(node, Leaf): # print(res.v, node.typ, node.value) return res