スタバで正しい呪文を詠唱できるように構文解析器を作った

# 呪文
スターバックス(スタバ）ではレジの注文の際に呪文を唱えることで、カスタマイズドリンクをカッコよく注文することができる。ネット上にはスタバの呪文が数多く紹介されているが、呪文の構文は定まっていない。「パートナー[^1]」と呼ばれる人がヒューリスティックに詠唱呪文を請けているため詠唱は成功するが、どうせ詠唱するなら正しくできたほうがかっこいいよね、ということで構文解析器を使って正しい呪文かそうでないかを判別できるようにしてみた。

# 方針
公式サイトに載っている呪文がおそらく正しい呪文であろうという推測から、[DISCOVER YOUR FAVORITES](https://dyf.starbucks.co.jp/)というスタバのサイトに載っているものからデータを集めた。期間限定のものまで実装すると際限なさそうだったので主要なレギュラーメニューのみを対象にした。そこからおおよそ当てはまるであろう言語規則を見つけ、EBNF（拡張バッカス・ナウア記法）に落とし込み構文解析器化する。EBNFから構文解析器へはよさげなパッケージがあったので[こちら](https://www.npmjs.com/package/ebnf)を利用した。

# 作ったもの
作ったものは下記リンク先のもの。標準入力に[DISCOVER YOUR FAVORITES](https://dyf.starbucks.co.jp/)にある呪文を入力すると正しい呪文か不正な呪文かを構文解析器にかけて判定してくれます。

https://replit.com/@kaku10/starbucks?v=1#index.js

## EBNF部分のみ抜き出し

```prolog
value ::= customized_coffee | customized_espresso | customized_frappuccino | customized_teavana

decaf_option ::= "ディカフェ" | "デカフェ"
foam_milk_option ::= "フォーミー" | "ライトフォーム" | "ノンフォーム"
cappuccino_foam_milk_option ::= "ドライ" | "ウェット"
hot_temperature_option ::= "エクストラホット" | "ライトホット"
temperature ::= hot_temperature_option | "アイス"
ice_option ::= "エクストラアイス" | "ライトアイス"
coffee_option ::= "ライトコーヒー" | "エクストラコーヒー"
milk_option ::= "ノンファットミルク" | "ノンファット" | "低脂肪タイプミルク" | "ソイ" | "アーモンドミルク" | "オーツミルク" | "ブレベミルク"
non_tea_option ::= "ノンティー"
syrup_option ::= "エクストラシロップ" | "ライトシロップ" | "ノンシロップ"
add_or_change_syrup_option ::= "バニラ" | "アーモンドトフィー" | "ホワイトモカ" | "キャラメル" | "モカ"
add_syrup_option ::=  "with" ("バニラシロップ" | "アーモンドトフィーシロップ" | "ホワイトモカシロップ" | "キャラメルシロップ" | "モカシロップ")
add_espresso_shot ::= "エクストラショット" | "リストレット" | "シングル"
sauce_option ::= "エクストラソース" | "ライトソース" | "ノンソース"
chocolate_chip_option ::= "エクストラチップ" | "ライトチップ" | "ノンチップ"
add_sauce_option ::= "with" ("チョコレートソース" | "ホイップクリーム" | "チョコレートチップ" | "キャラメルソース")
whip_cream_option ::= "ノンホイップ" | "ライトホイップ" | "エクストラホイップ"
powder_option ::= "ノンパウダー" | "ライトパウダー" | "エクストラパウダー"
condiment ::= "シナモンパウダー" | "ハチミツ" | "ガムシロップ" | "ブラウンシュガー" | "ココアパウダー" | "オレンジバニラシュガー"

common_suffix ::= add_sauce_option* add_syrup_option* condiment*

brewed_coffee_prefix ::= temperature? decaf_option? add_espresso_shot? add_or_change_syrup_option?
brewed_coffee ::= "ドリップコーヒー" | "カフェ アメリカーノ"
customized_brewed_coffee ::= brewed_coffee_prefix? brewed_coffee common_suffix?

cold_brewed_coffee_prefix ::= decaf_option? add_espresso_shot? add_or_change_syrup_option?
cold_brewed_coffee ::= "コールドブリュー コーヒー"
customized_cold_brewed_coffee ::= cold_brewed_coffee_prefix? cold_brewed_coffee common_suffix?

latte_prefix ::= temperature? decaf_option? add_espresso_shot? milk_option? foam_milk_option? add_or_change_syrup_option?
latte ::= "トリプルエスプレッソ ラテ" | "スターバックス ラテ" | "ムース フォーム ラテ" | "カフェ ミスト"
customized_latte ::= latte_prefix? latte common_suffix?

mocha_prefix ::= temperature? decaf_option? add_espresso_shot? milk_option? foam_milk_option? syrup_option? add_or_change_syrup_option?
mocha ::= "カフェ モカ" | "ホワイト モカ"
customized_mocha ::= mocha_prefix? mocha common_suffix?

cappuccino_prefix ::= decaf_option? add_espresso_shot? cappuccino_foam_milk_option? milk_option? add_or_change_syrup_option?
cappuccino ::= "カプチーノ"
customized_cappuccino ::= cappuccino_prefix? cappuccino common_suffix?

caramel_macchiato_prefix ::= temperature? decaf_option? add_espresso_shot? milk_option? foam_milk_option? syrup_option? add_or_change_syrup_option?
caramel_macchiato ::= "キャラメル マキアート"
customized_caramel_macchiato ::= caramel_macchiato_prefix? caramel_macchiato common_suffix?

macchiato_prefix ::= decaf_option? add_espresso_shot? milk_option? foam_milk_option? add_or_change_syrup_option?
macchiato ::= "エスプレッソ マキアート"
customized_macchiato ::= macchiato_prefix? macchiato common_suffix?

espresso_conpanna ::= "エスプレッソ コンパナ"
espresso_conpanna_prefix ::= decaf_option? whip_cream_option? add_or_change_syrup_option?
customized_espresso_conpanna ::= espresso_conpanna_prefix? espresso_conpanna common_suffix?

espresso_affogato_frappuccino ::= "エスプレッソ アフォガート フラペチーノ"
espresso_affogato_frappuccino_prefix ::= decaf_option? add_espresso_shot? syrup_option? add_or_change_syrup_option?
customized_espresso_affogato_frappuccino ::= espresso_affogato_frappuccino_prefix? espresso_affogato_frappuccino common_suffix?

coffee_frappuccino ::= "コーヒー フラペチーノ"
coffee_frappuccino_prefix ::= add_espresso_shot? coffee_option? milk_option? add_or_change_syrup_option?
customized_coffee_frappuccino ::= coffee_frappuccino_prefix? coffee_frappuccino common_suffix?

dark_mocha_chip_frappuccino ::= "ダーク モカ チップ フラペチーノ"
dark_mocha_chip_frappuccino_prefix ::= add_espresso_shot? coffee_option? whip_cream_option? chocolate_chip_option? powder_option? milk_option? syrup_option? add_or_change_syrup_option?
customized_dark_mocha_chip_frappuccino ::= dark_mocha_chip_frappuccino_prefix? dark_mocha_chip_frappuccino common_suffix?

caramel_frappuccino ::= "キャラメル フラペチーノ"
caramel_frappuccino_prefix ::= add_espresso_shot? coffee_option? whip_cream_option? milk_option? syrup_option? add_or_change_syrup_option?
customized_caramel_frappuccino ::= caramel_frappuccino_prefix? caramel_frappuccino common_suffix?

matcha_cream_frappuccino ::= "抹茶 クリーム フラペチーノ"
matcha_cream_frappuccino_prefix ::= add_espresso_shot? whip_cream_option? powder_option? milk_option? syrup_option? add_or_change_syrup_option?
customized_matcha_cream_frappuccino ::= matcha_cream_frappuccino_prefix? matcha_cream_frappuccino common_suffix?

vanilla_cream_frappuccino ::= "バニラ クリーム フラペチーノ"
vanilla_cream_frappuccino_prefix ::= add_espresso_shot? whip_cream_option? milk_option? syrup_option? add_or_change_syrup_option?
customized_vanilla_cream_frappuccino ::= vanilla_cream_frappuccino_prefix? vanilla_cream_frappuccino common_suffix?

dark_mocha_chip_cream_frappuccino ::= "ダーク モカ チップ クリーム フラペチーノ"
dark_mocha_chip_cream_frappuccino_prefix ::= add_espresso_shot? whip_cream_option? chocolate_chip_option? powder_option? milk_option? syrup_option? add_or_change_syrup_option?
customized_dark_mocha_chip_cream_frappuccino ::= dark_mocha_chip_cream_frappuccino_prefix? dark_mocha_chip_cream_frappuccino common_suffix?

mango_passion_tea_frappuccino ::= "マンゴー パッション ティー フラペチーノ"
mango_passion_tea_prefix ::= add_espresso_shot? non_tea_option? add_or_change_syrup_option?
customized_mango_passion_tea_frappuccino ::= mango_passion_tea_prefix? mango_passion_tea_frappuccino common_suffix?

tea_latte ::= "ほうじ茶 ティー ラテ" | "イングリッシュ ブレックファスト ティー ラテ" | "カモミール ティー ラテ" | "アール グレイ ティー ラテ" | "ゼンクラウド ウーロン ティー ラテ"
tea_latte_prefix ::= hot_temperature_option? add_espresso_shot? milk_option? foam_milk_option? syrup_option? add_or_change_syrup_option?
customized_tea_latte ::= tea_latte_prefix? tea_latte common_suffix?

matcha_tea_latte ::= "抹茶 ティー ラテ"
matcha_tea_latte_prefix ::= temperature? add_espresso_shot? powder_option? milk_option? foam_milk_option? syrup_option? add_or_change_syrup_option?
customized_matcha_tea_latte ::= matcha_tea_latte_prefix? matcha_tea_latte common_suffix?

chai_tea_latte ::= "チャイ ティー ラテ"
chai_tea_latte_prefix ::= temperature? add_espresso_shot? milk_option? foam_milk_option? syrup_option? add_or_change_syrup_option?
customized_chai_tea_latte ::= chai_tea_latte_prefix? chai_tea_latte common_suffix?

tea ::= "イングリッシュ ブレックファスト" | "アール グレイ" | "カモミール" | "ユースベリー" | "ほうじ茶" | "ゼンクラウド ウーロン"
tea_prefix ::= add_espresso_shot? add_or_change_syrup_option?
customized_tea ::= tea_prefix? tea common_suffix?

customized_coffee ::= customized_brewed_coffee | customized_cold_brewed_coffee
customized_espresso ::= customized_cappuccino | customized_latte | customized_mocha | customized_caramel_macchiato | customized_macchiato | customized_espresso_conpanna
customized_frappuccino ::= customized_espresso_affogato_frappuccino | customized_coffee_frappuccino | customized_dark_mocha_chip_frappuccino | customized_caramel_frappuccino | customized_matcha_cream_frappuccino | customized_vanilla_cream_frappuccino | customized_dark_mocha_chip_cream_frappuccino | customized_mango_passion_tea_frappuccino
customized_teavana ::= customized_tea_latte | customized_matcha_tea_latte | customized_chai_tea_latte | customized_tea
```

[^1]: スタバは店員のことはパートナーと呼ぶらしい

# 感想
最近スタバを飲んでるのでなんとなく作ってみた。思いのほか例外が多いのでEBNFに落とすのがかなり面倒だった。しかも一部カスタマイズには対応していなかったり、そもそも公式が一部カスタマイズの呪文を載せてなかったりする。オールミルクとか。どちらかというとドメイン設計の教材としてのほうが面白かったかもしれないと思いつつアドベントカレンダーに間に合わせるのであった。