SPVM言語仕様 1.0 ベータ

# SPVM 1.0 言語仕様

SPVMドキュメント > SPVM言語仕様

## SPVM言語仕様

SPVM 1.0の言語仕様がこのドキュメントには記述されています。SPVMは、1.0のリリースに向けて、ベータテスト中です。言語仕様は、警告なく変更されることがあります。

最終更新日 2019年6月5日 (SPVM言語仕様は、現在90%の完成度)

## 目次

## C99準拠

- C99準拠のコンパイラとランタイム
- C99との型の対応

### C99準拠のコンパイラとランタイム

SPVMコンパイラおよびSPVMランタイムのソースコードは、C言語で書かれ、C99に準拠します。

このことは、ユーザーが、C99以外のCのバージョン(C11、GNU拡張など)や、C++で書かれたコードをSPVMから呼び出せないという意味ではないので注意してください。SPVMのコンパイラとランタイムが、C99に準拠する必要があるという意味です。

### 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*型に一致します。
複数数値型 | 複数数値型のフィールドの個数と長さが一致する数値の配列型 | たとえば複数数値型が package Point_2i : mulnum_t { has x : int; has y : int; } として定義されていた場合は、フィールドの型がint型で、フィールドの個数が2なので C99の「int32_t var[2];」で宣言された型に一致します。

## 構文解析

- LALR(1)法
- 構成要素
- 行終端
- 空白文字
- 識別子
- 区切り文字
- 演算子
- 構文解析表

### LALR(1)法

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

### 構成要素

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

### 行終端

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

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

### 空白文字

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

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

### 識別子

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コードにおけるアルファベットあるいはアンダースコアの一文字以上」です。

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

    # 正しいフィールド名
    FOO
    FOO_BAR
    foo
    foo_bar
    _foo
    _foo_bar_

    # 不正なフィールド名
    foo__bar

#### パッケージ変数名

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

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

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

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

#### レキシカル変数名

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

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

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

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

### キーワード

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

    allow byte BEGIN case die default double elsif else enum eq
    eval for float gt ge has if callback_t isa int last break 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 mulnum_t while
    weaken wo __END__ __PACKAGE__ __FILE__ __LINE__

### 区切り文字

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

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

### 演算子

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

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

### 構文解析表

SPVMの文法を表現する、yacc/bisonにおける構文解析表と、トークンとキーワード・演算子の対応表です。

SPVM文法のyacc/bisonにおける構文解析表

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

    %type  grammar
    %type  opt_packages packages package package_block refcnt
    %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 switch_block if_require_statement
    %type  unary_op binary_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 allow
    %type  new array_init
    %type  my_var var
    %type  expression opt_expressions expressions opt_expression case_statements
    %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 REFCNT
    %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
      | allow
      | begin_block

    begin_block
      : BEGIN block

    use
      : USE basic_type ';'
      | USE basic_type '(' opt_sub_names ')' ';'

    require
      : REQUIRE basic_type

    allow
      : ALLOW 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 ';'
      | DIE ';'
      | DIE expression ';'
      | weaken_field ';'
      | unweaken_field ';'
      | ';'

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

    while_statement
      : WHILE '(' expression ')' block

    switch_statement
      : SWITCH '(' expression ')' switch_block

    switch_block
      : '{' case_statements '}'
      | '{' case_statements default_statement '}'

    case_statements
      : case_statements case_statement
      | case_statement

    case_statement
      : CASE expression ':' block
      | CASE expression ':'

    default_statement
      : DEFAULT ':' block
      | DEFAULT ':'

    if_require_statement
      : IF '(' require ')' block

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

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

    block
      : '{' opt_statements '}'

    eval_block
      : EVAL block ';'

    opt_expressions
      :	/* Empty */
      |	expressions

    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
      | refcnt
      | my_var
      | binary_op
      | unary_op
      | ref
      | deref
      | assign
      | inc
      | dec
      | '(' expressions ')'
      | CURRENT_PACKAGE
      | isweak_field
      | num_comparison_op
      | str_comparison_op
      | isa
      | logical_op

    refcnt
      : REFCNT var
    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

    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
      : expression LOGICAL_OR expression
      | expression LOGICAL_AND expression
      | LOGICAL_NOT expression

    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 var ARROW '{' field_name '}'

    unweaken_field
      : UNWEAKEN var ARROW '{' field_name '}'

    isweak_field
      : ISWEAK var ARROW '{' field_name '}'

    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
    %%

