SPVM公式ドキュメント 1.0 ベータ

昨日の記事はcharsbarさんでした。

本日はyukikimotoが書きます。リレー形式でつなげていくのは、楽しいですね!

この記事はSPVMの日本語公式ドキュメントです。書き始めですが、数時間で、書けるところまで書きます。

Perl Advent Calender 2018をきっかけにに作成されました。

目次

目的

SPVMが開発された主な目的を教えてください。

SPVMは、Perlの数値計算の遅さを改善するために開発されました。数値計算と配列の演算を高速化することができます。

どれくらいの速度の改善を目指していますか

仕様が完成した後にベンチマークテストを始める予定ですが、数値計算と配列の演算に対して、Perlの30倍、C言語の1/2の速度を、最初の目標としています。

他の目的はありますか

二つ目の目的は、C/C++のバインディングを簡単にすることです。XSやInline::Cは、C/C++のバインディングという点で、非常に難しいと感じています。

簡単なAPIを使って、メモリ安全に、C/C++のバインディングができる機能を提供しています。

さらに、他の目的はありますか

三つ目の目的は、単体で実行可能な実行ファイルを生成することです。

SPVMは、実行ファイルを生成する機能を持っているので、ランタイムなしに、プログラムの実行が可能で、配布することもできます。

共有ライブラリ(ダイナミックリンクライブラリ)の読み込みにも対応しています。

生成された実行ファイルのライセンスはどうなりますか

SPVMのソースコードには、MITライセンスが適用されるので、MITライセンスの元で、商用利用が可能です。

実行ファイルを生成して、何ができますか

たとえば、ボタンなどのGUI部品を備えた、配布可能なWindowsアプリケーションが作成できる予定です。

ユーザー

どのような人をSPVMのユーザーとして想定していますか。

遺伝子解析などのビオテクノロジーの研究者

Perlは、遺伝子解析などのビオテクノロジーの分野で利用されていますが、配列操作の遅さに不満を感じている研究者の方がいます。

配列操作を簡単に高速化でき、必要であればC/C++をバインディングし、openMPやSIMDなどの並列計算ができればと考えている研究者の方を想定しています。

Perlの数値計算を速くしたいが、XSやC言語は難しいと感じる方

SPVMを使うと、Perlの文法で、数値計算と配列演算を速くすることができます。

IoTの開発者

IoTの開発者は、ハードウェアからWebまでの幅広い知識が必要になります。大量の分野の知識が必要となるため、これを簡単に実現するライブラリがあれば便利です。

C/C++、アセンブラを使って、センサーのデバイスドライバを作成。HTTPクライアントライブラリを使って、Webにアクセス。開発環境で、実行ファイルを作成して、それぞれの機器にそのままコピー。このような一連の機能をSPVMは提供する予定です。

グラフィックスエンジニア

画像処理には、大量の配列演算が必要です。Perlは、配列のデータ構造を言語仕様として持たないため、不満を感じている方がいます。

SPVMは、連続した領域を持つ配列のデータ構造を提供しているので、DirectXやOpenGM、OpenCVなどと組み合わせて、画像処理をしたいユーザーに向いています。

AI・機械学習エンジニア

AI・機械学習には、配列演算が必要です。Perlは、配列のデータ構造を言語仕様として持たないため、不満を感じている方がいます。

SPVMは、連続した領域を持つ配列のデータ構造を提供しているので、機械学習ライブラリとの相性が高いです。

マイクロサービスの提供者

SPVMは、JSONモジュール、非同期IOをサポートしたHTTPクライアント/サーバーをコアで提供する予定です。実行ファイルを生成可能なので、小さなLinuxサーバーを構築し、実行ファイルをコピーして、マイクロサービスを運用可能です。

SPVMは、リファレンスカウント方式のGCを採用しているので、FullGCが起こらず、非同期IOの性能に大きく関わるレイテンシを小さくできます。

SPVMのインストール

SPVMはCPANモジュールです。cpanまたはcpanmを使ってインストールできます。

cpan SPVM
cpanm SPVM

機能

連続したデータ領域を持つ配列型

SPVMは連続したデータ領域を持つ配列型を提供しています。

仕様化された数値型と計算規則

Perlの数値計算の弱点の一つは、型を固定して計算することができないことです。ある操作によって浮動小数点に変換されたり、文字列型に変換されたりします。SPVMでは、仕様化された数値型と計算規則を持っているので、どの型によって計算されるかを正確把握することができます。

C99で追加された数学関数

