Blog

プログラミング言語 Tora を公開した

http://tora-lang.org/

Perl6 の開発が宣言されてから10年以上の歳月がたち、いまだ実用的な実装はあらわれません。
そんななかで、next perl 的なものをつくりたいと gfx がいってから 3ヶ月ぐらいたっていますが、こちらも一行もコードをかいた様子はありません

しょうがないので僕がつくってみました。まだあらけずりですが、意外とうごいています。

今回、とくに Perl5 にあった機能のなかでけずりたかったものは以下のとおり

つけたかったものは以下のとおり


未実装な点は以下のとおり

などなど。

git clone して、scons install するとはいります。osx lion と ubuntu の最新版で動作確認しています。

拡張なども簡単なのでおためしください。

以下が基礎文法です。

hello world

say("Hello world");

コメント

/* コメント */
# コメント

変数宣言

@な配列や%なハッシュはありません。Perl5 でいうところの arrayref と hashref しかないのです。

my $var;

実行

tora foo.tra

コンパイルチェック

tora -c foo.tra

リテラル

数値
my $num = 1;
my $num = 1.234;
四則演算
$num = 1 + 1;
$num = 1 - 1;
$num = 1 * 2;
$num = 1 / 2;
# 余り
$mod = 3 % 2;
インクリメントとデクリメント
$i++
++$i
$i--
--$i
文字列
my $str1 = 'abc';
my $str2 = "def";
my $str3 = "a\tbc\n";

# 変数展開(結果は abc def)
my $str4 = "$str1 def";
文字列操作
# 結合
my $join1 = 'aaa' + 'bbb';

# join
my $join2 = ['aaa', 'bbb', 'ccc'].join(' ');

# 分割
my @record = 'aaa,bbb,ccc'.split(',');

# 長さ
my $length = 'abcdef'.length;

# 切り出し
my $substr = 'abcd'.substr(0, 2); # ab

# 検索(未実装)
my $result = 'abcd'.index('cd'); # 見つかった場合はその位置、見つからなかった場合は-1が返る

配列

# 配列の宣言
my $array;

# 配列への代入
$array = [1, 2, 3];
配列の要素の参照と代入
# 要素の参照
$array[0];
$array[1];

# 要素の代入
$array[0] = 1;
$array[1] = 2;
配列の個数
my $array_num = $array.size();
配列の操作
# 先頭の要素を取り出す
my $first = $array.shift;

# 先頭に要素を追加
$array.unshift(5);

# 末尾の要素を取り出す
my $last = $array.pop();

# 末尾に要素を追加
$array.push(9);

ハッシュ

ハッシュの宣言と代入

Perl5 とおなじですが %hash はありません。

my $hash;
$hash = {a => 1, b => 2};
ハッシュの要素の参照と代入

ハッシュの参照のときは {} ではなく [] をつかいます。こういう風にしておいた方がユーザー定義コンテナをつくりやすいからです。

# 要素の参照
$hash[a];
$hash[a];

# 要素の代入
$hash[a] = 5;
$hash[b] = 7;

Perl5 とおなじくハッシュのキーは「a-zA-Z_」で構成されている場合は、シングルクォートやダブルクォートで囲む必要はありません。

ハッシュのメソッド
# キーの取得
$keys = $hash.keys();

# 値の取得
$values = $hash.values();

# キーの存在確認
$hash.exists('a');

# ハッシュのキーの削除
$hash.delete('a');

時刻

Perl5 の Time::Piece のようなクラスが組み込みで用意されています。

my $t = Time.now();
$t.year()
$t.month();
$t.epoch();

制御文

if

括弧はいらんです。

if 条件 {
   
}
if 〜 else文

if 〜 else文です。 あいかわらず括弧いらないです。

if 条件 {
   
} else {
   
}
if 〜 elsif 文

if 〜 elsif文です。またしても括弧はいらないですね。

if 条件 {
   
} elsif 条件 {
   
}
while 文

Perl5 とかわらないですが、括弧を省略できます。

my $i = 0;
while $i < 5 {
    $i++;
}
for 文

C-style の for 文です。

for (my $i = 0; $i < 5; $i++) {
   
}
for-each 文

for というキーワードです。foreach というキーワードはつかいません。Perl6 風です。

for $fields -> $field {
    ...
}

$fields の各要素が $field におさめられます。Perl5 とちがい、ユーザーがイテレータ/ジェネレータを実装できるようにしてあるのがいいです。

比較演算子

Perl5 とちがって、文字列比較も数値比較も同じ演算子です。基本的には左辺値を尊重するかんじです。

$p == $q     $pと$qは等しい
$p != $q     $pと$qは等しくない
$p < $q     $pは$qより小さい
$p > $q     $pは$qより大きい
$p <= $q     $pは$q以下
$p >= $q     $pは$q以上

tora には boolean 型があるので、true または false がかえります。わかりやすいです。

関数

引数をちゃんと名前でとれます。return は省略できます。

