// Lux Website - Interactive Features (function() { 'use strict'; // Example code for each playground tab const examples = { hello: `fn main(): Unit with {Console} = { Console.print("Hello, Lux!") } run main() with {}`, effects: `// Effects are declared in the type signature effect Logger { fn log(msg: String): Unit } fn greet(name: String): Unit with {Logger} = { Logger.log("Greeting " + name) Logger.log("Hello, " + name + "!") } // Run with a handler run greet("World") with { Logger = fn log(msg) => print(msg) }`, patterns: `type Shape = | Circle(Float) | Rectangle(Float, Float) | Triangle(Float, Float, Float) fn area(s: Shape): Float = match s { Circle(r) => 3.14159 * r * r, Rectangle(w, h) => w * h, Triangle(a, b, c) => { let s = (a + b + c) / 2.0 sqrt(s * (s - a) * (s - b) * (s - c)) } } let shapes = [Circle(5.0), Rectangle(3.0, 4.0)] let areas = List.map(shapes, area) // [78.54, 12.0]`, handlers: `effect Counter { fn increment(): Unit fn get(): Int } fn countToThree(): Int with {Counter} = { Counter.increment() Counter.increment() Counter.increment() Counter.get() } // Handler maintains state handler counter: Counter { let state = ref 0 fn increment() = { state := !state + 1 resume(()) } fn get() = resume(!state) } // Run with the counter handler run countToThree() with { Counter = counter } // Result: 3`, behavioral: `// Behavioral types provide compile-time guarantees fn add(a: Int, b: Int): Int is pure // No side effects is total // Always terminates is commutative // add(a,b) == add(b,a) = { a + b } fn chargeCard(amount: Int, cardId: String): Receipt is idempotent // Safe to retry with {Payment} = { Payment.charge(amount, cardId) } // The compiler verifies these properties! // Retry is safe because chargeCard is idempotent retry(3, || chargeCard(100, "card_123"))` }; // Simulated outputs for examples const outputs = { hello: `Hello, Lux!`, effects: `[Logger] Greeting World [Logger] Hello, World!`, patterns: `shapes = [Circle(5.0), Rectangle(3.0, 4.0)] areas = [78.53975, 12.0]`, handlers: `Counter.increment() -> state = 1 Counter.increment() -> state = 2 Counter.increment() -> state = 3 Counter.get() -> 3 Result: 3`, behavioral: `Analyzing behavioral properties... add: ✓ is pure (no effects in signature) ✓ is total (no recursion, no partial patterns) ✓ is commutative (verified by SMT solver) chargeCard: ✓ is idempotent (Payment.charge is idempotent) All behavioral properties verified!` }; // Simple interpreter for basic expressions class LuxInterpreter { constructor() { this.env = new Map(); this.output = []; } interpret(code) { this.output = []; this.env.clear(); try { // Check for known examples const normalized = code.trim().replace(/\s+/g, ' '); for (const [key, example] of Object.entries(examples)) { if (normalized === example.trim().replace(/\s+/g, ' ')) { return outputs[key]; } } // Simple expression evaluation return this.evaluateSimple(code); } catch (e) { return `Error: ${e.message}`; } } evaluateSimple(code) { const lines = code.split('\n'); const results = []; for (const line of lines) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('//')) continue; // let binding const letMatch = trimmed.match(/^let\s+(\w+)\s*=\s*(.+)$/); if (letMatch) { const [, name, expr] = letMatch; const value = this.evalExpr(expr); this.env.set(name, value); results.push(`${name} = ${this.formatValue(value)}`); continue; } // Console.print const printMatch = trimmed.match(/Console\.print\((.+)\)/); if (printMatch) { const value = this.evalExpr(printMatch[1]); this.output.push(String(value).replace(/^"|"$/g, '')); continue; } } if (this.output.length > 0) { return this.output.join('\n'); } if (results.length > 0) { return results.join('\n'); } if (code.includes('fn ') || code.includes('effect ') || code.includes('type ')) { return `// Code parsed successfully! // // To run locally: // lux run yourfile.lux`; } return '// No output'; } evalExpr(expr) { expr = expr.trim(); if (expr.startsWith('"') && expr.endsWith('"')) { return expr.slice(1, -1); } if (/^-?\d+(\.\d+)?$/.test(expr)) { return parseFloat(expr); } if (expr === 'true') return true; if (expr === 'false') return false; if (this.env.has(expr)) { return this.env.get(expr); } const arithMatch = expr.match(/^(.+?)\s*([\+\-\*\/])\s*(.+)$/); if (arithMatch) { const left = this.evalExpr(arithMatch[1]); const right = this.evalExpr(arithMatch[3]); switch (arithMatch[2]) { case '+': return left + right; case '-': return left - right; case '*': return left * right; case '/': return left / right; } } if (expr.startsWith('[') && expr.endsWith(']')) { const inner = expr.slice(1, -1); if (!inner.trim()) return []; return inner.split(',').map(item => this.evalExpr(item.trim())); } return expr; } formatValue(value) { if (Array.isArray(value)) { return '[' + value.map(v => this.formatValue(v)).join(', ') + ']'; } if (typeof value === 'string') { return `"${value}"`; } return String(value); } } const interpreter = new LuxInterpreter(); // DOM ready document.addEventListener('DOMContentLoaded', function() { // Mobile menu const mobileMenuBtn = document.getElementById('mobile-menu-btn'); const navLinks = document.getElementById('nav-links'); const menuIcon = document.getElementById('menu-icon'); if (mobileMenuBtn && navLinks) { mobileMenuBtn.addEventListener('click', function() { navLinks.classList.toggle('open'); menuIcon.innerHTML = navLinks.classList.contains('open') ? '✕' : '☰'; }); navLinks.querySelectorAll('a').forEach(function(link) { link.addEventListener('click', function() { navLinks.classList.remove('open'); menuIcon.innerHTML = '☰'; }); }); } // Playground tabs const tabs = document.querySelectorAll('.playground-tab'); const codeInput = document.getElementById('code-input'); const codeOutput = document.getElementById('code-output'); tabs.forEach(function(tab) { tab.addEventListener('click', function() { const tabName = tab.dataset.tab; tabs.forEach(function(t) { t.classList.remove('active'); }); tab.classList.add('active'); if (examples[tabName]) { codeInput.value = examples[tabName]; codeOutput.innerHTML = '// Click "Run" to execute'; } }); }); // Run button const runBtn = document.getElementById('run-btn'); if (runBtn && codeInput && codeOutput) { runBtn.addEventListener('click', function() { const code = codeInput.value; runBtn.disabled = true; runBtn.textContent = 'Running...'; setTimeout(function() { const result = interpreter.interpret(code); codeOutput.textContent = result; runBtn.disabled = false; runBtn.textContent = 'Run'; }, 300); }); } // Keyboard shortcut: Ctrl/Cmd + Enter to run if (codeInput) { codeInput.addEventListener('keydown', function(e) { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); runBtn.click(); } }); } // Copy buttons document.querySelectorAll('.copy-btn').forEach(function(btn) { btn.addEventListener('click', async function() { const text = btn.dataset.copy; try { await navigator.clipboard.writeText(text); const original = btn.textContent; btn.textContent = 'Copied!'; btn.classList.add('copied'); setTimeout(function() { btn.textContent = original; btn.classList.remove('copied'); }, 2000); } catch (e) { console.error('Failed to copy:', e); } }); }); // Smooth scroll document.querySelectorAll('a[href^="#"]').forEach(function(anchor) { anchor.addEventListener('click', function(e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); }); // Console message console.log('%c Lux ', 'background: #d4af37; color: #0a0a0a; font-size: 24px; padding: 10px; border-radius: 4px; font-weight: bold;'); console.log('%cSide effects can\'t hide.', 'font-size: 14px; color: #d4af37;'); console.log('%chttps://git.qrty.ink/blu/lux', 'font-size: 12px; color: #888;'); })();