C99で追加された新しいC言語の数学関数がすべて利用できます。

仕様化された言語仕様

SPVMの言語仕様は、メジャーバージョンごとに、仕様化されています。仕様を元に、独自にコンパイラを開発することが可能です。

サブルーチンの機械語へのコンパイル

SPVMのサブルーチンはコンパイル時に、機械語へコンパイルすることができます。

SPVMのチュートリアル

SPVMのチュートリアルです。

配列の和を求める

最初の簡単な例として配列の和を求めてみましょう。

SPVMモジュールの作成

「MyMath.spvm」というファイルファイルをlibディレクトリの中に作成してください。SPVMのソースファイルの拡張子は「.spvm」です。以下の内容を記述しましょう。

# lib/MyMath.spvm
package MyMath {
  sub sum : int ($nums : int[]) {
    
    my $total = 0;
    for (my $i = 0; $i < @$nums; $i++) {
      $total += $nums->[$i];
    }
    
    return $total;
  }
}

SPVMではパッケージ構文を使用して、パッケージを作成します。Perlのパッケージブロック構文と同じ文法です。

# パッケージ構文
package MyMath {

}

パッケージのブロックの中ではサブルーチン定義を行うことができます。

package MyMath {
  sub sum : int ($nums : int[]) {
    
  }
}

SPVMは静的型言語ですので、サブルーチンの宣言の中で、戻り値の型、引数名と引数の型を記述します。

戻り値は「int型」。引数は「int型の配列」です。int型は、32bit符号付整数を表現します。

配列の和を計算している部分をみてみましょう。

    my $total = 0;
    for (my $i = 0; $i < @$nums; $i++) {
      $total += $nums->[$i];
    }

    return $total;

和の計算方法の見た目は、Perlでfor文を使って書いたものとまったく同じです。

このようにSPVMではMPerlと同じ文法で記述できるのがひとつの特徴です。Perlユーザーが、新しい文法を覚える負担ができるだけ小さくなるように設計されています。

変数の宣言では型推論を使って、型を省略することができます。

    my $total = 0;

SPVMの型推論は、右辺の型が確定しているときに、左辺の型宣言を省略することができます。

数値リテラルの「0」は「int型」ですので、「$total」の型も「int型」になります。以下の記述と同じ意味になります。

    my $total : int = 0;

配列の長さは「@」を使うことで取得できます。

@$nums

SPVMには、コンテキストはなく「@」は常に配列の長さを返します。

次にSPVMで書かれたサブルーチンをPerlの側から呼び出してみましょう。

SPVMモジュールの呼び出し

「sum.pl」というファイルを作成して、以下の内容を記述してください。

SPVMで書かれた「MyMath」パッケージの「sum」サブルーチンを呼び出して、配列の和を計算します。

use FindBin;
use lib "$FindBin::Bin/lib";

use SPVM 'MyMath';

my $sp_nums = SPVM::new_iarray([3, 6, 8, 9]);

my $total = MyMath->sum($sp_nums);

print $total . "\n";

まず最初にlibディレクトリをモジュールの検索パスに追加します。

# libディレクトリをモジュールの検索パスに追加
use FindBin;
use lib "$FindBin::Bin/lib";

次にSPVMモジュールを読み込みます。

# SPVMモジュールを読み込み
use SPVM 'MyMath';

「use SPVM 'SPVMモジュール名'」という記述で、SPVMモジュールを読み込むことができます。

次にSPVMの配列を作成します。

# int型配列の作成
my $sp_nums = SPVM::new_iarray([3, 6, 8, 9]);

SPVM::new_iarray関数を使うと、配列のリファレンスを渡して、SPVMのint型の配列を作成することができます。

MyMathパッケージのsumサブルーチンを呼び出してみましょう。

# MyMathパッケージのsumサブルーチンを呼び出し
my $total = MyMath->sum($sp_nums);

SPVMのサブルーチン呼び出しは、Perlのクラスメソッド呼び出しになることに注意してください。「絶対名 MyMath::sub」で呼び出すことはできません。

sum関数の戻り値は「int型」ですが、SPVMの整数型は、自動的にPerlのスカラ型に変換されます。

言語仕様

SPVMの言語仕様の詳細です。

構文解析

ソースコードの文字コード

SPVMのソースコードの文字コードは、BOMなしのUTF-8で記述されます。

LALR法

SPVMのソースコードは、LALR法によって解析できます。yacc/bisonで生成されたパーサージェネレータによって解析することができます。

構成要素

