逆数式計算機は、数式の解析、計算を行う機能を持ちます。数式には、四則演算や括弧を含む各種演算子、変数、関数を使用することができます。また、実行する前の状態である命令列を外部ファイルに保存したり、それを読み込んで実行する事ができます(TODO: バージョン0.1.xでは未実装)。
Computerクラスで数式を計算する事ができます。
Computer comp = new Computer(); double value = comp.compute("1 + 2"); // value == 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に渡されるので、それらを計算し、返します。