fix(phase-4.3c-3): Fix StringBox literal handling in MIR builder

Phase 4-3c-3 Complete: WASM host functions now correctly output string content

## Changes:
- Fixed MIR builder to handle StringBox with string literal arguments
- Special case for  to generate proper string constants
- Removed debug output after successful verification
- WASM now correctly outputs "Hello MIR!" instead of "StringBox"

## Test Results:
- MIR generation:  Generates  correctly
- WASM compilation:  String data correctly placed at offset 4096
- WASM execution:  Outputs "Hello MIR\!" as expected

## Technical Details:
- Modified build_new_expression() to detect StringBox with literal arguments
- Generates Const instruction with actual string content
- Host function reads StringBox memory layout correctly

This completes the WASM string output functionality for Phase 4.

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-17 13:49:35 +09:00
parent bb3f2e8032
commit 3df87fb1ce
41 changed files with 4444 additions and 68 deletions

View File

@ -0,0 +1,87 @@
// FileBox パフォーマンスベンチマーク
// 静的版と動的版の速度比較
static box Main {
init { console, timer }
main() {
me.console = new ConsoleBox()
me.timer = new TimeBox()
me.console.log("📊 FileBox パフォーマンスベンチマーク開始")
// テスト設定
local iterations
iterations = 1000
local testFile
testFile = "benchmark_test.txt"
// ベンチマーク1: ファイル作成・削除
me.console.log("\n🔧 Test 1: FileBox作成 x " + iterations.toString())
local start1
start1 = me.timer.now()
local i
i = 0
loop(i < iterations) {
local file
file = new FileBox(testFile + i.toString())
i = i + 1
}
local elapsed1
elapsed1 = me.timer.elapsed(start1)
me.console.log("⏱️ 経過時間: " + elapsed1.toString() + "ms")
me.console.log("📈 平均: " + (elapsed1 / iterations).toString() + "ms/操作")
// ベンチマーク2: 読み書き操作
me.console.log("\n🔧 Test 2: 読み書き操作 x " + iterations.toString())
local file2
file2 = new FileBox(testFile)
local start2
start2 = me.timer.now()
i = 0
loop(i < iterations) {
file2.write("Test data " + i.toString())
local content
content = file2.read()
i = i + 1
}
local elapsed2
elapsed2 = me.timer.elapsed(start2)
me.console.log("⏱️ 経過時間: " + elapsed2.toString() + "ms")
me.console.log("📈 平均: " + (elapsed2 / iterations).toString() + "ms/操作")
// ベンチマーク3: exists()チェック
me.console.log("\n🔧 Test 3: exists()チェック x " + iterations.toString())
local start3
start3 = me.timer.now()
i = 0
loop(i < iterations) {
local exists
exists = file2.exists()
i = i + 1
}
local elapsed3
elapsed3 = me.timer.elapsed(start3)
me.console.log("⏱️ 経過時間: " + elapsed3.toString() + "ms")
me.console.log("📈 平均: " + (elapsed3 / iterations).toString() + "ms/操作")
// クリーンアップ
i = 0
loop(i < iterations) {
local fileName
fileName = testFile + i.toString()
// ファイル削除(手動で行う必要があります)
i = i + 1
}
me.console.log("\n✅ ベンチマーク完了!")
return "done"
}
}

View File

@ -0,0 +1,47 @@
// FileBox シンプルパフォーマンステスト
// 動的版と静的版の速度比較用
static box Main {
init { console }
main() {
me.console = new ConsoleBox()
me.console.log("📊 FileBox パフォーマンステスト")
// テスト設定
local iterations
iterations = 100 // 少なめにして手動計測
me.console.log("🔧 Test: FileBox作成・読み書き x " + iterations.toString())
me.console.log("⏱️ 開始時刻を記録してください...")
// FileBox作成・読み書きテスト
local i
i = 0
loop(i < iterations) {
local file
file = new FileBox("perf_test_" + i.toString() + ".txt")
// 書き込み
file.write("Performance test data " + i.toString())
// 読み込み
local content
content = file.read()
// 存在確認
local exists
exists = file.exists()
i = i + 1
}
me.console.log("⏱️ 終了!経過時間を確認してください")
me.console.log("✅ " + iterations.toString() + " 個のファイル操作完了")
// クリーンアップ用コメント
me.console.log("\n🧹 クリーンアップ: rm -f perf_test_*.txt")
return "done"
}
}

