こんにちは、24日目の投稿になります。
Introduction
みなさんはrakudo-and-nqp-internals-courseを知っていますか?
rakudo-and-nqp-internalsはMoarVMの開発者であるJonathan Worthington氏の作った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 (#2), HLL::Actions (#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");
のように()
つきでも出力できるようにしましょう
- 4.1で書いてきたコードについて、phpの
ソースコード:
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.