# メモリ管理・変数・クラスシステム

> memory.c のブロックアロケータ（alloc_memory / garbage_collection）、variable.c の変数番号管理、value.c の型解決（整数・浮動小数点・文字列）、class.c のオブジェクト生成・フィールドアクセス・静的メンバ管理、globalvars.c のグローバル状態を解説します。

- Repository: machikania/phyllosoma
- GitHub: https://github.com/machikania/phyllosoma
- Human wiki: https://grok-wiki.com/public/wiki/machikania-phyllosoma-fab169c4381c
- Complete Markdown: https://grok-wiki.com/public/wiki/machikania-phyllosoma-fab169c4381c/llms-full.txt

## Source Files

- `memory.c`
- `variable.c`
- `class.c`
- `value.c`
- `globalvars.c`
- `integer.c`
- `float.c`

---

<details>
<summary>関連するソースファイル</summary>
このWikiページの生成に使用したファイル:
- [memory.c](memory.c)
- [variable.c](variable.c)
- [class.c](class.c)
- [value.c](value.c)
- [globalvars.c](globalvars.c)
- [integer.c](integer.c)
- [float.c](float.c)
- [compiler.h](compiler.h)
</details>

# メモリ管理・変数・クラスシステム

Phyllosoma（KM-BASIC for ARM）は、ARM Cortex-M（RP2040 など）の制約された環境向けに設計されたBASICコンパイラです。本ページでは、実行時メモリの確保と解放を担う `memory.c` のブロックアロケータ、コンパイル時の変数番号管理を行う `variable.c`、整数・浮動小数点・文字列の型解決を行う `value.c` / `integer.c` / `float.c`、クラスのオブジェクト生成・フィールドアクセス・静的メンバ管理を実装する `class.c`、そしてすべてのモジュールが共有するグローバル状態を保持する `globalvars.c` を解説します。

これらのモジュールは密接に連携しており、コンパイル時に ARM Thumb-2 マシンコードを直接生成しながら、実行時に必要なデータ構造を管理します。メモリアロケータはヒープ領域を直接管理し、標準の `malloc/free` を一切使用しないことで、予測可能なメモリレイアウトと組み込みデバイス向けの小フットプリントを実現しています。

---

## グローバル状態の概観（globalvars.c）

システム全体で共有される主要なグローバル変数は `globalvars.c` で定義されています。

| 変数 | 型 | 役割 |
|---|---|---|
| `kmbasic_variables[ALLOC_BLOCK_NUM]` | `int[256]` | 各ブロックのヒープポインタ |
| `kmbasic_var_size[ALLOC_BLOCK_NUM]` | `unsigned short[256]` | 各ブロックのサイズ（`int` 単位） |
| `kmbasic_object[]` | `unsigned short[]` | コンパイル済みコード領域 |
| `g_next_varnum` | `int` | 次に割り当てる変数番号（テンポラリブロック境界も兼ねる） |
| `g_class_id_list` | `unsigned short*` | 登録済みクラスIDの配列 |
| `g_class_list` | `int*` | 各クラスのクラス構造へのポインタ配列 |
| `g_empty_object_list` | `int*` | 各クラスの空オブジェクトへのポインタ配列 |
| `g_garbage_collection` | `char` | 次回 `alloc_memory` 呼び出し時に一時ブロックを解放するフラグ |
| `g_constant_value_flag` | `char` | コンパイル時定数最適化フラグ |
| `g_scratch_int` / `g_scratch_float` | `volatile int*/float*` | 同一領域を複数の型で参照するスクラッチバッファ |

`kmbasic_variables[]` と `kmbasic_var_size[]` は、ファイルバッファ・コンパイルバッファとしても共有使用されます。

Sources: [globalvars.c:20-70]()

---

## メモリ管理（memory.c）

### ヒープ領域の構造

ヒープ領域は `HEAP_BEGIN`（コンパイル済みコード末尾の直後、`object[1]` のアドレスを4バイト整合）から `HEAP_END`（`g_objmax[]` アドレスまで）に広がります。

```text
┌──────────────────────┬─────────────────────────────────────────┬──────────────────┐
│  kmbasic_object[]    │          HEAP（動的割り当て領域）         │  g_objmax[]      │
│  (コンパイル済みコード) │  HEAP_BEGIN ←──── 確保済みブロック ────→ HEAP_END │                  │
└──────────────────────┴─────────────────────────────────────────┴──────────────────┘
```

