はじめに
前回は自作の数値計算ライブラリの実行用(フロントエンド)を紹介した。
この数理計算ライブラリには複素数、ベクトル、行列クラスなどがある。
そうなると欲しくなるのが四則演算+,-*,/
などの演算子オーバーロードである。
JavaScriptは演算子オーバーロードはサポートされていないがトランスパイラであるBabelのPluginを使うことで擬似的に作ることができる。
その方法を、備忘録を兼ねて記事にする。
Babel Plugin
Babelは言わずとしれたトランスパイラである。
Babel Pluginはこ のBabelの処理の途中に独自の処理を加えてソースコードを書き換えるものである。
その処理には元JSからAST(抽象構文木)に変換してそれを元に別のJSに変換するのもである。
そのため、この処理をより深く理解するためにはASTの知識が合ったほうがいいがその知識は参考文献にしておく。
白状するとこの記事はほぼこの記事、JavaScriptで演算子オーバーロードしてみる(BabelでAST)、のパクリです。
下準備
複素数クラスやベクトル、行列クラスにはそれぞれ+.-.*./
に対応するadd,sub,mul,div
等のメソッドを定義しておく
更にそれを橋渡しする関数mul(a,b)
等を以下のように定義する。
const mul=(a, b=void 0)=>{
if( typeof(a)==='number' && typeof(b)==='undefined' ) return a;
else if( typeof(a)==='number' && typeof(b)==='number' ) return a*b;
else if( typeof(a)==='object' && typeof(a.mul)==='function' ) return a.mul(b);
else if( typeof(b)==='object' && typeof(b.mul)==='function' ) return b.mul(a);
throw new Error('!!! mul invaild !!! undefined calculation');
}
export mul;
つまりnumberとnumberであればそのまま、一つがオブジェクト(クラス)でありadd
メソッドがあればそれを呼ぶ用にする。
つまり各クラスの掛け算の実体はクラスメソッドに定義しておく。
Pluginの登録
今回の使い方ではブラウザのJavaScript上で動かすのでブラウザ上で動かすので
<script src="node_modules/@babel/standalone/babel.js"></script>
のように読み込んでおく、これをするとBabel
オブジェクトが定義されるのでそれにPluginを登録する。
登録の本体は以下のようにする。
Babel.registerPlugin('operator_overload', ({ types: t })=>{
return {
visitor: {
Statement(parentPath){
parentPath.traverse({
BinaryExpression(path){
const { left, right, operator } = path.node;
let fname;
if( operator==='+' ) fname=t.identifier('add');
else if( operator==='-' ) fname=t.identifier('sub');
else if( operator==='*' ) fname=t.identifier('mul');
else if( operator==='/' ) fname=t.identifier('div');
else return;
path.replaceWith(t.callExpression(fname, [left, right]));
}
});
}
}
}
});
ここで{ types: t }
はBabelの識別子などを生成するコンテキスト(オブジェクト)であり、これを通じてコードなどを生成する。
ここでBynaryExpression
は二項表現でいわゆるオペレーターで2つの変数等をつないだものである。
callExpression
は普通の関数呼び出しで第一引数が関数名であるがこれらはt
から生成されたものでないといけない。
つまり、二項表現でさらに+,-,*,/
の場合だけ
a+b;
a+b+c;
add(a, b);
add(a, add(b, c));
のように書き換えている。
使用例
使うときはこのようにする。
function run(){
const { code }=Babel.transform(original_code, { presets: ['env'], plugins: ['operator_overload'] });
const f=Function(code);
f();
}
最後に
Babel pluginの簡単な例として演算子オーバーロードの例を解説した。
より体系的な解説などはここ(Babel Plugin Handbook)にあると思います