Skip to content
This repository was archived by the owner on Jun 9, 2022. It is now read-only.

Commit c23550d

Browse files
committed
Fixed jsgf_build_fsg_internal() to handle right-recursion in the presence of alternates.
1 parent 4ffc4b7 commit c23550d

File tree

2 files changed

+152
-6
lines changed

2 files changed

+152
-6
lines changed

src/libsphinxbase/lm/jsgf.c

Lines changed: 149 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ extern int yyparse(void *scanner, jsgf_t * jsgf);
5858
* into Sphinx finite-state grammars.
5959
**/
6060

61+
static void detect_recursion_rule(jsgf_t * grammar, jsgf_rule_t * rule);
6162
static int expand_rule(jsgf_t * grammar, jsgf_rule_t * rule,
6263
int rule_entry, int rule_exit);
6364

@@ -283,6 +284,100 @@ importname2rulename(char *importname)
283284
#define NO_NODE -1
284285
#define RECURSIVE_NODE -2
285286

287+
/**
288+
*
289+
* Detect recursion; label each RHS as to whether it's right-recursive.
290+
* Just a cut-down version of expand_rhs() and expand_rule().
291+
*/
292+
static void
293+
detect_recursion_rhs(jsgf_t * grammar, jsgf_rule_t * rule, jsgf_rhs_t * rhs)
294+
{
295+
gnode_t *gn;
296+
297+
/* Iterate over atoms in rhs */
298+
for (gn = rhs->atoms; gn; gn = gnode_next(gn)) {
299+
jsgf_atom_t *atom = (jsgf_atom_t *) gnode_ptr(gn);
300+
301+
if (jsgf_atom_is_rule(atom)) {
302+
jsgf_rule_t *subrule;
303+
char *fullname;
304+
int32 subrule_lookup_result;
305+
gnode_t *subnode;
306+
jsgf_rule_stack_t *rule_stack_entry = NULL;
307+
308+
if (0 == strcmp(atom->name, "<NULL>")) {
309+
continue;
310+
}
311+
else if (0 == strcmp(atom->name, "<VOID>")) {
312+
/* This entire RHS is unspeakable */
313+
return;
314+
}
315+
316+
fullname = jsgf_fullname_from_rule(rule, atom->name);
317+
subrule_lookup_result = hash_table_lookup
318+
(grammar->rules, fullname, (void **) &subrule);
319+
ckd_free(fullname);
320+
if (subrule_lookup_result == -1) {
321+
E_ERROR("Undefined rule in RHS: %s\n", fullname);
322+
return;
323+
}
324+
325+
/* Look for this subrule in the stack of expanded rules */
326+
for (subnode = grammar->rulestack; subnode;
327+
subnode = gnode_next(subnode)) {
328+
rule_stack_entry =
329+
(jsgf_rule_stack_t *) gnode_ptr(subnode);
330+
if (rule_stack_entry->rule == subrule)
331+
break;
332+
}
333+
334+
if (subnode != NULL) {
335+
/* Allow right-recursion only. */
336+
if (gnode_next(gn) != NULL) {
337+
E_ERROR
338+
("Only right-recursion is permitted (in %s.%s)\n",
339+
grammar->name, rule->name);
340+
return;
341+
}
342+
343+
/* Remember this RHS is right-recursive. */
344+
rhs->is_recursive = 1;
345+
}
346+
347+
/* If right-recursion has already been checked on this rule,
348+
there's no need to check it again. */
349+
else if (!subrule->ck_recursive) {
350+
/* Expand the subrule */
351+
detect_recursion_rule(grammar, subrule);
352+
}
353+
}
354+
}
355+
}
356+
357+
static void
358+
detect_recursion_rule(jsgf_t * grammar, jsgf_rule_t * rule)
359+
{
360+
jsgf_rule_stack_t *rule_stack_entry;
361+
jsgf_rhs_t *rhs;
362+
363+
/* Push this rule onto the stack */
364+
rule_stack_entry =
365+
(jsgf_rule_stack_t *) ckd_calloc(1, sizeof(jsgf_rule_stack_t));
366+
rule_stack_entry->rule = rule;
367+
rule_stack_entry->entry = NO_NODE /* unused */;
368+
grammar->rulestack = glist_add_ptr(grammar->rulestack,
369+
rule_stack_entry);
370+
371+
rule->ck_recursive = 1;
372+
for (rhs = rule->rhs; rhs; rhs = rhs->alt) {
373+
detect_recursion_rhs(grammar, rule, rhs);
374+
}
375+
376+
/* Pop this rule from the rule stack */
377+
ckd_free(gnode_ptr(grammar->rulestack));
378+
grammar->rulestack = gnode_free(grammar->rulestack, NULL);
379+
}
380+
286381
/**
287382
*
288383
* Expand a right-hand-side of a rule (i.e. a single alternate).
@@ -368,7 +463,7 @@ expand_rhs(jsgf_t * grammar, jsgf_rule_t * rule, jsgf_rhs_t * rhs,
368463

369464
/* Let our caller know that this rhs didn't reach an
370465
end state. */
371-
lastnode = RECURSIVE_NODE;
466+
lastnode = -lastnode + RECURSIVE_NODE;
372467
}
373468
else {
374469
/* If this is the last atom in this rhs, link its
@@ -415,6 +510,7 @@ expand_rule(jsgf_t * grammar, jsgf_rule_t * rule, int rule_entry,
415510
int rule_exit)
416511
{
417512
jsgf_rule_stack_t *rule_stack_entry;
513+
int multiple_alternates;
418514
jsgf_rhs_t *rhs;
419515

420516
/* Push this rule onto the stack */
@@ -425,18 +521,32 @@ expand_rule(jsgf_t * grammar, jsgf_rule_t * rule, int rule_entry,
425521
grammar->rulestack = glist_add_ptr(grammar->rulestack,
426522
rule_stack_entry);
427523

524+
/* Remember whether this rule has more than one alternate.
525+
This determines whether a null-transition has to be added
526+
in order to to separate right-recursive sub-rules from
527+
its sibling alternates. */
528+
multiple_alternates = (rule->rhs != NULL && rule->rhs->alt != NULL);
529+
530+
/* Deal with all non-right-recursive rules. (They will need
531+
the exit-node generated here.) */
428532
for (rhs = rule->rhs; rhs; rhs = rhs->alt) {
429533
int lastnode;
430534

535+
/* If this rule is right-recursive, deal with it in the
536+
next pass. */
537+
if (rhs->is_recursive && multiple_alternates) {
538+
continue;
539+
}
540+
431541
lastnode = expand_rhs(grammar, rule, rhs, rule_entry, rule_exit);
432542

433543
if (lastnode == NO_NODE) {
434544
return NO_NODE;
435545
}
436-
else if (lastnode == RECURSIVE_NODE) {
437-
/* The rhs ended with right-recursion, i.e. a transition to
438-
an earlier state. Nothing needs to happen at this level. */
439-
;
546+
else if (lastnode <= RECURSIVE_NODE) {
547+
/* Shouldn't happen in this pass. */
548+
E_ERROR("Internal error");
549+
return NO_NODE;
440550
}
441551
else if (rule_exit == NO_NODE) {
442552
/* If this rule doesn't have an exit state yet, use the exit
@@ -452,6 +562,37 @@ expand_rule(jsgf_t * grammar, jsgf_rule_t * rule, int rule_entry,
452562
rule_exit = rule_entry;
453563
}
454564

565+
/* Now deal with all the right-recursive rules. */
566+
for (rhs = rule->rhs; rhs; rhs = rhs->alt) {
567+
int this_rule_entry, lastnode;
568+
569+
/* If this rule is right-recursive, insert a null-transition to
570+
give the right-recursion a unique destination, i.e. so that
571+
it doesn't interfere with any of its sibling alternates. */
572+
this_rule_entry = rule_entry;
573+
if (!rhs->is_recursive || !multiple_alternates) {
574+
continue;
575+
}
576+
jsgf_add_link(grammar, NULL,
577+
rule_entry, grammar->nstate);
578+
this_rule_entry = grammar->nstate;
579+
++grammar->nstate;
580+
rule_stack_entry->entry = this_rule_entry;
581+
582+
lastnode = expand_rhs(grammar, rule, rhs, this_rule_entry, rule_exit);
583+
584+
if (lastnode > RECURSIVE_NODE) {
585+
/* Shouldn't happen in this pass. */
586+
E_ERROR("Internal error");
587+
return NO_NODE;
588+
}
589+
590+
/* Allow the right-recursion to exit. */
591+
lastnode = -lastnode + RECURSIVE_NODE;
592+
jsgf_add_link(grammar, NULL,
593+
lastnode, rule_exit);
594+
}
595+
455596
/* Pop this rule from the rule stack */
456597
ckd_free(gnode_ptr(grammar->rulestack));
457598
grammar->rulestack = gnode_free(grammar->rulestack, NULL);
@@ -541,6 +682,9 @@ jsgf_build_fsg_internal(jsgf_t * grammar, jsgf_rule_t * rule,
541682
grammar->links = NULL;
542683
grammar->nstate = 0;
543684

685+
/* Determine which rules are right-recursive. */
686+
detect_recursion_rule(grammar, rule);
687+
544688
/* Create the top-level entry state, and expand the
545689
top-level rule. */
546690
rule_entry = grammar->nstate++;

src/libsphinxbase/lm/jsgf_internal.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ struct jsgf_s {
8585

8686
/* Scratch variables for FSG conversion. */
8787
int nstate; /**< Number of generated states. */
88-
glist_t links; /**< Generated FSG links. */
88+
glist_t links; /**< Generated FSG links. */
8989
glist_t rulestack; /**< Stack of currently expanded rules. */
9090
};
9191

@@ -99,10 +99,12 @@ struct jsgf_rule_s {
9999
int refcnt; /**< Reference count. */
100100
char *name; /**< Rule name (NULL for an alternation/grouping) */
101101
int is_public; /**< Is this rule marked 'public'? */
102+
int ck_recursive;/**< Has right-recursion been checked on this rule already? */
102103
jsgf_rhs_t *rhs; /**< Expansion */
103104
};
104105

105106
struct jsgf_rhs_s {
107+
int is_recursive;/**< Does this RHS recurse to its containing rule? */
106108
glist_t atoms; /**< Sequence of items */
107109
jsgf_rhs_t *alt; /**< Linked list of alternates */
108110
};

0 commit comments

Comments
 (0)