Saturday, November 30, 2013

Information Dictionary – New Design

The original design of the information dictionary made the assumption that the additional information would be contained in a vector and was given a structure for the information.  The definition was a class template where the information structure was the argument, which was put into the vector, which the information dictionary handled directly.  However, in the case of the constant number dictionary, two vectors are needed so that memory is not wasted (see last post).

The details of the additional information need to be separated from the information dictionary.  In other words, the information dictionary should not know (or assume) that the additional information is a vector.  An abstract information class can be used that defines the interface to the additional information.  The interface requires several functions for accessing the information:
add element - add a new element to the end of the additional information

set element - set an element from information in a token used when a new element is added or an element previously deleted is reused

clear element - clear the contents of an element when the dictionary entry is removed (made available for reuse)
The actual information classes are derived from the abstract class and implement these functions to manipulate their information as required, which could be stored as a vector, two vectors, or something completely different.  The information dictionary has no knowledge of the information class internals and simply uses the interface functions.

The information dictionary class can be a normal class derived from the dictionary class containing a reference to the additional information.  The abstract information interface functions are used to manipulate the additional information.  The information dictionary needs re-implement these functions from the base dictionary class:
add - adds a new dictionary entry and additional information if not already in the dictionary and returns its index

remove - removes the additional information if the dictionary entry was removed

Friday, November 29, 2013

Information Dictionary Issues

The information dictionary class extended the base dictionary class by adding a vector for additional information and was implemented as a class template (see post from October 6).  The additional information in the constant string dictionary contained a pointer to a string instance (see post from October 6).

The memory leak in the constant string dictionary was caused by how the information dictionary template and constant string information classes were implemented.  The problem occurred when a string in an entry of the information vector was replaced with the same string.  A new information instance was created with a new string pointer, which was put into the information vector, and the old string instance was lost (a memory leak).

When an old program line was dereferenced after the new replacement line was encoded, a string being replaced by the same string had its reference incremented in the dictionary from one to two by the encode, then the dereference decremented the count back to one.  However, when the dereference was moved to after the encode, the reference count of the string went from one to zero and the dictionary entry was freed, but not the entry in the information vector.  When the new line was encoded, a new string instance was created overwriting the old string instance pointer.

While this problem was not difficult to correct, another issue was discovered, this time with the constant number dictionary where its additional information consisted of a double value and an integer value contained in a structure (see post from October 6).  Each double value was aligned on a double boundary (eight bytes) and because an integer is half of a double (four bytes), four bytes of padding is inserted by the compiler between each element in the vector (wasted memory).

The only way to correct this is to separate the two sets of values by having a double value vector and an integer value vector.  Unfortunately, the information dictionary template class only allows for a single information vector.  A new design is needed for information dictionaries.

Program – Dereferencing Replaced Lines

When a line is replaced, references to dictionary entries in the old line must be removed.  This was taking place after the replacement line was encoded.  When a dictionary entry is dereferenced, the reference may no longer be used causing the dictionary entry to be made available for another entry.  The new line may add new dictionary entries, but with the encode before the dereferencing, the new entry will be added to the end of the dictionary if there are no free slots.

It is desirable for new dictionary entries to use slots that may be freed with the old line being replaced.  This will help the dictionary from growing larger then it needs to be.  Therefore, the dereference call was moved to before the encode call.  With this change, the results for encoder test #2 changed slightly, but only with respect to indexes of a couple of dictionary entries.

A previously undiscovered memory error was reported on encoder test #2 when running the memory test script.  The problem occurred in the constant string dictionary with the allocation of the string pointers for the QString instances.  While investigating this problem, another issue was discovered in the constant number dictionary, though this issue is much less serious and only results in wasted memory.  The conclusion was that the information dictionary class (currently defined as a template) needs to be redesigned.

[commit f284a33ac8]

Tuesday, November 26, 2013

Program – Saved RPN Lists (Removal)

The translated RPN lists for program lines were being saved in the line information list, which also contains the offset of the line within the program code, the size of the line and the index to the error list if the line has an error.  These lists were originally used as the source of the data for the program view and were also used to detect when lines changed (by comparing the translated RPN list of the new line with the saved RPN list).

The use of the RPN lists for the program view was removed when the program code array was implemented.  With the decode routine, the program code of the lines can now be converted to an RPN list.  The line change detection in the update line routine was modified to decode the code of the line for the new line being changed to an RPN list, which is compared to the translated RPN list of the new line.  With the RPN lists in the line information list no longer being used, this variable was removed along with all references to it.

