9 戻り値に対応する

# はじめに

以前の記事で[複数の引数に対応](http://qiita.com/quwahara/items/0bea3bad4eedd2d803cf)しました。
続けて関数の戻り値に対応したいと思います。

# 関数の戻り値対応でやりたいこと

簡単にやりたいことを確認します。
例えば下のようなプログラムがあったら、`add3()`関数呼び出しの戻り値が返されて変数`v`に代入されます。
標準出力には変数`v`の値`6`が出力されることを目指します。

```JS
function add3(a1, a2, a3) {
  return a1 + a2 + a3
}
v = add3(1,2,3)
println(v)
```

変数のスコープに対応しないのは、以前の記事のままです。

# 実装の仕方

実装の仕方について、構文解析(Parser)、インタプリタ(Interpreter)と順に考えていきます。
字句解析(Lexer)はとくに変更しません。

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

`return`の構文解析を実装するにあたり、`return`の構文に注意する必要があります。
`return`の構文は`return 1`のように、戻り値が指定される場合もあれば、
`return`だけ指定され戻り値が指定されない場合もあります。
`return`だけの指定が構文として正しいのなら、
`return 1`と書かれた場合に、
それは戻り値を指定された場合の構文ともとれるし、
`1`が`return`とは独立した構文であると考えて、
戻り値が指定されない場合の構文ともとれます。
このどっちともとれる状況を簡単に解決するために、手抜きな方法をとります。
その方法は`return`トークンのあとに`}`トークンがきたら、
戻り値が指定されない場合の`return`とみなします。
逆に`}`トークンがこないなら、戻り値が指定される場合の`return`とみなします。
戻り値が指定されない場合の解析は変数名と同じように扱い、
戻り値が指定される場合の解析は、`- 1`のような単項演算子と同じように扱います。

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

インタプリタでは式を逐次実行するメソッド`body()`へ変更を加えます。
`body()`の中で`return`トークンにであったら、
逐次実行を中断して呼び出し元へ戻るようにします。
また`return`は関数内でしか呼び出しが許されません。
関数内での`return`呼び出しかの判定も行います。

# Javaで実装してみる

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

## Parser.java

Parser.javaの実装です。
トークンの意味に対して、どう動作するかの定義を追加します。
`return`は予約語となるので、
`<-- Update`とあるところに`return`を追加しました。

```Java:Parser.java
    public Parser() {
        degrees = new HashMap<>();
        degrees.put("(", 80);
        degrees.put("*", 60);
        degrees.put("/", 60);
        degrees.put("+", 50);
        degrees.put("-", 50);
        degrees.put("=", 10);
        factorKinds = Arrays.asList(new String[] { "digit", "ident" });
        binaryKinds = Arrays.asList(new String[] { "sign" });
        rightAssocs = Arrays.asList(new String[] { "=" });
        unaryOperators = Arrays.asList(new String[] { "+", "-" });
        reserved = Arrays.asList(new String[] { "function", "return" });    // <-- Update
    }
```

解析を行う部分の変更です。
`return`の解析を行うif文を`<-- Add`がある箇所へ加えました。
`return`トークンの次のトークンが、閉じの波カッコの種類を表す`eob`でなければ、
値を返す`return`と判断し、戻り値になるトークンを`left`フィールドへ保持します。

```Java:Parser.java
    private Token lead(Token token) throws Exception {
        if (token.kind.equals("ident") && token.value.equals("function")) {
            return func(token);
        } else if (token.kind.equals("ident") && token.value.equals("return")) {    // <-- Add
            token.kind = "ret";
            if (!token().kind.equals("eob")) {
                token.left = expression(0);
            }
            return token;
        } else if (factorKinds.contains(token.kind)) {
            return token;
        } else if (unaryOperators.contains(token.value)) {
            token.kind = "unary";
            token.left = expression(70);
            return token;
        } else if (token.kind.equals("paren") && token.value.equals("(")) {
            Token expr = expression(0);
            consume(")");
            return expr;
        } else {
            throw new Exception("The token cannot place there.");
        }
    }
```

## Interpreter.java

Interpreter.javaの実装です。

式を逐次実行するメソッド`body()`への変更です。

`body()`は戻り値を返さない`void`型でしたが、戻り値を返せるように`Object`型に変更しました。
`body()`のシグニチャーに`boolean[] ret`を加えました。
`ret`の役割りは2つです。
１つめは`ret`が`null`でないなら、`return`が可能な状況であることを表します。
２つめは`return`にであったら、呼び出し元にも`return`にであったことを伝播します。
`ret`を`boolean`の配列型にしたのは、呼び出し元に値を返すためです。
本来の配列を使う目的ではない、邪道な使い方をしています。
C#の`ref`キーワードをつけた引数のような働きを、簡単にさせたくてそうしました。

`for`の中で最初に、逐次実行するトークンが`return`かを判定します。
`return`でないなら、いままでの`body()`と同じ処理です。
`return`なら`return`可能な状況か判定します。
`ret`へ`return`にであったことを呼び出し元に伝えるために`true`を代入します。
戻り値がある`return`の場合は、戻り値あたるトークンを`expression()`で実行します。

```Java:Interpreter.java
    public Object body(List<Token> body, boolean[] ret) throws Exception {
        for (Token exprs : body) {
            if (exprs.kind.equals("ret")) {
                if (ret == null) {
                    throw new Exception("Can not return");
                }
                ret[0] = true;
                if (exprs.left == null) {
                    return null;
                } else {
                    return expression(exprs.left);
                }
            } else {
                expression(exprs);
            }
        }
        return null;
    }
```

`DynamicFunc`クラスの`invoke()`メソッドの変更です。
`<-- Update`が変更箇所です。
`body()`メソッドのシグニチャーと戻り値の変更に対応しています。
引数`ret`にインスタンスを代入して、`return`できる状況であることを表しています。
`body()`の戻り値をそのまま`invoke()`の戻り値にします。

```Java:Interpreter.java
    public static class DynamicFunc extends Func {

        public Interpreter context;
        public List<Token> params;
        public List<Token> block;

        @Override
        public Object invoke(List<Object> args) throws Exception {
            for (int i = 0; i < params.size(); ++i) {
                Token param = params.get(i);
                Variable v = context.variable(context.ident(param));
                if (i < args.size()) {
                    v.value = context.value(args.get(i));
                } else {
                    v.value = null;
                }
            }
            boolean[] ret = new boolean[1]; // <-- Update
            return context.body(block, ret); // <-- Update
        }
    }
```

`run()`メソッドの変更です。
`<-- Update`が変更箇所です。
`body()`メソッドのシグニチャーと戻り値の変更に対応しています。
`run()`が呼び出されている状況は、関数の中ではありません。
その状況は`return`できない状況なので仮引数`ret`は`null`を指定します。

```Java:Interpreter.java
    public Map<String, Variable> run() throws Exception {
        body(body, null); // <-- Update
        return variables;
    }
```

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

```JS
function add3(a1, a2, a3) {
  return a1 + a2 + a3
}
v = add3(1,2,3)
println(v)
```

を実行し、変数`v`へ代入される値`6`を標準出力へプリントします。

```Java:Interpreter.java
    public static void main(String[] args) throws Exception {
        String text = "";
        text += "function add3(a1, a2, a3) {";
        text += "  return a1 + a2 + a3";
        text += "}";
        text += "v = add3(1,2,3)";
        text += "println(v)";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> 6
    }
```

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

# おわりに

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

**Calc**
https://github.com/quwahara/Calc/tree/article-9-return-r2/Calc/src/main/java

続きの記事があります。

**if文に対応する**
http://qiita.com/quwahara/items/96a68cdee4f2a0452836