Skip to content

Instantly share code, notes, and snippets.

@phatnguyenuit
Forked from tanduong/boolean_expression.html
Created October 16, 2025 11:30
Show Gist options
  • Save phatnguyenuit/5309f56e079c1f78056f533e56550e9c to your computer and use it in GitHub Desktop.
Save phatnguyenuit/5309f56e079c1f78056f533e56550e9c to your computer and use it in GitHub Desktop.
<!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