lingo3d
Version:
Lingo3D is a React/Vue 3d game development framework that ships with a complete visual editor
142 lines • 5.14 kB
JavaScript
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