View File

@ -0,0 +1,44 @@
#!/bin/bash
# パフォーマンス測定スクリプト
echo "🔬 FileBox パフォーマンス測定"
echo "================================"
# クリーンアップ
rm -f perf_test_*.txt benchmark_test*.txt
# 動的版の測定
echo -e "\n📊 動的版 (dynamic-file feature 有効)"
echo "開始時刻: $(date +%H:%M:%S.%N)"
START=$(date +%s%N)
RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash 2>/dev/null
END=$(date +%s%N)
ELAPSED=$((($END - $START) / 1000000))
echo "終了時刻: $(date +%H:%M:%S.%N)"
echo "⏱️ 実行時間: ${ELAPSED}ms"
# クリーンアップ
rm -f perf_test_*.txt
# 静的版のビルド
echo -e "\n🔧 静的版をビルド中..."
cargo build --release --no-default-features 2>/dev/null
# 静的版の測定
echo -e "\n📊 静的版 (dynamic-file feature 無効)"
echo "開始時刻: $(date +%H:%M:%S.%N)"
START=$(date +%s%N)
RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash 2>/dev/null
END=$(date +%s%N)
ELAPSED=$((($END - $START) / 1000000))
echo "終了時刻: $(date +%H:%M:%S.%N)"
echo "⏱️ 実行時間: ${ELAPSED}ms"
# クリーンアップ
rm -f perf_test_*.txt
echo -e "\n✅ 測定完了!"

View File

@ -0,0 +1,58 @@
#!/bin/bash
# FileBox パフォーマンス比較
echo "🔬 FileBox 静的版 vs 動的版 パフォーマンス比較"
echo "============================================="
echo "テスト内容: 100個のファイル作成・書き込み・読み込み・存在確認"
echo ""
# クリーンアップ
rm -f perf_test_*.txt
# 動的版(現在)
echo "1⃣ 動的版 (dynamic-file feature 有効)"
echo -n " 実行時間: "
{ time RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash > /dev/null 2>&1; } 2>&1 | grep real | awk '{print $2}'
rm -f perf_test_*.txt
# 静的版ビルド
echo ""
echo " 静的版をビルド中..."
cargo build --release --no-default-features -j32 > /dev/null 2>&1
# 静的版
echo ""
echo "2⃣ 静的版 (FileBox組み込み)"
echo -n " 実行時間: "
{ time RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash > /dev/null 2>&1; } 2>&1 | grep real | awk '{print $2}'
rm -f perf_test_*.txt
# 複数回測定
echo ""
echo "📊 5回測定の平均:"
echo ""
echo "動的版:"
for i in {1..5}; do
echo -n " Run $i: "
{ time RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash > /dev/null 2>&1; } 2>&1 | grep real | awk '{print $2}'
rm -f perf_test_*.txt
done
# 静的版に切り替え
cargo build --release --no-default-features -j32 > /dev/null 2>&1
echo ""
echo "静的版:"
for i in {1..5}; do
echo -n " Run $i: "
{ time RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash > /dev/null 2>&1; } 2>&1 | grep real | awk '{print $2}'
rm -f perf_test_*.txt
done
# 動的版に戻す
echo ""
echo "元の動的版に戻しています..."
cargo build --release -j32 > /dev/null 2>&1
echo ""
echo "✅ 測定完了!"

View File