SPVMのソースコードは「空白文字」「コメント」「リテラル」「キーワード」「識別子」「区切り文字」「演算子」で構成されます。

行終端

SPVMのソースコードの行終端は、ASCIIコードの「LF」「CR」「CR LF」です。

行終端が現れたときは、適切に行番号がインクリメントされます。

空白文字

SPVMにおける空白文字はASCIIコードの「SP」「HT」「FF」と「行終端」です。

空白文字はソースコード上では意味を持ちません。

キーワード

SPVMにおけるキーワードは以下です。

byte BEGIN case croak default double elsif else enum eq
eval for float gt ge has if interface isa int last length
lt le long my native ne next new our object package private
public precompile pointer_t return require rw ro self switch
sub string short scalar undef unless use void value_t while
weaken wo __END__ __PACKAGE__ __FILE__ __LINE__

識別子

SPVMにおける識別子は「パッケージ名」「サブルーチン名」「フィールド名」「パッケージ変数名」「レキシカル変数名」です。

区切り文字

SPVMにおける区切り文字は以下です。

( ) { } [ ] ; , ->

演算子

SPVMにおける演算子は以下です。

=   >   <   !   ~
==  <=  >=  !=  &&  ||  ++  --
+   -   *   /   &   |   ^   %   <<   >>   >>>
+=  -=  *=  /=  &=  |=  ^=  %=  <<=  >>=  >>>=
\   $   @   .   

コメント

コメントは「#」で始まり改行で終わります。

# コメント

POD

POD(プレーンオールドドキュメント)を記述することができます。PODは行頭が「=」で始まる任意の文字列の行から始まり、行頭から行末まで「=cut」で終わる行までです。

PODの内側に書かれた文字列はソースコードとして解釈されず、コメントとして扱われます。

=pod

複数行
コメント

=cut
=head1

複数行
コメント

=cut

パッケージ

パッケージを定義するには以下の構文を使用します。

package パッケージ名 {

}

パッケージ名は「大文字」で始まる必要があります。

「パッケージ名」の後に「:」をつなげてその後ろに「デスクリプタ」を指定することができます。

package パッケージ名 : デスクリプタ {

}

パッケージの定義のサンプルです。

# パッケージ名のみ
package Point {

}
# パッケージ名とデスクリプタ
package Point : public {

}

パッケージデスクリプタ

パッケージで指定できるデスクリプタの一覧です。

デスクリプタ名 役割
public このパッケージに対するnewキーワードが他のパッケージから利用できます。
private このパッケージに対するnewキーワードが他のパッケージから利用できません。デフォルトの設定です。
interface このパッケージは「インターフェイス」になります。
value_t このパッケージは「値型」になります。
pointer_t このパッケージは「ポインタ型」になります。

「public」と「private」の両方のデスクリプタが指定された場合は、コンパイルエラーになります。

「interface」「value_t」「pointer_t」のひとつより多くが同時に指定されている場合は、コンパイルエラーになります。

パッケージ内部で定義できるもの

パッケージ内部では「use」「パッケージ変数」「フィールド」「列挙」「サブルーチン」が定義できます。

package Foo {
  # use
  use Point;
  
  # パッケージ変数
  our $VAR int;
  
  # フィールド
  has var : int;
  
  # 列挙
  enum {
    CONST_VAL
  }
  
  # サブルーチン
  sub foo : int ($num : int) {
  
  }
}

フィールド

フィールドとは「new」を使ってオブジェクト生成した場合に、オブジェクトからアクセスできるデータ領域のことです。

「has」キーワードを使用してフィールドを定義することができます。

has フィールド名 : 型名;

フィールド定義は「パッケージ定義」の直下で行う必要があります。

フィールド定義には「型名」が必要です。

フィールド名は「a~z」「A~Z」「0~9」「_」で構成する必要があります。先頭は、数字から始めることはできません。連続した「_」を使用することはできません。

フィールド定義には、デスクリプタを合わせて指定することができます。

has フィールド名 : デスクリプタ 型名;

フィールドデスクリプタ

フィールドで指定できるデスクリプタの一覧です。

デスクリプタ名 役割
public このフィールドは、外部のパッケージからアクセスできます。
private このフィールドは、外部のパッケージからアクセスできません。デフォルトの設定です。
ro このフィールドは、読み込み用のフィールドアクセッサを持ちます。フィールドアクセッサ名は、フィールド名と同じです。フィールド名が「foo」の場合は、フィールドアクセッサ名は「foo」になります。
wo このフィールドは、書き込み用のフィールドアクセッサを持ちます。フィールドアクセッサ名は、「set_フィールド名」になります。フィールド名が「foo」の場合は、フィールドアクセッサ名は「set_foo」になります。
rw このフィールドは、読み込み用のフィールドアクセッサと書き込み用のフィールドアクセッサを持ちます。読み込み用のフィールドアクセッサ名は「ro」で説明したものと同じです。書き込み用のフィールドアクセッサ名は「wo」で説明したものと同じです。