sub sum_foo($num1, $num2) {
    my $total = $num1 + $num2;
    return $total;
}

括弧を省略すると、Perl5 のように配列でとることもできます。その場合、$_ に配列がはいります。

sub sum_foo {
    my $n = 0;
    for $_ -> $i {
        $n += $i;
    }
    return $n;
}

ファイル入出力

Perl5 で「こうかけたらいいな」とおもってたかんじで、かけます。ファイル読み込みに失敗した場合は例外があがります。

my $fh = open($filename);
print($fh.slurp);
$fh.close();

その他

真偽値

undef と false 以外はすべて真です。 Perl5 とちがいます。

また Bool 型がありますので、JSON などでも安心です。

コマンドライン引数

グローバル変数 $ARGV からとれます。中身は Array です。

コンテキスト

コンテキストはもちろんありません。

unless

unless も勿論つかえます。

unless cond {
}
後置 if/unless

もちろんつかえます。

say("OK") if $x;
say("OK") unless $x;
後置 for

つかえます。$_ もつかえます。

say($_) for $x;
配列スライスとハッシュスライス

未実装です。

lambda

lambda さん書けます。ちなみに、引数を省略すると $_ に引数ははいります。こちらの場合、 $_ には1個目の引数そのものがはいります。

-> $x { }
-> $x, $y { }
-> { p($_) }
map
$array.map(-> $x { $x*2 });
grep(未実装)
$array.grep(-> $x { $x.match(/cat/) })
文字列リスト演算子

文字列のリストを簡単に書く構文があります。

my $strs = qw/aaa bbb ccc/;
単独のreturn

tora においては、すべての関数は返り値をもちます(void はない)。return を単独でかくと、undef がかえります。

sub x() {
  return;
}
例外処理

例外を投げるにはdieを使用します。

die("Error message");

例外を補足するときは try 文をつかいます。

my ($err, $orig) = try {
    die "FAIL";
};
my ($err, $orig) = try {
    return "foo";
};

try ブロックの中で例外がかえってきたときは最初の返り値が例外オブジェクトとなります。
そうでない場合は最初の返り値が undef となり、その後に元の return 文でかえってきた値がはいります。

この文法はあまり他ではみたことがないですが、意外と便利な気がしています。

3項演算子

Perl5 とかわらずつかえます。
>||my$num = $flg ? 1 : 2;

<
||=

Perl5 とおなじです。

$num ||= 2;

|

モジュールの読み込み
use Test::More *;
use Test::More;

Perl5 のように、モジュールごとにちがうルールで exporter をつかう必要はありません。
なにも指定しなければなにも export されず、* をつけた場合にはすべての public な関数が export されます。
public な関数とは _ が先頭についていないもののことです。
特定の名前だけを指定して export することもできるようになる予定ですが未実装です。

use Test::More qw(is);
クラス定義

クラスの定義方法という意味もこめて、tora でかかれた http server を掲載しておきます。
Perl5 でかくよりもずっとシンプルにかけているのではないかとおもいます。

use Socket *;
use HTTP::Parser *;
use HTTP::Status *;

class HTTP::Server::Simple {
    sub new($host, $port=80) {
        self.bless({host => $host, port => $port});
    }

    sub run($app) {
        my $server = Socket.socket(AF_INET, SOCK_STREAM, 0);
        $server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1);
        $server.bind(Socket.sockaddr_in(${self}['port'], Socket.inet_aton(${self
}['host'])));
        $server.listen(SOMAXCONN);
        while (1) {
            my ($csock, $sockaddr) = $server.accept();
            my $buf = b'';
            while (1) {
                $buf += $csock.read(4068);
                my $headers = {};
                my $ret = parse_http_request($buf, $headers);
                if ($ret == -2) {
                    say("[DEBUG] Incomplete request.");
                } elsif ($ret == -1) {  
                    say("[INFO] Broken request.");
                    last;
                } else {
                    # parsed.
                    say("[INFO] Parsed http request.");

                    my $res = $app.($headers);

                    my $code = $res[0]; 

                    my $content = $buf.substr($ret);
                    my $res_heads = $res[1];

                    my $header = "HTTP/1.0 " + $code + " " + status_message($code) + "\r\n";
                    for (my $i=0; $i<$res_heads.size(); $i+=2) {
                        $header += $res_heads[$i] + " : " + $res_heads[$i+1] + "\r\n";
                    }
                    $header += "\r\n";  
                    $csock.write($header);
                    for $res[2] -> $part {
                        $csock.write($part);
                    }
                    $csock.close();

                    last;
                }
            }
        }
        $server.close();
    }
}

__END__

HTP::Server::Simple
===================

SYNOPSIS
--------

    use HTTP::Server::Simple;

    my $httpd = HTTP::Server::Simple.new('127.0.0.1', 8080);
    $httpd.run(-> $env {
        return [200, [], ["OK"]];
    });

LICENSE
-------

Copyright (C)2011 Tokuhiro Matsuno

Licensed under the perl license.