SPVM言語仕様 1.0 ベータ

はじめに

SPVMの言語仕様です。SPVMは、静的な型を持ちPerlの文法で記述できるプログラム言語です。

このドキュメントには、SPVM言語仕様のすべてが記載されます。

PerlからSPVM言語の利用については「SPVMエクスチェンジAPI仕様」を見てください。

SPVMからC/C++などのネイティブコードの利用については、「SPVMネイティブAPI仕様」を見てください。

SPVMの標準関数については、「SPVM標準関数仕様」を見てください。

SPVMの標準モジュールについては、「SPVM標準モジュール仕様」を見てください。

最終更新日 2019年2月8日

目次

C99準拠

C99準拠のソースコード

SPVM自体のソースコードは、C言語で書かれ、C99に準拠します。

数値演算における仕様は、C99に準拠します。C99で、未定義・処理系依存のものは、SPVMにおいても未定義・処理系依存です。

加算演算子減算演算子、乗算演算子、除算演算子、剰余演算子、シフト演算子、ビット演算子においては、C99における演算との対応が仕様化されます。

数値から文字列への型変換における仕様は、C99のsrpintfの「%g」フォーマットが仕様になります。

C99との型の対応

SPVMの型は、C99の次の型と完全に一致します。

SPVMの型 C99の型 説明
byte int8_t SPVMのbyte型はC99のint8_t型に一致します。
short int16_t SPVMのshort型はC99のint16_t型に一致します。
int int32_t SPVMのint型はC99のint32_t型に一致します。
long int64_t SPVMのlong型はC99のint64_t型に一致します。
float float SPVMのfloat型はC99のfloat型に一致します。
double double SPVMのdouble型はC99のdouble型に一致します。
オブジェクト型 void* SPVMのオブジェクト型はC99のvoid*型に一致します。
byte& int8_t* SPVMのbyte&型はC99のint8_t*型に一致します。
short& int16_t* SPVMのshort&型はC99のint16_t*型に一致します。
int& int32_t* SPVMのint&型はC99のint32_t*型に一致します。
long& int64_t* SPVMのlong&型はC99のint64_t*型に一致します。
float& float* SPVMのfloat&型はC99のfloat*型に一致します。
double& double* SPVMのdouble&型はC99のdouble*型に一致します。
値型 値型のフィールドの個数を要素数とし、対応した型を持つ配列型 たとえば「package Point_2i : value_t { has x : int; has y : int; }」で定義されていた場合は、Point_2i型は、int32_t[2]型に一致します。

構文解析

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

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

LALR(1)法

SPVMのソースコードは、LALR(1)法によって解析できます。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_t 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における識別子は「パッケージ名」「サブルーチン名」「フィールド名」「パッケージ変数名」「レキシカル変数名」です。

パッケージ名

パッケージ名は、ASCIIコードにおけるアルファベットの大文字で始まります。

パッケージ名は、「ASCIIコードにおけるアルファベットあるいはアンダーバーの一文字以上」と「::」の組み合わせです。

最後は「::」で終わってはいけません。

アンダーバーは、二つ以上続いてはいけません。

パッケージ名が要求される場所で、不正なパッケージ名が現れた場合は、コンパイル時エラーが発生します。

# 正しいパッケージ名
Foo
Foo::Bar
Foo::Bar::Baz
Foo::bar
Foo_Bar::Baz_Baz

# 不正なパッケージ名
foo
Foo::Bar::
Foo__Bar

サブルーチン名

サブルーチン名は、「ASCIIコードにおけるアルファベットあるいはアンダーバーの一文字以上」です。

アンダーバーは、二つ以上続いてはいけません。

サブルーチン名が要求される場所で、不正なサブルーチン名が現れた場合は、コンパイル時エラーが発生します。

# 正しいサブルーチン名
FOO
FOO_BAR
foo
foo_bar
_foo
_foo_bar_

# 不正なサブルーチン名
foo__bar

パッケージ変数名

パッケージ変数名は、先頭が「$」で始まり、その後「ASCIIコードにおけるアルファベットあるいはアンダーバーの一文字以上」と「::」の組み合わせが続きます。

「$」の次に来る文字は、ASCIIコードにおけるアルファベットの大文字でなければなりません。

アンダーバーは、二つ以上続いてはいけません。

パッケージ変数の定義においては、パッケージ変数名に「::」が含まれてはいけません。

パッケージ変数名が要求される場所で、不正なパッケージ変数名が現れた場合は、コンパイル時エラーが発生します。

# 正しいパッケージ変数名
$FOO::BAR
$Foo::Bar
$FOO
$FOO_BAR

# 不正なパッケージ変数名
$foo
$FOO__BAR
$

レキシカル変数名

レキシカル変数名は、先頭が「$」で始まり、その後「ASCIIコードにおけるアルファベットあるいはアンダーバーの一文字以上」が続きます。

「$」の次に来る文字は、ASCIIコードにおけるアルファベットの子文字でなければなりません。

アンダーバーは、二つ以上続いてはいけません。

レキシカル変数名が要求される場所で、不正なレキシカル変数名が現れた場合は、コンパイル時エラーが発生します。

# 正しいレキシカル変数名
$foo
$foo_bar
$_foo

# 不正なレキシカル変数名
$Foo
$foo__bar
$

区切り文字

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

( ) { } [ ] ; , -> =>

演算子

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

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

構文解析表

yacc/bisonにおける、SPVM文法の構文解析表です。

%token  PACKAGE HAS SUB OUR ENUM MY SELF USE REQUIRE
%token  DESCRIPTOR
%token  IF UNLESS ELSIF ELSE FOR WHILE LAST NEXT SWITCH CASE DEFAULT EVAL
%token  NAME VAR_NAME CONSTANT PACKAGE_VAR_NAME EXCEPTION_VAR
%token  UNDEF VOID BYTE SHORT INT LONG FLOAT DOUBLE STRING OBJECT
%token  DOT3 FATCAMMA RW RO WO BEGIN NEW
%token  RETURN WEAKEN CROAK CURRENT_PACKAGE UNWEAKEN '[' '{' '('

%type  grammar
%type  opt_packages packages package package_block
%type  opt_declarations declarations declaration
%type  enumeration enumeration_block opt_enumeration_values enumeration_values enumeration_value
%type  sub anon_sub opt_args args arg invocant has use require our string_length
%type  opt_descriptors descriptors sub_names opt_sub_names
%type  opt_statements statements statement if_statement else_statement 
%type  for_statement while_statement switch_statement case_statement default_statement
%type  block eval_block begin_block if_require_statement
%type  unary_op binary_op comparison_op num_comparison_op str_comparison_op isa logical_op
%type  call_sub opt_vaarg
%type  array_access field_access weaken_field unweaken_field isweak_field convert array_length
%type  deref ref assign inc dec
%type  new array_init
%type  my_var var package_var_access
%type  term opt_expressions expressions expression condition opt_expression
%type  field_name sub_name
%type  type basic_type array_type array_type_with_length ref_type  type_or_void

%right  ASSIGN SPECIAL_ASSIGN
%left  LOGICAL_OR
%left  LOGICAL_AND
%left  BIT_OR BIT_XOR
%left  '&'
%nonassoc  NUMEQ NUMNE STREQ STRNE
%nonassoc  NUMGT NUMGE NUMLT NUMLE STRGT STRGE STRLT STRLE ISA
%left  SHIFT
%left  '+' '-' '.'
%left  MULTIPLY DIVIDE REMAINDER
%right  LOGICAL_NOT BIT_NOT '@' REF DEREF PLUS MINUS CONVERT SCALAR LENGTH ISWEAK
%nonassoc  INC DEC
%left  ARROW

%%

grammar
  : opt_packages

opt_packages
  :	/* Empty */
  |	packages
  
packages
  : packages package
  | package

package
  : PACKAGE basic_type package_block
  | PACKAGE basic_type ':' opt_descriptors package_block

package_block
  : '{' opt_declarations '}'

opt_declarations
  :	/* Empty */
  |	declarations

declarations
  : declarations declaration
  | declaration

declaration
  : has
  | sub
  | enumeration
  | our ';'
  | use
  | begin_block

begin_block
  : BEGIN block
    
use
  : USE basic_type ';'
  | USE basic_type '(' opt_sub_names ')' ';'

require
  : REQUIRE basic_type

enumeration
  : ENUM enumeration_block

enumeration_block 
  : '{' opt_enumeration_values '}'

opt_enumeration_values
  :	/* Empty */
  |	enumeration_values
    
enumeration_values
  : enumeration_values ',' enumeration_value 
  | enumeration_values ','
  | enumeration_value
  
enumeration_value
  : sub_name
  | sub_name ASSIGN CONSTANT

our
  : OUR PACKAGE_VAR_NAME ':' opt_descriptors type
has
  : HAS field_name ':' opt_descriptors type ';'

sub
  : opt_descriptors SUB sub_name ':' type_or_void '(' opt_args opt_vaarg')' block
  | opt_descriptors SUB sub_name ':' type_or_void '(' opt_args opt_vaarg')' ';'

anon_sub
  : opt_descriptors SUB ':' type_or_void '(' opt_args opt_vaarg')' block
  | '[' args ']' opt_descriptors SUB ':' type_or_void '(' opt_args opt_vaarg')' block

opt_args
  :	/* Empty */
  |	args
  | invocant
  | invocant ',' args

args
  : args ',' arg
  | arg

arg
  : var ':' type

opt_vaarg
  : /* Empty */
  | DOT3

invocant
  : var ':' SELF

opt_descriptors
  :	/* Empty */
  |	descriptors
    
descriptors
  : descriptors DESCRIPTOR
  | DESCRIPTOR

opt_statements
  :	/* Empty */
  |	statements
    
statements
  : statements statement 
  | statement

statement
  : if_statement
  | for_statement
  | while_statement
  | block
  | switch_statement
  | case_statement
  | default_statement
  | eval_block
  | if_require_statement
  | expression ';'
  | LAST ';'
  | NEXT ';'
  | RETURN ';'
  | RETURN expression ';'
  | CROAK ';'
  | CROAK expression ';'
  | weaken_field ';'
  | unweaken_field ';'
  | ';'

for_statement
  : FOR '(' opt_expression ';' term ';' opt_expression ')' block

while_statement
  : WHILE '(' term ')' block

switch_statement
  : SWITCH '(' expression ')' block

case_statement
  : CASE expression ':'

default_statement
  : DEFAULT ':'

if_require_statement
  : IF '(' require ')' block

if_statement
  : IF '(' term ')' block else_statement
  | UNLESS '(' term ')' block else_statement

else_statement
  : /* NULL */
  | ELSE block
  | ELSIF '(' term ')' block else_statement

block 
  : '{' opt_statements '}'

eval_block
  : EVAL block ';'

opt_expressions
  :	/* Empty */
  |	expressions
term
  : expression
  | condition

opt_expression
  : /* Empty */
  | expression

expression
  : var
  | EXCEPTION_VAR
  | package_var_access
  | CONSTANT
  | UNDEF
  | call_sub
  | field_access
  | array_access
  | convert
  | new
  | array_init
  | array_length
  | string_length
  | my_var
  | binary_op
  | unary_op
  | ref
  | deref
  | assign
  | inc
  | dec
  | '(' expressions ')'
  | CURRENT_PACKAGE

expressions
  : expressions ',' expression
  | expressions ','
  | expression

unary_op
  : '+' expression %prec PLUS
  | '-' expression %prec MINUS
  | BIT_NOT expression

inc
  : INC expression
  | expression INC

dec
  : DEC expression
  | expression DEC

binary_op
  : expression '+' expression
  | expression '-' expression
  | expression MULTIPLY expression
  | expression DIVIDE expression
  | expression REMAINDER expression
  | expression BIT_XOR expression
  | expression '&' expression
  | expression BIT_OR expression
  | expression SHIFT expression
  | expression '.' expression

condition
  : comparison_op
  | logical_op
  | isweak_field ';'
  | '(' condition ')'

comparison_op
  : num_comparison_op
  | str_comparison_op
  | isa

num_comparison_op
  : expression NUMEQ expression
  | expression NUMNE expression
  | expression NUMGT expression
  | expression NUMGE expression
  | expression NUMLT expression
  | expression NUMLE expression

str_comparison_op
  : expression STREQ expression
  | expression STRNE expression
  | expression STRGT expression
  | expression STRGE expression
  | expression STRLT expression
  | expression STRLE expression
    
isa
  : expression ISA type

logical_op
  : term LOGICAL_OR term
  | term LOGICAL_AND term
  | LOGICAL_NOT term

assign
  : expression ASSIGN expression
  | expression SPECIAL_ASSIGN expression

new
  : NEW basic_type
  | NEW array_type_with_length
  | anon_sub

array_init
  : '[' opt_expressions ']'

convert
  : '(' type ')' expression %prec CONVERT

array_access
  : expression ARROW '[' expression ']'
  | array_access '[' expression ']'
  | field_access '[' expression ']'

call_sub
  : NAME '(' opt_expressions  ')'
  | basic_type ARROW sub_name '(' opt_expressions  ')'
  | basic_type ARROW sub_name
  | expression ARROW sub_name '(' opt_expressions ')'
  | expression ARROW sub_name
  | expression ARROW '(' opt_expressions ')'

field_access
  : expression ARROW '{' field_name '}'
  | field_access '{' field_name '}'
  | array_access '{' field_name '}'

weaken_field
  : WEAKEN field_access

unweaken_field
  : UNWEAKEN field_access

isweak_field
  : ISWEAK field_access

array_length
  : '@' expression
  | '@' '{' expression '}'
  | SCALAR '@' expression
  | SCALAR '@' '{' expression '}'

string_length
  : LENGTH expression
    
deref
  : DEREF var

ref
  : REF var

my_var
  : MY var ':' type
  | MY var

var
  : VAR_NAME

package_var_access
  : PACKAGE_VAR_NAME

type
  : basic_type
  | array_type
  | ref_type

basic_type
  : NAME
  | BYTE
  | SHORT
  | INT
  | LONG
  | FLOAT
  | DOUBLE
  | OBJECT
  | STRING

ref_type
  : basic_type '&'

array_type
  : basic_type '[' ']'
  | array_type '[' ']'

array_type_with_length
  : basic_type '[' expression ']'
  | array_type '[' expression ']'

type_or_void
  : type
  | VOID

field_name
  : NAME

sub_name
  : NAME

opt_sub_names
  :	/* Empty */
  |	sub_names

sub_names
  : sub_names ',' sub_name
  | sub_name

%%

コメント

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

# コメント

POD

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

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

=pod

複数行
コメント

=cut
=head1

複数行
コメント

=cut

パッケージ

パッケージの定義

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

package パッケージ名 {

}

パッケージ名は「大文字」で始まる必要があります。パッケージ名には「::」を使用することができます。

Foo Foo::Bar Foo::Bar::Baz

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

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

}

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

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

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

}

パッケージデスクリプタ

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

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

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

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

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

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

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

デストラクタ

パッケージがクラス型である場合は、デストラクタを定義することができます。

デストラクタとは、オブジェクトが解放されるときに実行される特別なサブルーチンのことです。

デストラクタの名前は「DESTROY」でなければなりません。

デストラクタの戻り値の型は、void型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

デストラクタの引数は、ひとつで、self型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

sub DESTROY : void ($self : self) {
  
}

デストラクタの中で例外の発生が起こった場合は、プログラムは終了せず、例外メッセージが、標準エラーに出力されます。

デストラクタのサンプル

デストラクタのサンプルです。

package Foo {
  sub new : Foo {
    return new Foo;
  }
  
  sub DESTROY : void ($self : self) {
    print("DESTROY");
  }
}

モジュール

モジュールの概要

モジュールとは、SPVMのソースコードとして読み込むことができるひとつのファイルのことをいいます。

# lib/path/Foo/Bar.spvm
package Foo::Bar {

}

モジュールには、複数のパッケージを含むことができます。

# lib/path/Foo/Bar.spvm
package Foo::Bar {

}

package Foo::Bar::Baz {

}

モジュールのファイル名

