# LSLプリプロセッサ

LSL でのスクリプトの開発は面倒な作業です。特に、同じものを何度も使用するスクリプトを作成し、古いスクリプトの一部を新しいスクリプトにコピーし続ける場合はなおさらです。また、1 つのプロジェクト内であっても、スクリプトの一部をサブスクリプトにコピーしなければならないことがよくあります。（例: コマンドの識別子としてのリンク メッセージ番号）これらの番号を変更したり、コードの古いバージョンを更新してバグを修正したりすると、同じ関数の異なるバージョンが使用されるか、古いバージョンで見つかったバグが新しいバージョンでは修正されない可能性があります。LSLプリプロセッサは、これらの問題の多くを回避するのに役立つツールです。

デバッグ ステートメントの追加と削除も、プリプロセッサが役立つ機能です。通常、すべてが正常に動作しているかどうかを確認するためにスクリプトにデバッグ関数が含まれており、リリース前にそれらの関数を削除します。しかし、これ自体が新たな間違いの場所を生み出すだけでなく、最終審査時に見落とされたためにリリース日にスクリプトにデバッグ出力が残る機会を生み出します。 LSLプリプロセッサを使用すると、最終リリースにデバッグ出力が残らないようにする非常に簡単な方法が得られます。

## 有効化

### Firestormの場合

「環境設定」パネル（<kbd>CTRL</kbd>+<kbd>P</kbd>を開き、「Firestorm」タブから「製作１」を選択します。「LSLプリプロセッサを有効にする」チェックボックスをオンにします。

好みに応じて、プリプロセッサオプションを有効または無効にします。

### Alchemyの場合

スクリプトエディタで任意のスクリプトを開き、「ファイル」メニューから「スクリプトエディタの初期設定」を選んで「LSLプリプロセッサを有効にする」のチェックボックスをオンにします。

好みに応じて、プリプロセッサオプションを有効または無効にします。

エディタを開き直すと有効になります。

## 設定

- スクリプトの最適化
- switch() ステートメント
- 遅延（Lazy）リスト

#includes はローカルディスクから実行されます。これにより、下のフィールド「プリプロセッサのインクルードパス」が有効になります。「参照」ボタンをクリックしてファイル ピッカーを開き、すべてのLSLインクルードファイルを保存するフォルダーを選択できます。

## 使い方

セットアップ後、LSLエディターに「スクリプト」と「プリプロセス済み」（古いバージョンでは「後処理済み」）という２つのタブが表示されます。１つ目はアクティブなスクリプトウィンドウです。２番目は、プリプロセッサの出力、つまりスクリプトに保存される内容を示しています。処理されたスクリプトには、最初のコメントブロックに記述した内容のソース コード全体と、このコメント ブロックに続く処理された出力が含まれます。これにより、前処理をまったくサポートしていない古いバージョンの Firestormや他のビューアとの下位互換性が確保されます。

注: LSL/Mono コンパイラは前処理後にスクリプトを取得するため、エラーメッセージの行番号は処理されたスクリプトを指します。したがって、コンパイラ エラーが発生した場合は、元のスクリプトではなく、必ず「プリプロセス済み」タブでエラーを探してください。

## 簡単な例

インクルードフォルダーに debug.lsl という名前のファイルを作成します。次のスニペットをこのファイルにコピーします。

```lsl
#ifdef DEBUG
debug(string text)
{
    llOwnerSay(text);
}
#else
#define debug(dummy)
#endif
```

次に、新しいLSLスクリプトを作成し、次のコードをスクリプト エディターにコピーします。

```lsl
#define DEBUG
#include "debug.lsl"
 
default
{
    state_entry()
    {
        debug("LSLプリプロセッサを使用したデバッグ.");
    }
}
```

スクリプトを保存します。チャットコンソールに「LSLプリプロセッサを使用したデバッグ」というメッセージが表示されます。次に、LSLスクリプトの最初の行を次のように変更します。

```lsl
#undef DEBUG
```

スクリプトを再度保存します。デバッグ メッセージが消えていることがわかります。これは、すべてのデバッグ コードを１行で有効または無効にする非常に便利な方法です。実際に何が起こっているかを理解するには、「プリプロセス済み」タブを見てください。デバッグ行が「;」だけ消えていく様子がはっきりとわかります。

## スクリプト最適化

