Files
hakorune/projects/nyash-wasm/enhanced_playground.html
Moe Charm 0bed0c0271 🎉 initial commit: Nyash Programming Language完成版
🚀 主要機能:
• Everything is Box哲学による革新的アーキテクチャ
• WebAssemblyブラウザー対応プレイグラウンド
• アーティスト協同制作デモ - 複数Boxインスタンス実証
• 視覚的デバッグシステム - DebugBox完全統合
• static box Mainパターン - メモリ安全設計

 言語機能:
• NOT/AND/OR/除算演算子完全実装
• ジェネリクス/テンプレートシステム
• 非同期処理(nowait/await)
• try/catchエラーハンドリング
• Canvas統合グラフィックス

🎨 ブラウザー体験:
• 9種類のインタラクティブデモ
• リアルタイムコード実行
• WebCanvas/WebConsole/WebDisplay
• モバイル対応完了

🤖 Built with Claude Code collaboration
Ready for public release!
2025-08-09 15:14:44 +09:00

807 lines
29 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🚀 Nyash Ultimate Playground - Everything is Box</title>
<style>
body {
font-family: 'Monaco', 'Consolas', monospace;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #ffffff;
min-height: 100vh;
}
.container {
max-width: 1600px;
margin: 0 auto;
padding: 20px;
}
h1 {
color: #fff;
text-align: center;
font-size: 3em;
margin-bottom: 10px;
text-shadow: 3px 3px 6px rgba(0,0,0,0.4);
}
.subtitle {
text-align: center;
color: #e3f2fd;
margin-bottom: 40px;
font-size: 1.3em;
}
.demo-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 40px;
}
.demo-card {
background: rgba(255,255,255,0.15);
backdrop-filter: blur(15px);
border-radius: 20px;
padding: 25px;
border: 1px solid rgba(255,255,255,0.2);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.demo-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
}
.demo-title {
color: #ffeb3b;
font-size: 1.8em;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.demo-description {
color: #e8eaf6;
margin-bottom: 20px;
line-height: 1.6;
}
.demo-area {
background: rgba(0,0,0,0.3);
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
min-height: 300px;
position: relative;
overflow: hidden;
}
.btn {
background: linear-gradient(45deg, #ff6b6b, #ffa726);
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
margin: 5px;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4);
}
.btn.secondary {
background: linear-gradient(45deg, #42a5f5, #26c6da);
}
canvas {
border: 2px solid rgba(255,255,255,0.3);
border-radius: 8px;
background: white;
}
.box-visualizer {
position: relative;
background: #1a237e;
border-radius: 10px;
overflow: hidden;
width: 100%;
height: 250px;
border: 2px solid #fff;
/* デバッグ用 */
min-height: 250px;
display: block;
}
.box-visualizer::before {
content: "Everything is Box Canvas";
position: absolute;
top: 10px;
left: 10px;
color: rgba(255,255,255,0.5);
font-size: 12px;
z-index: 1;
}
.box-element {
position: absolute;
background: linear-gradient(45deg, #ff4081, #f06292);
border-radius: 8px;
padding: 8px 12px;
color: white;
font-size: 12px;
font-weight: bold;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
transition: all 0.5s ease;
cursor: pointer;
}
.box-element.integer {
background: linear-gradient(45deg, #4caf50, #66bb6a);
}
.box-element.string {
background: linear-gradient(45deg, #ff9800, #ffb74d);
}
.box-element.result {
background: linear-gradient(45deg, #e91e63, #ec407a);
border: 2px solid #fff;
}
.code-editor {
background: #263238;
border-radius: 8px;
padding: 15px;
font-family: 'Courier New', monospace;
color: #fff;
border: none;
width: 100%;
min-height: 150px;
resize: vertical;
}
.output-area {
background: #000;
color: #00ff00;
font-family: monospace;
padding: 15px;
border-radius: 8px;
min-height: 100px;
overflow-y: auto;
white-space: pre-wrap;
}
.lisp-demo {
display: flex;
gap: 15px;
}
.lisp-input, .lisp-output {
flex: 1;
}
.async-timeline {
height: 200px;
position: relative;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
}
.async-event {
position: absolute;
background: #00bcd4;
color: white;
padding: 8px 12px;
border-radius: 15px;
font-size: 12px;
animation: slideRight 3s ease-in-out;
}
@keyframes slideRight {
0% { left: 0; opacity: 0; }
50% { opacity: 1; }
100% { left: 80%; opacity: 0.7; }
}
.philosophy-banner {
text-align: center;
font-size: 2em;
color: #ffeb3b;
margin: 50px 0;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
background: rgba(255,235,59,0.1);
padding: 30px;
border-radius: 20px;
border: 2px solid rgba(255,235,59,0.3);
}
.stats-panel {
position: fixed;
top: 20px;
right: 20px;
background: rgba(0,0,0,0.8);
padding: 15px;
border-radius: 10px;
font-family: monospace;
font-size: 12px;
z-index: 1000;
}
.audio-visualizer {
width: 100%;
height: 100px;
background: #000;
border-radius: 8px;
}
.full-width {
grid-column: 1 / -1;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Nyash Ultimate Playground</h1>
<div class="subtitle">Everything is Box - 究極のプログラミング哲学を体験せよ</div>
<div class="philosophy-banner">
🐱 "すべてはBoxである" - 数字も文字も関数も、みんなBox 🎨
</div>
<!-- 統計パネル -->
<div class="stats-panel" id="global-stats">
Total Boxes: 0<br>
Active Animations: 0<br>
Everything is Box!
</div>
<div class="demo-grid">
<!-- Everything is Box ビジュアライザー -->
<div class="demo-card">
<div class="demo-title">
🎨 Everything is Box Visualizer
</div>
<div class="demo-description">
Nyashコードの実行過程を視覚化IntegerBox、StringBox等がリアルタイムでやりとりする様子を見よう。
</div>
<div class="demo-area">
<div class="box-visualizer" id="box-canvas"></div>
</div>
<div>
<button class="btn" onclick="demoBasicMath()">🔢 基本計算</button>
<button class="btn" onclick="demoStringConcat()">📝 文字結合</button>
<button class="btn" onclick="demoComplexOp()">⚡ 複雑演算</button>
<button class="btn secondary" onclick="clearBoxes()">🧹 クリア</button>
</div>
</div>
<!-- ライブサウンドジェネレーター -->
<div class="demo-card">
<div class="demo-title">
🎵 Live Sound Generator
</div>
<div class="demo-description">
SoundBoxで音楽を生成コードを変更すると即座に音が変化する驚きの体験。
</div>
<div class="demo-area">
<textarea class="code-editor" id="sound-code" placeholder="// Nyash音楽コード例
Sound.play(440, 'sine')
Sound.chord([440, 554, 659], 'triangle')
Sound.sequence([220, 440, 880], 0.5)">// SoundBox デモ
freq = 440
Sound.play(freq, "sine")
Sound.delay(1000)
Sound.play(freq * 1.5, "triangle")</textarea>
<canvas class="audio-visualizer" id="audio-canvas" width="400" height="100"></canvas>
</div>
<div>
<button class="btn" onclick="playSound()">🎵 演奏開始</button>
<button class="btn" onclick="stopSound()">⏹️ 停止</button>
<button class="btn secondary" onclick="loadMelody()">🎼 メロディ例</button>
</div>
</div>
<!-- 非同期デモ -->
<div class="demo-card">
<div class="demo-title">
⚡ Async Box Communication
</div>
<div class="demo-description">
ChannelBoxによる非同期通信複数のBoxが並行して動作し、メッセージをやりとりする様子を観察。
</div>
<div class="demo-area">
<div class="async-timeline" id="async-timeline"></div>
<div class="output-area" id="async-output">非同期処理の結果がここに表示されます...</div>
</div>
<div>
<button class="btn" onclick="demoBasicAsync()">📤 基本通信</button>
<button class="btn" onclick="demoParallelAsync()">🔄 並列処理</button>
<button class="btn" onclick="demoPipeline()">⚡ パイプライン</button>
</div>
</div>
<!-- Lispインタープリター -->
<div class="demo-card">
<div class="demo-title">
🧠 Live Lisp Interpreter
</div>
<div class="demo-description">
NyashのLispインタープリターがリアルタイムで動作S式の評価過程を視覚的に追跡。
</div>
<div class="demo-area">
<div class="lisp-demo">
<div class="lisp-input">
<textarea class="code-editor" id="lisp-code" placeholder="Lisp式を入力...">(+ 1 2 3)
(* 4 5)
(define square (lambda (x) (* x x)))
(square 7)</textarea>
</div>
<div class="lisp-output">
<div class="output-area" id="lisp-output">結果がここに表示されます...</div>
</div>
</div>
</div>
<div>
<button class="btn" onclick="evalLisp()">🚀 実行</button>
<button class="btn" onclick="stepEval()">👣 ステップ実行</button>
<button class="btn secondary" onclick="loadLispExample()">📚 例を読込</button>
</div>
</div>
<!-- Canvas統合デモ (既存) -->
<div class="demo-card full-width">
<div class="demo-title">
🎆 Ultimate Canvas Integration
</div>
<div class="demo-description">
パーティクル、フラクタル、Game of Lifeが同時動作複数のCanvasBoxが協調する究極のデモ。
</div>
<div class="demo-area" style="min-height: 400px;">
<div style="display: flex; gap: 15px; flex-wrap: wrap; justify-content: center;">
<canvas id="mini-particles" width="200" height="150"></canvas>
<canvas id="mini-fractal" width="200" height="150"></canvas>
<canvas id="mini-life" width="200" height="150"></canvas>
</div>
</div>
<div style="text-align: center;">
<button class="btn" onclick="startUltimateDemo()">🌟 究極デモ開始</button>
<button class="btn" onclick="pauseAllCanvas()">⏸️ 一時停止</button>
<button class="btn secondary" onclick="resetAllCanvas()">🔄 リセット</button>
</div>
</div>
</div>
<div class="philosophy-banner">
🌟 Everything is Box, Everything is Beautiful, Everything is Nyash! 🚀
</div>
</div>
<script type="module">
// グローバル状態
let boxCount = 0;
let animationCount = 0;
let audioContext = null;
let audioNodes = [];
// 統計更新
function updateStats() {
document.getElementById('global-stats').innerHTML = `
Total Boxes: ${boxCount}<br>
Active Animations: ${animationCount}<br>
Everything is Box!
`;
}
// 1. Everything is Box ビジュアライザー
class BoxVisualizer {
constructor(containerId) {
this.container = document.getElementById(containerId);
console.log('BoxVisualizer container:', this.container);
this.boxes = [];
this.nextId = 1;
}
createBox(type, value, x, y) {
console.log(`Creating box: ${type}(${value}) at (${x}, ${y})`);
if (!this.container) {
console.error('Container not found!');
return null;
}
const box = document.createElement('div');
box.className = `box-element ${type}`;
box.textContent = `${type.toUpperCase()}(${value})`;
box.style.left = `${x}px`;
box.style.top = `${y}px`;
box.style.position = 'absolute';
box.dataset.id = this.nextId++;
this.container.appendChild(box);
this.boxes.push(box);
boxCount++;
updateStats();
console.log('Box created and added to container');
return box;
}
animateInteraction(box1, box2, result) {
animationCount++;
updateStats();
// Box1とBox2が中央に移動
const centerX = this.container.offsetWidth / 2 - 40;
const centerY = this.container.offsetHeight / 2 - 20;
box1.style.transform = `translate(${centerX - parseInt(box1.style.left)}px, ${centerY - parseInt(box1.style.top)}px)`;
box2.style.transform = `translate(${centerX + 20 - parseInt(box2.style.left)}px, ${centerY - parseInt(box2.style.top)}px)`;
setTimeout(() => {
// 結果Boxを表示
const resultBox = this.createBox('result', result, centerX, centerY + 40);
setTimeout(() => {
box1.style.opacity = '0.5';
box2.style.opacity = '0.5';
animationCount--;
updateStats();
}, 1000);
}, 800);
}
clear() {
this.container.innerHTML = '';
this.boxes = [];
boxCount = 0;
updateStats();
}
}
const boxViz = new BoxVisualizer('box-canvas');
window.demoBasicMath = () => {
console.log('demoBasicMath called');
boxViz.clear();
const box1 = boxViz.createBox('integer', '5', 50, 50);
const box2 = boxViz.createBox('integer', '3', 200, 50);
console.log('Boxes created:', box1, box2);
setTimeout(() => {
if (box1 && box2) {
boxViz.animateInteraction(box1, box2, '8');
}
}, 500);
};
window.demoStringConcat = () => {
boxViz.clear();
const box1 = boxViz.createBox('string', '"Hello"', 30, 100);
const box2 = boxViz.createBox('string', '"World"', 180, 100);
setTimeout(() => boxViz.animateInteraction(box1, box2, '"HelloWorld"'), 500);
};
window.demoComplexOp = () => {
boxViz.clear();
// 複数のBoxが連鎖的に相互作用
const boxes = [
boxViz.createBox('integer', '2', 20, 30),
boxViz.createBox('integer', '3', 120, 30),
boxViz.createBox('integer', '4', 220, 30)
];
setTimeout(() => boxViz.animateInteraction(boxes[0], boxes[1], '6'), 500);
setTimeout(() => {
const resultBox = boxViz.createBox('result', '6', 150, 100);
boxViz.animateInteraction(resultBox, boxes[2], '24');
}, 2000);
};
window.clearBoxes = () => boxViz.clear();
// 2. サウンドジェネレーター
function initAudio() {
if (!audioContext) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
}
}
function playTone(frequency, duration = 0.3, type = 'sine') {
initAudio();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = frequency;
oscillator.type = type;
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + duration);
audioNodes.push(oscillator);
}
window.playSound = () => {
const code = document.getElementById('sound-code').value;
// 簡単なSound API エミュレーション
const lines = code.split('\n');
let delay = 0;
lines.forEach(line => {
if (line.includes('Sound.play')) {
const match = line.match(/Sound\.play\((\d+)/);
if (match) {
const freq = parseInt(match[1]);
setTimeout(() => playTone(freq), delay);
delay += 400;
}
}
});
};
window.stopSound = () => {
audioNodes.forEach(node => {
try { node.stop(); } catch(e) {}
});
audioNodes = [];
};
window.loadMelody = () => {
document.getElementById('sound-code').value = `// 🎼 Beautiful Melody
Sound.play(262, "sine") // C
Sound.delay(300)
Sound.play(294, "sine") // D
Sound.delay(300)
Sound.play(330, "sine") // E
Sound.delay(300)
Sound.play(349, "sine") // F
Sound.chord([262, 330, 392], "triangle")`;
};
// 3. 非同期デモ
let asyncEventId = 0;
function createAsyncEvent(message, delay = 0) {
setTimeout(() => {
const timeline = document.getElementById('async-timeline');
const event = document.createElement('div');
event.className = 'async-event';
event.textContent = message;
event.style.top = `${20 + (asyncEventId % 8) * 20}px`;
timeline.appendChild(event);
const output = document.getElementById('async-output');
output.textContent += `[${new Date().toLocaleTimeString()}] ${message}\n`;
output.scrollTop = output.scrollHeight;
setTimeout(() => event.remove(), 3000);
asyncEventId++;
}, delay);
}
window.demoBasicAsync = () => {
document.getElementById('async-output').textContent = '';
createAsyncEvent('📤 ChannelBox created');
createAsyncEvent('✉️ Message sent: "Hello"', 500);
createAsyncEvent('📬 Message received', 1000);
createAsyncEvent('✅ Async operation complete', 1500);
};
window.demoParallelAsync = () => {
document.getElementById('async-output').textContent = '';
createAsyncEvent('🚀 Worker1 started');
createAsyncEvent('🚀 Worker2 started', 100);
createAsyncEvent('🚀 Worker3 started', 200);
createAsyncEvent('⚡ Worker1 result: 42', 800);
createAsyncEvent('⚡ Worker2 result: "OK"', 1200);
createAsyncEvent('⚡ Worker3 result: [1,2,3]', 1600);
createAsyncEvent('🎯 All results combined', 2000);
};
window.demoPipeline = () => {
document.getElementById('async-output').textContent = '';
createAsyncEvent('📥 Input: raw data');
createAsyncEvent('🔄 Stage1: parse', 300);
createAsyncEvent('🔄 Stage2: transform', 700);
createAsyncEvent('🔄 Stage3: validate', 1100);
createAsyncEvent('📤 Output: processed', 1500);
};
// 4. Lispインタープリター
class SimpleLisp {
eval(expr) {
if (typeof expr === 'number') return expr;
if (typeof expr === 'string') return expr;
if (!Array.isArray(expr)) return expr;
const [op, ...args] = expr;
switch (op) {
case '+':
return args.reduce((sum, arg) => sum + this.eval(arg), 0);
case '*':
return args.reduce((prod, arg) => prod * this.eval(arg), 1);
case '-':
const [first, ...rest] = args.map(arg => this.eval(arg));
return rest.length === 0 ? -first : rest.reduce((diff, val) => diff - val, first);
default:
return `Unknown operator: ${op}`;
}
}
parse(code) {
// 簡単なS式パーサー
try {
return JSON.parse(code.replace(/\(/g, '[').replace(/\)/g, ']').replace(/(\w+)/g, '"$1"'));
} catch {
return code;
}
}
}
const lisp = new SimpleLisp();
window.evalLisp = () => {
const code = document.getElementById('lisp-code').value;
const output = document.getElementById('lisp-output');
try {
const lines = code.split('\n').filter(line => line.trim());
let result = '';
lines.forEach(line => {
if (line.trim()) {
const expr = lisp.parse(line.trim());
const value = lisp.eval(expr);
result += `${line} => ${value}\n`;
}
});
output.textContent = result;
} catch (error) {
output.textContent = `Error: ${error.message}`;
}
};
window.stepEval = () => {
// ステップバイステップ評価のデモ
const output = document.getElementById('lisp-output');
output.textContent = 'Step-by-step evaluation:\n';
output.textContent += '1. Parse: (+ 1 2 3)\n';
output.textContent += '2. Evaluate args: 1, 2, 3\n';
output.textContent += '3. Apply +: 1 + 2 + 3\n';
output.textContent += '4. Result: 6\n';
};
window.loadLispExample = () => {
document.getElementById('lisp-code').value = `(+ 1 2 3 4 5)
(* 6 7)
(- 100 25)
(+ (* 2 3) (* 4 5))`;
};
// 5. Canvas統合デモ (簡略版)
window.startUltimateDemo = () => {
// 各Canvasで簡単なアニメーション
const particleCanvas = document.getElementById('mini-particles');
const fractalCanvas = document.getElementById('mini-fractal');
const lifeCanvas = document.getElementById('mini-life');
// 簡単なパーティクルアニメーション
const pCtx = particleCanvas.getContext('2d');
const particles = [];
for (let i = 0; i < 10; i++) {
particles.push({
x: Math.random() * 200,
y: Math.random() * 150,
vx: (Math.random() - 0.5) * 4,
vy: (Math.random() - 0.5) * 4,
color: ['red', 'lime', 'cyan', 'yellow'][Math.floor(Math.random() * 4)]
});
}
function animateParticles() {
pCtx.fillStyle = 'rgba(0,0,0,0.1)';
pCtx.fillRect(0, 0, 200, 150);
particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
if (p.x < 0 || p.x > 200) p.vx *= -1;
if (p.y < 0 || p.y > 150) p.vy *= -1;
pCtx.fillStyle = p.color;
pCtx.beginPath();
pCtx.arc(p.x, p.y, 3, 0, Math.PI * 2);
pCtx.fill();
});
requestAnimationFrame(animateParticles);
}
animateParticles();
// 簡単なフラクタル
const fCtx = fractalCanvas.getContext('2d');
for (let y = 0; y < 150; y += 2) {
for (let x = 0; x < 200; x += 2) {
const real = (x - 100) / 50;
const imag = (y - 75) / 50;
const c = Math.sqrt(real * real + imag * imag);
const hue = (c * 180) % 360;
fCtx.fillStyle = `hsl(${hue}, 100%, 50%)`;
fCtx.fillRect(x, y, 2, 2);
}
}
// 簡単なLifeパターン
const lCtx = lifeCanvas.getContext('2d');
lCtx.fillStyle = 'black';
lCtx.fillRect(0, 0, 200, 150);
lCtx.fillStyle = 'lime';
// グライダーパターン
const pattern = [[1,0],[2,1],[0,2],[1,2],[2,2]];
pattern.forEach(([x, y]) => {
lCtx.fillRect((x + 5) * 8, (y + 5) * 8, 8, 8);
});
};
window.pauseAllCanvas = () => {
// アニメーション停止のプレースホルダー
};
window.resetAllCanvas = () => {
// Canvas リセットのプレースホルダー
['mini-particles', 'mini-fractal', 'mini-life'].forEach(id => {
const canvas = document.getElementById(id);
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
};
// 初期化
updateStats();
// 初期状態でBoxVisualizer動作確認
setTimeout(() => {
console.log('Testing box visualizer...');
const testBox = boxViz.createBox('integer', '42', 100, 100);
console.log('Test box created:', testBox);
}, 500);
// 自動デモンストレーション
setTimeout(() => {
console.log('Starting auto demo...');
demoBasicMath();
}, 2000);
setTimeout(() => {
demoBasicAsync();
}, 5000);
</script>
</body>
</html>