export enum TokenType {
Number,
Identifier,
Equals,
OpenParent, ClosedParent,
Let,
BinaryOperator,
}
const KEYWORDS: Record = {
"let": TokenType.Let,
}
export interface Token {
value: string,
type: TokenType,
}
export function token(value = "", type: TokenType): Token {
return {value, type};
}
export function isAlpha(src: string) {
return src.toUpperCase() != src.toLowerCase();
}
export function isSkippable(str: string) {
return str.trim().length === 0;
}
export function isInt(str: string) {
const c = str.charCodeAt(0);
const bounds = ["0".charCodeAt(0), "9".charCodeAt(0)];
return (c >= bounds[0] && c <= bounds[1]);
}
const SKIPPABLE_CHARS = new Set([' ', '\n', '\t']);
const BINARY_OPERATORS = new Set(['+', '-', '*', '/']);
export function tokenize(SourceCode: string): Token[] {
const tokens = new Array();
const src = SourceCode.split("");
while(src.length > 0) {
const char = src[0];
if (char == '(') {
tokens.push(token(char, TokenType.OpenParent));
}
else if (char == ')') {
tokens.push(token(char, TokenType.ClosedParent));
}
else if (BINARY_OPERATORS.has(char)) {
tokens.push(token(char, TokenType.BinaryOperator));
}
else if (char == '=') {
tokens.push(token(char, TokenType.Equals));
}
else {
if(isInt(char)) {
let num = "";
while(src.length > 0 && isInt(src[0])) {
num += src.shift();
}
tokens.push(token(num, TokenType.Number));
}
else if(isAlpha(char)) {
let ident = "";
while(src.length > 0 && isAlpha(src[0])) {
ident += src.shift();
}
const reserved = KEYWORDS[ident];
tokens.push(token(ident, reserved ?? TokenType.Identifier));
}
else if(SKIPPABLE_CHARS.has(char)) {
// Do nothing
}
else {
throw new Error(`Unexpected character: ${char}`);
}
}
}
return tokens;
}