トークンとキーワード・演算子の対応表

トークン | キーワード・演算子
--- | ---
PACKAGE | package
SUB | sub
OUR | our
ENUM | enum
MY | my
SELF | self
USE | use
REQUIRE | require
ALLOW | allow
DESCRIPTOR | descriptor
IF | if
UNLESS | unless
ELSIF | elsif
ELSE | else
FOR | for
WHILE | while
LAST | last
BREAK | break
NEXT | next
SWITCH | switch
CASE | case
DEFAULT | default
EVAL | eval
NAME | name
VAR | var
CONSTANT | リテラル
PACKAGE_VAR_NAME | パッケージ変数名
EXCEPTION_VAR | $@
UNDEF | undef
VOID | void
BYTE | byte
SHORT | short
INT | int
LONG | long
FLOAT | float
DOUBLE | double
STRING | string
OBJECT | object
DOT3 | ...
FATCAMMA | =>
RW | rw
RO | ro
WO | wo
BEGIN | BEGIN
NEW | new
RETURN | return
WEAKEN | weaken
DIE | die
CURRENT_PACKAGE | __PACKAGE__
UNWEAKEN | unweaken
ASSIGN | =
SPECIAL_ASSIGN | += -= *= /= &= |= ^= %= <<= >>= >>>= .=
LOGICAL_OR | ||
LOGICAL_AND | &&
BIT_OR | |
BIT_XOR | &
NUMEQ | ==
NUMNE | !=
STREQ | eq
STRNE | ne
NUMGT | >
NUMGE | >=
NUMLT | <
NUMLE | <=
STRGT | gt
STRGE | ge
STRLT | lt
STRLE | le
ISA | isa
SHIFT | << >> >>>
MULTIPLY | *
DIVIDE | /
REMAINDER | %
LOGICAL_NOT | !
BIT_NOT | ~
REF | \
DEREF | $
PLUS | +
MINUS | -
CONVERT | (型名)
SCALAR | scalar
LENGTH | length
ISWEAK | isweak
REFCNT | refcnt
INC | ++
DEC | --
ARROW | ->

## コメント

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

    # コメント

コメントは、プログラムの実行において無視されます。

## POD

POD(プレーンオールドドキュメント)は、簡単なドキュメントを記載するための機能です。

複数行のコメントを記述する方法として利用できます。

PODは、行頭が「=」で始まり、その後ろに、0文字以上の任意の文字列が続き行終端で終わる行から始まります。

PODは、行頭が「=cut」で、行終端で終わる行で終わります。この行が、存在しなかった場合は、ソースコードの終わりまでが、PODになります。

PODのサンプル

    =pod

    複数行
    コメント

    =cut

    =head1

    複数行
    コメント

    =cut

PODは、プログラムの実行において無視されます。

## パッケージ

- パッケージの定義
- デストラクタ

### パッケージの定義

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

    package パッケージ名 {

    }

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

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

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

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

    }

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

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

    }

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

    }

パッケージデスクリプタ

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

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

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

「callback_t」「mulnum_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 SomeCallback : callback_t {
      sub foo : int ($self : self, $arg : int);
    }

    # 別の関数内
    my $callback : SomeCallback;

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

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

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

## アクセス許可

デフォルトでは、プライベートなサブルーチン、フィールド、パッケージ変数には、パッケージの外部からは、アクセスすることができません。また、プライベートなパッケージは、パッケージの外部から、オブジェクトの生成を行うことができません。

外部のパッケージに対してアクセス許可を設定すると、外部のパッケージは、プライベートなサブルーチン、フィールド、パッケージ変数にアクセスすることができ、またオブジェクトの生成を行うことができます。

    allow パッケージ名;

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

    package Foo {
      allow Bar;
    }

この例では、Fooにおいてprivateが指定されているサブルーチン、フィールド、パッケージ変数に、Barからアクセスすることができます。また、BarからFooのオブジェクトの生成を行うことができます。

## パッケージ変数

- パッケージ変数の定義
- パッケージ変数の初期値
- パッケージ変数へのアクセス

### パッケージ変数の定義

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

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

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

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

パッケージ変数の定義には「型名」が必要です。型名には「数値型」と「オブジェクト型」を指定できます。

