windows-nt/Source/XPSP1/NT/enduser/speech/sapi/sapi/cfgengine.h

1441 lines
43 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*******************************************************************************
* CFGEngine.h *
*-------------*
* Description:
*-------------------------------------------------------------------------------
* Created By: RAL
* Copyright (C) 1998, 1999 Microsoft Corporation
* All Rights Reserved
*******************************************************************************/
#ifndef __CFGENGINE_H_
#define __CFGENGINE_H_
#include "cfggrammar.h"
#ifndef _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC
#endif
#include <crtdbg.h>
#include <stdio.h> // for wprintf
#ifndef _WIN32_WCE
#include <wchar.h> // for _wcsdup
#endif
class CCFGEngine;
class CBaseInterpreter;
class CInterpreterSite;
class CTransitionId;
// local datastructure to hold the ClientContext
struct WORDTABLEENTRY
{
ULONG cRefs;
ULONG ulTextOffset;
void * pvClientContext;
};
class CStateInfoListElement : public CSpStateInfo
{
public:
CStateInfoListElement(): CSpStateInfo()
{
}
CStateInfoListElement * m_pNext;
};
typedef struct SPPARSENODE
{
SPTRANSITIONID ID;
BYTE Type;
union
{
const WCHAR *pszRuleName;
const WCHAR *pszWordDisplayText;
};
ULONG ulRuleId;
BOOL fInvokeInterpreter;
BOOL fRuleExit;
BOOL fRedoWildcard;
union
{
SPWORDHANDLE hWord;
SPRULEHANDLE hRule;
};
ULONG ulFirstElement;
ULONG ulCountOfElements;
signed char RequiredConfidence;
} SPPARSENODE;
class CParseNode : public SPPARSENODE
{
public:
CParseNode() : m_pLeft(NULL), m_pRight(NULL), m_pNext(NULL), m_pParent(NULL)
{
pszRuleName = NULL;
ulRuleId = 0;
fInvokeInterpreter = FALSE;
fRedoWildcard = FALSE;
}
void Init()
{
m_pNext = NULL;
m_pLeft = NULL;
m_pRight = NULL;
m_pParent = NULL;
pszRuleName = NULL;
ulRuleId = 0;
fInvokeInterpreter = FALSE;
fRedoWildcard = FALSE;
}
CParseNode * m_pNext;
CParseNode * m_pLeft;
CParseNode * m_pRight;
CParseNode * m_pParent;
};
struct SPTIDNODE
{
SPTRANSITIONID tid;
DWORD ulIndex : 22; // count of words in the input (EPS will have the count of the *next* word!
DWORD fIsWord : 1;
DWORD dwReserved : 9;
};
class CTIDArray
{
public:
CTIDArray(ULONG cWords) : m_cWords(cWords), m_cArcs(0), m_cAllocedArcs(0), m_ulIndex(0), m_ulCurrentIndex(0)
{
m_aTID = new SPTIDNODE[16];
if (m_aTID)
{
m_cAllocedArcs = 16;
memset(m_aTID, 0, m_cAllocedArcs * sizeof(SPTIDNODE));
}
}
CTIDArray(ULONG cArcs, ULONG cWords) : m_cWords(cWords), m_cArcs(0), m_cAllocedArcs(0), m_ulIndex(0), m_ulCurrentIndex(0)
{
m_aTID = new SPTIDNODE[2*cArcs];
if (m_aTID)
{
m_cAllocedArcs = 2*cArcs;
memset(m_aTID, 0, m_cAllocedArcs * sizeof(SPTIDNODE));
}
}
~CTIDArray()
{
delete [] m_aTID;
}
HRESULT Insert(SPTRANSITIONID tid, BOOL fIsWord)
{
HRESULT hr = S_OK;
if (m_cArcs == (m_cAllocedArcs -1))
{
// double the size
SPTIDNODE *pTemp = new SPTIDNODE[2*m_cAllocedArcs];
if (pTemp)
{
memset(pTemp, 0, 2* m_cAllocedArcs * sizeof(SPTIDNODE));
memcpy(pTemp, m_aTID, m_cArcs*sizeof(SPTIDNODE));
m_cAllocedArcs *= 2;
delete [] m_aTID;
m_aTID = pTemp;
}
else
{
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr))
{
m_aTID[m_cArcs].fIsWord = fIsWord;
if (m_cArcs > 0)
{
m_ulIndex = m_aTID[m_cArcs-1].fIsWord ? m_ulIndex + 1 : m_ulIndex;
}
// SPDBG_ASSERT(m_ulIndex <= m_cWords);
m_ulIndex = (m_ulIndex > m_cWords) ? m_cWords : m_ulIndex;
m_aTID[m_cArcs].ulIndex = m_ulIndex;
m_aTID[m_cArcs++].tid = tid;
}
return hr;
}
HRESULT ConstructFromParseTree(CParseNode *pParseNode)
{
HRESULT hr = S_OK;
if (pParseNode->Type == SPTRANSEPSILON)
{
hr = Insert(pParseNode->ID, FALSE);
}
else if (pParseNode->Type == SPTRANSWORD)
{
hr = Insert(pParseNode->ID, TRUE);
}
else if (pParseNode->Type == SPTRANSDICTATION)
{
hr = Insert(pParseNode->ID, TRUE);
}
else if (pParseNode->Type == SPTRANSTEXTBUF)
{
hr = Insert(pParseNode->ID, TRUE);
}
else if (pParseNode->Type == SPTRANSWILDCARD)
{
hr = Insert(pParseNode->ID, TRUE);
}
if (SUCCEEDED(hr) && pParseNode->m_pLeft)
{
hr = ConstructFromParseTree(pParseNode->m_pLeft);
}
if (SUCCEEDED(hr) && pParseNode->m_pRight)
{
hr = ConstructFromParseTree(pParseNode->m_pRight);
}
return hr;
}
public:
SPTIDNODE * m_aTID;
ULONG m_cArcs;
ULONG m_ulIndex;
ULONG m_cAllocedArcs;
ULONG m_cWords;
ULONG m_ulCurrentIndex;
};
#define RULESTACKHASHSIZE 128
class CRuleStack
{
public:
CRuleStack()
{
}
CRuleStack *m_pNext;
CRuleStack *m_pParent;
SPTRANSITIONID m_TransitionId;
SPSTATEHANDLE m_hFollowState;
SPRULEHANDLE m_hRule;
inline Init(CRuleStack *pRuleStack, SPTRANSITIONID TransitionId, SPSTATEHANDLE hFollowState, SPRULEHANDLE hRule)
{
m_pNext = NULL;
m_pParent = pRuleStack;
m_TransitionId = TransitionId;
m_hFollowState = hFollowState;
m_hRule = hRule;
}
inline static ULONG GetHashEntry(CRuleStack *pRuleStack, SPTRANSITIONID TransitionId)
{
// NTRAID#SPEECH-7356-2000/08/24-agarside - Fix << 3
return (ULONG)(((ULONG_PTR)(pRuleStack) + (ULONG_PTR)(TransitionId)) % RULESTACKHASHSIZE);
}
};
#define SEARCHNODEHASHSIZE 1024
class CSearchNode
{
public:
CSearchNode()
{
m_pNext = NULL;
m_pStack = NULL;
m_hState = 0;
m_cTransitions = 0;
}
CSearchNode *m_pNext;
CRuleStack *m_pStack;
SPSTATEHANDLE m_hState;
UINT m_cTransitions;
inline void Init( CRuleStack *pRuleStack, SPSTATEHANDLE hState, UINT cTransitions )
{
m_pNext = NULL;
m_pStack = pRuleStack;
m_hState = hState;
m_cTransitions = cTransitions;
}
inline static ULONG GetHashEntry(CRuleStack *pRuleStack, SPSTATEHANDLE hState, UINT cTransitions)
{
// NTRAID#SPEECH-7356-2000/08/24-agarside - Fix << 3
return (ULONG)(((ULONG_PTR)(pRuleStack) + (ULONG_PTR)(hState) + cTransitions) % RULESTACKHASHSIZE);
}
};
////////////////////////////////////////////////////////////////
// Helper class for WORDTABLEENTRY
//
// allocates and maintains WORDTABLEENTRY on behalf of the CFGEngine
class CWordTableEntryBlob
{
public:
CWordTableEntryBlob()
{
m_pBlob = NULL;
m_cBlobEntries = 0;
m_ulNextUnusedEntry = 0;
}
~CWordTableEntryBlob()
{
if (m_pBlob)
{
free(m_pBlob);
}
}
HRESULT Init(ULONG ulInitSize)
{
HRESULT hr = S_OK;
if ((m_pBlob = (WORDTABLEENTRY *)malloc(ulInitSize * sizeof(WORDTABLEENTRY))) == NULL)
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
m_cBlobEntries = ulInitSize;
}
return hr;
}
HRESULT GetNewWordTableEntry(WORDTABLEENTRY **ppEntry)
{
if (SP_IS_BAD_WRITE_PTR(ppEntry))
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
if (m_ulNextUnusedEntry == m_cBlobEntries) // this also catches m_pBlob == NULL
{
WORDTABLEENTRY *pTemp = (WORDTABLEENTRY*) realloc(m_pBlob,(m_cBlobEntries + 1024)*sizeof(WORDTABLEENTRY));
if (pTemp == NULL)
{
hr = E_OUTOFMEMORY;
}
else
{
memset(pTemp+m_cBlobEntries,0, 1024*sizeof(WORDTABLEENTRY));
m_pBlob = pTemp;
m_cBlobEntries += 1024;
}
}
if (SUCCEEDED(hr))
{
*ppEntry = &m_pBlob[m_ulNextUnusedEntry++];
}
return hr;
}
HRESULT Clear()
{
HRESULT hr = S_OK;
m_ulNextUnusedEntry = 0;
return hr;
}
private:
WORDTABLEENTRY * m_pBlob;
ULONG m_cBlobEntries;
ULONG m_ulNextUnusedEntry;
};
/////////////////////////////////////////////////////////////////////////////
// CBaseInterpreter
/////////////////////////////////////////////////////////////////////////////
// CInterpreterSite
class CInterpreterSite : public ISpCFGInterpreterSite
{
public:
CInterpreterSite()
{
m_pPhrase = NULL;
m_pCFGEngine = NULL;
m_hParentProperty = NULL;
m_hThisNodeProperty = NULL;
m_ulFirstElement = 0;
m_ulCountOfElements = 0;
m_hRule = NULL;
}
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
{
if (riid == __uuidof(ISpCFGInterpreterSite) || riid == __uuidof(IUnknown))
{
*ppv = (ISpCFGInterpreterSite *)this;
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef()
{
return 2;
}
STDMETHODIMP_(ULONG) Release()
{
return 1;
}
STDMETHODIMP AddTextReplacement(SPPHRASEREPLACEMENT * pReplace)
{
if (SP_IS_BAD_READ_PTR(pReplace))
{
return E_INVALIDARG;
}
pReplace->ulFirstElement = m_ulFirstElement;
pReplace->ulCountOfElements = m_ulCountOfElements;
return m_pPhrase->AddReplacements(1,pReplace);
}
STDMETHODIMP AddProperty(const SPPHRASEPROPERTY *pProperty)
{
m_CritSec.Lock();
HRESULT hr = S_OK;
if (m_hThisNodeProperty == NULL)
{
SPPHRASEPROPERTY prop;
SpZeroStruct(prop);
prop.ulFirstElement = m_ulFirstElement;
prop.ulCountOfElements = m_ulCountOfElements;
prop.pszName = m_pszRuleName;
prop.ulId = m_ulRuleId;
hr = m_pPhrase->AddProperties(m_hParentProperty, &prop, &m_hThisNodeProperty);
}
m_CritSec.Unlock();
if (SUCCEEDED(hr))
{
hr = m_pPhrase->AddProperties(m_hThisNodeProperty, pProperty, NULL);
}
return hr;
}
STDMETHODIMP GetResourceValue(const WCHAR * pszResourceName, WCHAR **ppszResourceValue)
{
return m_pCFGEngine->GetResourceValue(m_hRule, pszResourceName, ppszResourceValue);
}
STDMETHODIMP Initialize(ISpCFGEngine *pCFGEngine, ISpPhraseBuilder *pPhrase,
const WCHAR * pszRuleName, ULONG ulRuleId,
const ULONG ulFirstElement, const ULONG ulCountOfElements,
SPPHRASEPROPERTYHANDLE hParentProperty, SPPHRASEPROPERTYHANDLE hThisNodeProperty,
SPRULEHANDLE hRule)
{
HRESULT hr = S_OK;
m_pPhrase = pPhrase;
m_ulFirstElement = ulFirstElement;
m_ulCountOfElements = ulCountOfElements;
m_pCFGEngine = pCFGEngine;
m_hParentProperty = hParentProperty;
m_hThisNodeProperty = hThisNodeProperty;
m_pszRuleName = pszRuleName;
m_ulRuleId = ulRuleId;
m_hRule = hRule;
return hr;
}
//
// Member data
//
const WCHAR * m_pszRuleName;
ULONG m_ulRuleId;
ISpCFGEngine * m_pCFGEngine;
ISpPhraseBuilder * m_pPhrase;
ULONG m_ulFirstElement;
ULONG m_ulCountOfElements;
SPPHRASEPROPERTYHANDLE m_hParentProperty;
SPPHRASEPROPERTYHANDLE m_hThisNodeProperty;
SPRULEHANDLE m_hRule;
CComAutoCriticalSection m_CritSec;
};
/////////////////////////////////////////////////////////////////////////////
// CCFGEngine
const DWORD MAXNUMGRAMMARS = 1024;
const DWORD ARCCHUNK = 32;
class CRuleHandle
{
private:
DWORD m_RuleIndex : 22;
DWORD m_GrammarId : 10;
public:
CRuleHandle()
{}
CRuleHandle(SPRULEHANDLE h)
{
*(DWORD*)this = HandleToUlong(h);
}
CRuleHandle(ULONG GrammarId, ULONG RuleIndex)
{
m_GrammarId = GrammarId;
m_RuleIndex = RuleIndex+1;
}
inline CRuleHandle(const CCFGGrammar * pGrammar, ULONG RuleIndex);
operator=(SPRULEHANDLE h)
{
*(DWORD*)this = HandleToUlong(h);
return *(DWORD *)this;
}
operator SPRULEHANDLE()
{
return (SPRULEHANDLE)LongToHandle(*(DWORD*)this);
}
ULONG GrammarId()
{
return m_GrammarId;
}
ULONG RuleIndex()
{
return m_RuleIndex-1;
}
};
class CStateHandle
{
private:
DWORD m_FirstArcIndex : 22;
DWORD m_GrammarId : 10;
public:
CStateHandle()
{}
CStateHandle(SPSTATEHANDLE h)
{
*(DWORD*)this = HandleToUlong(h);
}
CStateHandle(ULONG GrammarId, ULONG FirstArcIndex)
{
m_GrammarId = GrammarId;
m_FirstArcIndex = FirstArcIndex;
}
inline CStateHandle(const CCFGGrammar * pGrammar, ULONG ArcIndex);
operator =(SPSTATEHANDLE h)
{
*(DWORD*)this = HandleToUlong(h);
return *(DWORD*)this;
}
operator SPSTATEHANDLE()
{
return (SPSTATEHANDLE)LongToHandle(*(DWORD*)this);
}
ULONG GrammarId()
{
return m_GrammarId;
}
ULONG FirstArcIndex()
{
return m_FirstArcIndex;
}
};
class CTransitionId
{
private:
DWORD m_ArcIndex : 22;
DWORD m_GrammarId : 10;
public:
CTransitionId()
{}
CTransitionId(SPTRANSITIONID h)
{
*(DWORD*)this = HandleToUlong(h);
}
CTransitionId(ULONG GrammarId, ULONG ArcIndex)
{
m_GrammarId = GrammarId;
m_ArcIndex = ArcIndex;
}
inline CTransitionId(const CCFGGrammar * pGrammar, ULONG ArcIndex);
operator =(SPTRANSITIONID h)
{
*(DWORD*)this = HandleToUlong(h);
return *(DWORD*)this;
}
operator SPTRANSITIONID()
{
return (SPTRANSITIONID)LongToHandle(*(DWORD*)this);
}
ULONG GrammarId()
{
return m_GrammarId;
}
ULONG ArcIndex()
{
return m_ArcIndex;
}
void IncToNextArcIndex()
{
m_ArcIndex++;
}
};
class CWordHandle
{
private:
DWORD m_WordTableIndex;
public:
CWordHandle()
{}
CWordHandle(SPWORDHANDLE h)
{
*(DWORD*)this = HandleToUlong(h);
}
CWordHandle(ULONG WordTableIndex)
{
m_WordTableIndex = WordTableIndex;
}
operator =(SPWORDHANDLE h)
{
*(DWORD *)this = HandleToUlong(h);
return *(DWORD*)this;
}
operator =(ULONG WordTableIndex)
{
m_WordTableIndex = WordTableIndex;
return m_WordTableIndex;
}
operator SPWORDHANDLE()
{
return (SPWORDHANDLE)LongToHandle(*(DWORD*)this);
}
ULONG WordTableIndex()
{
return m_WordTableIndex;
}
};
// This class represents the results of a parse using ConstrctParseTree. It records total number
// of words parsed as well as how many of these were dictation tag or wildcard words.
// The Compare method allows two parses to be compared on the basis of which covers the most words
// and uses the least dictation words. This allows EmulateRecognition to pick a CFG parse over a dictation one.
class WordsParsed
{
public:
ULONG ulWordsParsed;
ULONG ulDictationWords;
ULONG ulWildcardWords;
WordsParsed()
: ulWordsParsed(0),
ulDictationWords(0),
ulWildcardWords(0)
{
}
void Zero()
{
ulWordsParsed = 0;
ulDictationWords = 0;
ulWildcardWords = 0;
}
void Add(WordsParsed *pSource)
{
ulWordsParsed += pSource->ulWordsParsed;
ulDictationWords += pSource->ulDictationWords;
ulWildcardWords += pSource->ulWildcardWords;
}
LONG Compare(WordsParsed *pSource)
{
if(ulWordsParsed > pSource->ulWordsParsed)
{
return 1;
}
else if(ulWordsParsed < pSource->ulWordsParsed)
{
return -1;
}
else if(ulWildcardWords < pSource->ulWildcardWords)
{
return 1;
}
else if(ulWildcardWords > pSource->ulWildcardWords)
{
return -1;
}
else if(ulDictationWords < pSource->ulDictationWords)
{
return 1;
}
else if(ulDictationWords > pSource->ulDictationWords)
{
return -1;
}
else
{
return 0;
}
}
};
class ATL_NO_VTABLE CCFGEngine :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CCFGEngine, &CLSID_SpCFGEngine>,
public ISpCFGEngine,
public ISpCFGInterpreter // Not exposed by QI, used internally
{
friend CCFGGrammar;
public:
DECLARE_REGISTRY_RESOURCEID(IDR_CFGENGINE)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CCFGEngine)
COM_INTERFACE_ENTRY(ISpCFGEngine)
END_COM_MAP()
// Non-interface methods
private:
HRESULT AllocateGrammar(CCFGGrammar ** ppNewGrammar);
signed char _CalcMultipleWordConfidence(const SPPHRASE *pPhrase, ULONG ulFirstElement, ULONG ulCountOfElements);
// Hash code.
HRESULT CreateParseHashes(void);
HRESULT DeleteParseHashes( BOOL final );
HRESULT FindCreateRuleStack(CRuleStack **pNewRuleStack, CRuleStack *pRuleStack, SPTRANSITIONID TransitionId, SPSTATEHANDLE hRuleFollowerState);
HRESULT FindCreateSearchNode(CSearchNode **pNewSearchNode, CRuleStack *pRuleStack, SPSTATEHANDLE hState, UINT cTransitions);
// End hash code.
// memory management
inline HRESULT AllocateStateInfo(CStateHandle StateHandle, CStateInfoListElement ** ppNewState);
inline HRESULT AllocateSearchNode( CSearchNode **pNewSearchNode );
inline HRESULT AllocateRuleStack( CRuleStack **pNewRuleStack );
inline void FreeRuleStack( CRuleStack * pRuleStack );
inline void FreeSearchNode( CSearchNode * pSearchNode );
inline HRESULT FreeParseTree(CParseNode * pParseNode);
// memory management
public:
// on behalf of CCFGGrammar
HRESULT AddWords(CCFGGrammar * pGrammar, ULONG ulOldNumWords, ULONG ulOldNumChars);
HRESULT RemoveWords(const CCFGGrammar * pGrammar);
HRESULT AddRules(CCFGGrammar * pGrammar, ULONG IndexStart);
HRESULT RemoveRules(const CCFGGrammar * pGrammar);
HRESULT ActivateRule(const CCFGGrammar * pGrammar, const ULONG ulRuleIndex);
HRESULT DeactivateRule(const ULONG ulGrammarID, const ULONG ulRuleIndex);
HRESULT SetGrammarState(const SPGRAMMARSTATE eGrammarState);
HRESULT InternalParseFromPhrase(ISpPhraseBuilder *pPhrase,
const SPPHRASE *pSPPhrase,
const _SPPATHENTRY *pPath,
const ULONG ulFirstElement,
const BOOL fIsITN,
const BOOL fIsHypothesis,
WordsParsed *pWordsParsed);
HRESULT ConstructParseTree(CStateHandle hState, const _SPPATHENTRY *pPath, const BOOL fUseWordHandles, const BOOL fIsITN,
const ULONG ulFirstTransition, const ULONG cTransitions, const BOOL fIsHypothesis,
WordsParsed *pWordsParsed, CParseNode **ppParseNode, BOOL *pfDeletedElements);
HRESULT InternalConstructParseTree(CStateHandle hState, const _SPPATHENTRY *pPath, const BOOL fUseWordHandles, const BOOL fIsITN,
const ULONG ulFirstTransition, const ULONG cTransitions, const BOOL fIsHypothesis,
WordsParsed *pWordsParsed, CParseNode **ppParseNode, CRuleStack *pRuleStack);
HRESULT RestructureParseTree(CParseNode *pParseNode, BOOL *pfDeletedElements);
HRESULT RecurseAdjustCounts(CParseNode *pParseNode, UINT iRemove);
HRESULT WalkParseTree(CParseNode *pParseNode,
const BOOL fIsITN,
const BOOL fIsHypothesis,
const SPPHRASEPROPERTYHANDLE hParentProperty,
const SPPHRASERULEHANDLE hParentRule,
const CTIDArray *pArcList,
const ULONG ulElementOffset,
const ULONG ulCountOfElements,
ISpPhraseBuilder * pResultPhrase,
const SPPHRASE *pPhrase);
HRESULT InternalGetStateInfo(CStateHandle StateHandle, SPSTATEINFO * pStateInfo, BOOL fWantImports);
//
// Note that we support this interface but QI will not return it. We simply
// support it internally so that we can always call m_cpInterpreter->Interpret()
// from a grammar. For all non-object type grammars, it defers to the CFG Engine
//
STDMETHODIMP InitGrammar(const WCHAR * pszGrammarName, const void ** pvGrammarData)
{
SPDBG_ASSERT(FALSE);
return E_NOTIMPL; // This method should never be called
}
STDMETHODIMP Interpret(ISpPhraseBuilder * pPhrase,
const ULONG ulFirstElement,
const ULONG ulCountOfElements,
ISpCFGInterpreterSite * pSite);
//
// ISpCFGEngine
//
public:
HRESULT FinalConstruct()
{
HRESULT hr = S_OK;
m_pGrammars = NULL;
m_cGrammarTableSize = 0;
m_cGrammarsLoaded = 0;
m_cLoadsInProgress = 0;
m_cArcs = 0;
m_cTotalRules = 0;
m_cTopLevelRules = 0;
m_cNonImportRules = 0;
m_pWordTable = NULL;
m_ulLargestIndex = 0;
m_cWordTableEntries = 0;
m_RuleStackList = NULL;
m_SearchNodeList = NULL;
m_bIsCacheValid = FALSE;
m_cLangIDs = 0;
m_CurLangID = 0;
m_pClient = NULL;
return hr;
}
~CCFGEngine()
{
SPDBG_ASSERT(m_cGrammarsLoaded == 0);
if( m_RuleStackList != NULL || m_SearchNodeList != NULL )
{
DeleteParseHashes( TRUE );
}
if (m_pWordTable)
{
delete[] m_pWordTable;
}
delete[] m_pGrammars;
};
STDMETHODIMP ParseITN( ISpPhraseBuilder *pPhrase );
STDMETHODIMP ParseFromTransitions(const SPPARSEINFO * pParseInfo, ISpPhraseBuilder **ppPhrase);
STDMETHODIMP ParseFromPhrase(ISpPhraseBuilder *pPhrase, const SPPHRASE *pSPPhrase, const ULONG ulFirstElementToParse, BOOL fIsITN, ULONG *pulWordsParsed);
STDMETHODIMP LoadGrammarFromFile(const WCHAR * pszGrammarName, void * pvOwnerCookie, void * pvClientCookie, ISpCFGGrammar **ppGrammarObject);
STDMETHODIMP LoadGrammarFromObject(REFCLSID rcid, const WCHAR * pszGrammarName, void * pvOwnerCookie, void * pvClientCookie, ISpCFGGrammar ** ppGrammarObject);
STDMETHODIMP LoadGrammarFromMemory(const SPBINARYGRAMMAR * pData,
void * pvOwnerCookie,
void * pvClientCookie,
ISpCFGGrammar **ppGrammarObject,
WCHAR * pszGrammarName);
STDMETHODIMP LoadGrammarFromResource(const WCHAR *pszModuleName,
const WCHAR *pszResourceName,
const WCHAR *pszResourceType,
WORD wLanguage,
void * pvOwnerCookie,
void * pvClientCookie,
ISpCFGGrammar **ppGrammarObject);
STDMETHODIMP SetClient(_ISpRecoMaster * pClient);
STDMETHODIMP GetWordInfo(SPWORDENTRY * pWordEntry, SPWORDINFOOPT Options);
STDMETHODIMP SetWordClientContext(SPWORDHANDLE hWord, void * pvClientContext);
STDMETHODIMP GetRuleInfo(SPRULEENTRY * pRuleEntry, SPRULEINFOOPT Options);
STDMETHODIMP SetRuleClientContext(SPRULEHANDLE hRule, void * pvClientContext);
STDMETHODIMP GetStateInfo(SPSTATEHANDLE hState, SPSTATEINFO * pStateInfo);
STDMETHODIMP GetOwnerCookieFromRule(SPRULEHANDLE rulehandle, void ** ppvOwnerCookie);
STDMETHODIMP GetResourceValue(const SPRULEHANDLE hRule, const WCHAR *pszResourceName, WCHAR ** ppsz);
STDMETHODIMP GetRuleDescription(const SPRULEHANDLE hRule, WCHAR ** ppszRuleDescription, ULONG *pulRuleId, LANGID * pLangID);
STDMETHODIMP GetTransitionProperty(SPTRANSITIONID ID, SPTRANSITIONPROPERTY **ppCoMemProperty);
STDMETHODIMP RemoveGrammar(ULONG ulGrammarID);
STDMETHODIMP SetLanguageSupport(const LANGID * paLangIds, ULONG cLangIds);
//
// Template function works for Rules, States, and Transitions
//
template<class C>
inline CCFGGrammar * GrammarOf(C h)
{
return m_pGrammars[h.GrammarId()];
}
inline RUNTIMERULEENTRY * RuleOf(CRuleHandle rh);
inline WORDTABLEENTRY * WordTableEntryOf(CWordHandle wh)
{
return m_pWordTable + wh.WordTableIndex();
}
inline const WCHAR * TextOf(CWordHandle wh)
{
return m_WordStringBlob.String(WordTableEntryOf(wh)->ulTextOffset);
}
inline const WCHAR * TextOf(CTransitionId tid)
{
CCFGGrammar *pGram = GrammarOf(tid);
if (pGram)
{
ULONG ulTid = pGram->m_Header.pArcs[tid.ArcIndex()].TransitionIndex;
if (ulTid == SPWILDCARDTRANSITION)
{
return SPWILDCARD;
}
else if (ulTid == SPDICTATIONTRANSITION)
{
return NULL; // the CFG engine then uses the SR engine provided data
}
else if (ulTid == SPTEXTBUFFERTRANSITION)
{
return NULL; // the CFG engine then uses the SR engine provided data
}
CWordHandle wh = pGram->m_IndexToWordHandle[ulTid];
return m_WordStringBlob.String(WordTableEntryOf(wh)->ulTextOffset);
}
return NULL;
}
inline ULONG IndexOf(const WCHAR *pszWord)
{
return m_WordStringBlob.Find(pszWord);
}
inline void * ClientContextOf(CWordHandle wh)
{
return WordTableEntryOf(wh)->pvClientContext;
}
void ScanForSlash(WCHAR **pp);
HRESULT SetWordInfo(WCHAR *pszText, SPWORDENTRY *pWordEntry);
BOOL CompareWords(const CWordHandle wh, const SPPHRASEELEMENT * elem, BOOL fCompareExact, BOOL fCaseSensitive);
void ResolveWordHandles(_SPPATHENTRY *pPath, const ULONG cElements, BOOL fCaseSensitive);
//
// Helper function for splitting up grammar words into its constituents
//
HRESULT AssignTextPointers(WCHAR *pszText, const WCHAR **ppszDisplayText,
const WCHAR **ppszLexicalForm, const WCHAR **ppszPronunciation)
{
HRESULT hr = S_OK;
SPDBG_ASSERT(pszText);
if (pszText[0] != L'/')
{
*ppszDisplayText = pszText;
*ppszLexicalForm = pszText;
*ppszPronunciation = NULL;
}
else
{
*ppszDisplayText = &pszText[0] + 1;
WCHAR *p = &pszText[0] + 1; // skipping over '/'
while(p && (*p != 0) && (*p != L'/'))
{
if (*p == L'\\')
{
p++;
}
p++;
}
if (*p == L'/')
{
*ppszLexicalForm = p + 1;
*p = 0;
p++; // skipping over '/'
while((*p != 0) && (*p != L';') && (*p != L'/'))
{
p++;
}
if (*p == L'/')
{
*ppszPronunciation = p + 1;
*p = 0;
}
else
{
*ppszPronunciation = NULL;
}
}
else
{
SPDBG_ASSERT(*p == 0);
*ppszLexicalForm = NULL;
*ppszPronunciation = NULL;
}
}
return hr;
}
inline BOOL GetPropertiesOfTransition(CTransitionId hTrans, SPPHRASEPROPERTY *pProperty, SPCFGSEMANTICTAG **ppTag, ULONG *pulGrammarId);
inline BOOL GetPropertiesOfRule(CRuleHandle rh, const WCHAR **ppszRuleName, ULONG *pulRuleId, BOOL *pfIsPropertyRule);
inline BOOL GetInterpreter(const CRuleHandle rh, ISpCFGInterpreter **ppInterpreter);
HRESULT ValidateHandle(CRuleHandle rh);
HRESULT ValidateHandle(CWordHandle wh);
HRESULT ValidateHandle(CStateHandle sh);
HRESULT ValidateHandle(CTransitionId th);
HRESULT InternalLoadGrammarFromFile(const WCHAR * pszGrammarName, void * pvOwnerCookie, void * pvClientCookie, BOOL fIsToplevelLoad, ISpCFGGrammar **ppGrammarObject);
HRESULT InternalLoadGrammarFromObject(REFCLSID rcid, const WCHAR * pszGrammarName, void * pvOwnerCookie, void * pvClientCookie, BOOL fIsToplevelLoad, ISpCFGGrammar ** ppGrammarObject);
HRESULT InternalLoadGrammarFromResource(const WCHAR *pszModuleName,
const WCHAR *pszResourceName,
const WCHAR *pszResourceType,
WORD wLanguage,
void * pvOwnerCookie,
void * pvClientCookie,
BOOL fIsToplevelLoad,
ISpCFGGrammar **ppGrammarObject);
HRESULT InternalLoadGrammarFromMemory(const SPBINARYGRAMMAR * pData,
void * pvOwnerCookie,
void * pvClientCookie,
BOOL fIsToplevelLoad,
ISpCFGGrammar **ppGrammarObject,
WCHAR * pszGrammarName);
//
// Member data
//
public:
CCFGGrammar ** m_pGrammars;
ULONG m_cLoadsInProgress;
private:
ULONG m_cGrammarTableSize;
ULONG m_cGrammarsLoaded;
LANGID m_aLangIDs[SP_MAX_LANGIDS];
ULONG m_cLangIDs;
LANGID m_CurLangID;
ULONG m_cArcs;
ULONG m_cTotalRules;
ULONG m_cNonImportRules;
ULONG m_cTopLevelRules;
ULONG m_cNonterminals;
_ISpRecoMaster* m_pClient; //weak pointer to avoid circular reference
CStringBlob m_WordStringBlob; // hash table for all words; index is used to access m_paWordTable
ULONG m_ulLargestIndex; // used to detect addition vs. duplicate
CWordTableEntryBlob m_WordTableEntryBlob; // memory area for WORDTABLEENTRY to avoid malloc for each individual word
WORDTABLEENTRY * m_pWordTable; // array that contains word info (indexed by String index rather than offset)
ULONG m_cWordTableEntries;
CSpBasicList<CRuleStack> **m_RuleStackList;
CSpBasicList<CSearchNode> **m_SearchNodeList;
// Memory management lists.
CSpBasicList<CStateInfoListElement> m_mStateInfoList;
CSpBasicList<CParseNode> m_mParseNodeList;
CSpBasicList<CSearchNode> m_mSearchNodeList;
CSpBasicList<CRuleStack> m_mRuleStackList;
CSpBasicList<FIRSTPAIR> m_mSpPairList;
BOOL m_bIsCacheValid;
// Cache code.
inline HRESULT AllocatePair( FIRSTPAIR ** pNewPair );
inline void FreePair( FIRSTPAIR *pNewPair );
inline BOOL IsInCache( RUNTIMERULEENTRY * pRuleEntry, SPWORDHANDLE hWord );
inline HRESULT CacheWord( RUNTIMERULEENTRY * pRuleEntry, SPWORDHANDLE hWord );
inline HRESULT InvalidateCache( RUNTIMERULEENTRY * pRuleEntry );
HRESULT InvalidateCache( const CCFGGrammar *pGram );
HRESULT InvalidateCache(void);
HRESULT CreateCache(SPRULEHANDLE hRule);
HRESULT CreateFanout( CStateHandle hState, CRuleStack * pRuleStack );
};
//***************************************************************************
//************************************************************** t-lleav ****
inline HRESULT CCFGEngine::InvalidateCache( RUNTIMERULEENTRY * pRuleEntry )
{
FIRSTPAIR * pDelete;
FIRSTPAIR * pPair = pRuleEntry->pFirstList;
while( pPair )
{
pDelete = pPair;
pPair = pPair->m_pNext;
FreePair( pDelete );
}
pRuleEntry->pFirstList = NULL;
pRuleEntry->eCacheStatus = CACHE_VOID;
return S_OK;
}
//***************************************************************************
//************************************************************** t-lleav ****
inline HRESULT CCFGEngine::CacheWord( RUNTIMERULEENTRY * pRuleEntry, SPWORDHANDLE hWord )
{
//
// Do an sorted insert.
//
HRESULT hr = S_FALSE;
FIRSTPAIR * pPair = pRuleEntry->pFirstList;
FIRSTPAIR ** ppPair = &pRuleEntry->pFirstList;
// find the end of the list or a hword greater than the one
// that we are trying to insert.
while( pPair && hWord > pPair->hWord )
{
ppPair = &pPair->m_pNext;
pPair = pPair->m_pNext;
}
// Insert if at end of list or if the words are not equal.
if( !pPair || hWord != pPair->hWord )
{
hr = AllocatePair( ppPair );
if( SUCCEEDED( hr ) )
{
(*ppPair)->m_pNext = pPair;
(*ppPair)->hWord = hWord;
hr = S_OK;
m_bIsCacheValid = TRUE;
}
}
return hr;
}
//***************************************************************************
//************************************************************** t-lleav ****
inline BOOL CCFGEngine::IsInCache( RUNTIMERULEENTRY * pRuleEntry, SPWORDHANDLE hWord )
{
BOOL bFound = FALSE;
FIRSTPAIR * pPair = pRuleEntry->pFirstList;
while( pPair && hWord > pPair->hWord )
{
pPair = pPair->m_pNext;
}
if( pPair && hWord == pPair->hWord )
{
bFound = TRUE;
}
return bFound;
}
//***************************************************************************
//************************************************************** t-lleav ****
inline HRESULT CCFGEngine::AllocatePair( FIRSTPAIR ** pNewPair )
{
SPDBG_FUNC("CCFGEngine::AllocatePair");
HRESULT hr = S_OK;
hr = m_mSpPairList.RemoveFirstOrAllocateNew(pNewPair);
return hr;
}
//***************************************************************************
//************************************************************** t-lleav ****
inline void CCFGEngine::FreePair( FIRSTPAIR *pNewPair )
{
m_mSpPairList.AddNode( pNewPair );
}
//***************************************************************************
// used by GetNewWordList
DWORD _GetWordHashValue (const WCHAR * pszWords, const DWORD nLengthHash);
inline RUNTIMERULEENTRY * CCFGEngine::RuleOf(CRuleHandle rh)
{
CCFGGrammar * pGram = m_pGrammars[rh.GrammarId()];
return pGram->m_pRuleTable + rh.RuleIndex();
}
inline CRuleHandle::CRuleHandle(const CCFGGrammar * pGrammar, ULONG RuleIndex)
{
m_GrammarId = pGrammar->m_ulGrammarID;
m_RuleIndex = RuleIndex+1;
}
inline CStateHandle::CStateHandle(const CCFGGrammar * pGrammar, ULONG FirstArcIndex)
{
m_GrammarId = pGrammar->m_ulGrammarID;
m_FirstArcIndex = FirstArcIndex;
}
inline CTransitionId::CTransitionId(const CCFGGrammar * pGrammar, ULONG ArcIndex)
{
m_GrammarId = pGrammar->m_ulGrammarID;
m_ArcIndex = ArcIndex;
}
inline BOOL CCFGEngine::GetPropertiesOfTransition(CTransitionId hTrans, SPPHRASEPROPERTY *pProperty,
SPCFGSEMANTICTAG **ppTag, ULONG *pulGrammarId)
{
if (FAILED(ValidateHandle(hTrans)) || SP_IS_BAD_WRITE_PTR(pProperty))
{
return FALSE;
}
CCFGGrammar * pGram = m_pGrammars[hTrans.GrammarId()];
SPDBG_ASSERT(pGram);
// linear search
SPCFGSEMANTICTAG *pTag = pGram->m_Header.pSemanticTags;
for (ULONG i = 0; i < pGram->m_Header.cSemanticTags; i++, pTag++)
{
if (pTag->ArcIndex == hTrans.ArcIndex())
{
if (pTag->PropNameSymbolOffset)
{
pProperty->pszName = &pGram->m_Header.pszSymbols[pTag->PropNameSymbolOffset];
}
if (pTag->PropValueSymbolOffset)
{
pProperty->pszValue = &pGram->m_Header.pszSymbols[pTag->PropValueSymbolOffset];
}
pProperty->ulId = pTag->PropId;
if (FAILED(AssignSemanticValue(pTag, &pProperty->vValue)))
{
return false;
}
*ppTag = pTag;
*pulGrammarId = hTrans.GrammarId();
return true;
}
}
return false;
}
inline BOOL CCFGEngine::GetPropertiesOfRule(CRuleHandle rh, const WCHAR **ppszRuleName, ULONG *pulRuleId, BOOL *pfIsPropRule )
{
if (FAILED(ValidateHandle(rh)))
{
return FALSE;
}
CCFGGrammar * pGram = m_pGrammars[rh.GrammarId()];
SPDBG_ASSERT(pGram);
if (rh.RuleIndex() > pGram->m_Header.cRules)
{
return false;
}
SPCFGRULE *pRule = pGram->m_Header.pRules + rh.RuleIndex();
if (pulRuleId)
{
*pulRuleId = pRule->RuleId;
}
if (ppszRuleName)
{
*ppszRuleName = pRule->NameSymbolOffset ? pGram->m_Header.pszSymbols + pRule->NameSymbolOffset : NULL;
}
if (pfIsPropRule)
{
if (pRule->fImport)
{
RUNTIMERULEENTRY * pImpRule = pGram->m_pRuleTable + rh.RuleIndex();
CCFGGrammar * pRefGram = pImpRule->pRefGrammar;
*pfIsPropRule = pRefGram->m_Header.pRules[pImpRule->ulGrammarRuleIndex].fPropRule && (pRefGram->m_LoadedType == Object);
// new rule id because it's an import!
if (pulRuleId)
{
*pulRuleId = pRefGram->m_Header.pRules[pImpRule->ulGrammarRuleIndex].RuleId;
}
if (ppszRuleName)
{
*ppszRuleName = pRefGram->m_Header.pRules[pImpRule->ulGrammarRuleIndex].NameSymbolOffset ?
pRefGram->m_Header.pszSymbols + pRefGram->m_Header.pRules[pImpRule->ulGrammarRuleIndex].NameSymbolOffset : NULL;
}
}
else
{
//*pfIsPropRule = pRule->fPropRule;
*pfIsPropRule = pRule->fPropRule && (pGram->m_LoadedType == Object);
}
}
return true;
}
/****************************************************************************
* CCFGEngine::GetInterpreter *
*----------------------------*
* Description:
* Gets interpreter if one is associated with the grammar associated
* with this rule handle.
*
* Returns:
* TRUE if this is a property rule, FALSE otherwise.
* ppInterpreter pointer to the interpreter object
*
***************************************************************** philsch ***/
inline BOOL CCFGEngine::GetInterpreter(CRuleHandle rh, ISpCFGInterpreter **ppInterpreter)
{
CCFGGrammar * pGram = m_pGrammars[rh.GrammarId()];
SPDBG_ASSERT(pGram);
if (rh.RuleIndex() > pGram->m_Header.cRules)
{
return false;
}
SPCFGRULE *pRule = pGram->m_Header.pRules + rh.RuleIndex();
if (pRule->fImport)
{
RUNTIMERULEENTRY * pImpRule = pGram->m_pRuleTable + rh.RuleIndex();
CCFGGrammar * pRefGram = pImpRule->pRefGrammar;
*ppInterpreter = pRefGram->m_cpInterpreter;
}
else
{
*ppInterpreter = pGram->m_cpInterpreter;
}
(*ppInterpreter)->AddRef();
return true;
}
/****************************************************************************
* CCFGEngine::AllocateStateInfo *
*-------------------------------*
* Description:
* Allocates a CStateInfoListElement from the free list and then, if successful,
* initializes the state info structure.
*
* Returns:
* Standard hresults
*
********************************************************************* RAL ***/
inline HRESULT CCFGEngine::AllocateStateInfo(CStateHandle StateHandle, CStateInfoListElement ** ppNewState)
{
SPDBG_FUNC("CCFGEngine::AllocateStateInfo");
HRESULT hr = S_OK;
hr = m_mStateInfoList.RemoveFirstOrAllocateNew(ppNewState);
if (SUCCEEDED(hr))
{
hr = InternalGetStateInfo(StateHandle, *ppNewState, TRUE);
if (FAILED(hr))
{
m_mStateInfoList.AddNode(*ppNewState);
*ppNewState = NULL;
}
}
return hr;
}
/****************************************************************************
* CCFGEngine::AllocateRuleStack *
****************************************************************************/
inline HRESULT CCFGEngine::AllocateRuleStack( CRuleStack **pNewRuleStack )
{
SPDBG_FUNC("CCFGEngine::AllocateStateInfo");
HRESULT hr = S_OK;
hr = m_mRuleStackList.RemoveFirstOrAllocateNew( pNewRuleStack );
return hr;
}
/****************************************************************************
* CCFGEngine::AllocateSearchNode *
****************************************************************************/
inline HRESULT CCFGEngine::AllocateSearchNode( CSearchNode **pNewSearchNode )
{
SPDBG_FUNC("CCFGEngine::AllocateStateInfo");
HRESULT hr = S_OK;
hr = m_mSearchNodeList.RemoveFirstOrAllocateNew(pNewSearchNode);
return hr;
}
/****************************************************************************
* CCFGEngine::FreeRuleStack *
****************************************************************************/
inline void CCFGEngine::FreeRuleStack( CRuleStack * pRuleStack )
{
m_mRuleStackList.AddNode( pRuleStack );
}
/****************************************************************************
* CCFGEngine::FreeSearchNode *
****************************************************************************/
inline void CCFGEngine::FreeSearchNode( CSearchNode * pSearchNode )
{
m_mSearchNodeList.AddNode( pSearchNode );
}
/****************************************************************************
* CCFGEngine::FreeParseTree *
*-------------------------------*
* Description:
* Recursively deletes the nodes of a parse tree
*
* Returns:
* Standard hresults
*
********************************************************************* RAL ***/
inline HRESULT CCFGEngine::FreeParseTree( CParseNode * pParseNode )
{
SPDBG_ASSERT( pParseNode != NULL );
// Eliminate stack overflows in the case of recursive nodes.
CParseNode * pLeft = pParseNode->m_pLeft;
CParseNode * pRight = pParseNode->m_pRight;
pParseNode->m_pLeft = NULL;
pParseNode->m_pRight = NULL;
SPDBG_ASSERT( pLeft != pParseNode );
SPDBG_ASSERT( pRight != pParseNode );
if (pLeft)
{
FreeParseTree(pLeft);
}
if (pRight)
{
FreeParseTree(pRight);
}
m_mParseNodeList.AddNode(pParseNode);
return S_OK;
}
#endif //__CFGENGINE_H_