2008-06-11 14:52:57 +04:00
|
|
|
// $Id$
|
|
|
|
|
|
|
|
/***********************************************************************
|
2014-05-18 00:30:54 +04:00
|
|
|
Moses - factored phrase-based language decoder
|
|
|
|
Copyright (C) 2006 University of Edinburgh
|
2014-05-19 17:34:27 +04:00
|
|
|
|
2014-05-18 00:30:54 +04:00
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
2014-05-19 17:34:27 +04:00
|
|
|
|
2014-05-18 00:30:54 +04:00
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
2014-05-19 17:34:27 +04:00
|
|
|
|
2014-05-18 00:30:54 +04:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
***********************************************************************/
|
2008-06-11 14:52:57 +04:00
|
|
|
|
|
|
|
#include <algorithm>
|
2015-03-28 16:09:03 +03:00
|
|
|
#include <cstdlib>
|
2012-10-28 21:47:22 +04:00
|
|
|
#include "util/exception.hh"
|
2011-10-14 20:40:30 +04:00
|
|
|
#include "util/tokenize_piece.hh"
|
|
|
|
|
2008-06-11 14:52:57 +04:00
|
|
|
#include "TargetPhrase.h"
|
|
|
|
#include "GenerationDictionary.h"
|
2011-10-13 18:27:01 +04:00
|
|
|
#include "LM/Base.h"
|
2008-06-11 14:52:57 +04:00
|
|
|
#include "StaticData.h"
|
|
|
|
#include "ScoreComponentCollection.h"
|
|
|
|
#include "Util.h"
|
2011-06-17 01:20:20 +04:00
|
|
|
#include "AlignmentInfoCollection.h"
|
2013-08-13 23:36:32 +04:00
|
|
|
#include "InputPath.h"
|
2013-10-03 14:05:53 +04:00
|
|
|
#include "moses/TranslationModel/PhraseDictionary.h"
|
2015-03-09 03:30:01 +03:00
|
|
|
#include <boost/foreach.hpp>
|
2012-04-10 18:45:18 +04:00
|
|
|
|
2008-06-11 14:52:57 +04:00
|
|
|
using namespace std;
|
|
|
|
|
2008-10-09 03:51:26 +04:00
|
|
|
namespace Moses
|
|
|
|
{
|
2014-12-13 14:52:47 +03:00
|
|
|
TargetPhrase::TargetPhrase( std::string out_string, const PhraseDictionary *pt)
|
2013-05-30 20:16:10 +04:00
|
|
|
:Phrase(0)
|
|
|
|
, m_fullScore(0.0)
|
|
|
|
, m_futureScore(0.0)
|
2013-05-29 21:16:15 +04:00
|
|
|
, m_alignTerm(&AlignmentInfoCollection::Instance().GetEmptyAlignmentInfo())
|
|
|
|
, m_alignNonTerm(&AlignmentInfoCollection::Instance().GetEmptyAlignmentInfo())
|
|
|
|
, m_lhsTarget(NULL)
|
2013-09-17 20:22:00 +04:00
|
|
|
, m_ruleSource(NULL)
|
2014-12-13 14:52:47 +03:00
|
|
|
, m_container(pt)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
|
|
|
|
//ACAT
|
|
|
|
const StaticData &staticData = StaticData::Instance();
|
2015-01-08 16:53:46 +03:00
|
|
|
// XXX should this really be InputFactorOrder???
|
2014-06-08 11:44:59 +04:00
|
|
|
CreateFromString(Output, staticData.GetInputFactorOrder(), out_string,
|
|
|
|
// staticData.GetFactorDelimiter(), // eliminated [UG]
|
|
|
|
NULL);
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
|
|
|
|
2014-08-04 22:28:04 +04:00
|
|
|
TargetPhrase::TargetPhrase(const PhraseDictionary *pt)
|
2013-05-29 21:16:15 +04:00
|
|
|
:Phrase()
|
|
|
|
, m_fullScore(0.0)
|
2013-05-30 20:16:10 +04:00
|
|
|
, m_futureScore(0.0)
|
2013-05-29 21:16:15 +04:00
|
|
|
, m_alignTerm(&AlignmentInfoCollection::Instance().GetEmptyAlignmentInfo())
|
|
|
|
, m_alignNonTerm(&AlignmentInfoCollection::Instance().GetEmptyAlignmentInfo())
|
|
|
|
, m_lhsTarget(NULL)
|
2013-09-17 20:22:00 +04:00
|
|
|
, m_ruleSource(NULL)
|
2014-08-04 22:28:04 +04:00
|
|
|
, m_container(pt)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-08-04 18:59:32 +04:00
|
|
|
TargetPhrase::TargetPhrase(const Phrase &phrase, const PhraseDictionary *pt)
|
2013-05-29 21:16:15 +04:00
|
|
|
: Phrase(phrase)
|
|
|
|
, m_fullScore(0.0)
|
2013-05-30 20:16:10 +04:00
|
|
|
, m_futureScore(0.0)
|
2013-05-29 21:16:15 +04:00
|
|
|
, m_alignTerm(&AlignmentInfoCollection::Instance().GetEmptyAlignmentInfo())
|
|
|
|
, m_alignNonTerm(&AlignmentInfoCollection::Instance().GetEmptyAlignmentInfo())
|
|
|
|
, m_lhsTarget(NULL)
|
2013-09-17 20:22:00 +04:00
|
|
|
, m_ruleSource(NULL)
|
2014-08-04 18:59:32 +04:00
|
|
|
, m_container(pt)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TargetPhrase::TargetPhrase(const TargetPhrase ©)
|
2013-05-29 21:16:15 +04:00
|
|
|
: Phrase(copy)
|
2015-03-09 03:30:01 +03:00
|
|
|
, m_cached_scores(copy.m_cached_scores)
|
2013-05-29 21:16:15 +04:00
|
|
|
, m_fullScore(copy.m_fullScore)
|
2013-05-30 20:16:10 +04:00
|
|
|
, m_futureScore(copy.m_futureScore)
|
2013-06-05 22:44:43 +04:00
|
|
|
, m_scoreBreakdown(copy.m_scoreBreakdown)
|
2013-05-29 21:16:15 +04:00
|
|
|
, m_alignTerm(copy.m_alignTerm)
|
|
|
|
, m_alignNonTerm(copy.m_alignNonTerm)
|
2015-01-29 18:52:21 +03:00
|
|
|
, m_properties(copy.m_properties)
|
2014-08-04 18:14:40 +04:00
|
|
|
, m_container(copy.m_container)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
if (copy.m_lhsTarget) {
|
|
|
|
m_lhsTarget = new Word(*copy.m_lhsTarget);
|
|
|
|
} else {
|
|
|
|
m_lhsTarget = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy.m_ruleSource) {
|
|
|
|
m_ruleSource = new Phrase(*copy.m_ruleSource);
|
|
|
|
} else {
|
|
|
|
m_ruleSource = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TargetPhrase::~TargetPhrase()
|
|
|
|
{
|
|
|
|
//cerr << "m_lhsTarget=" << m_lhsTarget << endl;
|
|
|
|
|
|
|
|
delete m_lhsTarget;
|
|
|
|
delete m_ruleSource;
|
|
|
|
}
|
|
|
|
|
2008-09-24 20:48:23 +04:00
|
|
|
#ifdef HAVE_PROTOBUF
|
2014-05-19 17:34:27 +04:00
|
|
|
void TargetPhrase::WriteToRulePB(hgmert::Rule* pb) const
|
|
|
|
{
|
|
|
|
pb->add_trg_words("[X,1]");
|
|
|
|
for (size_t pos = 0 ; pos < GetSize() ; pos++)
|
|
|
|
pb->add_trg_words(GetWord(pos)[0]->GetString());
|
|
|
|
}
|
2008-09-24 20:48:23 +04:00
|
|
|
#endif
|
2014-05-19 17:34:27 +04:00
|
|
|
|
2014-08-08 18:59:34 +04:00
|
|
|
void TargetPhrase::EvaluateInIsolation(const Phrase &source)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
const std::vector<FeatureFunction*> &ffs = FeatureFunction::GetFeatureFunctions();
|
2014-08-08 18:59:34 +04:00
|
|
|
EvaluateInIsolation(source, ffs);
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
|
|
|
|
2014-08-08 18:59:34 +04:00
|
|
|
void TargetPhrase::EvaluateInIsolation(const Phrase &source, const std::vector<FeatureFunction*> &ffs)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
if (ffs.size()) {
|
|
|
|
const StaticData &staticData = StaticData::Instance();
|
|
|
|
ScoreComponentCollection futureScoreBreakdown;
|
|
|
|
for (size_t i = 0; i < ffs.size(); ++i) {
|
|
|
|
const FeatureFunction &ff = *ffs[i];
|
|
|
|
if (! staticData.IsFeatureFunctionIgnored( ff )) {
|
2014-07-10 01:35:59 +04:00
|
|
|
ff.EvaluateInIsolation(source, *this, m_scoreBreakdown, futureScoreBreakdown);
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float weightedScore = m_scoreBreakdown.GetWeightedScore();
|
|
|
|
m_futureScore += futureScoreBreakdown.GetWeightedScore();
|
|
|
|
m_fullScore = weightedScore + m_futureScore;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-08 18:42:23 +04:00
|
|
|
void TargetPhrase::EvaluateWithSourceContext(const InputType &input, const InputPath &inputPath)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
const std::vector<FeatureFunction*> &ffs = FeatureFunction::GetFeatureFunctions();
|
|
|
|
const StaticData &staticData = StaticData::Instance();
|
|
|
|
ScoreComponentCollection futureScoreBreakdown;
|
|
|
|
for (size_t i = 0; i < ffs.size(); ++i) {
|
|
|
|
const FeatureFunction &ff = *ffs[i];
|
|
|
|
if (! staticData.IsFeatureFunctionIgnored( ff )) {
|
2014-07-10 02:06:54 +04:00
|
|
|
ff.EvaluateWithSourceContext(input, inputPath, *this, NULL, m_scoreBreakdown, &futureScoreBreakdown);
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
float weightedScore = m_scoreBreakdown.GetWeightedScore();
|
|
|
|
m_futureScore += futureScoreBreakdown.GetWeightedScore();
|
|
|
|
m_fullScore = weightedScore + m_futureScore;
|
|
|
|
}
|
|
|
|
|
2015-01-14 14:07:42 +03:00
|
|
|
void TargetPhrase::UpdateScore(ScoreComponentCollection* futureScoreBreakdown)
|
|
|
|
{
|
2015-01-06 16:54:40 +03:00
|
|
|
float weightedScore = m_scoreBreakdown.GetWeightedScore();
|
|
|
|
if(futureScoreBreakdown)
|
|
|
|
m_futureScore += futureScoreBreakdown->GetWeightedScore();
|
2015-01-14 14:07:42 +03:00
|
|
|
m_fullScore = weightedScore + m_futureScore;
|
2015-01-06 16:54:40 +03:00
|
|
|
}
|
|
|
|
|
2014-05-19 17:34:27 +04:00
|
|
|
void TargetPhrase::SetXMLScore(float score)
|
|
|
|
{
|
|
|
|
const FeatureFunction* prod = PhraseDictionary::GetColl()[0];
|
|
|
|
size_t numScores = prod->GetNumScoreComponents();
|
|
|
|
vector <float> scoreVector(numScores,score/numScores);
|
|
|
|
|
|
|
|
m_scoreBreakdown.Assign(prod, scoreVector);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TargetPhrase::SetAlignmentInfo(const StringPiece &alignString)
|
|
|
|
{
|
|
|
|
AlignmentInfo::CollType alignTerm, alignNonTerm;
|
|
|
|
for (util::TokenIter<util::AnyCharacter, true> token(alignString, util::AnyCharacter(" \t")); token; ++token) {
|
|
|
|
util::TokenIter<util::SingleCharacter, false> dash(*token, util::SingleCharacter('-'));
|
|
|
|
|
|
|
|
char *endptr;
|
|
|
|
size_t sourcePos = strtoul(dash->data(), &endptr, 10);
|
|
|
|
UTIL_THROW_IF(endptr != dash->data() + dash->size(), util::ErrnoException, "Error parsing alignment" << *dash);
|
|
|
|
++dash;
|
|
|
|
size_t targetPos = strtoul(dash->data(), &endptr, 10);
|
|
|
|
UTIL_THROW_IF(endptr != dash->data() + dash->size(), util::ErrnoException, "Error parsing alignment" << *dash);
|
|
|
|
UTIL_THROW_IF2(++dash, "Extra gunk in alignment " << *token);
|
|
|
|
|
|
|
|
if (GetWord(targetPos).IsNonTerminal()) {
|
|
|
|
alignNonTerm.insert(std::pair<size_t,size_t>(sourcePos, targetPos));
|
|
|
|
} else {
|
|
|
|
alignTerm.insert(std::pair<size_t,size_t>(sourcePos, targetPos));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SetAlignTerm(alignTerm);
|
|
|
|
SetAlignNonTerm(alignNonTerm);
|
|
|
|
// cerr << "TargetPhrase::SetAlignmentInfo(const StringPiece &alignString) this:|" << *this << "|\n";
|
|
|
|
}
|
|
|
|
|
2014-10-07 21:08:31 +04:00
|
|
|
// void TargetPhrase::SetAlignTerm(const AlignmentInfo::CollType &coll)
|
|
|
|
// {
|
|
|
|
// const AlignmentInfo *alignmentInfo = AlignmentInfoCollection::Instance().Add(coll);
|
|
|
|
// m_alignTerm = alignmentInfo;
|
2014-05-19 17:34:27 +04:00
|
|
|
|
2014-10-07 21:08:31 +04:00
|
|
|
// }
|
2014-05-19 17:34:27 +04:00
|
|
|
|
2014-10-07 21:08:31 +04:00
|
|
|
// void TargetPhrase::SetAlignNonTerm(const AlignmentInfo::CollType &coll)
|
|
|
|
// {
|
|
|
|
// const AlignmentInfo *alignmentInfo = AlignmentInfoCollection::Instance().Add(coll);
|
|
|
|
// m_alignNonTerm = alignmentInfo;
|
|
|
|
// }
|
2014-05-19 17:34:27 +04:00
|
|
|
|
|
|
|
void TargetPhrase::SetSparseScore(const FeatureFunction* translationScoreProducer, const StringPiece &sparseString)
|
|
|
|
{
|
|
|
|
m_scoreBreakdown.Assign(translationScoreProducer, sparseString.as_string());
|
|
|
|
}
|
|
|
|
|
2015-03-09 03:30:01 +03:00
|
|
|
boost::shared_ptr<Scores>
|
|
|
|
mergescores(boost::shared_ptr<Scores> const& a,
|
|
|
|
boost::shared_ptr<Scores> const& b)
|
|
|
|
{
|
|
|
|
boost::shared_ptr<Scores> ret;
|
|
|
|
if (!a) return b ? b : ret;
|
|
|
|
if (!b) return a;
|
|
|
|
if (a->size() != b->size()) return ret;
|
|
|
|
ret.reset(new Scores(*a));
|
|
|
|
for (size_t i = 0; i < a->size(); ++i)
|
|
|
|
{
|
|
|
|
if ((*a)[i] == 0) (*a)[i] = (*b)[i];
|
|
|
|
else if ((*b)[i])
|
|
|
|
{
|
|
|
|
UTIL_THROW_IF2((*a)[i] != (*b)[i], "can't merge feature vectors");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TargetPhrase::
|
|
|
|
Merge(const TargetPhrase ©, const std::vector<FactorType>& factorVec)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
|
|
|
Phrase::MergeFactors(copy, factorVec);
|
|
|
|
m_scoreBreakdown.Merge(copy.GetScoreBreakdown());
|
|
|
|
m_futureScore += copy.m_futureScore;
|
|
|
|
m_fullScore += copy.m_fullScore;
|
2015-03-09 03:30:01 +03:00
|
|
|
typedef ScoreCache_t::iterator iter;
|
|
|
|
typedef ScoreCache_t::value_type item;
|
|
|
|
BOOST_FOREACH(item const& s, copy.m_cached_scores)
|
|
|
|
{
|
|
|
|
pair<iter,bool> foo = m_cached_scores.insert(s);
|
|
|
|
if (foo.second == false)
|
|
|
|
foo.first->second = mergescores(foo.first->second, s.second);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TargetPhrase::ScoreCache_t const&
|
|
|
|
TargetPhrase::
|
|
|
|
GetExtraScores() const
|
|
|
|
{
|
|
|
|
return m_cached_scores;
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
|
|
|
|
2015-03-09 03:30:01 +03:00
|
|
|
Scores const*
|
|
|
|
TargetPhrase::
|
|
|
|
GetExtraScores(FeatureFunction const* ff) const
|
|
|
|
{
|
|
|
|
ScoreCache_t::const_iterator m = m_cached_scores.find(ff);
|
|
|
|
return m != m_cached_scores.end() ? m->second.get() : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
TargetPhrase::
|
|
|
|
SetExtraScores(FeatureFunction const* ff,
|
|
|
|
boost::shared_ptr<Scores> const& s)
|
|
|
|
{ m_cached_scores[ff] = s; }
|
|
|
|
|
|
|
|
|
2014-05-19 17:34:27 +04:00
|
|
|
void TargetPhrase::SetProperties(const StringPiece &str)
|
|
|
|
{
|
|
|
|
if (str.size() == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string> toks;
|
|
|
|
TokenizeMultiCharSeparator(toks, str.as_string(), "{{");
|
|
|
|
for (size_t i = 0; i < toks.size(); ++i) {
|
|
|
|
string &tok = toks[i];
|
|
|
|
if (tok.empty()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
size_t endPos = tok.rfind("}");
|
|
|
|
|
|
|
|
tok = tok.substr(0, endPos - 1);
|
|
|
|
|
|
|
|
vector<string> keyValue = TokenizeFirstOnly(tok, " ");
|
|
|
|
UTIL_THROW_IF2(keyValue.size() != 2,
|
|
|
|
"Incorrect format of property: " << str);
|
|
|
|
SetProperty(keyValue[0], keyValue[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-08 11:44:59 +04:00
|
|
|
void TargetPhrase::SetProperty(const std::string &key, const std::string &value)
|
2014-05-19 17:34:27 +04:00
|
|
|
{
|
2014-05-20 00:54:08 +04:00
|
|
|
const StaticData &staticData = StaticData::Instance();
|
|
|
|
const PhrasePropertyFactory& phrasePropertyFactory = staticData.GetPhrasePropertyFactory();
|
|
|
|
m_properties[key] = phrasePropertyFactory.ProduceProperty(key,value);
|
|
|
|
}
|
|
|
|
|
2014-06-13 18:34:24 +04:00
|
|
|
const PhraseProperty *TargetPhrase::GetProperty(const std::string &key) const
|
2013-08-27 12:42:24 +04:00
|
|
|
{
|
2014-05-20 00:54:08 +04:00
|
|
|
std::map<std::string, boost::shared_ptr<PhraseProperty> >::const_iterator iter;
|
2014-05-19 17:34:27 +04:00
|
|
|
iter = m_properties.find(key);
|
2014-05-20 00:54:08 +04:00
|
|
|
if (iter != m_properties.end()) {
|
2014-06-13 19:49:57 +04:00
|
|
|
const boost::shared_ptr<PhraseProperty> &pp = iter->second;
|
|
|
|
return pp.get();
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
2014-06-13 18:34:24 +04:00
|
|
|
return NULL;
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void TargetPhrase::SetRuleSource(const Phrase &ruleSource) const
|
|
|
|
{
|
|
|
|
if (m_ruleSource == NULL) {
|
|
|
|
m_ruleSource = new Phrase(ruleSource);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void swap(TargetPhrase &first, TargetPhrase &second)
|
|
|
|
{
|
|
|
|
first.SwapWords(second);
|
|
|
|
std::swap(first.m_fullScore, second.m_fullScore);
|
|
|
|
std::swap(first.m_futureScore, second.m_futureScore);
|
|
|
|
swap(first.m_scoreBreakdown, second.m_scoreBreakdown);
|
|
|
|
std::swap(first.m_alignTerm, second.m_alignTerm);
|
|
|
|
std::swap(first.m_alignNonTerm, second.m_alignNonTerm);
|
|
|
|
std::swap(first.m_lhsTarget, second.m_lhsTarget);
|
2015-03-09 03:30:01 +03:00
|
|
|
std::swap(first.m_cached_scores, second.m_cached_scores);
|
2014-05-19 17:34:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
TO_STRING_BODY(TargetPhrase);
|
|
|
|
|
|
|
|
std::ostream& operator<<(std::ostream& os, const TargetPhrase& tp)
|
|
|
|
{
|
|
|
|
if (tp.m_lhsTarget) {
|
|
|
|
os << *tp.m_lhsTarget<< " -> ";
|
|
|
|
}
|
|
|
|
|
|
|
|
os << static_cast<const Phrase&>(tp) << ":" << flush;
|
2013-04-10 21:40:25 +04:00
|
|
|
os << tp.GetAlignNonTerm() << flush;
|
2014-05-19 17:34:27 +04:00
|
|
|
os << ": term=" << tp.GetAlignTerm() << flush;
|
|
|
|
os << ": nonterm=" << tp.GetAlignNonTerm() << flush;
|
|
|
|
os << ": c=" << tp.m_fullScore << flush;
|
|
|
|
os << " " << tp.m_scoreBreakdown << flush;
|
2015-01-14 14:07:42 +03:00
|
|
|
|
2014-05-19 17:34:27 +04:00
|
|
|
const Phrase *sourcePhrase = tp.GetRuleSource();
|
|
|
|
if (sourcePhrase) {
|
|
|
|
os << " sourcePhrase=" << *sourcePhrase << flush;
|
|
|
|
}
|
|
|
|
|
2014-06-10 13:29:49 +04:00
|
|
|
if (tp.m_properties.size()) {
|
2015-01-14 14:07:42 +03:00
|
|
|
os << " properties: " << flush;
|
2014-06-10 13:29:49 +04:00
|
|
|
|
2015-01-14 14:07:42 +03:00
|
|
|
TargetPhrase::Properties::const_iterator iter;
|
|
|
|
for (iter = tp.m_properties.begin(); iter != tp.m_properties.end(); ++iter) {
|
|
|
|
const string &key = iter->first;
|
|
|
|
const PhraseProperty *prop = iter->second.get();
|
|
|
|
assert(prop);
|
2014-06-10 13:29:49 +04:00
|
|
|
|
2015-01-14 14:07:42 +03:00
|
|
|
os << key << "=" << *prop << " ";
|
|
|
|
}
|
2014-06-10 13:29:49 +04:00
|
|
|
}
|
|
|
|
|
2014-05-19 17:34:27 +04:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
2015-03-09 03:30:01 +03:00
|
|
|
|
|
|
|
|
2008-10-09 03:51:26 +04:00
|
|
|
}
|
|
|
|
|