🚀 主要機能: • 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!
807 lines
29 KiB
HTML
807 lines
29 KiB
HTML
<!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> |