Skip to content

Error Handling ​

MathFlow provides comprehensive error handling to help you debug and handle issues in mathematical expressions.

Error Types ​

MathFlow has three main types of errors:

LexicalError ​

Occurs during the tokenization phase when the input string contains invalid characters or tokens.

Common causes:

  • Invalid characters in expressions
  • Malformed numbers
  • Unknown operators

Example:

ts
try {
	ctx.solve("2 @ 3"); // Invalid operator @
} catch (error) {
	if (error.type === "LexicalError") {
		console.log(error.message);
		// Invalid character '@' at position 2
	}
}

SyntaxError ​

Occurs during parsing when the expression structure is invalid.

Common causes:

  • Mismatched parentheses
  • Invalid function calls
  • Missing operators
  • Incorrect expression structure

Example:

ts
try {
	ctx.solve("2 + * 3"); // Invalid syntax: consecutive operators
} catch (error) {
	if (error.type === "SyntaxError") {
		console.log(error.message);
		// Unexpected operator '*' after operator '+'
	}
}

RuntimeError ​

Occurs during evaluation when mathematical operations fail.

Common causes:

  • Division by zero
  • Invalid function arguments
  • Undefined variables
  • Numeric overflow

Example:

ts
try {
	ctx.solve("1/0"); // Division by zero
} catch (error) {
	if (error.type === "RuntimeError") {
		console.log(error.message);
		// Division by zero
	}
}

Error Structure ​

All errors in MathFlow follow a consistent structure:

ts
interface MathFlowError {
	name: "MathFlowError";
	type: "LexicalError" | "SyntaxError" | "RuntimeError";
	message: string;
	suggestion: string | null;
	toString(): string;
}

Error Properties ​

  • name: Always 'MathFlowError' for easy type checking
  • type: Specific error category
  • message: Detailed error description
  • suggestion: Optional hint for fixing the error
  • toString(): Formatted error message with suggestion if available

Safe Functions ​

By default, errors are thrown at any stage of the evaluation. Below is an example of good error handling where MathFlow operations are wrapped in try-catch blocks;

ts
try {
	const result = ctx.solve(userInput);
	displayResult(result);
} catch (error) {
	if (error.name === "MathFlowError") {
		handleMathFlowError(error);
	} else {
		throw error; // Re-throw unexpected errors
	}
}

A similar paradigm is used for safe functions listed below. No errors are thrown. The result of the operation is an object with either a data key - (result of the operation) or error key (error thrown during the operation).

ts
export type ISafeResult<T = unknown> =
	| {
			data: T;
			error: undefined;
	  }
	| {
			data: undefined;
			error: IError;
	  };

declare function safeTokenize(ctx: IContext, code: string): ISafeResult;

declare function safeParse(tokens: IToken[]): ISafeResult;

declare function safeEvaluate(
	ctx: IContext,
	node: INode,
	solution: ISolution
): ISafeResult;

declare function safeSolve(ctx: IContext, code: string): ISafeResult;

declare function safeSolveBatch(ctx: IContext, code: string): ISafeResult;

Best Practices ​

1. Error Type Checking ​

Check error types to provide appropriate feedback:

ts
function handleMathFlowError(error) {
	switch (error.type) {
		case "LexicalError":
			// Handle invalid characters/tokens
			break;
		case "SyntaxError":
			// Handle invalid expression structure
			break;
		case "RuntimeError":
			// Handle evaluation errors
			break;
	}
}

2. User Feedback ​

Use error suggestions to help users fix issues:

ts
function displayError(error) {
	const message = error.toString();
	// "SyntaxError: unexpected token '9' at 3:5 - expecting ')'"
	showErrorToUser(message);
}

3. Variable Validation ​

Check variables before evaluation to prevent runtime errors:

ts
const ctx = createContext();

// Validate variables before solving
function safeEvaluate(expr, variables) {
	for (const [name, value] of Object.entries(variables)) {
		if (typeof value !== "number" || !isFinite(value)) {
			throw createError(
				"RuntimeError",
				`Invalid value for variable '${name}'`,
				"Use finite numeric values only"
			);
		}
	}

	ctx.variables.clear();

	Object.entries(variables).forEach(([k, v]) => ctx.variables.set(k, v));

	return ctx.solve(expr);
}