「public」と「private」の両方のデスクリプタが指定された場合は、コンパイルエラーになります。

「ro」「wo」「rw」のひとつより多くが同時に指定されている場合は、コンパイルエラーになります。

フィールドアクセッサとは、フィールドにアクセスするためのメソッドのことです。

書き込み用のフィールドアクセッサの戻り値は「void型」です。

SPVMのソースコードの中からフィールドアクセッサが呼び出された場合は、フィールドアクセッサはインライン展開されます。それ以外の場合は、インライン展開されません。

フィールド定義のサンプル

フィールド定義のサンプルです。

package Foo {
  has num1 : byte;
  has num2 : short;
  has num3 : int;
  has num4 : long;
  has num5 : float;
  has num6 : double;

  has num_public : public int;
  has num_ro : ro int;
  has num_wo : wo int;
  has num_rw : rw int;
}

パッケージ変数定義

パッケージ変数とは、パッケージに属する、プログラムの開始から終了まで維持されるグローバル変数のことです。

「our」キーワードを使用してパッケージ変数を定義することができます。

our パッケージ変数名 : 型名;

パッケージ変数定義は「パッケージ定義」の直下で行う必要があります。

パッケージ変数定義には「型名」が必要です。

パッケージ変数名は「$」で始まり、次に「A-Z」、その後ろに、ひとつ以上の「a~z」「A~Z」「0~9」「_」を続けることができます。Perlと異なり、「$」の後ろには、必ず大文字の英数字が必要であることに注意してください。

パッケージ変数定義には、デスクリプタを合わせて指定することができます。

our パッケージ変数名 : デスクリプタ 型名;

パッケージ変数デスクリプタ

パッケージ変数で指定できるデスクリプタの一覧です。

デスクリプタ名 役割
public このパッケージ変数は、外部のパッケージからアクセスできます。
private このパッケージ変数は、外部のパッケージからアクセスできません。デフォルトの設定です。
ro このパッケージ変数は、読み込み用のパッケージ変数アクセッサを持ちます。パッケージ変数アクセッサ名は、パッケージ変数名から「$」を除いたものです。パッケージ変数名が「$FOO」の場合は、パッケージ変数アクセッサ名は「FOO」になります。
wo このパッケージ変数は、書き込み用のパッケージ変数アクセッサを持ちます。パッケージ変数アクセッサ名は、「SET_パッケージ変数名から$を除いたもの」になります。パッケージ変数名が「$FOO」の場合は、パッケージ変数アクセッサ名は「SET_FOO」になります。
rw このパッケージ変数は、読み込み用のパッケージ変数アクセッサと書き込み用のパッケージ変数アクセッサを持ちます。読み込み用のパッケージ変数アクセッサ名は「ro」で説明したものと同じです。書き込み用のパッケージ変数アクセッサ名は「wo」で説明したものと同じです。

「public」と「private」の両方のデスクリプタが指定された場合は、コンパイルエラーになります。

「ro」「wo」「rw」のひとつより多くが同時に指定されている場合は、コンパイルエラーになります。

パッケージ変数アクセッサとは、パッケージ変数にアクセスするためのクラスメソッドのことです。

書き込み用のパッケージ変数アクセッサの戻り値は「void型」です。

SPVMのソースコードの中からパッケージ変数アクセッサが呼び出された場合は、パッケージ変数アクセッサはインライン展開されます。それ以外の場合は、インライン展開されません。

パッケージ変数定義のサンプル

パッケージ変数定義のサンプルです。

package Foo {
  our $NUM1 : byte;
  our $NUM2 : short;
  our $NUM3 : int;
  our $NUM4 : long;
  our $NUM5 : float;
  our $NUM6 : double;

  our $NUM_PUBLIC : public int;
  our $NUM_RO : ro int;
  our $NUM_WO : wo int;
  our $NUM_RW : rw int;
}

整数型

SPVMの整数型は以下の4つです。

型名 説明 範囲
byte 8bit符号付整数 -128~127
short 16bit符号付整数 -32768~32767
int 32bit符号付整数 -2147483648~2147483647
long 64bit符号付整数 -9223372036854775808~9223372036854775807

