示例#1
0
 def make_in(p, start, end):
     p, values = p[:-1], p[-1]
     query_name, query, pdict, _ = check_function(p, 'query', start, end)
     num_values = _get_query_num_values(query, start, end)
     matched = set()
     unmatched = set(range(num_values))
     enum_values = {}
     if query.rv.type.startswith('enum['):
         enum_values_list = query.rv.type[5:-1].split(',')
         enum_values = {v.strip(): i for i, v in enumerate(enum_values_list)}
     for value in values:
         if isinstance(value.value, str):
             if not enum_values:
                 emit_error(f'Query "{query.name}" does not return an enum', start, end)
                 raise LogError()
             if value.value not in enum_values:
                 emit_error(f'Enum "{value.value}" is not a valid return value of "{query.name}"', start, end)
                 raise LogError()
             value.value = enum_values[value.value]
         if 0 > value.value or num_values <= value.value:
             emit_warning('{value.value} never returned by {query_name}, ignored', start, end)
             continue
         matched.add(value.value)
         unmatched.remove(value.value)
     if not matched or not unmatched:
         emit_warning(f'always true or always false check', start, end)
     return ((query, pdict), [(matched, True), (unmatched, False)])
示例#2
0
 def make_cmp(p, start, end):
     p, op, value = p[:-2], p[-2], p[-1]
     query_name, query, pdict, _ = check_function(p, 'query', start, end)
     num_values = _get_query_num_values(query, start, end)
     if isinstance(value.value, str):
         if query.rv.type.startswith('enum['):
             enum_values_list = query.rv.type[5:-1].split(',')
             value.value = enum_values_list.index(value.value)
             if value.value == -1:
                 emit_error(f'Enum "{value.value}" is not a valid return value of "{query.name}"', start, end)
                 raise LogError()
         else:
             emit_error(f'Query "{query.name}" does not return an enum', start, end)
             raise LogError()
     if op == '==' or op == '!=':
         matched = {value.value} if 0 <= value.value < num_values else set()
         unmatched = set(i for i in range(num_values) if i != value.value)
     elif op == '<' or op == '>=':
         matched = set(range(min(num_values, value.value)))
         unmatched = set(range(value.value, num_values))
     else:
         matched = set(range(min(num_values, value.value + 1)))
         unmatched = set(range(value.value + 1, num_values))
     if op in ('!=', '>=', '>'):
         matched, unmatched = unmatched, matched
     if not matched or not unmatched:
         emit_warning(f'always true or always false check', start, end)
     return ((query, pdict), [(matched, True), (unmatched, False)])
示例#3
0
 def _get_query_num_values(query, start, end):
     num_values = query.num_values
     if num_values == 999999999:
         emit_warning(f'maximum value for {query.name} unknown; assuming 50', start, end, print_source=False)
         emit_warning(f'setting a maximum value in functions.csv may reduce generated bfevfl size', start, end)
         num_values = 50
     return num_values
示例#4
0
    def make_switch(n, start, end):
        p, branches = n[:-1], n[-1]
        cases = branches[0] + branches[2]
        default = branches[1]

        query_name, query, pdict, _ = check_function(p, 'query', start, end)
        sw = SwitchNode(f'Event{next_id()}', query, pdict)
        entrypoints = []
        enum_values = {}
        if query.rv.type.startswith('enum['):
            enum_values_list = query.rv.type[5:-1].split(',')
            enum_values = {v.strip(): i for i, v in enumerate(enum_values_list)}
        for values, block in cases:
            eps, node, connector = block
            entrypoints.extend(eps)

            sw.add_out_edge(node)
            connector.add_out_edge(sw.connector)

            for value in values:
                if isinstance(value, str):
                    if not enum_values:
                        emit_error(f'Query "{query.name}" does not return an enum', start, end)
                        raise LogError()
                    if value not in enum_values:
                        emit_error(f'Enum "{value}" is not a valid return value of "{query.name}"', start, end)
                        raise LogError()
                    value = enum_values[value]
                sw.add_case(node, value)

        num_values = _get_query_num_values(query, start, end)
        default_values = set(range(num_values)) - set(sum((v for v, n in cases), []))
        if default_values:
            if default is not None:
                _, default, connector = default
                connector.add_out_edge(sw.connector)

            default_branch = default or sw.connector
            sw.add_out_edge(default_branch)
            for value in default_values:
                sw.add_case(default_branch, value)
        elif default:
            emit_warning(f'default branch for {query_name} call is dead code, ignoring', start, end)

        return entrypoints, (sw,)