モジュールは、モジュールの読み込みパスに、以下のファイル名で配置される必要があります。

「::」を「/」に変更。末尾に「.spvm」をつける。

Foo.spvm
Foo/Bar.spvm
Foo/Bar/Baz.spvm

モジュールの読み込み

モジュールを読み込むには、useキーワードを使用します。

use Foo;
use Foo::Bar;

モジュールはコンパイル時に読み込まれます。

モジュールが存在しなかった場合は、コンパイルエラーになります。

useキーワードは、パッケージの定義の直下で定義する必要があります。

package Foo {
  use Foo;
}

選択的なモジュールの読み込み

SPVMでは、モジュールが、検索パスに存在する場合だけ読み込み、そうでない場合は、ブロックの内部が存在しないことにできるif require文があります。これはC言語の「#ifdef」の一部の機能を実現するために設計されました。

if (require Foo) {
  
}

if require文では、elsifやelseを続けることができないので注意してください。

一つの例を見てみましょう。以下の例で、Fooが存在しない場合は、コンパイルエラーにはならず、ifブロックの中身が存在しないことになります。そのため「my $foo = new Foo;」は、存在しないことになっているので、コンパイルエラーになりません。

if (require Foo) {
  my $foo = new Foo;
}

ひとつのテクニックとして、if require構文とインターフェース型無名サブルーチンを使うと、モジュールを選択的に利用することができます。以下の例では、Foo->method1とBar->mehtod2を選択的に選んで実行しています。

package SomeInterface : interface_t {
  sub foo : int ($self : self, $arg : int);
}

# 別の関数内
my $interface : SomeInterface;

unless ($inteface) {
  if (require Foo) {
    $interface = sub : int ($self : self, $arg : int) {
      Foo->method1($arg);
    };
  }
}

unless ($inteface) {
  if (require Bar) {
    $interface = sub : int ($self : self, $arg : int) {
      Bar->method2($arg);
    }
  }
}

my $result = $interface->foo(3);

パッケージ変数

パッケージ変数の定義

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

「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;
}

パッケージ変数の初期値

パッケージ変数は、コンパイルが終了して、実行時に入る前に、型の初期値で初期化されます。

この初期値は、BEGINブロックを使うことで、変更することができます。

package Foo {
  our $VAR : int;
  
  BEGIN {
    $VAR = 3;
  }
}

パッケージ変数へのアクセス

パッケージ変数へのアクセスとは、パッケージ変数にアクセスして、値を取得したり、設定したりする操作のことです。

パッケージ変数の値の取得については、パッケージ変数の値の取得を見てください。

パッケージ変数の値の設定については、パッケージ変数の値の設定を見てください。

フィールド

フィールドの定義

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

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

has フィールド名 : 型名;

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

フィールド定義には「型名」が必要です。型名には「数値型」と「オブジェクト型」を指定できます。

フィールド名は、1文字以上の「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;
}

フィールドへのアクセス

フィールドへのアクセスとは、フィールドにアクセスして、値を取得したり、設定したりする操作のことです。また、以下の記述そのものを指します。

インボカント->{フィールド名}

フィールドへのアクセスは、一種類の構文で、三つの異なる意味を持ちます。

1. クラス型のフィールドへのアクセス

クラス型を元にオブジェクトの生成が行われた場合は、オブジェクトからフィールドにアクセスすることができます。

my $point = new Point;
$point->{x} = 1;

クラス型のフィールドの取得については、クラス型のフィールドの値の取得を見てください。

クラス型のフィールドの設定については、クラス型のフィールドの値の設定を見てください。

2. 値型のフィールドへのアクセス

2. 値型におけるフィールドは、値型の変数の宣言をすれば、その変数からフィールドにアクセスできます。これは、レキシカル変数領域におけるアクセスです。

my $z : SPVM::Complex_2d;
$z->{re} = 1;
$z->{im} = 3;

値型のフィールドの取得については、値型のフィールドの値の取得を見てください。

値型のフィールドの設定については、値型のフィールドの値の設定を見てください。

3. デリファレンスによる値型のフィールドのアクセス

値型の変数に対するリファレンス型から、デリファレンスを行って直接に値型のフィールドにアクセスできます。

my $z : SPVM::Complex_2d;
my $z_ref = \$z;
$z_ref->{re} = 1;
$z_ref->{im} = 3;

デリファレンスによる値型のフィールドの取得については、デリファレンスによる値型のフィールドの取得を見てください。

デリファレンスによる値型のフィールドの設定については、デリファレンスによる値型のフィールドの設定を見てください。

サブルーチン

サブルーチンの定義

「sub」キーワードを使用してサブルーチンを定義することができます。

sub サブルーチン名 : 戻り値の型名 (引数名1 : 引数の型名1, 引数名2 : 引数の型名2, 以下続く) {
  
}

サブルーチンの定義は「パッケージ定義」の直下で行う必要があります。

サブルーチン名は、1文字以上の「a~z」「A~Z」「0~9」「_」で構成する必要があります。先頭は、数字から始めることはできません。連続した「_」を使用することはできません。

サブルーチン名には、キーワードと同じ名前を使用することができます。

戻りの型名には、「void型」「数値型」「オブジェクト型」を指定することができます。

サブルーチンの定義には「戻り値の型名」と「0個以上の引数の定義」が必要です。「引数の定義」は「引数名」と「引数の型名」からなります。

引数名は、変数名でなければなりません。

引数の型名には、「数値型」「オブジェクト型」「リファレンス型」を指定することができます。

サブルーチンのブロックの中には、0個以上のステートメントを記述できます。

サブルーチンの定義には、デスクリプタを合わせて指定することができます。複数のデスクリプタを空白を使って並べることができます。

デスクリプタ名 sub サブルーチン名 : 戻り値の型名 (引数の変数名1 : 引数の型名1, 引数の変数名2 : 引数の型名2, 以下続く) {
  
}

サブルーチンデスクリプタ

サブルーチンで指定できるデスクリプタの一覧です。

デスクリプタ名 役割
native このサブルーチンは、ネイティブサブルーチンです。
precompile このサブルーチンはプリコンパイルされます。

「native」と「precompile」の両方のデスクリプタが指定された場合は、コンパイル時エラーが発生します。

引数の型名の後ろに「...」を続けると、可変長引数となります。最後の引数のみ可変長引数にすることができます。

# 可変長引数の定義
sub サブルーチン名 : 戻り値の型名 (引数名1 : 引数の型名1, 引数名2 : 引数の型名2...) {
  
}

可変長引数のサンプル

可変長引数のサンプルです。

# 可変長引数を持つサブルーチンの定義
sub sprintf : string ($format : string, $values : object[]...) {
  
}

# 可変長引数を持つサブルーチンの呼び出し
foo("aaa %d %f", SPVM::Int->new(1), SPVM::Double->new(2.0));

可変長引数を持つサブルーチンに配列を渡すと、配列のまま呼び出す事ができます。

foo("aaa %d %f", [(object)SPVM::Int->new(1), SPVM::Double->new(2.0)]);

もし、配列型の値を、可変長引数の個々の要素として扱いたい場合は、配列型ではない型にキャストしてください。

foo("aaa %d %f", (object)[(object)SPVM::Int->new(1), SPVM::Double->new(2.0)]);

定義されたサブルーチンは、呼び出すことができます。サブルーチンの呼び出しについては、サブルーチンの呼び出しを見てください。

定数サブルーチン

数値型の戻り値を持つサブルーチンで、戻り値が定数であるサブルーチンを定数サブルーチンといいます。

sub foo : int () { return 5; }
sub foo : long () { return 5L; }
sub foo : float () { return 5.0f; }
sub foo : double () { return 5.0; }

定数サブルーチンは、インライン展開されます。

SPVMは定数畳み込み最適化を行わないので、定数が演算されている場合は、定数サブルーチンにならず、インライン展開されないことに注意してください。

# 定数サブルーチンではなくインライン展開されない
sub foo : int () { return 5 + 3; }

無名サブルーチン

無名サブルーチンとは、名前を持たないサブルーチンのことです。

sub : 型名 ($self : self, 引数1, 引数2, ..., 引数n) {
  
}

無名サブルーチンを定義すると、内部的に、パッケージの定義がおこなわれ、そのパッケージを元にしたオブジェクトが生成され、として返されます。以下のように変数に代入することが可能です。

my $anon_sub = sub : 型名 ($self : self, 引数1, 引数2, ..., 引数n) {
  
};

無名サブルーチンは、メソッドでなければなりません。

無名サブルーチンのサンプル

my $comparator = sub : int ($self : self, $x1 : object, $x2 : object) {
  
}

無名サブルーチンは、呼び出すことができます。無名サブルーチンの呼び出しについては、サブルーチンの呼び出しを見てください。

メソッド

メソッドとは、第一引数にself型を持つサブルーチンのことです。

sub サブルーチン名 : 型名 ($self : self, 引数1 : 型1, 引数2 : 型2, ..., 引数n : 型n) {
  
}

メソッドは、オブジェクトの生成によって生成されたオブジェクトから呼び出すことができます。メソッドの呼び出しについては、サブルーチンの呼び出しを見てください。

インボカント

インボカントとは、self型が指定された第一引数のことをいいます。

sub サブルーチン名 : 型名 ($self : self, 引数1 : 型1, 引数2 : 型2, ..., 引数n : 型n) {
  
}

上記の例では$selfは、インボカントと呼ばれます。

シグネチャ

シグネチャとは、サブルーチンの戻り値と引数を次の規則で並べたものをいいます。引数は、存在しなくても構いません。間に空白を含むことはできません。

1. 戻り値の型名

2. (

3. 引数1,引数2, 引数3, 引数n

4. )

シグネチャのサンプルです。

# サブルーチン定義
sub foo : int ($num1 : double, $num2 : long[])

# シグネチャ
int(double,long[])

# サブルーチン定義
sub foo : void ()

# シグネチャ
void()

シグネチャは、SPVMを記述しているときには、意識する必要はありません。ネイティブからSPVMのサブルーチンを呼び出すときに使用します。

サブルーチンのコールスタック

サブルーチンのコールスタックとは、サブルーチンの呼び出しが行われるときに、確保されるメモリ領域のことを指します。

サブルーチンのコールスタックには、以下の情報が保存されます。

1. レキシカル変数のためのメモリ領域

2. モータル変数の場所

列挙

列挙の定義

列挙はint型の定数を定義したい場合に利用します。連続したint型の定数を簡単に定義できます。「enum」キーワードを使って定義します。

enum {
  FLAG1,
  FLAG2,
  FLAG3
}

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

package Foo {
  enum {
    FLAG1,
    FLAG2,
    FLAG3
  }
}

最初の値は「0」から始まります。値は「1」づつインクリメントされます。この例の場合は「FLAG1」は「0」、「FALG2」は「1」、「FLAG3は「2」になります。

列挙の末尾の要素の後ろには「,」をつけることができます。

enum {
  FLAG1,
  FLAG2,
  FLAG3,
}

列挙はint型を戻り値とする「定数サブルーチン」のエイリアスです。次のサブルーチンの定義と等価です。

sub FLAG1 : int () { return 0; }
sub FLAG2 : int () { return 1; }
sub FLAG3 : int () { return 2; }

enumの要素には、int型の値を設定することができます。

enum {
  FLAG1,
  FLAG2 = 4,
  FLAG3,
}

上記の場合は「FLAG1」は「0」、「FALG2」は「4」、「FLAG3」は「5」になります。

enumの定義が不正な場合は、コンパイル時エラーが発生します。

列挙の呼び出し

列挙は、定数サブルーチンのエイリアスなので、サブルーチン呼び出しとまったく同じ方法で呼び出すことができます。

my $flag1 = Foo->FLAG1;
my $flag2 = Foo->FLAG2;
my $flag3 = Foo->FLAG3;

switch文のcase文において利用することもできます。

switch ($num) {
  case Foo->FLAG1:
  
    last;
  case Foo->FLAG2:
    
    last:
  case Foo->FLAG3:
  
    last:
  default:
  
}

BEGINブロック

BEGINブロックとは、コンパイル時が終了し、実行時に入る前に、実行されるブロックのことです。

BEGINキーワードを使用してBEGINブロックを定義することができます。

BEGIN {

}

BEGINブロックは、パッケージの定義の直下にある必要があります。

package Foo {
  BEGIN {

  }
}

BEGINブロックの中には、0個以上のを書くことができます。

BEGIN {
  my $foo = 1 + 1;
  my $bar;
}

return文を書くことはできません。BEGINブロックは、引数がなく、戻り値がvoidのサブルーチンとして定義されます。

BEGINブロックは、いくつでも定義できます。

BEGINブロックの実行順序は、保証されません。他のパッケージにBEGINブロックが定義されている場合は、そのBEGINブロックが先に実行されることを想定しないでください。

BEGINブロックの一般的な用途は、パッケージ変数を初期化することです。

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

レキシカル変数

レキシカル変数の宣言

レキシカル変数とは、スコープブロックの中で利用できる、スコープを持つ変数のことです。C言語のローカル変数に該当します。

{
  my $num : int;
}

レキシカル変数は「my」キーワードによって宣言します。「:」の後ろに「」を指定する必要があります。

my $num : int;

レキシカル変数は、レキシカル変数の初期値によって初期化されます。

# 0で初期化される
my $num : int;

# 0で初期化される
my $num : double;

# undefで初期化される
my $point : Point;

# reは0, imは0で初期化される
my $z : SPVM::Complex_2d;

レキシカル変数の宣言と同時に初期化を行うことができます。

# 1で初期化される
my $num : int = 1;

# 2.5で初期化される
my $num : double = 2.5;

# Pointオブジェクトで初期化される
my $point : Point = new Point;

型推論を使うと、宣言するときに、型の指定を省略することができます。

# int
my $num = 1;

# double
my $num = 1.0;

レキシカル変数の宣言は、レキシカル変数の値を返します。これは、として利用できます。

my $ppp = my $bar = 4;

if (my $bar = 1) {
  
}

while (my $bar = 1) {
  
}

レキシカル変数の宣言は、レキシカル変数がオブジェクト型であった場合に、レキシカル変数を、モータル変数として、実行時に登録します。モータル変数として登録されたレキシカル変数に代入されているオブジェクトは、スコープの末尾で、リファレンスカウントが自動的に1減らされます。

レキシカル変数の初期値

レキシカル変数は、型の初期値で初期化されます。

レキシカル変数へのアクセス

レキシカル変数へのアクセスとは、レキシカル変数にアクセスして、値を取得したり、設定したりする操作のことです。

レキシカル変数の値の取得については、レキシカル変数の値の取得を見てください。

レキシカル変数の値の設定については、レキシカル変数の値の設定を見てください。

ブロック

「{」と「}」で囲まれた部分のことをブロックと呼びます。

# ブロック
{

}

ブロックの中には、スコープを作るスコープブロックがあります。

スコープ

スコープの役割

スコープとはスコープブロックに囲まれた範囲のことをいいます。

# スコープブロック
{

}

スコープの中で宣言されたレキシカル変数は、実行時に、宣言されている位置で、モータルなレキシカル変数として登録されます。

{
  # レキシカル変数をモータルとして登録
  my $num = new Foo;
}

モータルなレキシカル変数に代入されたオブジェクトが、未定義値ではない場合は、リファレンスカウントが1増やされます。

スコープの終わりに到達すると、モータルなレキシカル変数に代入されているオブジェクトは、未定義値でない場合、リファレンスカウントが1減らされ、0になった場合は、解放されます。

スコープブロック

スコープブロックとは、スコープを作るブロックのことです。スコープブロックには、0個以上のを記述することができます。

# スコープブロック
{
  my $num = 1;
  $num++;
}

スコープブロックの一覧

単純なブロック

# 単純なブロック
{

}

サブルーチンブロック

# サブルーチンのブロック
sub : int () {

}

evalブロック

# evalブロック
eval {

}

ifブロック

# ifブロック
if (条件1) {

}

elsifブロック

#elsifブロック
elsif (条件2) {

}

elseブロック

# elseブロック
else {

}

forブロック

