User-defined functions in Free42 - Printable Version +- HP Forums (https://www.hpmuseum.org/forum) +-- Forum: Not HP Calculators (/forum-7.html) +--- Forum: Not quite HP Calculators - but related (/forum-8.html) +--- Thread: User-defined functions in Free42 (/thread-16153.html) |
User-defined functions in Free42 - Thomas Okken - 01-10-2021 02:18 PM Hi all, In Free42 2.5.23, I introduced a few new functions that are meant to help create subroutines that behave like built-in functions. The idea is to make it easier to implement the proper stack behavior, and to allow subroutines to return error codes and to act as conditionals. The implementation of these functions in 2.5.23 is functional, but flawed. I wasn't happy about it and made several improvements, in the areas of error handling and RTN behavior. The descriptions on my web site cover the implementation in the official 2.5.23 release; the following documents the implementation in my current prototype: FUNC0, FUNC1, and FUNC2: These functions preserve the stack and LASTx, in preparation for restoring registers when the function returns. Upon RTN, a function initialized with FUNC0 will restore all four stack registers and LASTx to their original values; a function initialized with FUNC1 will leave X intact, restore the original X to LASTx, and restore Y, Z, and T to their original values; and a function initialized with FUNC2 will leave X intact, restore the original X to LASTx, and restore T to T and Z, and restore Z to Y. Thus, in order to implement a binary operator, all that's needed is to call FUNC2 at the beginning of the function, and make sure the result is in X when it returns. The stack restoration happens automagically upon RTN. The other additions are RTN variations: RTNYES and RTNNO allow a function to behave like a conditional. In terms of the flow of control, RTNYES acts just like RTN, while RTNNO returns to the line after the one RTN would return to, skipping the line right after the calling XEQ. The reason RTNYES is a separate function is because of its behavior when the function was XEQ'd from the keyboard: in that case, RTNYES will display the message "Yes" ans RTNNO will display the message "No". RTNERR allows a function to raise an error message. The error message is selected using a number in X, as follows: 0: No Error 1: Alpha Data Is Invalid 2: Out of Range 3: Divide by 0 4: Invalid Type 5: Invalid Data 6: Nonexistent 7: Dimension Error If the function was called from a program, the error message will be displayed and execution will halt on the calling XEQ; if the function was called from the keyboard or from SOLVE or INTEG, execution will halt on the RTNERR (except for the errors trapped by SOLVE, as usual). Note that when a function terminates with RTNERR, the stack contents saved by FUNC0/1/2 will be restored to their original state, i.e. all four stack registers and LASTx restored, regardless of which of the three functions was used. To help implement robust error handling, flag 25 is saved (and cleared) by FUNC0/1/2, and it is restored upon RTN. So, a caller can use flag 25 to catch errors raised by RTNERR, while functions can use, or not use, flag 25 internally as they see fit, and these usages will not interfere with each other. These changes will be in the next release, but I've put a test build up at https://thomasokken.com/free42/download/test/Free42Windows.zip for testing. Note that this introduces a new state file version, so be sure to save your existing state before using this if you want to be able to go back to using the previous version after looking at this one. I hope this is useful. Please feel free to share your thoughts here! Thomas RE: User-defined functions in Free42 - Werner - 01-10-2021 08:29 PM Hi Thomas, First thought: why not use XEQ0 etc. to save the stack to do XEQ and stack save at the same time? Werner RE: User-defined functions in Free42 - Thomas Okken - 01-10-2021 09:14 PM I don't like a special flavor of XEQ because I don't want the caller to have to be aware of the function's implementation details. A special kind of LBL would not have that issue, but putting the stack-saving functionality in a separate function made the whole thing a lot easier to implement. RE: User-defined functions in Free42 - Albert Chan - 01-10-2021 11:29 PM It would be nice if restoring stack also has its own function, say RESTORE RTN (or END) had the side-effect of quitting the program ... Code: FUNC1 ; a b c d Without an explicit restore stack function, we would need to do this. Code: XEQ 00 RE: User-defined functions in Free42 - Paul Dale - 01-10-2021 11:47 PM RESTORE seems kind of funny. Is there a practical use case for it? So long as FUNCn nests properly, I'm not sure why. Two things the 34S's xIN command allows that aren't supported by this proposal:
A thought: could the FUNC command take an argument instead of having the behaviour encoded into the command name? Pauli RE: User-defined functions in Free42 - rprosperi - 01-11-2021 01:15 AM (01-10-2021 02:18 PM)Thomas Okken Wrote: [snip] Thanks for these enhancements Thomas, well thought-out as usual. Regarding the features themselves, I agree with one of Pauli's comments, why not include FUNC3 and FUNC4, for (not common, but still happen) situations needing more arguments? It's just a matter of time before someone needs them, so why not add them at the start of all the testing, trying to break them, etc.? For multiple output arguments, a general case seems much harder to anticipate... Regarding state files, do you mean there is a new state file format, or merely that the new s/w version will write non-backward compatible state files, which is understandable? RE: User-defined functions in Free42 - Paul Dale - 01-11-2021 01:27 AM (01-11-2021 01:15 AM)rprosperi Wrote: For multiple output arguments, a general case seems much harder to anticipate... There are a reasonable number of existing functions that produce two outputs (statistics has several, plus some others). For three or four I think it is harder, although quadratic regression would benefit from this. Pauli RE: User-defined functions in Free42 - Thomas Okken - 01-11-2021 04:47 AM (01-11-2021 01:15 AM)rprosperi Wrote: Regarding the features themselves, I agree with one of Pauli's comments, why not include FUNC3 and FUNC4, for (not common, but still happen) situations needing more arguments? It's just a matter of time before someone needs them, so why not add them at the start of all the testing, trying to break them, etc.? Yes, I like that suggestion. The number of inputs and outputs could be parameterized pretty easily. Since neither can be greater than four, all possibilities could be expressed with a two-digit number, so the syntax could be, say, FUNC 31 for 3 inputs, 1 output. I see no reason not to allow all 25 possibilities. (01-11-2021 01:15 AM)rprosperi Wrote: Regarding state files, do you mean there is a new state file format, or merely that the new s/w version will write non-backward compatible state files, which is understandable? The latter. If you run the new version, and then run the previous version, you will get the message State File Too New. (See: FREE42_VERSION in free42/common/core_globals.cc) This happens every time some new piece of information needs to be added to the state file. All versions of Free42 can read previous versions' state files, but forward compatibility breaks whenever there is a format change like this. For this latest change, the internal version is going from 30 to 31, so this is not a rare event, and I don't usually mention it when it happens, but in case of a test build like this, I think a warning is required. RE: User-defined functions in Free42 - Werner - 01-11-2021 07:40 AM Thought 2: why should RTNYES and RTNNO display YES and NO, respectively, when executed from the keyboard? They are not conditionals themselves. (you'll probably give a reasonable explanation for this one, too ;-) Werner RE: User-defined functions in Free42 - Thomas Okken - 01-11-2021 07:50 AM I think I explained that badly. You can't actually execute RTNYES and RTNNO from the keyboard, you'll get Restricted Operation if you try. What I meant was: when you execute a user-defined function ending in RTNYES or RTNNO from the keyboard. RE: User-defined functions in Free42 - Thomas Okken - 01-11-2021 07:11 PM Philosophical question: in a function with 4 inputs and fewer than 4 outputs, what should be placed in the top registers of the stack? Fill with zeroes, or with the previous value of T, even though T has been consumed as a parameter? RE: User-defined functions in Free42 - Werner - 01-11-2021 07:42 PM I would say the previous T: L-XYZT -> X-RTTT Werner RE: User-defined functions in Free42 - rprosperi - 01-12-2021 02:45 AM After some thought, I agree with Werner, though your very straightforward question did catch me off-guard. I had never contemplated such a situation, which may be one of the first 'how should the stack behave?' questions that has never come up until now. I suppose one could argue that zero is as justifiable as T, but it just seems it's better to preserve some value, than none, and an RPN user always expects T to be there. I look forward to hearing arguments for why these should be zeros. RE: User-defined functions in Free42 - Thomas Okken - 01-12-2021 03:04 AM OK, a new test build, with fully parameterized FUNC, is now available at the same link as before: https://thomasokken.com/free42/download/test/Free42Windows.zip RE: User-defined functions in Free42 - Thomas Okken - 01-12-2021 05:52 PM It's not the most efficient style of programming, but recursive functions can be made very readable this way: Code: 00 { 24-Byte Prgm } RE: User-defined functions in Free42 - Sylvain Cote - 01-12-2021 07:18 PM (01-12-2021 05:52 PM)Thomas Okken Wrote: It's not the most efficient style of programming, but recursive functions can be made very readable this way:I was going to answer that with a 8 levels return stack, recursivity was very limited, but then I remembered that Free42 has a 1024 levels return stack, so recursivity can be reasonably used. RE: User-defined functions in Free42 - Albert Chan - 01-12-2021 08:01 PM (01-11-2021 04:47 AM)Thomas Okken Wrote: The number of inputs and outputs could be parameterized pretty easily. Since neither can be greater than four, all possibilities could be expressed with a two-digit number, so the syntax could be, say, FUNC 31 for 3 inputs, 1 output. I see no reason not to allow all 25 possibilities. Why do we need to specify number of inputs ? I thought the goal is to return original stack, except for the outputs. Since the stack is tiny, is it better to save them all ? Then, we specify number of outputs to return. Program may have branch that return 2 outputs, another branch just 1. Example, say LBLS = Label, but save all stacks LBLS "TEST" + * ÷ RTN1 ; only 1 return, restore the rest. RE: User-defined functions in Free42 - Thomas Okken - 01-12-2021 08:24 PM In order to know which stack registers to restore, and what to restore them to, you need to know the number of outputs and inputs. For example: FUNC 11, one parameter and one output, returns RYZT, while FUNC 21, two parameters and one output, returns RZTT. Etc. FUNC does save the entire stack, it has to do that anyway because RTNERR must restore the entire stack and LASTx to their original state. But when the function ends with RTN, RTNYES, or RTNNO, it restores the stack accoring to the parameter originally passed to FUNC. RE: User-defined functions in Free42 - Werner - 01-12-2021 08:28 PM If you have 2 functions, each returning 1 result R. Function 1 has only one argument, then the stack returned is L-XYZT -> X-RYZT (eg SQRT) Function 2 has two arguments, then : L-XYZT -> X-RZTT (eg +) So you have to know both number of inputs and outputs. Werner RE: User-defined functions in Free42 - Thomas Okken - 01-13-2021 03:53 AM I just uploaded a new test build (same URL as before): fixed a crash in RTN where under certain circumstances it would try to restore saved stack state that didn't exist. |