Compare commits
2 Commits
7e76acab18
...
bc60f1c8f1
| Author | SHA1 | Date | |
|---|---|---|---|
| bc60f1c8f1 | |||
| 52e3876b81 |
@@ -244,6 +244,7 @@ impl Parser {
|
||||
TokenKind::Let => Ok(Declaration::Let(self.parse_let_decl(visibility, doc)?)),
|
||||
TokenKind::Trait => Ok(Declaration::Trait(self.parse_trait_decl(visibility, doc)?)),
|
||||
TokenKind::Impl => Ok(Declaration::Impl(self.parse_impl_decl()?)),
|
||||
TokenKind::Run => Err(self.error("Bare 'run' expressions are not allowed at top level. Use 'let _ = run ...' or 'let result = run ...'")),
|
||||
_ => Err(self.error("Expected declaration (fn, effect, handler, type, trait, impl, or let)")),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,6 +194,14 @@
|
||||
<li><a href="middleware.html">Middleware</a></li>
|
||||
<li><a href="routing.html">Routing</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Projects</h2>
|
||||
<ul>
|
||||
<li><a href="/projects/">All Projects</a></li>
|
||||
<li><a href="/projects/#rest-api">REST API</a></li>
|
||||
<li><a href="/projects/#todo-app">Todo App</a></li>
|
||||
<li><a href="/projects/#json-parser">JSON Parser</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<div class="examples-content">
|
||||
|
||||
456
website/projects/index.html
Normal file
456
website/projects/index.html
Normal file
@@ -0,0 +1,456 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Projects - Lux</title>
|
||||
<meta name="description" content="Real-world example projects demonstrating Lux's capabilities.">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>✨</text></svg>">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Playfair+Display:wght@400;600;700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../static/style.css">
|
||||
<style>
|
||||
.projects-container {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: var(--space-xl) var(--space-lg);
|
||||
}
|
||||
|
||||
.projects-header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-2xl);
|
||||
}
|
||||
|
||||
.projects-header p {
|
||||
font-size: 1.1rem;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.projects-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
.project-card {
|
||||
background: var(--bg-glass);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: 12px;
|
||||
padding: var(--space-lg);
|
||||
transition: border-color 0.3s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.project-card:hover {
|
||||
border-color: var(--border-gold);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.project-card h2 {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: var(--space-sm);
|
||||
text-align: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.project-card h2 .icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.project-card .description {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--space-md);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.project-card .features {
|
||||
margin-bottom: var(--space-md);
|
||||
}
|
||||
|
||||
.project-card .features h3 {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: var(--space-xs);
|
||||
}
|
||||
|
||||
.project-card .features ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.project-card .features li {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
padding: 0.2rem 0;
|
||||
}
|
||||
|
||||
.project-card .features li::before {
|
||||
content: "\2022";
|
||||
color: var(--gold);
|
||||
margin-right: var(--space-xs);
|
||||
}
|
||||
|
||||
.project-card .code-preview {
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 6px;
|
||||
padding: var(--space-md);
|
||||
margin-bottom: var(--space-md);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.project-card .code-preview pre {
|
||||
font-family: var(--font-code);
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.project-card .actions {
|
||||
display: flex;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
|
||||
.project-card .actions a {
|
||||
padding: var(--space-xs) var(--space-md);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.showcase-section {
|
||||
margin-top: var(--space-3xl);
|
||||
padding-top: var(--space-2xl);
|
||||
border-top: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.showcase-section h2 {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.showcase-card {
|
||||
background: linear-gradient(135deg, var(--bg-tertiary) 0%, var(--bg-secondary) 100%);
|
||||
border: 1px solid var(--border-gold);
|
||||
border-radius: 16px;
|
||||
padding: var(--space-xl);
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.showcase-card h3 {
|
||||
font-size: 1.5rem;
|
||||
color: var(--gold);
|
||||
margin-bottom: var(--space-md);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.showcase-card p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: var(--space-lg);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.showcase-features {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: var(--space-lg);
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.showcase-feature {
|
||||
background: var(--bg-glass);
|
||||
border-radius: 8px;
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
.showcase-feature h4 {
|
||||
color: var(--gold);
|
||||
font-size: 1rem;
|
||||
margin-bottom: var(--space-xs);
|
||||
}
|
||||
|
||||
.showcase-feature p {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.showcase-code {
|
||||
background: var(--code-bg);
|
||||
border: 1px solid var(--code-border);
|
||||
border-radius: 8px;
|
||||
padding: var(--space-lg);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.showcase-code pre {
|
||||
font-family: var(--font-code);
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<a href="/" class="logo">Lux</a>
|
||||
<ul class="nav-links" id="nav-links">
|
||||
<li><a href="/install">Install</a></li>
|
||||
<li><a href="/tour/">Tour</a></li>
|
||||
<li><a href="/examples/">Examples</a></li>
|
||||
<li><a href="/docs/">Docs</a></li>
|
||||
<li><a href="/play">Play</a></li>
|
||||
<li><a href="https://git.qrty.ink/blu/lux" class="nav-source">Source</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main class="projects-container">
|
||||
<header class="projects-header">
|
||||
<h1>Example Projects</h1>
|
||||
<p>Real-world applications demonstrating Lux's unique capabilities. Clone these to learn by doing.</p>
|
||||
</header>
|
||||
|
||||
<div class="projects-grid">
|
||||
<div class="project-card">
|
||||
<h2><span class="icon">API</span> REST API</h2>
|
||||
<p class="description">
|
||||
A full REST API for task management with JSON responses, routing, and CRUD operations.
|
||||
</p>
|
||||
<div class="features">
|
||||
<h3>Demonstrates</h3>
|
||||
<ul>
|
||||
<li>HttpServer effect</li>
|
||||
<li>Pattern matching for routing</li>
|
||||
<li>JSON serialization</li>
|
||||
<li>Effect tracking</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="code-preview">
|
||||
<pre><code><span class="kw">fn</span> <span class="fn">router</span>(req): Response <span class="kw">with</span> {<span class="ef">Http</span>} =
|
||||
<span class="kw">match</span> (req.method, req.path) {
|
||||
(<span class="st">"GET"</span>, <span class="st">"/"</span>) => httpOk(welcome),
|
||||
(<span class="st">"GET"</span>, <span class="st">"/tasks"</span>) => httpOk(tasks),
|
||||
_ => httpNotFound(<span class="st">"404"</span>)
|
||||
}</code></pre>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="https://git.qrty.ink/blu/lux/src/branch/master/projects/rest-api" class="btn btn-primary">View Code</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card">
|
||||
<h2><span class="icon">TODO</span> Todo App</h2>
|
||||
<p class="description">
|
||||
A command-line todo application showcasing algebraic data types and pattern matching.
|
||||
</p>
|
||||
<div class="features">
|
||||
<h3>Demonstrates</h3>
|
||||
<ul>
|
||||
<li>ADTs for data modeling</li>
|
||||
<li>Pattern matching</li>
|
||||
<li>Recursive list operations</li>
|
||||
<li>Console effect</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="code-preview">
|
||||
<pre><code><span class="kw">type</span> <span class="ty">Priority</span> =
|
||||
| <span class="ty">Low</span>
|
||||
| <span class="ty">Medium</span>
|
||||
| <span class="ty">High</span>
|
||||
|
||||
<span class="kw">type</span> <span class="ty">TodoItem</span> =
|
||||
| <span class="ty">TodoItem</span>(Int, String, Bool, Priority)</code></pre>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="https://git.qrty.ink/blu/lux/src/branch/master/projects/todo-app" class="btn btn-primary">View Code</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card">
|
||||
<h2><span class="icon">JSON</span> JSON Parser</h2>
|
||||
<p class="description">
|
||||
A recursive descent JSON parser demonstrating ADTs for AST representation.
|
||||
</p>
|
||||
<div class="features">
|
||||
<h3>Demonstrates</h3>
|
||||
<ul>
|
||||
<li>ADTs for AST</li>
|
||||
<li>Recursive parsing</li>
|
||||
<li>String manipulation</li>
|
||||
<li>Pure functions</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="code-preview">
|
||||
<pre><code><span class="kw">type</span> <span class="ty">JsonValue</span> =
|
||||
| <span class="ty">JsonNull</span>
|
||||
| <span class="ty">JsonBool</span>(Bool)
|
||||
| <span class="ty">JsonNumber</span>(Int)
|
||||
| <span class="ty">JsonString</span>(String)
|
||||
| <span class="ty">JsonArray</span>(List<JsonValue>)
|
||||
| <span class="ty">JsonObject</span>(List<(String, JsonValue)>)</code></pre>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="https://git.qrty.ink/blu/lux/src/branch/master/projects/json-parser" class="btn btn-primary">View Code</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card">
|
||||
<h2><span class="icon">GAME</span> Guessing Game</h2>
|
||||
<p class="description">
|
||||
A simple number guessing game demonstrating effects for randomness and I/O.
|
||||
</p>
|
||||
<div class="features">
|
||||
<h3>Demonstrates</h3>
|
||||
<ul>
|
||||
<li>Random effect</li>
|
||||
<li>Console I/O</li>
|
||||
<li>Recursive game loop</li>
|
||||
<li>User input handling</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="code-preview">
|
||||
<pre><code><span class="kw">fn</span> <span class="fn">playGame</span>(): Unit
|
||||
<span class="kw">with</span> {<span class="ef">Console</span>, <span class="ef">Random</span>} = {
|
||||
<span class="kw">let</span> secret = Random.int(<span class="num">1</span>, <span class="num">100</span>)
|
||||
Console.print(<span class="st">"Guess a number!"</span>)
|
||||
guessLoop(secret, <span class="num">0</span>)
|
||||
}</code></pre>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="https://git.qrty.ink/blu/lux/src/branch/master/projects/guessing-game" class="btn btn-primary">View Code</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card">
|
||||
<h2><span class="icon">MD</span> Markdown Converter</h2>
|
||||
<p class="description">
|
||||
Convert Markdown to HTML using pattern matching and string processing.
|
||||
</p>
|
||||
<div class="features">
|
||||
<h3>Demonstrates</h3>
|
||||
<ul>
|
||||
<li>String manipulation</li>
|
||||
<li>Pattern matching</li>
|
||||
<li>Pure transformation</li>
|
||||
<li>List processing</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="code-preview">
|
||||
<pre><code><span class="kw">fn</span> <span class="fn">convertLine</span>(line: String): String =
|
||||
<span class="kw">if</span> startsWith(line, <span class="st">"# "</span>) <span class="kw">then</span>
|
||||
<span class="st">"<h1>"</span> + rest(line, <span class="num">2</span>) + <span class="st">"</h1>"</span>
|
||||
<span class="kw">else if</span> startsWith(line, <span class="st">"- "</span>) <span class="kw">then</span>
|
||||
<span class="st">"<li>"</span> + rest(line, <span class="num">2</span>) + <span class="st">"</li>"</span>
|
||||
<span class="kw">else</span> <span class="st">"<p>"</span> + line + <span class="st">"</p>"</span></code></pre>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="https://git.qrty.ink/blu/lux/src/branch/master/projects/markdown-converter" class="btn btn-primary">View Code</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="project-card">
|
||||
<h2><span class="icon">CALC</span> Mini Interpreter</h2>
|
||||
<p class="description">
|
||||
A tiny expression interpreter demonstrating language implementation patterns.
|
||||
</p>
|
||||
<div class="features">
|
||||
<h3>Demonstrates</h3>
|
||||
<ul>
|
||||
<li>ADTs for AST</li>
|
||||
<li>Recursive evaluation</li>
|
||||
<li>Environment handling</li>
|
||||
<li>Interpreter patterns</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="code-preview">
|
||||
<pre><code><span class="kw">type</span> <span class="ty">Expr</span> =
|
||||
| <span class="ty">Num</span>(Int)
|
||||
| <span class="ty">Add</span>(Expr, Expr)
|
||||
| <span class="ty">Mul</span>(Expr, Expr)
|
||||
| <span class="ty">Var</span>(String)
|
||||
| <span class="ty">Let</span>(String, Expr, Expr)</code></pre>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<a href="https://git.qrty.ink/blu/lux/src/branch/master/projects/mini-interpreter" class="btn btn-primary">View Code</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Featured Showcase -->
|
||||
<section class="showcase-section">
|
||||
<h2>Featured: Task Manager API</h2>
|
||||
<div class="showcase-card">
|
||||
<h3>A Complete Showcase of Lux's Unique Features</h3>
|
||||
<p>This comprehensive example demonstrates all three of Lux's killer features working together.</p>
|
||||
|
||||
<div class="showcase-features">
|
||||
<div class="showcase-feature">
|
||||
<h4>1. Algebraic Effects</h4>
|
||||
<p>Every side effect is explicit in function signatures. No hidden I/O.</p>
|
||||
</div>
|
||||
<div class="showcase-feature">
|
||||
<h4>2. Behavioral Types</h4>
|
||||
<p>Compile-time guarantees: <code>is pure</code>, <code>is total</code>, <code>is idempotent</code>.</p>
|
||||
</div>
|
||||
<div class="showcase-feature">
|
||||
<h4>3. Schema Evolution</h4>
|
||||
<p>Versioned types with automatic migration. Data structures evolve safely.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="showcase-code">
|
||||
<pre><code><span class="cm">// Task v1: Original data model</span>
|
||||
<span class="kw">type</span> <span class="ty">Task</span> <span class="cm">@v1</span> {
|
||||
id: String,
|
||||
title: String,
|
||||
done: Bool
|
||||
}
|
||||
|
||||
<span class="cm">// Task v2: Added priority field with migration</span>
|
||||
<span class="kw">type</span> <span class="ty">Task</span> <span class="cm">@v2</span> {
|
||||
id: String,
|
||||
title: String,
|
||||
done: Bool,
|
||||
priority: String,
|
||||
|
||||
<span class="cm">// Old tasks get "medium" priority by default</span>
|
||||
<span class="kw">from</span> <span class="cm">@v1</span> = {
|
||||
id: old.id,
|
||||
title: old.title,
|
||||
done: old.done,
|
||||
priority: <span class="st">"medium"</span>
|
||||
}
|
||||
}
|
||||
|
||||
<span class="cm">// Pure, total, idempotent business logic</span>
|
||||
<span class="kw">fn</span> <span class="fn">validateTitle</span>(title: String): <span class="ty">Result</span><String, String>
|
||||
<span class="kw">is</span> <span class="ty">pure</span>, <span class="ty">total</span> =
|
||||
<span class="kw">if</span> String.length(title) == <span class="num">0</span> <span class="kw">then</span>
|
||||
Err(<span class="st">"Title cannot be empty"</span>)
|
||||
<span class="kw">else</span>
|
||||
Ok(title)</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="actions" style="margin-top: var(--space-lg);">
|
||||
<a href="https://git.qrty.ink/blu/lux/src/branch/master/examples/showcase/task_manager.lux" class="btn btn-primary">View Full Code</a>
|
||||
<a href="/tour/06-effects-intro.html" class="btn btn-secondary">Learn About Effects</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Get Started -->
|
||||
<section class="showcase-section" style="text-align: center;">
|
||||
<h2>Build Your Own</h2>
|
||||
<p style="margin-bottom: var(--space-lg); color: var(--text-secondary);">Ready to start building with Lux?</p>
|
||||
<div style="display: flex; gap: var(--space-md); justify-content: center; flex-wrap: wrap;">
|
||||
<a href="/install" class="btn btn-primary">Install Lux</a>
|
||||
<a href="/tour/" class="btn btn-secondary">Take the Tour</a>
|
||||
<a href="/examples/" class="btn btn-tertiary">Browse Examples</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
66
website/serve.lux
Normal file
66
website/serve.lux
Normal file
@@ -0,0 +1,66 @@
|
||||
// Static File Server for Lux Website
|
||||
//
|
||||
// Usage: lux website/serve.lux
|
||||
// Then open http://localhost:8080
|
||||
//
|
||||
// This demonstrates serving the Lux website using Lux's own HTTP server!
|
||||
|
||||
fn main(): Unit with {HttpServer, Console, File} = {
|
||||
let port = 8080
|
||||
let root = "website"
|
||||
|
||||
HttpServer.listen(port)
|
||||
Console.print("Lux website server running at http://localhost:" + toString(port))
|
||||
Console.print("Serving files from: " + root)
|
||||
Console.print("Press Ctrl+C to stop")
|
||||
Console.print("")
|
||||
|
||||
serverLoop(root)
|
||||
}
|
||||
|
||||
fn serverLoop(root: String): Unit with {HttpServer, Console, File} = {
|
||||
let req = HttpServer.accept()
|
||||
|
||||
// Log request
|
||||
Console.print(req.method + " " + req.path)
|
||||
|
||||
// Only handle GET requests
|
||||
if req.method != "GET" then {
|
||||
HttpServer.respond(405, "Method Not Allowed")
|
||||
serverLoop(root)
|
||||
} else {
|
||||
serveFile(root, req.path)
|
||||
serverLoop(root)
|
||||
}
|
||||
}
|
||||
|
||||
fn serveFile(root: String, reqPath: String): Unit with {HttpServer, Console, File} = {
|
||||
// Determine file path
|
||||
let path = if reqPath == "/" then "/index.html" else reqPath
|
||||
let filePath = root + path
|
||||
|
||||
// Try to serve the file
|
||||
if File.exists(filePath) then {
|
||||
let content = File.read(filePath)
|
||||
HttpServer.respond(200, content)
|
||||
} else {
|
||||
// Try with .html extension for clean URLs
|
||||
let htmlPath = filePath + ".html"
|
||||
if File.exists(htmlPath) then {
|
||||
let content = File.read(htmlPath)
|
||||
HttpServer.respond(200, content)
|
||||
} else {
|
||||
// Try index.html for directory paths
|
||||
let indexPath = filePath + "/index.html"
|
||||
if File.exists(indexPath) then {
|
||||
let content = File.read(indexPath)
|
||||
HttpServer.respond(200, content)
|
||||
} else {
|
||||
Console.print(" -> 404 Not Found: " + filePath)
|
||||
HttpServer.respond(404, "Not Found: " + reqPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let output = run main() with {}
|
||||
Reference in New Issue
Block a user