Mery 電卓
概要
「Mery 電卓」は、Meryエディタ上で数式を計算するためのマクロです。標準的な数学関数をサポートしており、カーソル位置の行または選択したテキストを数式として解釈し、結果を表示します。計算結果は自動的にクリップボードにコピーされ、カンマ区切りの数値として表示されます。
本マクロをショートカットキーに設定し、いつでも起動できるようにしておくと日常シーンで何かと便利です。作者は「Ctrl + Alt + Enter」キーに割り当てて利用しています。
注意事項
- Mery Ver 3.7.19 で動作確認しています。
使い方
0. 事前準備
- 本マクロでは計算エンジンライブラリの「math.js」をインポートして利用しています。当該ライブラリをマクロフォルダーに配置します。
- ダウンロード先: https://unpkg.com/mathjs@15.0.0/lib/browser/math.js
- 保存ファイル名: math_v15.0.0.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 など)
- その他の利用可能な関数は math.js の公式リファレンス https://mathjs.org/docs/reference/functions.html を参照
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桁)
})
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), { lowerExp: -10, upperExp: 20 });
} catch (e) {
if (exp_replaced === undefined) {
alert("エラー発生" + "\n------\n" + e + e.message)
} 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 + ' = ' + result_str + '\n';
} else {
sel.Text = '\n' + inputExp + ' = ' + result_str + '\n';
}
sel.CharLeft();
}
if (COPY) {
ClipboardData.SetData(result_str);
Status = "計算結果 " + result_str + " をコピーしました"
}
}
/**
* 数式を置換する
*/
function replaceExpression(exp) {
const exp_replaced = exp.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%%% ")
// `/hour` などの文字列はスラッシュごと削除
.replace(/\/\s*[a-zA-Z\u0080-\uFFFF]/g, " ")
// 空の括弧を削除
.replace(/\(\s*\)|\[\s*\]|\{\s*\}/g, " ")
// コメント扱いの記載を削除
.replace(/`[^`]*`/g, " ")
// 関数形式になっていない文字列を削除
.replace(/( [a-zA-Z][a-zA-Z0-9]*)(%%% |\()?/g, (match, p1, p2) => {
return p2 ? p1 + p2 : " ";
})
// 退避文字を復元
.replace(/ %%%\$([a-zA-Z][a-zA-Z0-9]*)%%% /g, " $1 ")
.replace(/ %%%([^%]+)%%% /g, " $1 ")
return exp_replaced;
}
サードパーティライブラリ
スポンサーリンク