数式の解析、計算は、以下の段階を経て行われます。
構文解析は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に渡されるので、それらを計算し、返します。