I have created a calculator using RDP (recursive descent parser) to parse and evaluate mathematical expressions "such as: 5cos(30) -5(3+5)". The thing is I also have tried to include commands like "config" which lets the user configure the angle units (Gra, Rad, Deg). And here I found a problem with the design of the code.
Simplified example of the call stack:
int main()
{
parseexpr()
{
parseterm()
{
parsefactor()
{
parsecommand() or parsefct() or parsenumber();
}
}
}
}
Now when the user inputs the "config" command. I would want to let him configure the calculator (the configuration is inside a .txt file) and just jump back to "main()" (where he would be asked to enter expression again) since it would be problematic to return through the call stack, as I would need a return value, which "parsecommand()" cannot return.
My question is how can I clean the call stack such that I would return to main directly through a "goto" statement ?
Don't do that. That's actually a so-called "XY question".
The correct way to go about it is this:
Place all these functions in a module of their own, lets call it
parse.h / parse.c.In
parse.hcome up with a result/error type to be used by all applicable functions in this module. Something like:Applicable functions are then defined as:
Where any values that need to be returned are passed as pointer parameters.
In a nested number of function calls, each function will check the result and if not OK, return an error code to the caller. Often it is considered good practice to not modify the value parameters upon error.
The incorrect, very bad practice alternative is to utilize
setjmp/longjmpwhich are low level functions actually storing and restoring the callstack and other similar variables. So it's theoretically possible to do in standard C, but these functions are bad practice because:jmp_bufglobal variable being missing, which I'm then somehow supposed to implement in main(). Or worse yet: in order to linkparse.cI must also linkmain.c. Such tight coupling dependencies are neither portable nor maintainable, it's bad in almost every imaginable way.setjmp/longjmpcome with a long list of hazards and potential undefined behavior.