/******************************************************************** Читаем из текстового файла секции расширенного описания статьи. Под расширенным описанием подразумевается список координат-атрибутов, формы, грамматическая сеть и специфические для производных классов поля. *********************************************************************/ void Base_Entry::LoadTxtEx( Macro_Parser& txtfile, Grammar& gram ) { // *** Считываем описание статьи *** bool looping = true; BethToken token; CP_Array common; while (looping) { if (txtfile.eof()) { Print_Error(txtfile); gram.GetIO().merr().printf("End of file has been reached before entry body completely loaded\n"); throw E_ParserError(); } const BSourceState back = txtfile.tellp(); token = txtfile.read(); if (token.GetToken() == B_CFIGPAREN) { // Описание статьи закончено looping = false; break; } if (token.GetToken() == B_OTRIPAREN) { txtfile.seekp(token.GetBegin()); SkipNetSection(txtfile, gram); continue; } if (ProcessSection(txtfile, gram, token)) continue; txtfile.seekp(back); if (LoadAttribute(txtfile, gram)) continue; // Загружаем словоформу. LoadForm( txtfile, gram, common, GramCoordAdr(UNKNOWN, UNKNOWN), UNKNOWN, GramCoordAdr(UNKNOWN, UNKNOWN), UNKNOWN, UNKNOWN ); } // конец цикла считывания описания статьи return; }
void Form_Table::LoadBody(Macro_Parser &txtfile, Grammar& gram) { txtfile.read_it(B_OFIGPAREN); // *** Считываем описание Таблицы *** BethToken token; CP_Array common; FOREVER { if (txtfile.eof()) { Print_Error(txtfile); gram.GetIO().merr().printf("End of file has been reached before entry body completely loaded\n"); throw E_ParserError(); } const BSourceState back = txtfile.tellp(); token = txtfile.read(); if (token.GetToken() == B_CFIGPAREN) { // Описание Таблицы закончено. break; } if (ProcessSection(txtfile,gram,token)) continue; txtfile.seekp(back); if (LoadAttribute(txtfile,gram)) continue; LoadForm( txtfile, gram, common, GramCoordAdr(UNKNOWN,UNKNOWN), UNKNOWN, GramCoordAdr(UNKNOWN,UNKNOWN), UNKNOWN, UNKNOWN ); // Загружаем словоформу. } // конец цикла считывания описания статьи return; }
/**************************************************************************** Загружаем из текстового файла множество символьных строк. Если слово только одно, то оно может идти непосредственно. Если необходимо определить несколько слов, то они заключаются в фигурные скобочки. Форматы: 1. лексема 2. { лексема лексема ... лексема } Второй формат охватывает также и первый, если в фигурных скобочках указать только одну лексему. Способ хранения в обоих случаях одинаков! *****************************************************************************/ void UCStringSet::LoadTxt( const Sol_IO &io, Macro_Parser &txtfile ) { BethToken t = txtfile.read(); if( t.GetToken()!=B_OFIGPAREN ) { // Первый формат. push_back(t.c_str()); } else { // Второй формат. while( !txtfile.eof() ) { if( txtfile.eof() ) { Print_Error(t,txtfile); io.merr().printf( "End of file has been reached before " "set of lexems completely loaded\n" ); LEM_STOPIT; } t = txtfile.read(); if( t.GetToken()==B_CFIGPAREN ) break; push_back(t.string()); weight.push_back(1); } } return; }
/************************************************************************ Метод вызывается классом Automaton для загрузки специфических для Продукционной Машины структур данных. В случае успешного распознавания возвращаем true, иначе - false. *************************************************************************/ bool PM_Automat::ProcessLexem( const BethToken &t, Macro_Parser &txtfile, const Binarization_Options &options ) { if( t.GetToken()==B_FUNCTION ) { const TrFunction *fun = functions->Get().CompileDeclaration( *this, txtfile, procedure_declarations ); return true; } else if( t.string().eqi(L"static") ) { // это должно быть объявление глобальной переменной: // static тип = значение; // Мы скомпилируем его как функцию, инициализирующую переменную в глобальном пространстве. functions->Get().CompileStatic( *this, txtfile, procedure_declarations ); return true; } return false; }
/********************************************************************* Этот метод вызывается объектом класса Automaton при чтении секции automat gg {...} из текстового файла Словаря. Нас просят определить, должен ли структурный элемент в исходном текстовом файле Словаря, с начальным токеном t, загружаться нашим классом. В этом случае мы загружаем и возвращаем true, в противном случае возвращаем false. Так как мы перегрузили этот метод у родительского класса Grammar, "вручную" вызываем и его метод, чтобы Grammar мог загрузить общие для любой грамматики элементы Словаря. Графическая Грамматика - самая простая из всех автоматов Системы. В текстовом файле Словаря она описывается как набор статей, кажда статья есть множество представлений (отличающихся кодом) одной буквы Алфавита, например: большие и малые. Кроме того, Грамматика загружает Калибраторы. Каждый Калибратор определяет относительную частоту использования буквы в реальных текстах. С помошью калибраторов можно оптимизировать поиск символа в Алфавите, так как наиболее часто используемые символы будут размещены в просмотровой таблице сзади, и будут быстрее отыскиваться, без лишнего просмотра редких букв (поиск всегда будет идти с конца). **********************************************************************/ bool GraphGram::ProcessLexem( const BethToken &t, Macro_Parser& txt, const Binarization_Options &options ) { if (t.GetToken() == B_ALPHABET) { alphabet->LoadTxt(txt, GetDict()); return true; } else if (t.string().eqi(L"syllab_rule")) { SyllabRule *rule = new SyllabRule(); rule->LoadTxt(txt, GetDict()); rule->Store(storage); return true; } else if (t.string().eqi(L"operation")) { GG_CharOperation *x = new GG_CharOperation(); x->LoadTxt(txt, GetDict()); const int id = storage->FindCharOperation(x->GetName()); if (id != UNKNOWN) { lem::Iridium::Print_Error(t, txt); dict->GetIO().merr().printf("Character operation %us is already declared\n", x->GetName().c_str()); throw lem::E_BaseException(); } storage->StoreCharOperation(x); delete x; return true; } // С остальными элементами пусть разбирается базовый класс Грамматики. return Grammar::ProcessLexem(t, txt, options); }
/********************************************************** Загрузка тела описания статьи из текстового файла Словаря. Само тело состоит из нескольких факультативных частей. ***********************************************************/ void Base_Entry::LoadBody( Macro_Parser &txtfile, Grammar& gram ) { if (is_realized) { // Если теперь встретится '{', то значит имеем расширенное // описание словарной статьи. const BSourceState prefig = txtfile.tellp(); const BethToken isfig = txtfile.read(); #if LEM_DEBUGGING==1 int nf = CountForms(); #endif if (isfig.GetToken() != B_OFIGPAREN) txtfile.seekp(prefig); else LoadTxtEx(txtfile, gram); if (CountForms() == 0) BeforeFirstForm(gram); CheckAttr(txtfile, gram); } #if defined SOL_DETAILED if (gram.GetDict().GetDebugLevel_ir() >= 3) { // Эхо-сообщение: закончили трансляцию статьи. gram.GetIO().mecho().printf("Ok\n"); } #endif return; }
void SG_Language::LoadTxt(int Id, Macro_Parser& txt, GraphGram& gram) { // Store the beginning of language specification BSourceState beg = txt.tellp(); id = Id; name = txt.read().string(); const bool Me = gram.GetDict().GetDebugLevel_ir() >= 3; if (Me) { // Эхо-сообщение: начали трансляцию языка. gram.GetIO().mecho().printf( "%us [%vfE%us%vn]->", sol_get_token(B_LANGUAGE).c_str(), name.c_str() ); } BethToken t = txt.read(); if (t.GetToken() == B_AS) { c_name = txt.read().string(); t = txt.read(); } if (t.GetToken() != B_OFIGPAREN) { // Пустое объявление языка. txt.seekp(t.GetBegin()); } else { // Load the list of alphabets, allowed for the language. Also do load other // language parameters. while (!txt.eof()) { BethToken t = txt.read(); if (t.GetToken() == B_CFIGPAREN) break; if (t.GetToken() == B_ALPHABET) { // Add the valid alphabet name for this language BethToken abc_name = txt.read(); int id_abc = gram.Find_Alphabet(abc_name.string()); if (id_abc == UNKNOWN) { lem::Iridium::Print_Error(abc_name, txt); gram.GetIO().merr().printf("Unknown alphabet [%us] used in in language [%us] specification\n", abc_name.string().c_str(), name.c_str()); throw E_ParserError(); } alphabet.push_back(id_abc); continue; } else if (t.GetToken() == B_LINK) { Tree_Link l; l.LoadTxt(txt, gram.GetDict().GetSynGram()); txt.read_it(B_LANGUAGE); BethToken lang_name = txt.read(); int id_lang = gram.GetDict().GetSynGram().Find_Language(lang_name); if (id_lang == UNKNOWN) { lem::Iridium::Print_Error(lang_name, txt); gram.GetIO().merr().printf("Unknown language [%us] used in in language [%us] specification\n", lang_name.string().c_str(), name.c_str()); throw E_ParserError(); } lang_links.push_back(std::make_pair(l, id_lang)); continue; } else { // Синтаксис задания параметра: // 1. скалярный param_name = value // 2. векторный param_name = { value1 values2 ... } if (txt.pick().GetToken() == B_EQUAL) { txt.read_it(B_EQUAL); SG_LanguageParam *param = new SG_LanguageParam; param->name = t; if (txt.pick().GetToken() == B_OFIGPAREN) { txt.read_it(B_OFIGPAREN); while (!txt.eof()) { if (txt.pick().GetToken() == B_CFIGPAREN) { txt.read_it(B_CFIGPAREN); break; } UFString v = txt.read().GetFullStr(); v.strip(L'"'); v.trim(); param->values.push_back(v); } } else { param->values.push_back(lem::trim(lem::UFString(txt.read().GetFullStr()))); } params.push_back(param); continue; } } lem::Iridium::Print_Error(t, txt); gram.GetIO().merr().printf("Invalid token in language [%us] specification\n", name.c_str()); throw E_ParserError(); } } if (Me) { // Эхо-сообщение: закончили трансляцию. gram.GetIO().mecho().printf("%vfAOK%vn\n"); } return; }
/****************************************************************** Считывание описание узла BETH-дерева из текстового файла Словаря. *******************************************************************/ void Word_Form::LoadTxt( CompilationContext &context, Macro_Parser &txtfile, SynGram &gram ) { val=Real1(100); score=0; iversion = seq_iversion++; // Сначала считываем определение класса и имя словарной статьи. const BethToken class_name = txtfile.read(); entry_key=UNKNOWN_STATE; int iclass; // Если считанная лексема заключена в апострофы, то имеем дело // с сокращенным форматом: ?:строка. Этот формат применяется дл // описания лексемы, принадлежность которой к какому-либо классу // не очевидна или не важна, а важно лексическое представление. if( in_apostrophes(class_name.string()) ) { name = RC_Lexem( new Lexem(strip_quotes(class_name.string())) ); return; } if( class_name.string()==SOL_UNKNOWN_ENTRY_NAME ) { // Только одна статья принадлежит особому классу UNKNOWN! // Эта статья может быть описана только как "?:?" if( txtfile.read().string()==sol_get_token(B_COLON) && txtfile.read().string()==sol_get_token(B_UNKNOWN) ) { const Word_Coord iuu = gram.FindEntryForm( class_name.string() ); const SG_Entry& euu = gram.GetEntry(iuu.GetEntry()); iclass=UNKNOWN_STATE; entry_key = euu.GetKey(); return; } } // Пытаемся найти имя синтаксического класса в списке // уже загруженных в Словаре для синтаксической грамматики. if( (iclass=gram.FindClass(class_name.string()))==UNKNOWN ) { // Класс не определен. // // Предполагаем, что задано лексическое содержимое некоторой // словоформы. Если такая словоформа лексически уникальна, // то мы сами можем восстановить пару класс:статья. В случае // множественности вариантов тяжесть ответственности ложитс // на пользователя. Word_Coord ie = gram.FindEntryForm( class_name.string() ); if( ie.GetEntry()==UNKNOWN ) { // Выводим сообщение о неверном имени синтаксического класса или // ненайденной словоформе. lem::Iridium::Print_Error(class_name,txtfile); gram.GetIO().merr().printf("Neither a class previously declared in grammar, nor an entry form\n"); throw E_ParserError(); } if( ie.GetEntry()!=UNKNOWN ) { // Словоформа найдена! const SG_Entry& ef = gram.GetEntry(ie.GetEntry()); entry_key = ef.GetKey(); // Вероятно, следует также переслать координатные пары. const SG_EntryForm& eef = ef.forms()[ ie.GetForm() ]; const CP_Array& dims = eef.coords(); for( Container::size_type ii=0; ii<dims.size(); ii++ ) { // Здесь немного наворочено - прямо использовать конструктор без // копирования в промежуточные поля нельзя из-за ошибки в // кодогенераторе Symantec C++ (появляется General Protection Fault). const GramCoordAdr ca = dims[ii].GetCoord(); const int cs = dims[ii].GetState(); pair.push_back( GramCoordEx( ca, cs, true ) ); } // И наконец, пересылаем полное имя (лексическое содержимое) статьи. // icenter=0; // e_list.push_back( entry_key ); name = RC_Lexem( const_cast<Lexem*>(&ef.GetName()), null_deleter() ); } return; } const BSourceState back=txtfile.tellp(); const BethToken t = txtfile.read(); bool read_ofigparen=true; if( t.GetToken()==B_COLON ) { // Считываем имя статьи, которое может в общем случае состоять // из нескольких лексем. Признаком окончания имени служит // открывающая фигурная скобка '{'. Lexem *mname = new Lexem( sol_read_multyname( gram.GetIO(), txtfile,B_OFIGPAREN) ); // Преобразуем в мультилексему так, чтобы правильно распознавались // объявления имен типа "ЕЩ^Ё". gram.GetDict().GetLexAuto().TranslateLexem(*mname,false); mname->Translate( gram.GetDict().GetGraphGram(), 2 ); //(*name) = mname; name = RC_Lexem( mname ); // lexem_owner.resize(1); // lexem_owner.Nullify(); read_ofigparen=false; // Пытаемся найти статью. if( *mname != sol_get_token(B_ANY) ) { // Считан НЕ квантор всеобщности, так что это должно быть имя статьи. // Попытаемся найти статью среди уже загруженных, причем ищем с // критерием принадлежности определенному синтаксическому классу. const int ientry=gram.FindEntry2(*mname,iclass); // Нашли ? if( ientry==UNKNOWN ) { // Нет! Выводим сообщение об неверном имени словарной статьи. lem::Iridium::Print_Error(txtfile); gram.GetIO().merr().printf( "The entry [%us:%us] is not previously declared in grammar\n" , gram.classes()[iclass].GetName().c_str() , mname->ToString().c_str() ); throw E_ParserError(); } // Запомним КЛЮЧ словарной статьи. entry_key=gram.GetEntry(ientry).GetKey(); } else entry_key=ANY_STATE; } else { txtfile.seekp(back); entry_key=ANY_STATE; } bool load_precise=true; if( read_ofigparen ) { const BSourceState back = txtfile.tellp(); if( txtfile.read().GetToken()!=B_OFIGPAREN ) { // Секции уточнения координат нет. txtfile.seekp(back); load_precise = false; } } if(load_precise) LoadPreciser(context,txtfile,gram,iclass); return; }
/************************************************************************* ЗАГРУЗКА ОПИСАНИЯ ИЗ ТЕКСТОВОГО ФАЙЛА 1. Общий формат: 1.1 Заголовок enum имя, синоним1, синоним2, ... Кроме основного имени можно устанавливать альтернативные имена координаты (синонимы). Это необходимо для работы с некоторыми просторечными конструкциями (типа слова 'ЕГОННЫЙ'). Имя координаты должно начинаться с буквы или символа '_'. 1.2 Список состояний (два взаимоисключающих случая): ... = имя_источника - таким способом координата [имя] получает тот же список состояний, что и заявленые для [имя_источник]. ... { сост1 сост2 ... состN } - в фигурных скобочках перечисляются имена состояний. 2. Один из особых случаев - координаты с двумя состояниями, представимыми по смыслу как TRUE и FALSE. Формат описани их несколько иной. Объявление имеет такой формат: : enum имя_координаты : То есть имена состояний явно не объявляются. Внутреннее описание также не загружается подставными именами состояний, но при операциях с координатами появление координаты с нулевым количеством состояний отлавливается и особо учитывается. **************************************************************************/ void GramCoord::LoadTxt( Macro_Parser &txtfile, const Grammar& gram, bool IsRealized ) { is_realized = IsRealized; BethToken coord_name = txtfile.read(); sol_check_coord_name(gram.GetDict(), txtfile, coord_name); name.push_back(coord_name.c_str()); const bool Me = gram.GetDict().GetDebugLevel_ir() >= 3; if (Me) { // Эхо-сообщение: начали трансляцию координаты. gram.GetIO().mecho().printf( "%us [%vfE%us%vn]->", sol_get_token(B_COORDINATE).c_str(), name.get(0).c_str() ); } BSourceState back = txtfile.tellp(); bool cname_loaded = false; if (txtfile.read().GetToken() == B_AS) { c_name = txtfile.read(); back = txtfile.tellp(); cname_loaded = true; } else txtfile.seekp(back); // Цикл загрузки компонентов описания координаты. bool loading = true; while (loading) { const BethToken t1 = txtfile.read(); switch (t1.GetToken()) { case B_EQUAL: { // Встретили спецификацию '='. Так что пересылаем себе состояния // другой координаты, имя которой идёт следом. const BethToken another = txtfile.read(); const GramCoordAdr ianother = gram.FindCoord(another.string()); if (ianother.GetIndex() == UNKNOWN) { Print_Error(another, txtfile); MemFormatter f; f.printf( "The coordinate [%us] is not previously " "declared in grammar\n" , another.c_str() ); throw E_Solarix(f.string().c_str()); } state = gram.coords()[ianother.GetIndex()].state; // Пересылаем себе состояния #if defined SOL_COMPILER // Очищаем C-имена состояний for (lem::Container::size_type i = 0; i < state.size(); ++i) { state[i].Clear_API_Names(); } #endif loading = false; // Больше считывать нечего break; } case B_COMMA: { // Через запятую идут синонимы имени координаты. const BethToken t2 = txtfile.read(); sol_check_s_name(gram.GetDict(), txtfile, t2, true); if (name.Find(t2.c_str()) != UNKNOWN) { Print_Error(t2, txtfile); MemFormatter f; f.printf("Duplication of the name in coordinate [%us] declaration\n", t2.c_str()); throw E_Solarix(f.string().c_str()); } name.push_back(t2.c_str()); break; } case B_OFIGPAREN: { // В фигурных скобочках перечислены возможные состояния координаты. while (!txtfile.eof()) { const BSourceState back2 = txtfile.tellp(); // Если это '}', то список атрибутов завершен. if (txtfile.read().GetToken() == B_CFIGPAREN) break; txtfile.seekp(back2); GramCoordState dummy; dummy.LoadTxt(gram.GetIO(), txtfile); // Нет повторных объявлений состояний? for (lem::Container::size_type i = 0; i < dummy.size(); i++) { // Проверим, чтобы в загруженной подгруппе не было двух // одинаковых имен. for (lem::Container::size_type j = 0; j < dummy.size(); j++) { if (i != j) if (dummy[i] == dummy[j]) { Print_Error(txtfile); MemFormatter f; f.printf("Two equal names in a coordinate [%us] state subgroup\n", name.string().c_str()); throw E_Solarix(f.string().c_str()); } } const UCString& look_for = dummy.get(i); if (FindState(look_for) != UNKNOWN) { Print_Error(txtfile); MemFormatter f; f.printf("Two states of coordinate [%us] have got the same name\n", name.string().c_str()); throw E_Solarix(f.string().c_str()); } } state.push_back(dummy); if (Me) gram.GetIO().mecho().printf('.'); } loading = false; break; } default: { if (states().size()) { Print_Error(t1, txtfile); MemFormatter f; f.printf( "Definition of the coordinate [%us] is not" " identical to previous one.\n" , name.string().c_str() ); throw E_Solarix(f.string().c_str()); } // Если это не открывающая фигурная скобочка, то имеем координату // с двумя состояниями TRUE и FALSE (бистабильную координату). txtfile.seekp(back); // На этом считывание описания заканчиваем. loading = false; break; } } // end of switch } // end of while if (Me) { // Эхо-сообщение: закончили трансляцию координаты. gram.GetIO().mecho().printf("%vfAOK%vn\n"); } return; }
/************************************************************************ Собственно, загрузка содержимого тела секции Автомата. Дабы позволить производным классам автоматов разпознавать и загружать свои собственные структуры данных сверх того, что распознает и загружает наш класс, вызывается виртуальный метод ProcessLexem. *************************************************************************/ void Automaton::load( Macro_Parser &txtfile, const Binarization_Options &options ) { bool looping=true; while( looping ) { if( txtfile.eof() ) { Print_Error(txtfile); GetIO().merr().printf( "End of file has been reached before Automaton [%us] section completely loaded\n" , GetName().c_str() ); throw E_ParserError(); } const BethToken t=txtfile.read(); if( !ProcessLexem(t,txtfile,options) ) { switch(t.GetToken()) { case B_CRITERION: if( param!=NULL ) param->LoadTxt( GetIO(), txtfile ); break; case B_CFIGPAREN: looping=false; break; default: { const BSourceState back=txtfile.tellp(); if( param!=NULL ) { if( txtfile.read().GetToken()==B_EQUAL ) { txtfile.seekp( t.GetBegin() ); param->LoadTxt(GetIO(),txtfile); break; } } else { LEM_STOPIT; } txtfile.seekp(back); // Нераспознанная лексема. GetIO().merr().printf( "\nIncorrect lexem [%us]\n", t.string().c_str() ); Print_Error(t,txtfile); throw E_ParserError(); } } } } return; }
void SG_ComplexLink::LoadPoint( Macro_Parser &txtfile, SynGram &gram, lem::UFString &entry ) { BethToken t = txtfile.read(); if( t.GetToken()==B_ENTRY ) { // Особый формат entry Класс:Статья { уточнение } // преобразуется в ключ статьи и возвращается в виде #ключ UCString class0 = txtfile.read().string(); const int ic0 = class0==L"?" ? ANY_STATE : gram.FindClass(class0); if( ic0==UNKNOWN ) { Print_Error( txtfile ); gram.GetIO().merr().printf( "Unknown class %us\n", class0.c_str() ); throw E_BaseException(); } txtfile.read_it( B_COLON ); UCString entry0 = sol_read_multyname( gram.GetIO(), txtfile, B_OFIGPAREN ); entry0.strip(L'"'); entry0.trim(); // Может быть задана дополнительная фильтрующая координата Solarix::CP_Array coords0; coords0.LoadTxt( txtfile, gram ); if( gram.IsOmonym(ic0,lem::to_upper(entry0)) && coords0.empty() ) { Print_Error( txtfile ); gram.GetIO().merr().printf( "Omonym %us:%us requires the coordinate array\n", class0.c_str(), entry0.c_str() ); throw E_BaseException(); } const int ie0 = coords0.empty() ? gram.FindEntry(entry0,ic0,false) : gram.FindEntryOmonym(entry0,ic0,coords0); if( ie0==UNKNOWN ) { Print_Error( txtfile ); gram.GetIO().merr().printf( "Unknown entry %us:%us\n", class0.c_str(), entry0.c_str() ); throw E_BaseException(); } const int ekey = gram.GetEntry(ie0).GetKey(); entry = lem::format_str( L"#%d", ekey ); return; } bool figparen = t.GetToken()==B_OFIGPAREN; if( !figparen ) txtfile.seekp(t); entry.reserve(128); if( t.string()==L'@' ) { entry = L'@'; t = txtfile.read(); } while( !txtfile.eof() ) { BethToken t = txtfile.read(); if( figparen && t.GetToken()==B_CFIGPAREN ) break; if( !entry.empty() ) entry.Add_Dirty(L' '); UFString ts( t.GetFullStr() ); ts.strip(L'"'); entry.Add_Dirty( ts ); if( !figparen ) break; } entry.calc_hash(); if( entry.front()==L'@' ) { // Спецсимвол @ заставляет запомнить строку в виде "как есть" entry.remove(0); entry.trim(); } else { entry.strip(L'"'); gram.GetDict().GetLexAuto().TranslateLexem(entry,true); } return; }
/************************************************************************* Загрузка дескриптора ребра из текстового файла. Распознаются форматы: 1. <имя_коорд:имя_связки> Расширенный формат, явно задано имя координаты имя_коорд, одно из состояний которой имя_связки признается за имя связки. Угловые скобки обязательны. Частные случаи этого формата: 1.1 <имя_коорд:*> То есть имя связки (состояния координаты имя_коорд) задается квантором производьности, и в дальнейших проверках игнорируется. 1.2 <имя_коорд:?> Имя связки задано как квантор UNKNOWN. 2. <имя_связки> Сокращенный формат, имя связки должно быть объявлено как имя состояния координаты с предопределенным именем net. 3. <*> Связка задана как квантор всеобщности, так что в операциях сравнения она будет подходить для любой другой связки. 4. <?> Особое задание связки - через квантор UNKNOWN. ***************************************************************************/ bool Tree_Link::LoadTxt(Macro_Parser &txtfile, const SynGram &gram) { const BSourceState back = txtfile.tellp(); BethToken coord_name = txtfile.read(); if (coord_name.GetToken() == B_OTRIPAREN) { coord_name = txtfile.read(); switch (coord_name.GetToken()) { case ANY_STATE: icoord = ANY_STATE; break; case UNKNOWN_STATE: icoord = UNKNOWN_STATE; break; default: { const BethToken t = txtfile.read(); if (t.GetToken() == B_COLON) { // Полный, расширенный формат с указанием имени координаты. if ((icoord = gram.FindCoord(coord_name.string()).GetIndex()) == UNKNOWN) { lem::Iridium::Print_Error(coord_name, txtfile); gram.GetIO().merr().printf( "The coordinate [%us] is not previously declared in grammar\n" , coord_name.c_str() ); throw E_ParserError(); } } else { icoord = I_NET; txtfile.seekp(coord_name); } const BethToken state_name = txtfile.read(); switch (state_name.GetToken()) { case B_ANY: istate = ANY_STATE; break; case B_UNKNOWN: istate = UNKNOWN_STATE; break; default: if ((istate = gram.coords()[icoord].FindState(state_name.string())) == UNKNOWN) { lem::Iridium::Print_Error(state_name, txtfile); gram.GetIO().merr().printf( "State [%us] is not declared for coordinate [%us]\n" , state_name.c_str(), gram.coords()[icoord].GetName().string().c_str() ); throw E_ParserError(); } break; } break; } } txtfile.read_it(B_CTRIPAREN); return true; } else { txtfile.seekp(back); icoord = UNKNOWN; istate = UNKNOWN; return false; } return false; }