@ -0,0 +1,30 @@
#!/bin/bash
# シンプルなパフォーマンス測定
echo "🔬 FileBox パフォーマンス測定100回のファイル操作"
echo "=================================================="
# クリーンアップ
rm -f perf_test_*.txt
# 動的版
echo -e "\n📊 動的版 (現在のビルド)"
echo "計測開始..."
time RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash > /dev/null 2>&1
rm -f perf_test_*.txt
# 静的版ビルド
echo -e "\n🔧 静的版をビルド中..."
cargo build --release --no-default-features -j32 > /dev/null 2>&1
# 静的版
echo -e "\n📊 静的版 (dynamic-file無効)"
echo "計測開始..."
time RUST_LOG=error ./target/release/nyash local_tests/benchmark_filebox_simple.nyash > /dev/null 2>&1
rm -f perf_test_*.txt
# 動的版に戻す
echo -e "\n🔧 動的版に戻します..."
cargo build --release -j32 > /dev/null 2>&1
echo -e "\n✅ 測定完了!"

View File

@ -0,0 +1 @@
print("Hello MIR\!")

View File

@ -0,0 +1,233 @@
(module
(import "env" "print" (func $print (param i32) ))
(import "env" "print_str" (func $print_str (param i32 i32) ))
(import "env" "console_log" (func $console_log (param i32 i32) ))
(import "env" "canvas_fillRect" (func $canvas_fillRect (param i32 i32 i32 i32 i32 i32 i32 i32) ))
(import "env" "canvas_fillText" (func $canvas_fillText (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) ))
(import "env" "box_to_string" (func $box_to_string (param i32) (result i32)))
(import "env" "box_print" (func $box_print (param i32) ))
(import "env" "box_equals" (func $box_equals (param i32 i32) (result i32)))
(import "env" "box_clone" (func $box_clone (param i32) (result i32)))
(memory (export "memory") 1)
(data (i32.const 4096) "\48\65\6c\6c\6f\20\4d\49\52\5c\21")
(data (i32.const 4107) "\40\70\72\69\6e\74")
(global $heap_ptr (mut i32) (i32.const 2048))
(func $malloc (param $size i32) (result i32)
(local $ptr i32)
(local $aligned_size i32)
;; Align size to 4-byte boundary
local.get $size
i32.const 3
i32.add
i32.const -4
i32.and
local.set $aligned_size
;; Get current heap pointer
global.get $heap_ptr
local.set $ptr
;; Advance heap pointer by aligned size
global.get $heap_ptr
local.get $aligned_size
i32.add
global.set $heap_ptr
;; Return allocated pointer
local.get $ptr
)
(func $box_alloc (param $type_id i32) (param $field_count i32) (result i32)
(local $ptr i32)
(local $total_size i32)
;; Calculate total size: header (12) + fields (field_count * 4)
local.get $field_count
i32.const 4
i32.mul
i32.const 12
i32.add
local.set $total_size
;; Allocate memory
local.get $total_size
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
local.get $type_id
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
local.get $field_count
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_stringbox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 20
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4097
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 2
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_integerbox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 16
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4098
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 1
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_boolbox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 16
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4099
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 1
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_databox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 16
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4101
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 1
i32.store
;; Return box pointer
local.get $ptr
)
(func $main (local $0 i32) (local $1 i32) (local $2 i32)
nop
call $alloc_stringbox
local.set $0
local.get $0
i32.const 12
i32.add
i32.const 4096
i32.store
local.get $0
i32.const 16
i32.add
i32.const 11
i32.store
local.get $0
local.set $1
call $alloc_stringbox
local.set $2
local.get $2
i32.const 12
i32.add
i32.const 4107
i32.store
local.get $2
i32.const 16
i32.add
i32.const 6
i32.store
local.get $1
call $print
local.get $1
return
)
(export "main" (func $main))
)

View File

