UNPKG

quis

Version:

A simple DSL for data sorting and filtering

437 lines (371 loc) • 14.7 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="Interactive web demo of the Quis DSL Parser - a lightweight domain-specific language for data sorting and filtering"> <meta name="keywords" content="DSL, parser, data filtering, JavaScript, TypeScript, boolean expressions"> <meta name="author" content="Quis DSL Parser"> <link rel="icon" type="image/svg+xml" href="favicon.svg"> <title>Quis DSL Parser - Interactive Demo</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; line-height: 1.6; color: #333; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; } .header { text-align: center; color: white; margin-bottom: 40px; } .header h1 { font-size: 3rem; margin-bottom: 10px; font-weight: 300; } .header p { font-size: 1.2rem; opacity: 0.9; } .demo-section { background: white; border-radius: 12px; padding: 30px; margin-bottom: 30px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); } .demo-section h2 { color: #4a5568; margin-bottom: 20px; font-size: 1.5rem; } .input-group { margin-bottom: 20px; } .input-group label { display: block; margin-bottom: 8px; font-weight: 600; color: #4a5568; } .expression-input, .data-input { width: 100%; padding: 12px; border: 2px solid #e2e8f0; border-radius: 8px; font-size: 16px; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; transition: border-color 0.2s; } .expression-input:focus, .data-input:focus { outline: none; border-color: #667eea; } .data-input { height: 120px; resize: vertical; } .button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 12px 24px; border: none; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; } .button:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); } .result { margin-top: 20px; padding: 15px; border-radius: 8px; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; white-space: pre-wrap; } .result.success { background: #f0fff4; border: 2px solid #9ae6b4; color: #22543d; } .result.error { background: #fff5f5; border: 2px solid #feb2b2; color: #c53030; } .examples { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-top: 30px; } .example { background: #f7fafc; padding: 20px; border-radius: 8px; border-left: 4px solid #667eea; } .example h3 { color: #4a5568; margin-bottom: 10px; } .example-code { background: #2d3748; color: #e2e8f0; padding: 12px; border-radius: 6px; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 14px; margin: 10px 0; overflow-x: auto; } .example button { background: #4299e1; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 14px; margin-top: 10px; } .example button:hover { background: #3182ce; } .footer { text-align: center; color: white; margin-top: 40px; opacity: 0.8; } .footer a { color: white; text-decoration: none; } .footer a:hover { text-decoration: underline; } </style> </head> <body> <div class="container"> <div class="header"> <h1>Quis DSL Parser</h1> <p>Interactive demonstration of the lightweight data sorting DSL</p> </div> <div class="demo-section"> <h2>šŸš€ Interactive Demo</h2> <div class="input-group"> <label for="expressionInput">DSL Expression:</label> <input type="text" id="expressionInput" class="expression-input" placeholder="Enter your DSL expression (e.g., $user.age > 18 && $user.active)" value="$user.age > 18 && $user.active"> </div> <div class="input-group"> <label for="dataInput">Data Context (JSON):</label> <textarea id="dataInput" class="data-input" placeholder="Enter JSON data for variable resolution">{ "user": { "name": "Alice", "age": 25, "active": true, "level": 5 }, "config": { "debug": false, "maxRetries": 3 } }</textarea> </div> <button class="button" onclick="executeExpression()">Execute Expression</button> <div id="result" class="result" style="display: none;"></div> </div> <div class="demo-section"> <h2>šŸ“š Example Expressions</h2> <div class="examples"> <div class="example"> <h3>Boolean Logic</h3> <div class="example-code">true && false</div> <button onclick="runExample('true && false', '{}')">Try it</button> </div> <div class="example"> <h3>Numeric Comparison</h3> <div class="example-code">$user.age >= 21</div> <button onclick="runExample('$user.age >= 21', JSON.stringify({user: {age: 25}}, null, 2))">Try it</button> </div> <div class="example"> <h3>String Equality</h3> <div class="example-code">$user.name == "Alice"</div> <button onclick="runExample('$user.name == &quot;Alice&quot;', JSON.stringify({user: {name: 'Alice'}}, null, 2))">Try it</button> </div> <div class="example"> <h3>Complex Expression</h3> <div class="example-code">($user.level > 3 && $user.active) || $user.admin</div> <button onclick="runExample('($user.level > 3 && $user.active) || $user.admin', JSON.stringify({user: {level: 5, active: true, admin: false}}, null, 2))">Try it</button> </div> <div class="example"> <h3>Shorthand Operators</h3> <div class="example-code">$score gt 100 and $lives gte 1</div> <button onclick="runExample('$score gt 100 and $lives gte 1', JSON.stringify({score: 150, lives: 3}, null, 2))">Try it</button> </div> <div class="example"> <h3>Negation</h3> <div class="example-code">!$config.debug && $config.production</div> <button onclick="runExample('!$config.debug && $config.production', JSON.stringify({config: {debug: false, production: true}}, null, 2))">Try it</button> </div> <div class="example"> <h3>Custom Conditions</h3> <div class="example-code">$text custom:contains "hello"</div> <button onclick="runCustomConditionExample()">Try it</button> </div> </div> </div> <div class="demo-section"> <h2>šŸ“– Supported Operations</h2> <div class="examples"> <div class="example"> <h3>Comparison Operators</h3> <div class="example-code">==, !=, >, <, >=, <= is, is not, gt, lt, gte, lte</div> </div> <div class="example"> <h3>Boolean Operators</h3> <div class="example-code">&&, ||, ! AND, OR, and, or</div> </div> <div class="example"> <h3>Data Types</h3> <div class="example-code">Numbers: 42, 3.14 Strings: "hello", 'world' Booleans: true, false Null: null</div> </div> <div class="example"> <h3>Variable Access</h3> <div class="example-code">$variableName $object.property $nested.deep.value</div> </div> <div class="example"> <h3>Custom Conditions</h3> <div class="example-code">$value custom:conditionName $expected // Add custom condition quis.addCustomCondition('contains', (value, expected) => String(value).includes(String(expected)) );</div> </div> </div> </div> <div class="footer"> <p> Built with ā¤ļø | <a href="https://github.com/videlais/quis" target="_blank">View on GitHub</a> | <a href="https://npmjs.com/package/quis" target="_blank">NPM Package</a> </p> </div> </div> <!-- Load the Quis DSL Parser --> <script src="quis.min.js"></script> <script> // Initialize the demo function executeExpression() { const expressionInput = document.getElementById('expressionInput'); const dataInput = document.getElementById('dataInput'); const resultDiv = document.getElementById('result'); const expression = expressionInput.value.trim(); const dataText = dataInput.value.trim(); if (!expression) { showResult('Please enter a DSL expression', 'error'); return; } try { // Parse the JSON data let data = {}; if (dataText) { data = JSON.parse(dataText); } // Create values callback function const values = (variableName) => { return data[variableName] || null; }; // Execute the expression const result = window.quis.parse(expression, { values }); // Show the result showResult(`Expression: ${expression}\nResult: ${JSON.stringify(result, null, 2)}\nType: ${typeof result}`, 'success'); } catch (error) { let errorMessage = `Error: ${error.message}`; if (error.expected && error.found !== undefined) { errorMessage += `\nExpected: ${error.expected.map(e => e.description || e.type).join(', ')}`; errorMessage += `\nFound: ${error.found}`; if (error.location) { errorMessage += `\nLocation: Line ${error.location.start.line}, Column ${error.location.start.column}`; } } showResult(errorMessage, 'error'); } } function runExample(expression, data) { document.getElementById('expressionInput').value = expression; document.getElementById('dataInput').value = data; executeExpression(); } function showResult(message, type) { const resultDiv = document.getElementById('result'); resultDiv.textContent = message; resultDiv.className = `result ${type}`; resultDiv.style.display = 'block'; // Scroll to result resultDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } function runCustomConditionExample() { // Add a custom condition for string contains window.quis.addCustomCondition('contains', (value, expected) => { return String(value).includes(String(expected)); }); // Set up the example const expression = '$text custom:contains "hello"'; const data = JSON.stringify({ text: 'hello world from custom conditions!' }, null, 2); runExample(expression, data); // Show additional info about custom conditions setTimeout(() => { const conditions = window.quis.getCustomConditions(); const additionalInfo = `\n\nā„¹ļø Custom Conditions Demo:\n• Added "contains" condition\n• Available conditions: ${Object.keys(conditions).join(', ')}\n• Syntax: $value custom:conditionName $expected`; const resultDiv = document.getElementById('result'); if (resultDiv.style.display !== 'none') { resultDiv.textContent += additionalInfo; } }, 100); } // Allow Enter key to execute document.getElementById('expressionInput').addEventListener('keypress', function(e) { if (e.key === 'Enter') { executeExpression(); } }); // Execute the default expression on load window.addEventListener('load', function() { executeExpression(); }); </script> </body> </html>