SphinxBase
0.6
|
00001 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */ 00002 /* ==================================================================== 00003 * Copyright (c) 2007 Carnegie Mellon University. All rights 00004 * reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 00010 * 1. Redistributions of source code must retain the above copyright 00011 * notice, this list of conditions and the following disclaimer. 00012 * 00013 * 2. Redistributions in binary form must reproduce the above copyright 00014 * notice, this list of conditions and the following disclaimer in 00015 * the documentation and/or other materials provided with the 00016 * distribution. 00017 * 00018 * This work was supported in part by funding from the Defense Advanced 00019 * Research Projects Agency and the National Science Foundation of the 00020 * United States of America, and the CMU Sphinx Speech Consortium. 00021 * 00022 * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND 00023 * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 00024 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00025 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY 00026 * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00027 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00028 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00029 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00030 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00031 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00032 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 * 00034 * ==================================================================== 00035 * 00036 */ 00037 00038 #include <string.h> 00039 #include <assert.h> 00040 00041 #include "sphinxbase/ckd_alloc.h" 00042 #include "sphinxbase/strfuncs.h" 00043 #include "sphinxbase/hash_table.h" 00044 #include "sphinxbase/err.h" 00045 00046 #include "jsgf_internal.h" 00047 #include "jsgf_parser.h" 00048 #include "jsgf_scanner.h" 00049 00050 int yyparse (yyscan_t yyscanner, jsgf_t *jsgf); 00051 00059 jsgf_atom_t * 00060 jsgf_atom_new(char *name, float weight) 00061 { 00062 jsgf_atom_t *atom; 00063 00064 atom = ckd_calloc(1, sizeof(*atom)); 00065 atom->name = ckd_salloc(name); 00066 atom->weight = weight; 00067 return atom; 00068 } 00069 00070 int 00071 jsgf_atom_free(jsgf_atom_t *atom) 00072 { 00073 if (atom == NULL) 00074 return 0; 00075 ckd_free(atom->name); 00076 ckd_free(atom); 00077 return 0; 00078 } 00079 00080 jsgf_t * 00081 jsgf_grammar_new(jsgf_t *parent) 00082 { 00083 jsgf_t *grammar; 00084 00085 grammar = ckd_calloc(1, sizeof(*grammar)); 00086 /* If this is an imported/subgrammar, then we will share a global 00087 * namespace with the parent grammar. */ 00088 if (parent) { 00089 grammar->rules = parent->rules; 00090 grammar->imports = parent->imports; 00091 grammar->searchpath = parent->searchpath; 00092 grammar->parent = parent; 00093 } 00094 else { 00095 char *jsgf_path; 00096 00097 grammar->rules = hash_table_new(64, 0); 00098 grammar->imports = hash_table_new(16, 0); 00099 00100 /* Silvio Moioli: no getenv() in Windows CE */ 00101 #if !defined(_WIN32_WCE) 00102 if ((jsgf_path = getenv("JSGF_PATH")) != NULL) { 00103 char *word, *c; 00104 00105 /* FIXME: This should be a function in libsphinxbase. */ 00106 /* FIXME: Also nextword() is totally useless... */ 00107 word = jsgf_path = ckd_salloc(jsgf_path); 00108 while ((c = strchr(word, ':'))) { 00109 *c = '\0'; 00110 grammar->searchpath = glist_add_ptr(grammar->searchpath, word); 00111 word = c + 1; 00112 } 00113 grammar->searchpath = glist_add_ptr(grammar->searchpath, word); 00114 grammar->searchpath = glist_reverse(grammar->searchpath); 00115 } 00116 else { 00117 /* Default to current directory. */ 00118 grammar->searchpath = glist_add_ptr(grammar->searchpath, ckd_salloc(".")); 00119 } 00120 #endif 00121 } 00122 00123 return grammar; 00124 } 00125 00126 void 00127 jsgf_grammar_free(jsgf_t *jsgf) 00128 { 00129 /* FIXME: Probably should just use refcounting instead. */ 00130 if (jsgf->parent == NULL) { 00131 hash_iter_t *itor; 00132 gnode_t *gn; 00133 00134 for (itor = hash_table_iter(jsgf->rules); itor; 00135 itor = hash_table_iter_next(itor)) { 00136 ckd_free((char *)itor->ent->key); 00137 jsgf_rule_free((jsgf_rule_t *)itor->ent->val); 00138 } 00139 hash_table_free(jsgf->rules); 00140 for (itor = hash_table_iter(jsgf->imports); itor; 00141 itor = hash_table_iter_next(itor)) { 00142 ckd_free((char *)itor->ent->key); 00143 jsgf_grammar_free((jsgf_t *)itor->ent->val); 00144 } 00145 hash_table_free(jsgf->imports); 00146 for (gn = jsgf->searchpath; gn; gn = gnode_next(gn)) 00147 ckd_free(gnode_ptr(gn)); 00148 glist_free(jsgf->searchpath); 00149 for (gn = jsgf->links; gn; gn = gnode_next(gn)) 00150 ckd_free(gnode_ptr(gn)); 00151 glist_free(jsgf->links); 00152 } 00153 ckd_free(jsgf->name); 00154 ckd_free(jsgf->version); 00155 ckd_free(jsgf->charset); 00156 ckd_free(jsgf->locale); 00157 ckd_free(jsgf); 00158 } 00159 00160 static void 00161 jsgf_rhs_free(jsgf_rhs_t *rhs) 00162 { 00163 gnode_t *gn; 00164 00165 if (rhs == NULL) 00166 return; 00167 00168 jsgf_rhs_free(rhs->alt); 00169 for (gn = rhs->atoms; gn; gn = gnode_next(gn)) 00170 jsgf_atom_free(gnode_ptr(gn)); 00171 glist_free(rhs->atoms); 00172 ckd_free(rhs); 00173 } 00174 00175 jsgf_atom_t * 00176 jsgf_kleene_new(jsgf_t *jsgf, jsgf_atom_t *atom, int plus) 00177 { 00178 jsgf_rule_t *rule; 00179 jsgf_atom_t *rule_atom; 00180 jsgf_rhs_t *rhs; 00181 00182 /* Generate an "internal" rule of the form (<NULL> | <name> <g0006>) */ 00183 /* Or if plus is true, (<name> | <name> <g0006>) */ 00184 rhs = ckd_calloc(1, sizeof(*rhs)); 00185 if (plus) 00186 rhs->atoms = glist_add_ptr(NULL, jsgf_atom_new(atom->name, 1.0)); 00187 else 00188 rhs->atoms = glist_add_ptr(NULL, jsgf_atom_new("<NULL>", 1.0)); 00189 rule = jsgf_define_rule(jsgf, NULL, rhs, 0); 00190 rule_atom = jsgf_atom_new(rule->name, 1.0); 00191 rhs = ckd_calloc(1, sizeof(*rhs)); 00192 rhs->atoms = glist_add_ptr(NULL, rule_atom); 00193 rhs->atoms = glist_add_ptr(rhs->atoms, atom); 00194 rule->rhs->alt = rhs; 00195 00196 return jsgf_atom_new(rule->name, 1.0); 00197 } 00198 00199 jsgf_rule_t * 00200 jsgf_optional_new(jsgf_t *jsgf, jsgf_rhs_t *exp) 00201 { 00202 jsgf_rhs_t *rhs = ckd_calloc(1, sizeof(*rhs)); 00203 jsgf_atom_t *atom = jsgf_atom_new("<NULL>", 1.0); 00204 rhs->alt = exp; 00205 rhs->atoms = glist_add_ptr(NULL, atom); 00206 return jsgf_define_rule(jsgf, NULL, rhs, 0); 00207 } 00208 00209 void 00210 jsgf_add_link(jsgf_t *grammar, jsgf_atom_t *atom, int from, int to) 00211 { 00212 jsgf_link_t *link; 00213 00214 link = ckd_calloc(1, sizeof(*link)); 00215 link->from = from; 00216 link->to = to; 00217 link->atom = atom; 00218 grammar->links = glist_add_ptr(grammar->links, link); 00219 } 00220 00221 static char * 00222 extract_grammar_name(char *rule_name) 00223 { 00224 char* dot_pos; 00225 char* grammar_name = ckd_salloc(rule_name+1); 00226 if ((dot_pos = strrchr(grammar_name + 1, '.')) == NULL) { 00227 ckd_free(grammar_name); 00228 return NULL; 00229 } 00230 *dot_pos='\0'; 00231 return grammar_name; 00232 } 00233 00234 static char * 00235 jsgf_fullname(jsgf_t *jsgf, const char *name) 00236 { 00237 char *fullname; 00238 00239 /* Check if it is already qualified */ 00240 if (strchr(name + 1, '.')) 00241 return ckd_salloc(name); 00242 00243 /* Skip leading < in name */ 00244 fullname = ckd_malloc(strlen(jsgf->name) + strlen(name) + 4); 00245 sprintf(fullname, "<%s.%s", jsgf->name, name + 1); 00246 return fullname; 00247 } 00248 00249 static char * 00250 jsgf_fullname_from_rule(jsgf_rule_t *rule, const char *name) 00251 { 00252 char *fullname, *grammar_name; 00253 00254 /* Check if it is already qualified */ 00255 if (strchr(name + 1, '.')) 00256 return ckd_salloc(name); 00257 00258 /* Skip leading < in name */ 00259 if ((grammar_name = extract_grammar_name(rule->name)) == NULL) 00260 return ckd_salloc(name); 00261 fullname = ckd_malloc(strlen(grammar_name) + strlen(name) + 4); 00262 sprintf(fullname, "<%s.%s", grammar_name, name + 1); 00263 ckd_free(grammar_name); 00264 00265 return fullname; 00266 } 00267 00268 /* Extract as rulename everything after the secondlast dot, if existent. 00269 * Because everything before the secondlast dot is the path-specification. */ 00270 static char * 00271 importname2rulename(char *importname) 00272 { 00273 char *rulename = ckd_salloc(importname); 00274 char *last_dotpos; 00275 char *secondlast_dotpos; 00276 00277 if ((last_dotpos = strrchr(rulename+1, '.')) != NULL) { 00278 *last_dotpos='\0'; 00279 if ((secondlast_dotpos = strrchr(rulename+1, '.')) != NULL) { 00280 *last_dotpos='.'; 00281 *secondlast_dotpos='<'; 00282 secondlast_dotpos = ckd_salloc(secondlast_dotpos); 00283 ckd_free(rulename); 00284 return secondlast_dotpos; 00285 } 00286 else { 00287 *last_dotpos='.'; 00288 return rulename; 00289 } 00290 } 00291 else { 00292 return rulename; 00293 } 00294 } 00295 00296 static int expand_rule(jsgf_t *grammar, jsgf_rule_t *rule); 00297 static int 00298 expand_rhs(jsgf_t *grammar, jsgf_rule_t *rule, jsgf_rhs_t *rhs) 00299 { 00300 gnode_t *gn; 00301 int lastnode; 00302 00303 /* Last node expanded in this sequence. */ 00304 lastnode = rule->entry; 00305 00306 /* Iterate over atoms in rhs and generate links/nodes */ 00307 for (gn = rhs->atoms; gn; gn = gnode_next(gn)) { 00308 jsgf_atom_t *atom = gnode_ptr(gn); 00309 if (jsgf_atom_is_rule(atom)) { 00310 jsgf_rule_t *subrule; 00311 char *fullname; 00312 gnode_t *subnode; 00313 void *val; 00314 00315 /* Special case for <NULL> and <VOID> pseudo-rules */ 00316 if (0 == strcmp(atom->name, "<NULL>")) { 00317 /* Emit a NULL transition */ 00318 jsgf_add_link(grammar, atom, 00319 lastnode, grammar->nstate); 00320 lastnode = grammar->nstate; 00321 ++grammar->nstate; 00322 continue; 00323 } 00324 else if (0 == strcmp(atom->name, "<VOID>")) { 00325 /* Make this entire RHS unspeakable */ 00326 return -1; 00327 } 00328 00329 fullname = jsgf_fullname_from_rule(rule, atom->name); 00330 if (hash_table_lookup(grammar->rules, fullname, &val) == -1) { 00331 E_ERROR("Undefined rule in RHS: %s\n", fullname); 00332 ckd_free(fullname); 00333 return -1; 00334 } 00335 ckd_free(fullname); 00336 subrule = val; 00337 /* Look for this in the stack of expanded rules */ 00338 for (subnode = grammar->rulestack; subnode; subnode = gnode_next(subnode)) 00339 if (gnode_ptr(subnode) == (void *)subrule) 00340 break; 00341 if (subnode != NULL) { 00342 /* Allow right-recursion only. */ 00343 if (gnode_next(gn) != NULL) { 00344 E_ERROR("Only right-recursion is permitted (in %s.%s)\n", 00345 grammar->name, rule->name); 00346 return -1; 00347 } 00348 /* Add a link back to the beginning of this rule instance */ 00349 E_INFO("Right recursion %s %d => %d\n", atom->name, lastnode, subrule->entry); 00350 jsgf_add_link(grammar, atom, lastnode, subrule->entry); 00351 } 00352 else { 00353 /* Expand the subrule */ 00354 if (expand_rule(grammar, subrule) == -1) 00355 return -1; 00356 /* Add a link into the subrule. */ 00357 jsgf_add_link(grammar, atom, 00358 lastnode, subrule->entry); 00359 lastnode = subrule->exit; 00360 } 00361 } 00362 else { 00363 /* Add a link for this token and create a new exit node. */ 00364 jsgf_add_link(grammar, atom, 00365 lastnode, grammar->nstate); 00366 lastnode = grammar->nstate; 00367 ++grammar->nstate; 00368 } 00369 } 00370 00371 return lastnode; 00372 } 00373 00374 static int 00375 expand_rule(jsgf_t *grammar, jsgf_rule_t *rule) 00376 { 00377 jsgf_rhs_t *rhs; 00378 float norm; 00379 00380 /* Push this rule onto the stack */ 00381 grammar->rulestack = glist_add_ptr(grammar->rulestack, rule); 00382 00383 /* Normalize weights for all alternatives exiting rule->entry */ 00384 norm = 0; 00385 for (rhs = rule->rhs; rhs; rhs = rhs->alt) { 00386 if (rhs->atoms) { 00387 jsgf_atom_t *atom = gnode_ptr(rhs->atoms); 00388 norm += atom->weight; 00389 } 00390 } 00391 00392 rule->entry = grammar->nstate++; 00393 rule->exit = grammar->nstate++; 00394 if (norm == 0) norm = 1; 00395 for (rhs = rule->rhs; rhs; rhs = rhs->alt) { 00396 int lastnode; 00397 00398 if (rhs->atoms) { 00399 jsgf_atom_t *atom = gnode_ptr(rhs->atoms); 00400 atom->weight /= norm; 00401 } 00402 lastnode = expand_rhs(grammar, rule, rhs); 00403 if (lastnode == -1) { 00404 return -1; 00405 } 00406 else { 00407 jsgf_add_link(grammar, NULL, lastnode, rule->exit); 00408 } 00409 } 00410 00411 /* Pop this rule from the rule stack */ 00412 grammar->rulestack = gnode_free(grammar->rulestack, NULL); 00413 return rule->exit; 00414 } 00415 00416 jsgf_rule_iter_t * 00417 jsgf_rule_iter(jsgf_t *grammar) 00418 { 00419 return hash_table_iter(grammar->rules); 00420 } 00421 00422 jsgf_rule_t * 00423 jsgf_get_rule(jsgf_t *grammar, char const *name) 00424 { 00425 void *val; 00426 00427 if (hash_table_lookup(grammar->rules, name, &val) < 0) 00428 return NULL; 00429 return (jsgf_rule_t *)val; 00430 } 00431 00432 char const * 00433 jsgf_rule_name(jsgf_rule_t *rule) 00434 { 00435 return rule->name; 00436 } 00437 00438 int 00439 jsgf_rule_public(jsgf_rule_t *rule) 00440 { 00441 return rule->public; 00442 } 00443 00444 static fsg_model_t * 00445 jsgf_build_fsg_internal(jsgf_t *grammar, jsgf_rule_t *rule, 00446 logmath_t *lmath, float32 lw, int do_closure) 00447 { 00448 fsg_model_t *fsg; 00449 glist_t nulls; 00450 gnode_t *gn; 00451 00452 /* Clear previous links */ 00453 for (gn = grammar->links; gn; gn = gnode_next(gn)) { 00454 ckd_free(gnode_ptr(gn)); 00455 } 00456 glist_free(grammar->links); 00457 grammar->links = NULL; 00458 rule->entry = rule->exit = 0; 00459 grammar->nstate = 0; 00460 expand_rule(grammar, rule); 00461 00462 fsg = fsg_model_init(rule->name, lmath, lw, grammar->nstate); 00463 fsg->start_state = rule->entry; 00464 fsg->final_state = rule->exit; 00465 grammar->links = glist_reverse(grammar->links); 00466 for (gn = grammar->links; gn; gn = gnode_next(gn)) { 00467 jsgf_link_t *link = gnode_ptr(gn); 00468 00469 if (link->atom) { 00470 if (jsgf_atom_is_rule(link->atom)) { 00471 fsg_model_null_trans_add(fsg, link->from, link->to, 00472 logmath_log(lmath, link->atom->weight)); 00473 } 00474 else { 00475 int wid = fsg_model_word_add(fsg, link->atom->name); 00476 fsg_model_trans_add(fsg, link->from, link->to, 00477 logmath_log(lmath, link->atom->weight), wid); 00478 } 00479 } 00480 else { 00481 fsg_model_null_trans_add(fsg, link->from, link->to, 0); 00482 } 00483 } 00484 if (do_closure) { 00485 nulls = fsg_model_null_trans_closure(fsg, NULL); 00486 glist_free(nulls); 00487 } 00488 00489 return fsg; 00490 } 00491 00492 fsg_model_t * 00493 jsgf_build_fsg(jsgf_t *grammar, jsgf_rule_t *rule, 00494 logmath_t *lmath, float32 lw) 00495 { 00496 return jsgf_build_fsg_internal(grammar, rule, lmath, lw, TRUE); 00497 } 00498 00499 fsg_model_t * 00500 jsgf_build_fsg_raw(jsgf_t *grammar, jsgf_rule_t *rule, 00501 logmath_t *lmath, float32 lw) 00502 { 00503 return jsgf_build_fsg_internal(grammar, rule, lmath, lw, FALSE); 00504 } 00505 00506 int 00507 jsgf_write_fsg(jsgf_t *grammar, jsgf_rule_t *rule, FILE *outfh) 00508 { 00509 fsg_model_t *fsg; 00510 logmath_t *lmath = logmath_init(1.0001, 0, 0); 00511 00512 if ((fsg = jsgf_build_fsg_raw(grammar, rule, lmath, 1.0)) == NULL) 00513 goto error_out; 00514 00515 fsg_model_write(fsg, outfh); 00516 logmath_free(lmath); 00517 return 0; 00518 00519 error_out: 00520 logmath_free(lmath); 00521 return -1; 00522 } 00523 jsgf_rule_t * 00524 jsgf_define_rule(jsgf_t *jsgf, char *name, jsgf_rhs_t *rhs, int public) 00525 { 00526 jsgf_rule_t *rule; 00527 void *val; 00528 00529 if (name == NULL) { 00530 name = ckd_malloc(strlen(jsgf->name) + 16); 00531 sprintf(name, "<%s.g%05d>", jsgf->name, hash_table_inuse(jsgf->rules)); 00532 } 00533 else { 00534 char *newname; 00535 00536 newname = jsgf_fullname(jsgf, name); 00537 name = newname; 00538 } 00539 00540 rule = ckd_calloc(1, sizeof(*rule)); 00541 rule->refcnt = 1; 00542 rule->name = ckd_salloc(name); 00543 rule->rhs = rhs; 00544 rule->public = public; 00545 00546 E_INFO("Defined rule: %s%s\n", 00547 rule->public ? "PUBLIC " : "", 00548 rule->name); 00549 val = hash_table_enter(jsgf->rules, name, rule); 00550 if (val != (void *)rule) { 00551 E_WARN("Multiply defined symbol: %s\n", name); 00552 } 00553 return rule; 00554 } 00555 00556 jsgf_rule_t * 00557 jsgf_rule_retain(jsgf_rule_t *rule) 00558 { 00559 ++rule->refcnt; 00560 return rule; 00561 } 00562 00563 int 00564 jsgf_rule_free(jsgf_rule_t *rule) 00565 { 00566 if (rule == NULL) 00567 return 0; 00568 if (--rule->refcnt > 0) 00569 return rule->refcnt; 00570 jsgf_rhs_free(rule->rhs); 00571 ckd_free(rule->name); 00572 ckd_free(rule); 00573 return 0; 00574 } 00575 00576 00577 /* FIXME: This should go in libsphinxutil */ 00578 static char * 00579 path_list_search(glist_t paths, char *path) 00580 { 00581 gnode_t *gn; 00582 00583 for (gn = paths; gn; gn = gnode_next(gn)) { 00584 char *fullpath; 00585 FILE *tmp; 00586 00587 fullpath = string_join(gnode_ptr(gn), "/", path, NULL); 00588 tmp = fopen(fullpath, "r"); 00589 if (tmp != NULL) { 00590 fclose(tmp); 00591 return fullpath; 00592 } 00593 else 00594 ckd_free(fullpath); 00595 } 00596 return NULL; 00597 } 00598 00599 jsgf_rule_t * 00600 jsgf_import_rule(jsgf_t *jsgf, char *name) 00601 { 00602 char *c, *path, *newpath; 00603 size_t namelen, packlen; 00604 void *val; 00605 jsgf_t *imp; 00606 int import_all; 00607 00608 /* Trim the leading and trailing <> */ 00609 namelen = strlen(name); 00610 path = ckd_malloc(namelen - 2 + 6); /* room for a trailing .gram */ 00611 strcpy(path, name + 1); 00612 /* Split off the first part of the name */ 00613 c = strrchr(path, '.'); 00614 if (c == NULL) { 00615 E_ERROR("Imported rule is not qualified: %s\n", name); 00616 ckd_free(path); 00617 return NULL; 00618 } 00619 packlen = c - path; 00620 *c = '\0'; 00621 00622 /* Look for import foo.* */ 00623 import_all = (strlen(name) > 2 && 0 == strcmp(name + namelen - 3, ".*>")); 00624 00625 /* Construct a filename. */ 00626 for (c = path; *c; ++c) 00627 if (*c == '.') *c = '/'; 00628 strcat(path, ".gram"); 00629 newpath = path_list_search(jsgf->searchpath, path); 00630 ckd_free(path); 00631 if (newpath == NULL) 00632 return NULL; 00633 00634 path = newpath; 00635 E_INFO("Importing %s from %s to %s\n", name, path, jsgf->name); 00636 00637 /* FIXME: Also, we need to make sure that path is fully qualified 00638 * here, by adding any prefixes from jsgf->name to it. */ 00639 /* See if we have parsed it already */ 00640 if (hash_table_lookup(jsgf->imports, path, &val) == 0) { 00641 E_INFO("Already imported %s\n", path); 00642 imp = val; 00643 ckd_free(path); 00644 } 00645 else { 00646 /* If not, parse it. */ 00647 imp = jsgf_parse_file(path, jsgf); 00648 val = hash_table_enter(jsgf->imports, path, imp); 00649 if (val != (void *)imp) { 00650 E_WARN("Multiply imported file: %s\n", path); 00651 } 00652 } 00653 if (imp != NULL) { 00654 hash_iter_t *itor; 00655 /* Look for public rules matching rulename. */ 00656 for (itor = hash_table_iter(imp->rules); itor; 00657 itor = hash_table_iter_next(itor)) { 00658 hash_entry_t *he = itor->ent; 00659 jsgf_rule_t *rule = hash_entry_val(he); 00660 int rule_matches; 00661 char *rule_name = importname2rulename(name); 00662 00663 if (import_all) { 00664 /* Match package name (symbol table is shared) */ 00665 rule_matches = !strncmp(rule_name, rule->name, packlen + 1); 00666 } 00667 else { 00668 /* Exact match */ 00669 rule_matches = !strcmp(rule_name, rule->name); 00670 } 00671 ckd_free(rule_name); 00672 if (rule->public && rule_matches) { 00673 void *val; 00674 char *newname; 00675 00676 /* Link this rule into the current namespace. */ 00677 c = strrchr(rule->name, '.'); 00678 assert(c != NULL); 00679 newname = jsgf_fullname(jsgf, c); 00680 00681 E_INFO("Imported %s\n", newname); 00682 val = hash_table_enter(jsgf->rules, newname, 00683 jsgf_rule_retain(rule)); 00684 if (val != (void *)rule) { 00685 E_WARN("Multiply defined symbol: %s\n", newname); 00686 } 00687 if (!import_all) { 00688 hash_table_iter_free(itor); 00689 return rule; 00690 } 00691 } 00692 } 00693 } 00694 00695 return NULL; 00696 } 00697 00698 jsgf_t * 00699 jsgf_parse_file(const char *filename, jsgf_t *parent) 00700 { 00701 yyscan_t yyscanner; 00702 jsgf_t *jsgf; 00703 int yyrv; 00704 FILE *in = NULL; 00705 00706 yylex_init(&yyscanner); 00707 if (filename == NULL) { 00708 yyset_in(stdin, yyscanner); 00709 } 00710 else { 00711 in = fopen(filename, "r"); 00712 if (in == NULL) { 00713 fprintf(stderr, "Failed to open %s for parsing: %s\n", 00714 filename, strerror(errno)); 00715 return NULL; 00716 } 00717 yyset_in(in, yyscanner); 00718 } 00719 00720 jsgf = jsgf_grammar_new(parent); 00721 yyrv = yyparse(yyscanner, jsgf); 00722 if (yyrv != 0) { 00723 fprintf(stderr, "JSGF parse of %s failed\n", 00724 filename ? filename : "(stdin)"); 00725 jsgf_grammar_free(jsgf); 00726 yylex_destroy(yyscanner); 00727 return NULL; 00728 } 00729 if (in) 00730 fclose(in); 00731 yylex_destroy(yyscanner); 00732 00733 return jsgf; 00734 }