// The goal here is less to make anything useful...or even something that works, but to learn what parts an interpreted languages needs to have to function. // Define the types of tokens const TokenType = { NUMBER: 'NUMBER', PLUS: 'PLUS', IDENTIFIER: 'IDENTIFIER', ASSIGNMENT: 'ASSIGNMENT', FUNCTION: 'FUNCTION', LEFT_PAREN: 'LEFT_PAREN', RIGHT_PAREN: 'RIGHT_PAREN', LEFT_BRACE: 'LEFT_BRACE', RIGHT_BRACE: 'RIGHT_BRACE', SEMICOLON: 'SEMICOLON', }; // Lexer function lexer(input) { const tokens = []; let current = 0; while (current < input.length) { let char = input[current]; if (/\d/.test(char)) { let value = ''; while (/\d/.test(char)) { value += char; char = input[++current]; } tokens.push({ type: TokenType.NUMBER, value }); continue; } if (char === '+') { tokens.push({ type: TokenType.PLUS }); current++; continue; } if (/[a-z]/i.test(char)) { let value = ''; while (/[a-z]/i.test(char)) { value += char; char = input[++current]; } tokens.push({ type: TokenType.IDENTIFIER, value }); continue; } if (char === ':') { tokens.push({ type: TokenType.ASSIGNMENT }); current++; continue; } if (char === '=') { tokens.push({ type: TokenType.EQUAL }); current++; continue; } if (input.slice(current, current + 2) === 'if') { tokens.push({ type: TokenType.IF }); current += 2; continue; } if (input.slice(current, current + 4) === 'else') { tokens.push({ type: TokenType.ELSE }); current += 4; continue; } if (char === '(') { tokens.push({ type: TokenType.LEFT_PAREN }); current++; continue; } if (char === ')') { tokens.push({ type: TokenType.RIGHT_PAREN }); current++; continue; } if (char === '{') { tokens.push({ type: TokenType.LEFT_BRACE }); current++; continue; } if (char === '}') { tokens.push({ type: TokenType.RIGHT_BRACE }); current++; continue; } if (input.slice(current, current + 8) === 'function') { tokens.push({ type: TokenType.FUNCTION }); current += 8; continue; } if (char === ';') { tokens.push({ type: TokenType.SEMICOLON }); current++; continue; } current++; } return tokens; } // Parser function parser(tokens) { let current = 0; function walk() { if (current >= tokens.length) { return null; // Return null when there are no more tokens } let token = tokens[current]; if (token.type === TokenType.NUMBER) { current++; return { type: 'NumberLiteral', value: token.value, }; } if (token.type === TokenType.PLUS) { current++; return { type: 'PlusExpression', left: walk(), right: walk(), }; } if (token.type === TokenType.IDENTIFIER) { current++; return { type: 'Identifier', value: token.value, }; } if (token.type === TokenType.ASSIGNMENT) { current++; return { type: 'AssignmentExpression', name: tokens[current - 2].value, value: walk(), }; } if (token.type === TokenType.IF) { current++; let node = { type: 'IfExpression', test: walk(), consequent: walk(), alternate: tokens[current].type === TokenType.ELSE ? (current++, walk()) : null, }; return node; } if (token.type === TokenType.FUNCTION) { current++; let node = { type: 'FunctionDeclaration', name: tokens[current++].value, params: [], body: [], }; while (tokens[current].type !== TokenType.RIGHT_PAREN) { node.params.push(tokens[current++].value); } current++; // Skip right paren while (tokens[current].type !== TokenType.RIGHT_BRACE) { node.body.push(walk()); } current++; // Skip right brace return node; } if (token.type === TokenType.IDENTIFIER && tokens[current + 1].type === TokenType.LEFT_PAREN) { current++; let node = { type: 'FunctionCall', name: token.value, args: [], }; current++; // Skip left paren while (tokens[current].type !== TokenType.RIGHT_PAREN) { node.args.push(walk()); } current++; // Skip right paren return node; } if (token.type === TokenType.SEMICOLON) { current++; return; } throw new TypeError(token.type); } let ast = { type: 'Program', body: [], }; while (current < tokens.length) { const node = walk(); if (node !== null) { ast.body.push(node); } } return ast; } // Interpreter function interpreter(ast) { let globalScope = {}; function evalNode(node) { switch (node.type) { case 'NumberLiteral': return parseInt(node.value); case 'PlusExpression': return evalNode(node.left) + evalNode(node.right); case 'AssignmentExpression': globalScope[node.name] = evalNode(node.value); return; case 'Identifier': return globalScope[node.value]; case 'IfExpression': return evalNode(node.test) ? evalNode(node.consequent) : node.alternate ? evalNode(node.alternate) : undefined; case 'FunctionDeclaration': globalScope[node.name] = function() { let localScope = Object.create(globalScope); for (let i = 0; i < node.params.length; i++) { localScope[node.params[i]] = arguments[i]; } let lastResult; for (let bodyNode of node.body) { lastResult = evalNode(bodyNode); } return lastResult; }; return; case 'FunctionCall': if (globalScope[node.name] instanceof Function) { let args = node.args.map(evalNode); return globalScope[node.name].apply(null, args); } throw new Error(`Function ${node.name} is not defined`); default: throw new Error(`Unknown node type: ${node.type}`); } } return evalNode(ast.body[0]); } // Usage // const tokens = lexer('2 + 2'); // const ast = parser(tokens); // const result = interpreter(ast); // console.log(result); // 4 // const tokens2 = lexer('x : 2 + 2'); // const ast2 = parser(tokens2); // const result2 = interpreter(ast2); // console.log(result2); const fs = require('fs'); // Read the input from a file const input = fs.readFileSync('input.txt', 'utf-8'); // Usage const tokens = lexer(input); const ast = parser(tokens); const result = interpreter(ast); console.log(result);