パッケージ変数名は、パッケージ変数名で定義されている名前の規則に従い、かつ「::」が含まれていてはいけません。そうでない場合は、コンパイル時エラーが発生します。

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

    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) {

    }

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

### キャプチャ

無名サブルーチンにおいては、キャプチャという構文を使って、無名サブルーチンの外側で定義された変数を、無名サブルーチンの内側で使うことができます。

    # キャプチャ
    [変数名1 : 型1, 変数名2 : 型2] sub : int ($self : self, $x1 : object, $x2 : object) {

    };

キャプチャのサンプル。

    my $foo = 1;
    my $bar = 5L;

    my $comparator = [$foo : int, $bar : long] sub : int ($self : self, $x1 : object, $x2 : object) {

      print "$foo\n";
      print "$bar\n";
    }

キャプチャで使用する変数名は、フィールド名の先頭に「$」を付けたものでなければなりません。

キャプチャは、実際には、クラスのフィールドとして定義されます。キャプチャは、フィールドの定義と値の設定のシンタックスシュガーです。

スコープ内で、キャプチャ変数と同名のレキシカル変数が存在する場合は、レキシカル変数にアクセスします。

キャプチャ変数と同名のパッケージ変数が存在する場合は、キャプチャ変数にアクセスします。

無名サブルーチンとキャプチャをシンタックスシュガーを使わないで記述すると次のようになります。

    package ComapartorImpl {
      has foo : int;
      has bar : long;

      sub compare : int ($self : self, $x1 : object, $x2 : object) {

        print $self->{foo} . "\n";
        print $self->{bar} . "\n";
      }
    }

    my $foo = 1;
    my $bar = 5L;

    my $comparator = new ComparatorImpl;

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

キャプチャは、このような長い記述を、短く書くための文法です。

### メソッド

メソッドとは、第一引数に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: {

        break;
      }
      case Foo->FLAG2: {

        break:
      }
      case Foo->FLAG3: {

        break:
      }
      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++;
    }

スコープブロックの一覧

- 単純なブロック
- サブルーチンブロック
- evalブロック
- ifブロック
- elsifブロック
- elseブロック
- forブロック
- whileブロック
- switchブロック

#### 単純なブロック

    # 単純なブロック
    {

    }

#### サブルーチンブロック

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

    }

#### evalブロック

    # evalブロック
    eval {

    }

#### ifブロック

    # ifブロック
    if (式) {

    }

#### elsifブロック

    #elsifブロック
    elsif (式) {

    }

#### elseブロック

    # elseブロック
    else {

    }

#### forブロック

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

    }

#### whileブロック

    # whileブロック
    while (式) {

    }

#### switchブロック

    switch (式) {

    }

## 例外処理

- 例外処理の概要
- 例外の発生
- 例外変数のキャッチ
- 例外変数

### 例外処理の概要

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

### 例外の発生

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

    die 式;

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

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

    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におけるコールバック型とは、実装を持たないメソッドが一つだけ定義されたパッケージ型のことをいいます。パッケージの定義においてcallback_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 FooCallback : callback_t {
      sub bar : int ($self : self, $num : int);
    }

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

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

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

    my $foo : FooCallback;

    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次元の点、複素数、四元数などを表現することが想定されています。

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

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

    連続した倍精度浮動小数点が2つ
    package Complex_2d : mulnum_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};

## リファレンス

- リファレンスの概要
- デリファレンス

### リファレンスの概要

リファレンスとは、レキシカル変数のアドレスのことをいいます。リファレンスは、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;

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

- リファレンスカウント
- ウィークリファレンスのバックリファレンス
- 基本型のID
- 型の次元(常に0)

### 配列の生成

配列を作成するには、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]

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

- リファレンスカウント
- 基本型のID
- 型の次元(1以上の値)
- 配列の長さ

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

    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減らされる前に、ウィークリファレンスが解除されます。

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

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

## リテラル

- 整数リテラル
- 浮動小数点リテラル
- 文字リテラル
- 文字列リテラル

### 整数リテラル

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型」になります。

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

エスケープ文字

文字リテラルのサンプル

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

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

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

### 文字列リテラル

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

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

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

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