@ -0,0 +1,60 @@
// Math系Box動的ライブラリテスト
static box Main {
init { console }
main() {
me.console = new ConsoleBox()
me.console.log("🧮 Math系Box動的ライブラリテスト")
// MathBoxテスト
me.console.log("\n📐 MathBox テスト:")
local math = new MathBox()
me.console.log("math.toString(): " + math.toString())
me.console.log("math.sqrt(16): " + math.sqrt(16).toString())
me.console.log("math.pow(2, 10): " + math.pow(2, 10).toString())
me.console.log("math.sin(1.57): " + math.sin(1.57).toString())
me.console.log("math.cos(0): " + math.cos(0).toString())
me.console.log("math.abs(-42): " + math.abs(-42).toString())
me.console.log("math.floor(3.7): " + math.floor(3.7).toString())
me.console.log("math.ceil(3.2): " + math.ceil(3.2).toString())
me.console.log("math.round(3.5): " + math.round(3.5).toString())
me.console.log("math.min(10, 5): " + math.min(10, 5).toString())
me.console.log("math.max(10, 5): " + math.max(10, 5).toString())
// RandomBoxテスト
me.console.log("\n🎲 RandomBox テスト:")
local random = new RandomBox()
me.console.log("random.toString(): " + random.toString())
me.console.log("random.next(): " + random.next().toString())
me.console.log("random.range(1, 10): " + random.range(1, 10).toString())
me.console.log("random.int(1, 100): " + random.int(1, 100).toString())
// TimeBoxテスト
me.console.log("\n⏰ TimeBox テスト:")
local time = new TimeBox()
me.console.log("time.toString(): " + time.toString())
local now = time.now()
me.console.log("time.now(): " + now.toString())
// DateTimeBoxテスト
me.console.log("\n📅 DateTimeBox テスト:")
local dt = new DateTimeBox()
me.console.log("dt.toString(): " + dt.toString())
me.console.log("dt.year(): " + dt.year().toString())
me.console.log("dt.month(): " + dt.month().toString())
me.console.log("dt.day(): " + dt.day().toString())
me.console.log("dt.hour(): " + dt.hour().toString())
me.console.log("dt.minute(): " + dt.minute().toString())
me.console.log("dt.second(): " + dt.second().toString())
me.console.log("dt.timestamp(): " + dt.timestamp().toString())
// 文字列からDateTimeBox作成
local dt2 = new DateTimeBox("2025-08-17T12:34:56Z")
me.console.log("\nDateTimeBox from string: " + dt2.toString())
me.console.log("\n✅ 全テスト完了!")
return "done"
}
}

View File

@ -0,0 +1,19 @@
// Math系Box最小テスト
static box Main {
init { console }
main() {
me.console = new ConsoleBox()
me.console.log("🧮 MathBox最小テスト")
local math = new MathBox()
me.console.log("MathBox created: " + math.type_name())
// 基本的な数学演算テスト
local result = math.sqrt(16)
me.console.log("sqrt(16) = " + result.toString())
return "done"
}
}

View File

@ -0,0 +1,15 @@
// 最もシンプルなMathBoxテスト
static box Main {
init { console }
main() {
me.console = new ConsoleBox()
me.console.log("Creating MathBox...")
local math = new MathBox()
me.console.log("MathBox created!")
return "done"
}
}

View File

@ -0,0 +1,30 @@
# MIR 26命令セット完全動作確認テスト
# Phase 4-3: 新しい26命令セットの基本テスト
# 基本演算テスト
result = 10 + 5
print("Basic arithmetic: " + result)
# 比較演算テスト
comparison = (result > 12)
print("Comparison result: " + comparison)
# 条件分岐テスト
if comparison {
print("Conditional branch works")
}
# ループテスト
counter = 0
loop(counter < 3) {
print("Loop iteration: " + counter)
counter = counter + 1
}
# Box作成・アクセステスト基本的なEverything is Box
local testBox = new MapBox()
testBox.set("key1", "value1")
local value = testBox.get("key1")
print("Box operation result: " + value)
print("MIR 26 instruction test completed successfully!")

View File

@ -1,9 +1 @@
// Simple MIR test
static box Main {
main() {
local result
result = 42 + 8
print(result)
return result
}
}
print("Hello MIR\!")

View File