# for ブロック
for (my $i = 0; $i < 3; $i++) {

}

whileブロック

# whileブロック
while (条件) {

}

switchブロック

switch (条件) {

}

例外処理

例外処理の概要

SPVMは例外処理の仕組みを持っています。例外処理は、例外の発生と例外のキャッチからなります。

例外の発生

例外の発生を行うには、croak文を使用します。

croak 式;

式は、文字列互換型でなければなりません。

croak文が実行されると、スタックトレースと式で指定された文字列を表示して、プログラムは終了します。スタックトレースは、パッケージ名、サブルーチン名、ファイル名、行番号を含みます。ファイル名は、モジュールをロードしたパスからの相対ファイル名です。

Error
  from TestCase::Minimal->sum2 at TestCase/Minimal.spvm line 1640
  from TestCase->main at TestCase.spvm line 1198

例外のキャッチ

例外のキャッチとは、例外がスローされた場合に、プログラムが終了するのを止め、エラーメッセージを取得できる機能のことです。

例外のキャッチは、evalブロック文を使って行います。evalブロック文は、末尾にセミコロンが必要なので気をつけてください。

eval {
  # 例外を投げる可能性のある処理
};

evalブロックで例外をキャッチした場合は、プログラムの終了は止められ、例外変数例外の発生で指定したメッセージが代入されます。

例外変数

例外変数は「$@」で表現されるグローバル変数のことです。

$@

例外変数は、本当の意味では、グローバル変数ではありません。例外変数は、実は、スレッド変数です。SPVMはシングルスレッド設計なので、例外変数は、グローバル変数として振舞うように見えます。ネイティブにおいてスレッドを利用する場合に、違いがあります。

例外変数の値の取得については、例外変数の値の取得を見てください。

例外変数の値の設定については、例外変数の値の設定を見てください。

ウィークリファレンス

ウィークリファレンスとは、リファレンスカウントを増やさない参照のことです。ウィークリファレンスを使用すると、循環参照の問題を解決することができます。

SPVMは、リファレンスカウント型のGCを持ちます。リファレンスカウント型のGCにおいては、リファレンスカウントが0になった場合に、オブジェクトは自動的に開放されますが、循環参照が発生した場合は、リファレンスカウントが0にならず、オブジェクトが自動的に解放されません。

オブジェクトのフィールドが、循環参照している場合のサンプルです。

{
  my $foo = new Foo;
  my $bar = new Bar;

  $foo->{bar} = $bar;
  $bar->{foo} = $foo;
}

この場合は、スコープが終了しても、両方のオブジェクトは解放されません。なぜなら、循環参照が発生しており、リファレンスカウントが0にならないためです。

ウィークリファレンスは、リファレンスカウントGCを持つプログラミング言語において、循環参照が発生した場合に、オブジェクトの破棄を正しく行うための機能です。

このような場合は、ひとつのフィールドを、weaken文を使って、ウィークリファレンスに設定することで、正しく解放することができます。

{
  my $foo = new Foo;
  my $bar = new Bar;

  $foo->{bar} = $bar;
  $bar->{foo} = $foo;
  
  weaken $foo->{bar};
}

weaken文が実行される前の、$fooのリファレンスカウントは2、$barのリファレンスカウントは2です。

もしweaken文がない場合は、スコープが終了したとしても、$fooのリファレンスカウントも、$barのリファレンスカウントも0にならず、解放されません。

weaken文が実行されるとは、$fooのリファレンスカウントは2、$barのリファレンスカウントは1になります。

スコープが終了すると、$barのリファレンスカウントが1減らされ0になるので、正しく解放されます。

3つの循環参照の場合でも、ひとつのフィールドにウィークリファレンスを設定すれば、正しく解放できます。

{
  my $foo = new Foo;
  my $bar = new Bar;
  my $baz = new Baz;

  $foo->{bar} = $bar;
  $bar->{baz} = $baz;
  $baz->{foo} = $foo;
  
  weaken $foo->{bar};
}

ウィークリファレンス関連の文法として、ウィークリファレンスを解除できるweaken文と、フィールドがウィークリファレンスかどうかを確認できるisweak演算子があります。

インターフェース

SPVMにおけるインターフェースとは、実装を持たないメソッドが一つだけ定義されたパッケージ型のことをいいます。パッケージの定義においてinterface_tデスクリプタを指定すると、インターフェースになります。

インターフェースは、インターフェース型と同じ意味です。

インターフェース型の目的は、異なるオブジェクトが、同じメソッド定義を持つときに、どちらも代入できる型を提供することにあります。C言語の関数ポインタに該当する機能をSPVMにおいて実現するものと考えてください。

package Foo1 : public {
  sub bar : int ($self : self, $num : int) {
    return $num * 2;
  }
  
  sub xxx : int () {
    
  }
}

package Foo2 : public {
  sub bar : int ($self : self, $num : int) {
    return $num * 3;
  }
  sub yyy : int () {
    
  }
}

package FooInterface : interface_t {
  sub bar : int ($self : self, $num : int);
}

Foo1とFoo2は同じメソッド定義「sub bar : int ($self : self, $num : int)」を持ちます。ここで、選択的にFoo1またはFoo2のメソッドを呼び出したいとします。

この場合に、同じメソッド定義を持つインターフェース型FooInterfaceを定義すると、この型にどちらのオブジェクトも代入できます。そして、このオブジェクトから、メソッドを呼び出すことができます。

my $foo1 = new Foo1;
my $foo2 = new Foo2;

my $foo : FooInterface;

my $flag = 1;
if ($flag) {
  $foo = $foo1;
}
else {
  $foo = $foo2;
}

my $ret = $foo->bar(2);

$flagが1の場合は、Foo1のbarが、そうでない場合はFoo2のbarが呼び出されます。

インターフェース型の詳細については、インターフェース型を見てください。

例外処理の概要

配列

配列の概要

配列とは、複数の値の連続領域を表現するためのデータ構造です。

配列には次の種類があります。

数値型の配列とは、数値型の値が連続に並んだ配列のことです。

オブジェクト型の配列とは、オブジェクト型の値が連続に並んだ配列のことです。

値型の配列とは、値型の値が連続に並んだ配列のことです。

配列の生成

配列を生成については、配列の生成を見てください。

配列へのアクセス

配列へのアクセスとは、配列にアクセスして、値を取得したり、設定したりする操作のことです。また、以下の記述そのものを指します。

配列->[添え字]

配列の取得については、配列の要素の値の取得を見てください。

配列の設定については、配列の要素の値の設定を見てください。

値型の定義

SPVMには、他の言語ではあまり見ることのない値というものがあります。

値は、連続した数値を表現します。たとえば、連続した32bit符号付整数が3つであるとか、連続した倍精度浮動小数点が2つなどです。3次元の点、複素数、四元数などを表現することが想定されています。

値を使用するには、最初に、値型を定義します。値型は、パッケージの定義において、value_tデスクリプタを指定することで定義できます。

# 連続した32bit符号付整数が3つ
package Point_3i : value_t {
  has x : int;
  has y : int;
  has z : int;
}

連続した倍精度浮動小数点が2つ
package Complex_2d : value_t {
  re : double;
  im : double;
}

すべてのフィールドは同じ数値型でなければなりません。

値型の末尾は「_」「フィールドの個数」「値型のサフィックス」でなければなりません。

値型のサフィクスの一覧

数値型 サフィックス
byte b
short s
int i
long l
float f
double d

サフィックスは、実際に定義されている数値型と対応している必要があります。

数値型の定義において、フィールドの個数の最大は16です。

不正な値型の定義の場合は、コンパイル時エラーが発生します。

値型は、レキシカル変数の宣言の型として利用することができます。

値型は、サブルーチンの定義における引数の型として利用することができます。

値型は、サブルーチンの定義における戻り値の型として利用することができます。

値型は配列型基本型として利用することができます。

my $points = new Point_3i[5];

値型は、フィールドの型として利用することはできません。

値型は、パッケージ変数の型として利用することはできません。

値型の値に対しては、リファレンスを生成することができます。

my $point : Point_3i;
my $point_ref = \$point;

値型の値に、未定義値を代入することはできません。代入しようとした場合は、コンパイル時エラーが発生します。

値の配列

値の配列の概要

値型の値は、配列にすることができます。

my $points = new Point_3i[5];

my $zs = new Complex_2d[5];

値の配列は、値型の値が、連続したデータ構造をとります。配列の要素は、オブジェクトではなく、数値がメモリ上に一直線に並んで、配置されます。

たとえば、Point_3i型の5の長さの配列であれば、Point_3i型は、int型の数値が3つ並んだ型で、長さが5なので、「5 * 3 = 15」個のint型の数値が、メモリ上に一直線に並んだデータになります。

値の配列の型は、配列型です。

値の配列の要素の型は、値型です。

値の配列へのアクセス

値の配列へのアクセスとは、値の配列にアクセスして、値を取得したり、設定したりする操作のことです。また、以下の記述そのものを指します。

配列->[添え字]

値の配列へのアクセスは、配列のアクセスと同じ構文で行うことができます。

配列の取得については、配列の要素の値の取得を見てください。

配列の設定については、配列の要素の値の設定を見てください。

値型の使用

値型を使用するにはuse文を使って、モジュールをロードします。

use Point_3i;
use Complex_2d;

次に、レキシカル変数の宣言を行います。レキシカル変数の宣言を行うと、連続した領域が、サブルーチンのコールスタック上に確保されます。フィールドのすべての値は、型の初期値で初期化されます。

my $point : Point_3i;
my $z : Complex_2d;

値型の値は、オブジェクトではないので、オブジェクトの生成の構文では生成できないことに注意してください。

値型のフィールドへのアクセス

値型のフィールドへのアクセスとは、値型のフィールドにアクセスして、値を取得したり、設定したりする操作のことです。また、以下の記述そのものを指します。

インボカント->{フィールド名}

値型のフィールドの取得については、値型のフィールドの値の取得を見てください。

値型のフィールドの設定については、値型のフィールドの値の設定を見てください。

値型のフィールドの値の取得・設定のサンプル

値型のフィールドの値の取得・設定のサンプルです。

$ival2->{x} = 1;
my $y = $ival2->{y};

$dval4->{t} = 2.5;
my $z = $dval4->{z};

配列の生成

算術演算子の両辺、サブルーチンの引数、ifの条件部などになることができる文法の最小の単位を「項」と呼びます。

「項」には、「」と、条件部のみに記述できる「条件」があります。

式と条件の違いは、条件は、式として利用できないということだけです。

項は、代入演算子を除く、ほとんどの場合に、左から右の順番で評価されます。たとえば、以下の例では、3 * 3 が実行され結果は9になります。

  my $i = 2;
  my $j = ($i = 3)* $i;

exceptionサブルーチンが例外を発生させた場合は、$jの値は、10のままです。

  my $j = 10;
  eval {
    excpetion() / ($j = 3);
  };
}

リファレンス

リファレンスの概要

リファレンスとは、レキシカル変数のアドレスのことをいいます。リファレンスは、C言語のポインタに該当する機能を実現するために、設計されました。

レキシカル変数のアドレスは、リファレンス演算子を使用して取得できます。リファレンス型は、数値型あるいは値型の後ろに「&」を続けたものになります。

# 数値型の変数のリファレンス
my $num : int;
my $num_ref : int& = \$num;

# 値型の変数のリファレンス
my $point : Point_3d;
my $point_ref : Point_3d& = \$point;

$num_refの値は、レキシカル変数「$num」のアドレスになります。$point_refの値は、レキシカル変数「$point」のアドレスになります。

リファレンス演算子の対象は、数値型あるいは値型の値が格納された変数だけです。オブジェクト型の変数や定数に対して、使うことはできません。

リファレンス型の値は、サブルーチンの引数として利用することができます。

# サブルーチンの定義
sub sum : void ($out_ref : int&, $in1 : int, $in2 : int) {
  $$out_ref = $in1 + $in2;
}

# サブルーチンの利用
my $num1 = 1;
my $num2 = 2;
my $out : int;
my $out_ref = \$out;
sum($out_ref, $num1, $num2);

デリファレンス

デリファレンスとは、アドレスの場所にあるレキシカル変数の値を取得・設定する操作をいいます。

デリファレンスすることで、アドレスを通して値を取得したり、変更することができます。

# 数値型の値を取得
my $num2 = $$num_ref;

# 数値型の値を変更
$$num_ref = 3;

# 値型の値を取得
my $point2 = $$point_ref;

# 値型の値を設定
$$point_ref = $point2;

リファレンス型の対象が値型であった場合に、値型のフィールドの設定と取得は、アロー演算子で行うことができます。

# リファレンス型の対象が値型であった場合に、値型のフィールドを取得
my $x = $point_ref->{x};

# リファレンス型の対象が値型であった場合に、値型のフィールドを設定
$point_ref->{x} = 1;

数値のリファレンス型

数値のリファレンス型とは、数値型の変数に対するリファレンス型のことをいいます。

値のリファレンス型

値のリファレンス型とは、値型の変数に対するリファレンス型のことをいいます。

式とは

式とは、値の評価が可能なのことです。

式の一覧

レキシカル変数の値の取得

レキシカル変数の値の取得をするには、レキシカル変数自体を記述します。

$var

レキシカル変数の値の取得は、です。

レキシカル変数の値の設定

レキシカル変数の値の設定を行うには、代入演算子を使用します。

$var = 右式

代入が型の互換性を満たさない場合は、コンパイル時エラーが発生します。

レキシカル変数の値の設定は、設定後の値を返します。

レキシカル変数の値の設定は、です。

右式が、オブジェクト型であった場合は、オブジェクトのリファレンスカウントが1増やされます。

代入前に、すでに$varにオブジェクトが代入されていた場合は、そのオブジェクトのリファレンスカウントが1減らされます。

パッケージ変数の値の取得

パッケージ変数の値を取得するには以下の構文を使用します。

$クラス名::パッケージ変数名

自分自身のパッケージに属するパッケージ変数の場合は「クラス名::」を省略できます。

$パッケージ変数名

パッケージ変数の値の取得は、を返します。

定義されていないパッケージ変数の値を取得しようとした場合は、コンパイル時エラーが発生します。

パッケージの外側からプライベートなパッケージ変数にアクセスしようとした場合は、コンパイル時エラーが発生します。

パッケージ変数の値の取得のサンプル

package Foo {
  our $VAR : int;
  
  sub bar : int () {
    my $var1 = $Foo::VAR;
    my $var2 = $VAR;
  }
}

パッケージ変数の値の設定

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

$クラス名::パッケージ変数名 = 右式
自分自身のパッケージに属するパッケージ変数の場合は「クラス名::」を省略できます。

$パッケージ変数名 = 右式

パッケージ変数設定は、を返します。返される結果は、代入後のパッケージ変数の値です。

定義されていないパッケージ変数の値を設定しようとした場合は、コンパイル時エラーが設定します。

パッケージの外側からプライベートなパッケージ変数にアクセスしようとした場合は、コンパイル時エラーが発生します。

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

package Foo {
  our $VAR : int;
  
  sub bar : int () {
    $Foo::VAR = 1;
    $VAR = 3;
  }
}

例外変数の値の取得

例外変数の値を取得するには以下の構文を使用します。

$@

例外変数の値の取得は、を返します。

例外変数の値の取得は、文字列型の値を返します。

例外変数の値の取得のサンプル

例外変数の値の取得のサンプルです。

eval {
  foo();
};

if (my $message = $@) {
  
}

例外変数の値の設定

例外変数の値を設定するには以下の構文を使用します。

$@ = 右式

右式は文字列互換型でなければなりません。

例外変数の値の設定は、を返します。

例外変数の値の設定は、設定後の例外変数の値を返します。これは、文字列型です。

例外変数の値の設定のサンプル

例外変数の値の設定のサンプルです。

$@ = "Error";

クラス型のフィールドの値の取得

クラス型のフィールドの値の取得は、次のように記述します。

インボカント式->{フィールド名}