`init_memory` でヒープ境界を設定し、`reset_memory` では `g_heap_begin = g_heap_end` としてヒープ全体を使用済みとします（コンパイル再実行時の初期化に使用）。

Sources: [memory.c:44-70]()

### ブロックインデックスの3分割

`kmbasic_variables[]` の 256 エントリは、役割ごとに3つの領域に分割されます。

```text
インデックス 0           25  26           g_next_varnum  g_next_varnum+10   255
             ├─────────────┼──────────────┼──────────────┼───────────────────┤
             │  A-Z 変数   │  名前付き変数  │  テンポラリ   │  パーマネント       │
             │  (0-25)     │  (26 〜)      │  ブロック(10) │  ブロック           │
             └─────────────┴──────────────┴──────────────┴───────────────────┘
               ALLOC_TEMP_BLOCK = g_next_varnum
               ALLOC_PERM_BLOCK = g_next_varnum + TEMPVAR_NUMBER(=10)
```

- **変数ブロック**（0 〜 `ALLOC_TEMP_BLOCK-1`）：A〜Zおよびユーザー定義変数。
- **テンポラリブロック**（`ALLOC_TEMP_BLOCK` 〜 `+TEMPVAR_NUMBER-1`）：文字列演算などで一時的に確保され、`g_garbage_collection` フラグが立った次回 `alloc_memory` 呼び出し時に一括解放。
- **パーマネントブロック**（`ALLOC_PERM_BLOCK` 〜 255）：クラスオブジェクトやDIM配列などの長期確保に使用。明示的な `delete_memory` 呼び出しまで解放されない。

Sources: [memory.c:38-43](), [compiler.h:13-14]()

### `alloc_memory` の空き領域探索

```c
void* alloc_memory(int size, int var_num)
```

`var_num < 0` の場合はテンポラリブロックを自動割り当て、`0 <= var_num < 256` の場合は指定ブロックに割り当てます。空き領域の探索は以下の優先順で行います。

1. **削除リスト**（`g_deleted_pointer[]`）から適合サイズのブロックを再利用（クラスオブジェクト高速再確保のため）。
2. **既存ブロック末尾の直後**（ヒープの末端方向への線形拡張）。
3. **既存ブロック間の隙間**（全ブロックのペアを全数チェックして重複しない空間を探索）。
4. 上記すべてで失敗した場合は `stop_with_error(ERROR_OUT_OF_MEMORY)`。

Sources: [memory.c:88-175]()

### ガベージコレクションと削除リスト

`garbage_collection(data)` は、特定ポインタが格納されているテンポラリブロックのサイズを 0 にクリアすることで、個別の一時データを解放します。`g_garbage_collection` フラグによる一括解放とは別の仕組みです。

`delete_memory(data)` はブロックのポインタとサイズを `0` に設定し、削除済み領域を `g_deleted_pointer[]` / `g_deleted_size[]`（最大10エントリ）のリストに追加します。このリストは `alloc_memory` の最初のパスで参照され、適合サイズがあればその領域を即座に再利用します。

```c
// 削除リストが満杯の場合、末尾エントリと比較して大きければ置き換え
if (DELETE_LIST_SIZE <= g_deleted_num) {
    if (g_deleted_size[DELETE_LIST_SIZE-1] < size) {
        g_deleted_pointer[DELETE_LIST_SIZE-1] = data;
        g_deleted_size[DELETE_LIST_SIZE-1] = size;
    }
}
```

Sources: [memory.c:185-240](), [memory.c:243-270]()

### `var2permanent` による昇格

`var2permanent(var_num)` は、変数ブロックが指すヒープ領域を、パーマネントブロックにコピー（または既存パーマネントブロックを確認してソースのみクリア）します。オブジェクトフィールドに配列などを格納する際、`var2obj` の中から呼び出されます。

Sources: [memory.c:287-316]()

---

## 変数番号管理（variable.c）

### 変数番号の割り当て

`variable_init()` では `g_next_varnum = 26` に初期化します。インデックス 0〜25 はアルファベット一文字変数 A〜Z に固定割り当て済みです。

```c
void variable_init(void){
    g_next_varnum = 26;
}
```