示例#5
0
    def check_function(p, type_, start, end):
        if len(p) == 1:
            p = p[0]
        if isinstance(p, dict):
            actor = (p.pop('.actor_name'), p.pop('.actor_secondary'))
            name = p.pop('.name')
            negated = p.pop('.negated')
            params = pdict = p
            prepare_params = False
        else:
            actor_name, actor_secondary, name, params = p
            actor = (actor_name, actor_secondary or '')
            negated = False
            prepare_params = True

        function_name = f'EventFlow{type_.capitalize()}{name}'
        if actor not in actors:
            actors[actor] = gen_actor(*actor)
        mp = getattr(actors[actor], ['actions', 'queries'][type_ == 'query'])
        if function_name not in mp:
            emit_warning(f'no {type_} with name "{function_name}" found, using empty call', start, end)
            if type_ == 'action':
                actors[actor].register_action(Action(actor, function_name, []))
            else:
                actors[actor].register_query(Query(actor, function_name, [], IntType, False))
        function = mp[function_name]
        if prepare_params:
            try:
                pdict = function.prepare_param_dict([p for name, p in params if name is None])
                for name, p in params:
                    if name is not None:
                        if name in pdict:
                            emit_warning(f'keyword argument name {name} matches positional argument name', start, end)
                        pdict[name] = p
            except AssertionError as e:
                emit_error(str(e), start, end)
                raise LogError()
        return function_name, function, pdict, negated
示例#6
0
 def make_bool_function(p, start, end):
     query_name, query, pdict, negated = check_function(p, 'query', start, end)
     num_values = _get_query_num_values(query, start, end)
     if num_values > 2:
         emit_warning(f'call to {query_name} treated as boolean function but may not be', start, end)
     return ((query, pdict), [({0}, query.inverted != negated), (set(range(1, num_values)), (not query.inverted) != negated)])
示例#7
0
def __process_local_calls(roots: List[RootNode], local_roots: Dict[str, RootNode], exported_roots: Dict[str, RootNode], exported_tco: bool):
    post_calls: Dict[str, Set[Node]] = {}
    for root in roots:
        for node in find_postorder(root):
            if isinstance(node, SubflowNode):
                if node.ns == '':
                    if node.called_root_name not in exported_roots and node.called_root_name not in local_roots:
                        emit_warning(f'{node.called_root_name} called but not defined')
                    if node.out_edges and node.called_root_name in local_roots:
                        assert len(node.out_edges) == 1
                        post_calls[node.called_root_name] = post_calls.get(node.called_root_name, set())
                        post_calls[node.called_root_name].add(node.out_edges[0])

                    called_node = local_roots[node.called_root_name] if node.called_root_name in local_roots else exported_roots[node.called_root_name]
                    if called_node.entrypoint:
                        if node.params:
                            emit_error(f'entrypoint "{node.called_root_name}" should not be called with any parameters')
                            raise LogError()
                        continue
                    default_vardefs = {v.name: v for v in called_node.vardefs if v.initial_value is not None}
                    if len(called_node.vardefs) - len(default_vardefs) > len(node.params):
                        emit_error(f'{node.called_root_name} expects at least {len(called_node.vardefs) - len(default_vardefs)} parameters but received {len(node.params)}')
                        raise LogError()
                    for vardef in called_node.vardefs:
                        if vardef.name not in node.params and vardef.name not in default_vardefs:
                            emit_error(f'{node.called_root_name} expects parameter "{vardef.name}" of type {vardef.type}')
                            raise LogError()
                        if vardef.name not in node.params:
                            continue
                        param = node.params[vardef.name]
                        param_type = param.type
                        if param_type == ArgumentType:
                            vcand = [v.type for v in root.vardefs if v.name == param.value]
                            if not vcand:
                                emit_error(f'variable {param.value} not defined')
                                raise LogError()
                            param_type = vcand[0]

                        if param_type != vardef.type:
                            emit_error(f'{node.called_root_name} expects parameter "{vardef.name}" to be of type {vardef.type} but received {param_type} instead')
                            raise LogError()
                for param in node.params.values():
                    if isinstance(param.value, Argument):
                        param.type = ArgumentType

    for name, root in list(local_roots.items()):
        if name not in post_calls:
            continue
        if len(post_calls[name]) == 1 and all(v.initial_value is None for v in root.vardefs):
            continue_ = next(iter(post_calls[name]))
            if continue_ is TerminalNode:
                continue
            __replace_node(root, TerminalNode, continue_)
        else:
            emit_warning(f'flow {name} is local but forcing export')
            exported_roots[name] = local_roots[name]
            del local_roots[name]

    reroutes: Dict[SubflowNode, Node] = {}
    for root in roots:
        for node in find_postorder(root):
            if isinstance(node, SubflowNode) and node.ns == '':
                if not all(p.type == ArgumentType for p in node.params.values()):
                    continue
                tail_call = (len(node.out_edges) == 1 and node.out_edges[0] is TerminalNode)
                if node.called_root_name in local_roots:
                    reroutes[node] = local_roots[node.called_root_name].out_edges[0]
                elif tail_call and exported_tco:
                    # TODO fix mutual recursive case when turned off
                    reroutes[node] = exported_roots[node.called_root_name].out_edges[0]

    changed = True
    while changed:
        changed = False
        for from_, to in list(reroutes.items()):
            if to in reroutes:
                assert isinstance(to, SubflowNode)
                reroutes[from_] = reroutes[to]
                changed = True

    for root in roots:
        s: List[Node] = [root]
        added = set()
        while s:
            node = s.pop()
            for child in list(node.out_edges):
                if isinstance(child, SubflowNode) and child in reroutes:
                    node.reroute_out_edge(child, reroutes[child])
            for child in node.out_edges:
                if child not in added:
                    s.append(child)
                    added.add(child)