インボカント式は、クラス型の値でなければなりません。インボカント式が、値型の値であった場合は、値型のフィールドの値の取得になり、値のリファレンス型の値であった場合は、デリファレンスによる値型のフィールドの取得になり、それ以外の場合は、コンパイル時エラーが発生します。

フィールド名が、存在しない場合は、コンパイル時エラーが発生します。

クラス型のフィールドの値の取得は、オブジェクトに保存されているフィールドの値を返します。これはです。

返される値の型は、フィールドの型です。

クラス型のフィールドの値の取得のサンプル

クラス型のフィールドの値の取得のサンプルです。

my $point = Point->new;
my $x = $point->{x};

クラス型のフィールドの値の設定

クラス型のフィールドの値の設定は、次のように記述します。

インボカント式->{フィールド名} = 右式

インボカント式は、クラス型の値でなければなりません。インボカント式が、値型の値であった場合は、値型のフィールドの値の設定になり、値のリファレンス型の値であった場合は、デリファレンスによる値型のフィールドの設定になり、それ以外の場合は、コンパイル時エラーが発生します。

フィールド名が、存在しない場合は、コンパイル時エラーが発生します。

右式の型は、フィールドの型と同じでなければなりません。そうでない場合は、コンパイル時エラーが発生します。

クラス型のフィールドの値の設定は、設定後のオブジェクトに保存されているフィールドの値を返します。これはです。

返される値の型は、フィールドの型です。

右式が、オブジェクト型であった場合は、オブジェクトのリファレンスカウントが1増やされます。

代入前に、すでにフィールドにオブジェクトが代入されていた場合は、そのオブジェクトのリファレンスカウントが1減らされます。

クラス型のフィールドの値の設定のサンプル

クラス型のフィールドの値の設定のサンプルです。

my $point = Point->new;
$point->{x} = 1;

値型のフィールドの値の取得

値型のフィールドの値の取得は、次のように記述します。

インボカント式->{フィールド名}

インボカント式は、値型の値でなければなりません。インボカント式が、クラス型の値であった場合は、クラス型のフィールドの値の取得になり、値のリファレンス型の値であった場合は、デリファレンスによる値型のフィールドの取得になり、それ以外の場合は、コンパイル時エラーが発生します。

フィールド名が、存在しない場合は、コンパイル時エラーが発生します。

値型のフィールドの値の取得は、サブルーチンのコールスタックに保存されている該当するフィールドの値を返します。これはです。

返される値の型は、フィールドの型です。

値型のフィールドの値の取得のサンプル

値型のフィールドの値の取得のサンプルです。

my $z : SPVM::Complex_2d;
my $re = $z->{re};

値型のフィールドの値の設定

値型のフィールドの値の設定は、次のように記述します。

インボカント式->{フィールド名} = 右式

インボカント式は、値型の値でなければなりません。インボカント式が、クラス型の値であった場合は、クラス型のフィールドの値の設定になり、値のリファレンス型の値であった場合は、デリファレンスによる値型のフィールドの設定になり、それ以外の場合は、コンパイル時エラーが発生します。

フィールド名が、存在しない場合は、コンパイル時エラーが発生します。

右式の型は、フィールドの型と同じでなければなりません。そうでない場合は、コンパイル時エラーが発生します。

値型のフィールドの値の設定は、設定後のサブルーチンのコールスタックに保存されている該当するフィールドの値を返します。これはです。

返される値の型は、フィールドの型です。

値型のフィールドの値の設定のサンプル

値型のフィールドの値の設定のサンプルです。

my $z : SPVM::Complex_2d;
$z->{re} = 2.5;

デリファレンスによる値型のフィールドの値の取得

デリファレンスによる値型のフィールドの値の取得は、次のように記述します。

インボカント式->{フィールド名}

インボカント式は、値のリファレンス型の値でなければなりません。インボカント式が、クラス型の値であった場合は、クラス型のフィールドの値の取得になり、値型の値であった場合は、値型のフィールドの値の取得になり、それ以外の場合は、コンパイル時エラーが発生します。

フィールド名が、存在しない場合は、コンパイル時エラーが発生します。

デリファレンスによる値型のフィールドの値の取得は、サブルーチンのコールスタックに保存されている該当するフィールドの値を返します。これはです。

返される値の型は、フィールドの型です。

デリファレンスによる値型のフィールドの値の取得のサンプル

デリファレンスによる値型のフィールドの値の取得のサンプルです。

my $z : SPVM::Complex_2d;
my $z_ref = \$z;
my $re = $z_ref->{re};

デリファレンスによる値型のフィールドの値の設定

デリファレンスによる値型のフィールドの値の設定は、次のように記述します。

インボカント式->{フィールド名} = 右式

インボカント式は、値のリファレンス型の値でなければなりません。インボカント式が、クラス型の値であった場合は、クラス型のフィールドの値の設定になり、値型の値であった場合は、値型のフィールドの値の設定になり、それ以外の場合は、コンパイル時エラーが発生します。

フィールド名が、存在しない場合は、コンパイル時エラーが発生します。

右式の型は、フィールドの型と同じでなければなりません。そうでない場合は、コンパイル時エラーが発生します。

デリファレンスによる値型のフィールドの値の設定は、設定後のサブルーチンのコールスタックに保存されている該当するフィールドの値を返します。これはです。

返される値の型は、フィールドの型です。

デリファレンスによる値型のフィールドの値の設定のサンプル

デリファレンスによる値型のフィールドの値の設定のサンプルです。

my $z : SPVM::Complex_2d;
my $z_ref = \$z;
$z_ref->{re} = 2.5;

配列の要素の値の取得

配列の要素の値を取得するには、次のように記述します。

配列式->[インデックス式]

配列式は、配列型の値でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

インデックス式は、int型の値でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

配列の要素の値を取得は、指定したインデックスの要素の値を返します。これは、式です。

実行時に、配列式の値が、未定義値である場合は、実行時例外が発生します。

実行時に、インデックスの値が、0より小さい場合、あるいは、配列の最大のインデックスを超えている場合は、実行時例外が発生します。

配列の要素の値の取得のサンプル

配列の要素の値の取得のサンプルです。

my $nums = new int[3];
my $num = $nums->[1];

my $points = new Point[3];
my $point = $points->[1];

my $objects : oarray = $points;
my $object = (Point)$objects->[1];

配列の要素の値の設定

配列の要素の値を設定するには、次のように記述します。

配列式->[インデックス式] = 右式

配列式は、配列型の値でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

インデックス式は、int型の値でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

右式の型は、配列の要素の型と型の互換性がなければなりません。そうでない場合は、コンパイル時エラーが発生します。

配列の要素の値を設定は、設定後の値を返します。これは、式です。

実行時に、配列式の値が、未定義値である場合は、実行時例外が発生します。

実行時に、インデックスの値が、0より小さい場合、あるいは、配列の最大のインデックスを超えている場合は、実行時例外が発生します。

右式が、オブジェクト型であった場合は、オブジェクトのリファレンスカウントが1増やされます。

代入前に、すでに配列の要素にオブジェクトが代入されていた場合は、そのオブジェクトのリファレンスカウントが1減らされます。

配列の要素の値の設定のサンプル

配列の要素の値の設定のサンプルです。

my $nums = new int[3];
$nums->[1] = 3;

my $points = new Point[3];
$points->[1] = Point->new(1, 2);

my $objects : oarray = $points;
$objects->[2] = Point->new(3, 5);;

オブジェクトの生成

オブジェクトを生成するには、newキーワードと以下の構文をを使用します。

my $object = new パッケージ名;

指定されたパッケージは、クラス型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

フィールドは、すべて型の初期値で初期化されます。

オブジェクトの生成は、を返します。

生成直後のオブジェクトのリファレンスカウントは、0です。代入が実行されるタイミングで、リファレンスカウントが1増やされます。明示的な代入が行われない場合でも、一時変数が作成され、そこに代入されます。

オブジェクトの生成のサンプル

my $object = new Foo;

生成されたオブジェクトは内部的に次の情報を持っています。

配列の生成

配列を作成するには、newキーワードと以下の構文をを使用します。

my $object = new 型[要素数式];

型には、数値型オブジェクト型値型が指定できます。それ以外の型を指定した場合は、コンパイル時エラーが発生します。

要素数式は、int型以下の数値型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

要素数式に対して、単項数値拡大型変換が行われます。

要素数式で指定された長さの配列が生成されます。

配列の要素は、すべて型の初期値で初期化されます。

配列において、要素は、メモリ上に連続していることが保証されます。

配列の生成は、を返します。

配列は、オブジェクト型です。

配列の生成のサンプル

my $nums = new int[3];
my $objects = new Foo[3];
my $objects = new object[3];
my $values = new Complex_2d[3]

生成された配列は、内部的に次の情報を持っています。

多次元配列は、上記の構文を使用して、作成できます。要素は、型の初期値によって初期化されます。

my $nums = new int[][3];
my $nums = new int[][][3];

多次元配列の次元数の最大は、255です。

配列の初期化

SPVMには、配列の生成を簡単にするための配列の初期化の構文があります。式はなくてもかまいません。

[]
[式1, 式2, 式3]

配列の初期化は、式の要素数の長さを持った配列を返します。

配列の型は、式1の型を配列型にしたものです。要素が指定されない場合は、汎用オブジェクト型を配列型にしたものになります。

式2以降が、型の互換性を満たさない場合は、コンパイルエラーになります。

サブルーチンの呼び出し

サブルーチンの定義によって定義されたサブルーチンは呼び出すことができます。呼び出しには、2種類あって、関数の呼び出しメソッドの呼び出しがあります。

関数の呼び出し

関数の呼び出しとは、メソッドではないサブルーチンを呼び出す方法のことをいいます。メソッドかどうかの判断は、サブルーチンの定義において、第一引数にself型の引数が指定されていた場合が、メソッドになります。

関数の呼び出しは以下の方法で行うことができます。引数は、なくても構いません。最大で255個の引数を指定できます。

パッケージ名->サブルーチン名(引数1, 引数2, 引数3, ..., 引数n);

Perlにおいてクラスメソッドの呼び出しと呼ばれているものが、SPVMでは、関数の呼び出しと呼ばれていることに注意してください。

関数の呼び出しでは、パッケージ名を省略することもできます。

サブルーチン名(引数1, 引数2, 引数3, ..., 引数n);

どのパッケージのサブルーチンが呼び出されるかは、以下の順番によって決まります。

1. 現在のパッケージで定義されているサブルーチン、または現在のパッケージにインポートされたサブルーチン

2. 標準関数 - COREパッケージで定義されているサブルーチン

関数の呼び出しは、引数を受け取ります。引数の個数が、サブルーチンの定義で定義されている引数の個数と一致しない場合は、コンパイル時エラーが発生します。それぞれの引数の型が、サブルーチン定義で定義されている引数の型と型の互換性がない場合は、コンパイル時エラーが発生します。

関数の呼び出しは、戻り値がvoid型以外の場合は、戻り値を返します。

関数の呼び出しは、です。

サブルーチン呼び出しのサンプル

サブルーチン呼び出しのサンプルです。

my $ret = Foo->bar(1, 2, 3);
my $ret = bar(1, 2, 3);

メソッドの呼び出し

メソッドの呼び出しとは、メソッドであるサブルーチンを呼び出す方法のことをいいます。メソッドかどうかの判断は、サブルーチンの定義において、第一引数にself型の引数が指定されていた場合が、メソッドになります。

メソッドの呼び出しは、オブジェクトの生成によって生成されたオブジェクトを使って以下の構文で行うことができます。

オブジェクト->サブルーチン名(引数1, 引数2, 引数3, ..., 引数n);

メソッドの呼び出しは、引数を受け取ります。引数の個数が、サブルーチンの定義で定義されている引数の個数と一致しない場合は、コンパイル時エラーが発生します。それぞれの引数の型が、サブルーチン定義で定義されている引数の型と型の互換性がない場合は、コンパイル時エラーが発生します。

メソッドの呼び出しは、戻り値がvoid型以外の場合は、戻り値を返します。

メソッドの呼び出しは、です。

メソッドの呼び出しのサンプル

メソッドの呼び出しのサンプルです。

my $point = new Point;
$point->set_x(3);

無名サブルーチンの呼び出しは、メソッドの呼び出しになります。この場合は、名前なしで呼び出すことができます。

オブジェクト->(引数1, 引数2, 引数3, ..., 引数n);

無名サブルーチンの呼び出しのサンプル

無名サブルーチンの呼び出しのサンプルです。

my $anon_sub = sub : int ($self: self, $num1 : int, $num2 : int) {
  return $num1 + $num2;
};

my $ret = $anon_sub->(1, 2);

デリファレンスによる値の取得

デリファレンスによる値の取得とは、リファレンスから実際の値を取得するための操作のことです。C言語の関節演算子「*」を実現するために設計されました。

$変数

変数の型は、リファレンス型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

デリファレンスによる値の取得は、を返します。

        デリファレンスによる値の取得のサンプル
      
my $num : int;
my $num_ref : int& = \$num;
my $num_deref : int = $$num_ref;

my $z : SPVM::Complex_2d;
my $z_ref : SPVM::Complex_2d& = \$z;
my $z_deref : SPVM::Complex_2d = $$z_ref;

デリファレンスによる値の設定

デリファレンスによる値の設定とは、リファレンスから実際の値を設定するための操作のことです。C言語の関節演算子「*」を実現するために設計されました。

$変数 = 式

変数の型は、リファレンス型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

式の型は、デリファレンスされた場合の変数の型に一致していなければなりません。そうでない場合は、コンパイル時エラーが発生します。

デリファレンスによる値の設定は、設定された値を返します。これはです。

        デリファレンスによる値の設定のサンプル
      
my $num : int;
my $num_ref : int& = \$num;
$$num_ref = 1;

my $z : SPVM::Complex_2d;
my $z_ref : SPVM::Complex_2d& = \$z;

my $z2 : SPVM::Complex_2d;

$$z_ref = $z2;

現在のパッケージ名の取得

現在のパッケージ名の取得を行う「__PACKAGE__」キーワードを使用します。

__PACKAGE__

現在のパッケージ名の取得はを返します。

現在のパッケージ名の取得のサンプル

package Foo::Bar {
  sub baz : void () {
    # Foo::Bar
    my $package_name == __PACKAGE__;
  }
}

現在のファイル名の取得

現在のファイル名の取得を行う「__FILE__」キーワードを使用します。

__FILE__

現在のファイル名の取得はを返します。

現在のファイル名とは、モジュールの読み込みパスを基準とした、ファイルの相対パスのことをいいます。たとえば、ファイルの読み込みパスが「/mypath」でモジュール名が「Foo::Bar」であった場合は、絶対パスは「/mypath/Foo/Bar.spvm」で、相対パスは「Foo/Bar.spvm」です。相対パスである「Foo/Bar.spvm」が、現在のファイル名になります。

現在のファイル名の取得のサンプル

# Foo/Bar.spvm
package Foo::Bar {
  sub baz : void () {
    # Foo/Bar.spvm
    my $file_name == __FILE__;
  }
}
package Foo::Bar2 {
  sub baz : void () {
    # Foo/Bar.spvm
    my $file_name == __FILE__;
  }
}

現在の行番号の取得

現在の行番号の取得を行う「__LINE__」キーワードを使用します。

__LINE__

現在の行番号の取得はを返します。

現在の行番号の取得のサンプル

package Foo::Bar {
  sub baz : void () {
    # 4
    my $line == __LINE__;
  }
}

オブジェクトの解放

オブジェクトは、リファレンスカウントが0になるとメモリから解放されます。

オブジェクトが、オブジェクト型の値を要素として持つ配列だった場合は、オブジェクトの解放の前に、未定義値ではないすべての配列の要素のリファレンスカウントが1減らされます

オブジェクトが、クラス型で、オブジェクト型のフィールドを持つときは、オブジェクトの解放の前に、未定義値ではないすべてのオブジェクト型のフィールドが所有するオブジェクトのリファレンスカウントが1減らされます。フィールドに保存されているオブジェクトにウィークリファレンスが設定されていた場合は、リファレンスカウントが1減らされる前に、ウィークリファレンスが解除されます。

