14 関数式に対応する

# はじめに

以前の記事で[Scopeに対応](http://qiita.com/quwahara/items/d9f932195da1b655b617)しました。
今度は関数式に対応します。
前回の記事で対応したScopeもいかしてクロージャーを作ってみます。

# 関数式対応でやりたいこと

関数式対応でやりたいことを確認します。
例えば下のようなプログラムがあります。
外側の関数式が返す、内側の関数式を変数`counter`へ代入しています。
`counter`を関数として呼び出す度に、
外側の関数式で定義した変数`c`の値が加算されます。
最初の`println(counter())`は`1`が出力され、
次の`println(counter())`は`2`が出力されます。

```JS
counter = (function() {
  var c = 0
  return function() {
    c = c + 1
    return c
  }
})()
println(counter())
println(counter())
```

# 実装の仕方

実装の仕方について、構文解析(Parser)、インタプリタ(Interpreter)と順に考えていきます。

## 構文解析(Parser)の実装の仕方

関数式の構文解析は、関数定義の構文解析と比べると、関数名があるかないかの違いです。
その違いを関数定義の構文解析に反映します。

## インタプリタ(Interpreter)の実装の仕方

関数式を表すオブジェクトを導入します。
そのオブジェクトは値として扱えるようにし、かつ関数呼び出しにも対応します。
いままでは`Integer`のみが値として扱われるものでしたが、
関数式も値として扱えるようにしなければなりません。

# Javaで実装してみる

実装にうつります。
構文解析(Parser)、インタプリタ(Interpreter)について、
変更と追加をしたところを順にみていきます。

## Parser.java

Parser.javaの実装です。

関数定義を解析する`func()`メソッドの変更です。
変更箇所は最初のif文です。
元の実装では`function`トークンの後には、関数名にあたる`ident`トークンがくることを期待していました。
関数式の場合`function`トークンの後は`(`トークンがきます。
if文の判定は、`function`トークンの後が`(`トークンなら関数式の解析、
そうでなければ、関数定義の解析をするように変更しました。

```Java:Parser.java
    private Token func(Token token) throws Exception {
        if (token().value.equals("(")) {
            token.kind = "fexpr";
        } else {
            token.kind = "func";
            token.ident = ident();
        }
        consume("(");
        token.params = new ArrayList<Token>();
        if (!token().value.equals(")")) {
            token.params.add(ident());
            while (!token().value.equals(")")) {
                consume(",");
                token.params.add(ident());
            }
        }
        consume(")");
        token.block = body();
        return token;
    }
```

## Interpreter.java

Interpreter.javaの実装です。

変数を表す`Variable`クラスの変更です。
いままでは値は`Integer`しか扱ってきませんでした。
関数式を導入するので、関数式も値として扱えなければなりません。
扱えるようにするためフィールド変数`value`の型を、
`Object`へ`Integer`から変更しました。

```Java:Interpreter.java
    public static class Variable {
        public String name;
        public Object value;

        @Override
        public String toString() {
            return name + " " + value;
        }
    }
```

`expression()`メソッドの変更です。
式を表すトークンの意味(kind)によって分岐する処理です。
`// Add`の下へ関数式を表すトークン`fexpr`のための分岐を追加しました。

```Java:Interpreter.java
    public Object expression(Token expr) throws Exception {
        if (expr.kind.equals("digit")) {
            return digit(expr);
        } else if (expr.kind.equals("ident")) {
            return ident(expr);
        } else if (expr.kind.equals("func")) {
            return func(expr);
            // Add
        } else if (expr.kind.equals("fexpr")) {
            return fexpr(expr);
        } else if (expr.kind.equals("paren")) {
            return invoke(expr);
        } else if (expr.kind.equals("sign") && expr.value.equals("=")) {
            return assign(expr);
        } else if (expr.kind.equals("unary")) {
            return unaryCalc(expr);
        } else if (expr.kind.equals("sign")) {
            return calc(expr);
        } else {
            throw new Exception("Expression error");
        }
    }
```

上の`expression`メソッドで追加された分岐で呼び出される、`fexpr()`メソッドを追加しました。
関数式を表すオブジェクトを生成します。
関数式がクロージャーで使われることを考えて、関数式の親スコープを保持できるようにするため、
`func.context`には現状の`Interpreter`をクローンして保持します。

```Java:Interpreter.java
    public Object fexpr(Token token) throws Exception {
        List<String> paramCheckList = new ArrayList<String>();
        for (Token p : token.params) {
            String param = p.value;
            if (paramCheckList.contains(param)) {
                throw new Exception("Parameter name was used");
            }
            paramCheckList.add(param);
        }
        DynamicFunc func = new DynamicFunc();
        func.context = new Interpreter();
        func.context.global = global;
        func.context.local = local;
        func.context.body = body;
        func.params = token.params;
        func.block = token.block;
        return func;
    }
```

関数定義を表すインスタンスを生成する`func()`メソッドの変更です。
`fexpr()`メソッドが現状の`Interpreter`をクローンするのに同調して、
こちらも`// Update`の下でクローンするように変更しました。

```Java:Interpreter.java
    public Object func(Token token) throws Exception {
        String name = token.ident.value;
        if (local.functions.containsKey(name)) {
            throw new Exception("Name was used");
        }
        if (local.variables.containsKey(name)) {
            throw new Exception("Name was used");
        }
        List<String> paramCheckList = new ArrayList<String>();
        for (Token p : token.params) {
            String param = p.value;
            if (paramCheckList.contains(param)) {
                throw new Exception("Parameter name was used");
            }
            paramCheckList.add(param);
        }
        DynamicFunc func = new DynamicFunc();
        // Update
        func.context = new Interpreter();
        func.context.global = global;
        func.context.local = local;
        func.context.body = body;
        func.name = name;
        func.params = token.params;
        func.block = token.block;
        local.functions.put(name, func);
        return null;
    }
```

`value()`メソッドの変更と、`integer()`メソッドの追加です。

`value()`メソッドは、引数が`1`のような値そのものだったらその値を返し、
変数だったら、変数が保持する値を返します。
今までは値に`Integer`しか扱ってきませんでした。
対応する関数式も、このメソッドで返す値になるので対応します。
`value()`メソッドの返り値の型を、`Integer`から`Object`へ変更します。
これで`Integer`も関数式も`value()`メソッドで返せます。

`integer()`メソッドを追加した理由は、
`value()`メソッドの返り値を`Integer`から`Object`へ変更したためです。
今までは`value()`メソッドの返り値が`Integer`で保証されていましたが、
`Integer`から`Object`へ変更によって保証されなくなりました。
代わりに`integer()`メソッドで値が`Integer`であることを保証します。

これらの変更に影響して、値が`Integer`であることを保証するために、
`value()`メソッドの呼び出していた、
`unaryCalc()`メソッドや`calc()`メソッドでも、
代わりに`integer()`メソッドを呼び出すように変更しています。

```Java:Interpreter.java
    public Object value(Object value) throws Exception {
        if (value instanceof Integer) {
            return value;
        } else if (value instanceof Func) {
            return value;
        } else if (value instanceof Variable) {
            Variable v = (Variable) value;
            return value(v.value);
        }
        throw new Exception("right value error");
    }

    public Integer integer(Object value) throws Exception {
        if (value instanceof Integer) {
            return (Integer) value;
        } else if (value instanceof Variable) {
            Variable v = (Variable) value;
            return integer(v.value);
        }
        throw new Exception("right value error");
    }
```

`func()`メソッドの変更です。
引数が関数であることを保証するメソッドです。
`else if`のところへ、引数が`Variable`型だった場合、
その値が関数であることを保証する処理を追加しました。

```Java:Interpreter.java
    public Func func(Object value) throws Exception {
        if (value instanceof Func) {
            return (Func) value;
        } else if (value instanceof Variable) {
            Variable v = (Variable) value;
            return func(v.value);
        } else {
            throw new Exception("Not a function");
        }
    }
```

`isTrue`メソッドの変更です。
`isTrue`メソッドは値を真偽値へ解決する処理です。
いままでは値は`Integer`だけでしたが、関数も値として扱うようになったので、
値が関数だったときは、単純に`true`へ解決するようにしました。

```Java:Interpreter.java
    public boolean isTrue(Object value) throws Exception {
        if (value instanceof Integer) {
            return 0 != ((Integer) value);
        } else if (value instanceof Func) {
            return true;
        } else {
            return false;
        }
    }
```

以上の実装を使って下のプログラム

```JS
counter = (function() {
  var c = 0
  return function() {
    c = c + 1
    return c
  }
})()
println(counter())
println(counter())
```

を実行し、
最初の`println(counter())`は`1`が出力され、
次の`println(counter())`は`2`が出力されます。

```Java:Interpreter.java
    public static void main(String[] args) throws Exception {
        String text = "";
        text += "counter = (function() {";
        text += "  var c = 0";
        text += "  return function() {";
        text += "    c = c + 1";
        text += "    return c";
        text += "  }";
        text += "})()";
        text += "println(counter())";
        text += "println(counter())";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> 1
        // --> 2
    }
```

実装は以上です。
ありがとうございました。

# おわりに

ソースの全文はこちらで公開しています。

**Calc**
https://github.com/quwahara/Calc/tree/article-14-function-expression-r3/Calc/src/main/java

続きの記事があります。

**文字列に対応する**
http://qiita.com/quwahara/items/ddcbc8c37b9d442fe0f2