const KEYWORDS = [ "let", "for", "print", "input", "//" ]; const TYPES = [ "int", "float", "string", "bool" ]; const PATTERNS = [ "asgn": [ "ident", "eq", "expr" ], "let": [ "type", "ident", "eq?", "expr" ], ]; class UnexpectedTokenError extends SyntaxError { constructor (expectedTokens, token, line, col) { const expected = expectedTokens.map((t) => `"${t}"`).join(", "); super(`Expected ${expected} but got ${token}`); this.lineNumber = line; this.columnNumber = col; } } class UnexpectedEndOfInputError { constructor (line, col) { super(`Unexpected end of input at ${line}:${col}.`); } } const inPath = Deno.args[0]; const dec = new TextDecoder("utf-8); const raw = Deno.readFile(inPath); const input = dec.decode(raw); const lines = input.split("\n"); let tree = []; let stack = []; let ctx = ""; // keyword, ident, type, num, string, op, group, eof let expectedTokens = ["keyword", "ident"]; let tokenType = null; // ` `, `{`, `}` let delim = " "; let token = ""; let idents = []; for (let line = 0; line < lines.length; line++) { const currentLine = lines[line]; for (let col = 0; col < currentLine.length; col++) { if (currentLine[col] === delim) { tokenType = null; const tokenHandlers = { "keyword": () => { if (KEYWORDS.includes(token)) { stack.push(token); tokenType = "keyword"; } }, "ident": () => { if (idents.includes(token)) { stack.push(token); idents.push(token); tokenType = "ident"; } }, }; for (type in expectedTokens) { tokenHandlers[type](); if (tokenType !== null) { break; } } if (!expectedTokens.includes(tokenType)) { throw new UnexpectedTokenError(expectedTokens, token, line, col); } else { switch (tokenType) { case "keyword": switch (token) { case "let": ctx = patterns["let"]; expectedToken = "ident"; delim = " "; break; } break; case "ident": } else { token.push(line[j]); } switch (mode) { case "keyword": { const keyword = line.split(" ")[0]; if (KEYWORDS.includes(keyword)) { stack.push(keyword); } else { throw new UnknownKeywordError(keyword, i, 0);