LoginSignup
0
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-12-10

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

Introduction

みなさんはrakudo-and-nqp-internals-courseを知っていますか?
rakudo-and-nqp-internalsはMoarVMの開発者であるJonathan Worthington氏の作ったnqpとrakudoの内部構造に関するチュートリアルです。
スライドだけじゃなくて、エクササイズ問題も用意されています。
http://edumentab.github.io/rakudo-and-nqp-internals-course/
というわけで、詳しい話はスライドを見てもらうとして、今回のアドベントカレンダーでは私がエクササイズ問題に挑戦していきたいと思います。

ちなみに、3日目ではExercise 1に挑戦しました。
今日の10日目はExercise 2に挑戦します。

Exercise 2

2.1 Grammar

  • Perl 6のirclogログデータをGrammarでパースしてみようという問題です。
  • このログデータは時刻, ニックネーム, テキストの三要素で一つの行が構成され、行と行の間は改行区切りになっています。
!# nqp                                                                                                                                                           

grammar Utterance::Grammar {                                                                                                                                     
  token TOP {                                                                                                                                                    
    <linelist>                                                                                                                                                   
  }                                                                                                                                                              
  token linelist {                                                                                                                                               
    [                                                                                                                                                            
    | <line> \n                                                                                                                                                  
    | \n                                                                                                                                                         
    ]+                                                                                                                                                           
  }                                                                                                                                                              
  token line { <time> <.ws> <nick> <.ws> <text> }                                                                                                                
  token time { \d+ ':' \d+ }                                                                                                                                     
  token nick { \w+ }                                                                                                                                             
  token text { <[\w\h]>+ } # Note: Don't use \s                                                                                                                 
}                                                                                                                                                                

Utterance::Grammar.HOW.trace-on(Utterance::Grammar);  # (#1)                                                                                                           
my $m := Utterance::Grammar.parse(Q{                                                                                                                             
00:00  abc            abc de fa                                                                                                                                  
00:00  abc            abc de fb                                                                                                                                  
});  # (#2)                                                                                                                                                            

say($m<linelist>);                                                                                                                                               
  • 少なくともこの例では、ほとんどGrammarsと文法的な違いはありません。
  • .How.trace-onを利用するとパーサーの内部の動作を閲覧することができるようになります。 (#1)
  • Q{ }を利用することでirclogのフォーマットのテキストを簡単に表現することができます。(#2)

2.2 Actions

  • 行を表現するクラスUtterance::Model (※チュートリアルでは名称をUtteranceと指定してあるが、微妙に名称を変えています)を作って、これのインスタンスの配列でログを表現してみましょうという問題です。
    • Utterance::Modelが持つのは、time, nick, text の3つです。
!# nqp                                                                                                                                                           

grammar Utterance::Grammar {                                                                                                                                     
  token TOP {                                                                                                                                                    
    <linelist>                                                                                                                                                   
  }                                                                                                                                                              
  token linelist {                                                                                                                                               
    [                                                                                                                                                            
    | <line> \n                                                                                                                                                  
    | \n                                                                                                                                                         
    ]+                                                                                                                                                           
  }                                                                                                                                                              
  token line { <time> <.ws> <nick> <.ws> <text> }                                                                                                                
  token time { \d+ ':' \d+ }                                                                                                                                     
  token nick { \w+ }                                                                                                                                             
  token text { <[\w\h]>+ } # Note: Don't use \s                                                                                                                 
}                                                                                                                                                                

class Utterance::Model {                                                                                                                                         
  has $!time;                                                                                                                                                    
  has $!nick;                                                                                                                                                    
  has $!text;                                                                                                                                                    

  method new(:$time!, :$nick!, :$text!) {                                                                                                                        
     my $obj := nqp::create(self);                                                                                                                               
     $obj.BUILD(:$time, :$nick, :$text);                                                                                                                         
     $obj;                                                                                                                                                       
  }                                                                                                                                                              
  method BUILD(:$time!, :$nick!, :$text!) {                                                                                                                      
     $!time := $time;                                                                                                                                            
     $!nick := $nick;                                                                                                                                            
     $!text := $text;                                                                                                                                            
  }                                                                                                                                                              
  method time() { $!time } # (#1)                                                                                                                                      
  method nick() { $!nick }                                                                                                                                       
  method text() { $!text }                                                                                                                                       
}                                                                                                                                                                

class Utterance::Actions {                                                                                                                                       
  method TOP($/) { make $<linelist>.made }                                                                                                                       
  method linelist($/) {                                                                                                                                          
    my @array;                                                                                                                                                   
    for $<line> {                                                                                                                                                
      nqp::push(@array, Utterance::Model.new(:time($_<time>), :nick($_<nick>), :text($_<text>))); # (#2)                                                                
    }                                                                                                                                                            
    make @array;                                                                                                                                                 
  }                                                                                                                                                              
  method line($/) { make { time => $<time>.made, nick => $<nick>.made, text => $<text>.made } }                                                                  
  method time($/) { make ~$<time> }                                                                                                                              
  method nick($/) { make ~$<nick> }                                                                                                                              
  method text($/) { make ~$<text> }                                                                                                                              
}                                                                                                                                                                

#Utterance::Grammar.HOW.trace-on(Utterance::Grammar);                                                                                                            

my $model := Utterance::Model.new(:time("00:00"), :nick("abc"), :text("abc de fa"));                                                                             
is($model.time, "00:00");                                                                                                                                        
is($model.nick, "abc");                                                                                                                                          
is($model.text, "abc de fa");                                                                                                                                    

my $m := Utterance::Grammar.parse(Q{                                                                                                                             
00:00  abc            abc de fa                                                                                                                                  
00:00  abc            abc de fb                                                                                                                                  
}, :actions(Utterance::Actions));                                                                                                                                

my @result := $m.ast;                                                                                                                                            

is(+@result, 2); # (#3)                                                                                                                                           

is(@result[0].time, "00:00");                                                                                                                                    
is(@result[0].nick, "abc");                                                                                                                                      
is(@result[0].text, "abc de fa");                                                                                                                                

is(@result[1].time, "00:00");                                                                                                                                    
is(@result[1].nick, "abc");                                                                                                                                      
is(@result[1].text, "abc de fb");                                                                                                                                              
  • nqpのクラスではゲッターを自動生成する機能 (e.g. $.text)が入っていないので、自分でゲッターを記述します。(e.g. time()) (#1)
  • クラスのインスタンスの配列を生成したいときは、method linelist()のところで、lineをイテレートして、@arrayにインスタンスをpushすることで実現できます (#2)
  • nqpでもis()などを使ったテストを記述することができます (#3)

2.3, 2.4は解いてないです :)
以上、10日目の投稿でした。

ライセンス

rakudo-and-nqp-internals-course is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

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