Tuesday, September 9, 2014

Translator – Done Stack Realignment

The done stack of the translator is used for temporarily holding RPN items appended to the RPN output list to be consumed as operands by operators, functions and commands.  This will be the next stack changed to std::stack.  But first some realignment was required to move functionality from the Done Stack class to the Done Item structure.  Like the Hold Stack, the Done Stack also has QStack as its base class with functionality added.  Unlike the Hold Stack class, this added functionality did more than just add stack convenience functions.

Each item on the done stack contains a shared pointer to the RPN item plus token pointers to the first and last token of the expression of the RPN item, which may contain open and closing parentheses tokens that will eventually be dropped (in most cases).  These are for reporting errors to an entire expression including open and closing parentheses.  The parentheses token are deleted when they are no longer needed.  A closing parentheses is not deleted for extra parentheses that need recreated.

The non-stack functionality added was for handling these first and last tokens.   The added pop function only returned the RPN item of the done item popped from the stack, but also deleted the first and last tokens if they contained parentheses (checking if unused for the last token).  Similarly, the drop function also deleted the first and last tokens if parentheses.  These called functions in Done Item structure to do the check and delete of the tokens.  The final non-stack function replaced the first and last tokens of the done item on top of the stack.

This non-stack functionality was moved to the Done Item structure.  The replace top first last function of the done stack called the replace first and replace last functions of the done item (the only caller to these functions).  Each replace function deleted the token being replaced if a parentheses token.  These functions were combined into a single replace first last function, which is now called directly for the top item on the stack and the done stack function was removed.

The added pop and drop functions both deleted the first and last tokens if parentheses.  These functions also decreased the size of the stack (by a stack pop or resize down one element).  This would cause the destructor of the done item to be called.  No destructor was declared, but since the RPN item was changed to a shared pointer, the compiler generated a default destructor to call the destructor of the shared pointer.  The last and first token pointers were plain old data, so they were not affected.  The delete parentheses calls were removed from the pop and drop functions, and added to a new done item destructor (the RPN item destructor is still handled automatically).  However, this caused a curious problem in pop function (a multiple deleted closing parentheses token), which now contained the single statement:
return QStack::pop().rpnItem;
The pop() returns a copy of the done item on top of the stack.  The item on top of the stack is then removed, which calls the new done item destructor for the item (deleting a closing parentheses in the last token pointer).  The copy is used to get the RPN item, and then the copy goes out of scope, which calls the done item destructor.  Since the copy has the same last parentheses token as the first, it gets erroneously deleted twice.  This was happening previously, but since the [default] destructor was not affecting the first and last tokens, no double delete occurred.  This was corrected by replacing the above statement with:
RpnItemPtr rpnItem = top().rpnItem;
drop();
return rpnItem;
Now a temporary of only the RPN item is made.  The drop() call resizes the stack down one item, causing the new destructor to be called, deleting the last token holding an unused closing parentheses.  The RPN item is then returned.  This also matches the operations that will be required for std::stack.  These specialized delete parentheses token functions won't be needed once shared pointers are used for token pointers.

[branch cpp11 commit c23603ea2e]