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