rakudo-and-nqp-internals-courseに挑戦した話 その4

こんにちは、24日目の投稿になります。

# Introduction

みなさんは[rakudo-and-nqp-internals-course](https://github.com/edumentab/rakudo-and-nqp-internals-course)を知っていますか?
rakudo-and-nqp-internalsはMoarVMの開発者である[Jonathan Worthington氏](http://www.jnthn.net)の作ったnqpとrakudoの内部構造に関するチュートリアルです。

スライドだけじゃなくて、エクササイズ問題も用意されています。
http://edumentab.github.io/rakudo-and-nqp-internals-course/

というわけで、詳しい話はスライドを見てもらうとして、今回のアドベントカレンダーでは私がエクササイズ問題に挑戦していきたいと思います。

24日目はExercise 4に挑戦します。

# 4.1

+ PHPっぽいecho関数を実装してみましょうという問題です

ソースコード:

```
use NQPHLL; # (#1)

grammar NQP::Grammar is HLL::Grammar { # (#2)
    token TOP { <statementlist> }
    rule statementlist { [ <statement> \n+ ]+ }

    proto token statement {*}
    token statement:sym<echo> {
        <sym> <.ws> <?["]> <quote_EXPR: ':q'> ';'? # (#6)
    }
    token ws { <!ww> \h* || \h+ }
}

class NQP::Actions is HLL::Actions { # (#3)
    method TOP($/) {
         make QAST::Block.new( $<statementlist>.ast ); # QAST::Blockについてはスライドp133参照　
    }
    method statementlist($/) {
         my $stmts := QAST::Stmts.new( :node($/) ); # QAST::Stmtsについてはスライドp142参照
         for $<statement> {
             $stmts.push($_.ast)
         }
         make $stmts;
    }
    method statement:sym<echo>($/) { # (#6)
         make QAST::Op.new( # QAST::Opについてはスライドp136参照
              :op('say'),
              $<quote_EXPR>.ast
         );
    }
}

class NQP::Compiler is HLL::Compiler { # (#4)
}

my $nqpcomp := NQP::Compiler.new();
$nqpcomp.language('nqp');
$nqpcomp.parsegrammar(NQP::Grammar);
$nqpcomp.parseactions(NQP::Actions);

sub MAIN(*@ARGS) {
    $nqpcomp.command_line(@ARGS, :encoding('utf8')); # (#5)
}

```

+ NQPHLLをuseします (#1)
+ [HLL::Grammar](https://github.com/perl6/nqp/blob/master/src/HLL/Grammar.nqp) (#2), [HLL::Actions](https://github.com/perl6/nqp/blob/master/src/HLL/Actions.nqp) (#3), HLL::Compiler (#4) を継承したクラスをそれぞれ作ります
+ Rubyishの例(スライドp84)を参考にしてMAINを記述します。この関数の中で`.command_line`を呼び出します (#5)
+ `echo`関数を書きましょう。これもRubyishの`puts`(スライドp123)とほぼ一緒です。 (#6)

## 実行

中の挙動がわかるように`--target=parse`を指定して実行してみましょう:

```
$ nqp 4.1.nqp --target=parse
> echo "hello";
- statementlist: echo "hello";

  - statement: 1 matches
    - sym: echo
    - quote_EXPR: "hello"
      - quote_delimited: "hello"
        - stopper: "
        - starter: "
        - quote_atom: 1 matches
          - quote_atom:  isa NQPArray
```

今度はオプションを指定しないで実行してみましょう:

```
$ nqp 4.1.nqp
> echo "hello";
hello
```

できました。

# 4.2

+ 下記のような問題です
  + 4.1で書いてきたコードについて、phpの`echo`は自動的に改行を入れたりしないので、`say`じゃなくて`print`で書くようにしましょう
  + また`echo("with parentheses");`のように`()`つきでも出力できるようにしましょう

ソースコード:

```
use NQPHLL;

grammar NQP::Grammar is HLL::Grammar {
    token TOP { <statementlist> }
    rule statementlist { [ <statement> \n+ ]+ }

    proto token statement {*}
    token statement:sym<echo> {
        <sym> <.ws> [
        | <?["]> <quote_EXPR: ':q', ':b'> # (#1)
        | '(' <?["]> <quote_EXPR: ':q', ':b'> ')' # (#2)
        ] ';'?

    }
    token ws { <!ww> \h* || \h+ }
}

class NQP::Actions is HLL::Actions {
    method TOP($/) {
         make QAST::Block.new( $<statementlist>.ast ); # QAST::Blockについてはスライドp133参照
    }
    method statementlist($/) {
         my $stmts := QAST::Stmts.new( :node($/) );　# QAST::Stmtsについてはスライドp142参照
         for $<statement> {
             $stmts.push($_.ast)
         }
         make $stmts;
    }
    method statement:sym<echo>($/) {
         make QAST::Op.new( # QAST::Opについてはスライドp136参照
              :op('print'),
              $<quote_EXPR>.ast
         );
    }
}

class NQP::Compiler is HLL::Compiler {
}

my $nqpcomp := NQP::Compiler.new();
$nqpcomp.language('nqp');
$nqpcomp.parsegrammar(NQP::Grammar);
$nqpcomp.parseactions(NQP::Actions);

sub MAIN(*@ARGS) {
    $nqpcomp.command_line(@ARGS, :encoding('utf8'));
}

```

+ バックスラッシュのあるシーケンスをパースできるようにするために`:b`をquote_EXPRの引数に追加で指定します (#1)
+ `echo("hello");`のように`()`で囲んでも出力ができるようにシーケンスを追加します (#2)

## 実行

```
$ nqp 4.2.nqp
> echo ("hello\nI'm larray");
hello
I'm larray> 
```

よさそうです。

以上、24日目の投稿でした。

# ライセンス
rakudo-and-nqp-internals-course is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.