エスケープ文字

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演算子
- ビット否定演算子
- シフト演算子
- 左シフト演算子
- 算術右シフト演算子
- 論理右シフト演算子
- 比較演算子
- 数値比較演算子
- 文字列比較演算子
- isa演算子
- 論理演算子
- 論理AND演算子
- 論理OR演算子
- 論理NOT演算子
- 文字列連結演算子
- 代入演算子
- 特殊代入演算子
- リファレンス演算子
- 配列長演算子
- 文字列長演算子
- スカラ演算子
- isweak演算子

### 演算子の概要

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

### 単項演算子

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

    単項演算子 式

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

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

### 二項演算子

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

    左式 二項演算子 右式

二項演算子には、加算演算子、減算演算子、乗算演算子、除算演算子、剰余演算子、ビット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型、オブジェクト型における演算が定義されます。

    # 数値型の比較、オブジェクト型の比較
    (int32_t)(x == y);
    (int32_t)(x != y);

    # 数値型の比較
    (int32_t)(x > y);
    (int32_t)(x >= y);
    (int32_t)(x < y);
    (int32_t)(x <= y);

数値比較演算子の戻り値の型は、int型です。

数値比較演算子は、例外を発生させません。

### 文字列比較演算子

文字列比較演算子とは、文字列を比較するために、左式と右式の間に置かれる演算子で、式を返す演算子のことをいいます。

    左式 文字列比較演算子 右式

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

文字列比較演算子の一覧です。

演算子 | 解説
--- | ---
左式 eq 右式 | 左式と右式が等しい
左式 ne 右式 | 左式と右式が等しくない
左式 gt 右式 | 左式は右式より辞書式順序で比較して大きい
左式 ge 右式 | 左式は右式より辞書式順序で比較して大きいまたは等しい
左式 lt 右式 | 左式は右式より辞書式順序で比較して小さい
左式 le 右式 | 左式は右式より辞書式順序で比較して小さいまたは等しい

文字列比較演算子の戻り値の型は、int型です。条件が満たされた場合は1を、そうでない場合は0を返します。

### isa演算子

数値比較演算子とは、数値あるいはオブジェクトのアドレスを比較するために、左式と右式の間に置かれる演算子で、式を返す演算子のことをいいます。

isa演算子とはは、型の適合性をチェックするための演算子で、式を返します。

    左式 isa 右型

isa演算子は、右型に応じて、3種類の動作をします。

1. 右型が、数値型、複数数値型、汎用オブジェクト型、リファレンス型の場合は、コンパイル時に左式の型が右型と同一のものであるかをチェックします。同一であった場合はint型で1を、そうでない場合は0を返します。

2. 右型が、クラス型であった場合は、実行時に左式の型が、クラス型と一致するかをチェックします。一致した場合はint型で1を、そうでない場合は0を返します。左式の型は、オブジェクト型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

3. 右型が、コールバック型であった場合は、実行時に左式の型がクラス型であり、そのクラスがコールバック型を満たすかどうかをチェックします。満たした場合はint型の1を、そうでない場合は0を返します。左式の型は、オブジェクト型でなければなりません。そうでない場合は、コンパイル時エラーが発生します。

### 論理演算子

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

論理演算子は、式を返します。

### 論理AND演算子

論理AND演算子とは、論理AND演算を行うために、「&&」で表現される、式をオペランドとする二項演算子です。

    左式 && 右式

論理AND演算子の戻り値の型は、int型です。

論理AND演算子は次のように動作します。

1. 左式にブール型変換を実行します。

2. 左式の値が0以外であれば、右式に対して、ブール型変換を実行し、その値を返します。

3. 左式の値が0であれば、その値を返します。

論理AND演算子は式を返します。

論理AND演算子は、例外を発生させません。

### 論理OR演算子

論理OR演算子とは、論理OR演算を行うために、「||」で表現される、式をオペランドとする二項演算子です。

    左式 || 右式

論理OR演算子は次のように動作します。

論理OR演算子の戻り値の型は、int型です。

1. 左式にブール型変換を実行します。

2. 左式の値が0あれば、右式に対して、ブール型変換を実行し、その値を返します。

3. 左式の値が0以外であれば、その値を返します。

論理OR演算子は式を返します。

論理OR演算子は、例外を発生させません。

### 論理NOT演算子

論理NOT演算子とは、論理NOT演算を行うために、式の左に置かれる演算子で、「!」で表現される式を返す単項演算子です。式については、式を参考にしてください。

    !式