ただし、ファイルを含めることには欠点が 1 つあります。必要かどうかにかかわらず、ファイルの完全な内容を取得できます。ただし、LSLプリプロセッサは、コード内で実際に使用したもののみを保持し、スクリプト内で参照しなかったすべてのグローバル関数と変数を削除する最適化手法を使用します。これにより、スクリプトに多くの未使用のコードによる負担がかからなくなります。

## switch / case構文

また、プリプロセッサは、これまで欠落していた 一連の新しいコマンド、つまり他の多くの言語で知られている switch/case 構造をLSLエディタに追加します。

あるいは、スクリプトの先頭に USE_SWITCHESマクロを含めて、これを有効にすることもできます。

```lsl
#define USE_SWITCHES
```

switch/case は、if(…) else if() チェーンの便利な代替品です。さらに、スイッチ/ケースはあるケースから別のケースへの「フォールスルー」をサポートしているため、異なる条件で複数のケースを連鎖させることができます。 Break ステートメントはフォールスルーを防ぐために使用されます。どのケースも switch() 条件に一致しない場合は、デフォルトのケースが使用されます。

例：

```lsl
default
{
    state_entry()
    {
        integer i;
        switch(i)
        {
            case 1:
            {
                llOwnerSay("1");
                //case 2も実行されます。
            }
            case 2:
            {
                llOwnerSay("1 or 2");
                // ここより下は実行されません
                break;
            }
            case 3:
            {
                llOwnerSay("3");
                // defaultでも実行されます。
            }
            default:
            {
                llOwnerSay("3かdefault");
            }
        }
    }
}
```
ブロック (一連のステートメントを開始する開始中括弧 {) が直後に来る場合、 defaultまたはcase の後のコロン`:`は不要であることに注意してください。

例:
```lsl
switch(x)
{
    case 1: // コロンが必要です
    case 2  // 中括弧が次に開くため、コロンはオプションです
        {
            llOwnerSay("xは、1か2です。");
            break;
        }
    default  // 中括弧が次に開くため、コロンはオプションです
        {
            llOwnerSay("xは、1と2以外です。");
        }
}
```

## 遅延（Lazy）リスト

LSLでリストインデックスに値を割り当てるのは常に面倒な作業です（llListReplaceList()が必要です）。遅延リストは、次のように言う方法を提供することで、少し役立ちます。

```lsl
myList[index]=value;
```
リスト要素を取得するには、その前に型キャストを付けます。

例:
```lsl
list a = [1, 3, "blah", <5.31, 131.7, 11.331>];
llOwnerSay((string)a[2]); // outputs: blah
llOwnerSay((string)((vector)a[3])); // outputs: <5.31000, 131.70000, 11.33100>
llOwnerSay(llList2CSV((list)a[1, 2])); // outputs: 3, blah
```

これは以下のように変換されます：
```lsl
list a = [1, 3, "blah", <5.31, 131.7, 11.331>];
llOwnerSay(llList2String(a, 2));
llOwnerSay((string)(llList2Vector(a, 3)));
llOwnerSay(llList2CSV(llList2List(a, 1, 2)));
```

要素を設定する関数、lazy_list_set() はユーザーがオーバーライドできます。プロトタイプは次のようになります。
```lsl
lazy_list_set(list target, integer index, list value)
```

現在の関数では、現在のリストの長さを超える要素を設定すると、中間の空きスペースが整数のゼロで埋められます。これは一部のアプリケーションでは望ましくない場合があります (たとえば、別のデフォルト値が必要な場合があります)。そのため、関数をオーバーライドする必要がある場合があります。

```lsl
#define USE_LAZY_LISTS
```

## プリプロセッサのコマンドとマクロ
プリプロセッサは次のコマンドを理解します。

```lsl
#define 
#undef 
#ifdef 
#ifndef 
#if 
#elif 
#else 
#endif 
#warning 
#error 
#include
```
他にもいくつかありますが、これらはLSL 内では実際には役に立ちません。

さらに、スクリプト内で次のマクロを使用すると、デバッグに役立ち、その他の有用な情報も得られます。

* `__FILE__` - インクルード キャッシュに表示されるスクリプトの完全なパス。トップ スクリプトでは、その名前のみが使用されます。
* `__LINE__` - 現在のスクリプトの展開されている行。これは 0 行目から始まります。
* `__SHORTFILE__` - 完全なファイルパスを除いた現在のスクリプトの名前
* `__AGENTID__` - スクリプトをコンパイルするエージェントのキーの文字列カプセル化バージョン
* `__AGENTKEY__` - 上記と同じ、旧バージョン
* `__AGENTIDRAW__` - スクリプトをコンパイルするエージェントのキーの非文字列カプセル化バージョン
* `__AGENTNAME__` - スクリプトをコンパイルするエージェントのフルネームの文字列でカプセル化されたバージョン
* `__ASSETID__` - 現在のスクリプトの資産 ID の文字列でカプセル化されたバージョン。まれに、`NOT IN WORLD`または文字列でカプセル化されていない null キーを返す場合があります。
* 
このリストを提供してくれた Zwagoth Klaar に感謝します。 __FILE__ と __LINE__ はBoost::waveライブラリからのものです。

### #define文

#define は、スクリプトを保存およびコンパイルするときに置き換えられる大文字と小文字を区別するマクロを作成します。これは、単純な定数、文字列、さらには関数として適用できます。ソース コード内のリテラル テキストの置き換えが行われます。このため、マクロ内の`;`には注意してください。通常、これらは害を及ぼしませんが、1 行の条件文内で、たとえば行末に`;;`を作成すると、予期しない問題が発生する可能性があります。

次の例では、 #defineの 機能の概要を示します。プリプロセッサがソースから何を作成するかを確認するには、「プリプロセス済み」タブを確認してください。

例1:
```lsl
#define CHANNEL 12345 
llOwnerSay((string) CHANNEL); // CHANNEL は、上記の #define で指定されたリテラル 12345 に置き換えられます
```

#define は、置換コードに適用するパラメータを取ることもできます。

例2:
```lsl
#define OS(b,c) llOwnerSay(b+c) 
 OS("テスト","123"); // llOwnerSay("Test"+"123") に展開されます。
```

例 3: パラメータから文字列を作成する

```lsl
#define OS(a) llOwnerSay(#a) 
OS(1234); // llOwnerSay("1234"); に展開されます。
```

例 4: `##`を使用してパラメータを連結する
```lsl
#define OS(a,b) llOwnerSay((string) a##b) 
OS(1234,5678); // 次のように展開されます: llOwnerSay((string) 12345678);
```

例5: `\`を使用して複数行のマクロを作成する

```lsl
#define OS(a,b) if (a > 1) {\ 
  llOwnerSay((string)a);\ 
} else {\ 
  llOwnerSay((string)b);\ 
} 
OS(1234,5678); // 次のように展開されます: if (1234 > 1) { llOwnerSay((string)1234);} else { llOwnerSay((string)5678);};
```

単一行のコメント`//`は、望ましくない影響を引き起こします。代わりに複数行のコメントを使用してください。

