diff options
Diffstat (limited to 'js/scripting-lang/web')
-rw-r--r-- | js/scripting-lang/web/README-AST.md | 67 | ||||
-rw-r--r-- | js/scripting-lang/web/ast-viewer.html | 150 | ||||
-rw-r--r-- | js/scripting-lang/web/simple.html | 2 | ||||
-rw-r--r-- | js/scripting-lang/web/src/ast.js | 161 | ||||
-rw-r--r-- | js/scripting-lang/web/src/view.js | 4 | ||||
-rw-r--r-- | js/scripting-lang/web/style.css | 36 |
6 files changed, 417 insertions, 3 deletions
diff --git a/js/scripting-lang/web/README-AST.md b/js/scripting-lang/web/README-AST.md new file mode 100644 index 0000000..194aeec --- /dev/null +++ b/js/scripting-lang/web/README-AST.md @@ -0,0 +1,67 @@ +# Baba Yaga AST Visualizer + +A web-based tool for visualizing the Abstract Syntax Tree (AST) of Baba Yaga code. + +## Features + +- **Real-time AST Generation**: Enter Baba Yaga code and see its AST instantly +- **Token Visualization**: View the tokenized representation of your code +- **Error Display**: Clear error messages for invalid syntax +- **Example Code**: Pre-loaded examples demonstrating different language features +- **Copy to Clipboard**: One-click copying of AST and tokens for easy sharing +- **Clean Interface**: Simple, focused design following the project's design patterns + +## Usage + +1. Open `ast-viewer.html` in your browser +2. Enter Baba Yaga code in the text area +3. Click "Generate AST" or use Ctrl+Enter +4. View the AST and tokens in the output sections below +5. Use the "Copy AST" or "Copy Tokens" buttons to copy the content to your clipboard + +## Examples Included + +- **Simple Assignment**: Basic variable assignment +- **When Expression**: Pattern matching with when/is/then +- **Function Definition**: Arrow function with pattern matching +- **Table Literal**: Creating and accessing table structures +- **Arithmetic Expression**: Mathematical operations and function composition +- **Complex When Expression**: Multi-pattern matching + +## Technical Details + +- Uses the same `lexer.js` and `parser.js` modules as the main language +- No modifications to core language files required +- Pure client-side JavaScript with ES6 modules +- Responsive design that works on desktop and mobile + +## File Structure + +``` +web/ +├── ast.html # Main AST visualization interface +├── src/ +│ └── ast.js # AST generation logic +├── style.css # Shared styling +└── README-AST.md # This file +``` + +## Browser Compatibility + +Requires a modern browser with ES6 module support: +- Chrome 61+ +- Firefox 60+ +- Safari 10.1+ +- Edge 16+ + +## Development + +To run locally: +```bash +cd web +python3 -m http.server 8000 +# or +npx serve . +``` + +Then open `http://localhost:8000/ast.html` \ No newline at end of file diff --git a/js/scripting-lang/web/ast-viewer.html b/js/scripting-lang/web/ast-viewer.html new file mode 100644 index 0000000..269504f --- /dev/null +++ b/js/scripting-lang/web/ast-viewer.html @@ -0,0 +1,150 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Baba Yaga AST Viewer</title> + <link rel="stylesheet" href="style.css"> + <style> + textarea { + width: 100%; + min-height: 200px; + padding: 0.6em; + font-size: 1em; + font-family: 'Courier New', monospace; + border: 2px solid var(--color-input-border); + border-radius: 0.2em; + margin-bottom: 1em; + box-sizing: border-box; + resize: vertical; + } + + .output { + white-space: pre-wrap; + font-family: 'Courier New', monospace; + font-size: 0.9em; + background: var(--color-code-bg); + padding: 1em; + border-radius: 0.2em; + border: 1px solid var(--color-result-border); + max-height: 400px; + overflow-y: auto; + resize: vertical; + min-height: 200px; + } + + .error { + color: var(--color-error); + background: #ffeef0; + border-left: 4px solid var(--color-error); + padding: 1em; + margin-bottom: 1em; + } + + .example-selector { + margin-bottom: 1em; + } + + .example-selector select { + padding: 0.6em; + font-size: 1em; + border: 2px solid var(--color-input-border); + border-radius: 0.2em; + background: white; + margin-left: 0.5em; + } + + .example-selector select:focus { + outline: none; + border-color: #007acc; + } + + .output-section { + margin-top: 1.5em; + } + + .output-section h3 { + margin-bottom: 0.5em; + color: var(--color-label); + text-transform: uppercase; + font-size: 0.9em; + } + + .output-container { + position: relative; + } + + .copy-btn { + position: absolute; + top: 0.5em; + right: 0.5em; + background: var(--color-button-bg); + color: var(--color-button-text); + border: none; + border-radius: 0.2em; + padding: 0.3em 0.6em; + font-size: 0.8em; + font-weight: bold; + cursor: pointer; + z-index: 10; + } + + .copy-btn:hover { + background: #005a9e; + } + + .copy-btn:active { + transform: translateY(1px); + } + </style> +</head> +<body> + <main> + <h1>Baba Yaga AST Visualizer</h1> + + <div class="example-selector"> + <label for="examples">Load Example:</label> + <select id="examples"> + <option value="">Choose an example...</option> + <option value="simple">Simple Assignment</option> + <option value="when">When Expression</option> + <option value="function">Function Definition</option> + <option value="table">Table Literal</option> + <option value="arithmetic">Arithmetic Expression</option> + <option value="complex">Complex When Expression</option> + </select> + </div> + + <label for="code-input">Code:</label> + <textarea + id="code-input" + placeholder="Enter Baba Yaga code here... +Example: +x : 42; +result : when x is 42 then "correct" _ then "wrong";" + ></textarea> + + <button id="generate-btn">Generate AST</button> + + <div class="output-section"> + <h3>AST Output:</h3> + <div class="output-container"> + <textarea id="ast-output" class="output" readonly placeholder="AST will appear here..."></textarea> + <button id="copy-ast-btn" class="copy-btn">Copy AST</button> + </div> + </div> + + <div class="output-section"> + <h3>Tokens:</h3> + <div class="output-container"> + <textarea id="tokens-output" class="output" readonly placeholder="Tokens will appear here..."></textarea> + <button id="copy-tokens-btn" class="copy-btn">Copy Tokens</button> + </div> + </div> + + <div id="error-output" class="error" style="display: none;"></div> + </main> + + <script type="module" src="src/ast.js"></script> +</body> +</html> \ No newline at end of file diff --git a/js/scripting-lang/web/simple.html b/js/scripting-lang/web/simple.html index 2aa5dac..9b8fd19 100644 --- a/js/scripting-lang/web/simple.html +++ b/js/scripting-lang/web/simple.html @@ -39,7 +39,7 @@ <body> <main> <h1>Baba Yaga</h1> - + <div class="result" id="result" style="display: none;"> <div class="output" id="output"></div> </div> diff --git a/js/scripting-lang/web/src/ast.js b/js/scripting-lang/web/src/ast.js new file mode 100644 index 0000000..522d026 --- /dev/null +++ b/js/scripting-lang/web/src/ast.js @@ -0,0 +1,161 @@ +// ast.js +// AST visualization tool for Baba Yaga language + +import { lexer, parser } from '../../lang.js'; + +const examples = { + simple: `x : 42;`, + + when: `result : when x is 42 then "correct" _ then "wrong";`, + + function: `factorial : n -> + when n is + 0 then 1 + _ then n * (factorial (n - 1));`, + + table: `person : {name: "Baba Yaga", age: 99, active: true}; +numbers : {1, 2, 3, 4, 5};`, + + arithmetic: `result : 5 + 3 * 2; +composed : compose @double @increment 5;`, + + complex: `classify : x y -> + when x y is + 0 0 then "both zero" + 0 _ then "x is zero" + _ 0 then "y is zero" + _ _ then "neither zero";` +}; + +// DOM elements - will be initialized when DOM is ready +let codeInput, generateBtn, examplesSelect, astOutput, tokensOutput, errorOutput, copyAstBtn, copyTokensBtn; + +// Initialize when DOM is ready +document.addEventListener('DOMContentLoaded', () => { + // Initialize DOM elements + codeInput = document.getElementById('code-input'); + generateBtn = document.getElementById('generate-btn'); + examplesSelect = document.getElementById('examples'); + astOutput = document.getElementById('ast-output'); + tokensOutput = document.getElementById('tokens-output'); + errorOutput = document.getElementById('error-output'); + copyAstBtn = document.getElementById('copy-ast-btn'); + copyTokensBtn = document.getElementById('copy-tokens-btn'); + + // Example selector functionality + examplesSelect.addEventListener('change', () => { + const selectedExample = examplesSelect.value; + if (selectedExample && examples[selectedExample]) { + codeInput.value = examples[selectedExample]; + generateAST(); + } + }); + + // Generate button click handler + generateBtn.addEventListener('click', generateAST); + + // Copy button click handlers + copyAstBtn.addEventListener('click', () => copyToClipboard(astOutput, 'AST')); + copyTokensBtn.addEventListener('click', () => copyToClipboard(tokensOutput, 'Tokens')); + + // Auto-generate on Enter key (but not in textarea) + document.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && e.ctrlKey && document.activeElement !== codeInput) { + generateAST(); + } + }); + + // Initialize with a default example + codeInput.value = examples.when; + generateAST(); +}); + +// Generate AST from code +function generateAST() { + if (!codeInput) return; // DOM not ready yet + + const code = codeInput.value.trim(); + + if (!code) { + showError('Please enter some code to analyze.'); + return; + } + + try { + // Generate tokens + const tokens = lexer(code); + showTokens(tokens); + + // Generate AST + const ast = parser(tokens); + showAST(ast); + + // Clear any previous errors + showError(''); + + } catch (error) { + showError(`Parsing Error: ${error.message}`); + showAST(null); + showTokens(null); + } +} + +// Display AST in formatted JSON +function showAST(ast) { + if (!astOutput) return; // DOM not ready yet + + if (ast) { + astOutput.value = JSON.stringify(ast, null, 2); + } else { + astOutput.value = 'No AST available due to parsing error.'; + } +} + +// Display tokens in formatted JSON +function showTokens(tokens) { + if (!tokensOutput) return; // DOM not ready yet + + if (tokens) { + tokensOutput.value = JSON.stringify(tokens, null, 2); + } else { + tokensOutput.value = 'No tokens available due to parsing error.'; + } +} + +// Display error message +function showError(message) { + if (!errorOutput) return; // DOM not ready yet + + if (message) { + errorOutput.textContent = message; + errorOutput.style.display = 'block'; + } else { + errorOutput.style.display = 'none'; + } +} + +// Copy text to clipboard +async function copyToClipboard(textarea, label) { + if (!textarea || !textarea.value) { + showError(`No ${label} content to copy.`); + return; + } + + try { + await navigator.clipboard.writeText(textarea.value); + + // Show temporary success message + const originalText = errorOutput.textContent; + showError(`${label} copied to clipboard!`); + + // Clear success message after 2 seconds + setTimeout(() => { + if (errorOutput.textContent === `${label} copied to clipboard!`) { + showError(''); + } + }, 2000); + + } catch (error) { + showError(`Failed to copy ${label}: ${error.message}`); + } +} \ No newline at end of file diff --git a/js/scripting-lang/web/src/view.js b/js/scripting-lang/web/src/view.js index 6d591cf..ab64910 100644 --- a/js/scripting-lang/web/src/view.js +++ b/js/scripting-lang/web/src/view.js @@ -22,7 +22,9 @@ */ export function view(state) { return ` - <h1>Baba Yaga's PokéDex</h1> + <header class="app-header"> + <h1>Baba Yaga's PokéDex</h1> + </header> <container> <form id="search-form" autocomplete="off"> <label for="pokemon-query">Pokémon Name (or number)</label> diff --git a/js/scripting-lang/web/style.css b/js/scripting-lang/web/style.css index fea1820..4cd5c33 100644 --- a/js/scripting-lang/web/style.css +++ b/js/scripting-lang/web/style.css @@ -23,9 +23,43 @@ body { margin: 0; padding: 0; } +.app-header { + max-width: 800px; + margin: 2rem auto 1rem; + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 1.5rem; +} + +.app-header h1 { + margin: 0; + font-size: 1.8rem; +} + +.app-nav { + display: flex; + gap: 1rem; +} + +.nav-link { + color: var(--color-text); + text-decoration: none; + padding: 0.5rem 1rem; + border: 2px solid var(--color-main-border); + border-radius: 6px; + font-weight: 600; + transition: all 0.2s; +} + +.nav-link:hover { + background: var(--color-main-border); + color: var(--color-button-text); +} + main { max-width: 800px; - margin: 3rem auto; + margin: 0 auto 3rem; background: var(--color-main-bg); border: 2px solid var(--color-main-border); border-radius: 8px; |