論理NOT演算子の戻り値の型は、int型です。

論理NOT演算子は、式にブール型変換を実行し、その値が0の場合は1を、0以外の値の場合は、0を返します。

論理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演算子は、フィールドがウィークリファレンスの場合は、int型の1を、そうでない場合は0を返します。これは、式です。

## 演算子の優先順位

演算子の優先順位は、以下の通りです。下にいくほど、優先順位が高くなります。

結合方向 | 演算子
--- | ---
右結合 | 代入演算子「=」 加算代入演算子「+=」 減算代入演算子「-=」 乗算代入演算子「*=」 除算代入演算子「/=」 剰余代入演算子「%=」 ビット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文
- switch文
- case文
- while文
- for文
- default文
- 単純ブロック
- eval文
- if require文
- next文
- last文
- break文
- return文
- die文
- weaken文
- unweaken文
- 式文
- 空文

### if文

if文は、条件分岐を行うための文です。

    if (式) {

    }

式にブール型変換が実行され、値が0以外の場合に、ブロックが実行されます。

条件を複数書きたい場合には「elsif文」を続けることができます。 条件判定は上から実行され、それぞれの式にブール型変換が実行され、値が0以外の場合に対応するブロックが実行されます。

    if (式) {

    }
    elsif(式) {

    }

「else文」を使って、if文あるいは、elsif文が条件を満たさなかった場合の処理を記述することができます。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: {

        break;
      }
      case 定数2: {

        break;
      }
      case 定数n: {
        break;
      }
      default: {

      }
    }

条件式は、式を指定できます。条件式には、ブール型変換が実行されます。

case文で指定される定数は、byte型かint型の定数でなければなりません。byte型の定数の場合は、コンパイル時に、int型に型変換されます。enum型の値や、int型の定数サブルーチンは、int型の定数として、構文解析時に展開されるので、利用することができます。

case文に指定される定数は、重複してはいけません。重複している場合は、コンパイル時エラーが発生します。

条件式で指定された値が、case文で指定された値にマッチした場合は、そのcase文の位置にジャンプします。

マッチしなかった場合でdefault文が指定されている場合は、default文の位置にジャンプします。default文が指定されていなかった場合は、switchブロックは実行されません。

switch文には、少なくともひとつのcase文が必要です。そうでない場合は、コンパイルエラーが発生します。

default文は省略可能です。

switchブロックの直下に記述できるのはcase文とdefault文だけです。

caseとdefaultのブロックは省略することができます。

    switch (条件式) {
      case 定数1:
      case 定数2:
      {
        break;
      }
      default:
    }

break文を使用した場合は、switchブロックから抜けることができます。

    switch (条件式) {
      case 定数1: {
        break;
      }
      case 定数2: {
        break;
      }
      case 定数n: {
        break;
      }
      default: {

      }
    }

caseのブロックが存在する場合は、最後の文はbreak文でなければなりません。そうでない場合は、コンパイルエラーが発生します。

switch文のサンプル

