2
0

JavaScriptでなんちゃって演算子オーバーロード---四則演算子

Posted at

はじめに

前回は自作の数値計算ライブラリの実行用(フロントエンド)を紹介した。
この数理計算ライブラリには複素数、ベクトル、行列クラスなどがある。
そうなると欲しくなるのが四則演算+,-*,/などの演算子オーバーロードである。
JavaScriptは演算子オーバーロードはサポートされていないがトランスパイラであるBabelのPluginを使うことで擬似的に作ることができる。
その方法を、備忘録を兼ねて記事にする。

Babel Plugin

Babelは言わずとしれたトランスパイラである。
Babel Pluginはこ のBabelの処理の途中に独自の処理を加えてソースコードを書き換えるものである。
その処理には元JSからAST(抽象構文木)に変換してそれを元に別のJSに変換するのもである。
そのため、この処理をより深く理解するためにはASTの知識が合ったほうがいいがその知識は参考文献にしておく。

白状するとこの記事はほぼこの記事、JavaScriptで演算子オーバーロードしてみる(BabelでAST)、のパクリです。

下準備

複素数クラスやベクトル、行列クラスにはそれぞれ+.-.*./に対応するadd,sub,mul,div等のメソッドを定義しておく
更にそれを橋渡しする関数mul(a,b)等を以下のように定義する。

mul.js
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を登録する。

登録の本体は以下のようにする。

operator_overload.js
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から生成されたものでないといけない。
つまり、二項表現でさらに+,-,*,/の場合だけ

befor.js
a+b;
a+b+c;
after.js
add(a, b);
add(a, add(b, c));

のように書き換えている。

使用例

使うときはこのようにする。

run.js
function run(){
    const { code }=Babel.transform(original_code, { presets: ['env'], plugins: ['operator_overload'] });
    const f=Function(code);
    f();
}

最後に

Babel pluginの簡単な例として演算子オーバーロードの例を解説した。

より体系的な解説などはここ(Babel Plugin Handbook)にあると思います

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0