windows-nt/Source/XPSP1/NT/enduser/speech/sapi/sapi/backend.h
2020-09-26 16:20:57 +08:00

709 lines
23 KiB
C++

// Backend.h : Declaration of the CGramBackEnd
#ifndef __BACKEND_H__
#define __BACKEND_H__
#include "resource.h" // main symbols
#include "HandleTable.h"
class CGramBackEnd;
class CRule;
class CArc;
class CGramNode;
class CSemanticTag;
/////////////////////////////////////////////////////////////////////////////
// CGramBackEnd
class ATL_NO_VTABLE CGramBackEnd :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CGramBackEnd, &CLSID_SpGramCompBackend>,
// public ISpGramCompBackend,
public ISpGramCompBackendPrivate
{
public:
~CGramBackEnd()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_BACKEND)
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP(CGramBackEnd)
COM_INTERFACE_ENTRY(ISpGramCompBackendPrivate)
COM_INTERFACE_ENTRY(ISpGramCompBackend)
COM_INTERFACE_ENTRY(ISpGrammarBuilder)
END_COM_MAP()
HRESULT _InternalCreateRule(DWORD dwRuleId, const WCHAR * pszRuleName, BOOL fImport, CRule ** ppRule);
HRESULT FindRule(DWORD dwRuleId, const WCHAR * pszName, CRule ** ppRule);
HRESULT FinalConstruct();
void FinalRelease();
HRESULT ValidateAndTagRules();
HRESULT Reset();
HRESULT DynamicSave(DWORD dwReserved);
HRESULT AddSingleWordTransition(
SPSTATEHANDLE hFromState,
CGramNode * pSrcNode,
CGramNode * pDestNode,
const WCHAR * pszWord,
float flWeight,
CSemanticTag * pSemanticTag,
BOOL fUseDestNode,
CGramNode **ppActualDestNode,
CArc **ppArcUsed,
BOOL *pfPropertyMatched);
HRESULT PushProperty(CArc *pArc);
HRESULT GetNextWord(WCHAR *psz, const WCHAR * pszSep, WCHAR **ppszNextWord, ULONG *pulOffset );
HRESULT ConvertPronToId(WCHAR **ppStr);
// ISpGramCompBackendPrivate
public:
STDMETHODIMP GetRuleCount( long * pCount );
STDMETHODIMP GetHRuleFromIndex( ULONG index, SPSTATEHANDLE * pHRule );
STDMETHODIMP GetNameFromHRule( SPSTATEHANDLE HRule, WCHAR ** ppszRuleName );
STDMETHODIMP GetIdFromHRule( SPSTATEHANDLE HRule, long * pId );
STDMETHODIMP GetAttributesFromHRule( SPSTATEHANDLE HRule, SpeechRuleAttributes* pAttributes );
STDMETHODIMP GetTransitionCount( SPSTATEHANDLE hState, long * pCount);
STDMETHODIMP GetTransitionType( SPSTATEHANDLE hState, void * Cookie, VARIANT_BOOL *pfIsWord, ULONG * pulSpecialTransitions);
STDMETHODIMP GetTransitionText( SPSTATEHANDLE hState, void * Cookie, BSTR * Text);
STDMETHODIMP GetTransitionRule( SPSTATEHANDLE hState, void * Cookie, SPSTATEHANDLE * phRuleInitialState);
STDMETHODIMP GetTransitionWeight( SPSTATEHANDLE hState, void * Cookie, VARIANT * Weight);
STDMETHODIMP GetTransitionProperty( SPSTATEHANDLE hState, void * Cookie, SPTRANSITIONPROPERTY * pProperty);
STDMETHODIMP GetTransitionNextState( SPSTATEHANDLE hState, void * Cookie, SPSTATEHANDLE * phNextState);
STDMETHODIMP GetTransitionCookie( SPSTATEHANDLE hState, ULONG Index, void ** pCookie );
// ISpGramCompBackend
public:
STDMETHODIMP SetSaveObjects(IStream * pStream, ISpErrorLog * pErrorLog);
STDMETHODIMP InitFromBinaryGrammar(const SPBINARYGRAMMAR *pBinaryData);
// ISpBrammarBuilder
public:
STDMETHODIMP ResetGrammar(LANGID NewLanguage);
STDMETHODIMP GetRule(
const WCHAR * pszName, DWORD dwRuleId, DWORD dwAttributes,
BOOL fCreateIfNotExist, SPSTATEHANDLE * phInitialState);
STDMETHODIMP ClearRule(
SPSTATEHANDLE hState);
STDMETHODIMP CreateNewState(
SPSTATEHANDLE hExitingState, SPSTATEHANDLE * phNewState);
STDMETHODIMP AddWordTransition(
SPSTATEHANDLE hFromState,
SPSTATEHANDLE hToState,
const WCHAR * psz, // if NULL then SPEPSILONTRANS
const WCHAR * pszSeparators, // if NULL then psz contains single word
SPGRAMMARWORDTYPE eWordType,
float flWeight,
const SPPROPERTYINFO * pPropInfo);
STDMETHODIMP AddRuleTransition(
SPSTATEHANDLE hFromState,
SPSTATEHANDLE hToState,
SPSTATEHANDLE hRule, // must be initial state of rule
float flWeight,
const SPPROPERTYINFO * pPropInfo);
STDMETHODIMP AddResource(
SPSTATEHANDLE hRuleState,
const WCHAR * pszResourceName,
const WCHAR * pszResourceValue);
STDMETHODIMP Commit(DWORD dwReserved);
inline HRESULT LogError(HRESULT hr, UINT uID, const WCHAR * pszInsertString = NULL)
{
if (m_cpErrorLog)
{
USES_CONVERSION;
TCHAR sz[MAX_PATH]; // 260 chars max for error string.
if (::LoadString(_Module.GetModuleInstance(), uID, sz, sp_countof(sz)))
{
TCHAR szFormatted[MAX_PATH];
TCHAR * pszErrorText = sz;
if (pszInsertString)
{
::wsprintf(szFormatted, sz, W2T(LPWSTR(pszInsertString)));
pszErrorText = szFormatted;
}
m_cpErrorLog->AddError(0, hr, T2W(pszErrorText), NULL, 0);
}
}
return hr;
}
inline HRESULT LogError(HRESULT hr, UINT uID, const CRule * pRule);
inline HRESULT WriteStream(const void * pv, ULONG cb)
{
HRESULT hr = m_cpStream->Write(pv, cb, NULL);
if (FAILED(hr))
{
LogError(hr, IDS_WRITE_ERROR);
}
return hr;
}
inline bool isSeparator( WCHAR wch, const WCHAR *pszSep )
{
while( pszSep && *pszSep )
{
if( wch == *pszSep )
return true;
++pszSep;
}
return false;
}
inline bool fIsSpecialChar(WCHAR w)
{
return ((w == L'+') || (w == L'-') || (w == L'?'));
}
inline HRESULT RuleFromHandle(SPSTATEHANDLE hState, CRule ** ppRule);
template <class T>
HRESULT WriteStream(const T & obj)
{
return WriteStream(&obj, sizeof(obj));
}
public:
CStringBlob m_Words;
CStringBlob m_Symbols;
SPCFGHEADER * m_pInitHeader;
ULONG m_cResources;
GUID m_guid;
LANGID m_LangID;
float * m_pWeights;
BOOL m_fNeedWeightTable;
ULONG m_ulSpecialTransitions;
ULONG m_cImportedRules;
CSpHandleTable<CGramNode, SPSTATEHANDLE> m_StateHandleTable;
CSpBasicQueue<CRule, TRUE, TRUE> m_RuleList;
CComPtr<ISpErrorLog> m_cpErrorLog;
CComPtr<IStream> m_cpStream;
CComPtr<ISpPhoneConverter> m_cpPhoneConverter;
};
class CResource : public SPCFGRESOURCE
{
public:
CResource * m_pNext;
CGramBackEnd * m_pParent;
CResource(CGramBackEnd * pParent) : m_pParent(pParent) {}
HRESULT Init(const SPCFGHEADER * pHeader, const SPCFGRESOURCE * pResource)
{
HRESULT hr = S_OK;
if (pResource && m_pParent)
{
memcpy(this, pResource, sizeof(*pResource));
hr = m_pParent->m_Symbols.Add(&pHeader->pszSymbols[pResource->ResourceNameSymbolOffset], &ResourceNameSymbolOffset);
if (SUCCEEDED(hr))
{
hr = m_pParent->m_Symbols.Add(&pHeader->pszSymbols[pResource->ResourceValueSymbolOffset], &ResourceValueSymbolOffset);
}
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
const WCHAR * Name() const
{
return m_pParent->m_Symbols.String(ResourceNameSymbolOffset);
}
operator ==(const WCHAR * psz) const
{
return (wcscmp(Name(), psz) == 0);
}
static LONG Compare(const CResource * pElem1, const CResource * pElem2)
{
return wcscmp(pElem1->Name(), pElem2->Name());
}
};
typedef struct SPRULEREFLIST
{
CRule *pRule;
struct SPRULEREFLIST *m_pNext;
} SPRULEREFLIST;
typedef struct SPRULEIDENTIFIER
{
DWORD RuleId;
const WCHAR * pszRuleName;
} SPRULEIDENTIFIER;
class CRule : public SPCFGRULE
{
public:
// NOTE: Do *NOT* delete this member on destruction. It will be cleaned up
// by the compiler since the node will be inserted into the node handle table
// of the CGramBackEnd object when it is created.
CGramNode * m_pFirstNode;
SPSTATEHANDLE m_hInitialState;
CRule * m_pNext;
bool m_fHasExitPath;
bool m_fCheckingForExitPath;
bool m_fHasDynamicRef;
ULONG m_ulSerializeIndex;
ULONG m_ulOriginalBinarySerialIndex;
ULONG m_cNodes;
ULONG m_ulSpecialTransitions;
CSpBasicQueue<SPRULEREFLIST> m_ListOfReferencedRules;
bool m_fCheckedAllRuleReferences;
bool m_fStaticRule; // this is used to refer to a static rule from a dynamic rule
CGramBackEnd * m_pParent;
CSpBasicQueue<CResource> m_ResourceList;
CRule(CGramBackEnd * pParent, const WCHAR * pszRuleName, DWORD dwRuleId, DWORD dwAttributes, HRESULT * phr);
HRESULT CheckForExitPath();
// Structure used by CheckForDynmaicRef
struct CHECKDYNRULESTACK
{
CHECKDYNRULESTACK * m_pNext;
CRule * m_pRule;
SPRULEREFLIST * m_pNextRuleRef;
};
bool CheckForDynamicRef(CHECKDYNRULESTACK * pStack = NULL);
const WCHAR * Name() const
{
return m_pParent->m_Symbols.String(NameSymbolOffset);
}
operator ==(const WCHAR * pszRuleName)
{
const WCHAR * pszThisName = Name();
return (pszThisName && (wcscmp(pszThisName, pszRuleName) == 0));
}
operator ==(DWORD dw)
{
return (RuleId && (RuleId == dw));
}
operator ==(SPRULEIDENTIFIER ri)
{
const WCHAR * pszThisName = Name();
return ((ri.RuleId && (ri.RuleId == RuleId)) || (pszThisName && ri.pszRuleName && wcscmp(pszThisName, ri.pszRuleName) == 0));
}
//
// Place all imports at the start of the rule table and dynamic rules at the end
//
static LONG Compare(const CRule * pElem1, const CRule * pElem2)
{
if (pElem1->fImport)
{
return (pElem2->fImport) ? 0 : -1;
}
else if (pElem1->fDynamic)
{
return (pElem2->fDynamic) ? 0 : 1;
}
else
{
return (pElem2->fImport) ? 1 : (pElem2->fDynamic) ? -1 : 0;
}
}
HRESULT Serialize();
HRESULT SerializeResources();
HRESULT Validate();
};
class CSemanticTag : public SPCFGSEMANTICTAG
{
public:
CSemanticTag() : m_pStartArc(NULL), m_pEndArc(NULL) {};
CSemanticTag(CSemanticTag *pTag) : m_pStartArc(NULL), m_pEndArc(NULL)
{
if (pTag)
{
memcpy(this, pTag, sizeof(CSemanticTag));
}
}
public:
HRESULT Init(CGramBackEnd * pBackEnd, const SPPROPERTYINFO * pPropInfo);
HRESULT Init(CGramBackEnd * pBackEnd, const SPCFGHEADER * pHeader, CArc ** apArcTable, const SPCFGSEMANTICTAG *pSemTag);
BOOL operator==(CSemanticTag pTag)
{
return (memcmp(static_cast<SPCFGSEMANTICTAG*>(this), static_cast<SPCFGSEMANTICTAG*>(&pTag), sizeof(SPCFGSEMANTICTAG)) == 0);
}
public:
CArc * m_pStartArc;
CArc * m_pEndArc;
};
class CArc
{
public:
CArc * m_pNext;
CSemanticTag * m_pSemanticTag; // If non-null then has semantic tag associated with this
CArc * m_pNextArcForSemanticTag; // If non-null then semantic tag can move to here
CGramNode * m_pNextState;
ULONG m_ulIndexOfWord; // Either word index or pRule but not both
ULONG m_ulCharOffsetOfWord; // Offset into string blob
bool m_fOptional;
float m_flWeight;
char m_RequiredConfidence;
CRule * m_pRuleRef;
ULONG m_SpecialTransitionIndex; // If != 0 then transition to dictation, text buffer, or wildcard
ULONG m_ulSerializationIndex;
CArc();
~CArc();
HRESULT Init(CGramNode * pSrcNode, CGramNode * pDestNode,
const WCHAR * pszWord, CRule * pRuleRef,
CSemanticTag * pSemanticTag,
float flWeight,
bool fOptional,
char ConfRequired,
SPSTATEHANDLE hSpecialRuleRef);
HRESULT Init2(CGramNode * pSrcNode, CGramNode * pDestNode,
const ULONG ulCharOffsetOfWord,
const ULONG ulIndexOfWord,
CSemanticTag * pSemanticTag,
float flWeight,
bool fOptional,
char ConfRequired,
const ULONG hSpecialTransitionIndex);
HRESULT SerializeArcData(CGramBackEnd * pBackend, BOOL fIsEpsilon, ULONG ulArcIndex, float *pWeight);
HRESULT SerializeSemanticData(CGramBackEnd * pBackend, ULONG ArcDataIndex);
LONG SortRank() const
{
if (m_pRuleRef) return 1; // It's a rule - Place 2nd in list
if (m_ulIndexOfWord) return 2;// It's a word - Place last in list
if (m_SpecialTransitionIndex) return 3; // It's a special transition (dictation, text buffer, or wildcard)
return 0; // It's an epsilon -- We're first
}
static LONG Compare(const CArc * pElem1, const CArc * pElem2)
{
return pElem1->SortRank() - pElem2->SortRank();
}
bool HasSemanticTag()
{
return (m_pSemanticTag != NULL);
}
};
class CGramNode
{
enum RecurFlag // Flags used for recurive validation methods
{
RF_CHECKED_EPSILON = (1 << 0),
RF_CHECKED_EXIT_PATH = (1 << 1),
RF_CHECKED_LEFT_RECURSION = (1 << 2),
RF_IN_LEFT_RECUR_CHECK = (1 << 3)
};
public:
CGramNode(CRule * pRule)
{
m_pRule = pRule;
m_pParent = pRule->m_pParent;
m_RecurTestFlags = 0;
m_ulSerializeIndex= 0;
m_cArcs = 0;
m_cEpsilonArcs = 0;
m_hState = 0;
}
operator ==(const CRule * pRule)
{
return (pRule == m_pRule);
}
ULONG NumArcs()
{
return m_cArcs;
}
ULONG NumSemanticTags()
{
ULONG c = 0;
for (CArc * pArc = m_ArcList.GetHead(); pArc; pArc = pArc->m_pNext)
{
if (pArc->HasSemanticTag())
{
c += (pArc->m_fOptional == TRUE) ? 2 : 1;
}
}
return c;
}
HRESULT SerializeNodeEntries(float *pWeights, ULONG *pArcOffset, ULONG *pOffset)
{
HRESULT hr = S_OK;
ULONG i = 0;
CArc *pEpsArc = m_ArcList.GetHead();
for (; SUCCEEDED(hr) && (i < m_cEpsilonArcs); i++)
{
while(!pEpsArc->m_fOptional)
{
pEpsArc = pEpsArc->m_pNext;
SPDBG_ASSERT(pEpsArc != NULL); // we can't run out of arcs before we've found all espilons!
}
hr = pEpsArc->SerializeArcData(m_pParent, TRUE, (*pArcOffset)++, pWeights ? &pWeights[(*pOffset)++] : NULL);
pEpsArc = pEpsArc->m_pNext;
}
for (CArc * pArc = m_ArcList.GetHead(); pArc && SUCCEEDED(hr); pArc = pArc->m_pNext)
{
hr = pArc->SerializeArcData(m_pParent, FALSE, (*pArcOffset)++, pWeights ? &pWeights[(*pOffset)++] : NULL);
}
return hr;
}
HRESULT AddSingleWordTransition(const WCHAR * pszWord, const WCHAR * pszPropName, DWORD dwPropId,
const WCHAR * pszPropValue, ULONGLONG ullPropValue, BOOL fHasValue,
float flWeight, CGramNode * pDestState,
bool fOptional, BOOL fLowConfRequired, BOOL fHighConfRequired);
CArc * CGramNode::FindEqualWordPropertyTransition(
const WCHAR * psz,
const WCHAR * pszPropName,
DWORD dwPropId,
const WCHAR * pszPropStringValue,
ULONGLONG ullPropValue,
BOOL fHasValue,
float flWeight,
bool fOptional);
CArc * CGramNode::FindEqualRuleTransition(
const CGramNode * pDestNode,
const CRule * pRuleToTransitionTo,
SPSTATEHANDLE hSpecialRuleTrans,
float flWeight);
CArc * CGramNode::FindEqualWordTransition(
const WCHAR * psz,
float flWeight,
bool fOptional);
CArc * CGramNode::FindEqualEpsilonArc();
HRESULT SerializeSemanticTags()
{
HRESULT hr = S_OK;
ULONG i = m_ulSerializeIndex;
for (CArc * pArc = m_ArcList.GetHead(); pArc && SUCCEEDED(hr); pArc = pArc->m_pNext)
{
if (pArc->m_fOptional)
{
hr = pArc->SerializeSemanticData(m_pParent, i);
i++;
}
}
for (pArc = m_ArcList.GetHead(); pArc && SUCCEEDED(hr); pArc = pArc->m_pNext, i++)
{
hr = pArc->SerializeSemanticData(m_pParent, i);
}
return hr;
}
HRESULT CheckDynamicRefs(bool * pfHasDynamicRefs)
{
*pfHasDynamicRefs = TRUE;
return S_OK;
}
HRESULT CheckEpsilonRule()
{
HRESULT hr = S_OK;
if ((m_RecurTestFlags & RF_CHECKED_EPSILON) == 0)
{
m_RecurTestFlags |= RF_CHECKED_EPSILON;
for (CArc * pArc = m_ArcList.GetHead();
pArc && SUCCEEDED(hr) && pArc->m_pRuleRef == NULL &&
!pArc->m_SpecialTransitionIndex &&
(pArc->m_ulIndexOfWord == 0 || pArc->m_fOptional);
pArc = pArc->m_pNext)
{
if (pArc->m_pNextState)
{
hr = pArc->m_pNextState->CheckEpsilonRule();
}
else
{
hr = m_pParent->LogError(SPERR_ALL_WORDS_OPTIONAL, IDS_EPSILON_RULE, m_pRule);
}
}
}
return hr;
}
HRESULT CheckExitPath(ULONG cWords)
{
HRESULT hr = S_OK;
if (((m_RecurTestFlags & RF_CHECKED_EXIT_PATH) == 0) && (cWords < 256))
{
m_RecurTestFlags |= RF_CHECKED_EXIT_PATH;
for (CArc * pArc = m_ArcList.GetHead();
pArc && SUCCEEDED(hr) && (!m_pRule->m_fHasExitPath);
pArc = pArc->m_pNext)
{
if (pArc->m_pRuleRef)
{
hr = pArc->m_pRuleRef->CheckForExitPath();
if (SUCCEEDED(hr) && pArc->m_pRuleRef->m_fHasExitPath)
{
if (pArc->m_pNextState == NULL)
{
m_pRule->m_fHasExitPath = true;
}
else
{
hr = pArc->m_pNextState->CheckExitPath(pArc->m_pRuleRef ? cWords : ++cWords);
}
}
}
else
{
if (pArc->m_pNextState == NULL)
{
m_pRule->m_fHasExitPath = true;
}
else
{
hr = pArc->m_pNextState->CheckExitPath(pArc->m_pRuleRef ? cWords : ++cWords);
}
}
}
}
return hr;
}
HRESULT CheckLeftRecursion()
{
HRESULT hr = S_OK;
if (m_RecurTestFlags & RF_IN_LEFT_RECUR_CHECK)
{
hr = m_pParent->LogError(SPERR_CIRCULAR_RULE_REF, IDS_CIRCULAR_REF, m_pRule);
}
else
{
if ((m_RecurTestFlags & RF_CHECKED_LEFT_RECURSION) == 0)
{
m_RecurTestFlags |= RF_CHECKED_LEFT_RECURSION | RF_IN_LEFT_RECUR_CHECK;
// Only need to look at epsilon and rule references
for (CArc * pArc = m_ArcList.GetHead();
pArc && SUCCEEDED(hr) && pArc->m_ulIndexOfWord == 0;
pArc = pArc->m_pNext)
{
if (pArc->m_pRuleRef)
{
hr = pArc->m_pRuleRef->m_pFirstNode->CheckLeftRecursion();
}
else // It's a epsilon
{
if (pArc->m_pNextState && !pArc->m_SpecialTransitionIndex)
{
hr = pArc->m_pNextState->CheckLeftRecursion();
}
}
}
m_RecurTestFlags &= (~RF_IN_LEFT_RECUR_CHECK);
}
}
return hr;
}
void Reset()
{
m_ArcList.Purge();
m_cArcs = 0;
m_cEpsilonArcs = 0;
}
static LONG Compare(const CGramNode * pNode1, const CGramNode * pNode2)
{
return (pNode1->m_pRule->m_ulSerializeIndex - pNode2->m_pRule->m_ulSerializeIndex);
}
public:
CGramBackEnd * m_pParent;
CRule * m_pRule;
CGramNode * m_pNext;
CSpBasicQueue<CArc, TRUE, TRUE> m_ArcList;
ULONG m_ulSerializeIndex;
DWORD m_RecurTestFlags; // Flags used by recursive algorithms
ULONG m_cEpsilonArcs;
ULONG m_cArcs;
SPSTATEHANDLE m_hState;
};
inline HRESULT CGramBackEnd::LogError(HRESULT hr, UINT uID, const CRule * pRule)
{
if (m_cpErrorLog)
{
if (pRule->Name())
{
LogError(hr, uID, pRule->Name());
}
else
{
USES_CONVERSION;
TCHAR sz[40];
wsprintf(sz, _T("ID = %ld"), pRule->RuleId);
LogError(hr, uID, T2W(sz));
}
}
return hr;
}
/****************************************************************************
* ValidateSemanticVariantType *
*-----------------------------*
* Description:
*
* Returns:
*
********************************************************************* RAL ***/
inline HRESULT ValidateSemanticVariantType(VARTYPE Type)
{
switch (Type)
{
case VT_EMPTY:
case VT_I2:
case VT_I4:
case VT_R4:
case VT_R8:
case VT_CY:
case VT_DATE:
case VT_BOOL:
case VT_I1:
case VT_UI2:
case VT_UI4:
case VT_INT:
case VT_UINT:
case VT_BYREF | VT_I2:
case VT_BYREF | VT_I4:
case VT_BYREF | VT_R4:
case VT_BYREF | VT_R8:
case VT_BYREF | VT_CY:
case VT_BYREF | VT_DATE:
case VT_BYREF | VT_BOOL:
case VT_BYREF | VT_I1:
case VT_BYREF | VT_UI2:
case VT_BYREF | VT_UI4:
case VT_BYREF | VT_INT:
case VT_BYREF | VT_UINT:
case VT_BYREF | VT_VOID:
return S_OK;
default:
SPDBG_REPORT_ON_FAIL( E_INVALIDARG );
return E_INVALIDARG;
}
}
#endif //__GRAMMAR_H_