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:
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:
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:
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:
interface MathFlowError {
name: "MathFlowError";
type: "LexicalError" | "SyntaxError" | "RuntimeError";
message: string;
suggestion: string | null;
toString(): string;
}Error Properties
name: Always 'MathFlowError' for easy type checkingtype: Specific error categorymessage: Detailed error descriptionsuggestion: Optional hint for fixing the errortoString(): 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;
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).
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:
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:
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:
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);
}