A problem was found when RPN lists were compared.  The strings of tokens other than REM commands, REM operators and string constants (whose strings comparison must be case sensitive) should use a case insensitive string comparison.  However, the case sensitivity argument was not supplied to the compare call, so the default comparison used was case sensitive.  The correct argument was added.

[commit dd3e4b62ed]

Sunday, November 24, 2013

Program – Decoder

Before the internal code of a program line can be recreated, the program code needs to be decoded into an RPN list.  Like the encoder, which is part of the program model class because it needs access to the dictionaries, the decoder will also be part of the program model.

The decode routine is given the line information of the line to decode containing the offset of the line within the program code and its size.  A new RPN list is created and for each program word in the line, a new token is created and assigned the code and sub-code of the program word.  If the code has an operand text function in its table entry (implying the code has an operand word), the operand text function is called to get the text for the token from the operand, which is assigned to the string of the token.  The token is added to the RPN list.  After all the words of the line are processed, a pointer to the RPN list is returned.

Like the encode routine, the decode routine is a private function within the program model class.  To access recreated lines of the program, a new line text routine was added.  This routine is given the index to the line and starts be retrieving the information for the line, which is passed to the decode routine.  The pointer to the RPN list returned is passed to the recreate routine of the recreator instance (which was added to the program model class).  The RPN list is deleted and the string returned from the recreate routine is returned.

The temporary check to prevent encoder test files from being used with the recreate output option (-to) was removed from the tester class.  In the tester run routine for encoder test files after outputting the code of the program and the dictionary entries, if the recreate output option was selected, each line of the program is output using the line text routine.

The expected results files for the three encoder tests were created from the encoder test results files with the output of the program added to the end.  All the encoder tests are recreated correctly.  The test script and batch files were updated to also test the encoder test files with the recreate output option.

[commit 1f24a70152]

Saturday, November 23, 2013

Recreator – RPN Lists (Tagged)

