【Rust】TOMLで関数を自由に追加!自作電卓に設定読み込みとホットリロードを実装する #3

前回は、入力された数式を「意味のある塊」に分けるLexerを実装しました。 次に必要なのは、その塊が「定数なのか、関数なのか」を判断するための辞書です。
今回は、TOMLファイルを使って誰でも簡単に関数を拡張できる Definitions(定義管理) 機能を実装します。

前回 Lexerの実装 : 【Rust】自作計算機エンジンへの道:Lexer(字句解析)で数式をトークンに分解する #2

計算機構築までのワークフロー。現在地はtomlファイルによる関数読み込み実装

1. tomlで読み込ませたいもの

普通の電卓なら標準関数(sin, cosなど)があれば十分ですが、この電卓のコンセプトは 「自分専用の環境構築」 です。

  • 物理定数(pi, e, eVなど)を自由に追加したい
  • よく使う独自の計算式(畳み込みのサイズ計算など)を関数として登録したい
  • コードを書き換えずに、設定ファイルを追加するだけで機能を増やしたい

これらを実現するために、Rustと親和性の高いTOMLを採用しました。

計算式として解析するためには、文字列と関数の対応辞書が必要

2. 実装 Definitions : Serdeを使い倒す

Definitionsのとれる値は数値か、関数になります。

pub struct Definitions {
    pub constants: HashMap<String, f64>,
    pub functions: HashMap<String, FunctionDef>,
}

tomlのロード自体は、 toml::from_str(&content)?; といった感じで、tomlのパサーをそのまま利用しました。

pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
    let content = fs::read_to_string(path)?;
    let defs: Definitions = toml::from_str(&content)?;
    Ok(defs)
}

柔軟な定義を可能にする #[serde(untagged)]

このほかちょっと工夫したところとして、TOMLをある程度自由に書けるようにserde(untagged) をつかっています。これを使うことで、エイリアス風の記述と、変数定義からするしっかりとした記述両方拾えるようになっています。また、読み込みの深さをデフォルトで32にしており、再帰読み込みのバグなどで固まるのを防ぐようにしています。

// 関数定義構造体

#[derive(Debug, Deserialize, Clone)]
#[serde(untagged)]
pub enum FunctionDef {
    Simple(String),
    Complex {
        args: Vec<String>,
        expr: String,
        #[serde(default)]
        memoize: bool,
        #[serde(default = "default_depth")]
        max_depth: usize,
    },
}

fn default_depth() -> usize { 32 }

標準 definitions.toml

基本的なものは、definitions.tomlに記入していくことにしています。ユーザ定義のものは user.tomlとかの別ファイルで定義し、tomlの入っているフォルダを自動的に走査して読み込めるようにする予定です。

[constants]
pi = 3.1415926535
e = 2.71828

[functions]
# 式というかエイリアスの定義
golden_ratio = "1.61803"

# 引数を明示する定義にしています
conv = { args = ["W", "K"], expr = "W - K + 1" }

definitions.tomlに記入した例

3. 読み込めているか検証

出力部分を、前回のlexerの出力トークンを今回作成した Definitionsに渡し、解析できたかどうかを出力するように改変し、動かしてみました。

tomlで定数が読み込めているか検証した結果。 piが正常に読み込めた
定義した piが読み込めることを確認

tomlで定義した piが読み込めていることを確認。

次に、関数の読み込みができるか検証してみました。

定義した関数の読み込みを拡大した画像

解析できているようです。

ホットリロード機能を実装しているので動くかどうかも検証してみました。tomlを編集し、”eV = 1.602″というのを追記、そのごF5でリロードし読み込めているか検証します。

F5きーでのホットリロードが動いたことを確認
ホットリロードでeVが読み込めていることを確認

ホットリロード成功

さいごに

ここまでに、数式の解析→関数に定義したものの読み込みができたので、次回は、数式全体を構造木といして解析する仕組みを作っていこうと思います。

次回 Parser (構文解析)ASTにする:関連記事は、2026年4月29日に公開予定 (あと7時間)

ここまで読んでいただきありがとうございます。

では、次の記事で。 lumenHero

関連記事

初回 構想編【Rustで作る】ターミナル関数電卓を設計する(アーキテクチャ編) #1
前回 Lexer実装:【Rust】自作計算機エンジンへの道:Lexer(字句解析)で数式をトークンに分解する #2