WIP: math parser

This commit is contained in:
2025-02-03 18:22:26 +01:00
parent b3ac5ee075
commit 857a252fea

View File

@@ -1,27 +1,133 @@
type Token = number | '+' | '-' | '*' | '/' | '(' | ')';
type TokenType = 'number' | 'operator' | 'parenthesis';
type TokenValue = number | '+' | '-' | '*' | '/' | '(' | ')';
type ASTNode = {
type: string,
type Token = {
type: TokenType,
value: TokenValue,
}
type OperatorNode = {
type OperatorChars = '+' | '-' | '*' | '/';
type OperatorToken = Token & {
type: 'operator',
value: '+' | '-' | '*' | '/',
left: ASTNode,
right: ASTNode,
value: OperatorChars,
}
type NumberNode = {
const isOperatorChar = (char: string): char is OperatorChars => {
return char === '+' || char === '-' || char === '*' || char === '/';
}
type NumberToken = Token & {
type: 'number',
value: number,
}
type GroupNode = {
type: 'group',
value: ASTNode,
type ParenthesisChars = '(' | ')';
type ParenthesisToken = Token & {
type: 'parenthesis',
value: ParenthesisChars,
}
const isParenthesisChar = (char: string): char is ParenthesisChars => {
return char === '(' || char === ')';
}
const tokenize = (expression: string): Token[] => {
const tokens: Token[] = [];
const chars = expression.split('');
let done = false;
let i = 0;
while (!done) {
if (i >= chars.length) {
done = true;
continue;
}
const char = chars[i];
if (char === ' ') {
i++;
continue;
}
// parenthesis
if (isParenthesisChar(char)) {
tokens.push({ type: 'parenthesis', value: char });
i++;
continue;
}
// operator
if (isOperatorChar(char)) {
tokens.push({ type: 'operator', value: char });
i++;
continue;
}
// number
if (char.match(/\d/)) {
let number = '';
while (chars[i].match(/\d/)) {
number += chars[i];
i++;
}
tokens.push({ type: 'number', value: parseInt(number) });
} else {
throw new Error('Invalid character');
}
}
return tokens;
}
abstract class ASTNodeCommon {
abstract readonly type: string;
abstract get value(): number;
}
type ASTNode = NumberNode | OperatorNode | GroupNode | NegationNode;
class NumberNode extends ASTNodeCommon {
private _value: number;
type = 'number';
constructor(public numValue: number) {
super();
this._value = numValue;
}
get value() {
return this._value;
}
}
class OperatorNode extends ASTNodeCommon {
type = 'operator';
constructor(public operator: OperatorChars, public left: ASTNode, public right: ASTNode) {
super();
}
get value() {
const left = this.left.value;
const right = this.right.value;
switch (this.operator) {
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
default:
throw new Error('Invalid operator');
}
}
}
class GroupNode extends ASTNodeCommon {
type = 'group';
constructor(public value: ASTNode) {
super();
}
}
type NegationNode = {