UNPKG

lingo3d

Version:

Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor

142 lines 5.14 kB
const numbers = "0123456789._"; const operators = "+-*/="; const parentheses = "()"; const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const typeMap = new Map(); for (const char of numbers) typeMap.set(char, "number"); for (const char of operators) typeMap.set(char, "operator"); for (const char of parentheses) typeMap.set(char, "parenthesis"); for (const char of letters) typeMap.set(char, "letter"); typeMap.set(" ", "whitespace"); const mapTokenType = (type) => { switch (type) { case "number": return "number"; case "operator": return "operator"; case "parenthesis": return "parenthesis"; case "letter": return "text"; default: throw new Error(`Invalid type: ${type}`); } }; export class Token { type; value; prev; next; linked; constructor(type, value, prev) { this.type = type; this.value = value; this.prev = prev; } } class TokenIterator { current; constructor(current) { this.current = current; } next() { if (this.current) { const { current } = this; this.current = this.current.next; return { done: false, value: current }; } return { done: true, value: undefined }; } } export class TokenList { first; constructor(first) { this.first = first; } [Symbol.iterator]() { return new TokenIterator(this.first); } } const validOperators = new Set(["+", "-", "*", "**", "/", "%", "="]); const isPlusOrMinus = (val) => val === "+" || val === "-"; export const tokenize = (val, tokenList) => { let typeOld; let tokenOld; let tokenValue = ""; let openParentheses = 0; const openParenthesisStack = []; const openCloseParenthesisTokens = []; for (const char of val + "(") { const type = typeMap.get(char); if (!type) throw new Error(`Invalid character: ${char}`); if (typeOld === type && type !== "parenthesis" && !isPlusOrMinus(tokenValue)) tokenValue += char; else if (tokenValue) { if (typeOld === "number" && isNaN(Number(tokenValue))) throw new Error(`Invalid number: ${tokenValue}`); if (typeOld === "operator" && !validOperators.has(tokenValue)) throw new Error(`Invalid operator: ${tokenValue}`); if (typeOld !== "whitespace") { const token = new Token(mapTokenType(typeOld), tokenValue, tokenOld); if (token.type === tokenOld?.type && token.type !== "parenthesis" && !isPlusOrMinus(token.value)) throw new Error(`Unexpected token: ${tokenValue}`); if (tokenOld?.type === "sign" && !(token.type === "number" || token.type === "parenthesis" || token.type === "text")) throw new Error(`Unexpected token: ${tokenValue}`); if (tokenOld?.value === ")" && token.type !== "operator" && token.value !== ")") throw new Error(`Unexpected token: ${tokenValue}`); if (token.type === "parenthesis") if (token.value === "(") { if (tokenOld?.type === "number") throw new Error(`Unexpected token: ${tokenValue}`); openParentheses++; openParenthesisStack.push(token); } else if (token.value === ")") { if (tokenOld?.type === "operator") throw new Error(`Unexpected token: ${tokenValue}`); if (--openParentheses < 0) throw new Error(`Unexpected token: ${tokenValue}`); const openParenthesisToken = openParenthesisStack.pop(); if (!openParenthesisToken) throw new Error(`Unexpected token: ${tokenValue}`); openCloseParenthesisTokens.push([ openParenthesisToken, token ]); } if (token.type === "operator" && isPlusOrMinus(token.value) && (!tokenOld || tokenOld.type === "parenthesis" || tokenOld.type === "operator")) token.type = "sign"; if (tokenOld) tokenOld.next = token; tokenOld = token; tokenList.first ??= token; } tokenValue = char; } else tokenValue = char; typeOld = type; } if (openParentheses !== 0) throw new Error("Unbalanced parentheses"); return openCloseParenthesisTokens; }; //# sourceMappingURL=tokenize.js.map