@ -0,0 +1,233 @@
(module
(import "env" "print" (func $print (param i32) ))
(import "env" "print_str" (func $print_str (param i32 i32) ))
(import "env" "console_log" (func $console_log (param i32 i32) ))
(import "env" "canvas_fillRect" (func $canvas_fillRect (param i32 i32 i32 i32 i32 i32 i32 i32) ))
(import "env" "canvas_fillText" (func $canvas_fillText (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) ))
(import "env" "box_to_string" (func $box_to_string (param i32) (result i32)))
(import "env" "box_print" (func $box_print (param i32) ))
(import "env" "box_equals" (func $box_equals (param i32 i32) (result i32)))
(import "env" "box_clone" (func $box_clone (param i32) (result i32)))
(memory (export "memory") 1)
(data (i32.const 4107) "\40\70\72\69\6e\74")
(data (i32.const 4096) "\48\65\6c\6c\6f\20\4d\49\52\5c\21")
(global $heap_ptr (mut i32) (i32.const 2048))
(func $malloc (param $size i32) (result i32)
(local $ptr i32)
(local $aligned_size i32)
;; Align size to 4-byte boundary
local.get $size
i32.const 3
i32.add
i32.const -4
i32.and
local.set $aligned_size
;; Get current heap pointer
global.get $heap_ptr
local.set $ptr
;; Advance heap pointer by aligned size
global.get $heap_ptr
local.get $aligned_size
i32.add
global.set $heap_ptr
;; Return allocated pointer
local.get $ptr
)
(func $box_alloc (param $type_id i32) (param $field_count i32) (result i32)
(local $ptr i32)
(local $total_size i32)
;; Calculate total size: header (12) + fields (field_count * 4)
local.get $field_count
i32.const 4
i32.mul
i32.const 12
i32.add
local.set $total_size
;; Allocate memory
local.get $total_size
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
local.get $type_id
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
local.get $field_count
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_stringbox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 20
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4097
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 2
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_integerbox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 16
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4098
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 1
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_boolbox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 16
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4099
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 1
i32.store
;; Return box pointer
local.get $ptr
)
(func $alloc_databox (result i32)
(local $ptr i32)
;; Allocate memory for box
i32.const 16
call $malloc
local.set $ptr
;; Initialize type_id
local.get $ptr
i32.const 4101
i32.store
;; Initialize ref_count to 1
local.get $ptr
i32.const 4
i32.add
i32.const 1
i32.store
;; Initialize field_count
local.get $ptr
i32.const 8
i32.add
i32.const 1
i32.store
;; Return box pointer
local.get $ptr
)
(func $main (local $0 i32) (local $1 i32) (local $2 i32)
nop
call $alloc_stringbox
local.set $0
local.get $0
i32.const 12
i32.add
i32.const 4096
i32.store
local.get $0
i32.const 16
i32.add
i32.const 11
i32.store
local.get $0
local.set $1
call $alloc_stringbox
local.set $2
local.get $2
i32.const 12
i32.add
i32.const 4107
i32.store
local.get $2
i32.const 16
i32.add
i32.const 6
i32.store
local.get $1
call $print
local.get $1
return
)
(export "main" (func $main))
)

View File

@ -0,0 +1,23 @@
// Phase 2 MIR conversion test
static box Main {
init { console, result }
main() {
me.console = new ConsoleBox()
// Test print statement (should use @print intrinsic)
print("Testing Phase 2 conversions!")
// Test unary operations (should use @unary_* intrinsics)
local x = 42
local negated = -x // @unary_neg
local flag = true
local inverted = not flag // @unary_not
me.console.log("Negated: " + negated.toString())
me.console.log("Inverted: " + inverted.toString())
me.result = "Phase 2 test completed"
return me.result
}
}

View File

@ -0,0 +1 @@
local x = "Hello MIR\!"

View File

@ -0,0 +1,24 @@
#!/bin/bash
# WASM execution test script
echo "🎯 Testing WASM compilation and execution with host functions"
# First compile to WAT
echo "📝 Compiling test_mir_simple.nyash to WASM..."
../target/release/nyash --compile-wasm test_mir_simple.nyash
# Check if WAT was generated
if [ -f "test_mir_simple.wat" ]; then
echo "✅ WAT file generated successfully"
echo "📄 WAT content preview:"
head -20 test_mir_simple.wat
echo "..."
# Now we need a custom WASM runner that provides host functions
echo ""
echo "🚀 To execute WASM with host functions, we need to build a custom runner"
echo " that provides the required imports (env::print, etc.)"
else
echo "❌ WAT file generation failed"
exit 1
fi