オブジェクトがウィークリファレンスのバックリファレンスを持つときは、バックリファレンスとして登録されているフィールドすべてに、未定義値を代入し、バックリファレンスをすべて削除します。

上記の処理は、再帰的に行われます。

条件

条件は次の項目で定義されます。

条件の一覧

条件の結果は、真または偽です。

最後の「(条件)」というのは、条件を「()」で囲ったという意味です。

条件部

条件部とは、条件判定が行われる部分のことです。条件部には「」が記述できます。

if文のかっこの中

if (条件部) {

}

unless文のかっこの中

unless (条件部) {

}

forのかっこの中の二つ目

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

}

whileのかっこの中

while (条件部) {

}

論理AND演算子の左右

条件部 && 条件部

論理OR演算子の左右

条件部 || 条件部

論理否定演算子の右側

!条件部

条件部に式が書かれた場合は、結果の型はint型です。

式は、数値型あるいはオブジェクト型あるいは未定義型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

条件部に式が書かれた場合は、型に応じた結果を返します。

式が未定義値である場合は、0を返します。

式が、数値型の場合は、単項数値拡大型変換が行われます。

型がint型の場合は、その値を返します。

型がlong型、float型、double型、オブジェクト型である場合は、C99における以下の演算と完全に一致する演算を行います。

!!x

リテラル

整数リテラル

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の印字可能文字」あるいは「ひとつのエスケープ文字」です。

文字リテラルの型は「byte型」になります。

不正な文字リテラルの場合は、コンパイル時エラーが発生します。

エスケープ文字

エスケープ文字 説明
\0 Asciiコードの0「NUL」
\a Asciiコードの7「BEL」
\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「\」
\xの後ろに二桁の16進数 直接Asciiコードを指定します。16進数は「0~9」「a~f」「A~F」で表現します。

文字リテラルのサンプル

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

# 文字リテラル
'a'
'x'

# エスケープ文字を使った文字リテラル
'\a'
'\b'
'\t'
'\n'
'\f'
'\r'
'\"'
'\''
'\\'
'\x0D'
'\x0A'

文字列リテラル

文字列リテラルは、ダブルクォート「"」で囲まれます。

文字列リテラルの内容は「0個以上のAsciiの印字可能文字あるいはエスケープ文字」です。

文字列リテラルの型は「string型」になります。

不正な文字列リテラルの場合は、コンパイル時エラーが発生します。

エスケープ文字

エスケープ文字 説明
\0 Asciiコードの0「NUL」
\a Asciiコードの7「BEL」
\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「\」
\xの後ろに二桁の16進数 直接Asciiコードを指定します。16進数は「0~9」「a~f」「A~F」で表現します。
\N{}の中の複数桁の16進数 Unicodeのコードポイントを16進数で指定します。UTF-8に変換されます。16進数は「0~9」「a~f」「A~F」で表現します。

Unicodeのコードポイントを指定するエスケープ文字以外は、文字リテラルのエスケープ文字と共通です。

文字列リテラルのサンプル

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

# 文字列リテラル
"abc"
"あいう"

# エスケープ文字を使った文字列リテラル
"abc\tdef\n"
"\x0D\x0A"
"\N{U+3042}\N{U+3044}\N{U+3046}"

文字列

SPVMの文字列のデータ表現は、byte型の配列です。特別な内部表現を持たない、単なるバイト列です。

my $string = new byte[3];
$string->[0] = 'a';
$string->[1] = 'b';
$string->[2] = 'c';

byte型の配列は、文字列型に代入できます。文字列型は、コンパイル時には要素を変更できない型ですが、実行時はbyte型の配列になります。

my $string_const : string = $string;

文字列リテラルを代入することによって、文字列を作成できます。文字列リテラルを元にしたstring型の新しい文字列を返します。

my $string_const = "abc";

byte[]はC言語の「char*」、文字列型はC言語の「const char*」に該当するように設計されています。

未定義値

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

undef

ファットカンマ

ファットカンマは「=>」で表現されます。

=>

ファットカンマは「,」のエイリアスです。「,」が使える場所ではいつでも代わりに、ファットカンマが使えます。

# カンマ
["a", "b", "c", "d"]

# カンマの代わりにファットカンマを使う
["a" => "b", "c" => "d"]

ファットカンマの左型に置かれたパッケージ変数名、レキシカル変数名以外の識別子は、文字列リテラルとして扱われます。

# カンマの代わりにファットカンマを使う
["a" => "b", "c" => "d"]

# ファットカンマの左型に置かれた識別子は、文字列リテラルになる。上記と同じ意味
[a => "b", c => "d"]

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

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

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

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

演算子

演算子の概要

演算子は、単項演算子二項演算子インクリメント演算子デクリメント演算子比較演算子論理演算子代入演算子からなります。

単項演算子

単項演算子とは、の前に置かれる演算子のことをいいます。

単項演算子 項

単項演算子には、単項プラス演算子単項マイナス演算子ビット否定演算子論理否定演算子配列長演算子文字列長演算子があります。

インクリメント演算子とデクリメント演算子は、単項演算子には含まれません。

二項演算子

二項演算子とは、左項と右項の間に置かれる演算子のことをいいます。項については、を参考にしてください。

左項 二項演算子 右項

二項演算子には、加算演算子減算演算子乗算演算子除算演算子剰余演算子ビットAND演算子ビットOR演算子論理AND演算子論理OR演算子シフト演算子文字列連結演算子があります。

順次演算子

順次演算子とは、複数のを並べて記述でき、最後の値を返す演算子のことです。

(式1, 式2, 式3)

式は左から実行されます。最後の値が返されます。

順次演算子はを返します。

順次演算子のサンプル

順次演算子のサンプルです。

# $fooには3が代入される。
my $foo = (1, 2, 3);

