1/*---------------------------------------------------------------------------*
2 *  SR_GrammarImpl.c  *
3 *                                                                           *
4 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
5 *                                                                           *
6 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
7 *  you may not use this file except in compliance with the License.         *
8 *                                                                           *
9 *  You may obtain a copy of the License at                                  *
10 *      http://www.apache.org/licenses/LICENSE-2.0                           *
11 *                                                                           *
12 *  Unless required by applicable law or agreed to in writing, software      *
13 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
15 *  See the License for the specific language governing permissions and      *
16 *  limitations under the License.                                           *
17 *                                                                           *
18 *---------------------------------------------------------------------------*/
19
20#include "ESR_Session.h"
21#include "SR_AcousticModels.h"
22#include "SR_AcousticModelsImpl.h"
23#include "SR_Grammar.h"
24#include "SR_GrammarImpl.h"
25#include "SR_SemanticGraphImpl.h"
26#include "SR_SemanticProcessorImpl.h"
27#include "SR_VocabularyImpl.h"
28#include "SR_NametagImpl.h"
29#include "passert.h"
30#include "plog.h"
31#include "pmemory.h"
32
33#define MTAG NULL
34
35ESR_ReturnCode SR_Grammar_Create(SR_Grammar** self)
36{
37  SR_GrammarImpl* impl;
38  ESR_ReturnCode rc;
39  ESR_BOOL exists;
40
41  impl = NEW(SR_GrammarImpl, MTAG);
42  if (impl == NULL)
43  {
44    PLogError(L("ESR_OUT_OF_MEMORY"));
45    return ESR_OUT_OF_MEMORY;
46  }
47
48  impl->Interface.addNametagToSlot = &SR_Grammar_AddNametagToSlot;
49  impl->Interface.addWordToSlot = &SR_Grammar_AddWordToSlot;
50  impl->Interface.checkParse = &SR_Grammar_CheckParse;
51  impl->Interface.compile = &SR_Grammar_Compile;
52  impl->Interface.destroy = &SR_Grammar_Destroy;
53  impl->Interface.getParameter = &SR_Grammar_GetParameter;
54  impl->Interface.getSize_tParameter = &SR_Grammar_GetSize_tParameter;
55  impl->Interface.resetAllSlots = &SR_Grammar_ResetAllSlots;
56  impl->Interface.save = &SR_Grammar_Save;
57  impl->Interface.setDispatchFunction = &SR_Grammar_SetDispatchFunction;
58  impl->Interface.setParameter = &SR_Grammar_SetParameter;
59  impl->Interface.setSize_tParameter = &SR_Grammar_SetSize_tParameter;
60  impl->Interface.setupRecognizer = &SR_Grammar_SetupRecognizer;
61  impl->Interface.unsetupRecognizer = &SR_Grammar_UnsetupRecognizer;
62  impl->Interface.setupVocabulary = &SR_Grammar_SetupVocabulary;
63  impl->syntax = NULL;
64  impl->recognizer = NULL;
65  impl->vocabulary = NULL;
66  impl->eventLog = NULL;
67  impl->logLevel = 0;
68  impl->isActivated = ESR_FALSE;
69
70  CHKLOG(rc, ESR_SessionTypeCreate(&impl->parameters));
71
72  /**
73   * Create the Semantic Graph and Processor to support CheckParse function
74   * (Since this function gets called by 'New', a semgraph and semproc are always
75   * created when the grammar is created)
76   */
77  rc = SR_SemanticGraphCreate(&impl->semgraph);
78  if (rc != ESR_SUCCESS)
79  {
80    PLogError(ESR_rc2str(rc));
81    goto CLEANUP;
82  }
83
84  rc = SR_SemanticProcessorCreate(&impl->semproc);
85  if (rc != ESR_SUCCESS)
86  {
87    PLogError(ESR_rc2str(rc));
88    goto CLEANUP;
89  }
90
91  CHKLOG(rc, ESR_SessionExists(&exists));
92  if (exists)
93  {
94    rc = ESR_SessionGetProperty(L("eventlog"), (void **)&impl->eventLog, TYPES_SR_EVENTLOG);
95    if (rc != ESR_NO_MATCH_ERROR && rc != ESR_SUCCESS)
96    {
97      PLogError(ESR_rc2str(rc));
98      goto CLEANUP;
99    }
100    rc = ESR_SessionGetSize_t(L("SREC.Recognizer.osi_log_level"), &impl->logLevel);
101    if (rc != ESR_NO_MATCH_ERROR && rc != ESR_SUCCESS)
102    {
103      PLogError(ESR_rc2str(rc));
104      goto CLEANUP;
105    }
106  }
107
108  *self = (SR_Grammar*) impl;
109  return ESR_SUCCESS;
110CLEANUP:
111  FREE(impl);
112  return rc;
113}
114
115ESR_ReturnCode SR_GrammarCreate(SR_Grammar** self)
116{
117  ESR_ReturnCode rc;
118
119  if (self == NULL)
120  {
121    PLogError(L("ESR_OUT_OF_MEMORY"));
122    return ESR_OUT_OF_MEMORY;
123  }
124  CHKLOG(rc, SR_Grammar_Create(self));
125  return ESR_SUCCESS;
126CLEANUP:
127  return rc;
128}
129
130ESR_ReturnCode SR_Grammar_Compile(SR_Grammar* self)
131{
132  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
133
134  if (!CA_CompileSyntax(impl->syntax))
135    return ESR_SUCCESS;
136  PLogError(L("ESR_FATAL_ERROR"));
137  return ESR_FATAL_ERROR;
138}
139
140/*
141 * The buffer for the pron is set very large because the real size is lost later on
142 * and all that is checked is whether a single phoneme will fit in the buffer. There
143 * is no concept of decrementing the bytes left. Because that code is one big monolithic
144 * piece of crap, it is very difficult to fix correctly. This kludge is appropriate
145 * because we don't have time to fix this correctly and there are probably dozens of
146 * similar problems in other parts of the code.
147 */
148
149ESR_ReturnCode SR_Grammar_AddWordToSlot(SR_Grammar* self, const LCHAR* slot, const LCHAR* word,
150																				const LCHAR* pronunciation, int weight, const LCHAR* tag)
151{
152  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
153  SR_Vocabulary* vocab;
154  LCHAR buffer[4096];
155  const LCHAR* input_pronunciation = pronunciation;
156  size_t len = 4096;
157  ESR_ReturnCode rc = ESR_SUCCESS, logrc;
158  int ca_rc = -99;
159
160  if ( slot != NULL )
161  {
162    if ( strlen ( slot ) >= MAX_STRING_LEN )
163    {
164    PLogError ( "SR_Grammar_AddWordToSlot slot : %s too long : Max %d", slot, MAX_STRING_LEN - 1 );
165    return ( ESR_INVALID_ARGUMENT );
166    }
167  }
168  if ( word != NULL )
169  {
170    if ( strlen ( word ) >= MAX_STRING_LEN )
171    {
172    PLogError ( "SR_Grammar_AddWordToSlot word : %s too long : Max %d", word, MAX_STRING_LEN - 1 );
173    return ( ESR_INVALID_ARGUMENT );
174    }
175  }
176  if ( pronunciation != NULL )
177  {
178    if ( strlen ( pronunciation ) >= MAX_STRING_LEN )
179    {
180    PLogError ( "SR_Grammar_AddWordToSlot pronunciation : %s too long : Max %d", pronunciation, MAX_STRING_LEN  - 1 );
181    return ( ESR_INVALID_ARGUMENT );
182    }
183  }
184  if ( tag != NULL )
185  {
186    if ( strlen ( tag ) >= MAX_STRING_LEN )
187    {
188    PLogError ( "SR_Grammar_AddWordToSlot tag : %s too long : Max %d", tag, MAX_STRING_LEN - 1 );
189    return ( ESR_INVALID_ARGUMENT );
190    }
191  }
192#if 0
193  /* make sure to have the latest arbdata to add words, however since
194     the arbdata is known to be constant for all acoustic models we
195	 have (ie for the different sample rates), then there is no need
196	 to do this, it slows down addition anyways */
197  CA_Arbdata* ca_arbdata;
198  SR_AcousticModels* models;
199  impl->recognizer->getModels( impl->recognizer, &models);
200  ca_arbdata = models->GetArbdata(models);
201  CA_AttachArbdataToSyntax( impl->syntax , ca_arbdata);
202#endif
203
204  /* yw HACK: Xanavi's application has bug. remove this check to let it work */
205  /* TODO: add this word to the semantic graph with associated script tag */
206  if (impl->vocabulary == NULL)
207  {
208    PLogError(L("ESR_INVALID_STATE"));
209    return ESR_INVALID_STATE;
210  }
211
212  /* tag may be NULL which means no script (no-op denoted by a simple semi-colon) */
213  if (!tag || !*tag)
214    tag = L(";");
215
216  if (!pronunciation || !(*pronunciation) || !LSTRCMP(pronunciation, L("NULL")))
217  {
218    vocab = (SR_Vocabulary*) impl->vocabulary;
219    CHKLOG(rc, vocab->getPronunciation(vocab, word, buffer, &len));
220    pronunciation = buffer;
221  }
222
223  /*
224   * 'buffer' contains a list of null-terminated pronunciations.
225   * Two consecutive null characters denote the end of the list.
226   *
227   * (In theory yes, but right now, only one pron is supported)
228   */
229  if (impl->eventLog != NULL)
230  {
231    CHKLOG(logrc, SR_EventLogTokenInt_BASIC(impl->eventLog, impl->logLevel, L("igrm"), (int)impl));
232    CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("SLOT"), slot));
233    CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("WORD"), word));
234    if (input_pronunciation)
235      CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("PRON"), pronunciation));
236    else
237      CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("GPRON"), pronunciation));
238    CHKLOG(logrc, SR_EventLogTokenInt_BASIC(impl->eventLog, impl->logLevel, L("WEIGHT"), weight));
239    CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("TAG"), tag));
240  }
241
242  /* add word to syntax first */
243  /*
244   *
245   * if word already exists and pron is same (i.e. as if no action)               returns FST_SUCCESS
246   * if word already exists and pron is different (e.g. read-rEd and read-red)    returns FST_SUCCESS
247   * if word does not exist and no duplicate pron exists (homonyms not supported) returns FST_SUCCESS
248   *                                                                                 else FST_FAILED
249   */
250  ca_rc = CA_AddWordToSyntax(impl->syntax, slot, word, pronunciation, weight);
251  switch (ca_rc)
252  {
253    case FST_SUCCESS:
254      /* successful, now add word & tag to semgraph */
255      CHKLOG(rc, impl->semgraph->addWordToSlot(impl->semgraph, slot, word, tag, 1));
256      break;
257    case FST_SUCCESS_ON_OLD_WORD:
258    case FST_FAILED_ON_HOMOGRAPH:
259      /* successful, now add word & tag to semgraph */
260      CHKLOG(rc, impl->semgraph->addWordToSlot(impl->semgraph, slot, word, tag, 0));
261      break;
262    case FST_FAILED_ON_MEMORY:
263      rc = ESR_OUT_OF_MEMORY;
264      PLogError(ESR_rc2str(rc));
265      goto CLEANUP;
266    case FST_FAILED_ON_INVALID_ARGS:
267      rc = ESR_INVALID_ARGUMENT;
268      PLogError(ESR_rc2str(rc));
269      goto CLEANUP;
270    case FST_FAILED_ON_HOMONYM:
271      rc = ESR_NOT_SUPPORTED;
272      /* remove this message from product */
273#if !defined(NDEBUG) || defined(_WIN32)
274      PLogError(L("%s: Homonym '%s' could not be added"), ESR_rc2str(rc), word);
275#endif
276      goto CLEANUP;
277    default:
278      rc = ESR_INVALID_STATE;
279      PLogError(L("%s|%s|%s|ca_rc=%d"), word, pronunciation, ESR_rc2str(rc), ca_rc);
280      goto CLEANUP;
281  }
282
283  if (impl->eventLog != NULL && (impl->logLevel & OSI_LOG_LEVEL_ADDWD))
284  {
285    CHKLOG(logrc, SR_EventLogTokenInt_BASIC(impl->eventLog, impl->logLevel, L("caRC"), (int) ca_rc));
286    CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("RSLT"), L("ok")));
287    CHKLOG(logrc, SR_EventLogEvent_BASIC(impl->eventLog, impl->logLevel, L("ESRaddWd")));
288  }
289  return rc;
290CLEANUP:
291  PLogError(L("failed on |%s|%s|%s|\n"), slot, word, pronunciation);
292  if (impl->eventLog != NULL && (impl->logLevel & OSI_LOG_LEVEL_ADDWD))
293  {
294    CHKLOG(logrc, SR_EventLogTokenInt_BASIC(impl->eventLog, impl->logLevel, L("caRC"), (int) ca_rc));
295    CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("RSLT"), L("err1")));
296    CHKLOG(logrc, SR_EventLogEvent_BASIC(impl->eventLog, impl->logLevel, L("ESRaddWd")));
297  }
298  return rc;
299}
300
301ESR_ReturnCode SR_Grammar_ResetAllSlots(SR_Grammar* self)
302{
303  ESR_ReturnCode rc, logrc;
304  int irc;
305  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
306
307  rc = impl->semgraph->reset(impl->semgraph);
308  if (rc == ESR_SUCCESS)
309  {
310    irc = CA_ResetSyntax(impl->syntax);
311    rc = irc ? ESR_INVALID_STATE : ESR_SUCCESS;
312  }
313
314  if (impl->eventLog != NULL)
315  {
316    CHKLOG(rc, SR_EventLogTokenInt_BASIC(impl->eventLog, impl->logLevel, L("igrm"), (int)impl));
317    if (rc == ESR_SUCCESS)
318      CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("RSLT"), L("ok")));
319    else
320      CHKLOG(logrc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("RSLT"), L("fail")));
321    CHKLOG(logrc, SR_EventLogEvent_BASIC(impl->eventLog, impl->logLevel, L("ESRrstSlot")));
322  }
323  return ESR_SUCCESS;
324CLEANUP:
325  return rc;
326}
327
328ESR_ReturnCode SR_Grammar_AddNametagToSlot(SR_Grammar* self, const LCHAR* slot,
329                                           const SR_Nametag* nametag, int weight, const LCHAR* tag)
330{
331  SR_NametagImpl* nametagImpl = (SR_NametagImpl*) nametag;
332  ESR_ReturnCode rc;
333
334  CHKLOG(rc, self->addWordToSlot(self, slot, nametagImpl->id, nametagImpl->value, weight, tag));
335  return ESR_SUCCESS;
336CLEANUP:
337  return rc;
338}
339
340ESR_ReturnCode SR_Grammar_SetDispatchFunction(SR_Grammar* self, const LCHAR* functionName, void* userData, SR_GrammarDispatchFunction function)
341{
342  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
343  ESR_ReturnCode rc;
344  SR_SemanticProcessorImpl* semprocImpl = (SR_SemanticProcessorImpl*) impl->semproc;
345
346  CHKLOG(rc, EP_RegisterFunction(semprocImpl->parser, functionName, userData, function));
347  return ESR_SUCCESS;
348CLEANUP:
349  return rc;
350}
351
352ESR_ReturnCode SR_GrammarLoad(const LCHAR* grammar, SR_Grammar** self)
353{
354  SR_Grammar* Interface = NULL;
355  SR_GrammarImpl* impl;
356  LCHAR* tok;
357  ESR_ReturnCode rc;
358  LCHAR filename[P_PATH_MAX];
359  int addWords;
360
361
362  if (self == NULL)
363  {
364    PLogError(L("ESR_INVALID_ARGUMENT"));
365    return ESR_INVALID_ARGUMENT;
366  }
367  CHKLOG(rc, SR_Grammar_Create(&Interface));
368  impl = (SR_GrammarImpl*) Interface;
369
370  /**
371   * Our filename (referring to the grammar to load, may have associated grammar properties
372   * appended to the end of it. We need to split up the properties from the filename
373   * example:
374   *  recog_nm/namesnnumsSC_dyn,addWords=2000 becomes
375   *    filename: recog_nm/namesnnumsSC_dyn
376   *    property: addWords=2000
377   */
378
379  /* init */
380  LSTRCPY(filename, grammar);
381  addWords = 0;
382
383  for (tok = strtok(filename, ","); tok; tok = strtok(NULL, ","))
384  {
385    if (LSTRSTR(tok, "addWords"))
386    {
387      addWords = atoi(LSTRCHR(tok, L('=')) + sizeof(LCHAR));
388    }
389    else if (tok != filename)
390    {
391      PLogError(L("UNKNOWN grammar load property %s"), tok);
392      rc = ESR_INVALID_STATE;
393      goto CLEANUP;
394    }
395  }
396
397  /**
398   * Based on the filename, determine if you are loading from image or loading from text files.
399   * If from image, then the filename will have extension .g2g. If from file, then only a basename
400   * will be provided (i.e. without extension)
401   */
402
403  impl->syntax = CA_AllocateSyntax();
404  if (impl->syntax == NULL)
405  {
406    rc = ESR_OUT_OF_MEMORY;
407    goto CLEANUP;
408  }
409
410  if (LSTRSTR(filename, L(".g2g")))
411  {
412    /* if the filename ends with .g2g, then we have a binary image */
413    if (CA_LoadSyntaxFromImage(impl->syntax, (LCHAR*) filename))
414    {
415      rc = ESR_READ_ERROR;
416      PLogError(L("ESR_READ_ERROR: Problem loading syntax from image"));
417      goto CLEANUP;
418    }
419  }
420  else
421  {
422    if (CA_LoadSyntaxAsExtensible(impl->syntax, (LCHAR*) filename, addWords))
423    {
424      rc = ESR_READ_ERROR;
425      PLogError(L("ESR_READ_ERROR: Problem loading syntax from text"));
426      goto CLEANUP;
427    }
428  }
429
430  /*
431   * Semantic Graph Loading
432   *
433   * - it was already created in Grammar_Create()
434   * - reuse the same input labels from the recognition context
435   * - load knows how to load from base filename or .g2g image
436   */
437  rc = impl->semgraph->load(impl->semgraph, impl->syntax->synx->olabels, filename, addWords);
438  if (rc != ESR_SUCCESS)
439  {
440    PLogError(L("%s: loading semgraph from image %s"), ESR_rc2str(rc), filename);
441    impl->semgraph = NULL;
442    goto CLEANUP;
443  }
444
445  *self = Interface;
446  if (impl->eventLog)
447  {
448    CHKLOG(rc, SR_EventLogTokenInt_BASIC(impl->eventLog, impl->logLevel, L("igrm"), (int)impl));
449    CHKLOG(rc, SR_EventLogToken_BASIC(impl->eventLog, impl->logLevel, L("name"), filename));
450    CHKLOG(rc, SR_EventLogEvent_BASIC(impl->eventLog, impl->logLevel, L("ESRldgrm")));
451  }
452
453  return ESR_SUCCESS;
454CLEANUP:
455  if (Interface != NULL)
456    Interface->destroy(Interface);
457  *self = NULL;
458  return rc;
459}
460
461ESR_ReturnCode SR_Grammar_Save(SR_Grammar* self, const LCHAR* filename)
462{
463  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
464  int version_number = 2;
465
466  if (filename == NULL)
467  {
468    PLogError(L("ESR_INVALID_ARGUMENT"));
469    return ESR_INVALID_ARGUMENT;
470  }
471  if (CA_DumpSyntaxAsImage(impl->syntax, filename, version_number)) /* returns 1 on failure */
472  {
473    PLogError(L("ESR_INVALID_ARGUMENT"));
474    return ESR_INVALID_STATE;
475  }
476  if (SR_SemanticGraph_Save(impl->semgraph, filename, version_number) != ESR_SUCCESS)
477  {
478    PLogError(L("ESR_INVALID_ARGUMENT"));
479    return ESR_INVALID_STATE;
480  }
481
482  return ESR_SUCCESS;
483}
484
485ESR_ReturnCode SR_Grammar_SetParameter(SR_Grammar* self, const LCHAR* key, void* value)
486{
487  /*TODO: complete with logging*/
488  return ESR_NOT_IMPLEMENTED;
489}
490
491ESR_ReturnCode SR_Grammar_SetSize_tParameter(SR_Grammar* self, const LCHAR* key, size_t value)
492{
493  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
494  size_t temp;
495  ESR_ReturnCode rc;
496
497  rc = impl->parameters->getSize_t(impl->parameters, key, &temp);
498  if (rc == ESR_SUCCESS)
499  {
500    if (temp == value)
501      return ESR_SUCCESS;
502    CHKLOG(rc, impl->parameters->removeAndFreeProperty(impl->parameters, key));
503  }
504  else if (rc != ESR_NO_MATCH_ERROR)
505    return rc;
506
507  CHKLOG(rc, impl->parameters->setSize_t(impl->parameters, key, value));
508  return ESR_SUCCESS;
509CLEANUP:
510  return rc;
511}
512
513ESR_ReturnCode SR_Grammar_GetParameter(SR_Grammar* self, const LCHAR* key, void** value)
514{
515
516  /*TODO: complete with logging*/
517  return ESR_NOT_IMPLEMENTED;
518}
519
520ESR_ReturnCode SR_Grammar_GetSize_tParameter(SR_Grammar* self, const LCHAR* key, size_t* value)
521{
522  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
523  ESR_ReturnCode rc;
524
525  if (!LSTRCMP(key, "locale"))
526  {
527    ESR_Locale locale;
528    rc = SR_VocabularyGetLanguage(impl->vocabulary, &locale);
529    if (rc != ESR_SUCCESS)
530      return rc;
531
532    *value = locale;
533    return ESR_SUCCESS;
534  }
535  else
536  {
537    rc = impl->parameters->getSize_t(impl->parameters, key, value);
538    if (rc == ESR_NO_MATCH_ERROR)
539    {
540      CHKLOG(rc, ESR_SessionGetSize_t(key, value));
541      return ESR_SUCCESS;
542    }
543    if (rc != ESR_SUCCESS)
544    {
545      PLogError(ESR_rc2str(rc));
546      return rc;
547    }
548    return ESR_SUCCESS;
549  }
550CLEANUP:
551  return rc;
552}
553
554ESR_ReturnCode SR_Grammar_Destroy(SR_Grammar* self)
555{
556  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
557  ESR_ReturnCode rc;
558
559  if (impl->parameters != NULL)
560  {
561    CHKLOG(rc, impl->parameters->destroy(impl->parameters));
562    impl->parameters = NULL;
563  }
564
565  if (impl->syntax != NULL)
566  {
567    CA_FreeSyntax(impl->syntax);
568    impl->syntax = NULL;
569  }
570
571  if (impl->semgraph != NULL)
572  {
573    CHKLOG(rc, impl->semgraph->unload(impl->semgraph));
574    CHKLOG(rc, impl->semgraph->destroy(impl->semgraph));
575    impl->semgraph = NULL;
576  }
577
578  if (impl->semproc != NULL)
579  {
580    CHKLOG(rc, impl->semproc->destroy(impl->semproc));
581    impl->semproc = NULL;
582  }
583
584  if (impl->eventLog)
585  {
586    CHKLOG(rc, SR_EventLogTokenInt_BASIC(impl->eventLog, impl->logLevel, L("igrm"), (int)impl));
587    CHKLOG(rc, SR_EventLogEvent_BASIC(impl->eventLog, impl->logLevel, L("ESRklgrm")));
588  }
589
590  FREE(self);
591  return ESR_SUCCESS;
592CLEANUP:
593  return rc;
594}
595
596ESR_ReturnCode SR_Grammar_SetupRecognizer(SR_Grammar* self, SR_Recognizer* recognizer)
597{
598  ESR_ReturnCode rc;
599  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
600  CA_Arbdata* ca_arbdata;
601  SR_AcousticModels* models = NULL;
602
603  if (impl == NULL || recognizer == NULL)
604  {
605    PLogError(L("ESR_INVALID_ARGUMENT"));
606    return ESR_INVALID_ARGUMENT;
607  }
608  impl->recognizer  = recognizer;
609  recognizer->setWordAdditionCeiling( recognizer, self);
610
611  rc = recognizer->getModels( recognizer, &models);
612  if(rc != ESR_SUCCESS || models == NULL) {
613	  impl->recognizer = NULL;
614	  CA_AttachArbdataToSyntax( impl->syntax, NULL);
615	  return ESR_INVALID_STATE;
616  }
617  ca_arbdata = (CA_Arbdata*)(models->getArbdata( models));
618  rc = CA_AttachArbdataToSyntax( impl->syntax, ca_arbdata);
619  if(rc != 0)
620	  return ESR_INVALID_STATE;
621  return ESR_SUCCESS;
622}
623
624ESR_ReturnCode SR_Grammar_UnsetupRecognizer(SR_Grammar* self)
625{
626  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
627  if(impl == NULL) return ESR_INVALID_ARGUMENT;
628  impl->recognizer  = NULL;
629  CA_AttachArbdataToSyntax( impl->syntax, NULL);
630  return ESR_SUCCESS;
631}
632
633SREC_GRAMMAR_API ESR_ReturnCode SR_Grammar_SetupVocabulary(SR_Grammar *self, SR_Vocabulary *vocabulary)
634{
635  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
636
637  if (vocabulary == NULL)
638  {
639    PLogError(L("ESR_INVALID_ARGUMENT"));
640    return ESR_INVALID_ARGUMENT;
641  }
642  impl->vocabulary = vocabulary;
643  return ESR_SUCCESS;
644}
645
646ESR_ReturnCode SR_Grammar_CheckParse(SR_Grammar* self, const LCHAR* transcription, SR_SemanticResult** result, size_t* resultCount)
647{
648  ESR_ReturnCode rc;
649  SR_GrammarImpl* impl = (SR_GrammarImpl*) self;
650  size_t resultCountIn = *resultCount;
651
652  if (transcription == NULL)
653  {
654    PLogError(L("ESR_INVALID_ARGUMENT"));
655    return ESR_INVALID_ARGUMENT;
656  }
657
658  /* NULL for special implementation when CheckParse is called by application that does not know
659     about the hidden data structure SR_SemanticResult */
660  if (result == NULL)
661  {
662    if (CA_CheckTranscription(impl->syntax, transcription, 0) == 0)
663      *resultCount = 1;
664    else
665      *resultCount = 0;
666    return ESR_SUCCESS;
667  }
668  rc = impl->semproc->checkParse(impl->semproc, impl->semgraph, transcription, result, resultCount);
669  if (*resultCount == 0)
670  {
671    /* get the literal that did parse from the text_parser.c code */
672    char copy_of_trans[512];
673    strcpy(copy_of_trans, transcription);
674    *resultCount = resultCountIn;
675    if (CA_CheckTranscription(impl->syntax, (LCHAR*)copy_of_trans, 0) == 0)
676      rc = impl->semproc->checkParse(impl->semproc, impl->semgraph, copy_of_trans, result, resultCount);
677  }
678  return rc;
679}
680
681#define DISABLEcostdata 8192
682
683ESR_ReturnCode SR_GrammarAllowOnly(SR_Grammar* self, const char* transcription)
684{
685  char copy_of[512], *word;
686  int i, j;
687  wordID wdids[32], nw = 0;
688  SR_GrammarImpl* impl = (SR_GrammarImpl*)self;
689  CA_Syntax* ca_syntax = impl->syntax;
690  srec_context* fst = ca_syntax->synx;
691  ESR_ReturnCode rc = ESR_SUCCESS;
692
693  strcpy(copy_of, transcription);
694
695  for (word = strtok(copy_of, " "); word; nw++, word = strtok(NULL, " "))
696  {
697    wdids[nw] =   wordmap_find_index(fst->olabels, word);
698    if (wdids[nw] == MAXwordID)
699      rc = ESR_NO_MATCH_ERROR;
700  }
701
702  for (i = 0; i < fst->num_arcs; i++)
703  {
704    wordID wdid = fst->FSMarc_list[i].olabel;
705    if (wdid < EPSILON_OFFSET) ;
706    else if (wdid == fst->beg_silence_word) ;
707    else if (wdid == fst->end_silence_word) ;
708    else
709    {
710      for (j = nw; --j >= 0;)
711        if (wdid == wdids[j]) break;
712      if (j < 0)
713      {
714        fst->FSMarc_list[i].cost |= DISABLEcostdata; /* disable this arc */
715      }
716      else
717      {
718        /* pfprintf(PSTDOUT, "enabling arc %d for %d %s\n",
719           i, wdid, transcription); */
720        fst->FSMarc_list[i].cost &= ~(DISABLEcostdata); /* enable this arc */
721      }
722    }
723  }
724  /* added, this way we prevent more failures due to dead ends */
725  for (; ;)
726  {
727    FSMarc* arc;
728    arcID j, counter = 0;
729    nodeID node;
730    costdata mincost;
731
732    for (i = 0; i < fst->num_arcs; i++)
733    {
734      if (fst->FSMarc_list[i].cost < DISABLEcostdata)
735      {
736        node = fst->FSMarc_list[i].to_node;
737        if (node == fst->end_node) continue;
738        mincost = DISABLEcostdata;
739        for (j = fst->FSMnode_list[node].un_ptr.first_next_arc; j != MAXarcID; j = arc->linkl_next_arc)
740        {
741          arc = &fst->FSMarc_list[j];
742          if (arc->cost < mincost) mincost = arc->cost;
743        }
744        if (mincost >= DISABLEcostdata)
745        {
746          fst->FSMarc_list[i].cost |= DISABLEcostdata;
747          counter++;
748        }
749      }
750    }
751    if (counter == 0) break;
752  }
753
754  return rc;
755}
756
757ESR_ReturnCode SR_GrammarAllowAll(SR_Grammar* self)
758{
759  int i;
760  SR_GrammarImpl* impl = (SR_GrammarImpl*)self;
761  CA_Syntax* ca_syntax = impl->syntax;
762  srec_context* fst = ca_syntax->synx;
763  ESR_ReturnCode rc = ESR_SUCCESS;
764
765  for (i = 0; i < fst->num_arcs; i++)
766  {
767    fst->FSMarc_list[i].cost &= ~(DISABLEcostdata); /* enable this arc */
768  }
769  return rc;
770}
771
772