Skip to content

Language Reference ja

github-actions[bot] edited this page Sep 30, 2026 · 4 revisions

Language Reference

🌐 English · 日本語

目次

Mana 言語リファレンス

このセクションは、Mana の構文や言語機能を正確に調べるためのリファレンスです。

Tutorial は「順番に学ぶ」ため、Concepts は「なぜそう動くのか」を理解するための資料です。Language Reference では、用途ごとに構文・制約・例を確認できることを優先します。

基礎

  1. ソースコードの構造
  2. 型
  3. 変数
  4. 定数
  5. 式
  6. 演算子
  7. 文

Function とデータ型

  1. Function
  2. Struct

Actor 実行モデル

  1. Actor
  2. Action
  3. Request
  4. 実行制御

構成と連携

  1. Module
  2. Phantom
  3. Namespace と using
  4. Native Function
  5. ソースファイルと import / include
  6. 定義済みシンボル
  7. CLI

記述方針

各ページでは、可能な限り次の順で説明します。

  1. 概要
  2. 構文
  3. 動作
  4. 制約
  5. 例
  6. 関連項目

このリファレンスは現行コンパイラ、VM、テストを基準にします。旧 Primer に残っている古い構文より、現在の実装を優先します。

ソースコードの構造

Mana のソースコードはテキストファイルに記述します。標準的な拡張子は .mn です。

hello.mn

コメント

1行コメントは // です。

// 1行コメント
print("Hello\n");

複数行コメントは /* と */ で囲みます。

/*
複数行の
コメント
*/

現在の Lexer では、ブロックコメントの入れ子はエラーになります。

空白と改行

空白とタブは、トークンを区切るために使います。改行も通常は文法上の区切りにはなりません。

文の終端には、多くの場合 ; を書きます。

int count = 0;
count = count + 1;

ブロック

{ と } で複数の宣言や文をまとめます。

if (count > 0)
{
    print("positive\n");
}

Actor、Action、Function、namespace などでもブロックを使います。

識別子

現在の Lexer では、識別子の先頭には英字、_、? を使用でき、その後には数字も使用できます。

例:

count
_count
Enemy01
?temporary

ただし、可読性のため通常のコードでは英字と _ を中心に使うことを推奨します。

予約語は識別子として使用できません。

整数リテラル

10進整数はそのまま記述します。

0
10
1234

16進整数は 0x で始めます。

0x10
0xFF

2進整数は 0b で始めます。

0b1010
0b1111_0000

現在の Lexer では、2進表記中の _ を区切りとして使用できます。

浮動小数点リテラル

浮動小数点数は小数点を含む形で記述します。

1.0
0.5
12.25
1.0e3

文字列リテラル

文字列は " で囲みます。

"Hello"
"Hello\n"

\n などのエスケープシーケンスを文字列内で使用できます。

真偽値

真偽値は次の予約語で記述します。

true
false

Nil

現在の実装では、空の参照を表す予約語は Nil です。

Nil

N は大文字です。

名前修飾と Action 参照

namespace の区切りには :: を使います。

Game::AI::Enemy

Action 参照には -> を使います。

Enemy->think()

この2つは役割が異なります。

  • :: : 名前空間の修飾
  • -> : Actor の Action 参照

関連項目

型

Mana は静的に型を持つ言語です。変数、引数、戻り値などには型を指定します。

基本型

現在の Lexer で認識される基本型は次のとおりです。

型 概要
void 戻り値を持たない Function などで使用
char 8 bit 整数型
short 16 bit 整数型
bool 真偽値
int 32 bit 整数型
float 32 bit 浮動小数点型
string 文字列型
pointer 低レベルな参照を扱う型

例:

int count = 10;
float speed = 2.5;
bool opened = false;
string name = "Guard";

void

void は値を保持する通常の変数型ではなく、主に「戻り値がない」ことを表します。

void reset()
{
}

bool

bool は true または false を扱います。

bool enabled = true;

条件式では比較結果や論理演算の結果を使用できます。

string

string は文字列を扱います。

string message = "Hello";

文字列リテラルは " で囲みます。

Actor 型

Actor への参照を Function や native Function の引数として扱う場合、Actor 型を使用します。小文字の actor は Actor 宣言専用です。

void notify(Actor target)
{
}

self や sender なども Actor 参照として扱われます。

定義済み複合型

現行コンパイラは、よく使う値型として次の型をあらかじめ登録しています。

型 メンバー
vec2 float x, float y
vec3 float x, float y, float z
vec4 float x, float y, float z, float w
rotator float pitch, float yaw, float roll
color float r, float g, float b, float a

これらは Struct と同様にメンバーを . で参照できます。

actor BuiltInTypeExample
{
    action main()
    {
        vec3 position;
        position.x = 10.0;
        position.y = 20.0;
        position.z = 30.0;

        color tint;
        tint.r = 1.0;
        tint.g = 0.5;
        tint.b = 0.25;
        tint.a = 1.0;
    }
}

transform は現行コンパイラでは定義済み型として登録されていません。

Struct 型

struct でユーザー定義型を作成できます。

struct Position
{
    float x;
    float y;
}

Position p;

Struct の詳細は Struct で扱います。

pointer

pointer は主に VM と native Function の境界など、低レベルな用途で使用する型です。

通常のゲームイベント記述では、まず int、float、bool、string、Actor、定義済み複合型、Struct 型を中心に使用することを推奨します。

型チェック

Mana Compiler は、代入、Function 呼び出し、戻り値、演算などで型の整合性を確認します。

型が合わないコードは、原則としてコンパイル時に診断されます。

関連項目

変数

変数は値を保持するための名前です。

宣言

int count;
float speed;
bool opened;

初期値を同時に指定できます。

int count = 0;
float speed = 1.5;
bool opened = false;

代入

count = 10;
speed = 2.0;

