2020/06/02

2020/06/02

# 今週の進捗

  • OS研究会
    • marpのslideテーマ作りました
  • LLVMちょっとやったりしていた
  • Gearsのデバッグ
    • ビルド関連のコマンドを作りました
      • 雛形生成とかもしたい
    • mercurialのラッパーコマンドも作成している
  • Perl5.31.10で入った連鎖比較(chained comparisons)の実装を見ていた
  • Perlの雑誌の印刷所入稿が今日終わりました

# おしらせ

# OS研究会

  • zoomなのでいつもよりは緊張しなかった
    • しなかったわけではないですが…
  • こころなし普段より疲れた気がする
  • return less kernelというものがあるらしい
    • 攻撃から守るために特定の命令を検知して書き換える手法
    • 論文読みます…

# marp

  • node.js製のスライドジェネレーター
    • 以前electron製のものがり、それのリストア
    • slideshowよりは生成するスライドが良心的
  • CLIベースのものとvscodeベースのものがある
    • CLIベースのものはバイナリが配布されている
  • 研究室のロゴを右下にあてただけのテーマとmakefile等を作った
    • cssあまりかけないのでかなり雑な作りですが…

# Gears

  • 以前のGearsではexample以下のディレクトリで生成したインターフェイスは無視されていた
1
2
3
4
5
6
7
8
} elsif(/^#interface "(.*)"/) {
    # use interface
    my $interfaceHeader = $1;
    next if ($interfaceHeader =~ /context.h/);
    if (-f $interfaceHeader) {
        &getDataGear("$interfaceHeader");
        &getCodeGear("$interfaceHeader");
    }
  • perlの if (-f)はファイルがあればtrueをかえす

  • generate_stubを実行しているディレクトリから見えないと無視される

  • 今はビルドしている.cbcと同じディレクトリにあるものは含めるようにした

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
} elsif(/^#interface "(.*)"/) {
    # use interface
    my $interfaceHeader = $1;
    next if ($interfaceHeader =~ /context.h/);
    if (-f $interfaceHeader) {
        &getDataGear("$interfaceHeader");
        &getCodeGear("$interfaceHeader");
    } else {
      if ($filename =~ /([\w\/]+)\/(.+)$/) {
        $interfaceHeader = "$1/$interfaceHeader";
        if (-f $interfaceHeader) {
            &getDataGear("$interfaceHeader");
            &getCodeGear("$interfaceHeader");
        }
      }
    }
  • とりあえずはmonkeyをCbCに移植したpiposaruを動かすのを目的
    • par gotoがどう変換してくれるかがいまいちつかめず…
    • interface中の引数で独自定義したenumとかを使うとcontext.hにincludeされない問題
      • 生成Perlスクリプトで頑張る

# Gears関連のコマンド

  • いちいちビルドした.cとcbcを確認する際に行ったり来たりするのがめんどう

    • araganeコマンドをつくった
  • aragane cbc2c main.cbcでビルドしたcのパスを表示する

  • aragane cbc2c editor main.cbcで.cをエディタで開く

  • なんとなくgolangのcobraをつかっている

    • なぜかzsh補完も勝手に作ってくれるので便利…
  • 今の所Gearsにしか対応していないので、xv6とかにも対応させたい

    • make一発でrmとcmake再実行までしてくれる的な

# hg

  • cloneするのがめんどう
    • forkするのもめんどうなのでコマンド作りたいみたいな…

# 比較連鎖

Perl5.32で取り入れられる(厳密には5.31からですが)のおもしろ機能として連鎖比較(Chained comparisons)が存在します。

今までPerlで数値などが特定の範囲に含まれているかどうかをif文で判定するには次の様に書く必要がありました。

1
if (10 < $n && $n <= 20)

これがこう書ける様になります!!!

1
if ( 10 < $n <= 20 ) {...}

便利!!!!!!!!!!!!!!!!!!!!!!!!

………..というわけで、この便利な連鎖比較がどの様に実装されているかを探検してみます。

# リポジトリから探す

まずはPerl5のgitリポジトリを雑にcommit logを検索します。 とりあえずはchained comparisonsあたりで検索してみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ git log --grep='chained comparisons'
commit 88c28b3819570e2ea7914875b25f0f40654c570d
Author: Dan Book <grinnz@grinnz.com>
Date:   Tue Apr 28 15:18:18 2020 -0400

    perlop - Add more examples for chained comparisons

    The distinction between how many times an expression may be evaluated and how many times its result may be fetched is somewhat confusing. This calls out the

commit e8a86671097b355fe5e0d9da2473a926929d87c4
Author: Zefram <zefram@fysh.org>
Date:   Fri Feb 7 09:30:21 2020 +0000

    pod/perlop.pod: expand doc on chained comparisons

commit 02b85d3dab092d678cfc958a2dc252405333ed25
Author: Zefram <zefram@fysh.org>
Date:   Wed Feb 5 07:43:14 2020 +0000

    chained comparisons

この内前2つはそれぞれperlopのpodの更新のコミットであり、 連鎖比較の実装そのものの変更ではありませんでした。

1
2
3
4
5
commit 02b85d3dab092d678cfc958a2dc252405333ed25
Author: Zefram <zefram@fysh.org>
Date:   Wed Feb 5 07:43:14 2020 +0000

    chained comparisons

このコミットで連鎖比較の実装が行われています。

# 連鎖比較の実装のコミットを探る

というわけで上の02b85d3dab092d678cfc958a2dc252405333ed25の変更を見ていきます。

webから見たい方はGitHubから見れます

# 変更があったファイル一覧

$ git log -p 02b85d3dab092d678cfc958a2dc252405333ed25 --statとかで表示出来るらしいので見てみます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ git log -p 02b85d3dab092d678cfc958a2dc252405333ed25  --stat
commit 02b85d3dab092d678cfc958a2dc252405333ed25
Author: Zefram <zefram@fysh.org>
Date:   Wed Feb 5 07:43:14 2020 +0000

    chained comparisons
---
 MANIFEST             |    1 +
 embed.fnc            |    4 +
 embed.h              |    3 +
 ext/Opcode/Opcode.pm |    4 +-
 lib/B/Deparse.pm     |   58 +++++
 lib/B/Deparse.t      |   27 ++
 lib/B/Op_private.pm  |    2 +
 op.c                 |  115 +++++++++
 opcode.h             |   16 +-
 opnames.h            |    4 +-
 perly.act            |  772 ++++++++++++++++++++++++++++++--------------------------
 perly.h              |   88 +++----
 perly.tab            | 1790 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------
 perly.y              |   47 +++-
 pod/perlop.pod       |   41 ++-
 pp.c                 |   24 ++
 pp_proto.h           |    2 +
 proto.h              |   14 ++
 regen/opcodes        |    3 +
 t/op/cmpchain.t      |  170 +++++++++++++
 toke.c               |   50 ++--
 21 files changed, 1930 insertions(+), 1305 deletions(-)

結構いろいろなファイルが変更されているのがわかりますね。

lib/B以下のファイル群はPerlのBモジュール(Perlのバックエンドをいい感じにスクリプトから使えるくん)関連です。

# とりあえずテストから見る

連鎖比較の実装を追う前に、そもそもどういう使用感なのかをテストから探ります。 連鎖比較のテストは t/op/cmpchain.tに記述されています。

全体で1749ものテストが存在するので、全てでは無いですが一部を取り出してみます。 実行には5.32.0-RC0を利用しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$ prove -v  t/op/cmpchain.t
t/op/cmpchain.t ..
1..1749
ok 1 - <=> <=> non-associative
ok 2 - <=> cmp non-associative
ok 3 - <=> ~~ non-associative
ok 4 - cmp <=> non-associative
ok 5 - cmp cmp non-associative
ok 6 - cmp ~~ non-associative
ok 7 - ~~ <=> non-associative
ok 8 - ~~ cmp non-associative

ok 273 - isa ge ge non-associative
ok 274 - ge ge isa non-associative
ok 275 - == == legal
ok 276 - == != legal
ok 277 - == eq legal
ok 278 - == ne legal
ok 279 - != == legal


ok 1724 - 5 < 7 <= 8 > 5 with side effects
ok 1725 - operand evaluation order
ok 1726 - 5 == 7 != 8 == 6
ok 1727 - 5 < 7 <= 8 > 6
ok 1728 - 5 == 7 != 8 == 6 with side effects
ok 1729 - operand evaluation order

ok 1746 - 5 == 7 != 8 == 9 with side effects
ok 1747 - operand evaluation order
ok 1748 - 5 < 7 <= 8 > 9 with side effects
ok 1749 - operand evaluation order
ok
All tests successful.
Files=1, Tests=1749,  0 wallclock secs ( 0.12 usr  0.01 sys +  0.05 cusr  0.01 csys =  0.19 CPU)
Result: PASS

テストを見ると連鎖比較は 0 < $n < 10 のような3条件以外にも 5 < 7 <= 8 > 9の様にさらに複数連鎖可能な様です。 さらに大小以外にもcmpや~~なども使えるみたいですね。

# perly.(h|tab|y)

まず最初に見るのはperly.(h|tab|y)系のファイルです。

このファイルはPerlの心臓部と言ってもいいであろう構文解析系の実装を担当しています。 Perlは構文解析ではYacc(Bison)を利用しており、perly.yがその定義ファイル、 perly.tab及びperly.hはbisonが生成してくれるファイルです。

その為人間が変更したファイルであるperly.hの変更点を見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
diff --git a/perly.y b/perly.y
index 0325d663c0..c90b8b41b8 100644
--- a/perly.y
+++ b/perly.y
@@ -56,7 +56,7 @@
 %token <ival> GIVEN WHEN DEFAULT
 %token <ival> LOOPEX DOTDOT YADAYADA
 %token <ival> FUNC0 FUNC1 FUNC UNIOP LSTOP
-%token <ival> RELOP EQOP MULOP ADDOP
+%token <ival> MULOP ADDOP
 %token <ival> DOLSHARP DO HASHBRACK NOAMP
 %token <ival> LOCAL MY REQUIRE
 %token <ival> COLONATTR FORMLBRACK FORMRBRACK
@@ -76,6 +76,7 @@
 %type <opval> refgen_topic formblock
 %type <opval> subattrlist myattrlist myattrterm myterm
 %type <opval> termbinop termunop anonymous termdo
+%type <opval> termrelop relopchain termeqop eqopchain
 %type <ival>  sigslurpsigil
 %type <opval> sigvarname sigdefault sigscalarelem sigslurpelem
 %type <opval> sigelem siglist siglistornull subsigguts subsignature optsubsignature
@@ -97,8 +98,8 @@
 %left <ival> ANDAND
 %left <ival> BITOROP
 %left <ival> BITANDOP
-%nonassoc EQOP
-%nonassoc RELOP
+%left <ival> CHEQOP NCEQOP
+%left <ival> CHRELOP NCRELOP
 %nonassoc UNIOP UNIOPSUB
 %nonassoc REQUIRE
 %left <ival> SHIFTOP
@@ -1028,10 +1029,10 @@ termbinop:      term ASSIGNOP term                     /* $x = $y, $x += $y */
                        { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
        |       term SHIFTOP term                      /* $x >> $y, $x << $y */
                        { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
-       |       term RELOP term                        /* $x > $y, etc. */
-                       { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
-       |       term EQOP term                         /* $x == $y, $x eq $y */
-                       { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
+       |       termrelop %prec PREC_LOW               /* $x > $y, etc. */
+                       { $$ = $1; }
+       |       termeqop %prec PREC_LOW                /* $x == $y, $x cmp $y */
+                       { $$ = $1; }
        |       term BITANDOP term                     /* $x & $y */
                        { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
        |       term BITOROP term                      /* $x | $y */
@@ -1048,6 +1049,38 @@ termbinop:       term ASSIGNOP term                     /* $x = $y, $x += $y */
                        { $$ = bind_match($2, $1, $3); }
     ;
+termrelop:     relopchain %prec PREC_LOW
+                       { $$ = cmpchain_finish($1); }
+       |       term NCRELOP term
+                       { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
+       |       termrelop NCRELOP
+                       { yyerror("syntax error"); YYERROR; }
+       |       termrelop CHRELOP
+                       { yyerror("syntax error"); YYERROR; }
+       ;
+
+relopchain:    term CHRELOP term
+                       { $$ = cmpchain_start($2, $1, $3); }
+       |       relopchain CHRELOP term
+                       { $$ = cmpchain_extend($2, $1, $3); }
+       ;
+
+termeqop:      eqopchain %prec PREC_LOW
+                       { $$ = cmpchain_finish($1); }
+       |       term NCEQOP term
+                       { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
+       |       termeqop NCEQOP
+                       { yyerror("syntax error"); YYERROR; }
+       |       termeqop CHEQOP
+                       { yyerror("syntax error"); YYERROR; }
+       ;
+
+eqopchain:     term CHEQOP term
+                       { $$ = cmpchain_start($2, $1, $3); }
+       |       eqopchain CHEQOP term
+                       { $$ = cmpchain_extend($2, $1, $3); }
+       ;
+

結構いろいろ変更点がありますね。上から見ていきましょう。

1
2
3
4
5
6
7
8
9
@@ -56,7 +56,7 @@
 %token <ival> GIVEN WHEN DEFAULT
 %token <ival> LOOPEX DOTDOT YADAYADA
 %token <ival> FUNC0 FUNC1 FUNC UNIOP LSTOP
-%token <ival> RELOP EQOP MULOP ADDOP
+%token <ival> MULOP ADDOP
 %token <ival> DOLSHARP DO HASHBRACK NOAMP
 %token <ival> LOCAL MY REQUIRE
 %token <ival> COLONATTR FORMLBRACK FORMRBRACK

このdiffを確認すると %token <ival> RELOP EQOP MULOP ADDOPの中のRELOPとEQOPが削除されています。

これはYACC中のトークンとしてRELOP MULOPなどを宣言し、トークンが持つ値の型としてivalを宣言しています。 ivalそのものやyaccの宣言部分でI32型として宣言されています。

削除されたRELOPとEQOPが何であるかを見てみましょう。 実際にYACC内でこのトークンがどういうものかを定義している構文規則部分を見てみましょう。

1
2
3
4
 term RELOP term                        /* $x > $y, etc. */
         { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }
 term EQOP term                         /* $x == $y, $x eq $y */
                       { $$ = newBINOP($2, 0, scalar($1), scalar($3)); }

コメントからなんとなく雰囲気がわかるかと思いますが、 RELOPは左右をtermで囲う文法規則です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@@ -76,6 +76,7 @@
 %type <opval> refgen_topic formblock
 %type <opval> subattrlist myattrlist myattrterm myterm
 %type <opval> termbinop termunop anonymous termdo
+%type <opval> termrelop relopchain termeqop eqopchain
 %type <ival>  sigslurpsigil
 %type <opval> sigvarname sigdefault sigscalarelem sigslurpelem
 %type <opval> sigelem siglist siglistornull subsigguts subsignature optsubsignature
@@ -97,8 +98,8 @@
 %left <ival> ANDAND
 %left <ival> BITOROP
 %left <ival> BITANDOP
-%nonassoc EQOP
-%nonassoc RELOP
+%left <ival> CHEQOP NCEQOP
+%left <ival> CHRELOP NCRELOP
 %nonassoc UNIOP UNIOPSUB
 %nonassoc REQUIRE
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy