Skip to content
Open
7 changes: 5 additions & 2 deletions inflection/src/inflection/dialog/DictionaryLookupFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
*/
#include <inflection/npc.hpp>
#include <inflection/dialog/DictionaryLookupFunction.hpp>

#include <inflection/dialog/DisplayValue.hpp>
#include <inflection/dialog/SpeakableString.hpp>
#include <inflection/dictionary/DictionaryMetaData.hpp>
#include <inflection/util/Validate.hpp>
#include "inflection/tokenizer/TokenChain.hpp"
#include "inflection/tokenizer/Token_Word.hpp"
#include "inflection/tokenizer/TokenizerFactory.hpp"

namespace inflection::dialog {

DictionaryLookupFunction::DictionaryLookupFunction(const ::inflection::util::ULocale& locale, const ::std::vector<::std::u16string>& tags)
Expand Down Expand Up @@ -126,6 +124,11 @@ SpeakableString* DictionaryLookupFunction::getFeatureValue(const DisplayValue& d
if (result.empty()) {
return nullptr;
}
for(const auto& [feature, value]: displayValue.getConstraintMap()) {
if (feature.getName() == u"speak") {
return new SpeakableString(result, value);
}
}
return new SpeakableString(result);
}

Copy link
Contributor Author

@BHK4321 BHK4321 Sep 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grhoten
Was this the expected behavior in this function? If not, could you please point out where I am going wrong?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Within the DictionaryLookupFunction, you're only returning the grammeme value. This does not need to change.

Copy link
Contributor Author

@BHK4321 BHK4321 Sep 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DictionaryLookupFunction::getFeatureValue likely needs to be updated to query the relevant values from displayValue. It should probably query DisplayValue::getFeatureValue or DisplayValue::getConstraintMap

Okay sure I wanted to know exactly what I need to do, based on your previous comment I came up with this commit, could you please tell what is expected to make the tests pass so that i could implement the same and further add tests in InfectableStringConceptTest-c.cpp as well to complete this task ?

Expand Down
23 changes: 19 additions & 4 deletions inflection/src/inflection/dialog/DisplayValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
namespace inflection::dialog {


DisplayValue::DisplayValue(const ::std::u16string& displayString, const ::std::map<SemanticFeature, ::std::u16string>& constraintMap)
DisplayValue::DisplayValue(
const ::std::u16string& displayString,
const ::std::map<SemanticFeature, ::std::u16string>& constraintMap
)
: super()
, displayString(displayString)
, constraintMap(constraintMap)
Expand All @@ -21,11 +24,23 @@ DisplayValue::DisplayValue(const ::std::u16string& value)
{
}

DisplayValue::DisplayValue(const SpeakableString& value, const SemanticFeature& speakFeature)
: DisplayValue(value.getPrint(), {})
DisplayValue::DisplayValue(
const SpeakableString& value,
const SemanticFeature& speakFeature
)
: DisplayValue(value, speakFeature, {})
{
}

DisplayValue::DisplayValue(
const SpeakableString& value,
const SemanticFeature& speakFeature,
const ::std::map<SemanticFeature, ::std::u16string>& constraintMap
)
: DisplayValue(value.getPrint(), constraintMap)
{
if (!value.speakEqualsPrint()) {
constraintMap.emplace(speakFeature, value.getSpeak());
this->constraintMap.emplace(speakFeature, value.getSpeak());
}
}

Expand Down
7 changes: 7 additions & 0 deletions inflection/src/inflection/dialog/DisplayValue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ class INFLECTION_CLASS_API inflection::dialog::DisplayValue
* @param speakFeature The speakFeature from the SemanticFeatureModel that represents the SemanticFeature for the speak information for a SpeakableString.
*/
DisplayValue(const SpeakableString& value, const SemanticFeature& speakFeature);
/**
* Construct a display value with a SpeakableString.
* @param value A SpeakableString
* @param speakFeature The speakFeature from the SemanticFeatureModel that represents the SemanticFeature for the speak information for a SpeakableString.
* @param constraintMap The intitial constraint map.
*/
DisplayValue(const SpeakableString& value, const SemanticFeature& speakFeature, const ::std::map<SemanticFeature, ::std::u16string>& constraintMap);
/**
* The destructor
*/
Expand Down
27 changes: 26 additions & 1 deletion inflection/src/inflection/dialog/InflectableStringConcept-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@
* Copyright 2021-2024 Apple Inc. All rights reserved.
*/
#include <inflection/dialog/InflectableStringConcept.h>

#include <inflection/npc.hpp>
#include <inflection/dialog/InflectableStringConcept.hpp>
#include <inflection/dialog/SemanticFeatureConcept.h>
#include <inflection/exception/ClassCastException.hpp>
#include <inflection/util/ULocale.hpp>
#include <inflection/dialog/SemanticFeature.hpp>
#include <inflection/dialog/SemanticUtils.hpp>
#include <inflection/dialog/SemanticFeatureModel.hpp>
#include <inflection/util/TypeConversionUtils.hpp>
#include <inflection/util/Validate.hpp>


INFLECTION_CAPI IDSemanticFeatureConcept* iinf_toSemanticFeatureConcept(IDInflectableStringConcept* thisObject, UErrorCode*)
{
return (IDSemanticFeatureConcept*)thisObject;
Expand Down Expand Up @@ -47,6 +51,27 @@ iinf_create(const IDSemanticFeatureModel* model, const IDSpeakableString* value,
return nullptr;
}

INFLECTION_CAPI IDInflectableStringConcept*
iinf_createWithDefaults(const IDSemanticFeatureModel* model, const IDSpeakableString* value,
const IDDisplayValue_Constraint* defaultConstraints, int32_t defaultConstraintsLen, UErrorCode* status)
{
if (status != nullptr && U_SUCCESS(*status)) {
try {
auto defaultConstraintsMap(inflection::dialog::SemanticUtils::to_constraintMap(*npc((const inflection::dialog::SemanticFeatureModel*)model), defaultConstraints, defaultConstraintsLen));

return (IDInflectableStringConcept*) new ::inflection::dialog::InflectableStringConcept(
(const ::inflection::dialog::SemanticFeatureModel*)model,
*((const ::inflection::dialog::SpeakableString*)value),
defaultConstraintsMap
);
}
catch (const ::std::exception& e) {
inflection::util::TypeConversionUtils::convert(e, status);
}
}
return nullptr;
}

INFLECTION_CAPI void
iinf_destroy(IDInflectableStringConcept* thisObject)
{
Expand Down
17 changes: 15 additions & 2 deletions inflection/src/inflection/dialog/InflectableStringConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,23 @@

namespace inflection::dialog {

InflectableStringConcept::InflectableStringConcept(const SemanticFeatureModel* model, const SpeakableString& value)

InflectableStringConcept::InflectableStringConcept(
const SemanticFeatureModel* model,
const SpeakableString& value
)
: InflectableStringConcept(model, value, {})
{
}

InflectableStringConcept::InflectableStringConcept(
const SemanticFeatureModel* model,
const SpeakableString& value,
const ::std::map<SemanticFeature, ::std::u16string>& intitialConstraints
)
: super(model)
, value(value)
, defaultDisplayValue(value, *npc(super::getSpeakFeature()))
, defaultDisplayValue(value, *npc(super::getSpeakFeature()), intitialConstraints)
{
}

Expand Down
15 changes: 14 additions & 1 deletion inflection/src/inflection/dialog/InflectableStringConcept.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <inflection/dialog/SpeakableString.h>
#include <inflection/dialog/SemanticFeatureConcept.h>
#include <inflection/dialog/SemanticFeatureModel.h>

#include <inflection/dialog/DisplayValue.h>
/**
* An object that provides a way to format a word with additional grammatical category values or semantic features of a word for a given language.
*/
Expand Down Expand Up @@ -34,6 +34,19 @@ INFLECTION_CAPI IDInflectableStringConcept* iinf_toInflectableStringConcept(IDSe
* This is set to a failure when a failure has occurred during execution.
*/
INFLECTION_CAPI IDInflectableStringConcept* iinf_create(const IDSemanticFeatureModel* model, const IDSpeakableString* value, UErrorCode* status);
/**
* Constructs a concept given a semantic feature model and a speakable string
*
* @param model - The semantic feature model required to initialize the concept.
* @param value - The speakable string to convert to a concept
* @param defaultConstraints - The initial defaultConstraints to apply to the concept
* @param defaultConstraintsLen - The number of semantic features and values provided
* @param status Must be a valid pointer to an error code value,
* which must not indicate a failure before the function call.
* This is set to a failure when a failure has occurred during execution.
*/
INFLECTION_CAPI IDInflectableStringConcept* iinf_createWithDefaults(const IDSemanticFeatureModel* model, const IDSpeakableString* value,
const IDDisplayValue_Constraint* defaultConstraints, int32_t defaultConstraintsLen, UErrorCode* status);
/**
* Destructor
*/
Expand Down
9 changes: 9 additions & 0 deletions inflection/src/inflection/dialog/InflectableStringConcept.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ class INFLECTION_CLASS_API inflection::dialog::InflectableStringConcept
* @param value - The speakable string to convert to a concept
*/
InflectableStringConcept(const SemanticFeatureModel* model, const SpeakableString& value);

/**
* Constructs a concept given a semantic feature model and a speakable string
*
* @param model - The semantic feature model required to initialize the concept.
* @param value - The speakable string to convert to a concept
* @param intitialConstraints - The intitial constraints for the map.
*/
InflectableStringConcept(const SemanticFeatureModel* model, const SpeakableString& value, const std::map<SemanticFeature, ::std::u16string>& intitialConstraints);
/**
* Copy constructor
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ ::std::optional<::std::u16string> FrGrammarSynthesizer_CountLookupFunction::dete
int64_t wordGrammemes = 0;
dictionary.getCombinedBinaryType(&wordGrammemes, word);
if ((wordGrammemes & properNounProperty) == properNounProperty) {
return {{}};
return std::u16string{};
}
if (checkInvariantNouns(word, wordGrammemes)) {
return GrammemeConstants::NUMBER_SINGULAR();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ ::std::optional<::std::u16string> HiGrammarSynthesizer_HiDisplayFunction::inflec

::std::optional<::std::vector<::std::u16string>> HiGrammarSynthesizer_HiDisplayFunction::inflectSignificantWords(const std::vector<::std::u16string> &words, const ::std::map<SemanticFeature, ::std::u16string> &constraints, bool enableInflectionGuess) const {
if (words.empty()) {
return {{}};
return std::vector<std::u16string>{};
}
const auto &dictionary = dictionaryInflector.getDictionary();
int64_t adpositionIndex = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ ::std::optional<::std::vector<::std::u16string>> PtGrammarSynthesizer_PtDisplayF

::std::optional<::std::vector<::std::u16string>> PtGrammarSynthesizer_PtDisplayFunction::inflectSignificantWords(const std::vector<::std::u16string> &words, const ::std::map<::inflection::dialog::SemanticFeature, ::std::u16string> &constraints, bool enableInflectionGuess) const {
switch (words.size()) {
case 0: return {{}};
case 0: return std::vector<std::u16string>{};
case 1: {
int64_t wordType = 0;
dictionary.getCombinedBinaryType(&wordType, words[0]);
Expand Down
7 changes: 7 additions & 0 deletions inflection/test/resources/inflection/dialog/inflection/es.xml
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,11 @@
<test><source definiteness="definite">álgebra añeja</source><result>el álgebra añeja</result></test> <!-- An exception to the gender rule. -->
<test><source definiteness="definite">añeja álgebra</source><result>la añeja álgebra</result></test>
<test><source>área</source><result withDemAdj="esta área"/></test>

<!-- New Tests for stating grammemes -->
<test><source definiteness="definite">cometa</source><initial gender="masculine"/><result number="singular" gender="masculine">el cometa</result></test>
<test><source definiteness="definite">cometa</source><initial gender="feminine"/><result number="singular" gender="feminine">la cometa</result></test>
<test><source definiteness="definite">QQQQ</source><initial gender="masculine" number="plural"/><result number="plural" gender="masculine">los QQQQ</result></test>
<test><source definiteness="definite">QQQQ</source><initial gender="feminine" number="plural"/><result number="plural" gender="masculine">las QQQQ</result></test>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests look reasonable.


</inflectionTest>
40 changes: 34 additions & 6 deletions inflection/test/src/inflection/dialog/InflectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <inflection/dialog/InflectableStringConcept.hpp>
#include <inflection/dialog/SemanticFeatureModel.hpp>
#include <inflection/dialog/SemanticFeature.hpp>
#include <inflection/dialog/SpeakableString.hpp>
#include <inflection/dialog/LocalizedCommonConceptFactoryProvider.hpp>
#include <inflection/exception/XMLParseException.hpp>
Expand Down Expand Up @@ -35,9 +36,26 @@ static std::string getInfoStringForTestInput(const ::inflection::dialog::Semanti
return "locale=" + model.getLocale().getName() + (source.isEmpty() ? "" : std::string(" source=") + inflection::util::StringUtils::to_string(source.toString())) + " {" + constraintsStr + "}";
}

static void compareInflection(const ::inflection::dialog::SemanticFeatureModel& model, const ::inflection::dialog::SpeakableString& expected, const ::inflection::dialog::SpeakableString& source, const std::map<std::u16string, std::u16string>& constraints, const std::map<std::u16string, std::u16string>& resultAttributes)
static void compareInflection(const ::inflection::dialog::SemanticFeatureModel& model,
const ::inflection::dialog::SpeakableString& expected,
const ::inflection::dialog::SpeakableString& source,
const std::map<std::u16string, std::u16string>& constraints,
const std::map<std::u16string, std::u16string>& resultAttributes,
const std::map<std::u16string, std::u16string>& initialAttributes)
{
inflection::dialog::InflectableStringConcept inflectableConcept(&model, source);
std::map<inflection::dialog::SemanticFeature, ::std::u16string> intitialConstraints;
for(const auto& [attrKey, attrVal] : initialAttributes) {
auto featureName = model.getFeature(attrKey);
if (featureName == nullptr) {
FAIL_CHECK(model.getLocale().getName() + std::string(": feature name is not recognized: ") + inflection::util::StringUtils::to_string(attrKey));
}
const auto& boundedValues(npc(featureName)->getBoundedValues());
if (!boundedValues.empty() && boundedValues.find(attrVal) == boundedValues.end()) {
FAIL_CHECK(model.getLocale().getName() + std::string(": feature value \"") + inflection::util::StringUtils::to_string(attrVal) + "\" is not valid for feature \"" + inflection::util::StringUtils::to_string(attrKey) + "\"");
}
intitialConstraints[*featureName] = attrVal;
}
inflection::dialog::InflectableStringConcept inflectableConcept(&model, source, intitialConstraints);
for (const auto& constraint : constraints) {
auto featureName = model.getFeature(constraint.first);
if (featureName == nullptr) {
Expand Down Expand Up @@ -171,16 +189,26 @@ TEST_CASE("InflectionTest#testInflections")
if (xmlStrEqual(sourceNode->name, (const xmlChar *) "source") == 0) {
throw ::inflection::exception::XMLParseException(u"Expecting element <source>, got <" + ::inflection::util::StringUtils::to_u16string(std::string(reinterpret_cast<const char*>(sourceNode->name))) + u">");
}
xmlNodePtr resultNode = xmlNextElementSibling(sourceNode);
if (xmlStrEqual(resultNode->name, (const xmlChar *) "result") == 0) {
throw ::inflection::exception::XMLParseException(u"Expecting element <result>, got <" + ::inflection::util::StringUtils::to_u16string(std::string(reinterpret_cast<const char*>(resultNode->name))) + u">");

// optional initial child tag
curTestChild = xmlNextElementSibling(sourceNode);
xmlNodePtr initialNode = nullptr;
if(xmlStrEqual(curTestChild->name, (const xmlChar*) "initial") != 0) {
initialNode = curTestChild;
curTestChild = xmlNextElementSibling(curTestChild);
}

if (xmlStrEqual(curTestChild->name, (const xmlChar *) "result") == 0) {
throw ::inflection::exception::XMLParseException(u"Expecting element <result>, got <" + ::inflection::util::StringUtils::to_u16string(std::string(reinterpret_cast<const char*>(curTestChild->name))) + u">");
}
xmlNodePtr resultNode = curTestChild;
auto sourceConstraints(XMLUtils::getAttributes(sourceNode));
auto resultAttributes(XMLUtils::getAttributes(resultNode));
auto initialAttributes(XMLUtils::getAttributes(initialNode));
auto sourceString(getSpeakableString(sourceNode));
auto resultString(getSpeakableString(resultNode));

compareInflection(*npc(model), resultString, sourceString, sourceConstraints, resultAttributes);
compareInflection(*npc(model), resultString, sourceString, sourceConstraints, resultAttributes, initialAttributes);
numTests++;
}
xmlFreeDoc(xmlDoc);
Expand Down
1 change: 1 addition & 0 deletions inflection/test/src/util/XMLUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <inflection/npc.hpp>

std::map<std::u16string, std::u16string> XMLUtils::getAttributes(xmlNodePtr node) {
if(node == nullptr) return { };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split this line and use curly braces. It makes it easier to place break points.

xmlAttr* attribute = node->properties;
std::map<std::u16string, std::u16string> result;
while (attribute) {
Expand Down
Loading