switch文のサンプルです。

    my $code = 2;
    switch ($code) {
      case 1: {
        print("1\n");
        break;
      }
      case 2: {
        print("2\n");
        break;
      }
      case 3: {
        print("3\n");
        break;
      }
      case 4:
      case 5:
      {
        print("4 or 5\n"); {
        break;
      }
      default: {
        print("Other\n");
      }
    }

### case文

case文は、switchブロックの中で使うことができる、条件を指定するための文です。case文についての詳細は、switch文の解説を見てください。

### default文

default文は、switchブロックの中で使うことができる、デフォルトの条件を指定するための文です。default文についての詳細は、switch文の解説を見てください。

### while文

while文は、繰り返しを行うための文です。

    while (条件式) {

    }

条件式に、式を記述できます。条件式にブール型変換が実行され、値が0以外の場合に、ブロックが実行されます。そうでない場合は、ブロックを抜けます。

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 (初期化式; 条件式; インクリメント式) {

    }

初期化式には、式を記述できます。一般的には、ループ変数の初期化などの式を記述します。初期化式は省略することが可能です。

条件式、式を記述できます。条件式にブール型変換が実行され、値が0以外の場合に、ブロックが実行されます。そうでない場合は、ブロックを抜けます。

インクリメント式には、式を記述できます。一般的には、ループ変数のインクリメントの式を記述します。インクリメント式は省略することが可能です。

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型」以外の場合は、式の型と一致していなければなりません。そうでない場合は、コンパイル時エラーが発生します。

### die文

die文は、例外を発生させるための文です。

    die 式;

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

die文の詳しい解説については、例外処理を見てください。

### 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文」は、ループを脱出するための文です。whileブロック、forブロックの中で使うことができます。

実際の動作についてはwhile文、for文の解説を見てください。

    last;

### break文

「break文」は、switchブロックを脱出するための文です。switchブロックの中で使うことができます。

実際の動作についてはswitch文の解説を見てください。

    break;

### 式文

式文は「式」と「;」で構成される文のことです。

    式;

式文のサンプルです。

    1;
    $var;
    1 + 2;
    foo();
    my $num = 1 + 2;

### 空文

空文は「;」だけで終わる文のことです。

    ;

## 型

- 型の概要
- 型の初期値
- void型
- 数値型
- 整数型
- byte型
- short型
- int型
- long型
- 浮動小数点型
- float型
- double型
- パッケージ型
- オブジェクト型
- 数値オブジェクト型
- 未定義型
- クラス型
- コールバック型
- 汎用オブジェクト型
- self型
- 配列型
- 数値配列型
- byte[]型
- オブジェクト配列型
- 値配列型
- 汎用オブジェクト配列型
- 文字列型
- 文字列互換型
- 複数数値型
- リファレンス型
- 数値のリファレンス型
- 複数数値のリファレンス型

### 型の概要

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 : callback_t {

    }

    # 複数数値型
    package Foo : mulnum_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言語のポインタを保存することができます。

ポインタ型には、フィールドを定義することはできません。定義されていた場合は、コンパイル時エラーが発生します。

### コールバック型

コールバック型とは「パッケージの定義」において「callback_t デスクリプタ」が指定されたものをいいます。

    package SPVM::Comparator : callback_t {
      sub compare : int ($self : self, $x1 : object, $x2 : object);
    }

コールバック型は、C言語における関数ポインタに該当する機能を提供するために設計されました。

コールバック型は、サブルーチンの定義を一つだけ持ちます。サブルーチンは、メソッドでなければなりません。

コールバック型は「フィールドの定義」「パッケージ変数の定義」を持つことはできません。

コールバック型の値を、new演算子によって実体化することはできません。

コールバック型は「パッケージ型」です。

コールバック型は「オブジェクト型」です。

コールバック型には、コールバックを満たしたクラス型のオブジェクトを代入できます。この場合、クラス型はコールバック型に適合するといいます。クラス型がコールバック型を満たすのは次の二つの場合です。

1. コールバック型として定義されたサブルーチンと同一の名前とシグネチャを持つクラス型のオブジェクトは、コールバック型に適合します。

    # コールバック型の定義
    package SPVM::Comparator : callback_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 : callback_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[]
- short[]
- int[]
- long[]
- float[]
- double[]

数値の配列型によって表現されるデータは、要素が数値型のサイズで、配列の長さの個数で連続していなければなりません。

数値の配列型のすべての要素は、配列の生成が行われたときに型の初期値によって初期化されます。

### byte[]型

SPVMにおいては、byte[]型は文字列互換型であるという点において特別な型です。

    byte[]

文字列型は、コンパイル時には文字列型として扱われますが、実行時にはbyte[]型になります。

### オブジェクトの配列型

オブジェクトの配列型とは、オブジェクト型の値を要素に持つ配列型のことです。

オブジェクトの配列型のサンプル

- Foo[]
- Foo[][]
- Comparable[]
- object[]

オブジェクトの配列型によって表現されるデータは、要素がオブジェクト型のサイズで、配列の長さの個数で連続していなければなりません。

オブジェクトの配列型のすべての要素は、配列の生成が行われたときに型の初期値によって初期化されます。

### 値の配列型

値の配列型とは、複数数値型の値を要素に持つ配列型のことです。

値の配列型のサンプル

- Complex_2d[]
- Complex_2f[]

値の配列型によって表現されるデータは、要素が複数数値型のサイズで、配列の長さの個数で連続していなければなりません。

値の配列型のすべての要素は、配列の生成が行われたときに型の初期値によって初期化されます。

### 汎用オブジェクト配列型

汎用オブジェクト配列型とは、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[]型のことを言います。

### 複数数値型

複数数値型とは、連続した数値を表現できる型です。

パッケージの定義において「mulnum_t」デスクリプタを指定することで、複数数値型を定義できます。

    package Point_3i : mulnum_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型に変換されます。

二項数値拡大型変換が行われるのは以下の場合です。

- 加算演算子
- 減算演算子
- 乗算演算子
- 除算演算子
- 剰余演算子
- ビットAND演算子
- ビットOR演算子
- 数値比較演算子

### 数値縮小型変換

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

### 数値拡大型変換

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

### ボクシング変換

ボクシング変換とは、数値型の値を、数値オブジェクト型に変換する操作のことをいいます。

### アンボクシング変換

アンボクシング変換とは、数値オブジェクト型の値を、対応する数値型の値に変換する操作のことをいいます。

### ブール型変換

ブール型変換とは、if文の条件部などで適用される、真偽値判定のために、適用される変換のことです。

ブール型変換が行われる場所

if文のかっこの中

    if (条件部) {

    }

unless文のかっこの中

    unless (条件部) {

    }

forのかっこの中の二つ目

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

    }