### #undef

#define で以前に設定したマクロを削除します。最初からマクロが作成されていない場合は、何も起こりません。これは、デバッグ用にソース コードの一部を有効または無効にするのに便利な方法です。使用方法の例については、上記の #define を参照してください。

### #ifdef と #ifndef、#else と #endif

このコマンドは、#else および #endif に関連する条件付き前処理の一部です。 #ifdef は、マクロが以前に #define されているかどうかをチェックします。マクロに実際に値が割り当てられているかどうかは関係ありません。 #define する必要があるだけです。存在する場合、#ifdef 以降、#endif または #else までのすべてのコードがコードに置き換えられます。 #ifndef はまったく逆のことを行います。マクロが存在しない場合、コードはプリプロセッサに入ります。

例：
```lsl
#define OWNER_ONLY 
... 
#ifdef OWNER_ONLY
key var=llGetOwner(); 
#else
key var=llDetectedKey(0); 
#endif
```

### #if と #elif

これらは条件付き前処理コマンドでもあります。これらは一般的な条件を受け取り、その条件が TRUE と評価された場合にコードをプリプロセッサに渡します。 #else と組み合わせて使用することもできます。 #elif は else if と同等です。

例：
```lsl
#define DEBUGLEVEL 2 
 
#if DEBUGLEVEL==1 
llOwnerSay("ポイントに到達しました"); 
#elif DEBUGLEVEL==2 
llOwnerSay("ここにはさらに多くのデータがあります"); 
#else 
llOwnerSay("不明なデバッグ レベル: "+(string) DEBUGLEVEL); 
#endif
```

### #waningと#error

これら 2 つのコマンドは、コンパイラ ウィンドウに文字列を表示して、特定の問題について警告するか、致命的なエラーのためにコンパイルを直ちに停止します。現時点では、#warning と #error の両方によりコンパイラが停止します。 #warning によってコンパイラーが将来的に完了まで続行できるかどうかはまだ不明です。

