package router import ( "strings" "unicode" "github.com/kami/maven/internal/lexicon" ) var possessionQuestionWords = func() map[string]bool { words := make(map[string]bool) for _, word := range append(lexicon.Interrogatives(), lexicon.NarrativeRequests()...) { words[word] = true } return words }() // PossessionStatementGrammar recognises the Russian possessive construction // “у меня …” as a statement to remember. Russian has no present-tense “have”: // the preposition and genitive pronoun are the grammatical predicate, so a // sentence such as “у меня новый ноутбук” contains no verb for a generic // sentence parser to anchor on. Both the hash floor and the deployed routing // heads have measured this exact shape as a query and would try to answer it // instead of recording it. // // This is a token grammar, not a phrase regexp or a noun list. The two-token // frame is a closed grammatical construction and the remainder stays open. // Questions and explicit capture/reminder/narrative requests retain their // narrower routes; only a plain declaration is claimed. func PossessionStatementGrammar() Grammar { return Grammar{Name: "possession-statement", Decide: possessionStatementDecision} } func possessionStatementDecision(utterance string) (Decision, bool) { tokens := planTokens(utterance) if len(tokens) < 3 || tokens[0] != "у" || tokens[1] != "меня" { return Decision{}, false } if possessionQuestionShaped(utterance) || CarriesCaptureVerb(utterance) || carriesReminderVerbTokens(tokens) { return Decision{}, false } return Decision{ Stage: 0, Intent: IntentNote, Confidence: 1, Slots: Slots{Text: utterance}, }, true } // possessionQuestionShaped is the question half of this grammar over Russian // word structure. IsQuestionShaped intentionally tokenises punctuation away, // which makes the interrogative root in indefinite pronouns look like a // question: “что-то сломалось”, “кто-нибудь пришёл”, “когда-то работало”. Here // that would defeat the open possession statement this grammar exists for. // // Hyphenated indefinite forms are a closed grammatical construction, not a // phrase list: interrogative+{то, либо, нибудь}, or кое+interrogative. Every // other exact interrogative/narrative token remains a question, as does a // question mark. No noun or payload vocabulary is involved. func possessionQuestionShaped(text string) bool { if strings.HasSuffix(strings.TrimSpace(text), "?") { return true } for _, lexeme := range possessionLexemes(text) { if possessionQuestionWords[lexeme] { return true } parts := strings.Split(lexeme, "-") if indefiniteQuestionCompound(parts) { continue } for _, part := range parts { if possessionQuestionWords[part] { return true } } } return false } func possessionLexemes(text string) []string { return strings.FieldsFunc(strings.ToLower(text), func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '-' }) } func indefiniteQuestionCompound(parts []string) bool { if len(parts) != 2 { return false } if parts[0] == "кое" && possessionQuestionWords[parts[1]] { return true } if !possessionQuestionWords[parts[0]] { return false } switch parts[1] { case "то", "либо", "нибудь": return true default: return false } } func carriesReminderVerbTokens(tokens []string) bool { for _, verb := range lexicon.ReminderVerbs() { if hasTok(tokens, verb) { return true } } return false }