diff options
author | elioat <elioat@tilde.institute> | 2024-09-07 20:31:40 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2024-09-07 20:31:40 -0400 |
commit | e59ea3378b22261765f61ee8597bd2b7238cb98a (patch) | |
tree | 57b4e9ca2c34d68f9c646d1c11ea9a9784970eb5 /html/schemer | |
parent | da6be82d03a94c46069b9a04cb6b61cfe355eaf4 (diff) | |
download | tour-e59ea3378b22261765f61ee8597bd2b7238cb98a.tar.gz |
*
Diffstat (limited to 'html/schemer')
-rw-r--r-- | html/schemer/index.html | 233 | ||||
-rw-r--r-- | html/schemer/tls.pdf | bin | 0 -> 2359953 bytes |
2 files changed, 233 insertions, 0 deletions
diff --git a/html/schemer/index.html b/html/schemer/index.html new file mode 100644 index 0000000..ebadf83 --- /dev/null +++ b/html/schemer/index.html @@ -0,0 +1,233 @@ +<!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> + // Scheme interpreter (unchanged) + 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 === ')'; + 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; + } + + 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, + 'eq?': (...args) => args.every((val, i, arr) => val === arr[0]), + 'car': (list) => list[0], + 'cdr': (list) => list.slice(1), + 'cons': (a, b) => [a].concat(b), + 'null?': (list) => list.length === 0, + }; + + 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; + + if (first.type === 'Symbol') { + const operator = first.value; + + if (operator === 'define') { + const [symbol, expr] = rest; + env[symbol.value] = evaluate(expr, env); + return; + } + + if (operator === 'lambda') { + const [params, body] = rest; + + return function (...args) { + const lambdaEnv = { ...env }; + params.value.forEach((param, i) => { + lambdaEnv[param.value] = args[i]; + }); + return evaluate(body, lambdaEnv); + }; + } + + if (operator === 'if') { + const [test, consequent, alternate] = rest; + const condition = evaluate(test, env); + return condition ? evaluate(consequent, env) : evaluate(alternate, env); + } + } + + 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> diff --git a/html/schemer/tls.pdf b/html/schemer/tls.pdf new file mode 100644 index 0000000..7e28a5f --- /dev/null +++ b/html/schemer/tls.pdf Binary files differ |