複合代入も使用できます。

count += 1;
speed *= 2.0;

グローバル変数

Actor や Function の外側で宣言します。

int gScore = 0;

グローバル変数の初期化は、通常の Actor の init より前に実行されます。

Actor 変数

Actor 変数は Actor の中、Action の外側へ宣言します。その Actor に属するすべての Action から読み書きでき、値は Action 間で Actor の状態として保持されます。

actor Door
{
    bool mOpened;

    action init()
    {
        mOpened = false;
    }
}

ローカル変数

Action や Function のブロック内で宣言します。

actor Counter
{
    action main()
    {
        int count = 0;
        count = count + 1;
    }
}

ローカル変数は、その処理中に使う一時的な値です。

Struct のメンバー

struct Position
{
    float x;
    float y;
}

actor PositionExample
{
    action main()
    {
        Position p;
        p.x = 10.0;
        p.y = 20.0;
    }
}

固定長配列

変数宣言では固定長配列を使用できます。

actor ArrayExample
{
    action main()
    {
        int values[4];
        values[0] = 10;
        values[1] = 20;
    }
}

配列サイズには正の整数リテラル、または整数定数の名前を指定できます。

const int kValueCount = 4;

actor ArrayWithConstant
{
    action main()
    {
        int values[kValueCount];
    }
}

宣言子には複数の [] を続けて記述することもできます。

int grid[4][8];

配列要素は [] で参照します。

values[index]

実行時に決まる添字が配列範囲外になった場合、現行VMは ScriptError としてその Actor を停止し、範囲外アクセスを続行しません。

allocate と static

トップレベルでは、VM の変数メモリを明示的に構成するための allocate と static を使用できます。通常のゲームロジックより低レベルな機能です。

allocate

allocate 1024
{
    int gReservedValue;
}

allocate N { ... } はグローバル変数領域にサイズを明示した領域を確保し、その中へ変数を配置します。N はバイト数を表す整数リテラルです。

static

static
{
    float gStaticValue;
}

static { ... } の変数は、通常のグローバル変数とは別の VM static 変数領域へ配置されます。

サイズを明示する形式もあります。

static allocate 512
{
    int gStaticReservedValue;
}

現行コンパイラは、明示した領域に宣言された変数が収まるかを検査します。

Mana の static は VM 内の static 変数領域を選ぶ構文です。C++ の static と同じ意味やリンケージ規則を持つものとして解釈しないでください。

主なスコープ

宣言場所 主な用途
グローバル プログラム全体で共有する値
Actor 内 Actor ごとの状態
Action / Function 内 一時的な値
Struct 内 Struct のメンバー
static ブロック VM の static 変数領域

変更しない値には const を使用します。

関連項目

定数

変更しない値には const を使用します。

構文

const 型 名前 = 定数式;

例:

const int kMaxCount = 10;
const float kSpeed = 2.5;
const bool kDebug = false;
const string kMessage = "Hello";

代入はできない

const で宣言した名前へ後から代入することはできません。

const int kMaxCount = 10;

// エラー
kMaxCount = 20;

Compiler はこのような代入を診断します。

初期値は定数式

const の初期値には、コンパイル時に評価できる式を使用します。

const int kBase = 10;
const int kDouble = kBase * 2;

実行時にしか値が決まらない Function 呼び出しなどは、定数初期値として使用できません。

Nil は専用型であり、現行コンパイラでは定数式として使用できません。

Priority に名前を付ける

Priority のような数値に意味のある名前を付ける用途に向いています。

const int kTalkPriority = 10;
const int kMovePriority = 5;
request(kTalkPriority, NPC->talk());

数値を直接書くより、用途が分かりやすくなります。

配列サイズに使う

整数定数は固定長配列のサイズにも使用できます。

const int kValueCount = 4;

actor ArrayExample
{
    action main()
    {
        int values[kValueCount];
    }
}

define / undef について

現在の Lexer には旧形式の define / undef トークンが残っていますが、現行 Parser.yy の宣言構文には組み込まれていません。

したがって、新しいドキュメントでは define / undef を現行の定数宣言構文として扱いません。新しいコードでは const 型 名前 = 値; を使用してください。

関連項目

式

式は値を作る、参照する、計算する、または代入するための構文です。

リテラル

10
1.5
true
"Hello"
Nil

変数参照

count
speed
mOpened

算術式

count + 1
speed * 2.0
(a + b) * c

比較式

count == 0
count != 0
count < 10
count >= 1

比較結果は条件式などで使用できます。

論理式

enabled && visible
ready || force
!finished

代入式

count = 10
count += 1
speed *= 2.0

代入先には、書き込み可能な変数やメンバーなどを指定します。

条件演算子

?: を使用できます。

int value = enabled ? 1 : 0;

Function 呼び出し

calculate(10, 20)

Struct のメンバー Function は . で呼び出します。

value.reset()

メンバー参照

Struct のメンバーには . でアクセスします。

position.x

配列要素

配列要素は [] で参照します。

values[index]

Action 参照

Actor の Action は -> で参照します。

Enemy->think()
Game::AI::Enemy->think()

Action 参照は request、awaitStart、await などで使用します。

request(10, Enemy->think());

:: は namespace の名前修飾、-> は Action 参照です。

定義済みの値

Mana には実行状況を参照するための定義済みシンボルがあります。

priority
self
sender
this
Nil

それぞれの有効な位置と正確な意味は「定義済みシンボル」リファレンスで扱います。

sizeof

sizeof 演算子が用意されています。

詳細な対象と結果は演算子リファレンスで扱います。

型チェック

式の演算や代入について、Compiler が型の整合性を確認します。

関連項目

演算子

Mana で使用できる主な演算子をまとめます。

算術演算子

演算子 意味
+ 加算
- 減算
* 乗算
/ 除算
% 剰余
** べき乗