型変換

縮小型変換

縮小型変換とは、数値型において広い型から狭い型への変換が行われる場合に適用される変換の規則のことです。

型の順序は狭い方から「byte」「short」「int」「long」「float」「double」です。

拡大型変換

拡大型変換とは、数値型において狭い型から広い型への変換が行われる場合に適用される変換の規則のことです。

型の順序は狭い方から「byte」「short」「int」「long」「float」「double」です。

リテラル

整数リテラル

10進数表現

整数リテラルの数値部は「0~9」の1つ以上の連続した文字で表現されます。

先頭に「+」あるいは「-」の符号をつけることができます。

整数リテラルの型は、デフォルトでは「int型」になります。

整数リテラルがint型で表現できる数値の範囲を超えている場合は、コンパイルエラーになります。

末尾に「L」あるいは「l」のサフィックスをつけることで「long型」の整数リテラルを表現できます。

long型の整数リテラルの場合は、long型で表現できる数値の範囲を超えている場合は、コンパイルエラーになります。

区切り文字として「_」を使用することができます。区切り文字は意味を持ちません。

不正な整数リテラルの表現はコンパイルエラーになります。

整数リテラルがbyte型の変数に代入される場合、あるいはbyte型のサブルーチンの引数として渡される場合で、byte型で表現できる数値の範囲を超えていない場合は、縮小型変換によって、byte型に自動的に変換されます。範囲を超えている場合は、コンパイルエラーとなります。

整数リテラルがshort型の変数に代入される場合、あるいはshort型のサブルーチンの引数として渡される場合で、short型で表現できる数値の範囲を超えていない場合は、縮小型変換によって、short型に自動的に変換されます。範囲を超えている場合は、コンパイルエラーとなります。

整数リテラルのサンプルです。

123
+123
-123
123L
123l
123_456_789
-123_456_789L

16進数表現

整数リテラルの数値部は16進数を使って表現することができます。

数値部を16進数を使って表現するときは「0x」から始めます。

その後ろに「0~9」「a~f」「A~F」のひとつ以上の連続した文字が続きます。

不正な16進数表現は、コンパイルエラーになります。

整数リテラルを16進数で表現したサンプルです。

0x3b4f
-0x3F1A
0xDeL
0xFFFFFFFF_FFFFFFFF

8進数表現

整数リテラルの数値部は8進数を使って表現することができます。

数値部を8進数を使って表現するときは「0」から始めます。

その後ろに「0~7」のひとつ以上の連続した文字が続きます。

不正な8進数表現は、コンパイルエラーになります。

整数リテラルを8進数で表現したサンプルです。

0755
-0644
0666L
0655_755

2進数表現

整数リテラルの数値部は2進数を使って表現することができます。

数値部を2進数を使って表現するときは「0b」から始めます。

その後ろに「0」か「1」のひとつ以上の連続した文字が続きます。

不正な2進数表現は、コンパイルエラーになります。

整数リテラルを2進数で表現したサンプルです。

0b0101
-0b1010
0b110000L
0b10101010_10101010

浮動小数点リテラル

浮動小数点リテラルは「符号部」「数値部」「指数部」「サフィックス」から構成されます。

浮動小数点リテラルには「10進数浮動小数点リテラル」と「16進数浮動小数点リテラル」があります。

「符号部」は「+」か「-」で表現されます。「符号部」の存在は、任意です。「符号部」が存在する場合は、先頭にある必要があります。

「10進数浮動小数点リテラル」は、数値部が「一桁以上の10進数字」で始まる必要があります。

「10進数字」とは「0~9」のことです。

「10進数浮動小数点リテラル」は、数値部に「小数点」が含まれているか、含まれていない場合は「指数部」あるいは「サフィックス」が必要です。

「小数点」とは「.」のことです。

「16進数浮動小数点リテラル」は、数値部が「0x」あるいは「0X」で始まり、その後ろに「一桁以上の16進数字」が続く必要があります。

16進数字とは「0~9」「a~f」「A~F」のことです。

「16進数浮動小数点リテラル」は、「数値部」に「小数点」を含むことができます。

「数値部」はアンダーライン「_」を含むことができます。これは単なる区切り文字で、無視されます。

「16進数浮動小数点リテラル」は、「指数部」が必要です。

「指数部」は「指数表現」と「符号付10進整数」で構成されます。

「指数表現」は「10進数浮動小数点リテラル」の場合は「e」あるいは「E」、「16進数浮動小数点リテラル」の場合は「p」あるいは「P」になります。