# $xは3、$retは5になる
my $x = 1;
my $y = 2;
my $ret = ($x += 2, $x + $y);
  • 順次演算子
  • 算術演算子

    算術演算子は、算術を行う演算子のことで、加算演算子減算演算子乗算演算子除算演算子剰余演算子単項プラス演算子単項マイナス演算子インクリメント演算子デクリメント演算子からなります。

    単項プラス演算子

    単項プラス演算子は「+」で表現される単項演算子です。

    +式
    

    式は数値型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    に対して、単項数値拡大型変換が行われます。

    その後、単項プラス演算子は、与えられた値をコピーして返します。

    単項プラス演算子はを返します。

    単項プラス演算子の結果の型は、単項数値拡大型変換された型です。

    単項プラス演算子は例外を発生させません。

    単項プラス演算子のサンプル

    my $num = +10;
    

    単項マイナス演算子

    単項マイナス演算子は「-」で表現される単項演算子です。

    -式
    

    式は数値型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    に対して、単項数値拡大型変換が行われます。

    その後、単項マイナス演算子は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型、float型、double型における演算が定義されます。

    -x
    

    単項マイナス演算子はを返します。

    単項マイナス演算子の結果の型は、単項数値拡大型変換された型です。

    単項マイナス演算子は例外を発生させません。

    単項マイナス演算子のサンプル

    my $num = -10;
    

    加算演算子

    加算演算子は「+」で表現される、加算を行うための二項演算子です。

    左式 + 右式
    

    左式と右式は、数値型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左式と右式に対して、二項数値拡大型変換が行われます。

    その後、加算演算子は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型、float型、double型における演算が定義されます。

    x + y;
    

    加算演算子はを返します。

    加算演算子の結果の型は、二項数値拡大型変換された型です。

    加算演算子は、例外を発生させません。

    減算演算子

    減算演算子は「-」で表現される二項演算子です。

    左式 - 右式
    

    左式と右式は、数値型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左式と右式に対して、二項数値拡大型変換が行われます。

    その後、減算演算子は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型、float型、double型における演算が定義されます。

    x - y;
    

    減算演算子はを返します。

    減算演算子の結果の型は、二項数値拡大型変換された型です。

    減算演算子は、例外を発生させません。

    乗算演算子

    乗算演算子は「*」で表現される二項演算子です。

    左式 * 右式
    

    左式と右式は、数値型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左式と右式に対して、二項数値拡大型変換が行われます。

    その後、乗算演算子は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型、float型、double型における演算が定義されます。

    x * y;
    

    乗算演算子はを返します。

    乗算演算子の結果の型は、二項数値拡大型変換された型です。

    乗算演算子は、例外を発生させません。

    除算演算子

    除算演算子は「/」で表現される二項演算子です。

    左式 / 右式
    

    左式と右式は、数値型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左式と右式に対して、二項数値拡大型変換が行われます。

    その後、除算演算子は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型、float型、double型における演算が定義されます。

    x / y;
    

    除算演算子はを返します。

    除算演算子の結果の型は、二項数値拡大型変換された型です。

    整数型に対する演算の場合には、除算演算子は、右辺が0であった場合に、例外が発生します。

    浮動小数点型に対する演算の場合には、除算演算子は、例外を発生させません。

    剰余演算子

    剰余演算子は「%」で表現される二項演算子です。

    左式 % 右式
    

    左式と右式は、整数型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左式と右式に対して、二項数値拡大型変換が行われます。

    その後、剰余演算子は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型における演算が定義されます。

    x % y;
    

    剰余演算子はを返します。

    剰余演算子の結果の型は、二項数値拡大型変換された型です。

    剰余演算子は、右辺が0であった場合に、例外が発生します。

    インクリメント演算子

    インクリメント演算子は、値を1加算する演算子です。インクリメント演算子が前置されるか、後置されるかで、意味が変わります。

    # 前置のインクリメント
    ++レキシカル変数
    ++パッケージ変数
    ++フィールドへのアクセス
    ++配列へのアクセス
    ++デリファレンス
    
    # 後置のインクリメント
    レキシカル変数++
    パッケージ変数++
    フィールドへのアクセス++
    配列へのアクセス++
    デリファレンス++
    

    インクリメント演算子の対象は、レキシカル変数パッケージ変数フィールドへのアクセス配列へのアクセスデリファレンスでなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    インクリメント演算子の対象の型は、数値型でなければなりません。そうでない場合は、コンパイルエラーが発生します。

    前置のインクリメント演算子

    前置のインクリメント演算子は、インクリメントを行った後に、インクリメントされた値を返します。

    前置のインクリメント演算子は、次の式と等価です。対象に1が加算された後に、元の型で型キャストが行われ、対象に代入されます。

    (対象 = (型キャスト)(対象 + 1))
    

    たとえば、byte型の値の前置のインクリメントは、次の式と等価です。

    ($num = (byte)($num + 1))
    

    後置のインクリメント演算子

    後置のインクリメント演算子は、インクリメントを行った後に、インクリメントする前の値を返します。

    後置のインクリメント演算子は、順次演算子を使った、次の式と等価です。対象が一時変数に保存され、対象に1が足された後に、元の型で型キャストが行われ、対象に代入されます。その後、一時変数が返されます。

    (my 一時変数 = 対象, 対象 = (型キャスト)(対象 + 1), 一時変数)
    

    たとえば、byte型の値の後置のインクリメントは、次の式と等価です。

    (my $tmp = $num, $num = (byte)($num + 1), $tmp)
    

    デクリメント演算子

    デクリメント演算子は、値を1減算する演算子です。デクリメント演算子が前置されるか、後置されるかで、意味が変わります。

    # 前置のデクリメント
    --レキシカル変数
    --パッケージ変数
    --フィールドへのアクセス
    --配列へのアクセス
    --デリファレンス
    
    # 後置のデクリメント
    レキシカル変数--
    パッケージ変数--
    フィールドへのアクセス--
    配列へのアクセス--
    デリファレンス--
    

    デクリメント演算子の対象は、レキシカル変数パッケージ変数フィールドへのアクセス配列へのアクセスデリファレンスでなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    デクリメント演算子の対象の型は、数値型でなければなりません。そうでない場合は、コンパイルエラーが発生します。

    前置のデクリメント演算子

    前置のデクリメント演算子は、デクリメントを行った後に、デクリメントされた値を返します。

    前置のデクリメント演算子は、次の式と等価です。対象に1が減算された後に、元の型で型キャストが行われ、対象に代入されます。

    (対象 = (型キャスト)(対象 - 1))
    

    たとえば、byte型の値の前置のデクリメントは、次の式と等価です。

    ($num = (byte)($num - 1))
    

    後置のデクリメント演算子

    後置のデクリメント演算子は、デクリメントを行った後に、デクリメントする前の値を返します。

    後置のデクリメント演算子は、順次演算子を使った、次の式と等価です。対象が一時変数に保存され、対象に1が足された後に、元の型で型キャストが行われ、対象に代入されます。その後、一時変数が返されます。

    (my 一時変数 = 対象, 対象 = (型キャスト)(対象 - 1), 一時変数)
    

    たとえば、byte型の値の後置のデクリメントは、次の式と等価です。

    (my $tmp = $num, $num = (byte)($num - 1), $tmp)
    

    ビット演算子

    ビット演算子は、ビット演算を行う演算子のことで、>ビットAND演算子ビットOR演算子ビット否定演算子からなります。

    ビットAND演算子

    ビットANDは「&」で表現される二項演算子です。

    左式 & 右式
    

    左式と右式は、整数型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左式と右式に対して、二項数値拡大型変換が行われます。

    その後、ビットAND演算子の演算結果は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型における演算が定義されます。

    x & y;
    

    ビットAND演算子はを返します。

    ビットAND演算子の結果の型は、二項数値拡大型変換された型です。

    ビットAND演算子は、例外を発生させません。

    ビットOR演算子

    ビットORは「|」で表現される二項演算子です。

    左式 | 右式
    

    左式と右式は、整数型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左式と右式に対して、二項数値拡大型変換が行われます。

    その後、ビットOR演算子の演算結果は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型における演算が定義されます。

    x | y;
    

    ビットOR演算子はを返します。

    ビットOR演算子の結果の型は、二項数値拡大型変換された型です。

    ビットOR演算子は、例外を発生させません。

    ビット否定演算子

    ビット否定演算子は「~」で表現される単項演算子です。

    ~式
    

    式は整数型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    に対して、単項数値拡大型変換が行われます。

    その後、ビット否定演算子の演算結果は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型における演算が定義されます。

    ~x
    

    ビット否定演算子はを返します。

    ビット否定演算子の結果の型は、単項数値拡大型変換された型です。

    ビット否定演算子は例外を発生させません。

    ビット否定演算子のサンプル

    my $num = ~0xFF0A;
    

    シフト演算子

    シフト演算子は、ビットシフトを行う演算子で、左シフト演算子算術右シフト演算子論理右シフト演算子からなります。

    左シフト演算子

    左シフトは「<<」で表現される二項演算子です。

    左式 << 右式
    

    左式は、整数型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    最初に左式に対して、単項数値拡大型変換が行われます。

    右式は、int型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    左シフト演算子の演算結果は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型における演算が定義されます。

    x << y;
    

    左シフト演算子はを返します。

    左シフト演算子は、例外を発生させません。

    算術右シフト演算子

    算術右シフトは「>>」で表現される二項演算子です。

    左式 >> 右式
    

    左式は、整数型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    最初に左式に対して、単項数値拡大型変換が行われます。

    右式は、int型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    算術右シフト演算子の演算結果は、C99における以下の演算と完全に一致する演算を行います。xに対して、C99との型の対応に応じた、int型、long型における演算が定義されます。

    x >> y;
    

    算術右シフト演算子はを返します。

    算術右シフト演算子は、例外を発生させません。

    論理右シフト演算子

    論理右シフトは「>>>」で表現される二項演算子です。

    左式 >>> 右式
    

    左式は、整数型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    最初に左式に対して、単項数値拡大型変換が行われます。

    右式は、int型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    論理右シフト演算子の演算結果は、C99における以下の演算と完全に一致する演算を行います。xに対して、C99との型の対応に応じた、int型、long型における演算が定義されます。

    (符号付整数型キャスト)((符号なし整数型キャスト)x >> y);
    

    論理右シフト演算子はを返します。

    論理右シフト演算子は、例外を発生させません。

    比較演算子

    比較演算子とは、左式と右式の間に置かれる演算子で、条件を返す演算子のことをいいます。

    左式 比較演算子 右式
    

    比較演算子には、数値比較演算子文字列比較演算子、isa演算子があります。

    数値比較演算子

    数値比較演算子とは、数値あるいはオブジェクトのアドレスを比較するために、左式と右式の間に置かれる演算子で、条件を返す演算子のことをいいます。

    左式 数値比較演算子 右式
    

    数値比較演算子の一覧です。

    演算子 比較可能な型 解説
    左式 == 右式 左式と右式が数値型、左式と右式がオブジェクト型(未定義値を含む) 左式と右式が等しい
    左式 != 右式 左式と右式が数値型、左式と右式がオブジェクト型(未定義値を含む) 左式と右式が等しくない
    左式 > 右式 左式と右式が数値型 左式は右式より大きい
    左式 >= 右式 左式と右式が数値型 左式は右式より大きいまたは等しい
    左式 < 右式 左式と右式が数値型 左式は右式より小さい
    左式 <= 右式 左式と右式が数値型 左式は右式より小さいまたは等しい

    左辺と右辺の型は、比較可能な型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    数値型の比較の場合は、左式と右式に対して、二項数値拡大型変換が行われます。

    その後、数値比較演算子は、C99における以下の演算と完全に一致する演算を行います。C99との型の対応に応じた、int型、long型、float型、double型、オブジェクト型における演算が定義されます。

    # 数値型の比較、オブジェクト型の比較
    x == y;
    x != y;
    
    # 数値型の比較
    x > y;
    x >= y;
    x < y;
    x <= y;
    

    数値比較演算子の結果の型は、int型です。真である場合は0以外の値を、偽である場合は0を返します。

    数値比較演算子は、例外を発生させません。

    文字列比較演算子

    文字列比較演算子とは、文字列を比較するために、左式と右式の間に置かれる演算子で、条件を返す演算子のことをいいます。

    左式 文字列比較演算子 右式
    

    左式と右式は、文字列互換型でなければなりません。

    文字列比較演算子の一覧です。

    演算子 解説
    左式 eq 右式 左式と右式が等しい
    左式 ne 右式 左式と右式が等しくない
    左式 gt 右式 左式は右式より辞書式順序で比較して大きい
    左式 ge 右式 左式は右式より辞書式順序で比較して大きいまたは等しい
    左式 lt 右式 左式は右式より辞書式順序で比較して小さい
    左式 le 右式 左式は右式より辞書式順序で比較して小さいまたは等しい

    文字列比較演算子の結果の型は、int型です。真である場合は0以外の値を、偽である場合は0を返します。

    isa演算子

    数値比較演算子とは、数値あるいはオブジェクトのアドレスを比較するために、左式と右式の間に置かれる演算子で、条件を返す演算子のことをいいます。

    isa演算子とはは、型の適合性をチェックするための演算子で、条件を返します。

    左式 isa 右型
    

    isa演算子は、右型に応じて、3種類の動作をします。

    1. 右型が、数値型値型汎用オブジェクト型リファレンス型の場合は、コンパイル時に左式の型が右型と同一のものであるかをチェックします。同一であった場合は真を、そうでない場合は偽を返します。

    2. 右型が、クラス型であった場合は、実行時に左式の型が、クラス型と一致するかをチェックします。一致した場合は真を、そうでない場合は偽を返します。左式の型は、オブジェクト型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    3. 右型が、インターフェース型であった場合は、実行時に左式の型がクラス型であり、そのクラスがインターフェースを満たすかどうかをチェックします。満たした場合は真を、そうでない場合は偽を返します。左式の型は、オブジェクト型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    論理演算子

    論理演算子は、論理演算を行う演算子のことで、>論理AND演算子論理OR演算子論理否定演算子からなります。

    論理演算子は、条件を返します。

    論理AND演算子

    論理AND演算子とは、論理AND演算を行うために、「&&」で表現される、をオペランドとする二項演算子です。

    左項 && 右項
    

    論理AND演算子は次のように動作します。

    1. 左項の評価が真であれば、右項を実行し、右項の評価が、真であれば、真を返し、右項の評価が偽であれば、偽を返します。

    2. 左項の評価が偽であれば、偽を返します。右項は実行されません。

    論理AND演算子の結果の型は、int型です。

    論理AND演算子は条件を返します。

    論理AND演算子は、例外を発生させません。

    論理OR演算子

    論理OR演算子とは、論理OR演算を行うために、「||」で表現される、をオペランドとする二項演算子です。

    左項 || 右項
    

    論理OR演算子は次のように動作します。

    1. 左項の評価が真であれば、真を返します。右項は実行されません。

    2. 左項の評価が偽であれば、右項を実行し、右項の評価が、真であれば、真を返し、右項の評価が偽であれば、偽を返します。

    論理OR演算子の結果の型は、int型です。

    論理OR演算子は条件を返します。

    論理OR演算子は、例外を発生させません。

    論理NOT演算子

    論理NOT演算子とは、論理NOT演算を行うために、項の左に置かれる演算子で、「!」で表現される条件を返す単項演算子です。項については、を参考にしてください。

    !項
    

    論理NOT演算子は、項が真であれば、偽を返します。項が偽であれば、真を返します。

    論理NOT演算子の結果の型は、int型です。

    論理NOT演算子は条件うぃを返します。

    論理NOT演算子は、例外を発生させません。

    文字列連結演算子

    文字列連結演算子は「.」で表現される二項演算子です。

    左式 . 右式
    

    左式あるいは右式が、数値型であった場合は、数値から文字列への型変換によって文字列に変換されます。

    左式と右式はどちらも文字列互換型でなければなりません。そうでない場合は、コンパイルエラーになります。

    文字列連結演算子は、左式と右式で表現される文字列を連結し、新しい文字列を返します。

    文字列連結演算子はを返し、型は文字列型です。

    左式と右式の両方が、文字列リテラルであった場合は、コンパイル時に連結された文字列リテラルが生成されます。パフォーマンスのコストを意識せずに、文字列連結演算子で、文字列リテラルを連結できます。

    実行時に、左式あるいは右式が未定義値だった場合は、例外が発生します。

    文字列連結演算子のサンプル

    my $str = "abc" . "def";
    my $str = "def" . 34;
    my $str = 123 . 456;
    

    代入演算子

    代入演算子は「=」で表現される、代入を行うための二項演算子です。

    左式 = 右式
    

    代入演算子は、右辺と左辺によって、複数の意味を持ちます。各項目を参考にしてください。

    代入演算子においては、右式が評価された後に、左式が評価されます。これは、原則として、式は左から右へ実行されるということの例外です。

    特殊代入演算子

    特殊代入演算子とは、二項演算子代入演算子の組み合わせで表現される特殊な代入を行う二項演算子のことです。

    左式 特殊代入演算子 右式
    

    左式と右式が型の互換性を満たさない場合は、コンパイル時エラーが発生します。

    特殊代入演算子の一覧

    特殊代入演算子の一覧です。

    加算代入演算子 +=
    減算代入演算子 -=
    乗算代入演算子 *=
    除算代入演算子 /=
    剰余代入演算子 %=
    ビットAND代入演算子 &=
    ビットOR代入演算子 |=
    左シフト代入演算子 <<=
    算術右シフト代入演算子 >>=
    論理右シフト代入演算子 >>>=

    特殊代入演算子は、次のように展開されます。

    # 展開前
    左式 特殊代入演算子 右式
    
    # 展開後
    左式 代入演算子 (左式の型によるキャスト)(左式 指定された演算子 右式)
    

    たとえば、加算代入演算子の場合は、次のように展開されます。

    # 展開前 xはbyte型
    $x += 1;
    
    # 展開後
    $x = (byte)($x + 1)
    

    特殊代入演算子のサンプル

    特殊代入演算子のサンプルです。

    $x += 1;
    $x -= 1;
    $x *= 1;
    $x /= 1;
    $x &= 1;
    $x |= 1;
    $x ^= 1;
    $x %= 1;
    $x <<= 1;
    $x >>= 1;
    $x >>>= 1;
    

    リファレンス演算子

    リファレンス演算子は、数値型または値型の変数のアドレスを取得する演算子です。C言語のアドレス演算子「&」を実現するために設計されました。

    \変数
    

    変数が数値型または値型でなかった場合は、コンパイル時エラーが発生します。

    リファレンス演算子は式を返します。返される型は、リファレンス型です。

            リファレンス演算子のサンプル
          
    my $num : int;
    my $num_ref : int& = \$num;
    
    my $z : SPVM::Complex_2d;
    my $z_ref : SPVM::Complex_2d& = \$z;
    

    リファレンスの詳しい解説については、リファレンスを見てください。

    配列長演算子

    配列長演算子は、配列の長さを取得するための「@」で表現される単項演算子です。

    @右式
    

    右式は、配列型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    配列長演算子は、配列の長さをint型の値で返します。

    配列長演算子は、を返します。

    配列長演算子のサンプル

    配列長演算子のサンプルです。

    my $nums = new byte[10];
    my $length = @$nums;
    

    SPVMにはPerlにおけるコンテキストという考え方はなく、配列長演算子は、常に配列の長さを返すことに注意してください。

    文字列長演算子

    文字列長演算子は、文字列の長さを取得するための「length」で表現される単項演算子です。

    length 右式
    

    右式は、文字列互換型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    文字列長演算子は、文字列の長さをint型の値で返します。文字列長演算子が返す文字列の長さは、バイト列としてみたときの長さでです。

    文字列長演算子は、を返します。

    文字列長演算子のサンプル

    文字列長演算子のサンプルです。

    my $nums = "abcde";
    my $length = length $nums;
    

    スカラ演算子

    スカラ演算子は、何もしないで、与えられた値そのものを返す演算子です。配列長演算子の意味を分かりやすくするためだけに用意されています。

    scalar 右式
    

    右式は配列長演算子でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    スカラ演算子は、式を返します。

    スカラ演算子のサンプル

    スカラ演算子のサンプルです。

    my $nums = new int[3];
    foo(scalar @$nums);
    

    isweak演算子

    isweak演算子は、フィールドが、ウィークリファレンスかを確認する演算子です。

    isweak オブジェクト式->{フィールド名};
    

    オブジェクト式の型は、クラス型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    フィールド名は、存在するフィールド名でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    フィールドに保存される値の型は、オブジェクト型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    実行時にフィールドに保存されている値が、未定義値だった場合は、偽を返します。これは、条件です。

    isweak演算子は、フィールドがウィークリファレンスの場合は、真を、そうでない場合は偽を返します。これは、条件です。

    演算子の優先順位

    演算子の優先順位は、以下の通りです。下にいくほど、優先順位が高くなります。

    結合方向 演算子
    右結合
      代入演算子「=」
      加算代入演算子「+=」
      減算代入演算子「-=」
      乗算代入演算子「*=」
      除算代入演算子「/=」
      剰余代入演算子「%=」
      ビットAND代入演算子「&=」
      ビットOR代入演算子「|=」
      ビット排他OR代入演算子「^=」
      左シフト代入演算子「<<=」
      算術右シフト代入演算子「>>=」
      論理右シフト演算子「>>>=」
      文字列結合代入演算子「.=」
    左結合 条件OR演算子「||」
    左結合 条件AND演算子 「&&」
    左結合 ビットOR演算子「|」
    ビットXOR演算子 「^」
    左結合 ビットAND演算子「&」
    非結合 数値等価演算子「==」
    数値非等価演算子「!=」
    文字列等価演算子「eq」
    文字列非等価演算子「ne」
    非結合 数値大なり演算子「>」
    数値小なり演算子「<」
    数値大なり等価演算子「>=」
    数値小なり等価演算子「<=」
    文字列大なり演算子「gt」
    文字列大なり等価演算子「ge」
    文字列小なり演算子「lt」
    文字列小なり等価演算子「le」
    isa演算子「isa」
    左結合 左シフト演算子 「<<」
    符号付き右シフト演算子「>>」
    符号なし右シフト演算子「>>>」
    左結合 加算演算子「+」
    減算演算子「-」
    文字列連結演算子「.」
    左結合 乗算演算子「*」
    除算演算子「/」
    剰余演算子「%」
    右結合 条件NOT演算子「!」
    ビットNOT演算子「~」
    リファレンス演算子「\」
    プラス演算子「+」
    マイナス演算子「-」
    配列長取得演算子「@」
    デリファレンス演算子「$」
    型キャスト「(型名)」 スカラ演算子「scalar」
    文字列長取得演算子「length」
    require演算子「require」
    非結合 前置インクリメント演算子「++」
    後置インクリメント演算子「++」
    前置デクリメント演算子「--」
    後置デクリメント演算子「--」
    左結合 アロー演算子「->」

    演算子の優先順位は「()」を使うことによって、最優先にすることができます。

    # a * b が先
    a * b + c
    
    # b + c が先
    a * (b + c)
    

    文は、ひとつの処理のことで「スコープブロック」の中に複数記述することができます。のように値として評価されることはありません。

    文の一覧

    文の一覧です。

    if文

    if文は、条件分岐を行うための文です。

    if (項) {
      
    }
    

    項が真の場合に、ブロックが実行されます。SPVMにおける真偽については条件部を見てください。

    条件を複数書きたい場合には「elsif文」を続けることができます。 条件判定は上から実行され、項が真になった場合に対応するブロックが実行されます。

    if (項) {
      
    }
    elsif(項) {
      
    }
    

    「else文」を使って、項が真ではなかった場合の処理を記述することができます。if文とelsif文の条件判定がすべて偽であった場合にelseのブロックの内部の文が実行されます。elsif文は、なくてもかまいません。

    if (項) {
      
    }
    elsif(項) {
      
    }
    else {
      
    }
    

    if文のサンプル

    if文のサンプルです。

    my $flag = 1;
    
    if ($flag == 1) {
      print("One\n");
    }
    elsif ($flag == 2) {
      print("Tow\n");
    }
    else {
      print("Other");
    }
    

    if文は、内部的には、目には見えない単純なブロックで囲まれています。

    {
      if (項) {
        
      }
    }
    

    elsifは、内部的には、if文とelse文に展開されます。

    # 展開前
    if (項1) {
    
    }
    elsif (項2) {
    
    }
    else {
    
    }
    
    # 展開後
    if (項1) {
    }
    else {
      if (項2) {
      
      }
      else {
      
      }
    }
    

    if文の条件部で、変数宣言を行うときは、目には見えない単純なブロックで囲まれていることと、elsifは、内部的には、if文とelse文に展開されるということを、意識してください。

    # 展開前
    my $num = 1;
    if (my $num = 2) {
      
    }
    elsif (my $num = 3) {
      
    }
    else {
      
    }
    
    # 展開後
    my $num = 1;
    {
      if (my $num = 2) {
        
      }
      else {
        {
          if (my $num = 3) {
            
          }
          else {
            
          }
        }
      }
    }
    

    switch文

    switch文は、int型の整数を条件にして、条件分岐を行うための文です。条件がint型の整数で、たくさんの分岐がある場合は、if文よりも高速です。

    switch (条件式) {
      case 定数1:
      
      case 定数2:
      
      case 定数n:
      
      default:
    }
    

    条件式は、を指定できます。式の型は、int型の値でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    case文で指定される定数は、int型の定数でなければなりません。enum型の値や、int型の定数サブルーチンは、int型の定数として、構文解析時に展開されるので、利用することができます。

    case文に指定される定数は、重複してはいけません。重複している場合は、コンパイル時エラーが発生します。

    条件式で指定された値が、case文で指定された値にマッチした場合は、そのcase文の位置にジャンプします。

    マッチしなかった場合でdefault文が指定されている場合は、default文の位置にジャンプします。default文が指定されていなかった場合は、switchブロックは実行されません。

    switchブロックの中で、last文を使用した場合は、switchブロックから抜けることができます。

    switch (条件式) {
      case 定数1:
        last;
      case 定数2:
        last;
      case 定数n:
        last;
      default:
    }
    

    switch文のサンプル

    switch文のサンプルです。

    my $code = 2;
    switch ($code) {
      case 1:
        print("1\n");
        last;
      case 2:
        print("2\n");
        last;
      case 3:
        print("3\n");
        last;
      default:
        print("Other\n");
    }
    

    last文を書かない場合は、下にあるcase文やdefault文も実行されてしまうので注意してください。以下のように書いてしまうと「2」「3」「Other」と表示されます。

    my $code = 2;
    switch ($code) {
      case 1:
        print("1\n");
      case 2:
        print("2\n");
      case 3:
        print("3\n");
      default:
        print("Other\n");
    }
    

    case文

    case文は、switchブロックの中で使うことができる、条件を指定するための文です。case文についての詳細は、switch文の解説を見てください。

    default文

    default文は、switchブロックの中で使うことができる、デフォルトの条件を指定するための文です。default文についての詳細は、switch文の解説を見てください。

    switch文

    for文は、繰り返しを行うための文です。

    while文

    while文は、繰り返しを行うための文です。

    while (条件項) {
      
    }
    

    条件項、を記述できます。条件項が真であった場合に、ブロックが実行されます。そうでない場合は、ブロックを抜けます。

    while文のサンプル

    while文のサンプルです。

    my $i = 0;
    while ($i < 5) {
      
      print("$i\n");
      
      $i++;
    }
    

    whileブロックの内部では、last文を使って、whileブロックを抜けることができます。

    while (1) {
      last;
    }
    

    whileブロックの内部では、next文を使って、次に実行される条件項の直前に移動することができます。

    my $i = 0;
    while ($i < 5) {
      
      if ($i == 3) {
        $i++;
        next;
      }
      
      print("$i\n");
      $i++;
    }
    

    while文は、内部的には、目には見えない単純なブロックで囲まれています。

    {
      while (条件項) {
        
      }
    }
    

    while文の条件部で、変数宣言を行うときは、目には見えない単純なブロックで囲まれていることを意識してください。

    # 展開前
    my $num = 5;
    while (my $num = 3) {
      
      $i++;
    }
    
    # 展開後
    my $num = 5;
    {
      while (my $num = 3) {
        
        $i++;
      }
    }
    

    for文

    for文は、繰り返しを行うための文です。

    for (初期化式; 条件項; インクリメント式) {
      
    }
    

    初期化式には、を記述できます。一般的には、ループ変数の初期化などの式を記述します。初期化式は省略することが可能です。

    条件項、を記述できます。条件項が真であった場合に、ブロックが実行されます。そうでない場合は、ブロックを抜けます。

    インクリメント式には、を記述できます。一般的には、ループ変数のインクリメントの式を記述します。インクリメント式は省略することが可能です。

    for文は以下のwhile文と同じ意味を持ちます。インクリメント式は、ブロックの最後に実行されます。初期化式は、単純なブロックに囲まれています。

    {
      初期化式;
      while (条件項) {
        
        
        
        インクリメント式;
      }
    }
    

    for文のサンプル

    for文のサンプルです。

    for (my $i = 0; $i < 5; $i++) {
      
      print("$i\n");
    }
    

    forブロックの内部では、last文を使って、forブロックを抜けることができます。

    for (初期化式; 条件項; インクリメント式) {
      
    }
    

    forブロックの内部では、next文を使って、次に実行されるインクリメント式の直前に移動することができます。

    for (my $i = 0; $i < 5; $i++) {
      
      if ($i == 3) {
        next;
      }
    }
    

    return文

    return文を使うと、サブルーチンから脱出します。モータル変数に代入されているオブジェクトは、自動的に解放されます。

    return;
    

    戻り値がある場合は、を指定することができます。

    return 式;
    

    サブルーチンの定義において戻り値の型が「void型」である場合は、式が存在してはいけません。そうでない場合は、コンパイル時エラーが発生します。

    サブルーチンの定義において戻り値の型が「void型」以外の場合は、式の型と一致していなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    croak文

    croak文は、例外を発生させるための文です。

    croak 式;
    

    式は、文字列互換型でなければなりません。

    croak文の詳しい解説については、例外処理を見てください。

    weaken文

    weaken文は、フィールドに対して、ウィークリファレンスを設定する文です。

    weaken オブジェクト式->{フィールド名};
    

    オブジェクト式の型は、クラス型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    フィールド名は、存在するフィールド名でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    フィールドに保存される値の型は、オブジェクト型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    実行時にフィールドに保存されている値が、未定義値だった場合は、weaken文は、何もしません。

    実行時にフィールドに保存されている値が、未定義値でない場合は、次のことが行われます。

    1. フィールドに保存されているオブジェクトのリファレンスカウントを1減らします。

    2. フィールドに、ウィークリファレンスフラグを設定します。

    3. フィールドに保存されているオブジェクトのバックリファレンスに、フィールドを追加します。

    ウィークリファレンスフラグは、フィールドに保存されているオブジェクトに設定されるのではなく、フィールド自体に設定されることに注意してください。

    フィールドに保存されているオブジェクトのリファレンスカウントが、0になってしまった場合は、ウィークリファレンスは作成されず、フィールドに保存されているオブジェクトは解放されます。

    バックリファレンスとは、フィールドに保存されているオブジェクトが持つデータで、ウィークリファレンスフラグが設定されたフィールドを知るために、追加されます。これは、複数存在することがあります。

    # バックリファレンスが、複数あるサンプル
    my $foo = new Foo;
    my $bar = new Bar;
    my $baz = new Baz;
    
    $foo->{bar} = $bar;
    $foo->{baz} = $baz;
    
    $bar->{foo} = $foo;
    $baz->{foo} = $foo;
    
    weaken $bar->{foo};
    weaken $baz->{foo};
    

    上記の例では「$bar->{foo}」と「$baz->{foo}」に、ウィークリファレンスフラグが立ちます。$fooで表現されるオブジェクトは、バックリファレンス「$bar->{foo}」と「$baz->{foo}」を持ちます。

    バックリファレンスの情報が必要なのは、オブジェクトの解放が行われるときに、バックリファレンスが指すフィールドに未定義値を代入する必要があるからです。

    unweaken文

    unweaken文は、フィールドに対して、ウィークリファレンスを解除する文です。

    unweaken オブジェクト式->{フィールド名};
    

    オブジェクト式の型は、クラス型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    フィールド名は、存在するフィールド名でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    フィールドに保存される値の型は、オブジェクト型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

    実行時にフィールドに保存されている値が、未定義値だった場合は、unweaken文は、何もしません。

    実行時にフィールドに保存されている値が、未定義値でない場合は、次のことが行われます。

    1. フィールドに保存されているオブジェクトのリファレンスカウントを1増やします。

    2. フィールドのウィークリファレンスフラグを解除します。

    3. フィールドに保存されているオブジェクトのバックリファレンスから、フィールドを削除します。

    next文

    「next文」は、次のループの先頭に移動するための文です。whileブロックforブロックの中で使うことができます。

    next;
    

    実際の動作についてはwhile文for文の解説を見てください。

    last文

    「last文」は、ループあるいはswitchブロックを脱出するための文です。whileブロックforブロックswitchブロックの中で使うことができます。

    実際の動作についてはwhile文for文switch文の解説を見てください。

    last;
    

    式文

    式文は「」と「;」で構成される文のことです。

    式;
    

    式文のサンプルです。

    1;
    $var;
    1 + 2;
    foo();
    my $num = 1 + 2;
    

    空文

    空文は「;」だけで終わる文のことです。

    ;
    

    型の概要

    SPVMは、静的型言語です。すべてのデータは静的な型を持ちます。

    レキシカル変数の宣言、フィールドの定義、パッケージ変数の定義、サブルーチンの定義の引数と戻り値において、型が指定される必要があります。

    レキシカル変数の宣言においては、型推論を利用して、暗黙的に型を指定することもできます。

    型の初期値

    レキシカル変数の初期値、パッケージ変数の初期値、オブジェクトの生成におけるフィールドの初期値は、型の初期値によって決まります。

    型の初期化値の一覧です。データにおけるすべてのビット列は0に設定されます。

    型名 初期値
    byte 0
    short 0
    int 0
    long 0
    float 0
    double 0
    オブジェクト型 undef
    値型 すべてのフィールドが0

    数値型

    数値型は「整数型」と「浮動小数点型」からなります。

    整数型

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

    型名 説明 サイズ
    byte 8bit符号付整数型 1バイト
    short 16bit符号付整数型 2バイト
    int 32bit符号付整数型 4バイト
    long 64bit符号付整数型 8バイト

    SPVMの整数型には、符号なし整数型は存在しません。

    整数の計算規則については、算術演算子を参考にしてください。

    byte型

    byte型は、符号付8bit整数を表現する整数型です。C99のint8_t型と同じ型です。

    short型

    byte型は、符号付16bit整数を表現する整数型です。C99のint16_t型と同じ型です。

    int型

    int型は、符号付32bit整数を表現する整数型です。C99のint32_t型と同じ型です。

    long型

    long型は、符号付64bit整数を表現する整数型です。C99のint64_t型と同じ型です。

    浮動小数点型

    SPVMの浮動小数点型は以下の2つです。

    型名 説明 サイズ
    float 単精度浮動小数点型 - 32bitで浮動小数点を表現します 4バイト
    double 倍精度浮動小数点型 - 64bitで浮動小数点を表現します 8バイト

    浮動小数点の計算規則については、算術演算子を参考にしてください。

    float型

    float型は、単精度浮動小数点(32bit)を表現する浮動小数点型です。C99のfloat型と同じ型です。

    double型

    double型は、倍精度浮動小数点(64bit)を表現する浮動小数点型です。C99のdouble型と同じ型です。

    パッケージ型

    パッケージ型とは「パッケージの定義」によって定義される型のことをいいます。

    package Foo {
    
    }
    

    パッケージ型は「クラス型」「インターフェース型」「値型」からなります。

    # クラス型
    package Foo {
    
    }
    
    # インターフェース型
    package Foo : interface_t {
    
    }
    
    # 値型
    package Foo : value_t {
    
    }
    

    ポインタ型は、クラス型でもあるので、ポインタ型もパッケージ型になります。

    # ポインタ型
    package Foo : pointer_t {
    
    }
    

    オブジェクト型

    オブジェクト型とは「クラス型」「インターフェース型」「配列型」「文字列型」「汎用オブジェクト型」を合わせたものをいいます。「値型」「リファレンス型」は含みません。

    オブジェクト型の値は「汎用オブジェクト型」に代入できます。

    my $object : object = new Foo;
    my $object : object = new Foo[];
    my $object : object = "abc";
    

    オブジェクト型のサイズはC99における「sizeof(void*)」の値と一致していなければなりません。

    数値オブジェクト型

    SPVMには、数値オブジェクト型として以下の型があります。

    数値型 対応する数値オブジェクト型
    byte SPVM::Byte
    short SPVM::Short
    int SPVM::Int
    long SPVM::Long
    float SPVM::Float
    double SPVM::Double

    未定義型

    未定義型とは、未定義値が持っている型のことです。明示的に利用することはできません。

    未定義型の値は未定義値のみです。

    未定義型の値は、オブジェクト型に代入できます。他の型に代入した場合は、コンパイル時エラーが発生します。

    クラス型

    クラス型とは「パッケージの定義」によって定義される型で「値型」「インターフェース型」ではない型のことをいいます。

    packag Foo {
      
    }
    

    クラス型はnew演算子によって、オブジェクトを生成することができます。

    my $foo = new Foo;
    

    クラス型はオブジェクト型です。

    クラス型はパッケージ型です。

    ポインタ型はクラス型です。

    ポインタ型

    ポインタ型とは「パッケージの定義」において「pointer_t デスクリプタ」が指定されたものをいいます。

    package Foo : pointer_t {
      
    }
    

    ポインタ型は、クラス型の一種です。

    ポインタ型のデータには、C言語のポインタを保存することができます。

    ポインタ型には、フィールドを定義することはできません。定義されていた場合は、コンパイル時エラーが発生します。

    インターフェース型

    インターフェース型とは「パッケージの定義」において「interface_t デスクリプタ」が指定されたものをいいます。

    package SPVM::Comparator : interface_t {
      sub compare : int ($self : self, $x1 : object, $x2 : object);
    }
    

    インターフェスは、C言語における関数ポインタに該当する機能を提供するために設計されました。

    インターフェース型は、サブルーチンの定義を一つだけ持ちます。サブルーチンは、メソッドでなければなりません。

    インターフェース型は「フィールドの定義」「パッケージ変数の定義」を持つことはできません。

    インターフェース型の値を、new演算子によって実体化することはできません。

    インターフェース型は「パッケージ型」です。

    インターフェース型は「オブジェクト型」です。

    インターフェース型には、インターフェースを満たしたクラス型のオブジェクトを代入できます。この場合、クラスはインターフェースに適合するといいます。クラスがインターフェースを満たすのは次の二つの場合です。

    1. インターフェース型として定義されたサブルーチンと同一の名前とシグネチャを持つクラス型のオブジェクトは、インターフェースに適合します。

    # インターフェース型の定義
    package SPVM::Comparator : interface_t {
      sub compare : int ($self : self, $x1 : object, $x2 : object);
    }
    
    # クラスの定義
    package SomeComparator {
      sub compare : int ($self : self, $x1 : object, $x2 : object) {
        
      }
      
      sub foo : int () {
      
      }
    }
    
    # インターフェース型への代入
    my $comparator : comparator = new SomeComparator;
    

    2. インターフェース型として定義されたサブルーチンと同一のシグネチャを持つ無名サブルーチンは、インターフェースに適合します。

    # インターフェース型の定義
    package SPVM::Comparator : interface_t {
      sub compare : int ($self : self, $x1 : object, $x2 : object);
    }
    
    # インターフェース型への代入
    my $comparator : comparator = sub : int ($self : self, $x1 : object, $x2 : object) {
      
    }
    

    汎用オブジェクト型

    汎用オブジェクト型は「object」で表現します。C言語の「void*」型を表現するために設計されました。

    my $object : object;
    

    汎用オブジェクト型には「オブジェクト型」の値を代入できます。

    my $object : object = new Foo;
    my $object : object = "abc";
    my $object : object = new Foo[3];
    

    self型

    self型とは、自身の属するパッケージ型を表現し、引数がインボカントであることを示します。

    サブルーチンの定義において第一引数の型としてのみ利用できます。

    void型

    void型とは、サブルーチンの定義において戻り値の型としてだけ利用できる、存在しないことを示す特別な型です。

    基本型

    次元を持たない型を基本型と呼びます。数値型パッケージ型汎用オブジェクト型文字列型は、基本型です。

    配列型

    配列型は、連続した複数のデータ領域を表現します。基本型は、配列にすることができます。

    int[]
    double[]
    Point[]
    object[]
    string[]
    

    配列は次元を持ち最大255次元まで表現できます。

    # 二次元
    int[][]
    
    # 三次元
    int[][][]
    

    配列型は、オブジェクト型です。

    配列を作成するには、new演算子を使用します。以下の例では、要素数が3のint型の配列を作成しています。

    my $nums = new int[3];

    多次元配列を作成するときも、new演算子を使用します。以下の例では、要素数が3のint[]型の配列を作成しています。

    my $nums = new int[][3];

    数値の配列型

    数値の配列型とは、数値型の値を要素に持つ配列型のことです。

    数値の配列型の一覧

    数値の配列型によって表現されるデータは、要素が数値型のサイズで、配列の長さの個数で連続していなければなりません。

    数値の配列型のすべての要素は、配列の生成が行われたときに型の初期値によって初期化されます。

    byte[]型

    SPVMにおいては、byte[]型は文字列互換型であるという点において特別な型です。

    byte[]
    

    文字列型は、コンパイル時には文字列型として扱われますが、実行時にはbyte[]型になります。

    オブジェクトの配列型

    オブジェクトの配列型とは、オブジェクト型の値を要素に持つ配列型のことです。

    オブジェクトの配列型のサンプル

    オブジェクトの配列型によって表現されるデータは、要素がオブジェクト型のサイズで、配列の長さの個数で連続していなければなりません。

    オブジェクトの配列型のすべての要素は、配列の生成が行われたときに型の初期値によって初期化されます。

    値の配列型

    値の配列型とは、値型の値を要素に持つ配列型のことです。

    値の配列型のサンプル

    値の配列型によって表現されるデータは、要素が値型のサイズで、配列の長さの個数で連続していなければなりません。

    値の配列型のすべての要素は、配列の生成が行われたときに型の初期値によって初期化されます。

    汎用オブジェクト配列型

    汎用オブジェクト配列型とは、oarrayで表現される、オブジェクト型の値を要素として持つ任意の配列型の値を代入できる型です。C言語のqsort関数の第一引数には、任意の配列型をvoid*型にキャストして渡すことができますが、これに該当する機能を実現するために、汎用オブジェクト配列型は設計されました。

    my $array : oarray = new Point[3];
    my $array : oarray = new object[3];
    

    オブジェクト型以外の型を持つ値が代入された場合は、コンパイル時エラーが発生します。

    「oarray型」は「object[]型」と異なる型であることに注意してください。oarray型は、オブジェクト型の値を要素として持つ任意の配列型の値を代入できる型であるのに対して、「object[]型」は、「object型の値を要素に持つ配列」を表現する型で、任意の配列型の値を代入することはできません。

    汎用オブジェクト配列型は、配列型です。配列長演算子によって長さを取得すること、配列の要素の値の設定配列の要素の値の取得ができます。

    my $array : oarray = new SPVM::Int[3];
    
    # 汎用オブジェクト配列型の要素の長さを取得
    my $legnth = @$array;
    
    # 汎用オブジェクト配列型の要素の値の取得
    my $num = (SPVM::Int)$array->[0];
    
    # 汎用オブジェクト配列型の要素の値の設定
    $array->[0] = SPVM::Int->new(5);
    

    汎用オブジェクト配列型の要素の値の設定においては、要素の型が、配列の型の次元よりも1だけ小さくなっているかの実行時チェックが入ります。チェックに失敗した場合は、例外が発生します。汎用オブジェクト配列型は、実行時型安全性を保証します。

    文字列型

    文字列型は、文字列を表現する型です。stringによって表現します。C言語の「const char*」を表現するために設計されました。

    my $str : string;
    

    文字列リテラルによって、生成された文字列オブジェクトを代入できます。

    my $str : string = "abc";
    

    SPVMの文字列は、要素を変更できないバイト型の配列です。配列アクセスを行って、文字を取得することができます。

    # 文字の取得
    my $ch = $str->[1];
    

    要素を変更しようとした場合は、コンパイル時エラーが発生します。

    # 要素の変更はコンパイルエラー
    $str->[1] = 'd';
    

    文字列型は、コンパイルが終わった後は、バイト型の配列とまったく同じものになります。たとえば、一つ目の表現は、二つ目の表現として扱われます。

    # isa 文字列型
    if ($str isa string) {
      
    }
    
    # isa バイト型の配列
    if ($str isa byte[]) {
      
    }
    

    SPVMの文字列は、変更不可ですが、これは、コンパイル時チェックであることに注意してください。

    文字列型は、byte[]型に、キャストすることができ、実行時に文字列を変更することができます。

    my $bytes = (byte[])$str;
    $bytes->[1] = 'd';
    

    文字列は、常に変更が可能であるものとして、扱ってください。

    文字列互換型

    文字列互換型とは、文字列型byte[]型のことを言います。

    値型

    値型とは、連続した数値を表現できる型です。

    パッケージの定義において「value_t」デスクリプタを指定することで、値型を定義できます。

    package Point_3i : value_t {
      has x : int;
      has y : int;
      has z : int;
    }
    

    値型の詳しい解説については、を見てください。

    リファレンス型

    リファレンス型とは、変数のアドレスが格納できる型のことです。数値型あるいは値型の後ろに「&」を付けることで定義できます。

    my $num : int;
    my $num_ref : int& = \$num;
    
    my $point : Point_3i;;
    my $point_ref : Point_3i& = \$point;
    

    リファレンス型の値に代入できるのは、リファレンス演算子で取得したレキシカル変数のアドレスだけです。

    リファレンス型のレキシカル変数の宣言だけが行われた場合は、コンパイル時エラーが発生します。

    リファレンス型は、レキシカル変数の宣言の型として利用できます。必ずリファレンス演算子によって、レキシカル変数のアドレスが格納される必要があります。レキシカル変数の宣言だけの場合は、コンパイル時エラーが発生します。

    リファレンス型は、サブルーチンの定義における引数の型として利用できます。

    リファレンス型は、サブルーチンの定義における戻り値の型として利用できません。

    リファレンス型は、パッケージの定義におけるフィールドの型として利用できません。

    リファレンス型は、パッケージの定義におけるパッケージ変数の型として利用できません。

    不正な場所で、リファレンス型が使用された場合は、コンパイル時エラーが発生します。

    リファレンスの詳しい解説については、リファレンスを見てください。

    型推論

    型推論によって、レキシカル変数の宣言するときに、の指定を省略することができます。型推論は、常に代入演算子の右辺の型によって、行われます。

    # int
    my $num = 1;
    
    # double
    my $num = 1.0;
    
    # Foo
    my $foo = new Foo;
    

    型の互換性

    型に互換性があるというのは、型キャストを行わないで、値が移動ができる型のことです。

    型に互換性があるのは次の場合です。

    移動元と移動先の型が同一の場合

    移動元と移動先の型が同一の場合は、型の互換性があります。

    my $num1 : int;
    my $num2 : int;
    $num1 = $num2;
    

    移動元の型がbyte[]型で、移動先の型が文字列型の場合

    移動元の型がbyte[]型で、移動先の型が文字列型の場合は、型の互換性があります。

    my $bytes = new byte[3];
    my $str : string;
    $str = $bytes;
    

    移動元の型がオブジェクト型で、移動先の型が汎用オブジェクト型の場合

    my $foo : Foo = new Foo;
    my $object : object;
    $object = $foo;
    

    移動元の型と移動先の型が、汎用オブジェクト型あるいは、汎用オブジェクト型の配列で、移動元の型の次元数が、移動先の型の次元数以上の場合

    my $objects_dim2_src : object[];
    my $objects_dim1_dist : object;
    $objects_dim1_dist = $objects_dim2_src;
    

    注意点として、汎用オブジェクトの配列と基本型の配列には互換性はありません。

    # コンパイルエラー
    my $objets : object[] = new int[3];
    

    型に互換性がない場合は、暗黙的な型変換が試みられます。暗黙の型変換に失敗した場合は、コンパイル時エラーが発生します。

    型変換

    型キャスト

    型キャストとは、明示的に記述して行う型変換のことを言います。

    # 型キャスト
    (型)式
    

    int型の値をlong型に変換するサンプルは以下のようになります。

    my $num = (long)3;
    

    型キャストは、を返します。

    移動元の型と指定した型が同一の場合は、単に値のコピーになります。

    my $num : int = (int)4;
    

    型キャストにおける型変換の一覧

    型キャストにおける型変換の一覧です。この表にない型キャストが行われた場合は、コンパイル時エラーが発生します。

    指定した型 移動元の型 変換の内容
    byte[] string アドレス値がコピーされます。
    string byte[] アドレス値がコピーされます。
    数値型 数値型 数値型の型変換が行われます。
    数値オブジェクト型 数値型 ボクシング変換が行われます。数値型と数値オブジェクト型が表現する数値型は、同じでなければなりません。たとえば、数値型がintの場合は、数値オブジェクト型はSPVM::Int型でなければなりません。
    汎用オブジェクト型 数値型 ボクシング変換が行われます。
    数値型 数値オブジェクト型 アンボクシング変換が行われます。数値型と数値オブジェクト型が表現する数値型は、同じでなければなりません。たとえば、数値型がintの場合は、数値オブジェクト型はSPVM::Int型でなければなりません。
    数値型 汎用オブジェクト型 アンボクシング変換が行われます。
    文字列型 数値型 数値が、C標準のsprintf関数の「%g」フォーマットを使って、文字列化されます。

    数値型の型変換

    数値型の型変換とは、数値型から数値型への変換のことです。

    数値型の型変換は、対応するC言語における数値型による型変換と全く同じ処理を行います。例えば、SPVMにおけるintからlongへの型変換は、C言語のint32_t型からint64_t型への型変換と同じ処理を行います。

    # SPVMの変換
    my $src : int = 5;
    my $dist = (long)$src;
    
    # C言語における対応
    int32_t src = 5;
    int64_t dist = (int64_t)src;
    

    型の対応については、C99との型の対応を見てください。

    大きな型から小さな型に変換した場合や、浮動小数点から整数型に変換した場合は、正しい情報が保持されない場合があります。SPVMでは、C99に対応する型変換をそのまま行っているので、正しい情報が保持されない場合に、どの値に変換されるかは、C99の仕様と同じです。

    暗黙的な型変換

    暗黙的な型変換とは、SPVMによって行われる自動的な型変換のことです。次の箇所が、暗黙的な型変換が行われる可能性のある個所です。

    次の場合に暗黙的な型変換が行われます。

    移動元と移動先の型がどちらも数値型で、移動元の型よりも移動先の型が大きい場合は、数値拡大型変換が行われます。

    # 暗黙の拡大型変換
    my $num : long = 123;
    my $num : double = 12.5f;
    

    移動元と移動先の型がどちらも数値型で、移動元の型よりも移動先の型が小さい場合で、移動元の値が、整数リテラルかつ移動先の型の値の範囲で表現できる場合は、数値縮小型変換が行われます。

    # 暗黙の縮小型変換
    my $num : byte = 123;
    my $num : short = 134;
    

    移動元の型が数値型で、移動先の型が汎用オブジェクト型の場合は、対応する数値オブジェクト型へのボクシング変換が行われます。以下の場合の例では、SPVM::Int型のオブジェクトに変換されたものが、汎用オブジェクトに代入されます。

    # object型への暗黙のボクシング変換
    my $num = 123;
    my $object : object = $num;
    

    移動元の型が数値型で、移動先の型が対応する数値オブジェクト型の場合は、対応する数値オブジェクト型へのボクシング変換が行われます。

    # object型への暗黙のボクシング変換
    my $num = 123;
    my $object : SPVM::Int = $num;
    

    移動元の型が汎用オブジェクト型で、移動先の型が数値型の場合は、対応する数値型におけるアンボクシング変換が行われます。以下の場合の例では、SPVM::Int型のオブジェクトからint型への変換が試みられます。

    # object型からの暗黙のアンボクシング変換 - 
    my $object : object;
    my $num : int = $object;
    

    移動元の型が数値オブジェクト型で、移動先の型が対応する数値型の場合は、対応する数値型におけるアンボクシング変換が行われます。

    # 数値オブジェクト型からの暗黙のアンボクシング変換
    my $num_obj = SPVM::Int->new(3);
    my $num : int = $num_obj;
    
    移動元の型が数値型で、移動先の型が、文字列型の場合は、数値から文字列への型変換が行われます。以下の場合の例では、数値の「123」が文字列「"123"」に変換されたものが代入されます。

    # 文字列型への暗黙のボクシング変換
    my $num = 123;
    my $str : string = $num;
    

    数値型の順序

    数値型は、型の順序を持ちます。型の順序は小さい方から「byte」「short」「int」「long」「float」「double」です。

    単項数値拡大型変換

    単項数値拡大型変換とは、がbyte型あるいはshort型であった場合に、int型へ数値拡大型変換を行うことをいいます。

    単項数値拡大型変換が行われるのは以下の場合です。

    二項数値拡大型変換

    二項数値拡大型変換とは、左辺と右辺に数値型をとる二項演算子において、左式と右式に適用される数値拡大型変換のことをいいます。

    次のルールが適用されます。

    1. 一方の式が、double型の場合は、他方の型はdouble型に変換されます。

    2. 一方の式が、float型の場合は、他方の型はfloat型に変換されます。

    3. 一方の式が、long型の場合は、他方の型はlong型に変換されます。

    4. それ以外の場合は、int型に変換されます。

    二項数値拡大型変換が行われるのは以下の場合です。

    数値縮小型変換

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

    数値拡大型変換

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

    ボクシング変換

    ボクシング変換とは、数値型の値を、数値オブジェクト型に変換する操作のことをいいます。

    アンボクシング変換

    アンボクシング変換とは、数値オブジェクト型の値を、対応する数値型の値に変換する操作のことをいいます。

    数値から文字列への型変換

    数値から文字列への型変換とは、数値型文字列型に変換される場合に適用される変換規則のことです。

    数値型の値をC99のsprintfの「%g」フォーマットを使って、SPVMの文字列へ変換します。

    スレッドセーフ

    スレッドセーフの概要

    SPVMの言語仕様はスレッドをサポートしていません。SPVMの言語仕様はスレッドをサポートしていませんが、C/C++をバインディングすることによって、pthreadなどのC/C++のスレッドライブラリを呼び出すことは可能です。

    スレッドライブラリは、SPVMのサブルーチンを呼び出すことができます。ですので、スレッドが作成されて、SPVMのサブルーチンが呼び出された場合のスレッドセーフについて仕様化しておく必要があります。

    このドキュメントで書かれる、スレッドセーフとは、プログラムで利用されるデータの書き込みが正しく行われること、セグメンテーションフォールトが発生しないことを指します。

    スレッドセーフの条件

    SPVMは、グローバルな情報を持ちます。これは、プログラム全体で共有されます。パッケージ変数は、グローバルな情報です。

    SPVMは、実行環境を持ちます。実行環境は複数作成することができます。例外変数は、実行環境に属する情報です。

    スレッドを作成して、SPVMのサブルーチンを呼び出すときに、新しく作成した実行環境を渡すと、例外変数を、スレッドごとに持つことができます。

    このように作成した例外変数は、スレッドセーフです。スレッドごとに、情報を持つのでスレッドによるデータ競合が起こりません。

    パッケージ変数への書き込みは、スレッドセーフではありません。書き込みが行われるときは、データ競合が発生します。つまり、パッケージ変数に書き込んでいるサブルーチンの呼び出しは、スレッドセーフではありません。

    パッケージ変数に対する、データ競合を起こさないようにする簡単な方法は、異なるスレッドで同じデータ領域に書き込まないことです。たとえば、10のスレッドがあれば、配列型のパッケージ変数の定義を行い、スレッドを呼び出す前に、10の長さで配列を生成します。それぞれのスレッドが、それぞれのインデックスの位置に結果を書き込むようにします。

    スレッドの外側から、オブジェクト型のレキシカル変数をサブルーチンの引数や戻り値にすることは、スレッドセーフではありません。スレッドから呼び出すサブルーチンがオブジェクト型の引数や戻り値を持っていた場合は、スレッドセーフにはなりません。

    これは、SPVMがリファレンスカウント式のGCを採用しており、レキシカル変数に保存されたオブジェクトを自動的に解放する操作のときに、データ競合が発生するためです。リファレンスカウントの値が正しく操作されず、プログラムの内部状態が正しく保たれず、セグメンテーションフォールトが発生するでしょう。

    ですから、スレッドセーフにするためには、の引数を持たないサブルーチンを作成し、これをスレッドから呼び出すようにします。結果は、配列型のパッケージ変数に保存するようにします。

    sub foo : void ($thread_id : int) {
      
      # Process
      
      $Bar::BAZ->[$thread_id] = 1;
    }
    

    スレッド内から、レキシカル変数のみを引数と戻り値にとり、パッケージ変数の値の設定を行わないサブルーチンを呼び出すことは、スレッドセーフです。

    sub foo : void ($thread_id : int) {
      
      # Process
      my $ret = Bar->sum(1, 2);
      
      $Bar::BAZ->[$thread_id] = 1;
    }
    

    上記の例の場合は、Bar->sumが、パッケージ変数の値の設定を行わないサブルーチンであれば、スレッドセーフです。

    スレッドセーフの条件を満たしている場合は、スレッドセーフであることが、保証されます。

    自動的に読み込まれるモジュール

    自動的に読み込まれるモジュールは以下です。useキーワードによって読み込まなくても利用することができます。