PaddiM8 / kalker

Scientific calculator with math syntax that supports user-defined variables and functions, complex numbers, and estimation of derivatives and integrals
https://kalker.xyz
MIT License
1.64k stars 74 forks source link

Add max recursion depth check to avoid stack overflow during statement evaluation #122

Closed kvnxiao closed 1 year ago

kvnxiao commented 1 year ago

E.g.

f(x) = f(x) + 1

Before:

kalker
Type 'help' for instructions.
>> f(x) = f(x) + 1
>> f(1)

thread 'main' has overflowed its stack
fatal runtime error: stack overflow

After:

kalker
Type 'help' for instructions.
>> f(x) = f(x) + 1
>> f(1)
Operation recursed too deeply.
>> exit
kvnxiao commented 1 year ago

I'm currently using this as a bandaid solution for my use case. Let me know if you think there's a better way this can be structured and represented in the code.

PaddiM8 commented 1 year ago

Great! You'll need to decrement it as well though, since the stack won't overflow if you go in and out of recursions a lot. To make that work you'd probably need to increment right before function calls and decrement right after. Easiest would probably be to put that in eval_expr in the match arm for FnCall.

kvnxiao commented 1 year ago

Would it make sense to only increment / decrement the depth counter within this part of the FnCall code?

    match stmt_definition {
        Some(Stmt::FnDecl(_, arguments, fn_body)) => {
// ... <somewhere here in this body?>

Or did you mean something like:

// fn eval_expr(...)
    match expr {
        // ...
        Expr::FnCall(identifier, expressions) => {
            context.recursion_depth += 1;
            let res = eval_fn_call_expr(context, identifier, expressions, unit);
            context.recursion_depth -= 1;
            res
        }
kvnxiao commented 1 year ago

I updated the PR code, let me know if you think there's other things worth moving around

PaddiM8 commented 1 year ago

Looks good, thanks!