Mery 電卓

提供: MeryWiki
2025年10月25日 (土) 14:26時点におけるYuko (トーク | 投稿記録)による版
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
ナビゲーションに移動 検索に移動

概要

「Mery 電卓」は、Meryエディタ上で数式を計算するためのマクロです。標準的な数学関数をサポートしており、カーソル位置の行または選択したテキストを数式として解釈し、結果を表示します。計算結果は自動的にクリップボードにコピーされ、カンマ区切りの数値として表示されます。

本マクロをショートカットキーに設定し、いつでも起動できるようにしておくと日常シーンで何かと便利です。作者は「Ctrl + Alt + Enter」キーに割り当てて利用しています。

注意事項

  • Mery Ver 3.7.19 で動作確認しています。

使い方

0. 事前準備

  • 本マクロでは計算エンジンライブラリの「math.js」をインポートして利用しています。当該ライブラリをマクロフォルダーに配置します。

1. 行全体を計算する場合:

  • 計算したい数式が書かれた行にカーソルを置きます。
  • マクロを実行すると、次の行に「= 計算結果」が表示されます。
  • 既に結果行がある場合は、その行が更新されます。

2. 選択範囲を計算する場合:

  • 計算したい数式部分を選択します。
  • マクロを実行すると、選択範囲の後ろに「計算式 = 計算結果」が挿入されます。

3. サポートされている演算子:

  • 四則演算(+, -, *, /)
  • べき乗(^)
  • 剰余(%, mod)
  • 比較演算子(<, >, <=, >=, ==, !=)
  • 論理演算子(and, or, not)

4. サポートされている関数:

  • 三角関数(sin, cos, tan, asin, acos, atan)
  • 双曲線関数(sinh, cosh, tanh, asinh, acosh, atanh)
  • 対数関数(log, log10, log2, log1p)
  • その他の数学関数(sqrt, cbrt, abs, ceil, floor, round, trunc など)