長い名前の変数は `get_new_varnum()` で 26 以上の番号を採番し、`CMPDATA_VARNAME` レコードに名前文字列・ハッシュ・番号を登録します。採番上限は `ALLOC_BLOCK_NUM - TEMPVAR_NUMBER`（= 246）です。

`get_var_number()` はソースから変数名を読み取り、一文字の場合は `source[0] - 'A'`、長い名前の場合は `cmpdata_nsearch_string_first(CMPDATA_VARNAME, ...)` で既存レコードを検索して番号を返します。

Sources: [variable.c:22-76]()

### ARM Thumbコード生成

`r0_to_variable(vn)` と `variable_to_r0(vn)` は、変数番号 `vn` に対応する ARM Thumb-2 命令を `object[]` バッファに直接書き込みます。

変数領域はレジスタ R5（実行時フレームポインタ）相対のメモリに配置されます。番号が小さい（`vn < 32`）場合は即値オフセットによるワード `str`/`ldr`、それ以上は R1 にオフセットを設定してのインデックスストア/ロードを使います。

```c
// vn < 32 の場合（例: str r0, [r5, #vn*4]）
(object++)[0] = 0x6028 | (vn << 6);
// vn < 256 の場合（R1にオフセットをセットしてからストア）
(object++)[0] = 0x5068; // str r0, [r5, r1]
```

Sources: [variable.c:86-130]()

---

## 型解決（value.c / integer.c / float.c）

### 値モードと型ディスパッチ

Phyllosoma のコンパイラは3つの値モードを定義します。

| モード定数 | 値 | 意味 |
|---|---|---|
| `VAR_MODE_INTEGER` | 0 | 整数（R0 に 32 bit 整数） |
| `VAR_MODE_STRING` | 1 | 文字列（R0 に文字列ポインタ） |
| `VAR_MODE_FLOAT` | 2 | 浮動小数点（R0 に IEEE754 ビット表現） |

Sources: [compiler.h:230-232]()

### 演算子優先度パーサー（value.c）

`get_value(vmode)` は再帰的な演算子優先度解析（Pratt parser 相当）を行うエントリポイントです。

```
get_value(vmode)
  └─ get_value_sub(priority(OP_VOID), vmode)
       ├─ get_simple_value(vmode)   // 単項値・リテラル・変数参照
       └─ while(operator exists):
            if current_priority >= operator_priority → return
            value_push_r0()         // R0 をスタックに退避 (str r0, [sp, #n])
            get_value_sub(operator_priority, vmode)  // 右辺再帰
            value_pop_r1()          // スタックから R1 に復元 (ldr r1, [sp, #n])
            calculation(op, vmode)  // 演算結果を R0 に
```

スタック使用量は `g_sdepth` / `g_maxsdepth` で追跡し、ネストが生じた場合のみ `sub sp, #n` / `add sp, #n` のコードが後から確定します。

Sources: [value.c:98-170]()

### 整数値の解析（integer.c）

`get_simple_integer()` は以下の順で解析を試みます。

1. `+` / `-` の符号処理（`-` は `negs r0, r0` を生成）
2. `$XXXX` または `0xXXXX` 形式の16進リテラル
3. `0〜9` で始まる10進リテラル
4. `A〜Z` で始まる識別子：
   - クラス名と判断できる場合は `static_method_or_property()` へ委譲
   - そうでなければ `get_var_number()` → `variable_to_r0(vn)` でロード
   - `(` が続く場合は配列インデックス（`get_dim_value()`）
   - `.` が続く場合はオブジェクトフィールド（`method_or_property(0)`）
5. `&` 演算子：変数のアドレス取得（`adds r0, r0, r5`）
6. それ以外は `integer_functions()` でビルトイン関数を検索

Sources: [integer.c:148-235]()

### 浮動小数点値の解析（float.c）

`get_simple_float()` は `strtof` で浮動小数点リテラルをパースし、その IEEE754 ビット表現を `g_scratch_float[0]` 経由で `g_scratch_int[0]` に取得してから `set_value_in_register(0, ...)` でコードを生成します。変数参照時はサフィックス `#` の存在を確認します。

```c
f = strtof((const char*)&source[0], (char**)&err);
g_scratch_float[0] = f;   // float として書き込み
g_constant_float   = f;
return set_value_in_register(0, g_scratch_int[0]); // int ビット列として ldr
```

Sources: [float.c:54-75]()

