MoarVM の Configure.pl を Perl6 で書こうとした話
Perl が書けなかったので、MoarVM の ./Configure.pl を Perl6 で書いて理解してみようと思った:
use v6; use Getopt::Long; constant $NAME = 'moar'; constant $GENLIST = 'build/gen.list'; sub MAIN() { my Bool $failed = False; my %args; my %defaults; my %config; get-options( %args, <help|? debug:s optimize:s instrument! coverage os=s shell=s toolchain=s compiler=s ar=s cc=s ld=s make=s has-sha has-libuv static has-libtommath has-libatomic_ops has-dyncall has-libffi pkgconfig=s build=s host=s big-endian jit! enable-jit prefix=s bindir=s libdir=s mastdir=s make-install asan ubsan valgrind telemeh> ); say $args; # pod2usage( True ) $; # say 'Welcome to MoarVM!'; }
そもそも、Perl6 の Getopt::Long
の definition が Perl の Getopt::Long
と違う可能性が微レ存だったので、いきなりそこで躓いた。
それに、Perl6 の Getopt::Long
は = が必須みたいなので、Perl の Getopt::Long
とは全然違う感じがする。
use v6; use Getopt::Long; # # Perl5 の Getopt::Long のように実装されているなら、以下が動くはず: # my $capture = Getopt::Long.new( <help|?> ); # # 今のところ、動かない………。 # say $capture;
Getopt::Tiny
というのもあるんだけど、それも真偽値は =!
が必要。
use v6; use Getopt::Tiny; my Hash $opts = {help => False, version => False}; get-options( $opts, <help=! verbose=!> ); say $opts;
ということで、Getopt::OldFashion を書くことにした。
こんな風に書けるはず:
use v6; use Getopt::OldFashion; my %opts = get-options( <help|? version verbose> ); say %opts;
すごくシンプル過ぎるビルドシステム書いた
2 日も空いちゃったけど、書いた。
いつしか加わってしまった Emacs の markdown-mode の末尾の半角スペース 2 つが 2 つのアンダーバーになってしまう
クソ現象で markdown-mode が使えないため、text-mode を使っている。
と思ったら、Emacs を再起動したところ、治った。
そんなことはともかく、こういうへろーわーるどな C 言語のソースコードをコンパイルしていきたい。
// examples/hello.c #include <stdio.h> int main() { printf( "Hello, World!" ); return 0; }
こういう Build.p6
をソースツリーのトップに置き、このファイルを叩くことで基本的にビルドを行うことができる:
# examples/Build.p6 use v6; use IO::Glob; use Build::Simple; my @sources = glob( './*.c' ); my @objects = gather for @sources -> $source { take compile $source; } link 'hello', |@objects;
&compile
や &link
は Build::Simple
モジュールに入っている。
お好みで IO::Glob
を use
するととっても便利である。
叩く時は以下のようにする:
% perl6 -I../lib ./Build.p6
-I../lib
は perl6 が lib/Build/Simple.pm6
を探しやすくするために必要である。
が、perl6/ecosystem
に入った暁には zef install Build::Simple
すれば必要なくなる。
ということで、lib/Build/Simple.pm6
は以下の通り。
今回は is export
と書くことで Build::Simple::compile
と書かなくて良くなるということがわかった。
# lib/Build/Simple.pm6 use v6; unit module Build::Simple; sub compile($dependency) is export { my $target = $dependency.extension: 'o'; say "compiling {$dependency}"; shell [ 'gcc', '-c', $dependency, '-o', $target ].join(' '); $target; } sub link($target, *@objects) is export { say "linking {$target}"; shell [ 'gcc', |@objects, '-o', $target ]; }
%*ENV<CC>
とかでコンパイラのコマンドを環境変数からとってきたりしたいなーなどと思っている。
ということで、また次回。
Perl6 の参照の話
というわけで、アドベントカレンダーを登録してないけどそんな気持ちでクソエントリを書いていこうと思う。
前もやったかもしれないけど、Perl6 の参照の話をしよう。
my @puyo = (1, 2, 3);
簡単な配列を入れた @puyo
を宣言する。
そして、それを $myon
に入れる。
my $myon = @puyo;
ここで、$myon
は何を持っているだろう?それは、@puyo
が持っている配列の参照だ。
えーと、配列の参照って?
╰( ´◔ ω ◔ `)╯ わからん
じゃあ、$myon
をまた別の配列変数に入れてみよう:
my @hunyo = $myon;
ここで @hunyo
と @puyo
は等価だろうか?
say
で中身を表示してみよう:
[[1 2 3]]
違う。
@hunyo
の中身は [[1, 2, 3]]
となぜかネストした配列になっている。
これは、$myon
には配列そのものではなく、配列のリファレンスが入っていることが原因だ((おそらくそうなんだろうなと言う意味))。
別の表現をすると、配列型の変数には配列しか設定できないため、スカラー変数の値を配列変数に設定した場合、暗黙のキャストが起こり、配列は配列の配列へと変容してしまう。
これを防ぐには、変数の前に |
(パイプ)を付ける:
my @hunyo = |$myon;
このような現象は以下のように Map の値が配列であるというような場合に起こりやすい:
my %already = ( authors => ['John Due'] ); { my @authors = do if %already<authors>:exists { %already<authors> # %already<authors> はスカラーを返す! パイプを使え! } else { [] }; say @authors; # ウボァーーーーー!!!! @authors は [['John Due']] だ!!!! }
私はJSON を扱う際にこの現象にぶち当たり、うぎゃーーーーーとなった。
みなさんも Perl6 を書く時には注意して欲しい。
なんと Perl6 ではホームディレクトリを表すチルダを変換してくれない
嘘だろ?! って思ったんだけど、ほんとっぽい。
~/.bashrc
を /home/alice/.bashrc
って変換してくれるようなバンドルされてるメソッドとか関数がないって意味ね。
# -*- mode: per6; -*- # \file: 00-childa.t use v6; use Test; plan 2; is $*HOME.add( '.bashrc' ).Str, '~/.bashrc'.IO.absolute, '.absolute でチルダを変換できるだろ、常識的に考えて……'; is $*HOME.add( '.bashrc' ).Str, '~/.bashrc'.IO.resolve , '.resolve でチルダを変換できるだろ、常識的に考えて……'; done-testing;
これを実行してみると:
% perl6 ./00-childa.t
1..2
not ok 1 - .absolute でチルダを変換できるだろ、常識的に考えて……
# Failed test '.absolute でチルダを変換できるだろ、常識的に考えて……'
# at ./00-childa.t line 8
# expected: '/home/rihine/workspace/p6-midi-converter-demo/~/.bashrc'
# got: '/home/rihine/.bashrc'
not ok 2 - .resolve でチルダを変換できるだろ、常識的に考えて……
# Failed test '.resolve でチルダを変換できるだろ、常識的に考えて……'
# at ./00-childa.t line 9
# expected: '/home/rihine/workspace/p6-midi-converter-demo/~/.bashrc'
# got: '/home/rihine/.bashrc'
# Looks like you failed 2 tests of 2
グワーーーーーーーーーーーーーーーーーーー!!!!!!
ちょー簡単な &expand-filepath
はこんな感じかもだ:
sub expand-filepath($filepath) { $filepath.subst( '~', $*HOME.Str ) }
.subst
とかで変換できるんだし、無くてよくね?って感じなのかもしれない。
NativeCall つらい
GLib とか Gtk を NativeCall で呼び出そうとしてるんだけど、つらい。
C 言語でやるような typedef
はどーやんの?
GLib とかでこーいう感じの gsize
は:
typedef unsinged long gsize;
Perl6 では以下のように constant
る。
constant gsize = uint32;
構造体ってどーすんの?
Perl6 Document に書いてあるけど、こんな風に class
として書くよ!書くよ!
class GtkApplication is repr( 'CStruct' ) { has GtkApplication $.parent; has Pointer $.priv; }
列挙体を渡したい時は?
わからん。
こんな風にやったら、ダメだった:
enum GApplicationFlags ( G_APPLICATION_FLAGS_NONE => 0 ); sub gtk_application_new(Str, GApplicationFlags) returns Pointer[GtkApplication] is native('gtk-3') { * }
is repr('CEnum')
とかできるんだろーか?
Perl6 の正規表現は不思議なんじゃなくて、自由だった
ここ を見て Perl6 のコードに移植するっていうのをやっていた。
Perl6 の正規表現が不思議すぎるなーと思って書いていたんだけど、よく読んだら自由だったらしい。
function isNumber (x) { if ( typeof x === 'number' ) return true; if ( /^0x[0-9a-f]+$/i.test( x ) ) return true; return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test( x ); }
は Perl6 で書くと多分こうなる:
sub is-number($x) { return True if $x.WHAT ~~ Numeric; return True if $x ~~ /^0x<[0..9a..fA..F]>+$/; $x ~~ /^<[-+]>?(<:digit>+)?(\.<:digit>*)?(e<[-+]>?<:digit>+)?$/; }
ʕ•͡ω•ʔ 眠い
GMake サバイバルガイド 便利そうな関数編
make
は前回の記事で十分使えるようになったんじゃないかと思います*1。
私も険しいまけふぁいるの坂を駆け上がっている途中なので、ここで紹介できる関数は 2 つしかないのですが、
ええい、たらーん!!! という方もおられると思います。
そのような方はGNU make: Functionsをご覧ください。
notdir
関数
notdir
関数は実際 basename
コマンドと一緒です。
# makefile ref0 = ./packages/FParsec.1.0.2/lib/net40-client/FParsec.dll ref1 = ./packages/FParsec.1.0.2/lib/net40-client/FParsecCS.dll target-dir = ./bin/Debug all: echo $(target-dir)/$(notdir $(ref0))
以上のファイルを makefile
や Makefile
という名前でどこか空のディレクトリに保存し、make
と打ってみてください。
以下のように表示されるはずです:
% make
echo ./bin/Debug/FParsec.dll
./bin/Debug/FParsec.dll
shell
関数
shell
関数はバッククォーテーション文字列で括ったコマンドっぽいことをするようなやつです。
shell
関数を知る前はこのように書いていましたが:
SDL2_INCLUDES = `pkg-config --cflags-only-I sdl2` SDL2_DEFINES = `pkg-config --cflags-only-other sdl2` SDL2_LIBS = `pkg-config --libs sdl2) SDL2GFX_LIBS = `pkg-config --libs SDL2_gfx`
shell
関数を使うとこのように書けます:
SDL2_INCLUDES = $(shell pkg-config --cflags-only-I sdl2) SDL2_DEFINES = $(shell pkg-config --cflags-only-other sdl2) SDL2_LIBS = $(shell pkg-config --libs sdl2) SDL2GFX_LIBS = $(shell pkg-config --libs SDL2_gfx)
バッククォーテーションで括ったところはコマンドは無くなって、例えば pkg-config --cflags-only-I sdl2
の結果が SDL2_INCLUDES
にそのまま設定されます。
ビルドしてみると、このように出力されます:
% make -B
gcc -W -Wall -O2 -D_REENTRANT -I/usr/include/SDL2 -c main.c -o main.o
main.c: 関数 ‘main’ 内:
main.c:12:14: 警告: unused parameter ‘argc’ [-Wunused-parameter]
int main(int argc, char ** argv) {
^~~~
main.c:12:28: 警告: unused parameter ‘argv’ [-Wunused-parameter]
int main(int argc, char ** argv) {
^~~~
gcc -lSDL2_gfx -lSDL2 -lSDL2 main.o -o demo-sdl2-gfx
実際にやってみたい方は ( ノ╹◡◡╹)ノ SDL2_gfx を使ったデモっぽいやつ をご覧ください。
参考
*1:多分、きっと、おそらく
GMake サバイバルガイド
この GMake サバイバルガイドでは、まけふぁいる*1をささっと書いて楽をしたいけど、
どーやって書くのかわからないといった様な人たちに向けてこんな感じに書けば良いんじゃないかなーというのを書いていると思います。
あと、この記事のまけふぁいるのサンプルコードは便宜上 4 半角スペースでインデントしていますが、まけふぁいるのインデントは絶対にタブなので、実際にやって見る場合は注意してください。
最新のソースコードを実行したい!
Crystal のソースコードをコンパイルしてから実行するのは億劫ですよね。
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
何度も続けて行うなら、何とかして自動化してしまいたいもの。
まあ、cyrstal run ./hello.cr
とかすれば Crystal のソースコードから実行できるんですが、それは無視します。
以下のようなへろーわーるどなソースコードがあったとします:
# hello.cr puts "Hello, World!"
make
ってやるだけで、実行ファイルがなかったり古かったりしたらコンパイルして
とりあえず実行したい場合は私はまけふぁいるをこんな風に書きます:
.PHONY : run run: hello ./hello hello: hello.cr crystal build --release $^
.PHONY: run
というのは run
というシンボルはファイルじゃねーからよく覚えとけ!という意味を表明するために書いています。
これは省略可能です。
すぐ下の:
run: hello
./hello
こういうのをこの記事ではレシピ*2と呼びましょう。
レシピとはあるファイル○○を作るにはファイル△△が必要で、××するとできるというのをまけふぁいるの記述で書いたものです。
それをまけファイル用の記述で書くとこのようになります:
○○: △△ ××
先程のレシピに戻ると run
というファイルには hello
というものが必要で、run
を作るには ./hello
が必要で、 hello
コマンドを実行すればいいということが書いてあります。
しかし、run
というファイルは実際には作成されないので、何度も実行することが可能です。
そういえば、hello
はどうやって造られるのでしょう?
hello
のレシピはすぐ下にあります:
hello: hello.cr crystal build --release $^
hello
というファイルには hello.cr
というファイルが必要で、crystal build --release $^
を実行すればいいよね!ということが書いてあります。
$^
は必要なファイル──この場合では hello.cr
ですね──を参照する変数です。
作成するファイルを参照する変数は $@
なので、覚えておいてください。
とはいえ、この 2 つをもし忘れてもレシピは書けます。
hello: hello.cr
crystal build --release hello.cr
という風に書けばいいだけだからです。
替わりにめんどくさいというだけです。
たくさんのソースコードの中で、新しいのだけコンパイルしたい!!
おーけーべいべ!
まさにそのために make
は存在します。
例えば、ソースツリーに以下の LiveScript のソースコードがあったとします:
./routes.ls
./test/index.ls
./gulpfile.ls
./public/components/App.ls
./public/components/AddRoute.ls
./public/components/CounterRoute.ls
./public/store.ls
./public/action.ls
./server.ls
./config.ls
それに対して私の書いたまけふぁいるはこうです:
srcs = \ gulpfile.js \ routes.js \ server.js \ config.js \ test/index.js \ public/store.js \ public/action.js \ public/components/AddRoute.js \ public/components/App.js \ public/components/CounterRoute.js .PHONY : build build: $(srcs) gulpfile.js: gulpfile.ls lsc --debug --no-header -b -c $^ routes.js: routes.ls lsc --debug --no-header -b -c $^ server.js: server.ls lsc --debug --no-header -b -c $^ config.js: config.ls lsc --debug --no-header -b -c $^ test/index.js: test/index.ls lsc --debug --no-header -b -c $^ public/store.js: public/store.ls lsc --debug --no-header -b -c $^ public/action.js: public/action.ls lsc --debug --no-header -b -c $^ public/components/AddRoute.js: public/components/AddRoute.ls lsc --debug --no-header -b -c $^ public/components/App.js: public/components/App.ls lsc --debug --no-header -b -c $^ public/components/CounterRoute.js: public/components/CounterRoute.ls lsc --debug --no-header -b -c $^
これでもまだ力技を使っていますが、わかりやすいかもしれませんね。
test/index.ls
があるので、test
はこのように書けるかもしれません:
test: test/index.ls mocha $^ --compilers ls:livescript
まけふぁいるでは、変数も使えます。
srcs = \ gulpfile.js \ routes.js \ server.js \ config.js \ test/index.js \ public/store.js \ public/action.js \ public/components/AddRoute.js \ public/components/App.js \ public/components/CounterRoute.js
が、その例です。
これは、○○や△△、××で使用できます*3。
変数の値を参照するには $(src)
のように、$(
と )
で変数名を囲みます。
変数を使わない場合、build
はこのように、△△の部分が長くなってしまい、見るに絶えません。
build: gulpfile.js routes.js server.js config.js test/index.js public/store.js public/action.js public/components/AddRoute.js public/components/App.js public/components/CounterRoute.js
そういえば、build
には××の部分が存在しないことに気づいていると思います。
build
の××の部分は、既に△△のそれぞれの××が行っているので、必要ないのです。
(とはいえ、.o
を使って実行ファイルや .dll
、.so
を作成するような場合は必要です。)
今回は .ls
を .js
にトランスパイルするだけなので、××は書いていません。
えーん、あるファイル一つだけ make
したいよー
上の例で server.ls
だけをビルドしたいとしましょう。
この場合は以下のように打つだけです:
% make server.js
public/components/CounterRoute.ls
なら?
% make public/components/CounterRoute.js
ですね。
test/index.ls
なら?
% make test/index.js
ですね。
make CounterRoute.js
や make App.js
としないように気をつけてください。
それらは多分あなたが意図しているのとは違うファイルです。
そうそう、上の例で、make build
と打つ必要はありません。
make
と打つと、一番上のレシピ──この場合は build
──が実行されるからです。
いったんビルドしたのも全部ビルドしたいよ〜
make
にオプション -B
を付けてください。
% make -B
LiveScript でつまずいたこと
関数呼び出し中の do
hoge-func ham, tomato, beacon do a: \foo b: \bar
が:
hogeFunc( ham, tomato, beacon, { a: 'foo', b: 'bar' } );
になって欲しいのだが、時々こういう風になる:
hogeFunc( ham, tomato, beacon({ a: 'foo', b: 'bar' }) );
うん、beacon
はただの変数で、関数じゃねーよwwwwww
もうやだこの altJS…………。
地雷だよね………(´Д⊂グスン
これは、do
を使う前の引数が変数だとなるらしい。
なので、今までやっていた通り:
hoge-func ham, tomato, beacon, a: \foo b: \bar
こんな風に書くことにする。
変数?方式の関数定義は上の方に変数がいっぱい溜まるし、普通に書きたいよー
こーいうのは:
apply-options = (options) !-> heating-power := options.power number-of-people-of-cook := options.number_of_people
こんな風に書ける:
!function apply-options (options) heating-power := options.power number-of-people-of-cook := options.number_of_people
関数を渡す関数内で呼び出し元の this
を使いたい
これは私のじゃっばすくりぷよぢからが 1200 万パワーに満たないために発生したことなんだけど:
class Cook (@skillet) -> cook: (foods) !-> foods.for-each (food) !-> # skillet は Cook インスタンスのものだが、このコンテキスト(for-each に渡された関数)においては # @skillet はないため、あぼーんする @skillet.push food
こういうことをやっていたために、実行すると激しくあぼーんしたり、挙動がおかしくなっていた。
LiveScript の公式サイトを目を皿のようにして眺めたところ、~>
を使うと (・∀・)イイ!! らしいことがわかったので、このように変えた:
cook: (foods) !-> # 見づらいと思うけど、矢印(`->')の横棒(`-')をチルダ(`~')にすると(`~>')、 # lsc がうまく計らってくれる。 foods.for-each (food) !~> @skillet.push food ``