「指数部」の意味は「10進数浮動小数点リテラル」の場合は、10進数による桁移動、「16進数浮動小数点リテラル」の場合は、2進数による桁移動になります。

末尾に「f」あるいは「F」のサフィックスをつけると、浮動小数点リテラルの型は「float型」になります。

末尾に「d」あるいは「D」のサフィックスをつけると、浮動小数点リテラルの型は「double型」になります。

サフィックスが省略された場合は、浮動小数点リテラルの型は「double型」になります。

浮動小数点リテラルが「float型」の場合はC標準の「strtof関数」を使って、文字列からfloat型への変換が行われます。変換が失敗した場合は、コンパイルエラーになります。

浮動小数点リテラルが「double型」の場合はC標準の「strtod関数」を使って、文字列からdouble型への変換が行われます。変換が失敗した場合は、コンパイルエラーになります。

無限大を表現する浮動小数点リテラルはありません。標準関数である「INFINITY関数」「INFINITYF関数」を使用してください。

非値を表現する浮動小数点リテラルはありません。標準関数である「NAN関数」「NANF関数」を使用してください。

浮動小数点リテラルのサンプルです

1.32
-1.32
1.32f
1.32F
1.32e3
1.32e-3
1.32E+3
1.32E-3
0x3d3d.edp0
0x3d3d.edp3
0x3d3d.edP3
0x3d3d.edP-3f

文字リテラル

文字リテラルは、シングルクォート「'」で囲まれます。

文字リテラルの内容は「ひとつのAsciiの印字可能文字」あるいは「ひとつのエスケープ文字」です。

不正な文字リテラルの場合は、コンパイルエラーになります。

エスケープ文字

エスケープ文字 説明
\b Asciiコードの8「BS」
\t Asciiコードの9「HT」
\n Asciiコードの10「LF」
\f Asciiコードの12「FF」
\r Asciiコードの13「CR」
\" Asciiコードの34「"」
\' Asciiコードの39「'」
\\ Asciiコードの92「\」

上記以外のAsciiコードを直接表現したい場合は、整数リテラルを使用して、直接代入することができます。

my $ch = (byte)0;
my $ch = (byte)13;

文字リテラルのサンプル

文字リテラルのサンプルです。

# 文字
'a'
'x'

# エスケープ文字
'\b'
'\t'
'\n'
'\f'
'\r'
'\"'
'\''
'\\'

文字列リテラル

未定義値

未定義は「undef」で表現されます。

undef

未定義値は、任意のオブジェクト型の変数に代入することができます。

未定義値はオブジェクト型の値と「==」「!=」演算子を使用して、比較することができます。未定義値は、生成されたオブジェクトと等しくない事が保証されます。

未定義は条件部で使われた場合は、偽になります。

未定義値は、エクステンションにおいてC言語の値として利用された場合は、0と等しくなることが保証されます。

Perlと異なる点は、SPVMにおいては「undef」は関数ではなくリテラルであるということです。

条件部

条件部とは、条件判定が行われる部分のことです。SPVMでは、以下の部分が条件部となります。

if文のかっこの中。

if (条件部) {

}

unless文のかっこの中。

unless (条件部) {

}

forのかっこの中の二つ目。

for (初期化;条件部;次の値;) {

}

whileのかっこの中。

while (条件部) {

}

標準関数

SPVMの標準関数の一覧です。

INFINITY

sub INFINITY : double ()

無限大を「double型」で返します。C標準の「INFINITYマクロ」の単純なラッパーです。

INFINITYF

sub INFINITYF : float ()

無限大を「float型」で返します。C標準の「INFINITYマクロ」の単純なラッパーです。

NAN

sub NAN : double ()

非値を「double型」で返します。C標準の「NANマクロ」の単純なラッパーです。

NANF

sub NANF : float ()

非値を「float型」で返します。C標準の「NANマクロ」の単純なラッパーです。

FAQ

SPVMを開発するにあたって参考にした言語を教えてください。

言語仕様と文法についてはPerlを主に参考にしています。必要な部分でPerl 6の文法・キーワードも採用しています。「has」「native」「ro」「rw」「wo」など。

言語仕様とバーチャルマシンについては、Java言語とJavaVMの言語仕様を多くを参考にしています。

数値型と数値計算においては、Javaの計算規則とほぼ同じです。数値型の種類、演算子の種類、拡張型変換、縮小型変換などです。

言語仕様においては、ボクシング、アンボクシング、可変長引数については、Javaを参考に作りました。

