Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
7075 lines (5675 sloc) 192 KB
// -----------------------------------------------------------------------------
// File GRAMMAR_ENGINE_API.CPP
//
// (c) by Koziev Elijah www.wordysoft.com all rights reserved
//
// Help: http://www.solarix.ru/for_developers/api/grammar-engine-api.shtml
//
// You must not eliminate, delete or supress these copyright strings
// from the file!
//
// Content:
//
// This file contains some basic API functions which can be used by other
// programs under Win32 to perform operations with words and sentences (analysis
// and synthesys).
//
// API для доступа к грамматическому движку в виде обычной DLL Win32. Функции
// используются для выполнения операций со словами и предложениями.
//
// Описание API: http://www.solarix.ru/for_developers/api/grammar-engine-api.shtml
//
//
// 24.05.2007 - добавлена sol_TranslateToBases.
//
// 19.06.2007 - добавлено использование загружаемого внешнего стеммера
// в sol_Stemmer.
//
// 17.09.2007 - исправлена ошибка в sol_GetAdjectiveForm с некорректной
// перекодировкой результата.
//
// 30.09.2007 - добавлена sol_SeekThesaurus для работы с тезаурусом.
// 01.10.2007 - добавлена sol_ProjectMisspelledWord для нечеткой проекции.
// 06.12.2007 - добавлен API генератора фраз, включается макросом GM_PHRASE_GENERATOR
// 30.06.2008 - добавлен API OCR
// 11.07.2008 - поправлен учет флага Allow_Dynforms в sol_SyntaxAnalysis, в эту
// же процедуру добавлен аргумент Allow_Unknown
// 17.07.2008 - перевод API call convention на stdcall
// 17.07.2008 - изменена сигнатура sol_GeneratePhrase2, она возвращает число
// символов, записанных в буфер.
// 25.07.2008 - добавлены sol_FindClass, sol_FindEnum, sol_FindEnumState, sol_EnumInClass
// 27.07.2008 - добавлена sol_SeekNGrams
// 29.07.2008 - в процедуре sol_SeekThesaurus убран оверхед для типичных случаев
// за счет предварительного формирования списков допустимых связок
// и сохранения их в объекте движка
// 29.07.2008 - переработ алгоритм работы sol_SeekThesaurus для глаголов, теперь
// он автоматически делает поиск также для инфинитива.
// 01.08.2008 - исправлен тип возвращаемого значения в sol_LoadDictionary...,
// все возвращаемые bool'ы переделаны на int'ы
// 02.08.2008 - код sol_SeekThesaurus перенесен в LexiconAutomat, чтобы
// был унифицированный способ получать связанные статьи и в
// поисковой системе.
// 20.11.2008 - добавлена sol_MatchNGrams
// 02.12.2008 - добавлена sol_Syllabs - разбивка слова на слоги
// 14.02.2009 - добавлены API токенизатора и SentenceBroker'а
// 25.02.2009 - добавлена sol_PreloadNGramsCaches
// 11.03.2009 - добавлены sol_FindLanguage и sol_HasLanguage
// 08.04.2009 - из API удалена sol_PreloadCaches
// 20.07.2009 - переработка API доступа к базе N-грамм: убрана sol_SeekNGrams,
// добавлены sol_Seek2Grams, sol_Seek3Grams, sol_Seek4Grams,
// sol_Seek5Grams
// 31.07.2010 - добавлена sol_ProcessPhraseEntry
// 06.08.2010 - добавлены sol_GetPhraseLanguage и sol_GetPhraseClass
// 21.08.2010 - добавлена sol_SetLinkFlags
// 29.08.2010 - добавлена sol_AddWord
// 04.09.2010 - в ряд функций добвлена поддержка сохранения подробностей ошибки
// в переменной, содержимое которой возвращается через sol_GetError.
// 29.09.2010 - добавлены sol_SyntaxAnalysis8, sol_MorphologyAnalysis8,
// sol_LoadDictionary8, sol_CreateGrammarEngine8
// 23.12.2010 - добавлены utf8-версии функций sol_Seek?Grams
// 27.03.2011 - добавлена critical section для Errors API
// 03.04.2011 - в вызовы ProjectWord добавлен параметр - ID текущего языка,
// так как теперь правила проекции могут быть привязаны к языку.
// 19.04.2011 - функции sol_ListLinksTxt и sol_ListEntries в демо-версии отключены
// 22.06.2011 - добавлена sol_GetLeafLinkType
// 21.08.2011 - добавлена sol_ListPartsOfSpeech
// 26.09.2011 - изменения в сигнатуре функций sol_MorphologyAnalysis и
// sol_SyntaxAnalysis, по-другому задаются управляющие флаги.
// 27.09.2011 - добавлен режим ленивой загрузки лексикона.
// 09.01.2012 - переработка sol_Syllabs в связи с переделкой исполняющего движка,
// в частности в сигнатуру добавлен аргумент LanguageID.
// 29.06.2012 - из API убраны методы, связанные с переводом.
// 28.07.2012 - добавлен флаг SOL_GREN_ENABLE_RECONSTRUCTION для sol_MorphologyAnalysis
// и sol_SyntaxAnalysis, включающий реконструкцию токенов.
// 07.01.2013 - в sol_MorphologyAnalysis и sol_SyntaxAnalysis добавлено управление
// морфологической вероятностной моделью языка (флаг SOL_GREN_MODEL)
// 12.02.2013 - в sol_MorphologyAnalysis и sol_SyntaxAnalysis изменена семантика
// параметра таймаута - теперь старшие 10 битов задают максимальное
// число проверяемых при нисходящем разборе альтернативных путей, а
// младшие 22 бита - макс. таймаут в миллисекундах.
// 28.03.2016 - добавлена начальная реализация функции приведения словосочетаний
// к нормальной форме.
// 04.04.2018 - рефакторинг кода с использованием фич C++11
// -----------------------------------------------------------------------------
//
// CD->29.04.2007
// LC->21.04.2018
// --------------
#include <lem/config.h>
#if defined DLL_EXPORTS
#include <boost/bind.hpp>
#include <lem/startup.h>
#include <lem/xml_parser.h>
#include <lem/solarix/res_pack.h>
#include <lem/solarix/dictionary.h>
#include <lem/solarix/sg_autom.h>
#include <lem/solarix/la_autom.h>
#include <lem/solarix/gg_autom.h>
#include <lem/solarix/aa_autom.h>
#include <lem/solarix/version.h>
#include <lem/solarix/tree_node.h>
#include <lem/solarix/sentencebroker.h>
#include <lem/system_config.h>
#include <lem/solarix/sg_only_main_translations_tagfilter.h>
#include <lem/solarix/sg_tag_or_null_tagfilter.h>
#include <lem/solarix/print_variator.h>
#include <lem/solarix/ThesaurusNotesProcessor.h>
#include <lem/solarix/ThesaurusTagDefs.h>
#include <lem/solarix/LexiconStorage.h>
#include <lem/solarix/ThesaurusStorage.h>
#include <lem/solarix/WordLinkEnumerator.h>
#include <lem/solarix/PhraseEntries.h>
#include <lem/solarix/PhraseEnumerator.h>
#include <lem/solarix/WordEntries.h>
#include <lem/solarix/Thesaurus.h>
#include <lem/solarix/WordEntry.h>
#include <lem/solarix/ClassList.h>
#include <lem/solarix/ClassEnumerator.h>
#include <lem/solarix/PartOfSpeech.h>
#include <lem/solarix/CoordList.h>
#include <lem/solarix/TransactionGuard.h>
#include <lem/solarix/WordEntryEnumerator.h>
#include <lem/solarix/Paradigma.h>
#include <lem/solarix/dsa_main.h>
#include <lem/solarix/casing_coder.h>
#include <lem/logfile.h>
#include <lem/sqlite_helpers.h>
#include <lem/solarix/res_pack.h>
#include <lem/solarix/WrittenTextAnalysisSession.h>
#include <lem/solarix/PM_FunctionLoader.h>
#include <lem/solarix/ElapsedTimeConstraint.h>
#include <lem/solarix/solarix_grammar_engine.h>
#include "GrammarEngineHolder.h"
#define HFLEXIONTABLE void*
#define HFLEXIONS void*
#if defined LEM_THREADING
#define CATCH_API(hengine) \
catch( const lem::E_BaseException &x ) \
{ \
lem::Process::CritSecLocker locker( & HandleEngine(hengine)->cs_error ); \
HandleEngine(hengine)->error = x.what(); \
} \
catch( const std::exception &y ) \
{ \
lem::Process::CritSecLocker locker( & HandleEngine(hengine)->cs_error ); \
HandleEngine(hengine)->error = lem::to_unicode(y.what()); \
} \
catch(...) \
{ \
lem::Process::CritSecLocker locker( & HandleEngine(hengine)->cs_error ); \
HandleEngine(hengine)->error = L"Error"; \
}
#else
#define CATCH_API(hengine) \
catch( const lem::E_BaseException &x ) \
{ \
HandleEngine(hengine)->error = x.what(); \
} \
catch( const std::exception &y ) \
{ \
HandleEngine(hengine)->error = lem::to_unicode(y.what()); \
} \
catch(...) \
{ \
HandleEngine(hengine)->error = L"Unhandled exception"; \
}
#endif
// Ограничение для ознакомительной версии - глобальный лок при вызове некоторых важных функций.
// Это искусственное ограничение, в коммерческой версии его разумеется нет.
#if defined SOLARIX_DEMO && defined LEM_THREADS
#define DEMO_SINGLE_THREAD(_hEngine) lem::Process::CritSecLocker guard( & HandleEngine(_hEngine)->cs );
#else
#define DEMO_SINGLE_THREAD(_hEngine)
#endif
// Returns the number of items in array of integers
// http://www.solarix.ru/api/en/sol_CountInts.shtml
// http://www.solarix.ru/api/ru/sol_CountInts.shtml
GREN_API(int) sol_CountInts(HGREN_INTARRAY h)
{
if (h == nullptr)
return -1;
return CastSizeToInt(((lem::MCollect<int>*)h)->size());
}
// http://www.solarix.ru/api/en/sol_GetInt.shtml
// http://www.solarix.ru/api/ru/sol_GetInt.shtml
GREN_API(int) sol_GetInt(HGREN_INTARRAY h, int i)
{
if (h == nullptr)
return -1;
try
{
return ((lem::MCollect<int>*)h)->get(i);
}
catch (...)
{
return -1;
}
}
// http://www.solarix.ru/api/en/sol_DeleteInts.shtml
// http://www.solarix.ru/api/ru/sol_DeleteInts.shtml
GREN_API(void) sol_DeleteInts(HGREN_INTARRAY h)
{
delete h;
}
// ******************************************************************************
// Returns the max lexem length.
// http://www.solarix.ru/api/en/sol_MaxLexemLen.shtml
// Entry names and wordforms can be this length maximum (including C-string '\0')
//
// Возвращает максимальную длину лексемы.
// Имена словарных статей и словоформ могут иметь такую длину максимум (с учетом
// завершающего 0).
// ******************************************************************************
GREN_API(int) sol_MaxLexemLen(HGREN hEngine)
{
return LEM_CSTRING_LEN + 1;
}
// Maximum length of lexemes, part of speech names and so on, in utf-8.
GREN_API(int) sol_MaxLexemLen8(HGREN hEngine)
{
return LEM_CSTRING_LEN * 6 + 1;
}
// ************************************************************************
// Return version info
// Если Major, Minor и Build не nullptr, то через них возвращаются компоненты
// полной версии релиза.
//
// Возвращает 0 для DEMO, 1 для PRO
// ************************************************************************
GREN_API(int) sol_GetVersion(HGREN hEngine, int *Major, int *Minor, int *Build)
{
Solarix::Version_Info ver;
if (Major != nullptr) *Major = ver.major;
if (Minor != nullptr) *Minor = ver.minor;
if (Build != nullptr) *Build = ver.build;
#if defined SOLARIX_DEMO
return 0;
#else
return 1;
#endif
}
// ****************************************************************************
// Быстрый поиск слова. Возвращается некий числовой код, одинаковый для всех
// словоформ в рамках одной словарной статьи, либо -1 если поиск не удался.
// ****************************************************************************
GREN_API(int) sol_SeekWord(HGREN hEngine, const wchar_t *word, int Allow_Dynforms)
{
if (!hEngine || word == nullptr)
return -1;
try
{
if (HandleEngine(hEngine)->seeker.IsNull())
{
lem::MCollect<Solarix::Word_Coord> found_list;
lem::UCString w(word);
HandleEngine(hEngine)->dict->GetLexAuto().ProjectWord(w, found_list, UNKNOWN);
if (!found_list.empty())
return found_list.front().GetEntry();
else
return UNKNOWN;
}
else
{
// Создание объекта на стеке. Плохо.
lem::UCString w(word);
return HandleEngine(hEngine)->seeker->Find(w, Allow_Dynforms == 1);
}
}
CATCH_API(hEngine)
return -2;
}
// Releases the memory allocated by the engine and returned to the client code.
GREN_API(int) sol_Free(HGREN hEngine, void *ptr)
{
try
{
free(ptr);
return 0;
}
CATCH_API(hEngine)
return -1;
}
GREN_API(int) sol_CountStrings(HGREN_STR hStrings)
{
if (hStrings == nullptr)
return 0;
try
{
return CastSizeToInt(HandleStrList(hStrings)->list.size());
}
catch (...)
{
return -2;
}
}
GREN_API(int) sol_GetStrings(HGREN_STR hStrings, wchar_t **Res)
{
if (hStrings == nullptr || Res == nullptr)
return -1;
try
{
for (lem::Container::size_type i = 0; i < HandleStrList(hStrings)->list.size(); i++)
wcscpy(Res[i], HandleStrList(hStrings)->list[i].c_str());
return 0;
}
catch (...)
{
return -2;
}
}
GREN_API(int) sol_GetStringLen(HGREN_STR hStrings, int i)
{
if (hStrings == nullptr)
return -1;
try
{
return HandleStrList(hStrings)->list[i].length();
}
catch (...)
{
return -2;
}
}
GREN_API(int) sol_GetStringW(HGREN_STR hStrings, int i, wchar_t *Res)
{
if (hStrings == nullptr || Res == nullptr)
return -1;
try
{
*Res = 0;
wcscpy(Res, HandleStrList(hStrings)->list[i].c_str());
return 0;
}
catch (...)
{
return -2;
}
}
GREN_API(int) sol_GetStringA(HGREN_STR hStrings, int i, char *Res)
{
if (hStrings == nullptr || Res == nullptr)
return -1;
try
{
*Res = 0;
strcpy(Res, lem::to_ascii(HandleStrList(hStrings)->list[i].c_str()).c_str());
return 0;
}
catch (...)
{
return -2;
}
}
GREN_API(int) sol_GetString8(HGREN_STR hStrings, int i, char *ResUtf8)
{
if (hStrings == nullptr || ResUtf8 == nullptr)
return -1;
try
{
*ResUtf8 = 0;
strcpy(ResUtf8, lem::to_utf8(HandleStrList(hStrings)->list[i].c_str()).c_str());
return 0;
}
catch (...)
{
return -2;
}
}
GREN_API(int) sol_DeleteStrings(HGREN_STR hStrings)
{
try
{
if (hStrings != nullptr)
delete HandleStrList(hStrings);
return 0;
}
catch (...)
{
return -2;
}
}
#define ENGINE HandleEngine(hEngine)
using namespace lem;
using namespace Solarix;
#if defined SOLARIX_GRAMMAR_ENGINE || defined SOLARIX_SYNONYMIZER_ENGINE
// ***********************************************************************
// Grammar engine is completely removed from memory.
// Словарь полностью удаляется из оперативной памяти.
//
// http://www.solarix.ru/api/en/sol_DeleteGrammarEngine.shtml
// ***********************************************************************
GREN_API(int) sol_DeleteGrammarEngine(HGREN hEngine)
{
try
{
if (hEngine != nullptr)
{
GrammarEngineHolder * ptr = (GrammarEngineHolder*)hEngine;
delete ptr;
}
return 1;
}
CATCH_API(hEngine)
return 0;
}
#endif
#if defined SOLARIX_GRAMMAR_ENGINE
GREN_API(int) sol_LoadDictionaryExW(HGREN hEngine, const wchar_t *Filename, int Flags);
// Initializes a new instance of the grammatical dictionary in memory.
// http://www.solarix.ru/api/en/sol_CreateGrammarEngine.shtml
GREN_API(HGREN) sol_CreateGrammarEngineExW(const wchar_t *DictionaryXml, int Flags)
{
lem::Init();
int rc = 0;
#if defined LEM_THREADS
if ((Flags & SOL_CREATE_GREN_DONT_INIT_SQLITE) != SOL_CREATE_GREN_DONT_INIT_SQLITE)
{
// Инициализируем SQlite в безопасном многопоточном режиме, чтобы
// можно было одновременно из нескольких нитей работать с одним соединением.
// Если такое поведение нежелательно, то с помощью флага можно отключить инициализацию.
static bool SQLiteInitialized = false;
if (!SQLiteInitialized)
{
SQLiteInitialized = true;
if (!lem::sqlite_init_serialized_mode())
return nullptr;
}
}
#endif
HGREN hEngine = new GrammarEngineHolder();
if (!lem::lem_is_empty(DictionaryXml))
{
rc = sol_LoadDictionaryExW(hEngine, DictionaryXml, Flags);
if (rc != 1)
goto Failed;
}
else
{
rc = 0;
}
/*
// --------- DEBUG
wchar_t p[ lem::Path::MaxLen ];
memset( p, 0, sizeof(p) );
GetModuleFileNameW( GetModuleHandleW(L"solarix_grammar_engine.dll"), p, lem::Path::MaxLen );
MessageBoxW( nullptr, p, L"DEBUG-1", MB_OK );
// ---------------
*/
return hEngine;
Failed:;
#if defined SOLARIX_GRAMMAR_ENGINE || defined SOLARIX_SYNONYMIZER_ENGINE
sol_DeleteGrammarEngine(hEngine);
#else
sol_DeleteSearchEngine(hEngine);
#endif
return nullptr;
}
GREN_API(HGREN) sol_CreateGrammarEngineW(const wchar_t *DictionaryXml)
{
return sol_CreateGrammarEngineExW(DictionaryXml, 0);
}
#endif
#if defined SOLARIX_GRAMMAR_ENGINE || defined SOLARIX_SYNONYMIZER_ENGINE
// http://www.solarix.ru/api/en/sol_CreateGrammarEngine.shtml
GREN_API(HGREN) sol_CreateGrammarEngineExA(const char *DictionaryXml, int Flags)
{
lem::Init();
int rc = 0;
HGREN hEngine = new GrammarEngineHolder();
if (!lem::lem_is_empty(DictionaryXml))
{
rc = sol_LoadDictionaryA(hEngine, DictionaryXml);
if (rc != 1)
goto Failed;
}
else
{
rc = 0;
}
return hEngine;
Failed:;
sol_DeleteGrammarEngine(hEngine);
return nullptr;
}
GREN_API(HGREN) sol_CreateGrammarEngineA(const char *DictionaryXml)
{
return sol_CreateGrammarEngineExA(DictionaryXml, 0);
}
#endif
#if defined SOLARIX_GRAMMAR_ENGINE || defined SOLARIX_SYNONYMIZER_ENGINE
// http://www.solarix.ru/api/en/sol_CreateGrammarEngine.shtml
GREN_API(HGREN) sol_CreateGrammarEngine8(const char *DictionaryXmlUtf8)
{
lem::Init();
return sol_CreateGrammarEngineW(lem::from_utf8(DictionaryXmlUtf8).c_str());
}
GREN_API(HGREN) sol_CreateGrammarEngineEx8(const char *DictionaryXmlUtf8, int Flags)
{
lem::Init();
return sol_CreateGrammarEngineExW(lem::from_utf8(DictionaryXmlUtf8).c_str(), Flags);
}
#endif
#if defined SOLARIX_GRAMMAR_ENGINE
// http://www.solarix.ru/api/en/sol_LoadDictionary.shtml
GREN_API(int) sol_LoadDictionaryA(HGREN hEngine, const char *Filename)
{
return sol_LoadDictionaryW(hEngine, to_unicode(Filename).c_str());
}
// ***********************************************************************
// Load the grammatical dictionary engine in memory.
// Загрузка словаря в оперативную память из указанного дискового файла.
// http://www.solarix.ru/api/en/sol_LoadDictionary.shtml
//
// Returns:
// 0 - dictionary was not loaded
// 1 - dictionary was successfuly loaded
// 2 - dictionary is already loaded
// ***********************************************************************
GREN_API(int) sol_LoadDictionaryW(HGREN hEngine, const wchar_t *Filename)
{
if (lem::lem_is_empty(Filename) || hEngine == nullptr)
return 0;
#if defined LEM_THREADS
lem::Process::CritSecLocker guard(&(HandleEngine(hEngine)->cs));
#endif
if (!!ENGINE->dict)
// Dictionary is already loaded.
return 2;
int loaded_ok = 0;
try
{
loaded_ok = HandleEngine(hEngine)->Load(Filename, false) ? 1 : 0;
}
CATCH_API(hEngine)
return loaded_ok;
}
// http://www.solarix.ru/api/en/sol_LoadDictionary.shtml
GREN_API(int) sol_LoadDictionary8(HGREN hEngine, const char *FilenameUtf8)
{
return sol_LoadDictionaryW(hEngine, lem::from_utf8(FilenameUtf8).c_str());
}
GREN_API(int) sol_LoadDictionaryEx8(HGREN hEngine, const char *FilenameUtf8, int Flags)
{
return sol_LoadDictionaryExW(hEngine, lem::from_utf8(FilenameUtf8).c_str(), Flags);
}
GREN_API(int) sol_LoadDictionaryExA(HGREN hEngine, const char *Filename, int Flags)
{
return sol_LoadDictionaryExW(hEngine, to_unicode(Filename).c_str(), Flags);
}
GREN_API(int) sol_LoadDictionaryExW(HGREN hEngine, const wchar_t *Filename, int Flags)
{
if (lem::lem_is_empty(Filename) || hEngine == nullptr)
return 0;
#if defined LEM_THREADS
lem::Process::CritSecLocker guard(&ENGINE->cs);
#endif
if (!!ENGINE->dict)
// Dictionary is already loaded.
return 2;
int loaded_ok = 0;
try
{
const bool LazyLexicon = (Flags & SOL_CREATE_GREN_LAZY_LEXICON) == SOL_CREATE_GREN_LAZY_LEXICON;
loaded_ok = ENGINE->Load(Filename, LazyLexicon) ? 1 : 0;
}
CATCH_API(hEngine)
return loaded_ok;
}
#endif
// Returns the value indicating whether the dictionary has been initialized and loaded
// http://www.solarix.ru/api/en/sol_IsDictionaryLoaded.shtml
GREN_API(int) sol_IsDictionaryLoaded(HGREN hEngine)
{
#if defined LEM_THREADS
lem::Process::CritSecLocker guard(&ENGINE->cs);
#endif
return (hEngine != nullptr && !!HandleEngine(hEngine)->dict) ? 1 : 0;
}
#if defined SOLARIX_GRAMMAR_ENGINE
// Drops the grammatical dictionary in-memory structures, frees the allocated resources.
// http://www.solarix.ru/api/en/sol_UnloadDictionary.shtml
GREN_API(void) sol_UnloadDictionary(HGREN hEngine)
{
if (hEngine == nullptr)
return;
try
{
#if defined LEM_THREADS
lem::Process::CritSecLocker guard(&ENGINE->cs);
#endif
ENGINE->dict.reset();
ENGINE->seeker.reset();
ENGINE->fuzzy.reset();
}
CATCH_API(hEngine)
return;
}
#endif
// Check the availability of lexicon and thesaurus for specified language
// http://www.solarix.ru/api/en/sol_HasLanguage.shtml
GREN_API(int) sol_HasLanguage(HGREN hEngine, int LanguageID)
{
if (hEngine == nullptr || LanguageID == UNKNOWN || HandleEngine(hEngine)->dict.IsNull())
return 0;
int ret = 0;
try
{
#if defined SOL_CAA && !defined SOL_NO_AA
lem::MCollect<int> langs;
HandleEngine(hEngine)->dict->GetLanguages(langs);
return langs.find(LanguageID) != UNKNOWN;
#endif
}
CATCH_API(hEngine)
return 0;
}
// ***********************************************************************
// Count the total number of word and phrase entries in dictionary.
// Сколько словарных статей в словаре
//
// http://www.solarix.ru/api/en/sol_CountEntries.shtml
// http://www.solarix.ru/api/ru/sol_CountEntries.shtml
// ***********************************************************************
GREN_API(int) sol_CountEntries(HGREN hEngine)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict)
return -1;
int ret = -1;
try
{
const int n_words = HandleEngine(hEngine)->dict->GetSynGram().GetnEntry(ANY_STATE);
const int n_phrases = HandleEngine(hEngine)->dict->GetSynGram().GetStorage().CountPhrases();
return n_words + n_phrases;
}
CATCH_API(hEngine)
return ret;
}
// ***********************************************************************
// Count the total number of wordforms in dictionary.
// Сколько словоформ (явно определенных) в словаре.
// ***********************************************************************
GREN_API(int) sol_CountForms(HGREN h)
{
if (h == nullptr || !HandleEngine(h)->dict)
return -1;
try
{
return HandleEngine(h)->dict->GetSynGram().Count_Forms();
}
CATCH_API(h)
return -1;
}
// ****************************************************************************
// Returns the total number of links (word and phrase relations) in thesaurus.
// Статистика тезауруса: сколько записей всего.
//
// http://www.solarix.ru/api/en/sol_CountLinks.shtml
// ****************************************************************************
GREN_API(int) sol_CountLinks(HGREN hEngine)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict)
return -1;
try
{
return CastSizeToInt(HandleEngine(hEngine)->dict->GetSynGram().Get_Net().CountAllLinks());
}
CATCH_API(hEngine)
return -1;
}
// ********************************************************
// Возвращает номер версии словаря.
// http://www.solarix.ru/api/ru/sol_DictionaryVersion.shtml
// ********************************************************
GREN_API(int) sol_DictionaryVersion(HGREN hEngine)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict)
return -1;
return HandleEngine(hEngine)->dict->version.code;
}
// Set default language ID. This is vital parameter for correct normalization of characters.
GREN_API(int) sol_SetLanguage(HGREN hEngine, int LanguageID)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict)
return -1;
#if defined LEM_THREADS
lem::Process::CritSecLocker guard(&HandleEngine(hEngine)->cs);
#endif
HandleEngine(hEngine)->DefaultLanguage = LanguageID;
HandleEngine(hEngine)->dict->SetDefaultLanguage(LanguageID);
return 0;
}
// ***********************************************************************
// Поиск словоформы в Лексиконе.
// В случае успеха возвращается число успешных проекций, и индексы статьи
// и формы в полях ientry для ПЕРВОЙ проекции.
// и iform соответственно.
// Через поле iclass возвращается индекс грамматического класса.
//
// Если найти не удалось - возвращается -1.
//
// ***********************************************************************
GREN_API(int) sol_FindWord(
HGREN h,
const wchar_t *Word,
int *EntryIndex,
int *Form,
int *Class
)
{
if (h == nullptr || !HandleEngine(h)->dict)
return -1;
if (EntryIndex != nullptr) *EntryIndex = UNKNOWN;
if (Form != nullptr) *Form = UNKNOWN;
if (Class != nullptr) *Class = UNKNOWN;
DEMO_SINGLE_THREAD(h)
try
{
UCString uWord(Word);
uWord.to_upper();
// Look for it in dictionary
Lexem ml(uWord);
HandleEngine(h)->dict->GetLexAuto().TranslateLexem(ml, true);
RC_Lexem rc(&ml, null_deleter());
MCollect<Word_Coord> found_list;
MCollect<ProjScore> val_list;
PtrCollect<LA_ProjectInfo> inf_list;
HandleEngine(h)->dict->GetLexAuto().ProjectWord(rc, found_list, val_list, inf_list, LexicalAutomat/*::DynformsMode*/::Wordforms, 0, HandleEngine(h)->DefaultLanguage, nullptr);
if (!found_list.empty())
{
const int ientry0 = found_list.front().GetEntry();
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(ientry0);
if (EntryIndex) *EntryIndex = ientry0;
if (Form) *Form = found_list.front().GetForm();
if (Class) *Class = e.GetClass();
}
// Returns number of projections
return CastSizeToInt(found_list.size());
}
CATCH_API(h)
// Missing in dictionary
return UNKNOWN;
}
static void getForms(
HGREN h,
const wchar_t *Word,
bool Allow_Dynforms,
bool Synonyms,
bool Grammar_Links,
bool Translations,
bool Semantics,
int nJumps,
lem::MCollect<UCString> &res
)
{
const bool Thesaurus = (Synonyms || Grammar_Links) && nJumps > 0;
const LexicalAutomat::DynformsMode morphology = Allow_Dynforms ? LexicalAutomat::Dynforms_Last_Chance : LexicalAutomat::Wordforms;
const bool all_links = Synonyms && Grammar_Links && Translations && Semantics;
// Тут конечно возникает серьезный оверхед, так как этот список допустимых связок
// заполняется каждый раз при работе процедуры.
lem::MCollect< Tree_Link > allowed_links;
if (!all_links)
{
if (Synonyms)
allowed_links.push_back(SYNONYM_link);
if (Grammar_Links)
{
allowed_links.push_back(TO_VERB_link);
allowed_links.push_back(TO_INF_link);
allowed_links.push_back(TO_PERFECT);
allowed_links.push_back(TO_UNPERFECT);
allowed_links.push_back(TO_NOUN_link);
allowed_links.push_back(TO_ADJ_link);
allowed_links.push_back(TO_ADV_link);
allowed_links.push_back(TO_RETVERB);
allowed_links.push_back(WOUT_RETVERB);
}
if (Translations)
{
allowed_links.push_back(TO_ENGLISH_link);
allowed_links.push_back(TO_RUSSIAN_link);
allowed_links.push_back(TO_FRENCH_link);
allowed_links.push_back(TO_SPANISH_link);
allowed_links.push_back(TO_GERMAN_link);
allowed_links.push_back(TO_CHINESE_link);
allowed_links.push_back(TO_POLAND_link);
allowed_links.push_back(TO_ITALIAN_link);
allowed_links.push_back(TO_PORTUGUAL_link);
allowed_links.push_back(TO_JAPANESE_link);
}
if (Semantics)
{
allowed_links.push_back(CLASS_link);
allowed_links.push_back(ACTION_link);
allowed_links.push_back(ACTOR_link);
allowed_links.push_back(TOOL_link);
allowed_links.push_back(RESULT_link);
}
}
LexicalAutomat &la = HandleEngine(h)->dict->GetLexAuto();
SynGram &sg = HandleEngine(h)->dict->GetSynGram();
std::set<int> ientry_set;
Lexem *lex = new Lexem(Word);
la.TranslateLexem(*lex, true);
RC_Lexem rc_ml(lex);
MCollect<Word_Coord> found_list;
MCollect<ProjScore> val_list;
PtrCollect<LA_ProjectInfo> inf_list;
la.ProjectWord(rc_ml, found_list, val_list, inf_list, morphology, 0, HandleEngine(h)->DefaultLanguage, nullptr);
// Накапливаем неповторяющийся список индексов статей
for (Container::size_type i = 0; i < found_list.size(); i++)
{
int ie = found_list[i].GetEntry();
// Лексическое содержимое всех словоформ статьи
const SG_Entry &e = sg.GetEntry(ie);
const Container::size_type nf = e.forms().size();
for (Container::size_type k = 0; k < nf; k++)
{
const UCString &str = e.forms()[k].name();
bool f = false;
for (Container::size_type j = 0; j < res.size(); j++)
if (res[j] == str)
{
f = true;
break;
}
if (!f)
res.push_back(str);
}
// Если требуется искать связанные словарные статьи по тезаурусу
if (Thesaurus)
ientry_set.insert(ie);
}
if (Thesaurus)
{
std::set<int> links;
for (std::set<int>::const_iterator l = ientry_set.begin(); l != ientry_set.end(); l++)
{
links.clear();
// Связки
if (all_links)
sg.Get_Net().Find_Linked_Entries(sg.GetEntry(*l).GetKey(), nJumps, links);
else
sg.Get_Net().Find_Linked_Entries(sg.GetEntry(*l).GetKey(), nJumps, links, true, true, &allowed_links);
for (std::set<int>::const_iterator k = links.begin(); k != links.end(); k++)
{
const SG_Entry &e = sg.GetEntry(*k);
const Container::size_type nf = e.forms().size();
for (Container::size_type k = 0; k < nf; k++)
{
const UCString &str = e.forms()[k].name();
bool f = false;
for (Container::size_type j = 0; j < res.size(); j++)
if (res[j] == str)
{
f = true;
break;
}
if (!f)
res.push_back(str);
}
}
}
}
return;
}
GREN_API(HGREN_STR) sol_FindStringsEx(
HGREN h,
const wchar_t *Word,
int Allow_Dynforms,
int Synonyms,
int Grammar_Links,
int Translations,
int Semantics,
int nJumps
)
{
if (h == nullptr || !HandleEngine(h)->dict)
return nullptr;
GREN_Strings *res = new GREN_Strings;
lem::MCollect<UCString> list;
list.reserve(32);
try
{
getForms(h, Word, Allow_Dynforms == 1, Synonyms == 1, Grammar_Links == 1, Translations == 1, Semantics == 1, nJumps, list);
for (MCollect<UCString>::const_iterator i = list.begin(); i != list.end(); i++)
res->list.push_back(*i);
}
catch (...)
{
delete res;
return nullptr;
}
return res;
}
static int Decline(
HGREN h,
int ientry, int icase, int number, int anim_form);
static int Correlate_Nom(
HGREN h,
int ientry,
int factor,
int Anim
);
static int Correlate_Instr(
HGREN h,
int ientry,
int factor,
int Anim
);
static int Correlate_Accus(
HGREN h,
int ientry,
int factor,
int Anim
);
static int Correlate_All(
HGREN h,
int ientry,
int factor,
int Case,
int Anim
);
// ***********************************************************************
// Согласование существительного и числительного. Отдельно задается падеж
// и (опционально) - одушевленность/неодушевленность.
// ***********************************************************************
GREN_API(int) sol_CorrNounNumber(
HGREN h,
int EntryIndex,
int Value,
int Case, /*NOMINATIVE_CASE*/
int Anim, /*INANIMATIVE_FORM*/
wchar_t *Result
)
{
if (h == nullptr || !HandleEngine(h)->dict || Result == nullptr)
return -1;
*Result = 0;
if (!h || EntryIndex == -1)
return -1;
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex);
//UCString aaa = e.GetName();
if (e.GetClass() == NOUN_ru)
{
// Нашли среди проекций существительное русского языка
int factor = Value % 100;
factor = factor < 20l ? factor % 20l : factor % 10l;
int iform = UNKNOWN;
switch (Case)
{
case NOMINATIVE_CASE_ru:
iform = Correlate_Nom(h, EntryIndex, factor, Anim);
break;
case ACCUSATIVE_CASE_ru:
iform = Correlate_Accus(h, EntryIndex, factor, Anim);
break;
case INSTRUMENTAL_CASE_ru:
iform = Correlate_Instr(h, EntryIndex, factor, Anim);
break;
default:
iform = Correlate_All(h, EntryIndex, factor, Case, Anim);
}
if (iform == UNKNOWN)
// Не найдена подходящая форма слова
return -3;
// Нашли нужную форму слова.
// Извлекаем лексическое содержание формы
lem_strcpy(Result, e.forms()[iform].name().c_str());
return 0;
}
}
catch (...)
{
return -1;
}
// Слово не найдено в словаре
return -2;
}
static int Correlate_Nom(
HGREN h,
int ientry,
int factor,
int Anim
)
{
if (factor == 0)
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim
);
if (factor == 1)
return Decline(
h,
ientry,
NOMINATIVE_CASE_ru,
SINGULAR_NUMBER_ru,
Anim
);
if (factor >= 2 && factor <= 4)
{
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
SINGULAR_NUMBER_ru,
Anim
);
}
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim
);
}
static int Correlate_Instr(
HGREN h,
int ientry,
int factor,
int Anim
)
{
if (factor == 0)
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim
);
if (factor == 1)
return Decline(
h,
ientry,
INSTRUMENTAL_CASE_ru,
SINGULAR_NUMBER_ru,
Anim
);
return Decline(
h,
ientry,
INSTRUMENTAL_CASE_ru,
PLURAL_NUMBER_ru,
Anim
);
}
static int Correlate_Accus(
HGREN h,
int ientry,
int factor,
int Anim
)
{
if (factor == 0)
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim
);
if (factor == 1)
return Decline(
h,
ientry,
ACCUSATIVE_CASE_ru,
SINGULAR_NUMBER_ru,
Anim
);
if (factor >= 2 && factor <= 4)
{
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
SINGULAR_NUMBER_ru,
Anim
);
}
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim
);
}
static int Correlate_All(
HGREN h,
int ientry,
int factor,
int Case,
int Anim
)
{
if (factor == 0)
return Decline(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim
);
if (factor == 1)
return Decline(
h,
ientry,
Case,
SINGULAR_NUMBER_ru,
Anim
);
return Decline(
h,
ientry,
Case,
PLURAL_NUMBER_ru,
Anim
);
}
// ********************************************************************
// Поиск словоформы в указанной статье.
// ********************************************************************
static int Decline(
HGREN h,
int ientry,
int icase,
int number,
int anim_form
)
{
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(ientry);
CP_Array dim;
dim.push_back(GramCoordPair(GramCoordAdr(CASE_ru, 0), icase));
dim.push_back(GramCoordPair(GramCoordAdr(NUMBER_ru, 0), number));
return e.FindFormIndex(dim);
}
catch (...)
{
return UNKNOWN;
}
return UNKNOWN;
}
// ****************************************************************
// Число, заданное целочисленным аргументом value, преобразуем в
// текстовое представление.
// ****************************************************************
GREN_API(int) sol_Value2Text(
HGREN h,
wchar_t *Result,
int Value,
int Gender
)
{
if (h == nullptr || !HandleEngine(h)->dict || Result == nullptr)
return -1;
*Result = 0;
#if defined LEM_OFMT_MICROSOL
try
{
wstring res = to_text(Value, LEM_RUS, Gender != FEMININE_GENDER_ru);
lem_strcpy(Result, res.c_str());
}
catch (...)
{
return -1;
}
return 0;
#else
return -1;
#endif
}
// ***************************************************************************
// Find the entry ID.
//
// http://www.solarix.ru/api/en/sol_FindEntry.shtml
// http://www.solarix.ru/api/ru/sol_FindEntry.shtml
//
// Other API functions use this index to perform operations on words without
// repeated search in dictionary.
//
// Returns:
// -1 word entry not found
// -2 internal error
//
// Поиск индекса словарной статьи во внутреннем списке. Другие функции
// грамматической машины могут выполнять операции со словами с использованием
// этого индекса (чтобы не повторять поиск в словаре).
//
// Возвращает:
// -1 словарная статья не найдена
// -2 внутренний сбой
// ***************************************************************************
GREN_API(int) sol_FindEntry(
HGREN h,
const wchar_t *Word, // Entry name
int Class, // Required grammatical class
int Language // Language ID (if ambiguos)
)
{
if (h == nullptr || !HandleEngine(h)->dict)
return -2;
DEMO_SINGLE_THREAD(h)
try
{
// Convert to UNICODE lexem
UCString uWord(Word);
uWord.to_upper();
// Ищем словарную статью с таким именем
return HandleEngine(h)->dict->GetSynGram().FindEntry(uWord, Class, false);
}
catch (...)
{
// Internal error
return -2;
}
}
GREN_API(int) sol_FindEntry8(HGREN hEngine, const char *Word, int Class, int Language)
{
return sol_FindEntry(hEngine, lem::from_utf8(Word).c_str(), Class, Language);
}
// *************************************************************
// Returns the gender of noun (unapplayable for English)
//
// http://www.solarix.ru/api/en/sol_GetNounGender.shtml
// http://www.solarix.ru/api/ru/sol_GetNounGender.shtml
//
// Возвращает род существительного (неприменимо для английского)
// *************************************************************
GREN_API(int) sol_GetNounGender(
HGREN h,
int EntryIndex // Entry index (see sol_Find_Entry API functions)
)
{
if (!h || !HandleEngine(h)->dict || EntryIndex == -1)
return -2;
try
{
// The language that the entry belongs to.
int Lang = static_cast<const SG_Class&>(
HandleEngine(h)->dict->GetSynGram().classes()[HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex).GetClass()]
).GetLanguage();
if (Lang == RUSSIAN_LANGUAGE)
return HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex).GetAttrState(GramCoordAdr(GENDER_ru, 0));
}
catch (...)
{
}
return UNKNOWN;
}
// ***********************************************************************
// Get the noun form for given case and number. Result will contain the
// lexical content of the wordform (or empty string if not found).
//
// Возвращает в Result форму существительного с заданным падежом и числом.
// ***********************************************************************
GREN_API(int) sol_GetNounForm(
HGREN h,
int EntryIndex,
int Number,
int Case,
wchar_t *Result
)
{
*Result = 0;
if (!h || !HandleEngine(h)->dict || EntryIndex == -1 || Result == nullptr)
return -2;
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex);
// The language that the entry belongs to.
int Lang = static_cast<const SG_Class&>(
HandleEngine(h)->dict->GetSynGram().classes()[e.GetClass()]
).GetLanguage();
CP_Array dim;
switch (Lang)
{
case RUSSIAN_LANGUAGE:
{
dim.push_back(GramCoordPair(GramCoordAdr(CASE_ru, 0), Case));
dim.push_back(GramCoordPair(GramCoordAdr(NUMBER_ru, 0), Number));
break;
}
#if defined SOL_GM_ENGLISH
case ENGLISH_LANGUAGE:
{
dim.push_back(GramCoordPair(GramCoordAdr(NUMBER_xx, 0), Number));
if (Case != -1)
dim.push_back(GramCoordPair(GramCoordAdr(CASE_en, 0), Case));
else
dim.push_back(GramCoordPair(GramCoordAdr(CASE_en, 0), NOMINATIVE_CASE_en));
break;
}
#endif
}
int iform = e.FindFormIndex(dim);
if (iform == UNKNOWN)
// Не найдена подходящая форма слова
return -3;
// Нашли нужную форму слова.
// Извлекаем лексическое содержание формы
const SG_EntryForm &f = e.forms()[iform];
lem_strcpy(Result, e.forms()[iform].name().c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
// ************************************************************
// Get the proper verb form.
//
// Ищет подходящую форму глагола.
// ************************************************************
GREN_API(int) sol_GetVerbForm(
HGREN h,
int EntryIndex,
int Number,
int Gender,
int Tense,
int Person,
wchar_t *Result
)
{
if (!h || !HandleEngine(h)->dict || EntryIndex == -1 || Result == nullptr)
return -2;
*Result = 0;
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex);
// The language that the entry belongs to.
int Lang = static_cast<const SG_Class&>(
HandleEngine(h)->dict->GetSynGram().classes()[e.GetClass()]
).GetLanguage();
CP_Array dim;
switch (Lang)
{
case RUSSIAN_LANGUAGE:
{
dim.push_back(GramCoordPair(GramCoordAdr(NUMBER_ru, 0), Number));
if (
Number == SINGULAR_NUMBER_ru &&
Tense == PAST_ru
)
dim.push_back(GramCoordPair(GramCoordAdr(GENDER_ru, 0), Gender));
dim.push_back(GramCoordPair(GramCoordAdr(TENSE_ru, 0), Tense));
if (Tense == PRESENT_ru || Tense == FUTURE_ru)
dim.push_back(GramCoordPair(GramCoordAdr(PERSON_ru, 0), Person));
break;
}
#if defined SOL_GM_ENGLISH
case ENGLISH_LANGUAGE:
{
dim.push_back(GramCoordPair(GramCoordAdr(NUMBER_xx, 0), Number));
dim.push_back(GramCoordPair(GramCoordAdr(TENSE_en, 0), Tense));
dim.push_back(GramCoordPair(GramCoordAdr(PERSON_xx, 0), Person));
break;
}
#endif
}
int iform = e.FindFormIndex(dim);;
if (iform == UNKNOWN)
// Не найдена подходящая форма слова
return -3;
// Нашли нужную форму слова.
// Извлекаем лексическое содержание формы
lem_strcpy(Result, e.forms()[iform].name().c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
GREN_API(int) sol_CorrVerbNumber(
HGREN h,
int EntryIndex,
int Value,
int Gender,
int Tense,
wchar_t *Result
)
{
if (!h || !HandleEngine(h)->dict || EntryIndex == -1 || Result == nullptr)
return -2;
*Result = 0;
int f100 = Value % 100;
int f10 = Value % 10;
if (
(f100 == 1 ||
f100 > 20) &&
f10 == 1
)
return sol_GetVerbForm(
h,
EntryIndex,
SINGULAR_NUMBER_ru,
Gender,
Tense,
PERSON_3_ru,
Result
);
return sol_GetVerbForm(
h,
EntryIndex,
PLURAL_NUMBER_ru,
Gender,
Tense,
PERSON_3_ru,
Result
);
}
GREN_API(int) sol_GetAdjectiveForm(
HGREN h,
int EntryIndex,
int Number,
int Gender,
int Case,
int Anim,
int Shortness,
int Compar_Form,
wchar_t *Result
)
{
if (!h || !HandleEngine(h)->dict || EntryIndex == -1 || Result == nullptr)
return -2;
*Result = 0;
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex);
// The language that the entry belongs to.
int Lang = static_cast<const SG_Class&>(
HandleEngine(h)->dict->GetSynGram().classes()[e.GetClass()]
).GetLanguage();
CP_Array dim;
switch (Lang)
{
case RUSSIAN_LANGUAGE:
{
dim.push_back(GramCoordPair(GramCoordAdr(CASE_ru, 0), Case));
dim.push_back(GramCoordPair(GramCoordAdr(NUMBER_ru, 0), Number));
if (Number == SINGULAR_NUMBER_ru)
{
dim.push_back(GramCoordPair(GramCoordAdr(GENDER_ru, 0), Gender));
if (Gender == MASCULINE_GENDER_ru && Case == ACCUSATIVE_CASE_ru)
dim.push_back(GramCoordPair(GramCoordAdr(FORM_ru, 0), Anim));
}
else if (Number == PLURAL_NUMBER_ru && Case == ACCUSATIVE_CASE_ru)
{
dim.push_back(GramCoordPair(GramCoordAdr(FORM_ru, 0), Anim));
}
if (Shortness == UNKNOWN)
dim.push_back(GramCoordPair(GramCoordAdr(SHORTNESS_ru, 0), 0));
else
dim.push_back(GramCoordPair(GramCoordAdr(SHORTNESS_ru, 0), Shortness));
if (Compar_Form == UNKNOWN)
dim.push_back(GramCoordPair(GramCoordAdr(COMPAR_FORM_ru, 0), ATTRIBUTIVE_FORM_ru));
else
dim.push_back(GramCoordPair(GramCoordAdr(COMPAR_FORM_ru, 0), Compar_Form));
break;
}
#if defined SOL_GM_ENGLISH
case ENGLISH_LANGUAGE:
{
dim.push_back(GramCoordPair(GramCoordAdr(ADJ_FORM_en, 0), Compar_Form));
break;
}
#endif
}
int iform = e.FindFormIndex(dim);;
if (iform == UNKNOWN)
// Не найдена подходящая форма слова
return -3;
// Нашли нужную форму слова.
// Извлекаем лексическое содержание формы
const SG_EntryForm &f = e.forms()[iform];
lem_strcpy(Result, f.name().c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
static int Decline_Adj(HGREN h, int ientry, int icase, int number, int anim_form, int gender);
static int Correlate_Nom_Adj(
HGREN h,
int ientry,
int factor,
int Gender,
int Anim
);
static int Correlate_Instr_Adj(
HGREN h,
int ientry,
int factor,
int Gender,
int Anim
);
static int Correlate_Accus_Adj(
HGREN h,
int ientry,
int factor,
int Gender,
int Anim
);
static int Correlate_All_Adj(
HGREN h,
int ientry,
int factor,
int Case,
int Gender,
int Anim
);
GREN_API(int) sol_CorrAdjNumber(
HGREN h,
int EntryIndex,
int Value,
int Case,
int Gender,
int Anim,
wchar_t *Result
)
{
if (!h || !HandleEngine(h)->dict || EntryIndex == -1 || Result == nullptr)
return -2;
*Result = 0;
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex);
//UCString aaa = e.GetName();
if (e.GetClass() == ADJ_ru)
{
// Нашли среди проекций существительное русского языка
int factor = Value % 100;
factor = factor < 20l ? factor % 20l : factor % 10l;
int iform = UNKNOWN;
switch (Case)
{
case NOMINATIVE_CASE_ru:
iform = Correlate_Nom_Adj(h, EntryIndex, factor, Gender, Anim);
break;
case ACCUSATIVE_CASE_ru:
iform = Correlate_Accus_Adj(h, EntryIndex, factor, Gender, Anim);
break;
case INSTRUMENTAL_CASE_ru:
iform = Correlate_Instr_Adj(h, EntryIndex, factor, Gender, Anim);
break;
default:
iform = Correlate_All_Adj(h, EntryIndex, factor, Case, Gender, Anim);
}
if (iform == UNKNOWN)
// Не найдена подходящая форма слова
return -3;
// Нашли нужную форму слова.
// Извлекаем лексическое содержание формы
lem_strcpy(Result, e.forms()[iform].name().c_str());
return 0;
}
}
catch (...)
{
return -1;
}
return -2;
}
static int Correlate_Nom_Adj(
HGREN h,
int ientry,
int factor,
int Gender,
int Anim
)
{
if (factor == 0)
return Decline_Adj(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
if (factor == 1)
return Decline_Adj(
h,
ientry,
NOMINATIVE_CASE_ru,
SINGULAR_NUMBER_ru,
Anim,
Gender
);
/*
if( factor>=2 && factor<=4 )
{
return Decline_Adj(
ientry,
GENITIVE_CASE_ru,
SINGULAR_NUMBER_ru,
Anim,
Gender
);
}
*/
return Decline_Adj(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
}
static int Correlate_Instr_Adj(
HGREN h,
int ientry,
int factor,
int Gender,
int Anim
)
{
if (factor == 0)
return Decline_Adj(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
if (factor == 1)
return Decline_Adj(
h,
ientry,
INSTRUMENTAL_CASE_ru,
SINGULAR_NUMBER_ru,
Anim,
Gender
);
return Decline_Adj(
h,
ientry,
INSTRUMENTAL_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
}
static int Correlate_Accus_Adj(
HGREN h,
int ientry,
int factor,
int Gender,
int Anim
)
{
if (factor == 0)
return Decline_Adj(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
// вижу одну белую розу
// вижу один большой камень
// вижу одного большого пса
if (factor == 1)
return Decline_Adj(
h,
ientry,
ACCUSATIVE_CASE_ru,
SINGULAR_NUMBER_ru,
Anim,
Gender
);
// вижу 2 круглых камня
// вижу 2 пушистых кошки
// вижу 2 белых розы
if (factor >= 2 && factor <= 4)
{
return Decline_Adj(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
}
// вижу 5 круглых камней
// вижу 5 пушистых кошек
return Decline_Adj(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
}
static int Correlate_All_Adj(
HGREN h,
int ientry,
int factor,
int Case,
int Gender,
int Anim
)
{
if (factor == 0)
return Decline_Adj(
h,
ientry,
GENITIVE_CASE_ru,
PLURAL_NUMBER_ru,
Anim,
Gender
);
if (factor == 1)
return Decline_Adj(
h,
ientry,
Case,
SINGULAR_NUMBER_ru,
Anim,
Gender
);
return Decline_Adj(
h,
ientry,
Case,
PLURAL_NUMBER_ru,
Anim,
Gender
);
}
// ********************************************************************
// Поиск словоформы в указанной статье.
// ********************************************************************
static int Decline_Adj(HGREN h, int ientry, int Case, int Number, int Anim, int Gender)
{
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(ientry);
CP_Array dim;
dim.push_back(GramCoordPair(GramCoordAdr(CASE_ru, 0), Case));
dim.push_back(GramCoordPair(GramCoordAdr(NUMBER_ru, 0), Number));
if (Case == ACCUSATIVE_CASE_ru)
{
if (Number == SINGULAR_NUMBER_ru && Gender == MASCULINE_GENDER_ru)
dim.push_back(GramCoordPair(GramCoordAdr(FORM_ru, 0), Anim));
else if (Number == PLURAL_NUMBER_ru)
dim.push_back(GramCoordPair(GramCoordAdr(FORM_ru, 0), Anim));
}
if (Number == SINGULAR_NUMBER_ru)
{
dim.push_back(GramCoordPair(GramCoordAdr(GENDER_ru, 0), Gender));
}
return e.FindFormIndex(dim);
}
catch (...)
{
return UNKNOWN;
}
return UNKNOWN;
}
// **********************************************************
// Простая лемматизация слова - приведение к базовой форме
// **********************************************************
GREN_API(int) sol_LemmatizeWord(HGREN h, wchar_t *word, int Allow_Dynforms)
{
if (!h || HandleEngine(h)->seeker.IsNull() || word == nullptr)
return 0;
DEMO_SINGLE_THREAD(h)
// Создание объекта на стеке. Плохо.
UCString w(word);
const int ientry = HandleEngine(h)->seeker->Find(w, Allow_Dynforms == 1);
if (ientry != UNKNOWN)
{
// Копируем имя статьи - почти всегда это базовая форма
lem_strcpy(word, HandleEngine(h)->sg->GetEntry(ientry).GetName().c_str());
return 1;
}
return 0;
}
// ***************************************************************************
//
// Словарная статья приводится к семантически эквивалентному
// существительному, например "ИСКАТЬ->ПОИСК". Для работы необходим
// тезаурус!
// Входной аргумент EntryIndex - результат предыдущего вызова sol_Find_Entry.
// Возвращается ID словарной статьи (существительное) для использования,
// например, в sol_GetNounForm, либо -1 если привести к существительному
// не представляется возможным.
//
// http://www.solarix.ru/api/ru/sol_TranslateToNoun.shtml
//
// ***************************************************************************
GREN_API(int) sol_TranslateToNoun(HGREN hEngine, int EntryID)
{
if (EntryID == -1 || !hEngine || !HandleEngine(hEngine)->dict)
return -1;
DEMO_SINGLE_THREAD(hEngine)
try
{
const SG_Entry &e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(EntryID);
if (
e.GetClass() == NOUN_ru ||
e.GetClass() == NOUN_en ||
e.GetClass() == NOUN_fr ||
e.GetClass() == NOUN_es
)
// Преобразование не требуется, так как исходное слово уже является
// существительным.
return EntryID;
lem::MCollect<int> key_list; // список связанных существительных
if (HandleEngine(hEngine)->dict->GetLexAuto().Translate_To_Nouns(EntryID, key_list))
{
// НАШЛИ ФОРМУ СУЩЕСТВИТЕЛЬНОГО
if (key_list.size() > 1)
{
// выберем существительное с максимальной встречаемостью
int max_id = UNKNOWN, max_freq = 0;
for (lem::Container::size_type k = 0; k < key_list.size(); ++k)
{
const int id_entry = key_list[k];
const Solarix::SG_Entry & e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(id_entry);
if (e.GetFreq() > max_freq)
{
max_freq = e.GetFreq();
max_id = id_entry;
}
}
return max_id;
}
else
return key_list.front();
}
}
CATCH_API(hEngine);
return -1;
}
// ************************************************************************
//
// Приведение словарной статьи к семантически или грамматически связанной
// неопределенной форме глагола (инфинитиву).
//
// http://www.solarix.ru/api/ru/sol_TranslateToInfinitive.shtml
//
// ************************************************************************
GREN_API(int) sol_TranslateToInfinitive(HGREN hEngine, int EntryID)
{
if (EntryID == -1 || !hEngine || !HandleEngine(hEngine)->dict)
return -1;
DEMO_SINGLE_THREAD(hEngine)
try
{
const SG_Entry &e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(EntryID);
const int pos_id = e.GetClass();
if (pos_id == INFINITIVE_ru || pos_id == VERB_en)
return EntryID;
lem::MCollect<int> key_list; // список связанных инфинитивов
if (HandleEngine(hEngine)->dict->GetLexAuto().Translate_To_Infinitives(EntryID, key_list))
{
// НАШЛИ ФОРМУ ИНФИНИТИВА
return key_list.front();
}
}
CATCH_API(hEngine);
return -1;
}
// ***************************************************************************************
//
// Поиск заданного слова в слова и применение правил несловарной морфологии.
// Результат - список вариантов распознавания в виде кортежей id словарной статьи, теги.
//
// http://www.solarix.ru/api/ru/sol_ProjectWord.shtml
//
// ***************************************************************************************
GREN_API(HGREN_WCOORD) sol_ProjectWord(HGREN hEngine, const wchar_t *Word, int Allow_Dynforms)
{
if (!hEngine || !HandleEngine(hEngine)->dict || Word == nullptr)
return nullptr;
DEMO_SINGLE_THREAD(hEngine)
try
{
UCString uWord(Word);
uWord.to_upper();
// Look for it in dictionary
Lexem ml(uWord);
HandleEngine(hEngine)->dict->GetLexAuto().TranslateLexem(ml, true);
RC_Lexem rc(&ml, null_deleter());
MCollect<ProjScore> val_list;
PtrCollect<LA_ProjectInfo> inf_list;
GREN_WordCoords *res = new GREN_WordCoords;
HandleEngine(hEngine)->dict->GetLexAuto().ProjectWord(
rc,
res->list,
val_list,
inf_list,
Allow_Dynforms ? LexicalAutomat::Dynforms_Last_Chance : LexicalAutomat::Wordforms,
0,
HandleEngine(hEngine)->DefaultLanguage,
nullptr
);
// заполним списки тегов для каждого варианта распознавания.
if (!res->list.empty())
{
for (lem::Container::size_type i = 0; i < res->list.size(); ++i)
{
CP_Array item_tags;
const int ientry = res->list[i].GetEntry();
// для каждого варианта распознавания может быть получена пара из id статьи+id формы слова,
// или id UNK-статьи и список тегов в случае несловарной морфологии.
if (ientry != UNKNOWN)
{
const SG_Entry &e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(ientry);
item_tags = e.forms()[res->list[i].GetForm()].coords();
}
if (inf_list[i] != nullptr && !inf_list[i]->coords.empty())
{
for (lem::Container::size_type j = 0; j < inf_list[i]->coords.size(); ++j)
{
// добавляем в итоговый список тегов без повторов.
if (item_tags.find(inf_list[i]->coords[j]) == UNKNOWN)
{
item_tags.push_back(inf_list[i]->coords[j]);
}
}
}
// запоминаем получившийся список тегов для данного варианта распознавания.
res->tags.push_back(item_tags);
}
return res;
}
// Ни одного варианта распознавания.
delete res;
return nullptr;
}
CATCH_API(hEngine)
{
return nullptr;
}
}
GREN_API(HGREN_WCOORD) sol_ProjectWord8(HGREN hEngine, const char *WordUtf8, int Allow_Dynforms)
{
return sol_ProjectWord(hEngine, lem::from_utf8(WordUtf8).c_str(), Allow_Dynforms);
}
// ****************************************************************
// Нечеткая проекция - допускается nmissmax несовпадений символов.
// ****************************************************************
GREN_API(HGREN_WCOORD) sol_ProjectMisspelledWord(
HGREN hEngine,
const wchar_t *Word,
int Allow_Dynforms,
int nmaxmiss
)
{
if (!hEngine || !HandleEngine(hEngine)->dict || Word == nullptr)
return nullptr;
DEMO_SINGLE_THREAD(hEngine)
try
{
UCString uWord(Word);
uWord.to_upper();
// Look for it in dictionary
Lexem ml(uWord);
RC_Lexem rc(&ml, null_deleter());
MCollect<ProjScore> val_list;
PtrCollect<LA_ProjectInfo> inf_list;
GREN_WordCoords *res = new GREN_WordCoords;
HandleEngine(hEngine)->dict->GetLexAuto().ProjectWord(
rc,
res->list,
val_list,
inf_list,
Allow_Dynforms ? LexicalAutomat::Dynforms_Last_Chance : LexicalAutomat::Wordforms,
nmaxmiss,
HandleEngine(hEngine)->DefaultLanguage,
nullptr
);
if (!res->list.empty())
{
for (lem::Container::size_type i = 0; i < res->list.size(); ++i)
{
CP_Array item_tags;
const int ientry = res->list[i].GetEntry();
if (ientry != UNKNOWN)
{
const SG_Entry &e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(ientry);
item_tags = e.forms()[res->list[i].GetForm()].coords();
}
if (inf_list[i] != nullptr && !inf_list[i]->coords.empty())
{
for (lem::Container::size_type j = 0; j < inf_list[i]->coords.size(); ++j)
{
if (item_tags.find(inf_list[i]->coords[j]) == UNKNOWN)
{
item_tags.push_back(inf_list[i]->coords[j]);
}
}
}
res->tags.push_back(item_tags);
}
return res;
}
delete res;
return nullptr;
}
CATCH_API(hEngine)
{
return nullptr;
}
}
GREN_API(HGREN_WCOORD) sol_ProjectMisspelledWord8(
HGREN hEngine,
const char *WordUtf8,
int Allow_Dynforms,
int nmaxmiss
)
{
return sol_ProjectMisspelledWord(hEngine, lem::from_utf8(WordUtf8).c_str(), Allow_Dynforms, nmaxmiss);
}
// ***************************************************************************************
// Возвращает количество элементов (проекций) в списке hList, который получен в результате
// работы sol_ProjectWord или sol_ProjectMisspelledWord.
// ***************************************************************************************
GREN_API(int) sol_CountProjections(HGREN_WCOORD hList)
{
if (hList == nullptr)
return 0;
return CastSizeToInt(((const Solarix::GREN_WordCoords*)hList)->list.size());
}
GREN_API(void) sol_DeleteProjections(HGREN_WCOORD hList)
{
delete (Solarix::GREN_WordCoords*)hList;
}
// *************************************************************
// Возвращается id (primary key) словарной статьи среди проекций
// *************************************************************
GREN_API(int) sol_GetIEntry(HGREN_WCOORD hList, int Index)
{
if (!hList || Index < 0 || Index >= CastSizeToInt(((const Solarix::GREN_WordCoords*)hList)->list.size()))
return -1;
return ((const Solarix::GREN_WordCoords*)hList)->list[Index].GetEntry();
}
// **********************************************************************
// Возвращается состояние грамматического признака Coord у проекции Index
// **********************************************************************
GREN_API(int) sol_GetProjCoordState(
HGREN hEngine,
HGREN_WCOORD hList,
int Index,
int Coord
)
{
if (!hList || Index < 0 || Index >= CastSizeToInt(((const Solarix::GREN_WordCoords*)hList)->list.size()))
return -1;
int istate = UNKNOWN;
try
{
const CP_Array & tags = ((const Solarix::GREN_WordCoords*)hList)->tags[Index];
int i = tags.FindOnce(Coord);
if (i != UNKNOWN)
{
istate = tags[i].GetState();
}
}
catch (...)
{
return UNKNOWN;
}
return istate;
}
GREN_API(int) sol_GetProjCoordCount(
HGREN hEngine,
HGREN_WCOORD hList,
int Index
)
{
if (!hList || Index < 0 || Index >= CastSizeToInt(((const Solarix::GREN_WordCoords*)hList)->list.size()))
return -1;
try
{
const CP_Array & tags = ((const Solarix::GREN_WordCoords*)hList)->tags[Index];
return CastSizeToInt(tags.size());
}
catch (...)
{
return UNKNOWN;
}
}
GREN_API(int) sol_GetProjCoordId(
HGREN hEngine,
HGREN_WCOORD hList,
int ProjIndex,
int TagIndex
)
{
if (!hList || ProjIndex < 0 || TagIndex < 0 || ProjIndex >= CastSizeToInt(((const Solarix::GREN_WordCoords*)hList)->list.size()))
return -1;
try
{
const CP_Array & tags = ((const Solarix::GREN_WordCoords*)hList)->tags[ProjIndex];
return tags[TagIndex].GetCoord().GetIndex();
}
catch (...)
{
return UNKNOWN;
}
}
GREN_API(int) sol_GetProjStateId(
HGREN hEngine,
HGREN_WCOORD hList,
int ProjIndex,
int TagIndex
)
{
if (!hList || ProjIndex < 0 || TagIndex < 0 || ProjIndex >= CastSizeToInt(((const Solarix::GREN_WordCoords*)hList)->list.size()))
return -1;
try
{
const CP_Array & tags = ((const Solarix::GREN_WordCoords*)hList)->tags[ProjIndex];
return tags[TagIndex].GetState();
}
catch (...)
{
return UNKNOWN;
}
}
#define SOL_GREN_ALLOW_FUZZY 0x00000002
#define SOL_GREN_COMPLETE_ONLY 0x00000004
#define SOL_GREN_PRETOKENIZED 0x00000008
#define SOL_GREN_TOKENIZE_ONLY 0x00000010
#define SOL_GREN_DISABLE_FILTERS 0x00000020
#define SOL_GREN_REORDER_TREE 0x00000400
#define SOL_GREN_MODEL 0x00000800
#define SOL_GREN_FINDFACTS 0x00001000
#define SOL_GREN_MODEL_ONLY 0x00002000
// ****************************************************************************
// Выполнение морфологического анализа.
// ****************************************************************************
GREN_API(HGREN_RESPACK) sol_MorphologyAnalysis(
HGREN hEngine,
const wchar_t *Sentence,
int MorphologicalFlags,
int SyntacticFlags,
int Constraints,
int LanguageID
)
{
if (!hEngine || !HandleEngine(hEngine)->dict || Sentence == nullptr)
return nullptr;
DEMO_SINGLE_THREAD(hEngine)
try
{
const bool Allow_Fuzzy = (MorphologicalFlags & SOL_GREN_ALLOW_FUZZY) == SOL_GREN_ALLOW_FUZZY;
const bool CompleteAnalysisOnly = (MorphologicalFlags & SOL_GREN_COMPLETE_ONLY) == SOL_GREN_COMPLETE_ONLY;
const bool Pretokenized = (MorphologicalFlags & SOL_GREN_PRETOKENIZED) == SOL_GREN_PRETOKENIZED;
const int UseLanguageID = LanguageID == -1 ? HandleEngine(hEngine)->DefaultLanguage : LanguageID;
const bool ApplyModel = (MorphologicalFlags & SOL_GREN_MODEL) == SOL_GREN_MODEL;
const bool Schedule1 = (MorphologicalFlags == 0 && SyntacticFlags == 0);
WrittenTextAnalysisSession current_analysis(HandleEngine(hEngine)->dict.get(), nullptr);
current_analysis.params.SetLanguageID(UseLanguageID);
current_analysis.params.Pretokenized = Pretokenized;
current_analysis.params.AllowPrimaryFuzzyWordRecog = Allow_Fuzzy;
current_analysis.params.ApplyModel = ApplyModel;
// Ограничение на суммарное затраченное время в миллисекундах
const int MaxMillisecTimeout = 0x002fffff & Constraints;
current_analysis.params.timeout.max_elapsed_millisecs = MaxMillisecTimeout > 0 ? MaxMillisecTimeout : lem::int_max;
// Ограничение на макс. число параллельно проверяемых альтернативных путей построения синтаксического дерева
const int MaxAlt = ((0xffc00000 & Constraints) >> 22) & 0x000002ff;
current_analysis.params.timeout.max_alt = MaxAlt;
if (!CompleteAnalysisOnly)
{
current_analysis.params.CompleteAnalysisOnly = false;
current_analysis.params.ConfigureSkipToken();
}
if (Schedule1)
{
current_analysis.params.UseTopDownThenSparse = true;
current_analysis.params.CompleteAnalysisOnly = true;
}
else
{
current_analysis.params.CompleteAnalysisOnly = CompleteAnalysisOnly;
}
if ((MorphologicalFlags&SOL_GREN_TOKENIZE_ONLY) == SOL_GREN_TOKENIZE_ONLY)
{
// выполняем только токенизацию.
current_analysis.Tokenize(Sentence);
}
else if ((MorphologicalFlags&SOL_GREN_MODEL_ONLY) == SOL_GREN_MODEL_ONLY)
{
// применяем вероятностную модель морфологии для снятия неоднозначностей, но правила не применяем.
current_analysis.ApplyFilters(Sentence);
}
else
{
current_analysis.MorphologicalAnalysis(Sentence);
}
return current_analysis.PickPack();
}
CATCH_API(hEngine)
{
return nullptr;
}
return nullptr;
}
GREN_API(HGREN_RESPACK) sol_MorphologyAnalysis8(
HGREN hEngine,
const char *SentenceUtf8,
int Flags,
int UnusedFlags,
int Constraints,
int LanguageID
)
{
return sol_MorphologyAnalysis(
hEngine,
lem::from_utf8(SentenceUtf8).c_str(),
Flags,
UnusedFlags,
Constraints,
LanguageID
);
}
GREN_API(HGREN_RESPACK) sol_MorphologyAnalysisA(
HGREN hEngine,
const char *Sentence,
int Flags,
int UnusedFlags,
int Constraints,
int LanguageID
)
{
return sol_MorphologyAnalysis(
hEngine,
lem::to_unicode(Sentence).c_str(),
Flags,
UnusedFlags,
Constraints,
LanguageID
);
}
static void collect_nodes(const Solarix::Tree_Node & node, std::vector< std::pair<int, const lem::UCString*> > & result)
{
result.push_back(std::make_pair(node.GetNode().GetOriginPos(), &*node.GetNode().GetName()));
for (int i = 0; i < node.leafs().size(); ++i)
{
collect_nodes(node.leafs()[i], result);
}
return;
}
static lem::UFString NormalizePhrase(HGREN hEngine, HGREN_RESPACK hLinkages)
{
Solarix::Res_Pack * linkages = (Solarix::Res_Pack*)hLinkages;
if (!linkages->vars().empty())
{
const Variator * linkage = linkages->vars().front();
const Tree_Node * root = linkage->roots_list()[1];
Solarix::Dictionary & dict = *HandleEngine(hEngine)->dict;
lem::UCString func_name = lem::UCString(L"normalize_phrase_") + dict.GetSynGram().languages()[dict.GetDefaultLanguage()].GetName();
if (dict.GetLexAuto().GetFunctions().Get().IsFunction(func_name))
{
const TrFunction * func = dict.GetLexAuto().GetFunctions().Get().Find(func_name);
TrFunContext ctx0((TrFunContext*)nullptr);
TrContextInvokation ctx2(&ctx0);
ctx2.AddVar(L"((return))", lem::Ptr<TrValue>(new TrTypeValue(TrTreeType())));
lem::Ptr<TrValue> v(new TrValue(root, false));
ctx2.AddVar(func->arg_name[0], v);
ElapsedTimeConstraint null_constraints(100000, 10000);
func->GetBody().Run(null_constraints, dict.GetLexAuto(), ctx2, nullptr);
lem::Ptr<TrValue> func_ret(ctx2.GetVar(L"((return))"));
LEM_CHECKIT_Z(func_ret->GetType().IsTree());
Solarix::Tree_Node & new_tree = func_ret->GetTree();
typedef std::pair<int, const lem::UCString*> WORD_POS;
std::vector< WORD_POS > nodes;
collect_nodes(new_tree, nodes);
std::sort(nodes.begin(), nodes.end(), boost::bind(&std::pair<int, const lem::UCString*>::first, _1) < boost::bind(&std::pair<int, const lem::UCString*>::first, _2));
lem::MemFormatter mem;
for (std::vector< WORD_POS >::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
{
mem.printf("%us ", it->second->c_str());
}
return lem::trim(mem.string());
}
}
return L"";
}
GREN_API(wchar_t*) sol_NormalizePhraseW(
HGREN hEngine,
HGREN_RESPACK hLinkages
)
{
if (!hEngine || !HandleEngine(hEngine)->dict || hLinkages == nullptr)
return nullptr;
DEMO_SINGLE_THREAD(hEngine)
lem::UFString res_string = NormalizePhrase(hEngine, hLinkages);
wchar_t * w = (wchar_t*)malloc(sizeof(wchar_t)*(res_string.length() + 1));
lem::lem_strcpy(w, res_string.c_str());
return w;
}
GREN_API(char*) sol_NormalizePhrase8(HGREN hEngine, HGREN_RESPACK hLinkages)
{
if (!hEngine || !HandleEngine(hEngine)->dict || hLinkages == nullptr)
return nullptr;
DEMO_SINGLE_THREAD(hEngine)
lem::FString res_utf8 = lem::to_utf8(NormalizePhrase(hEngine, hLinkages));
char * w = (char*)malloc(res_utf8.length() + 1);
lem::lem_strcpy(w, res_utf8.c_str());
return w;
}
// ****************************************************************************
// Выполнение синтаксического анализа - на входе фраза, на выходе получается
// объект, хранящий альтернативные варианты построения синтаксического графа.
// ****************************************************************************
GREN_API(HGREN_RESPACK) sol_SyntaxAnalysis(
HGREN hEngine,
const wchar_t *Sentence,
int MorphologicalFlags,
int SyntacticFlags,
int Constraints,
int LanguageID
)
{
if (!hEngine || !HandleEngine(hEngine)->dict || Sentence == nullptr)
return nullptr;
DEMO_SINGLE_THREAD(hEngine);
try
{
const bool Allow_Fuzzy = (MorphologicalFlags & SOL_GREN_ALLOW_FUZZY) == SOL_GREN_ALLOW_FUZZY;
const bool CompleteAnalysisOnly = (MorphologicalFlags & SOL_GREN_COMPLETE_ONLY) == SOL_GREN_COMPLETE_ONLY;
const bool Pretokenized = (MorphologicalFlags & SOL_GREN_PRETOKENIZED) == SOL_GREN_PRETOKENIZED;
const int UseLanguageID = LanguageID == -1 ? HandleEngine(hEngine)->DefaultLanguage : LanguageID;
const bool ApplyModel = (MorphologicalFlags & SOL_GREN_MODEL) == SOL_GREN_MODEL;
const bool ReorderTree = (SyntacticFlags&SOL_GREN_REORDER_TREE) == SOL_GREN_REORDER_TREE;
const bool FindFacts = (SyntacticFlags&SOL_GREN_FINDFACTS) == SOL_GREN_FINDFACTS;
const bool Schedule1 = (SyntacticFlags & ~SOL_GREN_REORDER_TREE) == 0;
WrittenTextAnalysisSession current_analysis(HandleEngine(hEngine)->dict.get(), nullptr);
current_analysis.params.SetLanguageID(UseLanguageID);
current_analysis.params.Pretokenized = Pretokenized;
current_analysis.params.AllowPrimaryFuzzyWordRecog = Allow_Fuzzy;
current_analysis.params.ApplyModel = ApplyModel;
// Ограничение на суммарное затраченное время в миллисекундах
const int MaxMillisecTimeout = 0x002fffff & Constraints;
//lem::mout->printf( "Constraints=%d MaxMillisecTimeout=%d\n", Constraints, MaxMillisecTimeout );
current_analysis.params.timeout.max_elapsed_millisecs = MaxMillisecTimeout > 0 ? MaxMillisecTimeout : lem::int_max;
// Ограничение на макс. число параллельно проверяемых альтернативных путей построения синтаксического дерева.
// Оно задается в старших 10 битах
const int MaxAlt = ((0xffC00000 & Constraints) >> 22) & 0x000002ff;
current_analysis.params.timeout.max_alt = MaxAlt;
current_analysis.params.timeout.max_bottomup_trees = MaxAlt;
current_analysis.params.timeout.max_recursion_depth = 500;
if ( /*(SyntacticFlags & SOL_GREN_SKIPTOKENS)==SOL_GREN_SKIPTOKENS &&*/ !CompleteAnalysisOnly)
{
current_analysis.params.CompleteAnalysisOnly = false;
current_analysis.params.ConfigureSkipToken();
}
if (Schedule1)
{
current_analysis.params.UseTopDownThenSparse = true;
current_analysis.params.CompleteAnalysisOnly = true;
}
else
{
current_analysis.params.CompleteAnalysisOnly = CompleteAnalysisOnly;
}
current_analysis.params.ReorderTree = ReorderTree;
current_analysis.FindFacts = FindFacts;
current_analysis.SyntacticAnalysis(Sentence);
return current_analysis.PickPack();
}
CATCH_API(hEngine)
{
return nullptr;
}
return nullptr;
}
GREN_API(HGREN_RESPACK) sol_SyntaxAnalysis8(
HGREN hEngine,
const char *SentenceUtf8,
int MorphologicalFlags,
int SyntacticFlags,
int Constraints,
int LanguageID
)
{
return sol_SyntaxAnalysis(
hEngine,
lem::from_utf8(SentenceUtf8).c_str(),
MorphologicalFlags,
SyntacticFlags,
Constraints,
LanguageID
);
}
GREN_API(HGREN_RESPACK) sol_SyntaxAnalysisA(
HGREN hEngine,
const char *Sentence,
int MorphologicalFlags,
int SyntacticFlags,
int Constraints,
int LanguageID
)
{
return sol_SyntaxAnalysis(
hEngine,
lem::to_unicode(Sentence).c_str(),
MorphologicalFlags,
SyntacticFlags,
Constraints,
LanguageID
);
}
GREN_API(void) sol_DeleteResPack(HGREN_RESPACK hPack)
{
try
{
delete (Solarix::Res_Pack*)hPack;
}
catch (...)
{
}
}
// *************************************************************
// Возвращает "имя" словарной статьи - обычно это базовая форма,
// но могут быть особые случаи (например, статьи ЕСТЬ - кушать
// и ЕСТЬ - иметься)
//
// http://www.solarix.ru/api/en/sol_GetEntryName.shtml
// http://www.solarix.ru/api/ru/sol_GetEntryName.shtml
// *************************************************************
GREN_API(int) sol_GetEntryName(
HGREN h,
int EntryIndex,
wchar_t *Result
)
{
if (!h || EntryIndex == -1 || Result == nullptr)
return -2;
*Result = 0;
DEMO_SINGLE_THREAD(h)
try
{
const SG_Entry &e = HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex);
lem::UFString w(e.GetName().c_str());
const int icasing = e.GetAttrState(GramCoordAdr(CharCasing));
switch (icasing)
{
case UNKNOWN:
case DECAPITALIZED_CASED:
{
w.to_lower();
break;
}
case FIRST_CAPITALIZED_CASED:
{
w.to_Aa();
break;
}
case ALL_CAPITALIZED_CASED:
{
w.to_upper();
break;
}
case EACH_LEXEM_CAPITALIZED_CASED:
{
Solarix::MakeEachLexemAa(w);
break;
}
}
w.subst_all(L" - ", L"-");
w.subst_all(L" ' ", L"'");
lem::lem_strcpy(Result, w.c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
GREN_API(int) sol_GetEntryName8(HGREN h, int EntryIndex, char *Result)
{
try
{
wchar_t buf[lem::UCString::max_len + 1];
*buf = 0;
int rc = sol_GetEntryName(h, EntryIndex, buf);
lem::FString u8 = lem::to_utf8(buf);
lem::lem_strcpy(Result, u8.c_str());
return rc;
}
catch (...)
{
return -1;
}
}
// Возвращает индекс грамматического класса, к которому относится
// словарная статья.
// http://www.solarix.ru/api/en/sol_GetEntryClass.shtml
// http://www.solarix.ru/api/ru/sol_GetEntryClass.shtml
GREN_API(int) sol_GetEntryClass(HGREN h, int EntryIndex)
{
if (!h || !HandleEngine(h)->dict || EntryIndex == -1)
return -2;
try
{
return HandleEngine(h)->dict->GetSynGram().GetEntry(EntryIndex).GetClass();
}
catch (...)
{
}
return UNKNOWN;
}
// http://www.solarix.ru/api/en/sol_GetEntryCoordState.shtml
GREN_API(int) sol_GetEntryCoordState(HGREN hEngine, int EntryID, int CategoryID)
{
if (!hEngine || !HandleEngine(hEngine)->dict || CategoryID == -1 || EntryID == -1)
return -2;
try
{
const Solarix::SG_Entry &e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(EntryID);
const int state = e.GetAttrState(GramCoordAdr(CategoryID));
return state;
}
catch (...)
{
}
return UNKNOWN;
}
// В некоторых случаях статья может иметь несколько состояний одной координаты. Например,
// падежная валентность глагола РИСОВАТЬ включает винительный, дательный и творительный падежи.
// Данная процедура позволяет узнать, присутствует ли координатная пара среди атрибутов.
// http://www.solarix.ru/api/ru/sol_FindEntryCoordPair.shtml
GREN_API(int) sol_FindEntryCoordPair(HGREN hEngine, int EntryID, int CategoryID, int StateID)
{
if (!hEngine || !HandleEngine(hEngine)->dict || EntryID == -1 || CategoryID == -1 || StateID == -1)
return -2;
try
{
const Solarix::SG_Entry &e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(EntryID);
return e.attrs().FindOnce(Solarix::GramCoordPair(CategoryID, StateID)) != -1;
}
catch (...)
{
}
return UNKNOWN;
}
// **********************************************************************************
// Возвращает имя грамматического класса (части речи) по его ID
// http://www.solarix.ru/api/en/sol_GetClassName.shtml
// http://www.solarix.ru/api/ru/sol_GetClassName.shtml
//
// Результат: 0 имя часчти речи скопировано в Result
// -1 ошибка, например указан некорректный ClassIndex
// **********************************************************************************
GREN_API(int) sol_GetClassName(HGREN hEngine, int ClassID, wchar_t *Result)
{
if (!hEngine || !HandleEngine(hEngine)->dict || ClassID == -1)
return -2;
*Result = 0;
try
{
const Solarix::GramClass &c = HandleEngine(hEngine)->dict->GetSynGram().classes()[ClassID];
wcscpy(Result, c.GetName().c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
// http://www.solarix.ru/api/en/sol_GetClassName.shtml
GREN_API(int) sol_GetClassName8(HGREN h, int ClassID, char *ResultUtf8)
{
if (!h || !HandleEngine(h)->dict || ClassID == -1)
return -2;
*ResultUtf8 = 0;
try
{
const Solarix::GramClass &c = HandleEngine(h)->dict->GetSynGram().classes()[ClassID];
strcpy(ResultUtf8, lem::to_utf8(c.GetName().c_str()).c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
// http://www.solarix.ru/api/en/sol_GetCoordName.shtml
GREN_API(int) sol_GetCoordName(HGREN hEngine, int CoordID, wchar_t *Result)
{
if (!hEngine || !HandleEngine(hEngine)->dict || CoordID == -1)
return -2;
*Result = 0;
try
{
const Solarix::GramCoord & c = HandleEngine(hEngine)->dict->GetSynGram().coords()[CoordID];
wcscpy(Result, c.GetName().string().c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
// http://www.solarix.ru/api/en/sol_GetCoordType.shtml
// http://www.solarix.ru/api/ru/sol_GetCoordType.shtml
GREN_API(int) sol_GetCoordType(HGREN hEngine, int CoordId, int ClassId)
{
if (!hEngine || !HandleEngine(hEngine)->dict || CoordId == -1 || ClassId == -1)
return -2;
try
{
const Solarix::SG_Class &cls = HandleEngine(hEngine)->dict->GetSynGram().GetClass(ClassId);
if (cls.attrs().find(CoordId) != UNKNOWN)
return 0;
if (cls.dims().find(CoordId) != UNKNOWN)
return 1;
if (cls.tags().find(CoordId) != UNKNOWN)
return 2;
return UNKNOWN;
}
catch (...)
{
}
return UNKNOWN;
}
// http://www.solarix.ru/api/en/sol_GetCoordName.shtml
GREN_API(int) sol_GetCoordName8(HGREN hEngine, int CoordID, char *ResultUtf8)
{
if (!hEngine || !HandleEngine(hEngine)->dict || CoordID == -1)
return -2;
*ResultUtf8 = 0;
try
{
const Solarix::GramCoord & c = HandleEngine(hEngine)->dict->GetSynGram().coords()[CoordID];
strcpy(ResultUtf8, lem::to_utf8(c.GetName().string().c_str()).c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
// Return: name of the grammatical coordinate state
// http://www.solarix.ru/api/en/sol_GetCoordStateName.shtml
GREN_API(int) sol_GetCoordStateName(HGREN hEngine, int CoordID, int StateID, wchar_t *Result)
{
if (!hEngine || !HandleEngine(hEngine)->dict || CoordID == -1)
return -2;
*Result = 0;
try
{
const Solarix::GramCoord & c = HandleEngine(hEngine)->dict->GetSynGram().coords()[CoordID];
if (!c.IsBistable())
wcscpy(Result, c.GetStateName(StateID).c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
// http://www.solarix.ru/api/en/sol_GetCoordStateName.shtml
GREN_API(int) sol_GetCoordStateName8(HGREN hEngine, int CoordID, int StateID, char *ResultUtf8)
{
if (!hEngine || !HandleEngine(hEngine)->dict || CoordID == -1)
return -2;
*ResultUtf8 = 0;
try
{
const Solarix::GramCoord & c = HandleEngine(hEngine)->dict->GetSynGram().coords()[CoordID];
if (!c.IsBistable())
strcpy(ResultUtf8, lem::to_utf8(c.GetStateName(StateID).c_str()).c_str());
return 0;
}
catch (...)
{
}
return UNKNOWN;
}
// Return the number of enumeration items (coordinate states).
// It returns 0 for bistable coordinates without explicit states
// http://www.solarix.ru/api/en/sol_CountCoordStates.shtml
GREN_API(int) sol_CountCoordStates(HGREN hEngine, int CoordID)
{
if (!hEngine || !HandleEngine(hEngine)->dict || CoordID == -1)
return -2;
try
{
const Solarix::GramCoord & c = HandleEngine(hEngine)->dict->GetSynGram().coords()[CoordID];
return c.GetTotalStates();
}
catch (...)
{
}
return UNKNOWN;
}
// ***************************************************
// Сколько альтернативных вариантов анализа фразы?
// ***************************************************
GREN_API(int) sol_CountGrafs(HGREN_RESPACK hPack)
{
if (hPack == nullptr)
return 0;
return CastSizeToInt(((Res_Pack*)hPack)->vars().size());
}
// *************************************************************
// Сколько деревьев (узлов верхнего уровня) в заданном графе?
// *************************************************************
GREN_API(int) sol_CountRoots(HGREN_RESPACK hPack, int iGraf)
{
try
{
if (hPack == nullptr)
return 0;
if (iGraf < 0 || iGraf >= CastSizeToInt(((Res_Pack*)hPack)->vars().size()))
return -1;
return ((Res_Pack*)hPack)->vars()[iGraf]->size();
}
catch (...)
{
return -1;
}
}
GREN_API(int) sol_GetGrafScore(HGREN_RESPACK hPack, int iGraf)
{
try
{
if (hPack == nullptr)
return 0;
if (iGraf < 0 || iGraf >= CastSizeToInt(((Res_Pack*)hPack)->vars().size()))
return -1;
return ((Res_Pack*)hPack)->vars()[iGraf]->GetScore();
}
catch (...)
{
return -1;
}
}
// *****************************************************
// Получение указателя на заданный узел верхнего уровня
// *****************************************************
GREN_API(HGREN_TREENODE) sol_GetRoot(HGREN_RESPACK hPack, int iGraf, int iRoot)
{
#if !defined SOL_NO_AA
if (hPack == nullptr)
return nullptr;
try
{
return &(((Res_Pack*)hPack)->vars()[iGraf]->get(iRoot));
}
catch (...)
{
return nullptr;
}
#else
return nullptr;
#endif
}
// *****************************************************
// Количество прикрепленных к данному узлу веток
// *****************************************************
GREN_API(int) sol_CountLeafs(HGREN_TREENODE hNode)
{
#if !defined SOL_NO_AA
if (hNode == nullptr)
return 0;
return CastSizeToInt(HandleNode(hNode)->leafs().size());
#else
return 0;
#endif
}
// *****************************************************
// Возвращает указатель на заданную ветку
// http://www.solarix.ru/api/ru/sol_GetLeaf.shtml
// *****************************************************
GREN_API(HGREN_TREENODE) sol_GetLeaf(HGREN_TREENODE hNode, int iLeaf)
{
#if !defined SOL_NO_AA
if (hNode == nullptr)
return 0;
try
{
return &HandleNode(hNode)->leafs()[iLeaf];
}
catch (...)
{
return nullptr;
}
#else
return nullptr;
#endif
}
// http://www.solarix.ru/api/ru/sol_GetLeafLinkType.shtml
GREN_API(int) sol_GetLeafLinkType(HGREN_TREENODE hNode, int iLeaf)
{
#if !defined SOL_NO_AA
if (hNode == nullptr)
return 0;
try
{
return HandleNode(hNode)->leafs()[iLeaf].GetLink().GetState();
}
catch (...)
{
return -2;
}
#else
return UNKNOWN;
#endif
}
// *****************************************************
// Возвращает индекс словарной статьи в узле.
// *****************************************************
GREN_API(int) sol_GetNodeIEntry(HGREN hEngine, HGREN_TREENODE hNode)
{
if (hEngine == nullptr || HandleEngine(hEngine)->dict.IsNull())
return UNKNOWN;
#if !defined SOL_NO_AA
if (hNode == nullptr)
return 0;
int ekey = HandleNode(hNode)->GetNode().GetEntryKey();
return ekey;//HandleEngine(hEngine)->dict->GetSynGram().FindEntryIndexByKey(ekey);
#else
return UNKNOWN;
#endif
}
GREN_API(int) sol_GetNodeVerIEntry(HGREN hEngine, HGREN_TREENODE hNode, int iver)
{
if (hEngine == nullptr || HandleEngine(hEngine)->dict.IsNull())
return UNKNOWN;
#if !defined SOL_NO_AA
if (hNode == nullptr)
return 0;
int ekey = UNKNOWN;
if (iver == 0)
ekey = HandleNode(hNode)->GetNode().GetEntryKey();
else
ekey = HandleNode(hNode)->GetNode().GetAlts()[iver - 1]->GetEntryKey();
return ekey;//HandleEngine(hEngine)->dict->GetSynGram().FindEntryIndexByKey(ekey);
#else
return UNKNOWN;
#endif
}
GREN_API(int) sol_GetNodeVersionCount(HGREN hEngine, HGREN_TREENODE hNode)
{
#if !defined SOL_NO_AA
if (hNode == nullptr)
return 0;
return CastSizeToInt(HandleNode(hNode)->GetNode().GetAlts().size()) + 1;
#else
return 0;
#endif
}
GREN_API(int) sol_GetNodePosition(HGREN_TREENODE hNode)
{
if (hNode == nullptr)
return -1;
#if !defined SOL_NO_AA
const Word_Form &wf = HandleNode(hNode)->GetNode();
return wf.GetOriginPos();
#else
return -1;
#endif
}
GREN_API(int) sol_CountNodeMarks(HGREN_TREENODE hNode)
{
if (hNode == nullptr)
return -1;
try
{
return CastSizeToInt(HandleNode(hNode)->GetMarks().size());
}
catch (...)
{
return -1;
}
}
GREN_API(int) sol_GetNodeMarkNameW(HGREN_TREENODE hNode, int mark_index, wchar_t * name_buffer)
{
if (hNode == nullptr)
return -1;
try
{
*name_buffer = 0;
wcscpy(name_buffer, HandleNode(hNode)->GetMarks()[mark_index]->GetName().c_str());
return 0;
}
catch (...)
{
return -1;
}
}
GREN_API(HGREN_LONGSTRING) sol_SerializeNodeMark(HGREN hEngine, HGREN_TREENODE hNode, int mark_index, int format)
{
if (hNode == nullptr)
return nullptr;
try
{
const TreeMarks & marks = *HandleNode(hNode)->GetMarks()[mark_index];
lem::MemFormatter mem;
marks.PrintXML(mem, *HandleEngine(hEngine)->dict);
lem::UFString * str = new lem::UFString(mem.string());
return str;
}
catch (...)
{
return nullptr;
}
}
GREN_API(int) sol_GetLongStringLenW(HGREN_LONGSTRING hString)
{
if (hString == nullptr)
return 0;
try
{
return ((const lem::UFString*)hString)->length();
}
catch (...)
{
return -1;
}
}
GREN_API(int) sol_GetLongStringW(HGREN_LONGSTRING hString, wchar_t * buffer)
{
if (hString == nullptr)
return 0;
try
{
const lem::UFString & str = *(const lem::UFString*)hString;
if (str.empty())
*buffer = 0;
else
wcscpy(buffer, str.c_str());
return str.length();
}
catch (...)
{
return -1;
}
}
GREN_API(int) sol_DeleteLongString(HGREN_LONGSTRING hString)
{
try
{
delete (lem::UFString*)hString;
return 0;
}
catch (...)
{
return -1;
}
}
// *****************************************************
// Текстовое содержимое узла.
//
// *****************************************************
GREN_API(void) sol_GetNodeContents(HGREN_TREENODE hNode, wchar_t *Buffer)
{
if (Buffer == nullptr)
return;
try
{
*Buffer = 0;
#if !defined SOL_NO_AA
if (hNode == nullptr)
return;
const Word_Form &wf = HandleNode(hNode)->GetNode();
UCString s = wf.GetName()->ToWord();
wcscpy(Buffer, s.c_str());
#endif
}
catch (...)
{
}
return;
}
GREN_API(void) sol_GetNodeContents8(HGREN_TREENODE hNode, char *BufferUtf8)
{
if (BufferUtf8 == nullptr)
return;
try
{
*BufferUtf8 = 0;
#if !defined SOL_NO_AA
if (hNode == nullptr)
return;
const Word_Form &wf = HandleNode(hNode)->GetNode();
UCString s = wf.GetName()->ToWord();
strcpy(BufferUtf8, lem::to_utf8(s.c_str()).c_str());
#endif
}
catch (...)
{
}
return;
}
GREN_API(int) sol_GetNodeContentsLen(HGREN_TREENODE hNode)
{
try
{
#if !defined SOL_NO_AA
if (hNode == nullptr)
return -1;
const int len = HandleNode(hNode)->GetNode().GetName()->ToWord().length();
return len;
#endif
}
catch (...)
{
}
return -1;
}
GREN_API(int) sol_RestoreCasing(HGREN hEngine, wchar_t *Word, int EntryIndex)
{
if (!hEngine || EntryIndex == -1 || lem::lem_is_empty(Word))
return -2;
try
{
lem::UFString w(Word);
CasingCoder& cc = HandleEngine(hEngine)->dict->GetLexAuto().GetCasingCoder();
cc.RestoreCasing(w, EntryIndex);
w.subst_all(L" - ", L"-");
w.subst_all(L" ' ", L"'");
lem::lem_strcpy(Word, w.c_str());
return 0;
}
CATCH_API(hEngine);
return 3;
}
GREN_API(int) sol_RestoreCasing8(HGREN hEngine, char *WordUtf8, int EntryIndex)
{
if (WordUtf8 == nullptr || EntryIndex == UNKNOWN)
return -1;
wchar_t buf[lem::UCString::max_len + 1];
lem::lem_strcpy(buf, lem::from_utf8(WordUtf8).c_str());
int rc = sol_RestoreCasing(hEngine, buf, EntryIndex);
if (rc == 0)
{
strcpy(WordUtf8, lem::to_utf8(buf).c_str());
}
return rc;
}
// ***********************************************************************
// Приведение к базовой форме. Вернет 1, если преобразование имело место.
//
// Аргумент AllowDynforms позволяет включить сложную морфологию.
// ***********************************************************************
GREN_API(int) sol_TranslateToBase(HGREN hEngine, wchar_t *Word, int AllowDynforms)
{
if (hEngine == nullptr || HandleEngine(hEngine)->dict.IsNull() || Word == nullptr)
return UNKNOWN;
DEMO_SINGLE_THREAD(hEngine)
try
{
// Быстрый поиск словоформы.
UCString w(Word);
int ientry = UNKNOWN;
if (HandleEngine(hEngine)->seeker.IsNull())
{
MCollect<Word_Coord> found_list;
HandleEngine(hEngine)->dict->GetLexAuto().ProjectWord(w, found_list, UNKNOWN);
if (!found_list.empty())
ientry = found_list.front().GetEntry();
}
else
{
w.to_upper();
ientry = HandleEngine(hEngine)->seeker->Find(w, AllowDynforms == 1);
}
if (ientry == UNKNOWN)
return 0;
// Зная индекс словарной статьи, можем преобразовать к названию статьи.
const SG_Entry &e = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(ientry);
wcscpy(Word, e.GetName().c_str());
sol_RestoreCasing(hEngine, Word, e.GetKey());
return 1;
}
CATCH_API(hEngine)
return UNKNOWN;
}
// *****************************************************************
// Возвращает список базовых форм для слова.
// *****************************************************************
GREN_API(HGREN_STR) sol_TranslateToBases(
HGREN hEngine,
const wchar_t *Word,
int AllowDynforms
)
{
if (!hEngine || !HandleEngine(hEngine)->dict || Word == nullptr)
return nullptr;
DEMO_SINGLE_THREAD(hEngine)
try
{
UCString uWord(Word);
uWord.to_upper();
// Look for it in dictionary
Lexem ml(uWord);
RC_Lexem rc(&ml, null_deleter());
lem::MCollect<ProjScore> val_list;
lem::MCollect<Solarix::Word_Coord> coords;
PtrCollect<LA_ProjectInfo> inf_list;
HandleEngine(hEngine)->dict->GetLexAuto().ProjectWord(
rc,
coords,
val_list,
inf_list,
AllowDynforms ? LexicalAutomat::Dynforms_Last_Chance : LexicalAutomat::Wordforms,
0,
HandleEngine(hEngine)->DefaultLanguage,
nullptr
);
if (coords.empty())
return nullptr;
SynGram &sg = HandleEngine(hEngine)->dict->GetSynGram();
GREN_Strings *res = new GREN_Strings;
wchar_t buf[lem::UCString::max_len + 1];
for (lem::Container::size_type i = 0; i < coords.size(); i++)
{
const int ekey = coords[i].GetEntry();
const SG_Entry &e = sg.GetEntry(ekey);
if (!e.IsQuantor())
{
const UCString &s = e.GetName();
lem::lem_strcpy(buf, s.c_str());
sol_RestoreCasing(hEngine, buf, ekey);
lem::UCString u(buf);
if (res->list.find(u) == UNKNOWN)
res->list.push_back(u);
}
}
if (res->list.empty())
{
res->list.push_back(lem::UCString(Word));
}
return res;
}
CATCH_API(hEngine)
return nullptr;
}
// *****************************************************************************
// Стеммер - возвращает число символов в слове, которые составляют
// "корень", неизменный для всех форм этого слова. В случае невозможности
// выделить корень, вернет 0.
// *****************************************************************************
GREN_API(int) sol_Stemmer(HGREN hEngine, const wchar_t *Word)
{
#if defined GM_STEMMER
if (!hEngine || !HandleEngine(hEngine)->dict || Word == nullptr)
return -1;
DEMO_SINGLE_THREAD(hEngine)
try
{
// Быстрый поиск словоформы по лексикону
UCString w(Word);
int ientry = HandleEngine(hEngine)->seeker.IsNull() ? UNKNOWN : HandleEngine(hEngine)->seeker->Find(w, true);
if (ientry == UNKNOWN)
{
// Это слово отсутствует в лексиконе.
// Попробуем применить известные правила аффиксов.
LEM_CHECKIT_Z(HandleEngine(hEngine)->fuzzy.NotNull());
if (HandleEngine(hEngine)->fuzzy.NotNull())
// Отсутствует нужный модуль.
return -1;
lem::MCollect<UCString> roots;
lem::MCollect<float> roots_val;
HandleEngine(hEngine)->fuzzy->GetAffixTable().GenerateRoots(w, 0.0, roots, roots_val);
if (!roots.empty())
{
// Вообще говоря, тут есть выбор - возвращать самый длинный корень или самый короткий.
int best_len = roots.front().length();
for (lem::Container::size_type j = 1; j < roots.size(); j++)
{
best_len = std::min(best_len, roots[j].length());
}
return best_len;
}
if (!!HandleEngine(hEngine)->dict->stemmer)
{
// Попробуем использовать загруженный стеммер.
UCString stem;
if (HandleEngine(hEngine)->dict->stemmer->Stem(w, stem))
return stem.length();
}
return 0;
}
else
{
// Так как словарная статья найдена, то используем известный для нее корень.
return HandleEngine(hEngine)->dict->GetSynGram().GetEntry(ientry).GetRoot().length();
}
return 0;
}
CATCH_API(hEngine)
return -2;
#else
return -1;
#endif
}
// **********************************************************
// Поиск в тезаурусе связанных статей для указанной iEntry.
// Возвращается список индексов статей.
// **********************************************************
GREN_API(HGREN_INTARRAY) sol_SeekThesaurus(
HGREN hEngine,
int iEntry,
int Synonyms,
int Grammar_Links,
int Translations,
int Semantics,
int nJumps
)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict)
return nullptr;
#if defined GM_THESAURUS
lem::MCollect<int> *list = new lem::MCollect<int>();
list->reserve(32);
DEMO_SINGLE_THREAD(hEngine)
try
{
HandleEngine(hEngine)->dict->GetLexAuto().SeekThesaurus(HandleEngine(hEngine)->dict->GetSynGram().GetEntry(iEntry).GetKey(), Synonyms == 1, Grammar_Links == 1, Translations == 1, Semantics == 1, nJumps, *list);
for (lem::Container::size_type i = 0; i < list->size(); ++i)
{
#if LEM_DEBUGGING==1
int key = list->get(i);
int ie = key;
#endif
(*list)[i] = list->get(i);
}
return list;
}
CATCH_API(hEngine)
delete list;
return nullptr;
#endif
return nullptr;
}
GREN_API(HGREN_INTARRAY) sol_Thesaurus(
HGREN hEngine,
int iEntry,
int Link
)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict || iEntry == UNKNOWN || Link == UNKNOWN)
return nullptr;
#if defined SOLARIX_PRO && defined GM_THESAURUS
lem::MCollect<int> *list = new lem::MCollect<int>();
list->reserve(32);
try
{
const int ekey = HandleEngine(hEngine)->dict->GetSynGram().GetEntry(iEntry).GetKey();
Tree_Link l(Link);
IntCollect links;
HandleEngine(hEngine)->dict->GetSynGram().Get_Net().Find_Linked_Entries(ekey, l, links, nullptr);
for (lem::Container::size_type i = 0; i < links.size(); ++i)
{
const int key = links[i];
list->push_back(key);
}
return list;
}
catch (...)
{
delete list;
return nullptr;
}
#endif
return nullptr;
}
// ***********************************************************************
// http://www.solarix.ru/for_developers/api/ngrams-api.shtml
//
// Возвращается количество N-грамм заданного порядка (Order=1...5) и
// типа (0-буквальные,1-нормализованные).
//
// Так как записей может быть>4 млрд, то 64-битный результат возвращается
// в виде двух 32-битных частей.
//
// Возвращаемое значение: 1 - успех, 0 - нет записей или какая-то ошибка.
// ************************************************************************
GREN_API(int) sol_CountNGrams(HGREN hEngine, int type, int Order, unsigned int *Hi, unsigned int *Lo)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict || type < 0 || type>1 || Order < 1 || Order>5 || Hi == nullptr || Lo == nullptr)
return 0;
#if !defined SOL_NO_NGRAMS
try
{
*Hi = *Lo = 0;
lem::Ptr<Ngrams> ngrams = HandleEngine(hEngine)->dict->GetNgrams();
if (ngrams.NotNull())
{
lem::int64_t c = 0;
switch (Order)
{
case 1: c = type == 1 ? ngrams->CountRaw1() : ngrams->CountLiteral1(); break;
case 2: c = type == 1 ? ngrams->CountRaw2() : ngrams->CountLiteral2(); break;
case 3: c = type == 1 ? ngrams->CountRaw3() : ngrams->CountLiteral3(); break;
case 4: c = type == 1 ? ngrams->CountRaw4() : ngrams->CountLiteral4(); break;
case 5: c = type == 1 ? ngrams->CountRaw5() : ngrams->CountLiteral5(); break;
}
*Hi = (lem::uint32_t)(c >> 32);
*Lo = (lem::uint32_t)(c);
return c > 0;
}
return 0;
}
catch (...)
{
return 0;
}
#endif
return 0;
}
// http://www.solarix.ru/for_developers/api/ngrams-api.shtml
GREN_API(int) sol_Seek1Grams(HGREN hEngine, int type, const wchar_t *word1)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict || word1 == nullptr)
return 0;
#if !defined SOL_NO_NGRAMS
try
{
lem::Ptr<Ngrams> ngrams = HandleEngine(hEngine)->dict->GetNgrams();
if (ngrams.NotNull())
{
float wf = 0;
int iw = 0;
if (ngrams->FindNGrams(type == 1, word1, wf, iw))
return iw;
return 0;
}
return 0;
}
catch (...)
{
return 0;
}
#endif
return 0;
}
GREN_API(int) sol_Seek1Grams8(HGREN hEngine, int type, const char *Word1Utf8)
{
return sol_Seek1Grams(hEngine, type, lem::from_utf8(Word1Utf8).c_str());
}
// http://www.solarix.ru/for_developers/api/ngrams-api.shtml
GREN_API(int) sol_Seek2Grams(
HGREN hEngine,
int type,
const wchar_t *word1,
const wchar_t *word2
)
{
if (hEngine == nullptr || !HandleEngine(hEngine)->dict || word1 == nullptr || word2 == nullptr)
return 0;
#if !defined SOL_NO_NGRAMS
try
{
lem::Ptr<Ngrams> ngrams = HandleEngine(hEngine)->dict->GetNgrams();
if (ngrams.NotNull())
{
float wf = 0;
int iw = 0;
if (n