例:

int a = 10 + 2;
int b = 10 % 3;

比較演算子

演算子 意味
== 等しい
!= 等しくない
< より小さい
<= 以下
> より大きい
>= 以上

論理演算子

演算子 意味
&& 論理 AND
`
! 論理 NOT
if (enabled && visible)
{
}

ビット演算子

演算子 意味
& AND
` `
^ XOR
~ NOT
<< 左シフト
>> 右シフト

代入演算子

=
+=  -=  *=  /=  %=
&=  |=  ^=
<<= >>=

例:

count += 1;
flags |= 0x10;

インクリメント / デクリメント

現行文法では、前置形式と後置形式の両方を記述できます。

++count;
--count;
count++;
count--;

条件演算子

condition ? trueValue : falseValue

例:

int sign = value >= 0 ? 1 : -1;

sizeof

現行文法の sizeof は、型を括弧で指定します。

sizeof(int)
sizeof(Position)

任意の式を渡す構文ではありません。

Action 参照演算子 ->

-> は Actor の Action を参照するための Mana 固有の演算子です。

Enemy->think()
request(10, Enemy->think());

C/C++ のポインタメンバーアクセスとは意味が異なります。

namespace 修飾 ::

:: は namespace を含む名前を修飾します。

Game::AI::Enemy

Action まで指定する場合は組み合わせます。

Game::AI::Enemy->think()

主な優先順位

代入演算子を除く代表的な式について、現在の Parser では概ね次の順に結合が強くなります。

?:
&& ||
== !=
< <= > >=
| ^
&
<< >>
+ -
* / %
**
sizeof
! ~
単項 + -
++ --

特に && と || は現在の Parser では同じ優先順位として宣言されています。

意図を明確にしたい場合は、優先順位だけに頼らず括弧を使用してください。

if ((a || b) && c)
{
}

関連項目

文

Mana で使用する基本的な文をまとめます。

式文

式の後ろに ; を付けます。

count = count + 1;
update();

ブロック

{
    int count = 0;
    count += 1;
}

if / else

if (condition)
{
    print("true\n");
}
else
{
    print("false\n");
}

while

while (condition)
{
    update();
}

do / while

do
{
    update();
}
while (condition);

for

for (int i = 0; i < 10; ++i)
{
    print("loop\n");
}

loop

終了条件を式として持たない繰り返しには loop を使用できます。

loop
{
    update();
}

break

現在の繰り返しや switch から抜けます。

while (true)
{
    if (finished)
        break;
}

continue

現在の反復の残りを飛ばし、次の反復へ進みます。

for (int i = 0; i < 10; ++i)
{
    if (i == 5)
        continue;

    print("run\n");
}

switch

switch (value)
{
case 0:
    print("zero\n");
    break;

case 1:
    print("one\n");
    break;

default:
    print("other\n");
    break;
}

return

Function では呼び出し元へ戻ります。

int add(int a, int b)
{
    return a + b;
}

Action の中でも return; を使用できます。この場合は現在の Action を終了し、その Priority を解放します。下位 Priority に中断中の Action があれば、VM はそこへ復帰できます。

actor NPC
{
    action talk()
    {
        if (sender == Nil)
            return;

        print("Hello\n");
    }
}

Action は戻り値を持たないため、Action では return expression; を使用しません。

goto とラベル

ラベルは identifier:、分岐は goto identifier; で記述できます。

actor GotoExample
{
    action main()
    {
        goto Done;
        print("skip\n");

Done:
        print("done\n");
    }
}

存在しないラベルへの goto はコンパイル時の名前解決エラーになります。

通常の条件分岐や繰り返しで表現できる場合は if、switch、while、for などの構造化された制御文を優先することを推奨します。

print

print("Hello\n");

Request 系

Actor の Action を依頼する構文です。

request(10, Enemy->think());
awaitStart(10, Enemy->think());
await(10, Enemy->think());

詳細な待機条件や Priority との関係は Request と 実行制御 を参照してください。

Action の実行制御

Action の進行、Priority の巻き戻し、Request の受付状態などを制御する文があります。

yield
join
rollback
halt
lock
refuse
comply

正確な構文と動作は 実行制御 にまとめています。

関連項目

Function

Function は、値を受け取り、処理をまとめ、必要に応じて値を返す通常のサブルーチンです。

Action と違い、Function は Request の対象ではなく、呼び出した処理の流れの中で同期的に実行されます。

構文

return_type functionName(arguments)
{
    statements
}

グローバル Function と Struct のメンバー Function は、引数がない場合に限り空の () を糖衣構文として省略できます。引数がある定義には () が必要です。呼び出し時は functionName() や counter.reset() のように常に () が必要です。

たとえば Actor current() { ... } と Actor current { ... } は Actor を返すグローバル Function の定義で、actor current { ... } は Actor 宣言です。

例:

int add(int a, int b)
{
    return a + b;
}

Function は Action や別の Function から呼び出せます。

actor FunctionExample
{
    action main()
    {
        int value = add(2, 3);
        print("%d\n", value);
    }
}

引数

引数は型と名前を並べて宣言します。

float distance(float x, float y)
{
    return x + y;
}

型には Actor を含む組み込み型やユーザー定義型を使用できます。

戻り値

戻り値がある Function は return expression; で値を返します。

int getCount()
{
    return 10;
}

戻り値がない場合は void を使用します。

void reset()
{
    return;
}

void Function から値を返すこと、または値を返す Function で return; のみを書くことはコンパイルエラーです。

Struct のメンバー Function

Function は struct の中にも定義できます。

struct Counter
{
    int value;

    void reset()
    {
        value = 0;
    }
}

actor CounterExample
{
    action main()
    {
        Counter counter;
        counter.reset();
    }
}

呼び出しには . を使います。

Struct のメンバー Function については Struct を参照してください。

Action との違い

Function Action
通常の関数呼び出しで実行 Request で実行できる
引数を持てる 現行構文では引数を持たない
戻り値を持てる 現行構文では戻り値を持たない
呼び出し元の処理の一部 Actor の実行単位

ゲーム内の独立した行動は Action、Action の内部で再利用する処理は Function、と分けると整理しやすくなります。

Native Function

C++ 側へ接続する Function は native で宣言します。

native void playSound(string name);

詳細は Native Function を参照してください。

関連項目

Struct

struct は、複数の値と、それらを扱う Function を一つの型としてまとめるための機能です。

構文

struct TypeName
{
    members
}

例:

struct Status
{
    int hp;
    int mp;
}

変数として使います。

actor StatusExample
{
    action main()
    {
        Status status;
        status.hp = 100;
        status.mp = 20;
    }
}

メンバー変数

Struct の中には変数を宣言できます。

struct CharacterData
{
    string name;
    int level;
    float speed;
    Actor owner;
}

別の Struct をメンバーに持つこともできます。

struct Position
{
    float x;
    float y;
}

struct Unit
{
    Position position;
    int hp;
}

actor UnitExample
{
    action main()
    {
        Unit unit;
        unit.position.x = 10.0;
        unit.hp = 100;
    }
}

メンバー参照には . を使います。

メンバー Function

Struct 内には通常の Function を定義できます。

struct Counter
{
    int value;

    void reset()
    {
        value = 0;
    }
}

actor CounterExample
{
    action main()
    {
        Counter counter;
        counter.reset();
    }
}

現行コンパイラでは、Struct のメンバー Function からも通常の Mana の処理を記述できます。例えば Actor を引数として受け取り、Request を送ることもできます。

struct Helper
{
    void call(Actor target)
    {
        request(1, target->talk());
    }
}

Native メンバー Function

Struct の中には native Function も宣言できます。

struct Transform
{
    native void reset();
}

actor TransformExample
{
    action main()
    {
        Transform transform;
        transform.reset();
    }
}

呼び出し側の構文は通常のメンバー Function と同じです。

C++ 側との対応方法は Native Function と Native Functions Integration を参照してください。

Struct と Actor の違い

Struct は値をまとめるデータ型です。Actor のように独立した Action 実行主体にはなりません。

Struct Actor
データ型 実行主体
Function を持てる Action と状態を持てる
Request の対象ではない Request の対象になる
変数として保持する VM が Actor インスタンスを管理する

関連項目

Actor

actor は、Mana で独立して Action を実行する基本的な実行単位です。

Actor は状態を保持し、複数の Action を持ち、他の Actor から Request を受け取れます。

構文

actor ActorName
{
    members
}

例:

actor NPC
{
    int mTalkCount;

    action talk()
    {
        mTalkCount++;
        print("Hello\n");
    }
}

Actor のメンバー

Actor の中には、主に次のものを書けます。

  • Action
  • 変数
  • 定数
  • extend による Module の取り込み

例:

actor Guard
{
    int mAlertLevel;
    const int kMaxAlert = 3;

    action patrol()
    {
    }
}

Actor のメンバー変数は Actor の状態として保持され、Action が終了しても値は残ります。

VM 起動時の生成

通常の actor は Program Image のロード時に Mana VM がインスタンス化します。

これは phantom との大きな違いです。Phantom はロード時にはインスタンス化されず、C++ 側から明示的に生成します。

init と main

Mana VM はプログラムをロードした後、Actor に対して特別な Action を要求します。

現行VMでは、

  1. main を Priority 0 で予約
  2. init を最高優先度(2147483647)で Request

の順で全 Actor に送ります。

Actor がその Action を定義していない場合、その Request は実行されません。

actor Example
{
    action init()
    {
        print("init\n");
    }

    action main()
    {
        print("main\n");
    }
}

init は状態の初期化、main は通常の開始処理として使えます。全 Actor の初期化完了は待ちません。init がない Actor は main から開始します。init 内で自 Actor の低優先度 Action の完了を待つと、その Action を実行できず待機が続きます。

Actor 型

Actor への参照を保持する型は Actor です。小文字の actor は Actor 宣言専用のキーワードです。Actor は予約語のため、ユーザー定義名には使用できません。

Actor target;

Actor 参照に対して Action を指定できます。

request(1, target->talk());

Action 参照の詳細は Request を参照してください。

Namespace

Actor は namespace 内に定義できます。

namespace Game::NPC
{
    actor Shopkeeper
    {
        action talk()
        {
        }
    }
}

完全修飾名では Game::NPC::Shopkeeper となります。

Actor はキャラクターに限定されない

Actor は実行主体であり、ゲームキャラクター専用の概念ではありません。

例えば次のような役割にも使用できます。

  • イベント進行
  • UI 制御
  • ギミック
  • シーン管理
  • バトル進行
  • エフェクト制御

関連項目

Action

action は Actor が実行する処理単位です。

Function と異なり、Action は Request の対象になり、Priority に従って開始・中断・再開されます。

構文

action actionName()
{
    statements
}

例:

actor NPC
{
    action talk()
    {
        print("Hello\n");
    }
}

引数と戻り値

現在、Action は引数も戻り値も持ちません。() を付けた形が正式な構文で、省略形は糖衣構文です。action talk と action talk() は同じ Action を定義します。

action talk()
{
}

Actor 間で値を共有したい場合は、Actor の状態、グローバルデータ、Struct、Native Function などを用途に応じて使います。

Action を実行する

Action は request、awaitStart、await などから実行できます。

request(1, NPC->talk());

NPC->talk() は Action 参照です。

-> と ::

Action 参照には -> を使います。

NPC->talk()

namespace の修飾には :: を使います。

Game::NPC::Shopkeeper->talk()

旧形式の Actor::action() はコンパイラに互換構文として残っていますが、deprecated warning が出ます。新しいコードでは Actor->action() を使用してください。

init と main

init と main は VM 起動時に特別扱いされる Action 名です。

actor Example
{
    action init()
    {
    }

    action main()
    {
    }
}

現行VMは全 Actor に対して、init を最高優先度(2147483647)、main を Priority 0 で Request します。全 Actor の init 完了を待ってから main を開始する仕組みではありません。各 Actor は自分の init が終了すると、予約済みの Action を優先度順に実行します。

Action 実行中の定義済み値

Action では、現在の実行状態を表す定義済み値を利用できます。

  • self : 現在の Actor
  • sender : この Action を Request した Actor
  • priority : 現在実行中の Priority

例:

actor NPC
{
    action talk()
    {
        print("priority = %d\n", priority);
    }
}

sender は Request の送信元を保持するため、どの Actor から実行を依頼されたかを識別する用途に使えます。

詳細は 定義済みシンボル を参照してください。

Priority と中断

Actor が Action を実行中に別の Request を受けると、Priority によって実行順が決まります。

  • より高い Priority: 現在の Action に割り込む
  • より低い Priority: 現在の Action が終わるまで保留される
  • 同じ Priority: 現行VMでは同じ Priority の Request がすでに存在すると新しい Request は受理されない

Priority は数値が大きいほど高くなります。

詳細は Request と 実行制御 を参照してください。

Function との違い

Action Function
Actor の実行単位 通常のサブルーチン
Request の対象 通常の関数呼び出し
Priority を持つ Priority を持たない
中断・再開され得る 呼び出し元の処理として実行
現行構文では引数・戻り値なし 引数・戻り値を持てる

関連項目

Request

Request は、Actor に対して Action の実行を要求する仕組みです。

Mana では、Actor 同士の協調を通常の Function 呼び出しだけで表すのではなく、Request と Priority を使って表現します。

request

構文:

request(priority, actor_expression->actionName());

例:

request(10, NPC->talk());

第1引数は Priority、第2引数は Action 参照です。 () を付けた形が正式な構文で、空の () は糖衣構文として省略できます。Actor->action と Actor->action() はどちらも引数なしの Action を参照します。これ自体が Action を実行するのではなく、request が実行を要求します。Action の引数はまだ使用できません。

Priority は値が大きいほど高くなります。

Action 参照

推奨構文は -> です。

NPC->talk()

namespace を含む場合:

Game::NPC::Shopkeeper->talk()

Actor 型の式も使用できます。

Actor target;
request(1, target->talk());

Parser は expression->actionName() を Action 参照として受け付けます。

旧形式:

NPC::talk()

も互換構文として認識されますが、deprecated warning が出ます。新しいコードでは使用しないでください。

request の動作

通常の request は Action の完了を待ちません。

request(10, NPC->talk());
print("continue\n");

Request を送った Actor は、そのまま後続処理を続けます。

対象 Actor 側では Priority によって処理されます。

  • 要求 Priority が現在より高い: 現在の処理へ割り込む
  • 要求 Priority が現在より低い: 後で実行するため保持する
  • 要求 Priority が現在と同じ: その Priority の実行状態がすでに存在するため、新しい Request は受理されない

同じ Priority に複数の Action をキューとして積む仕組みではありません。

Request が受理されない条件

現行VMの Actor::Request では、少なくとも次の場合に Request が失敗します。

  • Priority が VM の最低割り込み Priority 以下
  • 対象 Actor が halt 状態
  • 対象 Actor が refuse() 状態
  • 同じ Priority の Request がすでに存在する
  • 指定した Action が存在しない

スクリプトの request 文自体には成否を返す戻り値はありません。

awaitStart

awaitStart(priority, actor_expression->actionName());

Request を送り、対象 Actor がその Priority を開始できる状態になるまで呼び出し側を待機させます。

要求が受理された場合、対象 Actor の現在 Priority が要求 Priority 以下になった時点で待機を解除します。要求した Action の最初の文が実行済みであることまでは保証しません。

awaitStart(10, NPC->talk());

await

await(priority, actor_expression->actionName());

Request を送り、その Priority の処理が完了するまで待機します。

要求が受理された場合、対象 Actor の現在 Priority が要求 Priority 未満になった時点で待機を解除します。要求ごとの完了通知を保持して待つ仕組みではありません。

await(10, NPC->talk());

await の要求が受理されない場合

awaitStart と await は、最初の Request が受理されなければ待たずに次へ進みます。同じ Priority が空くまで再要求する命令ではありません。待機から戻ったことだけでは、要求した Action が実行されたことや、その行動が成功したことを保証しません。

自分自身への await

awaitStart と await で self を対象にすると、現行VMはスクリプトエラーにします。

await(10, self->talk()); // error

自分自身を待機対象にすると、待機中の Actor 自身が進まなければ完了できないためです。

通常の request で自分自身へ Request することは可能です。

sender

Request が受理されると、送信元 Actor は対象 Action の sender として記録されます。

actor NPC
{
    action talk()
    {
        // sender はこの Action を Request した Actor
    }
}

VM 起動時のシステム Request など、送信元 Actor が存在しない場合もあります。

join との違い

join は新しい Request を送りません。

join(0, NPC);

対象 Actor がすでに実行している処理の Priority を監視し、指定 Priority 以下になるまで待ちます。

命令 新しい Request 待機
request 送る しない
awaitStart 送る 開始可能になるまで
await 送る 完了まで
join 送らない 既存 Priority が指定値以下になるまで

関連項目

実行制御

このページでは、Action の待機・中断・再開や Request の受付状態を制御する文をまとめます。

yield()

yield();

現在の Action を終了せず、その時点の実行をいったん中断して Mana VM に制御を返します。

次に実行機会が来たとき、yield() の後から再開します。

print("step 1\n");
yield();
print("step 2\n");

yield() は時間指定の待機ではありません。「1フレーム待つ」「1秒待つ」とは保証されず、再実行のタイミングはホスト側が VM をどのように更新するかにも依存します。

join

join(priority, actorExpression);

新しい Request は送らず、既に動作している対象 Actor を待ちます。

join(0, NPC);

現行VMでは、対象 Actor の現在 Priority が指定 Priority 以下になるまで待機します。

await が「Request を送ってその完了を待つ」のに対し、join は「既存の実行状態を待つ」ための命令です。

rollback

rollback priorityExpression;

例:

rollback 1;

rollback は現在の Action 実行を終了し、Actor に保存されている Priority の実行状態を指定値の方向へ巻き戻します。

現行VMの Actor::Rollback は、現在の Priority を解放し、必要に応じて指定値より上側に残っている実行状態を取り除いた後、再開可能な保存済み Action を復元します。再開できる Action がなければ Actor は停止状態へ戻ります。

通常の Action 終了時にも VM 内部では同じ Rollback 機構が使われています。

rollback は制御フローへの影響が大きいため、通常の順次処理よりも Priority の実行状態を明示的に戻したい場合に使用します。

halt()

halt();

現在の Actor の実行を停止します。

現行VMでは Actor を halt 状態にし、保持している割り込み実行状態をクリアします。halt 状態の Actor に対する新しい Request は Actor::Request で受理されません。

VM 側で Actor を Restart すると halt 状態は解除されます。

refuse()

refuse();

Actor を Request 拒否状態にします。

現行VMでは Actor の Refused フラグを立て、それ以降に届く新しい Request を受理しなくします。

既に登録済みの実行状態をクリアする命令ではありません。

comply()

comply();

refuse() による Request 拒否状態を解除します。

現行VMでは Refused フラグをクリアします。

refuse();
// 新しい Request を拒否する区間
comply();

lock

構文:

lock statement

通常はブロックと組み合わせます。

lock
{
    // statements
}

現行コンパイラは lock の開始時に NonPreEmptive、終了時に PreEmptive 命令を生成します。現行VMでは、これらは現在の Priority 実行状態にある Synchronized フラグを ON / OFF します。

重要な点として、現在の Actor::Request 実装はこの Synchronized フラグを Request の受付判定や Priority の割り込み判定には直接参照していません。

そのため、現行実装の lock を C++ の mutex や「必ず割り込まれない atomic 区間」と同じ意味として扱わないでください。リファレンス上は、現在の実装では同期実行フラグを切り替える構文として扱います。

request / awaitStart / await

これらは Action の実行要求と待機を組み合わせた命令です。

request(10, NPC->talk());
awaitStart(10, NPC->talk());
await(10, NPC->talk());

詳細は Request を参照してください。

Priority の取得

現在実行中の Priority は定義済み値 priority で取得できます。

print("%d\n", priority);

関連項目

Module

module は、複数の Actor で再利用する Action やメンバー定義をまとめるための仕組みです。

Module 自体は実行主体ではありません。actor から extend することで、Module に含まれる定義を Actor 側で利用します。

構文

module CommonActions
{
    action greet()
    {
        print("Hello\n");
    }
}

actor Villager
{
    extend CommonActions;
}

extend の基本形は次の通りです。

extend ModuleName;

Module に書けるもの

現行文法では、Module の本体は Actor と同じ actions 文法を使用します。

そのため、Module には次のような定義を書けます。

  • Action
  • メンバー変数
  • 定数
  • extend
module Talkable
{
    int mTalkCount;

    const int kTalkPriority = 10;

    action talk()
    {
        mTalkCount = mTalkCount + 1;
    }
}

namespace 内の Module

Module は namespace 内に定義できます。

namespace Game::NPC
{
    module Talkable
    {
        action talk()
        {
        }
    }
}

完全修飾名で指定できます。

actor Villager
{
    extend Game::NPC::Talkable;
}

using で namespace を探索対象へ追加した場合は、短い名前でも参照できます。

using Game::NPC;

actor Villager
{
    extend Talkable;
}

Module は Actor ではない

Module は Program Image に定義情報として含まれますが、通常の Actor のように VM ロード時に実行主体として生成されません。

そのため、Module 自体へ request を送って独立実行させる用途ではなく、Actor へ共通定義を追加するために使用します。

名前の衝突

extend によって取り込まれる定義と Actor 側の定義で同名シンボルが発生した場合、コンパイラのシンボル解決規則の対象になります。

再利用用 Module では、同名定義による上書きを前提にせず、役割が明確に分かれる名前を使用することを推奨します。

関連項目

Phantom

phantom は、VM 起動時にはインスタンスを作らず、C++ 側から必要なタイミングで Actor を生成するためのテンプレートです。

構文

phantom EnemyTemplate
{
    int mHp;

    action main()
    {
    }

    action damage()
    {
    }
}

文法上の本体は actor と同じ形式で、Action やメンバーを定義できます。

Actor との違い

通常の actor は Program Image のロード時に VM がインスタンスを生成し、Actor 一覧へ登録します。

phantom はロード時には Actor 一覧へ生成されません。VM は Phantom の定義情報を保持し、C++ 側から明示的に生成されたときに Actor インスタンスを作ります。

actor phantom
VMロード時に生成 される されない
Actionを定義 できる できる
Actor変数を持つ できる できる
主な用途 常駐する実行主体 動的生成用テンプレート

C++ から生成する

現行 VM API では CreateActorFromPhantom を使用します。

std::shared_ptr<mana::Actor> enemy =
    vm->CreateActorFromPhantom("EnemyTemplate", "Enemy01");

第1引数は Phantom の定義名、第2引数は生成する Actor の名前です。

生成された Actor は VM の Actor 一覧へ登録され、Phantom に定義されていた Action と Actor 変数領域を持ちます。

自動起動との違い

VM が Program Image をロードすると、通常の Actor に対して init と main の Request を行います。

Phantom はその時点では Actor として生成されていないため、このロード時の一括 Request の対象にはなりません。

動的生成した Actor をどのように初期化・開始するかは、生成側の C++ コードと VM API の利用方法を含めて設計してください。

スクリプトからの生成

現行言語には、Mana スクリプトから Phantom を直接インスタンス化する構文はありません。

Phantom の生成はホスト側 C++ API の責務です。

エラー

存在しない Phantom 名を CreateActorFromPhantom に渡すと、VM は Phantom not found の実行時エラーを発生させます。

関連項目

Namespace と using

namespace は、Actor、Module、Struct、Function、変数、定数などの名前を階層化して整理するための仕組みです。

namespace

namespace Game::AI
{
    actor Enemy
    {
        action think()
        {
        }
    }
}

完全修飾名は :: で区切ります。

Game::AI::Enemy

:: は namespace の名前を修飾する演算子です。Action 参照で使う -> とは役割が異なります。

request(1, Game::AI::Enemy->think());

using で namespace を探索対象へ追加する

using Game::AI;

actor Controller
{
    action main()
    {
        request(1, Enemy->think());
    }
}

using Game::AI; により、未修飾名 Enemy の解決候補として Game::AI::Enemy が追加されます。

Actor / Module を using する

現行実装では using の対象として namespace だけでなく Actor / Module も解決できます。

namespace Game::AI
{
    actor Enemy
    {
        action think()
        {
        }
    }
}

using Game::AI::Enemy;

この場合、最後の名前 Enemy がエイリアスとして現在のスコープへ追加されます。

using のシンボル対象は現行実装では Actor / Module に限定されています。Struct や通常Functionなどを同じ方式で using する構文としては扱いません。

スコープ

using は、その宣言が属する namespace スコープの名前解決へ影響します。

namespace を抜けると、その内側で追加した using スコープも終了します。

前方参照

Mana はパース後に全体のシンボルと namespace を解析するため、後方で定義される namespace や Actor / Module を using から参照できます。

using Game::AI;

actor Controller
{
    action main()
    {
        request(1, Enemy->think());
    }
}

namespace Game::AI
{
    actor Enemy
    {
        action think()
        {
        }
    }
}

曖昧な名前

複数の候補が同じ未修飾名として見つかる場合、コンパイラは曖昧な参照としてエラーにします。

代表的な診断には次があります。

  • ambiguous using
  • ambiguous symbol reference
  • ambiguous type reference
  • ambiguous actor reference
  • unresolved using

曖昧になる場合は完全修飾名を使用してください。

ファイルと namespace は別の概念

ソースファイルを分けても、自動的に namespace が作られるわけではありません。

  • ファイル: ソースコードを物理的に整理する単位
  • namespace: 名前を論理的に整理する単位

複数ファイルを一つの Program Image へまとめながら、namespace で名前の衝突を避けられます。

関連項目

Native Function

native は、Mana スクリプトから C++ 側に登録された外部関数を呼び出すための宣言です。

グローバル native 関数

native int nativeAdd(int a, int b);

native 関数は宣言だけを持ち、Mana 側に本文を書きません。

actor Main
{
    action main()
    {
        int value = nativeAdd(10, 20);
        print("%d\n", value);
    }
}

実行時には VM が関数名から C++ 側の登録済み関数を検索して呼び出します。

C++ 側では、例えば RegisterFunction で登録できます。

vm->RegisterFunction("nativeAdd", &OnNativeAdd);

Struct の native メソッド

native は Struct メンバーとしても宣言できます。

struct Vec
{
    float x;
    float y;

    native void normalize();
}

void update(Vec value)
{
    value.normalize();
}

Struct の native メソッドは、外部関数名として Struct名::メソッド名 の形式で解決されます。

Vec::normalize

VM の外部関数コールバックには、実行中 Actor に加えて Struct インスタンスを指すポインタが渡されます。

宣言構文

native 戻り値型 関数名(引数...);

Struct 内では次の形式です。

struct TypeName
{
    native 戻り値型 メソッド名(引数...);
}

通常Functionとの違い

通常の Mana Function は Mana Compiler が生成した命令列へ分岐して実行されます。

native Function は Mana 側に本文を持たず、実行時に名前で登録済み外部関数を検索します。

C++ 側の登録型

現行 VM の基本コールバック型は次の形式です。

std::function<void(const std::shared_ptr<mana::Actor>& actor,
                   void* structPointer)>

引数と戻り値の受け渡しは Actor の外部関数向け API と VM スタックを通して行われます。詳細は Integration の Native Functions で扱います。

外部関数が見つからない場合

VM に同名の外部関数が登録されていない場合、VM は外部関数が見つからないことをエラーとして報告します。

Script側の宣言とC++側の登録名を一致させてください。

注意点

native は C++ と Mana の境界です。Mana 側の宣言と C++ 側の引数・戻り値の扱いを一致させる必要があります。

特に Struct native メソッドでは、通常のグローバル native 関数と異なり Struct インスタンスへのポインタが渡されます。

関連項目

ソースファイルと import / include

Mana のソースファイルは通常 .mn 拡張子を使用します。

大きなプログラムでは、import または include を使って複数のソースファイルを一つのコンパイルへ取り込めます。

import

import "npc.mn";

import は指定したソースを読み込みます。同じ解決済みパスのソースがすでに読み込まれている場合、現行Lexerは2回目以降の読み込みを省略します。

そのため、通常のファイル分割には import を推奨します。

main.mn
 ├─ import "npc.mn"
 └─ import "event.mn"

取り込まれた定義は同じコンパイル結果にまとめられ、一つの Program Image が生成されます。

include

include "common.mn";

include も指定したソースを読み込みますが、import と異なり重複読み込みの抑止を行いません。

同じファイルを複数回 include すると、同じ宣言を複数回解析することになり、重複定義エラーの原因になる場合があります。

通常は import を使用し、同じソースを意図的に再読込する必要がある場合だけ include を検討してください。

パスの解決

読み込み対象のパスは SourceResolver によって解決されます。

標準のファイルベースの利用では、現在読み込んでいるソースファイルの場所を基準に相対パスを解決できます。

project/
├─ main.mn
└─ actors/
   └─ npc.mn
import "actors/npc.mn";

組み込み時に独自 SourceResolver を使う場合、実際のパス解決規則はその実装に依存します。

前方参照

Mana は取り込まれたソースを含む構文木を作成した後、シンボルと namespace のセマンティック解析を行います。

そのため、Actor、Module、namespace などは別ファイルに分かれていても、解析可能な名前であれば定義順だけを理由に参照できなくなる設計ではありません。

// main.mn
import "enemy.mn";

actor Controller
{
    action main()
    {
        request(1, Enemy->think());
    }
}
// enemy.mn
actor Enemy
{
    action think()
    {
    }
}

namespace とは独立している

ファイル名やディレクトリ構造から namespace が自動生成されることはありません。

actors/enemy.mn

というファイルを作っても、自動的に actors::Enemy になるわけではありません。

名前の論理的な整理には namespace を明示的に使用します。

CLI の強制インクルード

mana コマンドには -I filename があり、ソースコードを書き換えずにコンパイル対象へファイルを強制追加できます。

mana -I common.mn main.mn

-I は複数回指定できます。

関連項目

定義済みシンボル

Mana には、実行中の Actor や Request の文脈を参照するための定義済みシンボルがあります。

一覧

名前 型 / 種類 意味
self Actor 現在実行している Actor
sender Actor 現在の Action を Request した Actor
priority int 現在実行中の Action の Priority
this Struct の受信側 Struct メンバーFunctionで現在の Struct インスタンスを参照するための予約語
Nil Nil 空の参照を表す特殊値

self

actor Worker
{
    action main()
    {
        request(1, self->update());
    }

    action update()
    {
    }
}

self は現在の Actor を表します。

Action や Actor 上で実行される Function から、自分自身へ Request を送る場合などに使用できます。

sender

actor Receiver
{
    action receive()
    {
        request(1, sender->reply());
    }
}

sender は、その Action を Request した Actor を表します。

ただし VM 自身が起動時に送る init / main などのシステム Request では、送信元 Actor が存在しません。sender が常に有効な Actor を指すことを前提にしないでください。

priority

actor Worker
{
    action work()
    {
        print("%d\n", priority);
    }
}

priority は現在実行している Action の Priority を int として取得します。

Request に指定した Priority と、現在どの割り込みレベルで動いているかを調べたい場合に使用します。

this

this は Struct のメンバーFunctionで、現在の Struct インスタンスを参照するための予約語です。

コンパイラ内部では this という受信側識別子として解決されます。通常のグローバルFunctionやActionで一般的な Actor 自己参照として使うものではありません。Actor 自身を参照する場合は self を使用します。

Nil

Nil は空の参照を表す特殊値です。

Nil

先頭の N は大文字です。 現行Lexerは Nil を予約語として認識し、nil は同じトークンとして扱いません。

Nil は通常の数値定数とは異なる専用型を持ち、定数式では使用できません。

true / false

真偽値リテラルとして true と false も使用できます。

bool visible = true;
bool finished = false;

これらは bool 型のリテラルです。

関連項目

CLI

mana は、Mana ソースのコンパイル、Program Image の出力、Program Image の実行を行うコマンドラインツールです。

基本形

mana [options] input

コンパイルして実行

mana main.mn

出力ファイルを指定しない場合、ソースをコンパイルした後、その Program Image をそのまま Mana VM で実行します。

Program Image を保存

mana main.mn -o game.mx

-o filename を指定すると、コンパイル結果の Program Image をファイルへ保存し、自動実行は行いません。

ファイル名の拡張子は任意です。

現行CLIでは、値を付けずに -o を指定した場合、入力ソースと同じベース名の .mx ファイル名を自動生成します。

mana main.mn -o

この場合は main.mx が出力先になります。

Program Image を実行

mana --execute game.mx

--execute を指定すると、入力を Mana ソースではなくコンパイル済み Program Image として読み込み、VM で実行します。

現行 driver/Main.cpp では短縮形 -e は実装されていません。

C++ 型宣言ヘッダーを生成

mana main.mn -t public_types.h

-t filename は、コンパイラが生成する公開型宣言をファイルへ出力します。

値を付けずに -t を指定した場合、入力ソースと同じベース名の .h が出力先になります。

mana main.mn -t

この場合は main.h が生成されます。

強制インクルード

mana -I common.mn main.mn

-I filename は、指定ファイルをコンパイル対象へ強制追加します。

複数回指定できます。

mana -I common.mn -I platform.mn main.mn

情報表示

mana --help
mana --version
mana --copyright
オプション 内容
--help 使用方法を表示
--version Mana のバージョンを表示
--copyright 著作権表示

公開オプション一覧

オプション 内容
-o filename Program Image の出力先
-t filename C++ 型宣言ヘッダーの出力先
-I filename 強制インクルード。複数指定可
--execute 入力を Program Image として実行
--help ヘルプ表示
--version バージョン表示
--copyright 著作権表示

終了コード

コンパイルに失敗した場合や出力ファイルを保存できない場合、CLI は失敗を表す終了コードを返します。

ビルドスクリプトやCIでは終了コードとコンパイラ診断を確認してください。

関連項目

Clone this wiki locally