注: これらのコマンドのいずれかがプリプロセッサによってヒットされた場合、スクリプトは保存されません。

例１：
```
#warning このインクルードファイルは時代遅れです！
```

例２：
```lsl
#error このインクルードファイルはもう動きません。アップデートしてください。
```

### #include

これはおそらくLSLプリプロセッサ の最も強力な機能です。ハードディスクまたはインベントリ内の同じフォルダ ツリーからソースコードファイル全体を、作業中のスクリプトに含めます。この機能の小さな例を上に示します。

#include は、Firestormの場合、「環境設定」＞「Firestorm」＞「制作」＞「プリプロセッサのインクルードパス」、Alchemyの場合「ファイル」＞「LSLスクリプトエディタの初期設定」＞「プリプロセッサのインクルードパス」で設定されたインクルード パスを基準としたファイル名を取ります。サブフォルダー内のファイルもインクルードできます。インベントリからスクリプトをコンパイルする場合、プリプロセッサは作業中のインベントリ パスを検索し、サブフォルダーまで降りて、参照されているインクルード ファイルを見つけます。使用されていない関数とグローバル変数の宣言は、オプティマイザーによって削除されます。

相対パス ./ および ../ も使用できます。静的な最上位フォルダー名を必要としないプロジェクトに役立ちます。

```lsl
#include "command_ids.lsl"
#include "general_functions.lsl"
#include "hud/layout.lsl"
#include "../generic_lib.lsl"
#include "./classes/lawmower.lsl"
```
注: #includes 内からファイルを含める場合は注意してください。実際に追跡していない場合は、ファイルを 2 回 #include する可能性があり、問題が発生する可能性があります。この問題は通常、いわゆる「インクルード ガード」を使用することで解決されます。基本的には #define マクロを設定し、それがすでに存在するかどうかを確認する条件付きコンパイルがあります。そうでない場合は、ファイルの内容を #include します。設定されている場合は、内容を無視してください。

```lsl
#ifndef SCRIPT_NAME_LSL
#define SCRIPT_NAME_LSL
your_script_starts_here()
{
}
#endif //SCRIPT_NAME_LSL
```

これらの #ifndef、#define、および #endif コマンドは、ファイルが実際に #include で参照される頻度に関係なく、#include が 1 回だけ実行されるようにします。

## 型定義

プリプロセッサには、LSLでより単純な型キャストを使用できるようにする事前定義マクロがいくつかあります。例えば：

* `integer(var)` - ((integer)(var)) に展開されます
* `float(var)` - ((float)(var)) に展開されます


## 既知の問題点
ハードドライブから#endifを最後の行（改行なし）として含むファイルをインクルードすると、改行がないという警告は表示されず、ステートメントエラーが表示されます。

これは、ファイルの末尾に改行を追加することで修正できます。
「テキスト圧縮を有効にする」はスクリプトを壊します! (少なくとも現在の形式では。将来的には修正または削除される可能性があります。)

---

## Page Navigation

- Canonical URL: https://sl.logue.be/ビューア/機能/LSLプリプロセッサ
- Permalink: https://sl.logue.be/664eb087997fd7ce75c398b8
- Parent: [機能](/66471fe86f76b0a2f2db4a10.md)
- Children: 0 total
- Total descendants: 0
- Siblings: 11 total
  - [RLVa](/664720146f76b0a2f2db4c27.md)
  - [UIフォント変更](/6647206f6f76b0a2f2db51be.md)
  - [アニメーションオーバーライド](/664720886f76b0a2f2db54b6.md)
  - [エリアサーチ](/68171e6c30cfaf38c42a9910.md)
  - [ポーザー](/664723b66f76b0a2f2db61dd.md)
  - [ミュージック・ティッカー](/664ef6a1997fd7ce75c3f255.md)
  - [ライトボックス](/664e8fc7997fd7ce75c364e2.md)
  - [リージョントラッカー](/664723ee6f76b0a2f2db64fe.md)
  - [取引ログ](/66de8362bc43d08fd2a450f8.md)
  - [検索](/674d8a96cc5135441028936a.md)
  - [自動応答](/6677bd78d2062716ac0be2a9.md)
- Last updated: 2024-09-09T06:39:56.823Z by logue
- Full page listing (all children regardless of count): https://sl.logue.be/_api/v3/page-listing/children?id=664eb087997fd7ce75c398b8