SPVMのバーチャルマシンの初期実装は、JavaVMを参考にして、可変長バイト命令を解釈するスタック型VMとして作成されました。現在のSPVMは、64bitの固定長命令を解釈するレジスター型VMとなっています。

文字列がUTF-8であることと、インターフェースの実装については、go言語の言語仕様を参考にしています。

なぜレジスタ型VMを採用していますか

レジスタ型VMを採用している最も大きな理由は、SPVMのオペレーションコードを、C言語のソースコードに変換するときに、1対1で対応させることができるためです。gccの最適化が適用できます。

列挙にint型以外の型を利用することはできますか

残念なことですが、列挙に利用できるるのはint型だけです。

他の型の定数を利用したい場合は、定数を返すサブルーチンを定義してください。

sub FOO : double () {
  return 3.14;
}

一つの定数を返すサブルーチンは、定数としてインライン展開されることが仕様上で保証されているので、パフォーマンスを気にせず利用できます。

SPVMのサブルーチン呼び出しでかっこを省略できないのはなぜですか。

SPVMにおいては、メソッド呼び出し、クラスメソッド呼び出しについては、かっこの省略が可能です。サブルーチン名だけを指定した場合においては、かっこの省略ができません。

これは、サブルーチン名だけでは、識別子名がサブルーチンであることを、ソースコードの中で、決定できないためです。パッケージ名やフィールド名との区別ができません。

他のソースファイルを解析することによって、かっこを省略する構文は、理論的には可能ですが、SPVMでは、単一ファイルの中で、静的な構文解析を完了できるということを重要視しました。

サポートされるGCCのバージョンを教えてください

gcc 4.3で確認しており、保証される最低のバージョンはgcc 4.3です。C99がサポートされている必要があります。

符号なし整数型はありませんか

残念ながら、SPVMには、符号なし整数型はありません。

対応しているOSを教えてください。

Unix、Linux、macOS、Windowsに対応しています。

Perlと異なる点を教えてください。

コンテキストは存在しません。関数呼び出しには括弧が必要です。三項演算子はありません。シングルクォートは、文字定数です。

標準関数や標準モジュールは、Perlとは完全に異なっています。

サブルーチンは、必ずメソッドか、クラスメソッドになります。サブルーチンの絶対名での呼び出しはできません。

モジュールの拡張子は「spvm」です。

型はすべて静的型です。サブルーチン呼び出しは、コンパイル時に解決されます。配列は静的です。動的配列とハッシュは、モジュールとして提供されます。

スレッドは利用できますか

スレッドは、コアではサポートされていませんが、エクステンションを使ったユーザーモジュールを作成することで、間接的に利用できます。

SPVMはシングルスレッドで動くように設計されています。シングルスレッドは、利用者にとって簡単で安全な設計です。

スレッドの機能はSPVMのコアにはありませんが、CやC++のスレッドライブラリを利用して、エクステンションから利用することはできます。

エクステンションでは、スレッド用のSPVMの実行環境を生成して、スレッド上で、利用できます。

SPVMのモジュールとして作成すれば、SPVMからサブルーチンを通して、間接的にスレッドを利用できます。

エクステンションとは何ですか

エクステンションとは、SPVMからC/C++の関数を呼び出すための仕組みのことです。

プリコンパイルとは何ですか

プリコンパイルとは、SPVMのサブルーチンを、コンパイル時に機械語に変換する仕組みのことです。機械語に変換されたサブルーチンは、高速に実行できます。

precompileが指定されたサブルーチンを含むモジュールファイルは、コンパイル時に、Cのソースコードに変換されます。

precompile sub sum : int ($num1 : int, $num2 : int) {
  return $num1 + $num2;
}

Cのソースコードは、ビルドディレクトリの中に作成されます。

生成されたCのソースコードは、Perlをコンパイルしたコンパイラ(通常はgccかclang)によって、機械語(.oの拡張子を持つオブジェクトファイル)にコンパイルされます。

機械語に変換された後、各OSで呼び出すことのできる共有ライブラリ(.soや.dll)にリンクされます。

ビルドディレクトリとは何ですか

プリコンパイルする場合に、必要となるディレクトリのことです。

ビルドディレクトリを利用することをSPVMに教えるにはSPVM::BuildDirモジュールを使用する必要があります。

use SPVM::BuildDir;

スクリプトがあるディレクトリの「spvm_build」というディレクトリがデフォルトのビルドディレクトリ名になります。