The recreator is now complete and all of the initial set of commands (LET, PRINT, INPUT, and REM) are fully supported.  The repository has been tagged v0.6.1 to mark this milestone.  Some minor issues were also corrected for this commit including:
  • Corrected an expected results for the recreated translator test #12 (INPUT tests).
  • Reorganized the access functions in the recreator class.
  • Renamed the recreator class is empty access function to output is empty so as not to be confused with the stack is empty function.
  • Renamed the recreator class last access function to the more explicit output last character and removed the output string is empty check.
  • Corrected some comment formatting and added some missing comments.
  • Corrected formatting issues where spaces were incorrectly added instead of tab characters (problems are caused by QtCreator editor bugs where is doesn't always pay attention to the spacing/tab settings).
  • Added some missing FLAG option comments (details below).
The next major step is to integrate the recreator with the GUI, but in order to do this, the program code of lines needs to be decoded into an RPN list of tokens for the recreator.  When a line is entered into the program, it will be recreated and the recreated text will replaced the entered text in the edit box.  Preferences will be added to control the formatting of the recreated output, for example, if spaces should be added around operators, after commas, etc.  The FLAG option comments show where checks for these options will be.

[commit 0cd7b84700]

Recreator – Colons

Colons between statements are handled by setting the colon sub-code of the last token of a statement.  To support the recreation of colons, a check was added to the main recreator loop after the check for the parentheses sub-code that if the colon sub-code is set, a colon and a space is added to the output string.

This change caused a slight problem with the recreation of the remark operator if there is a colon just before the remark.  Two spaces were being added in front of the "'" operator.  To prevent this, a check was added after a non-empty output string check to also not add a space if the last character in the output string is already a space.

A new last access function was added to the recreator class that returns the last character in the output string or a null character if the output string is empty.  The expected recreated outputs for translator test  #16 (colon tests) were updated and are recreated correctly.

[commit fc90cf0e32]

Recreator – Remarks

There are two codes for remarks, the command code (for the REM command) and the operator code (for the "'" operator).  The token contains the string of the remark.  Generally, the keyword (REM or "'") along with the remark string is added to the output string.  However, there were two issues to be handled.

Unlike all the other commands, no space is required after the REM command when followed by a letter, so a statement like "REMARK A Comment" is valid.  The issue is for a REM statement entered as "remark a comment" in all lower case.  When recreated, the result would have been the "REMark a comment" statement.  So, if the first character of the remark string is lower case, the REM keyword is converted to lower case.  This still won't work if something like "Remark A Comment" is entered.

The second issue involves the remark operator.  A space is needed before the "'" operator if the command is not at the beginning of the line to provide some separation between the previous statement.  To determine if the remark is at the beginning of the line, an is empty access function was added to the recreator class  that returns whether the output string is empty (which implies this the beginning of the line if nothing was added yet).

A single rem recreate function was added to handle both remark codes and a pointer to this function was added to the remark code table entries.  To test the lower case check, an all lower case remark statement was added to translator test #15 (REM tests).  The expected recreated outputs for this test were updated and are recreated correctly.

[commit 03c73c3de7]

Friday, November 22, 2013

Recreator – INPUT Statements

Like PRINT statements, INPUT statements contain several codes including the input parse item (double, integer or string), input begin, input begin with string prompt, input assign reference (double, integer or string), and the input or input prompt command code.  The input begin with string prompt and input command codes could also have the option sub-code set.  The separator of the recreator class will be used between these codes to keep track of the separators between the input reference items.

As the codes of the INPUT statement are processed, the resulting INPUT statement is built up by adding to the string on top of the holding stack.  The string of the current reference is added to the string being built with a separator in between.  After each reference or prompt string, the separator is set to a comma (or a semicolon after the prompt string without the option sub-code).  At the end of the statement, the INPUT or INPUT PROMPT keyword is added to the output string along with the built up string that is popped from the stack.

Implementation

The input begin string code follows the input prompt string expression, the string of which will be on top of the stack.  The input prompt begin recreate function for the input begin string code sets the separator to a comma if the option sub-code is set, otherwise sets it to a semicolon.

The input assign recreate function contains a local string.  If the separator is set, the string is set to the string of the reference that is popped from the stack.  The separator is added to the string on top of the stack followed by the string of the reference.  The separator is not set for the first reference of an INPUT statement, so no action is needed (the string of the reference is left on the stack).  The separator is set to a comma for the next reference.

The input recreate function for both the INPUT and INPUT PROMPT code adds the command keyword to the output string.  A space is added followed by the built up string that is popped from the stack.  If the command code has the option sub-code set (for keeping cursor on the same line), a semicolon is added to the output string.  The separator is cleared for the next statement.

The table class name access function with token pointer argument was modified to return the full name of the code.  This includes a space and the second word of a code that has the two word option set along with a second name.  This was needed for the INPUT PROMPT command that contains two words.

Pointers to the new input recreate functions were added to the table entries of the various input codes.  The input parse item and input begin codes do not produce anything during recreation and their table entries were set to the pointer of the blank recreate function.  The expected recreated outputs for translator test #12 (INPUT statements) were updated and are recreated correctly.

[commit 9c8631000f]

Saturday, November 16, 2013

Recreator – Unary Operator Problems

Many of the other translator tests were being recreated correctly including tests #7 (errors), #8 (more errors), #9 (semicolon errors), #10 (expression errors), #11 (temporary errors), and #14 (parser errors) once the expected results were updated as these did not have the not yet implemented INPUT and REM statements or colons.  However, tests #13 (negative constants) and #17 (constants) were not recreated correctly due to problems involving unary operators.

A problem occurred when a negate unary operator preceded a numeric constant.  When created, there was no space between the negate operator and the number.  If this statement is translated again, the negate operator and the number become a negative constant, which is not the same, through technically equivalent.  This will cause the line change detection in the program model to incorrectly detect a change.  The unary operator recreate function was modified to also add a space after the unary operator if the operand begins with a digit or decimal point.

A problem occurred with the negate integer operator.  The precedence of the negate integer was incorrectly set to 40 causing parentheses to be added incorrectly during recreation.  The precedence should have been 48, the same as the negate double operator, so the table entry for was corrected.

A problem occurred when a unary operator followed a power (exponential) operator, a higher precedence operator.  The operand was incorrectly surrounded with parentheses.  This binary operator recreate function was modified to also check if the second operand is a unary operator then the operand is not surrounded by parentheses.

These corrections allowed tests #13 and #17 to be recreated correctly.  The regression test script was also modified to not ignore white space when comparing to the expected results.  Without this change, the first problem above was not detected.  The memory test was already not ignoring white space.  I'm not sure what the reason was for making the regression test ignore white space.

[commit cf67d09f36]

Recreator – PRINT Statements

There are several codes that make up a PRINT statement including print item (double, integer or string), comma, print function (TAB and SPC), semicolon (only at the end of the statement) and the print command.  A recreate function was implemented for each of these codes.  Because the PRINT statement is composed of several codes, the separator member variable of the recreator instance is used between the processing of these codes to keep track of separators between the print items.

As the codes of the PRINT statement are processed, the resulting PRINT statement is built up by adding to the string on top of the holding stack.  Generally, the string for the current item is popped from the stack, a separator is added to the string on top of the stack, which contains previous items and the string of the current item is added to the string of the previous items that is on top of the stack.  At the end of the statement, the PRINT keyword is added to the output string along with the built up string of the print items and separators that is popped from the stack.

Implementation

The print item recreate function contains a local string variable.  If the separator is set from a previous print code, the string is set to it.  If this separator is not a space, then a space is added after the separator.  The separator is a space if the last print code was a comma (see below).  The string on top of the stack is popped and added to the string.  If the stack is now empty, then the string is pushed to the stack, otherwise the string is added to the string on top of the stack.  The separator is set to a semicolon for the next item if there is one.

Spaces are normally added after a comma like a semicolon, but spaces are not added between multiple commas.  The print comma recreate function contains a local string variable.  If the holding stack is not empty, the string on top of the stack is popped into the local string.  A comma is added to the string (which is empty if the stack was empty, like when there is a comma directly after the PRINT keyword).  The local string is pushed to the stack.  The separator is set to a space.  A space is only added after the last consecutive comma.

The print function recreate function first calls the internal function recreate function (since print functions are translated the same as other internal functions), which will process the print function and its operand and leave the result on top of the stack.  The print item recreate function is called to process the print function like any other item.

The semicolon code is only found at the end of a PRINT statement and replaces the print command code.  The print semicolon recreate function pops the string from the holding stack, adds a semicolon, and pushes it back to the stack.  The print recreate function is called to complete the PRINT statement.

The print recreate function adds the PRINT keyword to the output string.  If the holding stack is not empty, a space is added to the output string, and the string is popped from the stack and added to the output string.

A new separator is set access function was added to the recreator to a specific character, which is used by the print item recreate function.  Pointers to the new print recreate functions were added to the table entries of the various codes.  The expected recreated outputs for translator test #6 (PRINT statements) were updated and now recreated correctly.

[commit 1d3a05f610]

Recreator – Interactive Testing

Up to now, the only way to test the recreator was by using the expression and translator test files with the batch test mode.  An interactive recreator mode was not implemented since the recreator only supported expressions and creating separate modes for both expressions and commands was unnecessary.  With support for commands (just assignments at the moment), an interactive mode for the recreator could be added.

Before implementing the interactive recreator mode, the translator, program unit and recreator instances (which were local variable in the tester class run routine), were changed to the member variables of the tester class.  As local variables, it was necessary to pass references to them between the various tester routines, which defeated the purpose of having a class.  The output stream is now given to the tester constructor, which is stored in a member variable so that it can be shared by all the class routines, instead of being an argument to the run routine and passed to the other routines.

The tester class was modified to support the new interactive recreator test mode, which is activated with the new "-tr" command line option.

The translate input routine was modified to accept a header string for the list of translated token output.  If this header string is not used, then "Output:" is used as before.  This routine was also modified to return the pointer to the RPN list if the header string is used.  Otherwise, the RPN list is deleted as before and a null pointer is returned, which is also returned when the input line has an error.

The new recreate input routine was added, which starts be calling the translate input routine with the header set to the "Token:" string.  If an RPN list is returned (no error detected), the RPN list is recreated and deleted.  The recreated output is prefixed with the "Output:" string as its header.

[commit 2dc4b17e97] [commit 0ccfb105e7]

Monday, November 11, 2013

Recreator – Sub-String Assignments

Translated sub-string assignments (previously described on July 14) consist of the string reference, followed by the expressions of the arguments of the sub-string function with the assign sub-string code.  This is the same form as when these functions appear in expressions, so sub-strings can be recreated the same way.  This is not the case with multiple sub-string assignments where the sub-string assign codes are all at the end of the translated statement with special assign keep codes except for the final code.

At run time, after popping the value to assign and assignment the value, the assign keep codes push the value back to the stack for the next assignment.  The final non-keep assignment just pops the value off of the stack.  The recreation of multiple sub-string assignments works similarly.  The assign keep code will pop the recreated string of the value expression to assign.  A sub-string assign keep code recreates the sub-string as a function and push the result back to the stack.  For all assign keep codes, the reference string is popped, the current separator is added followed by the value string.  This string is pushed back to the stack.

For the final non-keep assign code, the LET keyword is appended to the output string if the assign code has the option sub-code.  The value string is popped from the stack, which will also contain the other references in a multiple assignment statement.  The string of the final reference is popped and appended to the output string.  Finally the value string is appended to the output string.

Implementation

A separator character member variable was added to the recreator class with access functions for getting the separator character, checking if it is set (not the null character), clearing it (setting it to the null character), and setting it.

The new assign string recreate function contains a local string variable and starts by checking if the separator character is not set.  An unset separator indicates the first and maybe only reference. For the first reference, the string is set to the assignment operator (an equal) surrounded by spaces and the separator is set to a comma.  If the separator is set, then the string is set to it (which is a comma) plus a space.  The string on top of the holding stack is popped and appended to the string.

If the assignment token has the sub-string flag set, then the code of the original sub-string function is obtained.  This was accomplished by making the original sub-string function code the second associated code in all the table entries of the assign keep and non-keep sub-string codes.  The name and the operand count is obtained for the original sub-string function code and the push with operands routine is called to process the arguments of the sub-string function.  The resulting string is left on top of the holding stack.

If the assignment code is a keep code (determined if the second associated code index is zero, which is only the case for the keep codes), the value string with the separator and other references prefixed is appended to the string on top of the holding stack.  Otherwise the assignment code is the end of the statement.  The let recreate function is called to append the LET keyword if the option sub-code is set.  The last reference is popped from the holding stack, appended to the output string, and the string with the rest of the references, separators and value is appended to the output string.  The separator is cleared for the next command.

All of the translator assignment tests (#1 through #5) are all now recreated corrected except for a single PRINT statement in test #5 (since the PRINT recreation has not been implemented yet).

[commit b1c7dc5bce]

Sunday, November 10, 2013

Recreator – Assignments

Translated assignment statements consists of one (single assignment) or more (list assignment) variables or arrays (references) followed by the expression of the value to assign with an assignment code at the end, which may have the option sub-code indicating the optional LET keyword was entered at the beginning of the statement.  When these statement are being recreated upon reaching the assignment code at the end, the strings of the references will be on the string holding stack (the first or only variable at the bottom) along with the string of the value expression (at the top of the stack).

The new assign recreate function handles the assign double, assign integer, assign double list, assign integer list and assign string list codes.  The assign string code could be at the end of a mixed sub-string assignment statement and these will be handled differently (see July 14).  The strings on the holding stack need to be appended to the output string in reverse order with the necessary separators between them (a comma between the references when there are more than one, and an equal between the last or only reference and the value expression).

To reverse the strings, the assign recreate function contains a local string stack and starts by popping the value expression string from the holding stack and pushing it to this local stack.  A local separator string is set to the assignment operator (an equal) with surrounding spaces.  The routine loops until the holding stack is empty.  For each reference, the separator string is appended and the popped string is pushed to the local stack.  The separator string is set to the comma with a space for the next reference.

A new let recreator support function was also added that checks if the assignment token has the option sub-code set and appends the LET keyword with a space to the output string if it does.  This function will also be used for multiple mixed sub-string assignment statements.  The assign recreate function calls this function and then loops until the local stack is empty appending each string popped to the output string.

A new recreator stack is empty access function was needed to determine if the holding stack is empty.  This was implemented in the recreator class header file since it was only one line of code.  Since the recreator top, top append, and append access functions are also only one line of code, their definitions were also moved to the header file.  Finally, the pointer to the assign recreate function was added to the table entries of the above mentioned assignment codes.

The results for translator tests #1 to #5 (various assignment tests) were changed to the expected results (inputs with spaces added and upper case keywords).  The statements in tests #1 (assignment tests) and #3 (data type assignments) are recreated correctly.  Many of the statements in the other tests are also recreated correctly, but the ones that are not contain string and sub-string list assignments yet to be implemented.

[commit 97c666a9d3]

Saturday, November 9, 2013

Recreator – Expressions (Tagged)

Expressions are now fully supported by the recreator.  All the expressions test recreate the original source correctly.  The next step is to add support for the first round of commands (LET, PRINT, INPUT and REM).  The first tag of the 0.6 development series was added, v0.6.0.

[commit 0dc1f0ebf5]

Recreator – Arrays and Functions

Supporting arrays and functions in the recreator is only necessary for the translator tests.  So that arrays and functions are recreated properly for these tests, preliminary recreate functions were created for arrays, defined functions and user functions.

The array and function recreate functions get the name from the string of the token.  An open parentheses is added to the name since it is not stored in the token.  The name and the attached count in the RPN item is passed to the push with operands routine.  The define function recreate function handles both define function with and without tokens, so an open parentheses is only added to the name if the attached count is not zero.  Pointers to these functions were added to the preliminary table entries for the codes of these tokens.

All the expressions in expression test #3 (parenthetical tokens) now recreate the correct output. 

[commit b6e8973352]