<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Scheme Interpreter with PDF</title> <style> body, html { margin: 0; padding: 0; height: 100%; font-family: Arial, sans-serif; } #pdf-container { width: 100%; height: 67%; overflow: hidden; } #repl-container { width: 100%; height: 33%; display: flex; flex-direction: column; border-top: 1px solid #ccc; padding: 10px; } textarea { flex: 1; width: 100%; font-family: monospace; font-size: 16px; padding: 10px; } button { margin-top: 10px; padding: 10px; font-size: 16px; } #scheme-output { font-family: monospace; background-color: #f0f0f0; padding: 10px; margin-top: 10px; overflow-wrap: break-word; } </style> </head> <body> <div id="pdf-container"> <embed src="tls.pdf" type="application/pdf" width="100%" height="100%"> </div> <div id="repl-container"> <textarea id="scheme-input" placeholder="Scheme here..."></textarea> <div id="scheme-output"></div> <button onclick="evaluateScheme()">Run Code</button> </div> <script> function tokenizeScheme(input) { const tokens = []; let current = 0; const isWhitespace = (char) => /\s/.test(char); const isDigit = (char) => /[0-9]/.test(char); const isParen = (char) => char === '(' || char === ')'; // Symbols can include letters, numbers, and some punctuation like - _ ! ? const isSymbolChar = (char) => /[a-zA-Z0-9\+\-\*\/\=\?\!\_]/.test(char); while (current < input.length) { let char = input[current]; if (isWhitespace(char)) { current++; continue; } if (isParen(char)) { tokens.push({ type: 'paren', value: char }); current++; continue; } if (isDigit(char) || (char === '-' && isDigit(input[current + 1]))) { let number = ''; while (isDigit(char) || char === '-') { number += char; char = input[++current]; } tokens.push({ type: 'number', value: number }); continue; } // Handle symbols, including letters, numbers, punctuation if (isSymbolChar(char)) { let symbol = ''; while (isSymbolChar(char)) { symbol += char; char = input[++current]; } tokens.push({ type: 'symbol', value: symbol }); continue; } throw new Error(`Unexpected character: ${char}`); } return tokens; } function parseScheme(tokens) { let current = 0; function walk() { let token = tokens[current]; if (token.type === 'number') { current++; return { type: 'NumberLiteral', value: Number(token.value) }; } if (token.type === 'symbol') { current++; return { type: 'Symbol', value: token.value }; } if (token.type === 'paren' && token.value === '(') { current++; const node = { type: 'List', value: [] }; while (!(tokens[current].type === 'paren' && tokens[current].value === ')')) { node.value.push(walk()); } current++; // Skip closing ')' return node; } throw new Error(`Unexpected token: ${token.type}`); } return walk(); } const globalEnv = { '+': (...args) => args.reduce((acc, curr) => acc + curr), '-': (...args) => args.reduce((acc, curr) => acc - curr), '*': (...args) => args.reduce((acc, curr) => acc * curr), '/': (a, b) => a / b, // Only two arguments for division 'eq?': (...args) => args.every((val, i, arr) => val === arr[0]), 'car': (list) => { if (list.type !== 'List' || list.value.length === 0) { throw new Error('car expects a non-empty list'); } return list.value[0]; }, 'cdr': (list) => { if (list.type !== 'List' || list.value.length === 0) { throw new Error('cdr expects a non-empty list'); } return { type: 'List', value: list.value.slice(1) }; }, 'cons': (a, b) => { if (b.type !== 'List') { throw new Error('cons expects second argument to be a list'); } return { type: 'List', value: [a].concat(b.value) }; }, 'null?': (list) => list.type === 'List' && list.value.length === 0, 'zero?': (n) => n === 0, 'atom?': (x) => typeof x !== 'object' || x === null, 'number?': (x) => typeof x === 'number', 'add1': (n) => n + 1, 'sub1': (n) => n - 1, 'quote': (x) => x, // Simply return the quoted expression 'and': (...args) => args.every(Boolean), 'or': (...args) => args.some(Boolean), 'true': true, 'false': false }; function evaluate(node, env = globalEnv) { if (node.type === 'NumberLiteral') { return node.value; } if (node.type === 'Symbol') { if (env[node.value] !== undefined) { return env[node.value]; } throw new Error(`Undefined symbol: ${node.value}`); } if (node.type === 'List') { const [first, ...rest] = node.value; // Is the first element a symbol, like an operator or function name? if (first.type === 'Symbol') { const operator = first.value; // Special case for define if (operator === 'define') { const [symbol, expr] = rest; env[symbol.value] = evaluate(expr, env); return; } // Special case for lambda if (operator === 'lambda') { const [params, body] = rest; // Create a closure to return return function (...args) { const lambdaEnv = { ...env }; // Bind each argument to the corresponding parameter... params.value.forEach((param, i) => { lambdaEnv[param.value] = args[i]; }); // ...and then evaluate the body with the environment return evaluate(body, lambdaEnv); }; } // Special case for if if (operator === 'if') { const [test, consequent, alternate] = rest; const condition = evaluate(test, env); return condition ? evaluate(consequent, env) : evaluate(alternate, env); } // Special case for quote if (operator === 'quote') { return rest[0]; // Return the quoted expression without evaluating it } // Special case for cond if (operator === 'cond') { for (let clause of rest) { const [test, expr] = clause.value; if (evaluate(test, env)) { return evaluate(expr, env); } } return null; // No matching condition } // Special case for letrec (recursive let) if (operator === 'letrec') { const [bindings, body] = rest; const letEnv = { ...env }; // Loop through bindings and evaluate each bindings.value.forEach(binding => { const [name, expr] = binding.value; letEnv[name.value] = evaluate(expr, letEnv); }); return evaluate(body, letEnv); } } // Evaluate the first element const func = evaluate(first, env); if (typeof func !== 'function') { throw new Error(`Expected a function but got: ${func}`); } const args = rest.map(arg => evaluate(arg, env)); return func(...args); } throw new Error(`Unexpected node type: ${node.type}`); } function evalScheme(input) { const tokens = tokenizeScheme(input); const ast = parseScheme(tokens); return evaluate(ast); } // Function to evaluate the input in the REPL function evaluateScheme() { const input = document.getElementById('scheme-input').value; let output; try { output = evalScheme(input); } catch (error) { output = `Error: ${error.message}`; } document.getElementById('scheme-output').innerText = JSON.stringify(output, null, 2); } </script> </body> </html>