// Simplify the children of the given node, removing any that never match or do // nothing. // The out_changed variable is set to true when any simplifications occur. static void bnf_simplify_children(bnf_t* tree, bnf_t* parent, bool* out_never, bool* out_nop, bool *out_changed) { pony_assert(parent != NULL); if(out_never != NULL) *out_never = false; if(out_nop != NULL) *out_nop = false; // Run through the child list bnf_t* prev = NULL; bnf_t* p = parent->child; while(p != NULL) { // Simplify the child bnf_simplify_node(tree, p, out_changed); if(p->id == BNF_NEVER && out_never != NULL) *out_never = true; if(p->id == BNF_NOP && out_nop != NULL) *out_nop = true; if(p->id == BNF_NEVER || p->id == BNF_NOP) { // Remove this child if(prev == NULL) // Removing first node in list parent->child = p->sibling; else prev->sibling = p->sibling; bnf_t* next = p->sibling; p->sibling = NULL; bnf_free(p); p = next; *out_changed = true; } else { prev = p; p = p->sibling; } } parent->last_child = prev; }
// Attempt to simplify the given node. // We simplify from the bottom up, removing subrules that can never match or do // nothing. We also inline trivial rules when they are referenced. // The out_changed variable is set to true when any simplifications occur. static void bnf_simplify_node(bnf_t* tree, bnf_t* bnf, bool *out_changed) { assert(bnf != NULL); assert(out_changed != NULL); switch(bnf->id) { case BNF_TREE: bnf_simplify_children(tree, bnf, NULL, NULL, out_changed); break; case BNF_DEF: bnf_simplify_node(tree, bnf->child, out_changed); break; case BNF_SEQ: { bool any_never = false; bnf_simplify_children(tree, bnf, &any_never, NULL, out_changed); if(any_never) { bnf->id = BNF_NEVER; *out_changed = true; } else if(bnf->child == NULL) { // Empty sequence bnf->id = BNF_NOP; *out_changed = true; } else if(bnf->child->sibling == NULL) { // Lone node in sequence bnf_use_child(bnf); *out_changed = true; } break; } case BNF_OR: { bool any_nop = false; bnf_simplify_children(tree, bnf, NULL, &any_nop, out_changed); if(any_nop) { bnf->optional = true; *out_changed = true; } if(bnf->child == NULL) { // Empty set bnf->id = (bnf->optional) ? BNF_NOP : BNF_NEVER; *out_changed = true; } else if(bnf->child->sibling == NULL && !bnf->optional) { // Lone node in or bnf_use_child(bnf); *out_changed = true; } break; } case BNF_REPEAT: bnf_simplify_children(tree, bnf, NULL, NULL, out_changed); if(bnf->child == NULL) { // Empty body bnf->id = BNF_NOP; *out_changed = true; } break; case BNF_RULE: { // Check for inlinable rules if(bnf->name == NULL) // Hack rules aren't inlinable break; bnf_t* def = bnf_find_def(tree, bnf->name); assert(def != NULL); bnf_t* rule = def->child; assert(rule != NULL); // We inline rules that are nevers, nops, single token / rule references // or have been explicitly marked to inline if(rule->id == BNF_NEVER || rule->id == BNF_NOP || rule->id == BNF_TOKEN || rule->id == BNF_QUOTED_TOKEN || rule->id == BNF_RULE || def->inline_rule) { // Inline rule bnf->id = rule->id; bnf->name = rule->name; bnf->optional = rule->optional; bnf->last_child = NULL; bnf->child = bnf_copy(rule->child, &bnf->last_child); // Child of def should only ever have one child, so don't need to worry // about copying siblings assert(rule->sibling == NULL); // Don't worry about simplifying children now, leave that til the next // iteration *out_changed = true; } break; } default: break; } }