SphinxBase 0.6

src/libsphinxbase/lm/jsgf.c

Go to the documentation of this file.
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 }