Exemplo n.º 1
0
def parse_elements(rulename, elements):
    machine = StateMachine(rulename)
    machine_start = State(START_STATE)
    machine_end = State(FINAL_STATE)

    elements = elements_splitter.split_into_tokens(elements)
    start_edges, end_states, states = recursive_parse_elements(elements)

    for state in states:
        machine.add_state(state)
    for start_edge in start_edges:
        machine_start.add_edge(start_edge)
    for end_state in end_states:
        end_state.add_edge(Edge('', FINAL_STATE))

    machine.add_state(machine_start)
    machine.add_state(machine_end)

    return machine
Exemplo n.º 2
0
def apply_repetition(in_edges, end_states, states_to_repeat, repetition,
                     is_optional):
    logger.debug(
        f'Applying repetition of {repetition} and is_optional of {is_optional} to:\nin_edges={in_edges}\nend_states={end_states}\nstates_to_repeat={states_to_repeat}'
    )
    if not isinstance(in_edges, list):
        in_edges = [in_edges]
    if not isinstance(end_states, list):
        end_states = [end_states]
    if not isinstance(states_to_repeat, list):
        states_to_repeat = [states_to_repeat]

    if not is_optional and repetition == None:
        logger.debug(f'Got request that has no repetition! Returning as-is.')
        return in_edges, end_states, states_to_repeat

    end_state_ids = {state.id for state in end_states}

    # If the batch is optional, that's the same as having 0-1 repetition.
    if is_optional:
        min_reps = 0
        max_reps = 1
    else:
        min_reps, max_reps = repetition
    logger.debug(
        f'Applying repetition of {min_reps}-{max_reps} to states {states_to_repeat}'
    )

    copies_to_create = max_reps
    if max_reps == float('inf'):
        logger.debug(
            f'max_reps is inf, making min_reps copies instead and adding self-loop on last copy.'
        )
        copies_to_create = min_reps

    states = [state.clone() for state in states_to_repeat]
    new_end_states = [state for state in states if state.id in end_state_ids]
    iteration_suffix = ''
    for i in range(2, copies_to_create + 1):
        iteration_suffix = f'_{i}'
        logger.debug(f'Making copy {i} with suffix {iteration_suffix}.')

        # Copy the in_edges with the new suffix, and add them to the old end_states,
        # in order to link the last copy up to the current copy.
        for edge in in_edges:
            for end_state in new_end_states:
                end_to_start_edge = edge.clone()
                end_to_start_edge.dest += iteration_suffix
                logger.debug(
                    f'Adding end_to_start_edge {end_to_start_edge} to state {end_state}.'
                )
                end_state.add_edge(end_to_start_edge)

        # Copy all of the original states for the new iteration.
        new_end_states = []
        for state in states_to_repeat:
            logger.debug(f'states_to_repeat: {states_to_repeat}')
            state_id = state.id
            n_state_id = f'{state_id}' + iteration_suffix
            logger.debug(f'Copying state {state.id} as {n_state_id}')

            next_state = State(n_state_id, is_automata=state.is_automata)
            # Copy all of the edges for the state, adding the new suffix. Note
            # that there aren't any edges beyond end_states, so we don't have to
            # worry about this going outside of the current copy iteration.
            for edge in state.edges:
                logger.debug(f'Updating edge {edge}.')
                next_edge = edge.clone()
                next_edge.dest += iteration_suffix
                logger.debug(
                    f'Adding edge {next_edge} to new state {next_state.id}')
                next_state.add_edge(next_edge)

            # Append the state to the states list to keep track of it, and add
            # if to the new_end_states if it's a copy of an end state.
            logger.debug(f'Adding state to states: {state}')
            states.append(next_state)
            if state_id in end_state_ids:
                new_end_states.append(next_state)
        logger.debug(f'new_end_states: {new_end_states}')

    logger.debug(
        f'Finished creating states. Created state IDs are: {[state.id for state in states]}'
    )

    # If we have infinite repetition we obviously can't create infinite states,
    # so we add a self-loop from the end of the last copy to its beginning.
    if max_reps == float('inf'):
        logger.debug(f'Handling skips for infinite max_reps.')
        new_in_edges = []
        for end_state in new_end_states:
            for edge in in_edges:
                repeat_last_copy_edge = edge.clone()
                repeat_last_copy_edge.dest += iteration_suffix
                logger.debug(
                    f'Adding infinite loop edge {repeat_last_copy_edge} to state {end_state.id}'
                )
                end_state.add_edge(repeat_last_copy_edge)
            if min_reps == 0:
                skip_edge = Edge('', end_state.id)
                logger.debug(
                    f'Got optional or 0-min repetition, adding skip edge {skip_edge} to new_in_edges.'
                )
                new_in_edges.append(skip_edge)
        in_edges += new_in_edges
    # If it's not infinite, then we made max_reps states, but any of them beyond
    # min_reps can be skipped, so we add a skip edge from each end state of an
    # optional copy cohort to each end state of the full batch.
    else:
        logger.debug(
            f'Handling skips for min_reps={min_reps}, max_reps={max_reps}.')

        skip_edges = [Edge('', state.id) for state in new_end_states]
        logger.debug(f'skip_edges: {skip_edges}')

        for i in range(min_reps, max_reps):
            logger.debug(f'Adding skips to copy {i}.')

            if i == 0:
                for end_state in new_end_states:
                    skip_edge = Edge('', end_state.id)
                    logger.debug(
                        f'Got optional or 0-min repetition, adding skip edge {skip_edge} to in_edges.'
                    )
                    in_edges.append(skip_edge)
                continue

            state_suffix = f'_{i}'
            if i == 1:
                state_suffix = ''

            ith_end_state_ids = {
                state.id + state_suffix
                for state in end_states
            }
            logger.debug(f'ith_end_state_ids: {ith_end_state_ids}')
            logger.debug(f'states: {states}')
            for state in states:
                if state.id in ith_end_state_ids:
                    for edge in skip_edges:
                        logger.debug(
                            f'Adding skip edge {edge} to state {state.id}')
                        state.add_edge(edge.clone())

    logger.debug(
        f'Returning:\n  in_edges: {in_edges}\n  new_end_states: {new_end_states}\n  states: {states}'
    )
    return in_edges, new_end_states, states