5. 特別な機能:

  • カンマ区切り表示: 計算結果は読みやすいようにカンマ区切りで表示されます。変数「COMMA」を調整することで制御できます。
  • 自動コピー: 計算結果は自動的にクリップボードにコピーされます。変数「COPY」を調整することで制御できます。
  • 計算結果の再利用: 行頭の「= 」は計算実行時に無視されるため、計算結果をスムーズに次の式として利用できます。
  • 変数の利用: 複数行、または「;」で区切ることで、変数を使った数式が利用できます。変数形式は「$[a-zA-Z][a-zA-Z0-9]*」に対応しています。例えば「$a = 2; $a * 3」として実行すると、結果は「6」と出力されます。
  • メモ書きから計算: 数字や関数以外の文字はできるだけ除外した上で計算をします。例えば「月100円 * 12ヶ月」のようなメモ書きに対して実行すると「1,200」と出力されます。また「` (バッククォート)」で囲んだ文字は確実に無視されるため、計算メモ用として使うことができます (使用例: 『1500円 `金額未定のため仮置き`』)。

ソースコード

#title = "Mery 電卓"
#language = "v8"
#include "Common.js"
#include "math_v15.0.0.js"
Redraw = false;
BeginUndoGroup();

// 以下のようなCDNから math.js 本体を入手し、マクロフォルダに配置して利用している。
// https://unpkg.com/mathjs@15.0.0/lib/browser/math.js
// 以下の math.js 関数リファレンス掲載の関数を式内で利用することができる。
// https://mathjs.org/docs/reference/functions.html

// 結果をクリップボードにコピー
var COPY = true;
// 出力結果をカンマ区切りにする
var COMMA = true;

var sel = document.selection;

// math.js 初期設定
math.config({
    number: 'BigNumber',  // デフォルトの数値型を BigNumber に設定
    precision: 64         // 有効桁数 (デフォルトは64桁)
})
const lowerAndUpperExp = { lowerExp: -7, upperExp: 15 };

doMultiEdit(main);

function main() {
    var isEmpty = sel.IsEmpty;
    var activePointY = sel.GetActivePointY(mePosLogical);
    if (isEmpty) {
        var exp = document.GetLine(activePointY);
    } else {
        var exp = sel.Text;
    }
    // 空白行の場合は何もしない
    if (exp.match(/^\s*$/)) {
        return;
    }


    // 計算実行
    try {
        var exp_replaced = replaceExpression(exp);
        var result_str = math.format(math.evaluate(exp_replaced), lowerAndUpperExp);
    } catch (e) {
        if (exp_replaced === undefined) {
            alert("エラー発生" + "\n------\n" + e)
        } else {
            alert("整形後の数式で計算エラーが発生" + "\n------\n" + e + "\n元の数式: " + exp + "\n整形後の数式: " + exp_replaced)
        }
        return;
    }

    // 小数点以上のみカンマ区切り
    if (COMMA && result_str.match(/^[0-9]+(\.[0-9]+)?$/)) {
        var result_parts = result_str.split(".");
        result_parts[0] = result_parts[0].replace(/(\d)(?=(\d{3})+$)/g, "$1,");
        if (result_parts.length > 1) {
            result_str = result_parts.join(".");
        } else {
            result_str = result_parts[0];
        }
    }

    // 計算行が = 始まりの場合、= を削除する
    if (isEmpty && exp.match(/^= /)) {
        sel.SetActivePoint(mePosLogical, 1, activePointY, false);
        sel.EndOfLine(true, mePosLogical);
        sel.Text = sel.Text.replace(/^= /, "");
    }

    // 既存の計算結果を上書きするか判定
    var pattern = /^=\s*(-?[0-9.,]|true|false|\[[0-9., \[\]]+\])*\s*$/;
    var nextLine = document.GetLine(activePointY + 1);
    if (isEmpty && nextLine.match(pattern)) {
        sel.SetActivePoint(mePosLogical, 1, activePointY + 1, false);
        sel.EndOfLine(true, mePosLogical);
        sel.Text = '= ' + result_str
    } else if (isEmpty) {
        sel.LineOpen();
        sel.Text = '= ' + result_str
    } else {
        sel.CharRight();
        const inputExp = exp.replace(/((?!;)\n)/mg, "; ").replace(/;\s*;/g, ";").replace(/ /g, " ").replace(/;\s*$/, "").trim();
        if (exp.slice(-1) === "\n") {
            sel.Text = inputExp + '\n= ' + result_str + '\n';
        } else {
            sel.Text = '\n' + inputExp + '\n= ' + result_str + '\n';
        }
        sel.CharLeft();
    }

    if (COPY) {
        ClipboardData.SetData(result_str);
        Status = "計算結果 " + result_str + " をコピーしました"
    }
}

/**
 * 数式を置換する
 */
function replaceExpression(exp) {
    // コメント扱いの記載を削除
    let exp_replaced = exp.replace(/`[^`]*`/g, " ")

    // 他の言語で対応する演算子などはあらかじめエラーにする
    const invalidOperators = ["**", "//"];
    invalidOperators.forEach(operator => {
        if (exp.includes(operator)) {
            throw new Error(`「${operator}」はサポートされていません`);
        }
    });

    exp_replaced = exp_replaced.trim()
        // 先頭の = を削除
        .replace(/^= /, "")
        // 改行を ; に置換
        .replace(/(?<!;)\n/mg, ";")
        // 非ASCII文字を削除
        .replace(/[^\x20-\x7e]/g, " ")
        // 全角スペースを半角スペースに変換
        .replace(/ /g, " ")
        // 数字内のカンマを取り除く。[1,2] [1, 2] {a: 1, b: 2} mod(1, 2) のような形式では削除しない
        .replace(/(\d),(?=\d{3})/g, '$1')
        // 予約語を退避
        .replace(/(?:^|\s)(PI|true|false)(?:$|\s)/g, " %%%$1%%% ")
        .replace(/([0-9]+|\$[a-zA-Z0-9]+) (mod|and|or|xor) ([0-9]+|\$[a-zA-Z0-9]+)/g, "$1 %%%$2%%% $3")
        .replace(/not ([0-9]+|\$[a-zA-Z0-9]+)/g, " %%%$1%%% $2")
        // 指数形式を退避
        .replace(/([0-9]+(?:\.[0-9]+)?[eE][+\-]?[0-9]+)/g, " %%%$1%%% ")
        // 変数形式を退避してからドル記号削除
        .replace(/(\$[a-zA-Z][a-zA-Z0-9]*)/g, " %%%$1%%% ")
        .replace(/\$/g, "")
        // 空の括弧を削除
        .replace(/\(\s*\)|\[\s*\]|\{\s*\}/g, " ")
        // 関数形式になっていない文字列を削除
        .replace(/([ ,+\-*\/%^&~|<>?])([a-zA-Z][a-zA-Z0-9]*)(%%% |\()?/g, (match, p1, p2, p3) => {
            return p3 ? p1 + p2 + p3 : p1 + " ";
        })
        // `/hour` などの文字列があるとスラッシュだけ残るためスラッシュを削除
        .replace(/\/\s*([,+\-*\/%^&~|<>?/)\];\n]|$)/g, "$1")
        // 退避文字を復元
        .replace(/ %%%\$([a-zA-Z][a-zA-Z0-9]*)%%% /g, " $1 ")
        .replace(/ %%%([^%]+)%%% /g, " $1 ")

    return exp_replaced;
}

サードパーティライブラリ

math.js

更新履歴

  • 2025-10-19 公開
  • 2025-10-25 対象テキストを数式に整形する処理を調整
スポンサーリンク