diff --git a/convert_rule.go b/convert_rule.go index d6a2ef7..1e984a9 100644 --- a/convert_rule.go +++ b/convert_rule.go @@ -14,7 +14,8 @@ type convertRule struct { EnableWhenSentenceSeparation bool // 文の区切り(単語の後に句点か読点がくる、あるいは何もない)場合だけ有効にする AppendLongNote bool // 波線を追加する DisablePrefix bool // 「お」を手前に付与しない - Value string // この文字列に置換する + EnableKutenToExclamation bool + Value string // この文字列に置換する } /* @@ -78,9 +79,10 @@ func (c convertRule) disablePrefix(v bool) convertRule { // continuousConditionsConvertRule は連続する条件がすべてマッチしたときに変換するルール。 type continuousConditionsConvertRule struct { - Conditions convertConditions - AppendLongNote bool - Value string + Conditions convertConditions + AppendLongNote bool + EnableKutenToExclamation bool + Value string } // sentenceEndingParticleConvertRule は「名詞」+「動詞」+「終助詞」の組み合わせによる変換ルール。 @@ -186,6 +188,7 @@ var ( newCond([]string{"動詞", "自立"}, "し"), newCond([]string{"助動詞"}, "ます"), }, + EnableKutenToExclamation: true, }, { @@ -194,6 +197,7 @@ var ( newCond([]string{"助動詞"}, "だ"), newCond([]string{"助詞", "接続助詞"}, "から"), }, + EnableKutenToExclamation: true, }, { @@ -203,6 +207,7 @@ var ( newCond([]string{"名詞", "非自立", "一般"}, "ん"), newCond([]string{"助動詞"}, "だ"), }, + EnableKutenToExclamation: true, }, { @@ -211,6 +216,7 @@ var ( newCond([]string{"助動詞"}, "だ"), newCond([]string{"助詞", "終助詞"}, "よ"), }, + EnableKutenToExclamation: true, }, { @@ -219,6 +225,7 @@ var ( newCond(posPronounGeneral, "なん"), newCond(posSubPostpositionalParticle, "じゃ"), }, + EnableKutenToExclamation: true, }, { Value: "なんですの", @@ -226,6 +233,7 @@ var ( newCond(posPronounGeneral, "なん"), newCond(posAuxiliaryVerb, "だ"), }, + EnableKutenToExclamation: true, }, { Value: "なんですの", @@ -233,6 +241,7 @@ var ( newCond(posPronounGeneral, "なん"), newCond(posAssistantParallelParticle, "や"), }, + EnableKutenToExclamation: true, }, { @@ -241,6 +250,7 @@ var ( condNounsGeneral, newCond(posAuxiliaryVerb, "じゃ"), }, + EnableKutenToExclamation: true, }, { Value: "@1ですの", @@ -248,6 +258,7 @@ var ( condNounsGeneral, newCond(posAuxiliaryVerb, "だ"), }, + EnableKutenToExclamation: true, }, { Value: "@1ですの", @@ -255,6 +266,7 @@ var ( condNounsGeneral, newCond(posAuxiliaryVerb, "や"), }, + EnableKutenToExclamation: true, }, { @@ -263,6 +275,7 @@ var ( condPronounsGeneral, newCond(posAuxiliaryVerb, "じゃ"), }, + EnableKutenToExclamation: true, }, { Value: "@1ですの", @@ -270,6 +283,7 @@ var ( condPronounsGeneral, newCond(posAuxiliaryVerb, "だ"), }, + EnableKutenToExclamation: true, }, { Value: "@1ですの", @@ -277,6 +291,7 @@ var ( condPronounsGeneral, newCond(posAuxiliaryVerb, "や"), }, + EnableKutenToExclamation: true, }, } @@ -375,8 +390,9 @@ var ( AfterIgnoreConditions: convertConditions{ {Features: posSubParEndParticle}, }, - AppendLongNote: true, - Value: "ですわ", + AppendLongNote: true, + EnableKutenToExclamation: true, + Value: "ですわ", }, { Conditions: convertConditions{ @@ -385,8 +401,9 @@ var ( AfterIgnoreConditions: convertConditions{ {Features: posSubParEndParticle}, }, - AppendLongNote: true, - Value: "ですわ", + AppendLongNote: true, + EnableKutenToExclamation: true, + Value: "ですわ", }, { Conditions: convertConditions{ @@ -394,6 +411,7 @@ var ( }, EnableWhenSentenceSeparation: true, AppendLongNote: true, + EnableKutenToExclamation: true, Value: "いたしますわ", }, { @@ -402,6 +420,7 @@ var ( }, EnableWhenSentenceSeparation: true, AppendLongNote: true, + EnableKutenToExclamation: true, Value: "なりますわ", }, { @@ -426,8 +445,9 @@ var ( Conditions: convertConditions{ newCond(posSentenceEndingParticle, "わ"), }, - AppendLongNote: true, - Value: "ですわ", + AppendLongNote: true, + EnableKutenToExclamation: true, + Value: "ですわ", }, { Conditions: convertConditions{ @@ -472,8 +492,9 @@ var ( Conditions: convertConditions{ newCond(posAuxiliaryVerb, "ます"), }, - AppendLongNote: true, - Value: "ますわ", + AppendLongNote: true, + EnableKutenToExclamation: true, + Value: "ますわ", }, { Conditions: convertConditions{ @@ -481,6 +502,7 @@ var ( }, EnableWhenSentenceSeparation: true, AppendLongNote: true, + EnableKutenToExclamation: true, Value: "たわ", }, { @@ -502,13 +524,15 @@ var ( Conditions: convertConditions{ newCond(posVerbNotIndependence, "ください"), }, - Value: "くださいまし", + EnableKutenToExclamation: true, + Value: "くださいまし", }, { Conditions: convertConditions{ newCond(posVerbNotIndependence, "くれ"), }, - Value: "くださいまし", + EnableKutenToExclamation: true, + Value: "くださいまし", }, newRuleInterjection("ありがとう", "ありがとうございますわ"), newRuleInterjection("じゃぁ", "それでは"), diff --git a/ojosama.go b/ojosama.go index 7b5eb1a..c9d508c 100644 --- a/ojosama.go +++ b/ojosama.go @@ -13,8 +13,14 @@ import ( // ConvertOption はお嬢様変換時のオプショナルな設定。 type ConvertOption struct { - forceAppendLongNote forceAppendLongNote // 単体テスト用のパラメータ - forceCharsTestMode *chars.TestMode // 単体テスト用のパラメータ + // 句点を!に変換する機能をOFFにする。句点を!に変換してしまうと変換元の文章 + // のニュアンスを破壊する可能性があるため、オプションパラメータで無効にでき + // るようにする。 + DisableKutenToExclamation bool + + forceAppendLongNote forceAppendLongNote // 単体テスト用のパラメータ + forceCharsTestMode *chars.TestMode // 単体テスト用のパラメータ + forceKutenToExclamation bool // KutenToExclamationで強制的に3番目の要素を選択する } // forceAppendLongNote は強制的に波線や感嘆符や疑問符を任意の数追加するための設定。 @@ -35,6 +41,11 @@ const ( var ( alnumRegexp = regexp.MustCompile(`^[a-zA-Z0-9]+$`) + + featKuten = []string{"記号", "句点"} // 。 + featToten = []string{"記号", "読点"} // 、 + + shuffleElementsKutenToExclamation = []string{"。", "。", "!", "❗"} ) func init() { @@ -90,11 +101,20 @@ func Convert(src string, opt *ConvertOption) (string, error) { } // お嬢様言葉に変換 - buf, nounKeep, i = convert(data, tokens, i, buf, nounKeep, opt) + var kutenToEx bool + buf, nounKeep, i, kutenToEx = convert(data, tokens, i, buf, nounKeep, opt) // 形容詞、自立で文が終わった時は丁寧語ですわを追加する if isAppendablePoliteWord(data, tokens, i) { buf += politeWord + kutenToEx = true + } + + if kutenToEx { + if ok, s, pos := randomKutenToExclamation(tokens, i, opt); ok { + buf += s + i = pos + } } result.WriteString(buf) @@ -207,12 +227,21 @@ func convertContinuousConditions(tokens []tokenizer.Token, tokenPos int, opt *Co surface = "お" + surface } result = strings.ReplaceAll(result, "@1", surface) + + // 句点と~が同時に発生することは無いので早期リターンで良い + if ok, s, pos := randomKutenToExclamation(tokens, n, opt); ok { + result += s + n = pos + return result, n, true + } + if mc.AppendLongNote { if note, pos := newLongNote(tokens, n, opt); note != "" { result += note n = pos } } + return result, n, true } return "", -1, false @@ -249,13 +278,13 @@ excludeLoop: } // convert は基本的な変換を行う。 -func convert(data tokenizer.TokenData, tokens []tokenizer.Token, i int, surface string, nounKeep bool, opt *ConvertOption) (string, bool, int) { +func convert(data tokenizer.TokenData, tokens []tokenizer.Token, i int, surface string, nounKeep bool, opt *ConvertOption) (string, bool, int, bool) { var ok bool var c convertRule if ok, c = matchConvertRule(data, tokens, i); !ok { result := surface result, nounKeep = appendPrefix(data, tokens, i, result, nounKeep) - return result, nounKeep, i + return result, nounKeep, i, false } result := c.Value @@ -274,7 +303,7 @@ func convert(data tokenizer.TokenData, tokens []tokenizer.Token, i int, surface result, nounKeep = appendPrefix(data, tokens, i, result, nounKeep) } - return result, nounKeep, pos + return result, nounKeep, pos, c.EnableKutenToExclamation } func matchConvertRule(data tokenizer.TokenData, tokens []tokenizer.Token, i int) (bool, convertRule) { @@ -390,7 +419,7 @@ func isAppendablePoliteWord(data tokenizer.TokenData, tokens []tokenizer.Token, // isSentenceSeparation は data が文の区切りに使われる token かどうかを判定する。 func isSentenceSeparation(data tokenizer.TokenData) bool { - return containsFeatures([][]string{{"記号", "句点"}, {"記号", "読点"}}, data.Features) || + return containsFeatures([][]string{featKuten, featToten}, data.Features) || containsString([]string{"!", "!", "?", "?"}, data.Surface) } @@ -488,3 +517,35 @@ func getContinuousExclamationMark(tokens []tokenizer.Token, i int, feq *chars.Ex func isPoliteWord(data tokenizer.TokenData) bool { return strings.HasPrefix(data.Reading, "オ") } + +// randomKutenToExclamation はランダムで句点を!に変換する。 +func randomKutenToExclamation(tokens []tokenizer.Token, tokenPos int, opt *ConvertOption) (bool, string, int) { + if opt != nil && opt.DisableKutenToExclamation { + return false, "", tokenPos + } + + pos := tokenPos + 1 + if len(tokens) <= pos { + return false, "", tokenPos + } + + data := tokenizer.NewTokenData(tokens[pos]) + if !isKuten(data) { + return false, "", tokenPos + } + + // テスト用に値をすげ替えられるようにする + var s []string + if opt != nil && opt.forceKutenToExclamation { + s = []string{"❗", "❗"} + } else { + s = shuffleElementsKutenToExclamation + } + + rand.Shuffle(len(s), func(i, j int) { s[i], s[j] = s[j], s[i] }) + return true, s[0], pos +} + +func isKuten(data tokenizer.TokenData) bool { + return equalsFeatures(data.Features, featKuten) && data.Surface == "。" +} diff --git a/ojosama_test.go b/ojosama_test.go index 9d32469..93ec4c1 100644 --- a/ojosama_test.go +++ b/ojosama_test.go @@ -8,6 +8,10 @@ import ( ) func TestConvert(t *testing.T) { + opt := &ConvertOption{ + DisableKutenToExclamation: true, + } + tests := []struct { desc string src string @@ -19,175 +23,175 @@ func TestConvert(t *testing.T) { desc: "正常系: 名詞の手前には「お」をお付けいたしますわ", src: "これはハーブです", want: "こちらはおハーブですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「名詞,一般」の後に「動詞,自立」が来た時は2Tokenで1つの動詞として解釈いたしますので、「お」を付けませんわ", src: "プレイする", want: "プレイいたしますわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「プレイする」の例文ですわ", src: "〇〇をプレイする", want: "〇〇をプレイいたしますわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 二人称はすべて「貴方」に変換いたしますわ", src: "あなたは。あんたは。おまえは。お前は。てめぇは。てめえは。貴様は。君は。", want: "貴方は。貴方は。貴方は。貴方は。貴方は。貴方は。貴方は。貴方は。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: わたしはわたくしに変換いたしますわ", src: "わたし", want: "わたくし", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「ですか」のときは「ですの」に変換いたしますわ", src: "ビデオテープはどこで使うんですか", want: "おビデオテープはどちらで使うんですの", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「汚い|きたない」のときは「きったねぇ」に変換いたしますわ", src: "汚いです", want: "きったねぇですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「汚い|きたない」のときは「きったねぇ」に変換いたしますわ", src: "きたないです", want: "きったねぇですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「臭い|くさい」のときは「くっせぇ」に変換いたしますわ", src: "臭いです", want: "くっせぇですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「臭い|くさい」のときは「くっせぇ」に変換いたしますわ", src: "くさいです", want: "くっせぇですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 複数の文も処理できますわ", src: "〇〇をプレイする。きたないわ。", want: "〇〇をプレイいたしますわ。きったねぇですわ。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「ました」は「おりました」ですの", src: "わたしも使ってました", want: "わたくしも使っておりましたわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: アルファベット単語の場合は「お」をつけませんの", src: "これはgrassです。あれはabcdefg12345です", want: "こちらはgrassですわ。あちらはabcdefg12345ですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「します」は「いたしますわ」に変換しますわ", src: "使用します", want: "使用いたしますわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 名詞が連続する場合は最初の1つ目にだけ「お」を付けますわ", src: "一般女性。経年劣化。トップシークレット", want: "お一般女性。お経年劣化。おトップシークレット", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: お嬢様は変更しませんわ", src: "お嬢様", want: "お嬢様", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: サロメお嬢様は固有名詞ですわ", src: "わたしは壱百満天原サロメです", want: "わたくしは壱百満天原サロメですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 名字も1単語として認識いたしますわ", src: "わたしの名字は壱百満天原です", want: "わたくしのお名字は壱百満天原ですわ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 壱百満点の笑顔を皆様方にお届けするのがわたくしの使命ですわ", src: "壱百満点", want: "壱百満点", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 部分一致の場合は処理しませんわ", src: "壱", want: "お壱", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 文の途中の「する」の場合は変換しませんわ", src: "テキストファイルをまるごと変換する場合は、以下のように実行します。", want: "おテキストファイルをまるごと変換する場合は、以下のように実行いたしますわ。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 文の途中の「する」の場合は変換しませんわ", src: "以下のお嬢様を可能な限り再現するアルゴリズムを目指してます。", want: "以下のお嬢様を可能な限り再現するおアルゴリズムを目指してますわ。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: すでに直前に「お」が付与されている場合は、「お」を付与しませんわ", src: "お寿司。おハーブ", want: "お寿司。おハーブ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「ください」は「くださいまし」に変換いたしますわ", src: "お使いください", want: "お使いくださいまし", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「ください」は「くださいまし」に変換いたしますわ", src: "お使いください!", want: "お使いくださいまし!", - opt: nil, + opt: opt, wantErr: false, }, { @@ -259,7 +263,7 @@ func TestConvert(t *testing.T) { desc: "正常系: (動詞)ないはそのままですわ", src: "限らない、飾らない、数えない、くだらない", want: "限らない、飾らない、数えない、くだらない", - opt: nil, + opt: opt, wantErr: false, }, @@ -284,7 +288,7 @@ func TestConvert(t *testing.T) { desc: "正常系: 一人称はすべて「私」に変換いたしますわ", src: "俺は。オレは。おれは。僕は。ボクは。ぼくは。あたしは。わたしは。", want: "私は。ワタクシは。わたくしは。私は。ワタクシは。わたくしは。わたくしは。わたくしは。", - opt: nil, + opt: opt, wantErr: false, }, @@ -294,7 +298,7 @@ func TestConvert(t *testing.T) { desc: "正常系: 小説の一文をテストしますわ", src: "俺の体験した怖い話、聞いてくれるか?ありがとう。じゃぁ、話すよ。", want: "私の体験した怖い話、聞いてくれますの?ありがとうございますわ。それでは、話すよ。", - opt: nil, + opt: opt, wantErr: false, }, @@ -302,7 +306,7 @@ func TestConvert(t *testing.T) { desc: "正常系: 小説の一文をテストしますわ", src: "俺の転職前の会社の頃の話だから、もう3年も前の話になる。", want: "私の転職前のお会社の頃の話ですので、もう3年も前の話になりますわ。", - opt: nil, + opt: opt, wantErr: false, }, @@ -310,7 +314,7 @@ func TestConvert(t *testing.T) { desc: "正常系: 小説の一文をテストしますわ", src: "通勤時に通る横断歩道の話なんだ。よくあるだろ、事故が多い横断歩道の話だよ。", want: "通勤時に通る横断歩道の話なんですの。よくありますでしょう、お事故が多い横断歩道の話ですわ。", - opt: nil, + opt: opt, wantErr: false, }, @@ -318,7 +322,7 @@ func TestConvert(t *testing.T) { desc: "正常系: 小説の一文をテストしますわ", src: "ただ内容は、偶然事故が多いって話じゃないから安心してくれ。実際に俺が体験した話だ。", want: "ただお内容は、偶然お事故が多いって話ではありませんので安心してくださいまし。実際に私が体験した話ですわ。", - opt: nil, + opt: opt, wantErr: false, }, @@ -327,7 +331,7 @@ func TestConvert(t *testing.T) { desc: "正常系: 小説の一文をテストしますわ", src: "信じてもいいし、信じなくても良い。まぁ、ゆったり聞いてくれ。夜は長いからな。", want: "信じてもいいですし、信じなくても良いですわ。まぁ、ゆったり聞いてくださいまし。夜は長いのでね。", - opt: nil, + opt: opt, wantErr: false, }, @@ -336,7 +340,7 @@ func TestConvert(t *testing.T) { // desc: "正常系: 小説の一文をテストしますわ", // src: "それで、横断歩道の話に戻るぞ。通勤時に使う横断歩道だからな、当然毎日通るだろ?", // want: "それで、横断歩道の話に戻りますわね。通勤時に使う横断歩道ですのでね、当然毎日通るでしょう?", - // opt: nil, + // opt: opt, // wantErr: false, // }, @@ -345,126 +349,126 @@ func TestConvert(t *testing.T) { desc: "正常系: 小説の一文をテストしますわ", src: "入社直後は道も周囲の店も何もわからなくて新鮮に感じたもんだけどさ、一番楽に通勤できるルートが確立したら、すぐに通勤が退屈になるわけだ。", want: "入社直後はお道もお周囲のお店も何もわからなくて新鮮に感じたものですわけれど、一番楽に通勤できるおルートが確立したら、すぐに通勤が退屈になるわけですわ。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: お嬢様らしく短く笑いますわ", src: "うふ", want: "おほ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: お嬢様らしく笑いますわ", src: "うふふ", want: "おほほ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: お嬢様らしくそこそこ笑いますわ", src: "うふふふふ", want: "おほほほほ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: お嬢様らしく長く笑いますわ", src: "うふふふふふ", want: "おほほほほほ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 切れ目があってもお嬢様らしく笑いますわ", src: "うふうふふふふふふふふ", want: "おほおほほほほほほほほ", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: パパはおパパ上、ママはおママ上とお呼びいたしますわ", src: "パパ、ママ、父様、母様、パパ上、ママ上", want: "おパパ上、おママ上、お父様、お母様、おパパ上、おママ上", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: パパはおパパ上、ママはおママ上とお呼びいたしますわ", src: "皆、皆様", want: "皆様方、皆様方", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 罵倒には「お」を付けませんのよ", src: "カス", want: "カス", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: こそあど言葉(こ)にも対応しておりましてよ", src: "これ、この、ここ、こちら、こう、こんな。", want: "こちら、こちらの、こちら、こちら、こう、このような。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: こそあど言葉(そ)にも対応しておりましてよ", src: "それ、その、そこ、そちら、そう、そんな。", want: "そちら、そちらの、そちら、そちら、そう、そのような。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: こそあど言葉(あ)にも対応しておりましてよ", src: "あれは、あの、あそこ、あちら、ああ、あんな。", want: "あちらは、あちらの、あちら、あちら、ああ、あのような。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: こそあど言葉(ど)にも対応しておりましてよ", src: "どれ、どの、どこ、どちら、どう、どんな。", want: "どちら、どちらの、どちら、どちら、どう、どのような。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 二重丁寧語にはしませんの", src: "お嬢様、おにぎり、お腹、お寿司、おませさん、お利口さん", want: "お嬢様、おにぎり、お腹、お寿司、おませさん、お利口さん", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 読みが「お」で始まる言葉には「お」を付けませんの", src: "追い剥ぎ、大型、大分、おろし金", want: "追い剥ぎ、大型、大分、おろし金", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: サ辺接続が手前に来る単語には「お」を付けませんわ", src: "横断歩道、解体新書、回覧板、回転寿司、駐車場", want: "横断歩道、解体新書、回覧板、回転寿司、駐車場", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 名詞+動詞+終助詞の組み合わせも変換いたしますわ~~!!この処理すっごく大変でしたの!!", src: "野球しようぜ。サッカーやろうよ。バスケやるか。柔道やるな。陸上すんな。テニスするぞ。卓球やるべ。ゲームするの。", want: "お野球をいたしませんこと。おサッカーをいたしませんこと。おバスケをいたしますわ。お柔道をしてはいけませんわ。お陸上をしてはいけませんわ。おテニスをいたしますわよ。お卓球をいたしませんこと。おゲームをいたしますわよ。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 名詞+動詞+助動詞のみで終助詞がない場合でもエラーにはなりませんのよ", src: "流鏑馬やろう", want: "流鏑馬やろう", - opt: nil, + opt: opt, wantErr: false, }, { @@ -519,28 +523,46 @@ func TestConvert(t *testing.T) { desc: "正常系: 意味のない文章のテストですわ", src: "あ!い❓❗う", want: "あ!い❓❗う", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 「ー」の前に「お」は付きませんの", src: "しようぜー。しようぜーー。しようぜ~。しようぜ~~。しようぜー~。", want: "しようぜー。しようぜーー。しようぜ~。しようぜ~~。しようぜー~。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: なん(じゃ|や|だ)は、いずれも「なんですの」に変換いたしますわ", src: "なんじゃこれ。なんだこれ。なんやこれ。", want: "なんですのこちら。なんですのこちら。なんですのこちら。", - opt: nil, + opt: opt, wantErr: false, }, { desc: "正常系: 名詞+(じゃ|だ|ですの)ですの", src: "アホだ。カラスや。バナナじゃ。これだ。それや。あれじゃ。", want: "おアホですの。おカラスですの。おバナナですの。これですの。それですの。あれですの。", - opt: nil, + opt: opt, + wantErr: false, + }, + { + desc: "正常系: 形容詞+自立の後に「ですわ」を付与する場合、「。」をランダムに!に変換いたしますわ", + src: "とても悲しい。", + want: "とても悲しいですわ❗", + opt: &ConvertOption{ + forceKutenToExclamation: true, + }, + wantErr: false, + }, + { + desc: "正常系: 「。」で終わる時に「!」に変換いたしますわ!", + src: "プレイする。ショットガンだ。", + want: "プレイいたしますわ❗おショットガンですの❗", + opt: &ConvertOption{ + forceKutenToExclamation: true, + }, wantErr: false, }, }