LoginSignup
1
0

More than 5 years have passed since last update.

11 比較演算子と論理演算子に対応する

Last updated at Posted at 2017-03-24

はじめに

以前の記事でif文に対応しました。
if文の条件文に指定できる演算子がないので対応したいと思います。

比較演算子と論理演算子対応でやりたいこと

簡単にやりたいことを確認します。
例えば下のようなプログラムがあります。
1行目は不等号が正しいので、真をあらわす1を出力します。
2行目は等号付き不等号が間違いなので、偽をあらわす0を出力します。
3行目は論理演算の結果が真になるので、1を出力します。
例では出力するだけですが、if文の条件式として記述できます。

println(10 > 1)
println(5 <= 1)
println(!(1 == 2) && (3 != 4))

実装の仕方

実装の仕方については、構文解析の実装インタプリタの実装で行ったことから、
新しいことがないのでそちらを参照して下さい。

Javaで実装してみる

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

Lexer.java

Lexer.javaの実装です。
追加した演算子に対応しています。
いままでの演算子はすべて1文字でしたが、2文字の演算子も増えたのが、目新しいかもしれません。

Lexer.java
    private boolean isSignStart(char c) {
        return c == '=' || c == '+' || c == '-' || c == '*' || c == '/' || c == '!' || c == '<' || c == '>' || c == '&'
                || c == '|';
    }

    private Token sign() throws Exception {
        Token t = new Token();
        t.kind = "sign";
        char c1 = next();
        char c2 = (char) 0;
        if (!isEOT()) {
            if (c1 == '=' || c1 == '!' || c1 == '<' || c1 == '>') {
                if (c() == '=') {
                    c2 = next();
                }
            } else if (c1 == '&') {
                if (c() == '&') {
                    c2 = next();
                }
            } else if (c1 == '|') {
                if (c() == '|') {
                    c2 = next();
                }
            }
        }
        String v;
        if (c2 == (char) 0) {
            v = Character.toString(c1);
        } else {
            v = Character.toString(c1) + Character.toString(c2);
        }
        t.value = v;
        return t;
    }

Parser.java

Parser.javaの実装です。
トークンの意味に対して、どう動作するかの定義を追加します。
<-- Addのところから、新しい演算子を追加しました。
!が単項演算子なので、<-- Updateところへ追加しています。

Parser.java
    public Parser() {
        degrees = new HashMap<>();
        degrees.put("(", 80);
        degrees.put("*", 60);
        degrees.put("/", 60);
        degrees.put("+", 50);
        degrees.put("-", 50);
        degrees.put("==", 40); // <-- Add
        degrees.put("!=", 40);
        degrees.put("<", 40);
        degrees.put("<=", 40);
        degrees.put(">", 40);
        degrees.put(">=", 40);
        degrees.put("&&", 30);
        degrees.put("||", 30);
        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[] { "+", "-", "!" });                 // <-- Update
        reserved = Arrays.asList(new String[] { "function", "return", "if", "else" });
    }

Interpreter.java

Interpreter.javaの実装です。

条件式が真かを判定するメソッドに便利なので、引数の型違いで多様性をもたせました。

Interpreter.java
    public boolean isTrue(Token token) throws Exception {
        return isTrue(value(expression(token)));
    }

    public boolean isTrue(Integer value) throws Exception {
        return 0 != value;
    }

この言語で真偽値は整数であらわしているので、論理値型を整数へ変換するメソッドを用意しておきます。
真は1へ変換するようにしました。

Interpreter.java
    public Integer toInteger(boolean b) {
        return b ? 1 : 0;
    }

単項演算子を実行するメソッドです。
<-- Addのところへ、論理否定を追加しました。

Interpreter.java
    public Object unaryCalc(Token expr) throws Exception {
        Integer left = value(expression(expr.left));
        if (expr.value.equals("+")) {
            return left;
        } else if (expr.value.equals("-")) {
            return -left;
        } else if (expr.value.equals("!")) { // <-- Add
            return toInteger(!isTrue(left));
        } else {
            throw new Exception("Unknown sign for unary calc");
        }
    }

二項演算子を実行するメソッドです。
<-- Addのところから、新しい演算の実装を追加しています。

Interpreter.java
    public Object calc(Token expr) throws Exception {
        Integer left = value(expression(expr.left));
        Integer right = value(expression(expr.right));
        if (expr.value.equals("+")) {
            return left + right;
        } else if (expr.value.equals("-")) {
            return left - right;
        } else if (expr.value.equals("*")) {
            return left * right;
        } else if (expr.value.equals("/")) {
            return left / right;
        } else if (expr.value.equals("==")) { // <-- Add
            return toInteger(left == right);
        } else if (expr.value.equals("!=")) {
            return toInteger(left != right);
        } else if (expr.value.equals("<")) {
            return toInteger(left < right);
        } else if (expr.value.equals("<=")) {
            return toInteger(left <= right);
        } else if (expr.value.equals(">")) {
            return toInteger(left > right);
        } else if (expr.value.equals(">=")) {
            return toInteger(left >= right);
        } else if (expr.value.equals("&&")) {
            return toInteger(isTrue(left) && isTrue(right));
        } else if (expr.value.equals("||")) {
            return toInteger(isTrue(left) || isTrue(right));
        } else {
            throw new Exception("Unknown sign for Calc");
        }
    }

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

println(10 > 1)
println(5 <= 1)
println(!(1 == 2) && (3 != 4))

を実行し、標準出力へ101と出力します。

Interpreter.java
    public static void main(String[] args) throws Exception {
        String text = "";
        text += "println(10 > 1)";
        text += "println(5 <= 1)";
        text += "println(!(1 == 2) && (3 != 4))";
        List<Token> tokens = new Lexer().init(text).tokenize();
        List<Token> blk = new Parser().init(tokens).block();
        new Interpreter().init(blk).run();
        // --> 1
        // --> 0
        // --> 1
    }

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

おわりに

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

Calc
https://github.com/quwahara/Calc/tree/article-11-compare-r2/Calc/src/main/java

続きの記事があります。

while文に対応する
http://qiita.com/quwahara/items/36f6704ae9c756240068

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0