---

## クラスシステム（class.c）

### コンパイル時データ構造（CMPDATA）

クラスに関するコンパイル情報はすべて `CMPDATA` レコードとして管理されます。

```text
CMPDATA_CLASSNAME  ─→  クラス名文字列 → クラスID (unsigned short)
CMPDATA_FIELDNAME  ─→  フィールド/メソッド名文字列 → フィールドID
CMPDATA_CLASS      ─→  クラスID → フィールド/メソッド情報配列
CMPDATA_CLASS_ADDRESS ─→  クラスID → (クラス構造アドレス, 空オブジェクトアドレス)
CMPDATA_METHOD     ─→  メソッドID → メソッドコードアドレス
CMPDATA_STATIC     ─→  クラスID → (フィールドID, 変数番号)
```

フィールド/メソッド情報の各エントリは 32bit で次のフラグを組み合わせます。

| フラグ定数 | ビット | 意味 |
|---|---|---|
| `CLASS_METHOD` | 0x00010000 | メソッド |
| `CLASS_FIELD` | 0x00020000 | フィールド |
| `CLASS_PUBLIC` | 0x00100000 | パブリックアクセス可 |
| `CLASS_STATIC` | 0x00200000 | 静的メンバ |

下位 16bit はフィールドID または変数番号、上位 8bit（`>>24`）はフィールドに対応する変数番号です。

Sources: [class.c:1-52](), [compiler.h:263-266]()

### オブジェクトのメモリ構造

実行時のオブジェクトは `lib_new` が確保したヒープ連続領域に格納されます。

```text
object[0] = クラス構造へのポインタ（class_structure*）
object[1] = フィールド値 または メソッドポインタ
object[2] = フィールド値 または メソッドポインタ
...
object[n] = フィールド値 または メソッドポインタ
```

クラス構造（`class_structure[]`）も同様のレイアウトで、`class_structure[0]` にフィールド/メソッド数、`class_structure[1..n]` にフラグ+IDのエントリが並びます。

Sources: [class.c:55-58]()

### オブジェクト生成（lib_new）

コンパイル時、`NEW(ClassName)` 式は `LIB_NEW` ライブラリ呼び出しにコンパイルされます。実行時は以下の手順でオブジェクトを生成します。

```c
int lib_new(int r0, int r1, int r2){
    unsigned short class_id = r0;
    // 1. g_class_id_list からクラスIDを検索
    // 2. g_class_list[i] からクラス構造を取得
    // 3. g_empty_object_list[i] からテンプレートを取得
    // 4. get_permanent_block_number() でパーマネントブロックを確保
    // 5. calloc_memory(num+1, i) でゼロ初期化済みメモリを確保
    // 6. 空オブジェクトの内容をコピー
    return (int)object; // オブジェクトポインタを R0 で返す
}
```

確保した後、`INIT` メソッドが存在する場合はコンストラクタとして自動呼び出しされます。

Sources: [class.c:154-182]()

### フィールドアクセス（lib_resolve_field_address）

フィールド読み書きは `LIB_OBJ_FIELD` ライブラリ経由で実行時に解決されます。

```c
int lib_resolve_field_address(int r0, int r1, int r2){
    // r0: オブジェクトポインタ, r1: フィールドID
    unsigned int* class_structure = (unsigned int*)object[0];
    int num = class_structure[0];
    for(i=1; 1<=num; i++){
        if (field_id == (class_structure[i] & 0xffff)) break;
    }
    if (!(class_structure[i] & CLASS_PUBLIC)) stop_with_error(ERROR_NOT_PUBLIC);
    return (int)(&object[i]); // フィールドのアドレスを返す
}
```

コンパイル側では `get_pointer_to_field()` が `set_value_in_register(1, fid)` でフィールドIDをR1に設定し、`call_lib_code(LIB_OBJ_FIELD)` でアドレスを取得した後、`ldr r0, [r0, #0]` で値を読み出します。

Sources: [class.c:109-131]()

### メソッド呼び出しとフレーム切り替え