ビルドディレクトリ名を自分で指定したい場合は、次のようにします。

use FindBin;
use SPVM::BuildDir "$FindBin::Bin/mydir;

インターフェースとは何ですか

SPVMでは、インターフェスとは、実装を持たないメソッドが一つだけ定義されているパッケージのことをいいます。

インターフェースのひとつの例は、標準モジュールである「SPVM::Comparable」です。

package SPVM::Comparable : interface {
  sub compare : int ($self : self, $object1 : object, $object2 : object);
}

機能としては、C言語の関数ポインタに似ています。

ジェネリクスはありますか

残念なことですが、SPVMにはジェネリクスはありません。SPVMは、コンパイル時の型決定性よりも、型の簡単さを選択しました。

コンテナの要素は、汎用オブジェクト型で定義してください。汎用オブジェクトから実際のオブジェクトを取得するためには、型キャストが必要です。

sub add : void ($self : self, $object : ojbect) { ... }
sub get : object ($self : self, $index : int) { ... }
my $list = SPVM::List->new;
$list->add("hello!");
my $str = (string)$list->get(0);

サブルーチンのオーバーロードはありますか。

サブルーチンのオーバーロードはありません。サブルーチンは、サブルーチン名で一意的に識別されます。

採用されていない最も大きな理由は、Perl自体が型を持たないために、PerlからSPVMのサブルーチンへ渡す値の型が決定できないためです。

このためSPVMでは、サブルーチン名によって、戻り値と引数の型がわかるように設計されています。

浮動小数点の演算は処理系依存ですか

はい、浮動小数点の表現方法、および演算は、処理系に依存します。

パッケージ名、フィールド名、サブルーチン名などの識別子に連続したアンダーラインが使えないのはなぜですか

二つの連続したアンダーラインは、エクステンションにおいて、パッケージ名とサブルーチン名の区切りとして利用されるためです。

エクステンションはC言語で書かれます。

# SPVMのサブルーチン
package Foo::Bar {
  sub baz : void () { }
}
// エクステンションにおける関数名
SPVM_NATIVE_Foo__Bar__baz(SPVM_ENV* env, SPVM_VALUE* stack) {

}

SPVMの「Foo::Barパッケージのbazサブルーチン」はエクステンションの「SPVM_NATIVE_Foo__Bar__baz」に対応します。これは1対1に対応し、相互に名前の変換が可能です。

SPVMからPerlのサブルーチンを呼び出すことはできますか

残念ながら、SPVMからPerlのサブルーチンを呼び出すことはできません。

C言語のconstやJavaのfinalのような機能はありますか。

一度代入した値を変更できなくする機能はありませんが、他の機能の組み合わせで実現することができます。

コンパイル時定数で数値型の場合

定数サブルーチンを使用します。

package Foo {
  sub VAL : double () {
    return 5.1234;
  }
}

コンパイル時定数でオブジェクト型の場合

パッケージ変数を定義します。パッケージ変数に読み込み用のアクセッサを定義します。BEGINブロックを使って、パッケージ変数を初期化します。

package Foo {
  our $POINT : ro int;
  
  BEGIN {
    $POINT = Point->new;
  }
}

シングルトンを生成することはできますか。

はいできます。

パッケージ変数を定義します。BEGINブロックを使って、パッケージ変数を初期化します。singletonメソッドで、オブジェクトを返します。

package Foo {
  our $SINGLETON : Foo;
  BEGIN {
    $SINGLETON = new Foo;
  }
  
  sub singleton : Foo () {
    return $SINGLETON;
  }
}

SPVMはPerlを置き換えることを目指していますか

SPVMはPerlを置き換えることを目指していません。PerlとC言語が苦手な分野に焦点を当てて、その間を補うことがプロジェクトの目的です。

SPVMの開発に参加することはできますか。

コア機能、標準関数、標準モジュールについては、作者が決定を行っています。その範囲の中であれば、開発への参加が可能です。

バグ報告、ベンチマーク、言語評価、ブログなどでの紹介は歓迎です。

開発情報

SPVMの開発に参加

「README」の中に開発手順が記載されています。

著者

木本裕紀(kimoto.yuki@gmail.com)

コアデベロッパー

moti(motohiko.ave@gmail.com)

リポジトリ

SPVM(GitHub)

CPAN

SPVM(CPAN)

バグ報告

バグ報告はGitHubのIssueで行うことができます。

GitHub Issue

ドキュメント最終更新日

2018年12月12日

明日は

明日はaeroastroさんです。

Perl Advent Calendar 2018