Callem Pittard Callem Pittard - 3 months ago 12
TypeScript Question

Math Parser/Lexer - Token interface design

Currently working on a small pet project, a Math Parser/Lexer and eventually solver for fun/learning. I've bashed out a basic prototype and am now looking to convert this into TypeScript to clean things up. Prototype can be found here https://github.com/chips5k/math-solver-prototype for those interested.

I am trying to come up with a clean interface based approach to dealing with my Tokens. I suspect i am looking at this the wrong way, but hopefully someone can offer useful advice.

In my TS design i have several interfaces, the base interface being Token, with NumericToken and FunctionToken extending these. I then have several classes that implement these interfaces such as: AdditionToken, VariableToken, NumberToken, PiToken, SineToken etc...

My problem is that in order to work with these interfaces i end up requiring methods to check the basic type e.g isNumeric, isFunction, isVariable or alternatively a direct type === TokenFactory.NUMERIC etc... This, to me, feels wrong as it basically voids the point of using an interface. I suspect that there is a nicer/cleaner more polymorphic approach i could take but unfortunately i'm out of ideas and have been unable to find info on what i am doing.

An example of where things fall apart shows itself when attempting to solve a series of tokens:

if(t.isFunction()) {
var a = stack.unshift();
var b = stack.unshift();
if(a.isNumeric() && b.isNumeric()){
result.push(tokenFactory.create(t.evaluate<number>(a.evaluate<number>, b.evaluate<number>));
} else {
//return to stack and move on, e.g can't solve x + 1 directly
}
} else {
stack.push(t);
}


Basically looking for what is considered the ideal approach for handling a scenario like this, and i suspect it may be an alternate approach to the design.

TIA!

Answer

basic type e.g isNumeric, isFunction, isVariable or alternatively a direct type === TokenFactory.NUMERIC etc... This, to me, feels wrong

Nope. This is fairly idiomatic as the type controls what functionality is there.

E.g you will see the TypeScript checker.ts littered with check on .kind (SyntaxKind) which is at TypeScript AST nodes discriminator.

Also you might want to consider adding a visitor that is recursive e.g.

function visit(item){

if (item.addition) {
  return visit(item.left) + visit(item.right)
}

if (item.literal) {
  return literal.value();
}

// ETC.
}