```mermaid
sequenceDiagram
    participant Caller as 呼び出し元コード
    participant PreMethod as lib_pre_method
    participant Method as メソッド本体
    participant PostMethod as lib_post_method

    Caller->>PreMethod: R1=R6(現フレーム)
    PreMethod->>PreMethod: caller オブジェクトのフィールドをvar2obj()で保存
    PreMethod->>PreMethod: 新オブジェクトのフィールドをobj2var()で変数に展開
    PreMethod-->>Method: R0=戻り値そのまま
    Method->>Method: 変数(R5相対)でフィールドを読み書き
    Method->>PostMethod: R1=R6
    PostMethod->>PostMethod: 現オブジェクトのフィールドをvar2obj()で保存
    PostMethod->>PostMethod: caller オブジェクトのフィールドをobj2var()で復元
    PostMethod-->>Caller: R0=メソッド戻り値
```

`obj2var` はクラス構造のフィールドエントリを走査し、`class_structure[i] >> 24` に格納された変数番号に対応する `kmbasic_variables[var_num]` を更新します。`var2obj` はその逆です。これにより、メソッド内部では通常の変数アクセス（R5相対）と同じコードでフィールドを操作できます。

Sources: [class.c:184-220]()

### 静的メンバ管理

静的フィールドは変数番号で管理され、オブジェクトインスタンスとは独立して存在します。

- コンパイル時：`register_class_static_field(var_number)` が `CMPDATA_STATIC` レコードに `(クラスID, フィールドID<<16 | 変数番号)` を登録。
- アクセス時（`ClassName::Field`）：`static_method_or_property()` → `resolve_var_number_from_id()` で変数番号を取得 → `variable_to_r0(vn)` で通常の変数として読み書き。
- 静的フィールドは `CMPDATA_CLASS` エントリの `CLASS_STATIC` フラグが立っており、`post_compilling_a_class` で生成するクラス構造本体には含まれません（インスタンスのメモリレイアウトから除外）。

Sources: [class.c:331-370](), [class.c:65-75]()

---

## データフローの全体像

```text
ソースコード (.BAS)
    │
    ▼ compile_file()
┌──────────────────────────────────────────────────┐
│ コンパイラ                                         │
│  variable.c  ─→  変数番号採番 (g_next_varnum)      │
│  value.c     ─→  演算子優先度解析                   │
│  integer.c   ─→  整数リテラル・変数参照コード生成     │
│  float.c     ─→  浮動小数点リテラル・変数参照         │
│  class.c     ─→  CMPDATA登録・クラス構造生成         │
│  memory.c    ─→  alloc_memory でコンパイル時確保     │
└──────────────────────────────────────────────────┘
    │ ARM Thumb-2 コード → kmbasic_object[]
    ▼ run_code()
┌──────────────────────────────────────────────────┐
│ ランタイム                                         │
│  lib_new()           → オブジェクト生成 (heap)      │
│  lib_pre/post_method → obj2var / var2obj 切替      │
│  lib_resolve_field_address → フィールドアドレス解決  │
│  garbage_collection  → 一時ブロック解放              │
│  delete_memory       → パーマネントブロック解放       │
└──────────────────────────────────────────────────┘
```

---

## まとめ

Phyllosoma のメモリ・変数・クラスシステムは以下の設計原則に基づいています。

- **単一の `kmbasic_variables[]` 配列が変数ブロック・テンポラリブロック・パーマネントブロックを統合管理**する。`g_next_varnum` がコンパイル進行に伴って動的に境界を移動させる設計は、変数番号割り当てとメモリ管理の両方の役割を1つのインデックスで処理する点が特徴的です。
- **コンパイル時に ARM Thumb-2 コードを直接生成**することで、変数アクセスやフィールド解決のオーバーヘッドを最小化しています。`variable.c` の `r0_to_variable` / `variable_to_r0` はその核心です。
- **クラスシステムは CMPDATA レコードのコンパイル時メタデータと、ヒープ上のランタイム構造（クラス構造・空オブジェクト）の2層**で動作する。メソッド呼び出し時の `obj2var` / `var2obj` によるフレーム切り替えにより、メソッド内部のコードは通常変数と同一のアドレッシングでフィールドにアクセスできます。
- **ガベージコレクションは明示的なフラグ駆動**で、一時ブロックはライブラリ呼び出し後に `g_garbage_collection` フラグが立った次回のアロケーション時に一括解放されます。パーマネントブロックは `DELETE` 文による明示的な `delete_memory` 呼び出しで解放されます。

Sources: [memory.c:38-70](), [variable.c:22-24](), [class.c:184-220](), [compiler.h:263-266]()
