diff --git a/lib/construct.c b/lib/construct.c index 3eb4811..0697046 100644 --- a/lib/construct.c +++ b/lib/construct.c @@ -99,35 +99,33 @@ static void retarget_merged_rules( if (0 == rules[i].next) continue; - // IDs less than initial have to be offset by one less than - // base_count because the final state (ID zero) is not copied. - // If they are greater it's two less as the initial state is - // also not copied. Finally, if the target is the initial - // state then it should be changed to the base's initial - // state. + // If the state came before the initial state it should be + // offset by one less than base_count, because the final state + // (index zero) came before it and was not copied into the + // base. + const int before_offset = base_count - 1; + + // If it came after the initial state it must be offset by two + // less than base_count because both the final state and the + // initial state came before it and were not copied -- unless + // the initial state is the same state as the final state, in + // which case the offset is still only one less than + // base_count. + const int after_offset = base_count - (0 != initial ? 2 : 1); + if (rules[i].next < initial) - rules[i].next += base_count - 1; + rules[i].next += before_offset; else if (rules[i].next > initial) - rules[i].next += base_count - 2; - else + rules[i].next += after_offset; + else if (rules[i].next == initial) rules[i].next = base_initial; } } static void merge_fsas(fsa_t *base, const fsa_t *other) { - const int new_count = base->count + other->count - 2; - if (base->capacity < new_count) { - do - base->capacity *= 2; - while (base->capacity < new_count); - base->states - = realloc(base->states, base->capacity * sizeof(fsa_state_t)); - assert(base->states); - } - - // Copy rules from the other's initial state into the base's, then - // retarget them. + // Copy rules from the other's initial state into the base's + // initial state. fsa_state_t *initial = &base->states[base->initial]; const fsa_state_t *other_initial = &other->states[other->initial]; const int new_rule_count = initial->count + other_initial->count; @@ -142,13 +140,23 @@ static void merge_fsas(fsa_t *base, const fsa_t *other) memcpy( &initial->rules[initial->count], other_initial->rules, other_initial->count * sizeof(fsa_rule_t)); + + // Retarget the copied rules. retarget_merged_rules( &initial->rules[initial->count], other_initial->count, other->initial, base->initial, base->count); - initial->count = new_rule_count; - // Copy other states, skipping the initial state, then retarget - // their rules. + // Copy other states, skipping the initial state. + const int skipped_states = other->initial != 0 ? 2 : 1; + const int new_count = base->count + other->count - skipped_states; + if (base->capacity < new_count) { + do + base->capacity *= 2; + while (base->capacity < new_count); + base->states + = realloc(base->states, base->capacity * sizeof(fsa_state_t)); + assert(base->states); + } int offset = base->count; if (1 < other->initial) { const int copy_count = other->initial - 1; @@ -163,15 +171,20 @@ static void merge_fsas(fsa_t *base, const fsa_t *other) &base->states[offset], &other->states[other->initial], copy_size); } + + // Retarget the copied states' rules. for (int i = base->count; i < new_count; ++i) { retarget_merged_rules( base->states[i].rules, base->states[i].count, other->initial, base->initial, base->count); } + + initial->count = new_rule_count; base->count = new_count; free(other->states[0].rules); - free(other->states[other->initial].rules); + if (other->initial != 0) + free(other->states[other->initial].rules); free(other->states); assert(base->states[0].final);