Sunday, November 16, 2014

Token Errors (Minor Refactoring)

The plan is to modify the translator routines to throw error exceptions when an error is detected.  Errors consist of a status, column and length.  For translator errors, the column and length will always be obtained from a token, so it made sense to add a constructor that takes status and token pointer arguments.

The Error structure was a plain structure with no constructors, so a default constructor is generated by the compiler taking arguments for the three member variables.  Once a constructor is added, this default constructor is no longer generated, so one was added.  The constructor taking status and token pointer arguments was also added.  Since the structure now has constructors, the member variables were renamed with the member "m_" prefix.

The Error structure was defined in the main header file.  This header file does not have access to the token header so that the new constructor can retrieve the column and length from the token, and an include couldn't be added for the token header because the token header already includes this main header file.  The Error structure was therefore moved to the token header file.  The name Error was a little generic so this structure was renamed to the more appropriate Token Error.

[branch misc-cpp-stl commit 97ce3592c3]

Translator – Exceptions (Top-Level)

The goal of the next step is to modify the translator to throw errors instead of returning an RPN list containing an error.  This will be done in steps starting at the top-level of the translator in the function operator function with a couple of other minor improvements to the code.

The first minor improvement was made to the Error Item class that holds information about an error in the program (includes type, line number, column, length and error status).  The is empty function that checks if the error type is none (no error) was replaced with the explicit operator bool function, which allows an error item to be checked for an error without a named function (this function is called when a boolean is expected and an error item instance is provided):
if (!errorItem.isEmpty())       →       if (errorItem)
At the end of the translator function operator function, if the status is not Done, the RPN list was cleared and its error member variables were set (column, length and status).  This was changed to throw an Error structure with the status, token column and token length.  The RPN list no longer needs to be cleared since it will be cleared when the temporary translator instance goes out of scope.

The tester translate input routine was modified to catch an error in a try block.  This routine no longer needs to check if RPN list has an error, and if it did, retrieve the error information to create a local error instance and print the error.  Now upon a caught error, it simply prints the error.  It also returns an empty RPN list that the caller uses to detect an error.

The program model update line routine was modified to catch an error in a try block.  For an error, the local error item is set to an error with the info in the thrown error.  If there is no error then the error item will not contain an error (the default constructor sets the error type to none).

To detect a change, the update line routine compared the new RPN list to the current RPN list decoded from the program.  The lists were not equal if either contained an error.  The current RPN list is empty for a line with an error, but the decode routine does not set the error variables.  Now that the RPN list does not have an error status, so the error item also needs to be checked when checking if the line changed.  The RPN error variables are no longer used and were removed along with their access functions.  The check for errors in the RPN list equality operator function was also removed.

The final improvement made was to changed the RPN list argument of the encode routine from a constant reference to an rvalue reference.  All calls to the encode routine no longer needs the RPN list after the call, so it can be moved.  Since the arguments of these calls were not a temporary instance, the local RPN list variable needs to moved using std::move.

[branch misc-cpp-stl commit 52359711e7]

Translator – Function Operator

The Translator class has a single purpose, to task a BASIC input string and create an RPN list representation of the BASIC code.  This is similar to the Parser, Tester, and Recreator classes, which were already changed to be function operator classes.  This started with renaming the translate function to operator().

Like the recreator where a temporary instance is used, the translator can be used in the same way except that the input string is passed to the constructor so that it can be used to instance the parser (which lives throughout the translation).  The parser no longer needs to be instanced at the beginning and reset before returning in the function operator function.  Since only a temporary instance is needed to translate, the translator member pointers in the tester and program model classes were removed.

The clean up function was called when the translator returned an error, which deleted the hold and done stack items, cleared the RPN output list and reset the pending parentheses token pointer.  Now at the end of a translation, including when an error is detected, the temporary translator instance goes out of scope, all all these actions occur automatically except for the clearing of the output list (since it is moved to the caller upon return).  This clean up function was removed.  For an error, the output list is cleared.

[branch misc-cpp-stl commit b597b5b0a7]