-
-
Save phatnguyenuit/5309f56e079c1f78056f533e56550e9c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Boolean Expression Evaluator Demo</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .container { | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); | |
| padding: 40px; | |
| max-width: 800px; | |
| width: 100%; | |
| } | |
| h1 { | |
| color: #333; | |
| margin-bottom: 10px; | |
| font-size: 28px; | |
| } | |
| .subtitle { | |
| color: #666; | |
| margin-bottom: 30px; | |
| font-size: 14px; | |
| } | |
| .section { | |
| margin-bottom: 25px; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 8px; | |
| color: #555; | |
| font-weight: 600; | |
| font-size: 14px; | |
| } | |
| input[type="text"], textarea { | |
| width: 100%; | |
| padding: 12px; | |
| border: 2px solid #e0e0e0; | |
| border-radius: 6px; | |
| font-size: 14px; | |
| font-family: 'Courier New', monospace; | |
| transition: border-color 0.3s; | |
| } | |
| input[type="text"]:focus, textarea:focus { | |
| outline: none; | |
| border-color: #667eea; | |
| } | |
| textarea { | |
| resize: vertical; | |
| min-height: 80px; | |
| } | |
| button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border: none; | |
| padding: 12px 30px; | |
| border-radius: 6px; | |
| font-size: 16px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| width: 100%; | |
| } | |
| button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4); | |
| } | |
| button:active { | |
| transform: translateY(0); | |
| } | |
| .result { | |
| margin-top: 20px; | |
| padding: 20px; | |
| border-radius: 6px; | |
| font-weight: 600; | |
| font-size: 18px; | |
| text-align: center; | |
| display: none; | |
| } | |
| .result.success { | |
| background: #d4edda; | |
| border: 2px solid #28a745; | |
| color: #155724; | |
| } | |
| .result.error { | |
| background: #f8d7da; | |
| border: 2px solid #dc3545; | |
| color: #721c24; | |
| } | |
| .examples { | |
| background: #f8f9fa; | |
| padding: 15px; | |
| border-radius: 6px; | |
| margin-top: 30px; | |
| } | |
| .examples h3 { | |
| color: #333; | |
| margin-bottom: 10px; | |
| font-size: 16px; | |
| } | |
| .examples ul { | |
| list-style: none; | |
| } | |
| .examples li { | |
| padding: 8px 0; | |
| color: #555; | |
| font-family: 'Courier New', monospace; | |
| font-size: 13px; | |
| } | |
| .examples li strong { | |
| color: #667eea; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Boolean Expression Evaluator</h1> | |
| <p class="subtitle">Tokenize → Parse → Evaluate</p> | |
| <div class="section"> | |
| <label for="expression">Expression (use AND, OR, NOT):</label> | |
| <input type="text" id="expression" placeholder="A AND (B OR C)" value="isPremium AND (regionUS OR regionEU) AND NOT isBanned"> | |
| </div> | |
| <div class="section"> | |
| <label for="values">Variables (JSON format):</label> | |
| <textarea id="values" placeholder='{"A": true, "B": false, "C": true}'>{"isPremium": true, "regionUS": false, "regionEU": true, "isBanned": false}</textarea> | |
| </div> | |
| <button onclick="evaluateExpression()">Evaluate</button> | |
| <div id="result" class="result"></div> | |
| <div class="examples"> | |
| <h3>Examples:</h3> | |
| <ul> | |
| <li><strong>Expression:</strong> A AND (B OR C)</li> | |
| <li><strong>Variables:</strong> {"A": true, "B": false, "C": true}</li> | |
| <li><strong>Result:</strong> true</li> | |
| <li style="margin-top: 15px;"><strong>Expression:</strong> NOT isActive OR (isPremium AND hasAccess)</li> | |
| <li><strong>Variables:</strong> {"isActive": false, "isPremium": true, "hasAccess": true}</li> | |
| <li><strong>Result:</strong> true</li> | |
| </ul> | |
| </div> | |
| </div> | |
| <script> | |
| // Boolean Expression Language | |
| // Tokenizer → Parser → Evaluator | |
| // ============================================================================ | |
| // TOKENIZER - Convert text into tokens | |
| // ============================================================================ | |
| const TokenType = { VARIABLE: 'VARIABLE', AND: 'AND', OR: 'OR', NOT: 'NOT', | |
| LPAREN: 'LPAREN', RPAREN: 'RPAREN', EOF: 'EOF' }; | |
| const KEYWORDS = { AND: 'AND', OR: 'OR', NOT: 'NOT' }; | |
| function tokenize(input) { | |
| const tokens = []; | |
| let pos = 0; | |
| while (pos < input.length) { | |
| const char = input[pos]; | |
| if (/\s/.test(char)) { pos++; continue; } | |
| if (char === '(') { tokens.push({ type: TokenType.LPAREN, value: char }); pos++; continue; } | |
| if (char === ')') { tokens.push({ type: TokenType.RPAREN, value: char }); pos++; continue; } | |
| if (/[a-zA-Z]/.test(char)) { | |
| let value = ''; | |
| while (pos < input.length && /[a-zA-Z0-9_]/.test(input[pos])) value += input[pos++]; | |
| const upper = value.toUpperCase(); | |
| const type = KEYWORDS[upper] ? TokenType[upper] : TokenType.VARIABLE; | |
| tokens.push({ type, value: KEYWORDS[upper] || value }); | |
| continue; | |
| } | |
| throw new Error(`Unexpected character '${char}' at position ${pos}`); | |
| } | |
| tokens.push({ type: TokenType.EOF, value: null }); | |
| return tokens; | |
| } | |
| // ============================================================================ | |
| // PARSER - Build Abstract Syntax Tree | |
| // ============================================================================ | |
| class Parser { | |
| constructor(tokens) { this.tokens = tokens; this.pos = 0; } | |
| current = () => this.tokens[this.pos]; | |
| advance = () => this.pos++; | |
| match = (...types) => types.includes(this.current().type); | |
| expect(type) { | |
| if (this.current().type !== type) throw new Error(`Expected ${type}`); | |
| this.advance(); | |
| } | |
| parse() { | |
| const ast = this.parseOr(); | |
| this.expect(TokenType.EOF); | |
| return ast; | |
| } | |
| parseBinary(next, ops) { | |
| let left = next.call(this); | |
| while (this.match(...ops)) { | |
| const op = this.current().value; | |
| this.advance(); | |
| left = { type: 'BinaryOp', operator: op, left, right: next.call(this) }; | |
| } | |
| return left; | |
| } | |
| parseOr = () => this.parseBinary(this.parseAnd, [TokenType.OR]); | |
| parseAnd = () => this.parseBinary(this.parseNot, [TokenType.AND]); | |
| parseNot() { | |
| if (this.match(TokenType.NOT)) { | |
| this.advance(); | |
| return { type: 'UnaryOp', operator: 'NOT', operand: this.parseNot() }; | |
| } | |
| return this.parsePrimary(); | |
| } | |
| parsePrimary() { | |
| if (this.match(TokenType.VARIABLE)) { | |
| const name = this.current().value; | |
| this.advance(); | |
| return { type: 'Variable', name }; | |
| } | |
| if (this.match(TokenType.LPAREN)) { | |
| this.advance(); | |
| const expr = this.parseOr(); | |
| this.expect(TokenType.RPAREN); | |
| return expr; | |
| } | |
| throw new Error('Unexpected token'); | |
| } | |
| } | |
| function parse(tokens) { | |
| return new Parser(tokens).parse(); | |
| } | |
| // ============================================================================ | |
| // EVALUATOR - Compute boolean results | |
| // ============================================================================ | |
| const OPS = { | |
| AND: (l, r) => l && r, | |
| OR: (l, r) => l || r, | |
| NOT: (x) => !x | |
| }; | |
| function evaluateAST(node, values) { | |
| if (node.type === 'Variable') { | |
| if (!(node.name in values)) throw new Error(`Undefined variable: ${node.name}`); | |
| return values[node.name]; | |
| } | |
| if (node.type === 'BinaryOp') { | |
| return OPS[node.operator](evaluateAST(node.left, values), evaluateAST(node.right, values)); | |
| } | |
| if (node.type === 'UnaryOp') { | |
| return OPS[node.operator](evaluateAST(node.operand, values)); | |
| } | |
| throw new Error(`Unknown node type: ${node.type}`); | |
| } | |
| // ============================================================================ | |
| // API | |
| // ============================================================================ | |
| function evaluate(expression, values) { | |
| return evaluateAST(parse(tokenize(expression)), values); | |
| } | |
| // ============================================================================ | |
| // UI LOGIC | |
| // ============================================================================ | |
| function evaluateExpression() { | |
| const expressionInput = document.getElementById('expression').value; | |
| const valuesInput = document.getElementById('values').value; | |
| const resultDiv = document.getElementById('result'); | |
| try { | |
| const values = JSON.parse(valuesInput); | |
| const result = evaluate(expressionInput, values); | |
| resultDiv.className = 'result success'; | |
| resultDiv.style.display = 'block'; | |
| resultDiv.innerHTML = ` | |
| <div>Result: <strong>${result ? 'TRUE ✓' : 'FALSE ✗'}</strong></div> | |
| <div style="font-size: 12px; margin-top: 8px; opacity: 0.8;"> | |
| Expression evaluated successfully | |
| </div> | |
| `; | |
| } catch (error) { | |
| resultDiv.className = 'result error'; | |
| resultDiv.style.display = 'block'; | |
| resultDiv.innerHTML = ` | |
| <div>Error</div> | |
| <div style="font-size: 14px; margin-top: 8px; font-weight: normal;"> | |
| ${error.message} | |
| </div> | |
| `; | |
| } | |
| } | |
| // Allow Enter key to evaluate | |
| document.getElementById('expression').addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') evaluateExpression(); | |
| }); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment