使い方

数式の解析、計算は、以下の段階を経て行われます。

  1. 構文解析器による、文字列で表された通常の数式から抽象構文木の構築(構文解析)。
  2. コンパイラーによる、抽象構文木から命令列への変換(コンパイル)。
  3. 計算機による、命令列の実行(計算)。

構文解析

構文解析はParserクラスによって行われます。Parserクラスは、文字列を読み込み、構文規則に従って抽象構文木を構築します。

Parser parser = new Parser();
Node Node = parser.parse("1 + 2"); // top level node
                

得られたNodeインスタンスが、抽象構文木の最上位ノードです。全てのノードはNodeクラスで表されます。実際は各構文規則に対応するノードのクラスが有り、それらのインスタンスで構成されています。

抽象構文木は、組替えたり(TODO: 0.1.xでは難しい。簡単にできるようにする)、自分で構築する事ができます。

IntegerLiteralNode value1 = new IntegerLiteralNode("1");
IntegerLiteralNode value2 = new IntegerLiteralNode("2");
AdditiveExpressionNode.OperatorNode addOpe = new AdditiveExpressionNode.AddNode();
AdditiveExpressionNode addExp = new AdditiveExpressionNode(value1, addOpe, value2);

Node expNode = new ExpressionStatementNode(addExp); // top level node
                

構文規則は以下の通りです(TODO: JavaCC構文規則を記載する)。

コンパイル

コンパイルはCompilerクラスによって行われます。Compilerクラスは、Nodeインスタンスを読み込み、命令列へ変換します。

Compiler compiler = new Compiler();
CommandList cl = compiler.compile(node); // command list
                

得られたCommandListインスタンスが、命令列を表します。CommandListインスタンスはCommandインスタンスのコンテナであり、Commandクラスは1つの命令を表します。

命令列は、組替えたり、自分で構築する事ができます(TODO: イミュータブルじゃないし、ちゃんとListを実装する)。

Command[] commands = new Command[3];
commands[0] = new PushStackCommand(1);
commands[1] = new PushStackCommand(2);
commands[2] = new AddCommand();

CommandList cl = new CommandList(commands);
                

計算

計算はComputerクラスによって行われます。Computerクラスは、命令列を読み込み、実行します。

Computer comp = new Computer();
double value = comp.compute(cl);
                

命令列ではなく文字列を読み込ませることで、構文解析、コンパイル、計算を一度にすることができます。

Computer comp = new Computer();
double value = comp.compute("1 + 2");
                

得られたdouble値が計算結果です。計算は、全てdouble型で行われます。

変数

数式中で変数を使用することができます。以下のように使用します。

Computer comp = new Computer();
comp.setVariable("x", 10);
comp.setVariable("y", 0);

double result = comp.compute("y = x * 2 + 1"); // 11
double xValue = comp.getVariable("x"); // 10
double yValue = comp.getVariable("y"); // 11
                

数式中で使用する変数は、計算前に定義する必要があります。変数の定義はsetVariable()メソッドで行います。今回の数式で使用されたxのようにあらかじめ値を設定します。yのように数式中で設定される場合は、適当な値で構いません。変数の値は、計算前でも後でも、getVariable()メソッドで取得することが出来ます。計算後ならば、計算中で代入された値に変更されています。

関数

数式中で関数を呼び出すことが出来ます。以下のように使用します。

class SinFunction implements Function {
    double call(double[] arguments) {
        return Math.sin(arguments[0]);
    }
}
                
Computer comp = new Computer();
comp.addFunction("sin", new SinFunction());
comp.setVariable("PI", Math.PI);

double result = comp.compute("sin(0.5 * PI)"); // about 1
                

数式中で使用する関数は、計算前に定義する必要があります。定義は、Functionインターフェイスを実装し、そのインスタンスをaddFunction()メソッドに指定します。call()メソッドは、数式中で指定された引数がargumentsに渡されるので、それらを計算し、返します。

演算子

以下の演算子をサポートします。

+, -, *, /, %
加算、減算、乗算、除算、剰余算演算子。
>, <, >=, <=, ==, !=
比較演算子。真の場合は1、偽の場合は0を返します。
++, --
前置、後置インクリメント、デクリメント。
&, |, ^
論理積、論理和、排他的論理和。
&&, ||
条件論理積、条件論理和。
<<, >>, >>>
シフト演算子。
?:
条件演算子。
=, +=, -=, *=, /=, %=, &=, |=, ^=, <=, >>=, >>>=
代入演算子。