whileのかっこの中

    while (条件部) {

    }

論理AND演算子の左右

    条件部 && 条件部

論理OR演算子の左右

    条件部 || 条件部

論理否定演算子の右側

    !条件部

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

ブール型変換の戻り値は、int型の式です。

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

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

式がint型の場合は、その値が返されます。

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

    !!x

式がオブジェクト型の場合は、未定義値の場合は0、そうでない場合は1が返されます。

## スレッドセーフ

- スレッドセーフの概要
- スレッドセーフの条件

### スレッドセーフの概要

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が、パッケージ変数の値の設定を行わないサブルーチンであれば、スレッドセーフです。

スレッドセーフの条件を満たしている場合は、スレッドセーフであることが、保証されます。

## 自動的に読み込まれるモジュール

自動的に読み込まれるモジュールです。モジュールの読み込みを行わないで利用することができます。

- SPVM::CORE
- SPVM::List
- SPVM::Hash
- SPVM::Byte
- SPVM::Short
- SPVM::Int
- SPVM::Long
- SPVM::Float
- SPVM::Double

## 参考情報

SPVMの公式ドキュメントについては、「SPVMドキュメント」を見てください。

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

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

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

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

## 制限

SPVMにおける制限です。

### パッケージ変数の個数の制限

ひとつのパッケージに定義できるパッケージ変数の個数の最大値は、65535個です。最大値を超えた場合は、コンパイル時エラーが発生します。

### フィールドの個数の制限

ひとつのパッケージに定義できるフィールドの個数の最大値は、65535個です。最大値を超えた場合は、コンパイル時エラーが発生します。

### サブルーチンの個数の制限

ひとつのパッケージに定義できるサブルーチンの個数の最大値は、65535個です。最大値を超えた場合は、コンパイル時エラーが発生します。

### switch文の個数の制限

ひとつのパッケージに定義できるswitch文の個数の最大値は、65535個です。最大値を超えた場合は、コンパイル時エラーが発生します。

### サブルーチンの行数の制限

ひとつのサブルーチンで定義できる行数の最大値は、65535個です。最大値を超えた場合は、コンパイル時エラーが発生します。

### オペレーションコードの個数の制限

SPVMのサブルーチンは、内部でオペレーションコードにコンパイルされます。オペレーションコードの個数の最大値は、65535個です。最大値を超えた場合は、コンパイル時エラーが発生します。

### サブルーチンの引数の個数の制限

SPVMのサブルーチンの引数の個数の最大は255個です。

複数数値型の引数を渡すときは、フィールドの個数が、引数の個数としてカウントされます。たとえばSPVM::Complex_2d型が引数で指定された場合は、2個と数えます。

複数数値のリファレンス型の引数を渡すときは、複数数値型のフィールドの個数が、引数の個数としてカウントされます。たとえばSPVM::Complex_2d&型が引数で指定された場合は、2個と数えます。

もし、最大値を超えた場合は、コンパイル時エラーが発生します。

### 複数数値型のフィールドの個数の制限

複数数値型のフィールドの個数の最大値は、16個で、少なくともひとつのフィールドが必要です。そうでない場合は、コンパイル時エラーが発生します。