LoginSignup
2
0

More than 5 years have passed since last update.

Node.js でつくる Node.js - Extra 2: 演算子を追加する

Posted at

はじめに

「RubyでつくるRuby ゼロから学びなおすプログラミング言語入門」(ラムダノート, Amazon) に感銘をうけて、ミニNode.js(以下mininode)を作ってきました。今回はおまけとして、論理演算子などを追加したいと思います。

追加する演算子

次の演算子を追加します。

  • 比較演算子: !==, ==
  • 否定演算子: !
  • 論理演算子: &&, ||

比較演算子はこれまでの2項演算子と同じですが、否定演算子と論理演算子は新しい種類になります。

新しい演算子のAST

否定演算子

こちらのソースをesprimaでパースしてみます。

! 1;

ASTはこちら。UnaryExpression というのが新しく登場しました。

Script {
  type: 'Program',
  body:
   [ ExpressionStatement {
       type: 'ExpressionStatement',
       expression:
        UnaryExpression {
          type: 'UnaryExpression',
          operator: '!',
          argument: Literal { type: 'Literal', value: 1, raw: '1' },
          prefix: true } } ],
  sourceType: 'script' }

単純化した構文木Treeは、こちらの形を目指します。

[ '!', [ 'lit', 1 ] ]

論理演算子

同様にこちらのソースをesprimaでパースしてみます。

1 || 0;
1 && 0;

ASTの該当箇所はこちら。今度は LogicalExpression が新登場です。

   [ ExpressionStatement {
       type: 'ExpressionStatement',
       expression:
        BinaryExpression {
          type: 'LogicalExpression',
          operator: '||',
          left: Literal { type: 'Literal', value: 1, raw: '1' },
          right: Literal { type: 'Literal', value: 0, raw: '0' } } },
     ExpressionStatement {
       type: 'ExpressionStatement',
       expression:
        BinaryExpression {
          type: 'LogicalExpression',
          operator: '&&',
          left: Literal { type: 'Literal', value: 1, raw: '1' },
          right: Literal { type: 'Literal', value: 0, raw: '0' } } } ],

単純化した構文木Treeは、2項演算子と同じ形にしました。

[ 'stmts',
  [ '||', [ 'lit', 1 ], [ 'lit', 0 ] ],
  [ '&&', [ 'lit', 1 ], [ 'lit', 0 ] ] ]

単純化処理の拡張

新しい演算子に対応するため、simplify() を次のように拡張しました。

function simplify(exp) {
  // ... 省略 ...

  // --- Extra 2 ---
  if (exp.type === 'UnaryExpression') {
    return [exp.operator, simplify(exp.argument)];
  }
  if (exp.type === 'LogicalExpression') {
    return [exp.operator, simplify(exp.left), simplify(exp.right)];
  }
  // ... 省略 ...
}

演算子の実行

追加した演算子を実行するため、evaluate()も拡張します。

function evaluate(tree, genv, lenv) {
  // ... 省略 ...

  // ---- Extra2 ----
  if (tree[0] === '!==') {
    return evaluate(tree[1], genv, lenv) !== evaluate(tree[2], genv, lenv);
  }
  if (tree[0] === '==') {
    return evaluate(tree[1], genv, lenv) == evaluate(tree[2], genv, lenv);
  }
  if (tree[0] === '!') {
    let val = ! evaluate(tree[1], genv, lenv);
    return val;
  }
  if (tree[0] === '||' ) {
    return evaluate(tree[1], genv, lenv) || evaluate(tree[2], genv, lenv);
  }
  if (tree[0] === '&&' ) {
    return evaluate(tree[1], genv, lenv) && evaluate(tree[2], genv, lenv);
  }

  // ... 省略 ...
}

シンプルです。

使ってみる

こちらのテストコードを用意しました。

test_extra2.js
let a = true;
println(a); // true
println(! a); // false
println(a || false); // true
println(a && false); //false 

println(a !== (1 > 0)) //false
println(a == 1) //true
println(a === 1) //false

実行結果はこちら。うまく行ったようです。

$ node mininode_extra_2.js sample/test_extra2.js
true
false
true
false
false
true
false

さらに、mininodeでmininodeを実行したブートストラップ状態でも実行できます。

$ node mininode_extra_2.js mininode_extra_2.js sample/test_extra2.js

なんて、本当は初めてやった時にはバグで動きませんでした。正しいファイルをrequre()していなかったのが原因でした。一見意味のないブートストラップも、バグの発見に役立ちます。

ここまでのソース

今回のソースもGitHubにあげておきます。

2
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
2
0