SphinxBase  0.6
jsgf.c
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* ====================================================================
3  * Copyright (c) 2007 Carnegie Mellon University. All rights
4  * reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * This work was supported in part by funding from the Defense Advanced
19  * Research Projects Agency and the National Science Foundation of the
20  * United States of America, and the CMU Sphinx Speech Consortium.
21  *
22  * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND
23  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY
26  * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * ====================================================================
35  *
36  */
37 
38 #include <string.h>
39 #include <assert.h>
40 
41 #include "sphinxbase/ckd_alloc.h"
42 #include "sphinxbase/strfuncs.h"
43 #include "sphinxbase/hash_table.h"
44 #include "sphinxbase/err.h"
45 
46 #include "jsgf_internal.h"
47 #include "jsgf_parser.h"
48 #include "jsgf_scanner.h"
49 
50 int yyparse(void *scanner, jsgf_t *jsgf);
51 
60 jsgf_atom_new(char *name, float weight)
61 {
62  jsgf_atom_t *atom;
63 
64  atom = ckd_calloc(1, sizeof(*atom));
65  atom->name = ckd_salloc(name);
66  atom->weight = weight;
67  return atom;
68 }
69 
70 int
71 jsgf_atom_free(jsgf_atom_t *atom)
72 {
73  if (atom == NULL)
74  return 0;
75  ckd_free(atom->name);
76  ckd_free(atom);
77  return 0;
78 }
79 
80 jsgf_t *
82 {
83  jsgf_t *grammar;
84 
85  grammar = ckd_calloc(1, sizeof(*grammar));
86  /* If this is an imported/subgrammar, then we will share a global
87  * namespace with the parent grammar. */
88  if (parent) {
89  grammar->rules = parent->rules;
90  grammar->imports = parent->imports;
91  grammar->searchpath = parent->searchpath;
92  grammar->parent = parent;
93  }
94  else {
95  char *jsgf_path;
96 
97  grammar->rules = hash_table_new(64, 0);
98  grammar->imports = hash_table_new(16, 0);
99 
100  /* Silvio Moioli: no getenv() in Windows CE */
101  #if !defined(_WIN32_WCE)
102  if ((jsgf_path = getenv("JSGF_PATH")) != NULL) {
103  char *word, *c;
104 
105  /* FIXME: This should be a function in libsphinxbase. */
106  /* FIXME: Also nextword() is totally useless... */
107  word = jsgf_path = ckd_salloc(jsgf_path);
108  while ((c = strchr(word, ':'))) {
109  *c = '\0';
110  grammar->searchpath = glist_add_ptr(grammar->searchpath, word);
111  word = c + 1;
112  }
113  grammar->searchpath = glist_add_ptr(grammar->searchpath, word);
114  grammar->searchpath = glist_reverse(grammar->searchpath);
115  }
116  else {
117  /* Default to current directory. */
118  grammar->searchpath = glist_add_ptr(grammar->searchpath, ckd_salloc("."));
119  }
120  #endif
121  }
122 
123  return grammar;
124 }
125 
126 void
128 {
129  /* FIXME: Probably should just use refcounting instead. */
130  if (jsgf->parent == NULL) {
131  hash_iter_t *itor;
132  gnode_t *gn;
133 
134  for (itor = hash_table_iter(jsgf->rules); itor;
135  itor = hash_table_iter_next(itor)) {
136  ckd_free((char *)itor->ent->key);
137  jsgf_rule_free((jsgf_rule_t *)itor->ent->val);
138  }
139  hash_table_free(jsgf->rules);
140  for (itor = hash_table_iter(jsgf->imports); itor;
141  itor = hash_table_iter_next(itor)) {
142  ckd_free((char *)itor->ent->key);
143  jsgf_grammar_free((jsgf_t *)itor->ent->val);
144  }
145  hash_table_free(jsgf->imports);
146  for (gn = jsgf->searchpath; gn; gn = gnode_next(gn))
147  ckd_free(gnode_ptr(gn));
148  glist_free(jsgf->searchpath);
149  for (gn = jsgf->links; gn; gn = gnode_next(gn))
150  ckd_free(gnode_ptr(gn));
151  glist_free(jsgf->links);
152  }
153  ckd_free(jsgf->name);
154  ckd_free(jsgf->version);
155  ckd_free(jsgf->charset);
156  ckd_free(jsgf->locale);
157  ckd_free(jsgf);
158 }
159 
160 static void
161 jsgf_rhs_free(jsgf_rhs_t *rhs)
162 {
163  gnode_t *gn;
164 
165  if (rhs == NULL)
166  return;
167 
168  jsgf_rhs_free(rhs->alt);
169  for (gn = rhs->atoms; gn; gn = gnode_next(gn))
170  jsgf_atom_free(gnode_ptr(gn));
171  glist_free(rhs->atoms);
172  ckd_free(rhs);
173 }
174 
175 jsgf_atom_t *
176 jsgf_kleene_new(jsgf_t *jsgf, jsgf_atom_t *atom, int plus)
177 {
178  jsgf_rule_t *rule;
179  jsgf_atom_t *rule_atom;
180  jsgf_rhs_t *rhs;
181 
182  /* Generate an "internal" rule of the form (<NULL> | <name> <g0006>) */
183  /* Or if plus is true, (<name> | <name> <g0006>) */
184  rhs = ckd_calloc(1, sizeof(*rhs));
185  if (plus)
186  rhs->atoms = glist_add_ptr(NULL, jsgf_atom_new(atom->name, 1.0));
187  else
188  rhs->atoms = glist_add_ptr(NULL, jsgf_atom_new("<NULL>", 1.0));
189  rule = jsgf_define_rule(jsgf, NULL, rhs, 0);
190  rule_atom = jsgf_atom_new(rule->name, 1.0);
191  rhs = ckd_calloc(1, sizeof(*rhs));
192  rhs->atoms = glist_add_ptr(NULL, rule_atom);
193  rhs->atoms = glist_add_ptr(rhs->atoms, atom);
194  rule->rhs->alt = rhs;
195 
196  return jsgf_atom_new(rule->name, 1.0);
197 }
198 
199 jsgf_rule_t *
200 jsgf_optional_new(jsgf_t *jsgf, jsgf_rhs_t *exp)
201 {
202  jsgf_rhs_t *rhs = ckd_calloc(1, sizeof(*rhs));
203  jsgf_atom_t *atom = jsgf_atom_new("<NULL>", 1.0);
204  rhs->alt = exp;
205  rhs->atoms = glist_add_ptr(NULL, atom);
206  return jsgf_define_rule(jsgf, NULL, rhs, 0);
207 }
208 
209 void
210 jsgf_add_link(jsgf_t *grammar, jsgf_atom_t *atom, int from, int to)
211 {
212  jsgf_link_t *link;
213 
214  link = ckd_calloc(1, sizeof(*link));
215  link->from = from;
216  link->to = to;
217  link->atom = atom;
218  grammar->links = glist_add_ptr(grammar->links, link);
219 }
220 
221 static char *
222 extract_grammar_name(char *rule_name)
223 {
224  char* dot_pos;
225  char* grammar_name = ckd_salloc(rule_name+1);
226  if ((dot_pos = strrchr(grammar_name + 1, '.')) == NULL) {
227  ckd_free(grammar_name);
228  return NULL;
229  }
230  *dot_pos='\0';
231  return grammar_name;
232 }
233 
234 char const *
236 {
237  return jsgf->name;
238 }
239 
240 static char *
241 jsgf_fullname(jsgf_t *jsgf, const char *name)
242 {
243  char *fullname;
244 
245  /* Check if it is already qualified */
246  if (strchr(name + 1, '.'))
247  return ckd_salloc(name);
248 
249  /* Skip leading < in name */
250  fullname = ckd_malloc(strlen(jsgf->name) + strlen(name) + 4);
251  sprintf(fullname, "<%s.%s", jsgf->name, name + 1);
252  return fullname;
253 }
254 
255 static char *
256 jsgf_fullname_from_rule(jsgf_rule_t *rule, const char *name)
257 {
258  char *fullname, *grammar_name;
259 
260  /* Check if it is already qualified */
261  if (strchr(name + 1, '.'))
262  return ckd_salloc(name);
263 
264  /* Skip leading < in name */
265  if ((grammar_name = extract_grammar_name(rule->name)) == NULL)
266  return ckd_salloc(name);
267  fullname = ckd_malloc(strlen(grammar_name) + strlen(name) + 4);
268  sprintf(fullname, "<%s.%s", grammar_name, name + 1);
269  ckd_free(grammar_name);
270 
271  return fullname;
272 }
273 
274 /* Extract as rulename everything after the secondlast dot, if existent.
275  * Because everything before the secondlast dot is the path-specification. */
276 static char *
277 importname2rulename(char *importname)
278 {
279  char *rulename = ckd_salloc(importname);
280  char *last_dotpos;
281  char *secondlast_dotpos;
282 
283  if ((last_dotpos = strrchr(rulename+1, '.')) != NULL) {
284  *last_dotpos='\0';
285  if ((secondlast_dotpos = strrchr(rulename+1, '.')) != NULL) {
286  *last_dotpos='.';
287  *secondlast_dotpos='<';
288  secondlast_dotpos = ckd_salloc(secondlast_dotpos);
289  ckd_free(rulename);
290  return secondlast_dotpos;
291  }
292  else {
293  *last_dotpos='.';
294  return rulename;
295  }
296  }
297  else {
298  return rulename;
299  }
300 }
301 
302 static int expand_rule(jsgf_t *grammar, jsgf_rule_t *rule);
303 static int
304 expand_rhs(jsgf_t *grammar, jsgf_rule_t *rule, jsgf_rhs_t *rhs)
305 {
306  gnode_t *gn;
307  int lastnode;
308 
309  /* Last node expanded in this sequence. */
310  lastnode = rule->entry;
311 
312  /* Iterate over atoms in rhs and generate links/nodes */
313  for (gn = rhs->atoms; gn; gn = gnode_next(gn)) {
314  jsgf_atom_t *atom = gnode_ptr(gn);
315  if (jsgf_atom_is_rule(atom)) {
316  jsgf_rule_t *subrule;
317  char *fullname;
318  gnode_t *subnode;
319  void *val;
320 
321  /* Special case for <NULL> and <VOID> pseudo-rules */
322  if (0 == strcmp(atom->name, "<NULL>")) {
323  /* Emit a NULL transition */
324  jsgf_add_link(grammar, atom,
325  lastnode, grammar->nstate);
326  lastnode = grammar->nstate;
327  ++grammar->nstate;
328  continue;
329  }
330  else if (0 == strcmp(atom->name, "<VOID>")) {
331  /* Make this entire RHS unspeakable */
332  return -1;
333  }
334 
335  fullname = jsgf_fullname_from_rule(rule, atom->name);
336  if (hash_table_lookup(grammar->rules, fullname, &val) == -1) {
337  E_ERROR("Undefined rule in RHS: %s\n", fullname);
338  ckd_free(fullname);
339  return -1;
340  }
341  ckd_free(fullname);
342  subrule = val;
343  /* Look for this in the stack of expanded rules */
344  for (subnode = grammar->rulestack; subnode; subnode = gnode_next(subnode))
345  if (gnode_ptr(subnode) == (void *)subrule)
346  break;
347  if (subnode != NULL) {
348  /* Allow right-recursion only. */
349  if (gnode_next(gn) != NULL) {
350  E_ERROR("Only right-recursion is permitted (in %s.%s)\n",
351  grammar->name, rule->name);
352  return -1;
353  }
354  /* Add a link back to the beginning of this rule instance */
355  E_INFO("Right recursion %s %d => %d\n", atom->name, lastnode, subrule->entry);
356  jsgf_add_link(grammar, atom, lastnode, subrule->entry);
357  }
358  else {
359  /* Expand the subrule */
360  if (expand_rule(grammar, subrule) == -1)
361  return -1;
362  /* Add a link into the subrule. */
363  jsgf_add_link(grammar, atom,
364  lastnode, subrule->entry);
365  lastnode = subrule->exit;
366  }
367  }
368  else {
369  /* Add a link for this token and create a new exit node. */
370  jsgf_add_link(grammar, atom,
371  lastnode, grammar->nstate);
372  lastnode = grammar->nstate;
373  ++grammar->nstate;
374  }
375  }
376 
377  return lastnode;
378 }
379 
380 static int
381 expand_rule(jsgf_t *grammar, jsgf_rule_t *rule)
382 {
383  jsgf_rhs_t *rhs;
384  float norm;
385 
386  /* Push this rule onto the stack */
387  grammar->rulestack = glist_add_ptr(grammar->rulestack, rule);
388 
389  /* Normalize weights for all alternatives exiting rule->entry */
390  norm = 0;
391  for (rhs = rule->rhs; rhs; rhs = rhs->alt) {
392  if (rhs->atoms) {
393  jsgf_atom_t *atom = gnode_ptr(rhs->atoms);
394  norm += atom->weight;
395  }
396  }
397 
398  rule->entry = grammar->nstate++;
399  rule->exit = grammar->nstate++;
400  if (norm == 0) norm = 1;
401  for (rhs = rule->rhs; rhs; rhs = rhs->alt) {
402  int lastnode;
403 
404  if (rhs->atoms) {
405  jsgf_atom_t *atom = gnode_ptr(rhs->atoms);
406  atom->weight /= norm;
407  }
408  lastnode = expand_rhs(grammar, rule, rhs);
409  if (lastnode == -1) {
410  return -1;
411  }
412  else {
413  jsgf_add_link(grammar, NULL, lastnode, rule->exit);
414  }
415  }
416 
417  /* Pop this rule from the rule stack */
418  grammar->rulestack = gnode_free(grammar->rulestack, NULL);
419  return rule->exit;
420 }
421 
424 {
425  return hash_table_iter(grammar->rules);
426 }
427 
428 jsgf_rule_t *
429 jsgf_get_rule(jsgf_t *grammar, char const *name)
430 {
431  void *val;
432 
433  if (hash_table_lookup(grammar->rules, name, &val) < 0)
434  return NULL;
435  return (jsgf_rule_t *)val;
436 }
437 
438 char const *
440 {
441  return rule->name;
442 }
443 
444 int
446 {
447  return rule->public;
448 }
449 
450 static fsg_model_t *
451 jsgf_build_fsg_internal(jsgf_t *grammar, jsgf_rule_t *rule,
452  logmath_t *lmath, float32 lw, int do_closure)
453 {
454  fsg_model_t *fsg;
455  glist_t nulls;
456  gnode_t *gn;
457 
458  /* Clear previous links */
459  for (gn = grammar->links; gn; gn = gnode_next(gn)) {
460  ckd_free(gnode_ptr(gn));
461  }
462  glist_free(grammar->links);
463  grammar->links = NULL;
464  rule->entry = rule->exit = 0;
465  grammar->nstate = 0;
466  expand_rule(grammar, rule);
467 
468  fsg = fsg_model_init(rule->name, lmath, lw, grammar->nstate);
469  fsg->start_state = rule->entry;
470  fsg->final_state = rule->exit;
471  grammar->links = glist_reverse(grammar->links);
472  for (gn = grammar->links; gn; gn = gnode_next(gn)) {
473  jsgf_link_t *link = gnode_ptr(gn);
474 
475  if (link->atom) {
476  if (jsgf_atom_is_rule(link->atom)) {
477  fsg_model_null_trans_add(fsg, link->from, link->to,
478  logmath_log(lmath, link->atom->weight));
479  }
480  else {
481  int wid = fsg_model_word_add(fsg, link->atom->name);
482  fsg_model_trans_add(fsg, link->from, link->to,
483  logmath_log(lmath, link->atom->weight), wid);
484  }
485  }
486  else {
487  fsg_model_null_trans_add(fsg, link->from, link->to, 0);
488  }
489  }
490  if (do_closure) {
491  nulls = fsg_model_null_trans_closure(fsg, NULL);
492  glist_free(nulls);
493  }
494 
495  return fsg;
496 }
497 
498 fsg_model_t *
500  logmath_t *lmath, float32 lw)
501 {
502  return jsgf_build_fsg_internal(grammar, rule, lmath, lw, TRUE);
503 }
504 
505 fsg_model_t *
507  logmath_t *lmath, float32 lw)
508 {
509  return jsgf_build_fsg_internal(grammar, rule, lmath, lw, FALSE);
510 }
511 
512 fsg_model_t *
513 jsgf_read_file(const char *file, logmath_t * lmath, float32 lw)
514 {
515  fsg_model_t *fsg;
516  jsgf_rule_t *rule;
517  jsgf_t *jsgf;
518  jsgf_rule_iter_t *itor;
519 
520  if ((jsgf = jsgf_parse_file(file, NULL)) == NULL) {
521  E_ERROR("Error parsing file: %s\n", file);
522  return NULL;
523  }
524 
525  rule = NULL;
526  for (itor = jsgf_rule_iter(jsgf); itor;
527  itor = jsgf_rule_iter_next(itor)) {
528  rule = jsgf_rule_iter_rule(itor);
529  if (jsgf_rule_public(rule)) {
530  jsgf_rule_iter_free(itor);
531  break;
532  }
533  }
534  if (rule == NULL) {
535  E_ERROR("No public rules found in %s\n", file);
536  return NULL;
537  }
538  fsg = jsgf_build_fsg(jsgf, rule, lmath, lw);
539  jsgf_grammar_free(jsgf);
540  return fsg;
541 }
542 
543 int
544 jsgf_write_fsg(jsgf_t *grammar, jsgf_rule_t *rule, FILE *outfh)
545 {
546  fsg_model_t *fsg;
547  logmath_t *lmath = logmath_init(1.0001, 0, 0);
548 
549  if ((fsg = jsgf_build_fsg_raw(grammar, rule, lmath, 1.0)) == NULL)
550  goto error_out;
551 
552  fsg_model_write(fsg, outfh);
553  logmath_free(lmath);
554  return 0;
555 
556 error_out:
557  logmath_free(lmath);
558  return -1;
559 }
560 jsgf_rule_t *
561 jsgf_define_rule(jsgf_t *jsgf, char *name, jsgf_rhs_t *rhs, int public)
562 {
563  jsgf_rule_t *rule;
564  void *val;
565 
566  if (name == NULL) {
567  name = ckd_malloc(strlen(jsgf->name) + 16);
568  sprintf(name, "<%s.g%05d>", jsgf->name, hash_table_inuse(jsgf->rules));
569  }
570  else {
571  char *newname;
572 
573  newname = jsgf_fullname(jsgf, name);
574  name = newname;
575  }
576 
577  rule = ckd_calloc(1, sizeof(*rule));
578  rule->refcnt = 1;
579  rule->name = ckd_salloc(name);
580  rule->rhs = rhs;
581  rule->public = public;
582 
583  E_INFO("Defined rule: %s%s\n",
584  rule->public ? "PUBLIC " : "",
585  rule->name);
586  val = hash_table_enter(jsgf->rules, name, rule);
587  if (val != (void *)rule) {
588  E_WARN("Multiply defined symbol: %s\n", name);
589  }
590  return rule;
591 }
592 
593 jsgf_rule_t *
594 jsgf_rule_retain(jsgf_rule_t *rule)
595 {
596  ++rule->refcnt;
597  return rule;
598 }
599 
600 int
601 jsgf_rule_free(jsgf_rule_t *rule)
602 {
603  if (rule == NULL)
604  return 0;
605  if (--rule->refcnt > 0)
606  return rule->refcnt;
607  jsgf_rhs_free(rule->rhs);
608  ckd_free(rule->name);
609  ckd_free(rule);
610  return 0;
611 }
612 
613 
614 /* FIXME: This should go in libsphinxutil */
615 static char *
616 path_list_search(glist_t paths, char *path)
617 {
618  gnode_t *gn;
619 
620  for (gn = paths; gn; gn = gnode_next(gn)) {
621  char *fullpath;
622  FILE *tmp;
623 
624  fullpath = string_join(gnode_ptr(gn), "/", path, NULL);
625  tmp = fopen(fullpath, "r");
626  if (tmp != NULL) {
627  fclose(tmp);
628  return fullpath;
629  }
630  else
631  ckd_free(fullpath);
632  }
633  return NULL;
634 }
635 
636 jsgf_rule_t *
637 jsgf_import_rule(jsgf_t *jsgf, char *name)
638 {
639  char *c, *path, *newpath;
640  size_t namelen, packlen;
641  void *val;
642  jsgf_t *imp;
643  int import_all;
644 
645  /* Trim the leading and trailing <> */
646  namelen = strlen(name);
647  path = ckd_malloc(namelen - 2 + 6); /* room for a trailing .gram */
648  strcpy(path, name + 1);
649  /* Split off the first part of the name */
650  c = strrchr(path, '.');
651  if (c == NULL) {
652  E_ERROR("Imported rule is not qualified: %s\n", name);
653  ckd_free(path);
654  return NULL;
655  }
656  packlen = c - path;
657  *c = '\0';
658 
659  /* Look for import foo.* */
660  import_all = (strlen(name) > 2 && 0 == strcmp(name + namelen - 3, ".*>"));
661 
662  /* Construct a filename. */
663  for (c = path; *c; ++c)
664  if (*c == '.') *c = '/';
665  strcat(path, ".gram");
666  newpath = path_list_search(jsgf->searchpath, path);
667  ckd_free(path);
668  if (newpath == NULL)
669  return NULL;
670 
671  path = newpath;
672  E_INFO("Importing %s from %s to %s\n", name, path, jsgf->name);
673 
674  /* FIXME: Also, we need to make sure that path is fully qualified
675  * here, by adding any prefixes from jsgf->name to it. */
676  /* See if we have parsed it already */
677  if (hash_table_lookup(jsgf->imports, path, &val) == 0) {
678  E_INFO("Already imported %s\n", path);
679  imp = val;
680  ckd_free(path);
681  }
682  else {
683  /* If not, parse it. */
684  imp = jsgf_parse_file(path, jsgf);
685  val = hash_table_enter(jsgf->imports, path, imp);
686  if (val != (void *)imp) {
687  E_WARN("Multiply imported file: %s\n", path);
688  }
689  }
690  if (imp != NULL) {
691  hash_iter_t *itor;
692  /* Look for public rules matching rulename. */
693  for (itor = hash_table_iter(imp->rules); itor;
694  itor = hash_table_iter_next(itor)) {
695  hash_entry_t *he = itor->ent;
696  jsgf_rule_t *rule = hash_entry_val(he);
697  int rule_matches;
698  char *rule_name = importname2rulename(name);
699 
700  if (import_all) {
701  /* Match package name (symbol table is shared) */
702  rule_matches = !strncmp(rule_name, rule->name, packlen + 1);
703  }
704  else {
705  /* Exact match */
706  rule_matches = !strcmp(rule_name, rule->name);
707  }
708  ckd_free(rule_name);
709  if (rule->public && rule_matches) {
710  void *val;
711  char *newname;
712 
713  /* Link this rule into the current namespace. */
714  c = strrchr(rule->name, '.');
715  assert(c != NULL);
716  newname = jsgf_fullname(jsgf, c);
717 
718  E_INFO("Imported %s\n", newname);
719  val = hash_table_enter(jsgf->rules, newname,
720  jsgf_rule_retain(rule));
721  if (val != (void *)rule) {
722  E_WARN("Multiply defined symbol: %s\n", newname);
723  }
724  if (!import_all) {
725  hash_table_iter_free(itor);
726  return rule;
727  }
728  }
729  }
730  }
731 
732  return NULL;
733 }
734 
735 jsgf_t *
736 jsgf_parse_file(const char *filename, jsgf_t *parent)
737 {
738  yyscan_t yyscanner;
739  jsgf_t *jsgf;
740  int yyrv;
741  FILE *in = NULL;
742 
743  yylex_init(&yyscanner);
744  if (filename == NULL) {
745  yyset_in(stdin, yyscanner);
746  }
747  else {
748  in = fopen(filename, "r");
749  if (in == NULL) {
750  E_ERROR_SYSTEM("Failed to open %s for parsing", filename);
751  return NULL;
752  }
753  yyset_in(in, yyscanner);
754  }
755 
756  jsgf = jsgf_grammar_new(parent);
757  yyrv = yyparse(yyscanner, jsgf);
758  if (yyrv != 0) {
759  E_ERROR("Failed to parse JSGF grammar from '%s'\n", filename ? filename : "(stdin)");
760  jsgf_grammar_free(jsgf);
761  yylex_destroy(yyscanner);
762  return NULL;
763  }
764  if (in)
765  fclose(in);
766  yylex_destroy(yyscanner);
767 
768  return jsgf;
769 }
jsgf_rule_t * jsgf_get_rule(jsgf_t *grammar, char const *name)
Get a rule by name from a grammar.
Definition: jsgf.c:429
int32 start_state
Must be in the range [0..n_state-1].
Definition: fsg_model.h:101
jsgf_t * jsgf_parse_file(const char *filename, jsgf_t *parent)
Parse a JSGF grammar from a file.
Definition: jsgf.c:736
Miscellaneous useful string functions.
Internal definitions for JSGF grammar compiler.
hash_entry_t * ent
Current entry in that table.
Definition: hash_table.h:170
SPHINXBASE_EXPORT int32 hash_table_lookup(hash_table_t *h, const char *key, void **val)
Look up a key in a hash table and optionally return the associated value.
Definition: hash_table.c:309
#define jsgf_rule_iter_next(itor)
Advance an iterator to the next rule in the grammar.
Definition: jsgf.h:112
int32 final_state
Must be in the range [0..n_state-1].
Definition: fsg_model.h:102
#define ckd_calloc(n, sz)
Macros to simplify the use of above functions.
Definition: ckd_alloc.h:248
#define E_INFO
Print logging information to standard error stream.
Definition: err.h:147
Sphinx's memory allocation/deallocation routines.
glist_t links
Generated FSG links.
Definition: jsgf_internal.h:82
SPHINXBASE_EXPORT void hash_table_iter_free(hash_iter_t *itor)
Delete an unfinished iterator.
Definition: hash_table.c:689
SPHINXBASE_EXPORT int logmath_log(logmath_t *lmath, float64 p)
Convert linear floating point number to integer log in base B.
Definition: logmath.c:447
A node in a generic list.
Definition: glist.h:100
int jsgf_rule_public(jsgf_rule_t *rule)
Test if a rule is public or not.
Definition: jsgf.c:445
SPHINXBASE_EXPORT hash_iter_t * hash_table_iter(hash_table_t *h)
Start iterating over key-value pairs in a hash table.
Definition: hash_table.c:653
SPHINXBASE_EXPORT int logmath_free(logmath_t *lmath)
Free a log table.
Definition: logmath.c:342
#define ckd_salloc(ptr)
Macro for ckd_salloc
Definition: ckd_alloc.h:264
fsg_model_t * jsgf_read_file(const char *file, logmath_t *lmath, float32 lw)
Read JSGF from file and return FSG object from it.
Definition: jsgf.c:513
int entry
Entry state for current instance of this rule.
Definition: jsgf_internal.h:92
#define hash_entry_val(e)
Access macros.
Definition: hash_table.h:175
SPHINXBASE_EXPORT hash_table_t * hash_table_new(int32 size, int32 casearg)
Allocate a new hash table for a given expected size.
Definition: hash_table.c:158
glist_t searchpath
List of directories to search for grammars.
Definition: jsgf_internal.h:78
char const * jsgf_grammar_name(jsgf_t *jsgf)
Get the grammar name from the file.
Definition: jsgf.c:235
SPHINXBASE_EXPORT void ckd_free(void *ptr)
Test and free a 1-D array.
Definition: ckd_alloc.c:241
SPHINXBASE_EXPORT glist_t glist_add_ptr(glist_t g, void *ptr)
Create and prepend a new list node, with the given user-defined data, at the HEAD of the given generi...
Definition: glist.c:74
#define E_WARN
Print warning information to standard error stream.
Definition: err.h:164
char * name
Grammar name.
Definition: jsgf_internal.h:73
jsgf_rhs_t * alt
Linked list of alternates.
Definition: jsgf_internal.h:98
SPHINXBASE_EXPORT logmath_t * logmath_init(float64 base, int shift, int use_table)
Initialize a log math computation table.
Definition: logmath.c:62
glist_t rulestack
Stack of currently expanded rules.
Definition: jsgf_internal.h:83
jsgf_rule_iter_t * jsgf_rule_iter(jsgf_t *grammar)
Get an iterator over all rules in a grammar.
Definition: jsgf.c:423
SPHINXBASE_EXPORT void hash_table_free(hash_table_t *h)
Free the specified hash table; the caller is responsible for freeing the key strings pointed to by th...
Definition: hash_table.c:695
int exit
Exit state for current instance of this rule.
Definition: jsgf_internal.h:93
int public
Is this rule marked 'public'?
Definition: jsgf_internal.h:89
int nstate
Number of generated states.
Definition: jsgf_internal.h:81
A note by ARCHAN at 20050510: Technically what we use is so-called "hash table with buckets" which is...
Definition: hash_table.h:149
char * charset
JSGF charset (default UTF-8)
Definition: jsgf_internal.h:71
SPHINXBASE_EXPORT glist_t glist_reverse(glist_t g)
Reverse the order of the given glist.
Definition: glist.c:169
jsgf_rhs_t * rhs
Expansion.
Definition: jsgf_internal.h:90
void jsgf_grammar_free(jsgf_t *jsgf)
Free a JSGF grammar.
Definition: jsgf.c:127
SPHINXBASE_EXPORT void glist_free(glist_t g)
Free the given generic list; user-defined data contained within is not automatically freed...
Definition: glist.c:133
SPHINXBASE_EXPORT gnode_t * gnode_free(gnode_t *gn, gnode_t *pred)
Free the given node, gn, of a glist, pred being its predecessor in the list.
Definition: glist.c:257
#define gnode_ptr(g)
Head of a list of gnodes.
Definition: glist.h:109
char * name
Rule name (NULL for an alternation/grouping)
Definition: jsgf_internal.h:88
Implementation of logging routines.
jsgf_t * jsgf_grammar_new(jsgf_t *parent)
Create a new JSGF grammar.
Definition: jsgf.c:81
int refcnt
Reference count.
Definition: jsgf_internal.h:87
SPHINXBASE_EXPORT void * hash_table_enter(hash_table_t *h, const char *key, void *val)
Try to add a new entry with given key and associated value to hash table h.
Definition: hash_table.c:508
#define jsgf_rule_iter_rule(itor)
Get the current rule in a rule iterator.
Definition: jsgf.h:117
fsg_model_t * jsgf_build_fsg_raw(jsgf_t *grammar, jsgf_rule_t *rule, logmath_t *lmath, float32 lw)
Build a Sphinx FSG object from a JSGF rule.
Definition: jsgf.c:506
char * version
JSGF version (from header)
Definition: jsgf_internal.h:70
SPHINXBASE_EXPORT hash_iter_t * hash_table_iter_next(hash_iter_t *itor)
Get the next key-value pair in iteration.
Definition: hash_table.c:663
#define ckd_malloc(sz)
Macro for ckd_malloc
Definition: ckd_alloc.h:253
glist_t atoms
Sequence of items.
Definition: jsgf_internal.h:97
SPHINXBASE_EXPORT char * string_join(const char *base,...)
Concatenate a NULL-terminated argument list of strings, returning a newly allocated string...
Definition: strfuncs.c:62
fsg_model_t * jsgf_build_fsg(jsgf_t *grammar, jsgf_rule_t *rule, logmath_t *lmath, float32 lw)
Build a Sphinx FSG object from a JSGF rule.
Definition: jsgf.c:499
jsgf_t * parent
Parent grammar (if this is an imported one)
Definition: jsgf_internal.h:77
char * name
Rule or token name.
#define E_ERROR
Print error message to standard error stream.
Definition: err.h:169
#define jsgf_rule_iter_free(itor)
Free a rule iterator (if the end hasn't been reached).
Definition: jsgf.h:122
hash_table_t * rules
Defined or imported rules in this grammar.
Definition: jsgf_internal.h:75
Hash table implementation.
char * locale
JSGF locale (default C)
Definition: jsgf_internal.h:72
void * val
Key-length; the key string does not have to be a C-style NULL terminated string; it can have arbitrar...
Definition: hash_table.h:155
Word level FSG definition.
Definition: fsg_model.h:91
int jsgf_write_fsg(jsgf_t *grammar, jsgf_rule_t *rule, FILE *outfh)
Convert a JSGF rule to Sphinx FSG text form.
Definition: jsgf.c:544
#define E_ERROR_SYSTEM
Print error text; Call perror("");.
Definition: err.h:142
float weight
Weight (default 1)
hash_table_t * imports
Pointers to imported grammars.
Definition: jsgf_internal.h:76
char const * jsgf_rule_name(jsgf_rule_t *rule)
Get the rule name from a rule.
Definition: jsgf.c:439