
Final Year Project Report
"Optical Communications Simulation Software with Windows Interface"
Student: Patrick O'Halloran
ID Number: 9637079
Department: Electronic and Computer Engineering
Supervisors: Michael Connelly and John Nelson
Abstract
The Study of Compiler and Translator concepts in relation to an Optical Simulation Package.
This Project will discuss the concepts of compilers and translators on a general level. It will also explore the manner in which certain translators manage these concepts. The report will demonstrate one design method, which shows how to implement the translated code in a more object-oriented manner using Object Modelling Technique (OMT) and the Unified Modelling Language (UML). It will also show in detail the design for the Graphical User Interface (GUI) and the manner in which OMT and UML were used in the specification of this project. This report will discuss the implementation language, Microsofts Visual C++ Version 6, and some of the details, which had to be taken into account in the translation from Pascal. Finally, this report will contain results and conclusions that relate to the project.
Table Of Contents
1.Introduction
Project Goal
What the Project fully entailed
Report Layout
2. Background Information
Compilers and Translators
Phases of a Compiler
Lexical Analyser
Symbol Table Management
Syntactic Analysis
Semantic Analysis
Intermediate Code Generation
Code Generation
2.2 Background on Translator Used
2.3 Microsoft Visual C++ 6
3. Analysis and Design
Analysis Phase
Overview of Analysis Phase
Analysis of the Application
Problem statement
Requirements of system
Use Cases
Analysis of Translators
Design Phase
Overview of System Design
Object Model of System
The application window
The Graphing Options
The object model of Application Window
The Run Options of the System
Dialogs
Object Model for Run Options
Complete system Model
4. AmpSoftware Implementation Phase
Translator Implementation
Graphical User Interface Implementation Phase
MainFrame Implementation
GUI Operations
5. Conclusions
6. Acknowledgements
7. References
Appendix A
Appendix B
Appendix C
Appendix D
1. Introduction
1.1 Project Goal
The goal of this project was to take an application, which was written in Pascal with a DOS based user interface, and translate the project into C\C++. This was to be done by either developing a Translator or by building on an existing translator package, which was available. The path, which was taken, was to use an already existing translator and to build upon it. This path was chosen based on the advice given, this advice stated that the timescale in generating a fully functional Pascal to C\C++ translator was outside the timescale for this given project. The students aspiration is that the translated code, which is generated, should have identical behaviour to the original code. The student also aspires to producing a functional Windows based graphical user interface, which makes the application a little more user friendly.
1.2 What the Project Fully Entailed
The project entailed researching the whole area of translators, which are based on general compiler theory. The student will demonstrate these concepts in more detail in further sections of this report. Following the research was the design phase of the project, this section dealt with the specifications, which the project had to meet and the design format, which the project was to follow. The design also shows the manner in which the converted code can latter be implemented in a more object-oriented manner. The most challenging part of the project was in the replication of the functionality of the previous application. This section of development included adjusting the translated code to ensure it produced the same data values as those produced in the original. In dealing with these data values a lot of time was spent debugging the code to eliminate and prevent spurious values being produced. These will all be detailed later. In the process of developing this project the student also had to become competent in the use of Microsoft Visual C++ Version 6. This came later after all the analysis and design was completed.
The project report will have the following layout; firstly the student will detail a lot of the research behind translators and compiler theory. Secondly will come the design decisions which were made and implemented, these will include choices made in the course of the project lifecycle. These choices will be explained in detail later. Each choice will include examples in each case for the reasons in choosing one approach over another. This report will also detail information about object-oriented practices. After this will come conclusion and appendixes.
2. Background Information
This project stems from the development of a compiler. Dealing with the first section and of this project we can see that background information comes from the design and implementation phases of a compiler. Since this project deals with a translator rather than a typical complier. The code generation phase will be different. In this phase instead of producing machine code, C/C++ code is generated. This compiler-based design will hopefully translate syntactically correct Pascal code to semantically and syntactically correct C/C++ code.
2.1 Compilers and Translators
The main parts of compilation are analysis and synthesis. The analysis part breaks up the source program into constituent pieces. This then creates an intermediate representation of the program. The synthesis part constructs the desired target program from the intermediate representation. These two sections (analysis and synthesis) are treated as front and back ends of the compiler respectively. The front end consists of those phases or parts of phases that depend primarily on the source language and are largely independent of the target machine; these normally include lexical and syntactic analysis, the creation of the symbol table, semantic analysis and the generation of intermediate code. . The front end includes error handling that goes along with these sections.
Plus it can also do a certain amount of code optimisation. In compiling, analysis can be segregated into three phases as shown below and code generation.
Linear Analysis:
This is where the stream of characters representing the source program is read from left to right and grouped into tokens. Tokens are sequences of characters having a specific collective meaning i.e. in the case of the Pascal programming language the relational operators are examples of this {<>, <=, >=} and keywords of the language can also be symbolized in a similar manner.
Syntactical Analysis:
Involves the grouping of tokens of the source program into grammatical phrases that are used by the compiler to synthesize the output. This also insures that the syntax of the source program is correct, as it is perceived by the grammar of the language.
Semantic Analysis:
This is where certain checks are performed to ensure that the components of a program fit together meaningfully. This section insures that the output program makes since and has the same behaviour as the source program.
Code Generation:
This section will deal with the actual conversion of the syntactically and semantically correct code being converted to the destination language, using all the information gathered from the previous sections of the project. The destination language in this case will be C++.
2.1.1 Phases of a Compiler

2.1.1.1 Lexical Analyser
The lexical analyser is the first phase of a compiler. The basic driver of a lexical analyser is a Finite State Machine with actions appended to each state. The main task of the lexical analyser is to read the input characters of a specified source program and produce as output a sequence of tokens that the parser uses for syntax analysis.
The manner in which it deals with the input is determined by the FSM (Finite State Machine). The FSM is derived from a grammar representation of the Pascal language which recognises such elements as numbers, identifiers, constants, uses, variables, begin-end blocks, statements, and other program units, as will be defined in more detail later.

Sometimes lexical analysers are divided into a cascade of two phases, the first called "scanning" and the second "lexical analysis". Since it is the lexical analyser that reads the source program, it may execute secondary tasks while reading in the source text. During the scanning phase of the lexical analyser white space and comments may be removed from the source text. When we refer to lexical analysis we use the terms "tokens", "pattern", and "lexeme" with specific meaning. In general there is a set of strings in the input for which the same token is produced as output. This set of strings is described by a rule called a pattern associated with the token. A lexeme is a sequence of characters in the source program that is matched by the pattern for a token. We treat tokens as terminal symbols in the grammar. In most programming languages the following are treated as tokens: keywords, operators, identifiers, constants, literal strings, and punctuation symbols such as parenthesis, commas and semicolons. When a character sequence appears in the source program a token representing the type of sequence seen is returned to the parser. The lexical analyser collects information about tokens into their associated attributes, all of which are used in the initial development of a specific data structure, called a symbol table.
2.1.1.2 Symbol Table Management
An essential function of a compiler is to record the identifiers used in the source program and to collect information about the storage allocated for an identifier. The Symbol Table is a data structure containing a record for each identifier. It also contains fields for the attributes of each of those identifiers. The attributes involved consist of its type, its scope, and in the case of procedure names such things as the number and types of the procedures arguments. The method by which each argument is passed and the type returned also need to be recorded. When the lexical analyser detects an identifier in the source program, the identifier is entered into the symbol table. However the attributes of an identifier cannot normally be determined during lexical analysis. For example consider the sample Pascal code declaration below:
var count, initial, time : integer;
The lexical analyser does not know the identifiers are of type integer when they are read. Therefore the remaining phases enter information about identifiers into the symbol table, and then use this information in various ways. When doing semantic analysis and intermediate code generation, we need to know what the types of identifiers are, so we check to see if the source program uses them correctly and once this has been established we can generate proper operations on them. A symbol table mechanism must allow us to add new entries and find existing entries efficiently. Hence in the development of this data structure one would have to take into consideration the time required to add entries and to make inquiries to the symbol table. Another consideration in implementing the symbol table will be whether or not it will be generated dynamically or whether it will be set to a fixed size, in which case the size will have to be large enough to handle any source program. With this in mind the focus of this research will keep its emphasis on a dynamically generated symbol table.
2.1.1.3 Syntactic Analysis
Every programming language has rules that dictate the syntactic structure of well-formed programs. A grammar gives a precise, yet easy to understand syntactic specification of a programming language. Utilising certain classes of grammars one can automatically construct an efficient parser that determines if a source program is syntactically well formed. As an additional benefit the parser construction process can reveal syntactic ambiguities and other difficult to parse constructs that might otherwise go undetected in the initial phase of compiler development. Starting with the root labelled with the starting non-terminal and repeatedly performing the following two steps one can generate a recursive descent parse tree:
In general there are a number of tasks that can be conducted during parsing, such as collecting information about various tokens and placing this gathered information in the symbol table and checking for other kinds of semantic errors. The error handling associated with the syntactic analysis stage has set goals:
2.1.1.4 Semantic Analysis
In general the compiler must check that the source program follows both the syntactic and semantic rules associated with the programming language in question. This inspection is called static checking as it is done before the execution of the target program. Static checking ensures that certain kinds of programming errors will be detected and reported. Example of static checks include the following:
The compiler will report an error when an operator is applied to an incompatible operand, for example, if we tried to add an integer value to a function call.
An example of this would be in the use of a break statement in C, where the program breaks the flow of control from the smallest enclosing repetition loop or switch statement, and transfer flow of control the somewhere else, we need to be able to report an error if such an enclosing statement is not present. There are just two brief examples of static checking many more will need reviewed and considered in the process of writing this translator. This is a brief overview of the manner in which type checking should be handled. A type checker verifies that the type of a construct matches that expected by its context.
Syntactic Analyser Semantic Analyser Intermediate code Generator

For example a type checker must verify that a user-defined procedure is assigned the correct number of parameter arguments and also the correct type of arguments for which it was defined. When designing a type checker for Pascal it will have to work on the basis of the syntactic structure of the language, and the rules associated with assigning types to language constructs, such examples are detailed below as from Compilers, Principles, Techniques and Tool, By Aho, Sethi and Ullman, Addison-Wesley 1986.
" If both operands of the arithmetic operators of addition, subtraction and multiplication are of type integer, then the result is of type integer." The result of the unary & operator is a pointer to the object referred to by the operand. If the type of the operand is "---" the type of the result is a pointer to ---."
When considering types in a language, we can break the types up into basic types or constructed types. Basic Types are said to have no internal structure to them. Examples of basic types include character, integer, real, and Boolean. While constructed types are arrays, records, sets, and pointers. The above information is only a basic outline of the considerations, which have to be taken account of when designing a semantic analyser
2.1.1.5 Intermediate Code Generation
In the synthesis model of a compiler, the front end translates a source program into an intermediate representation from which the back end generates the target code. Details of the target code are confined to the back end of the Compiler as far as possible. The reason for this is because the target code to be generated can be changed as long as the front and back ends are kept separate i.e. you can translate directly to a different programming language or a machine dependant language. Plus you can apply a code optimiser between the two sections to produce more efficient code. Syntax trees or postfix notation are two kinds of intermediate representations for programming languages.
2.1.1.6 Code Generation
Source program

The final phase in the translator is the actual translation of the code using the code generator. It takes as input the intermediate representation of the source program and produce as output the equivalent target code i.e. C++. The requirements, which are imposed on the code generator, are severe. The output code should be efficient and correct.
2.2 Background on Translator Used
The translator, which was chosen, was PtoC. This is yet another Pascal to C/C++ converter. The primary idea of this converter is to produce readable and supportable code, which preserves style of original code as far as possible. The converter recognizes Pascal dialects which are compatible with Turbo Pascal 4.0/5.0 and ISO Pascal standard - IEC 7185:1990(E) (including conformant arrays). At this moment it was tested with Turbo Pascal, Oregon Pascal, Sun Pascal and HP Pascal. The converter can produce both C++ and C output. Using of C++ language allows encapsulation of some Pascal types and constructions into C++ classes. So mapping between Pascal and C++ becomes more direct then between Pascal and C. The developer uses C++ templates to implement Pascal arrays and files. Special template classes are used for conformant arrays. Streams similar to those used in C++ are used to implement Pascal IO routines. The same runtime library is used both for C and C++. PtoC recognizes Turbo Pascal's extensions, such as units, strings, some special types and operations. Turbo Pascal extensions are supported only for C++ language. When this project was completed PtoC was capable of translating more than 400,000 lines of Oregon Pascal to C (from RSX to OpenVMS).
Structure of converter
Below there is a short description of converter itself:
The readme file for the converter will be included in the Appendices.
2.3 Microsoft Visual C++ 6
When creating a program for any Windows platform and you are using this compiler, the programmer has two choices: C or C++. With C, the programmer codes at the level of the Windows Application Program Interface (API). This interface consists of a collection of hundreds of C functions described in Window's API Reference books. For Window's NT, the API is typically referred to as the "Win32 API," to distinguish it from the original 16-bit API of lower-level Windows products like Windows 3.1. Microsoft also provides a C++ library that sits on top of any of the Windows APIs and makes the programmer's job easier. This library is the Microsoft Foundation Class library (MFC); this library's primary advantage is efficiency. It greatly reduces the amount of code that must be written to create a Windows program. It also provides all the advantages normally found in C++ programming, such as software reusability, inheritance and encapsulation. MFC is portable, so that, for example, code created under Windows 3.1 can move to Windows NT or Windows 95 very easily. It was for these is reason that Visual C++ was chosen as the implementation language, this and the fact that the student wanted to become more familiar with these development tools. Visual C++ is a complete application development environment that, when used as intended, it lets you fully exploit the object-oriented nature of C++ to create professional applications. The MFC class hierarchy encapsulates the user interface portion of the Windows API, and makes it significantly easier to create Windows applications in an object oriented way. When using the Windows Foundation Class the developer can use a number of standard controls, such as buttons, menus, scroll bars, and lists, that are already familiar to Windows users, which are all standard classes in the foundation class. Hence using these tools can increase the development speed of both small and large-scale projects.
3. Analyses and Design
This chapter will deal with the project analysis and design phases, which the student followed over the project lifecycle. Firstly: the student will show the analysis stage. This phase determined the requirements for the translator and the reasons behind the specific translator choice. Secondly: the student will show the design phases taken. These design phases will include, the System Design Phase and the Object Design Phase.
3.1 Analysis Phase
3.1.1 Overview of Analysis Phase
The first stage of the analysis phase is to set the objective of the project or the problem statement for the project. This is a brief description of the problem in very explicable English. The problem statement is worked out by the analyst (or in this case the programmer) and the end-user to determine what is expected as an end product.
It is through this that the requirements document for the project was determined.
The second stage is the establishment of a requirements document. This details all the different requirements, which the system must adhere to. The third stage is then to determine Use Cases for each requirement. These Use Cases are an added factor to the original OMT methodology, and are used in the UML process. A use case is a description of some behaviour that is required of the system, detailing how the system interacts with one or more actors. A use case is a description of a system function.
An actor is an external (outside the system boundary) entity that in some way participates in the story of the use case. An actor typically stimulates the system with input events or receives something from it. Actors may be the end user, a database administrator, a computer operator, another computer system application, or another entity needing to exchange information with the system. The final stage of the analysis deals with the context diagram, which comprises of the main entities derived from the previous stages.
3.1.2 Analysis of the Application
3.1.2.1 Problem statement
The main task of the problem is to translate the original Pascal code to very similar replicated code and easily very explicable C\C++ code. The second task of the problem is to produce a Windows Graphical User Interface; this should have extended functionality as compared to the previous DOS based interface. The first task shall be implemented using an already existing Pascal to C converter, the only factors which have to be considered now is to determine what translator to use this will be based on the requirements which are documented in a later section. The application should allow the user to open any files specific to the project in this case; these will include specific parameter files. The application should also allow the user to select these files using a Windows Explorer type tree structure. The user should then be able to view, create, modify and save any of these files. The user will then be given to option to run specific algorithms based on the input files, depending on which option is chosen. The user will also be able to run a graphing program specific to the data files which are produced. The user should be able to refresh the tree at any time to check updates made to the system.
3.1.2.2 Requirements of system
Although in this case the student will not be implementing the translator, the translation phase must still be completed and some requirements in choosing a translator must be laid out, these requirements are as follows:
Requirements for Translator:
Requirements for GUI:
3.1.2.3 Use Cases
This section will deal with some of the major use cases of the system and their error scenarios.
Major Use Cases:
The system prompts the user for the name of the file they wish to modify.
The user enters the name of the file that he wishes to modify.
The system opens the file specified.
The user cancels the operation.
The user cancels the operation.
The menu options available are displayed in a pop down menu.
The user selects the specific option they want to choose.
The progress bar for the calculation status is shown.
The calculation runs to completion.
The system informs the user that the task has completed.
The system tree is refreshed with new information.
GUI states "Updating System Information".
System Refresh completes, system returns to idle state.
The menu options available are displayed in a pop down menu.
The user selects the specific option they want to choose. (In this case the aspirations of the student is to have one option at first).
The graphing function chosen execute and graphs the specific data for the option.
User closes graph program and system returns to idle state
System prompts the user that an error has occurred and the system returns to the idle state.
System prompts the user that an data interruption has occurred and the system returns to the idle state.
The user then just presses the F5 Key.
The system displays a message in the status bar stating "Updating System Information".
Once the tree is displayed the system returns to the idle state.
This is a very brief outline of four of the Use Cases, which were used for this project, if all the Use cases for the system were to be displayed it would prove to be very impractical.
3.2 Analysis of Translators
This phase of analysis was completed at a very early stage in the project lifecycle.
It dealt with all the requirements, which were specified, in the original requirements documents for the system. For a recap on these requirements see section 3.1.2.2. The analysis consisted of comparing two or more previously available Pascal to C converters. These existing products are TtoC, P2C and PtoC. However due to the fact that the source code was unavailable the analysis of TtoC was short lived. On first appearances the student believed that the P2C translator was the one to choose but after further analysis of the translated code, the PtoC option was reconsidered and chosen. The difference in the output code shall be shown later in the Appendices, this will visually demonstrate the reasons for choosing the PtoC option. Although the PtoC option was chosen it was not without considerable time and effort that the system was translated to produce similar output to the original Pascal application. There were many modifications to be made included in the process of this analysis stage. The major changes will be documented in more detail in the implementation phase of the report. On the basis of this decision the project developed. As were it not for the conversion factor of the project the final result would merely be but a prototype GUI shell and little or none of the original functionality. The functional details of the PtoC translator are shown in translator background section of the report.
3.3 Design Phase
3.3.1 Overview of System Design
Once the analysis phase has been completed the developer now must select a method of designing a solution to the problem. System design is a high-level strategy for solving the problem and building a solution. This includes breaking down the system into subsystems where necessary. Major conceptual and policy decisions are made at this phase of development. When considering the design method to use the student must take into consideration the following concepts:
There are two major subsystems to this project. Firstly; the translation phase. In the case of this project although the translator was present and designed, it was still necessary to set out certain requirements in choosing which to choose. Secondly; the Windows Graphical User Interface, most of the project design will be focused on the GUI, but this will also factor in the interaction of the whole system, i.e. the manner in which the translated code and its dependencies will interact as a complete system. The next section will deal with an overall object view of the system and its interdependencies. This object view will only detail the main system object. Later sections will deal with segmented views of the subsystems and outline the associations present.
3.3.2 Object Model of System

Fig 3.3.2
The object model describes the static structure of the system and is based on modelling the system data. It graphically shows the entities that exist within the system and the relationships that can exist between the entities. However due to the confines of space, the complete system all the operations and attributes of each of the objects have been left out, you can see a detailed view of each object in separately defined subsystems over the following sections these will detail operations and attributes of these subsystems of the project. Previously the student stated that the system could be broken up into subsystems some of which are major as in the cases of the Translator and the GUI being the two main, however due to its design we can also show the GUI in segmented smaller subsystems. In the design of the application we can split the application into the following subsystems:
3.3.2.1 The application window
The main application frame window in derived from the CFrameWnd class and comprises of the following objects. The class diagram below shows all the operations and attributes associated with this subsystem. The CMainFrame class is derived from the CFrameWnd class of an SDI (Single Document Interface) application in MFC. It contains all the necessary information dealing with the Main Frame window of any SDI application. In designing this project the student had to consider the type of interface, which was needed in the case of this application. Working from the requirements it has been decided that the system should contain, a method of displaying the files and directories specific to the system. The best means of showing this would be by means of a Windows Explorer type file and directory tree view structure. Secondly, seeing as the user needs to have access to edit and modify specific files and data input, it has been decided that the system also contain an Edit View which will allow the user to create, open, modify and save any files associated with this panel of the application.
3.3.2.2 The Graphing Options
At the start of the project the original design plans for this section was to try and create a graphing class specific to the project however in the process of the development and due to unforeseen complications in some areas of the development this design section had to be modified, now the design plan is to call and already existing plotting program which has become available and to build this into the system. Using available source code from the application and using API commands, which allow the user to modify using a type of dependency file for the plotting system. This design should allow the user to plot specific files based on the parameters passed in this file.
3.3.2.3 The object model of main Application Window

3.3.2.4 The Run Options of the System
When considering the functionality of the system based on the requirements and use case options outlined, the student had to consider the manner in which the interface between the translated functional algorithmic code and the GUI was to be implemented. After considerations about the manner in which the data results of the translated code would be represented and how each specific algorithm for a set operation was to be carried out, the best solution to the problem was adapted. In the students view the best method was through the use separate threads. The design approach taken was to create a separate thread for each operation, with each thread updating the main GUI thread with information based on the progress of the calculation being processed in the background.
3.3.2.5 Dialogs
The use of Dialogs in this Design is mainly to display information about the project and to detail progress in specific calculations.
The About Dialog is a default dialog in the SDI application and is used to display information on the details of the development of the software versions and updates which a system has been put through. In this case it will be used to display the background information of previous developments to this date.
The ErrDlg will be used as an interface between the threads running in the background once a system run operation has been chosen. This will also display the information being passed form that thread in the form of a progress bar stating the progression of the calculation being processed.
3.3.2.6 Object Model for Run Options

3.3.2.7 Complete system Model
This class diagram shows the general class hierarchy of the complete system; from the Translator whose design is mainly based on the functional design of the original non OOD of the Pascal system, to the class hierarchies of the GUI including the derivations from the standard MFC classes. This diagram does not show all the intricate operations and attributes of each class involved, as this would be impossible to detail fully in this report.

4. AmpSoftware Implementation Phase
After completion of the Analysis and Design phases, the natural progression based on these phases is the initiation of the Implementation stage of the project. It is here that the developer recaps on the information gained in relation to the system and starts to build on it. This should be much more mechanical seeing as all the decisions have been made in the previous phases of the application development.
4.1 Translator Implementation
This stage of the implementation was basically spent in working with a previously developed translator program called PtoC. The appendices will show the main details of the translated code produced by this program. The appendices will also show the comparison of the previously analysed translators and the code produced by each.
During the process of translator implementation there were some major errors and faults, which had to be dealt with in relation to the translated code. These included some of the minor functionality details of the Pascal language which were used in the original code but which were not supported by the translator. These included to addition of an append function to the translator, and also the support for a Frac function to the translator.
4.1.1 Code Segment
double frac(double in) /* new defined fraction */
{
int x;
double out;
x = (int) in;
out = in - x;
if (out <= 0)
{
out += 1;
return out;
}
else
return out;
}
This function took into account that the system rounded both up and down for int values.
Although these may seem to be simple additions to the system it did take time to get used of the structure of the code used by the PtoC translator, this was aided by the fact that the student was in direct contact with the developer of said translator. This fact will be detailed more in the Acknowledgment section of the report. The translation of the code also produced many errors in reference to the code upon completion of the translation, to be precise, starting with the translated code and trying to compile this version produced 702 compilation errors and 293 warnings.
This provided the student with a major insight into debugging a large system project using the Microsoft Developer Studio and hence has given the student good experience with this tool, however it is here that a major amount of the system development lifecycle was spent, hence there had to be modifications made to the original system design.
After many modifications, error corrections, and elimination of warnings the student obtained a Console application, which had the appearance (less some text colours) of the original. However, although the appearance of the system may have been similar and the system may have seemed to execute in the correct manner this was far from the true picture. After firstly correcting the fact that the output files were not being created, the student then had to once again debug through the code and find out why the system was producing out of bounds error in the newly produced data files. This again lead to a lot more time debugging and trying to step through the code trying to remedy these bounds errors. After a lot of time an effort a solution was found which remedied this error. The error was mainly due to a situation where a real or double value was being compared and this lead to some unusual repercussions in the data output values as certain section of code were being skipped due to this. After discussing the problem with the original designer of the AmpSoftware package, a solution was proposed.
Below is an example of the Console application produced after the translation and compilation error correction was completed.

Fig 4.1.1
After all these modifications have been made it can be said by comparison to the original data which has been produced by the AmpSoftware system and the replicated system the data values can be seen to be 95% accurate.
4.2 Graphical User Interface Implementation Phase
The design of the system made this section of the project much easier in terms of not having to make any vital decisions in the process of development. During the process of implementing this section the student firstly had to become familiar with Microsoft Visual C++, or more correctly with the MFC classes and windows programming in general. This led to a major amount of research and practical experimentation with this development system in order to get familiar with the whole system. The design has already show the interactions of the system, now these will be detailed in the manner in which the design was implemented. The first step was in choosing a Single Document Interface as the main backbone for the system. This led to a system, which had to be defined in one document.
4.2.1 MainFrame Implementation
In order to get the functionality of the system, which the student was aiming for, based on the requirements set out, it was necessary for the main view of the system be split using a splitter window. All of these concepts had been new to the developer and hence had to be experimented with. After determining the method of how to split the window and extracting from the design the views, which were to be, contained in each mainframe panel these ideas were implemented as shown below.
This code segment shows how the mainframe window is split into two panels and the views, which are contained in each panel.

Fig 4.2.1.1
4.2.1.1 Code Segment:
((CAmpSoftwareApp*)AfxGetApp())->m_pDoc = (CAmpSoftwareDoc*)(pContext-> m_pCurrentDoc);
pContext->m_pCurrentFrame = this;
pContext->m_pNewViewClass = RUNTIME_CLASS(CAmpTreeView);
rc = m_wndSplitter.CreateView(0, 0,pContext->m_pNewViewClass ,paneSize , pContext);
if(!rc)
return FALSE;
pContext->m_pNewViewClass = RUNTIME_CLASS(CAmpEditView);
pContext->m_pCurrentDoc=((CAmpSoftwareApp*)AfxGetApp())->m_pDoc;
pContext->m_pCurrentFrame = this;
In the previous code segment the student is accessing the mainframe through a pointer to the main application, it is through this pointer you determine the runtime document and frame in use in the application, in this case there is only but a singular document in use. Through this we can set the size of each panel and set runtime class, which shall be used in the splitter panels. The runtime classes identified above, which are used in the system, are developer defined, these classes have been derived from existing MFC classes but the functionality defined in them were added to by the student. Through the process of developing the project, and from the requirements set out, the student realised that in order to produce the tree view that was needed i.e. a file and directory view of the system dependant files and directories, it was necessary for the student to define a specific class for this section. The tree view which was derived allows the user to view the system information in a Windows explorer type view, however there have been filters added to the system to allow the application to only detect files that are dependant on the AmpSoftware system. In order to accomplish this the student defined specific functionality, which created a system specific image list, filled the tree view by firstly determining all the directory in the system and then determining all the files which were dependant on the system itself, it has been realised over the final phase of the development that this method had proven to be very slow in building and refreshing the tree structure and this is one of the areas which can be modified in later versions. The appendices will contain some snippets from this class and will detail the information behind it.

Fig 4.2.1.2
The second panel of the splitter window is the edit view class, here is where all the data is displayed once the user either opens or creates a specific file. The only changes which were made to this class view was that of the serialize code for saving the data files, this was done by having a serialize function specific to this class which overrides the documents main serialize function of the SDI.
4.2.2 GUI Operations
This section of the GUI deals with the operations, which the user is given to calculate specific algorithmic calculations. In this section the student had to take the original translated code and supply the GUI with the operations equivalent to that of the original DOS console as shown earlier. To do this the student had to separate each specific operation to be completed in separate tasks. The student implemented this using a specific Thread wrapper class, which allows the developer to create any number of threads, which inherits the thread attributes of the CThread class used in the wrapper.
In the case of this project the threads, which are defined, have been detailed in the Run Options Design, this details the COppoint class, the CAmpVsBins class and the CAmpVsPin. Any of the thread specific code, which is critical to each operation, is placed in the thread handler function of the derived class.

Fig 4.2.2.1
Each of these Threads are called based on the option which the user selects from the MenuBar. The reasons for using threads were to allow the user to see the progress of any of the operation calculations.

Fig 4.2.2.2
This is achieved using a Dialog containing a progress bar; the calculations update the main GUI thread from the worker threads running in the background, these worker threads are the COppoint versions etc. these execute the calculations for the system. This is the only method by which the student could see of getting around the fact that the Progress Bar had to be Modal (which implies that the Dialog had to remain constant during the execution of the background thread so the user can monitor the execution progress). The use of threads allows the system to update the GUI thread using the worker threads. The specific details of the code have not been included in the main body of the code, however the appendices will contain some of the more important code segments. The graphing operations of the system have been implemented using an existing plotting program called Easy Plot it has been used as a separate process, this is created when the user selects the graphing option of the system. The user can specify the file to plot using a batch file associated with the program. The plotting program has been interfaced with the system, by using standard Windows API calls. Using the source code for the plotter the student has interfaced the system using the instructions, which were detailed in the user manual for the Easy Plot program. More details of this will be shown in the appendices.
5. Conclusions
The project was a very interesting and challenging experience on a whole.
During the course of the development of the project, the student came across many problems. Although at stages it seemed as if some of the problems would never be solved it was a relief to get a chance to debug the system and hence become so familiar with the code. However during the course of the development it was also noticeable that the information, which was available about the Translators, was not very forth coming, this led the student into direct contact with the developers, which was a very interesting experience. Receiving first hand feedback about the system was very useful in solving all the problems related to the translators. Another major factor was the lack of decent information about MFC and the intricate details surrounding it. The best source of information was the MSDN libraries. Hence in terms of practical implementation information and a listing of all the details of the classes, which can be used, the MSDN libraries are a must. It was realised after reading a lot of books in this area that the amount on high quality in-depth information on this system was very scarce. So this is one major downfall n terms of the use of this tool for development. In dealing with the Design phase it was difficult for the student to get used to designing using the Rational Rose tool, as this was the first time to use such a tool, and the documentation available was a little vague, however it is a very useful tool and the student hopes to become more familiar with it in the future. Over all the student believe that although this project may have been very time consuming and challenging it has been an interesting experience and the challenge with getting used of new technologies was a pleasant experience.
6. Acknowledgements
I would like to thank my supervisors for all their help and guidance over the period of the project. Especially Dr. Michael Connolly for putting up with me asking silly questions and not turning up for all the meetings, sorry Michael. I would like to give special thanks to Konstantin Knizhnik and Dave Gillespie for providing me with information on their Pascal to C translators and for answering my stupid questions I may have had. I would also like to sincerely thank my parents for giving me all the opportunities I have been given in life, and I promise I will pay you back for everything over the next few years (Honest I will). I would now like to thank "The Lads" for basically letting me move into your house, it is greatly appreciated even though I may never and will never admit to it again, unless under extreme torture. Now for a few more, I reckon Im almost ready for the Oscars now. I would like to thank Anto, who Ive known "with" the last four years, for the comic relief provided over the last few years, oh yeah by the way, Anto you are going to Europe with us, its in paper now. I would like to thank Barry for basically being Barry, for the use of the zip drive, zip disks and anything else that I have forgotten. I would also like to Kasper for all his wisdom, seeing as it comes with old age, only joking, I just remembered I have to respect my elders. I suppose I have to thank Stephen now as well, thanks for all the interesting information, now honestly Stephen thanks for any of the help you gave me over the last few years, its greatly appreciated. Well Cedric what can I say I guess all I can say is thanks for being around and for the use of the kitchen table and for being there man!! Bill I guess I have to thank you for all your humorous input and Dilbert columns, plus your back seat programming over my shoulder, it was all appreciated. Hey Puneet didnt forget you . honest, I have to thank you for all the insightful help you gave over the last four years, and for all the guidance you gave in that time, hope you enjoy America. To all those in 49 Curragh Bírin, thanks for putting up with me, and sorry for not being around much over the last while.
7. References
Alfred V.Aho, Ravi Sethi, and Jeffrey D.Ullman; Compliers : Principles, Techniques and Tool;Addison and Wesley; USA; 1986.
Ronald Mak; Writing Compilers and Interpreters : An applied approach Using C++, Second Ed.;Wiley & Sons; USA; 1996.
Colin Flanagan; CE4717- Language Processors;UL Printroom;Ireland;1999.
http://www.kashpureff.org/nic/linux/softlist.en/p2c.html
http://www.cplus.about.com/compute/cplus/cs/advancedc1/index.htm
Appendix A
Source Files |
Header Files |
Related Information |
| GUI
Sourdce Files StdAfx.cpp AmpSoftwareView.cpp AmpSplitterView.cpp Splash.cpp AmpSoftware.cpp MainFrm.cpp ErrDlg.cpp AmpEditView.cpp AmpSoftwareDoc.cpp AmpTreeView.cpp Thread.cpp OpPoint.cpp AmpVsBins.cpp AmpVsPin.cpp Translated Source Files ampsoft.cpp umaths.cpp univer.cpp uc.cpp uchar.cpp uop.cpp umodel.cpp |
GUI
Header Files StdAfx.h AmpSoftwareDoc.h AmpSoftwareView.h AmpEditView.h AmpSplitterView.h Splash.h MainFrm.h AmpTreeView.h Resource.h ErrDlg.h AmpSoftware.h Thread.h OpPoint.h AmpVsBins.h AmpVsPin.h Translated Source Headers Uc.h Uchar.h umaths.h Umodel.h uop.h univer.h PTOC Header Files array.h paslib.h io.h main.h nmtbl.h parser.h ptoc.h set.h token.h |
The StdAfx files are used in defining specific windows based functionality associated with the MFC libraries and must be enclosed in each cpp file to be included in the project. |
Appendix B
Pascal to C Translator Comparison
This Appendix will deal with the comparison made between the PTOC translator and the P2C translator. This will display the reasons for choosing PTOC over P2C, it will show some of the generated code from each translator.
Sample Pascal source code:
PROCEDURE CSqrt; { Calculates the square root of a complex number }
BEGIN
R := Modulus(C1);
IF(C1.Re = 0.0) THEN BEGIN
IF(C1.Im < 0) THEN
Theta := Pi/2
ELSE
Theta := -Pi/2;
END ELSE
IF(C1.Re < 0) THEN
Theta := ArcTan(C1.Im/C1.Re) + Pi
ELSE
Theta := ArcTan(C1.Im/C1.Re);
C2.Re := Sqrt(R)*Cos(Theta/2);
C2.Im := Sqrt(R)*Sin(Theta/2);
END; { procedure CSqrt }
PROCEDURE CZero; { Sets a complex number = 0 }
BEGIN
C1.Re := 0.0; C1.Im := 0.0;
END; { of procedure CZero }
PROCEDURE CpowerInt; { nth power of a complex number }
VAR r,arg: double;
BEGIN
r := PowerInt(sqrt(sqr(C1.Re) + sqr(C1.Im)),B);
arg := B*ArcTan(C1.Im/C1.Re);
C2.Re := r*Cos(arg); C2.Im := r*Sin(arg);
END; { of procedure CpowerInt }
PROCEDURE CExp; { Exponential of a complex number }
BEGIN
C2.Re := exp(C1.Re)*Cos(C1.Im);
C2.Im := exp(C1.Re)*Sin(C1.Im);
END; { of procedure CExp }
PTOC conversion of above code segment:
void csqrt(complex c1, complex& c2)
/* Calculates the square root of a complex number */
{
r = modulus(c1);
if(c1.re == 0.0) {
if(c1.im < 0)
theta = pi/2;
else
theta = -pi/2;
} else
if(c1.re < 0)
theta = atan(c1.im/c1.re) + pi;
else
theta = atan(c1.im/c1.re);
c2.re = sqrt(r)*cos(theta/2);
c2.im = sqrt(r)*sin(theta/2);
} /* procedure CSqrt */
void czero(complex& c1) /* Sets a complex number = 0 */
{
c1.re = 0.0; c1.im = 0.0;
} /* of procedure CZero */
void cpowerint(complex c1, integer b, complex& c2)
/* nth power of a complex number */
{
double r,arg;
r = powerint(sqrt(sqr(c1.re) + sqr(c1.im)),b);
arg = b*atan(c1.im/c1.re);
c2.re = r*cos(arg); c2.im = r*sin(arg);
} /* of procedure CpowerInt */
P2C conversion of above code segment:
Local Void CSqrt_(LINK)
struct LOC_BER_DPSK *LINK;
{
/* Calculates the square root of a complex number */
LINK->r = Modulus_(LINK);
if (LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->C1.Re == 0.0)
{
if (LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->C1.Im < 0)
{
LINK->theta = M_PI / 2;
}
else
{
LINK->theta = M_PI / -2;
}
}
else
{
if (LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->C1.Re < 0)
{
LINK->theta = atan(LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->C1.Im / LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK-> C1.Re) + M_PI;
}
else
{
LINK->theta = atan(LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
C1.Im / LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->C1.Re);
}
}
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->C2->Im =
sqrt(LINK->r) * sin(LINK->theta / 2);
} /* CSqrt */
Local Void CZero(LINK)
struct LOC_BER_DPSK *LINK;
{
/* Sets a complex number = 0 */
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->C1.Re = 0.0;
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->C1.Im = 0.0;
} /* of procedure CZero */
Local Void CpowerInt_(LINK)
struct LOC_BER_DPSK *LINK;
{
/* nth power of a complex number */
double r, arg;
r = PowerInt_(LINK);
arg = LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->C1.Im / LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->C1.Re);
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->C2->Re = r * cos(arg);
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->C2->Im = r * sin(arg);
} /* of procedure CpowerInt */
Local Void CExp_(LINK)
struct LOC_BER_DPSK *LINK;
{
/* Exponential of a complex number */
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->C2->Re = exp(LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->C1.Re) * cos(LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->C1.Im);
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->C2->Im = exp(LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->
LINK->LINK->C1.Re) * sin(LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK->LINK
->LINK->LINK->LINK->LINK-> LINK->LINK->LINK->LINK->LINK->LINK->C1.Im);
} /* of procedure CExp */
Even after looking at the translated versions of the source code it is plain to see why, the PTOC translator was chosen. Firstly is produces very similar code to the original Pascal source code and Secondly it produces much more readable code as compared to the latter translation. The P2C translator used structures to try and replicate the use of procedures defined inside procedures as can be implemented in Pascal, however inside each structure was a self referential pointer the structure, but in each case the name of that self referential pointer for different structures, all of which was labelled LINK, hence this lead to extreme readability problems. The following Appendices detail more information about the PtoC translator, including the complete functionality and implementation details.
Appendix C
Sample MFC GUI Code
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAmpTreeView::FillEmptyTree The Following Method if CAmpTreeview, is used to
// fill the TreeView of the Ampsoftware application. It creates a new Image List
// for the sytem and uses these images to display the system details i.e. the
// drives on the system and calls other methods which detail the directory and
// File structure of the system in question
void CAmpTreeView::FillEmptyTree()
{
// The real Explorer has a more sophisticated approach
// so that the tree can be built quickly. Also, it uses
// a thread (note progressive painting in Explorer).
CWaitCursor cWait;
UINT uType;
HTREEITEM hItemDrive;
CString str_StatText;
CMainFrame *pFWnd = (CMainFrame *)AfxGetMainWnd();
ASSERT(pFWnd != NULL);
CStatusBar& r_StatBar = pFWnd->m_wndStatusBar;
m_count++;
if (m_count > 1)
{
str_StatText = "Updating System Information";
r_StatBar.SetPaneText(0, (LPCTSTR) str_StatText);
}
CTreeCtrl& r_ThisTree = GetTreeCtrl();
// form the one and only tree root - the desktop
TV_INSERTSTRUCT strInsert;
// must use a selectedimage even if same
strInsert.item.mask = TVIF_TEXT | TVIF_IMAGE |TVIF_SELECTEDIMAGE;
// a root item - so parent is NULL
strInsert.hParent = NULL;
// index of the image in the list
strInsert.item.iImage = IDB_DESKTOP - IDB_REMOVE;
strInsert.item.iSelectedImage = strInsert.item.iImage;
// force to "Desktop" - the real Explorer uses the
// registry-based items (like "Computer")
strInsert.item.pszText = (LPSTR)"Desktop";
HTREEITEM hDesktop = r_ThisTree.InsertItem(&strInsert);
// do the "drive type" work
// get bitmask: LSB = A; LSB+1 = B; and so on
DWORD dwDrives = GetLogicalDrives();
char szDrive[20];
szDrive[1] = ':';
szDrive[2] = '\0';
// Show all Interanal Drives , does not show Network Drives
for( int i = 0; i < 32; i++ )
{
// all drives have this as their parent
strInsert.hParent = hDesktop;
szDrive[0] = 'A' + (char)i;
DWORD dwTemp = dwDrives;
if( dwTemp & 1 )
{
strInsert.item.pszText = (LPSTR)szDrive;
ASSERT(strlen(szDrive) == 2);
char szTemp[8];
strcpy(szTemp, szDrive);
strcat(szTemp, "\\");
uType = GetDriveType( (LPCTSTR)szTemp );
switch(uType)
{
case DRIVE_REMOVABLE:
{
strInsert.item.iImage = IDB_REMOVE - IDB_REMOVE;
m_bRemote = FALSE;
break;
}
case DRIVE_FIXED:
{
strInsert.item.iImage = IDB_FIXED - IDB_REMOVE;
m_bRemote = FALSE;
break;
}
case DRIVE_REMOTE:
{
strInsert.item.iImage = IDB_REMOTE - IDB_REMOVE;
m_bRemote = TRUE;
break;
}
case DRIVE_CDROM:
{
strInsert.item.iImage = IDB_CD - IDB_REMOVE;
m_bRemote = FALSE;
break;
}
default :
{
strInsert.item.iImage = 0;
m_bRemote = FALSE;
break;
}
}
strInsert.item.iSelectedImage = strInsert.item.iImage;
if(!m_bRemote)
{
hItemDrive = r_ThisTree.InsertItem(&strInsert);
// Inserts Drive Type
InsertDir(&strInsert,szTemp,hItemDrive);
InsertFiles(&strInsert,szTemp,hItemDrive);
}
}
dwDrives = dwDrives >> 1;
}
//expand the Desktop
r_ThisTree.Expand( hDesktop, TVE_EXPAND );
if(m_count > 1)
{
str_StatText = "Ready";
// Reset status Bar message to OnIdle Message
r_StatBar.SetPaneText(0, (LPCTSTR) str_StatText);
}
}
///////////////////////////////////////////////////////////////////////
// CAmpTreeView::OnDblclk This method deal with the functionality which //allows the user to select a file from the tree and by double clicking //on this image it allows the user to view the specified file.
///////////////////////////////////////////////////////////////////////
void CAmpTreeView::OnDblclk(NMHDR* pNMHDR, LRESULT* pResult)
{
NMTREEVIEW* pNMTreeView = (NMTREEVIEW*)pNMHDR;
// Grab TreeCtrl
CTreeCtrl& r_ThisTree = GetTreeCtrl();
// Get Edit View
CMainFrame * pFWnd = (CMainFrame *)AfxGetMainWnd();
CAmpEditView * pView =
ASSERT( pView != NULL);
// Returns a Handle to selected Item in the TreeView.
HTREEITEM hItem= r_ThisTree.GetSelectedItem();
// Returns the FullPath of the Selected Item.
CString str_returned = GetFullPath(hItem);
int r_count = str_returned.GetLength() - str_returned.Find('\\');
CString str_FilePath = str_returned.Right(r_count - 1);
// Creates a instantiation of a CFile object
CFile f_edit;
// Opens selected file for reading and writing if possible
if(!f_edit.Open((LPCTSTR) str_FilePath,CFile::modeReadWrite))
{
AfxMessageBox("Error : Unable to Open Specified Selection!!");
}
else
{
CArchive archive(&f_edit, CArchive::load);
//Calls CAmpEditView Serialize function to load selected file
pView->Serialize(archive);
archive.Close(); // close archive
f_edit.Close(); // close selected file
}
*pResult = 0;
}
/////////////////////////////////////////////////////////////////////////////
// CAmpSoftwareDoc serialization
//
// This Function finds the active Main Window of the Application.
// Gets a pointer to the EditView of the Main Window which is the Left
// Pane of the Application. This then calls the Serialize Function of
// the EditView. This saves or loads the Data in the EditView to or
// from the specified file.
void CAmpSoftwareDoc::Serialize(CArchive& ar)
{
CMainFrame * pFWnd = (CMainFrame *)AfxGetMainWnd();
CAmpEditView * pView =
ASSERT( pView != NULL);
if (ar.IsStoring())
{
// TODO: add storing code here
pView->Serialize(ar);
}
else
{
// TODO: add loading code here
pView->Serialize(ar);
}
}
Appendix D
Pascal runtime library emulation
Arrays
All Pascal arrays in C are stored as zero based (first element has offset 0). Low value of Pascal array bounds is subtracted from index expression. When array is passed to conformant array parameter or to input/output functions Pascal array bounds are passed before array pointer. Low bound is always passed as it was mentioned in Pascal array definition (symbolic constant or integer literal). High bound is calculated depending on value of low bound (if it is explicit constant) and whether variable is formal parameter. if variable is not formal parameter and low bound is either 0 or 1 macro items(x) is used to calculate high array bound:
#define items(x) (sizeof(x)/sizeof(*(x)))
procedure foo(a : array [l1..h1,l2..h2:integer] of char); external;
var a : array [1..10,-10..10] of char;
begin foo(a); end;
==>
foo(const int l1, const int h2, const int l2, const int h2,
char* arr);
char a[10][21];
{ foo(1, items(a), -10, items(*a), *a); }
If left operand is not formal parameter and hence 'sizeof' operation can be used to obtain array size two macros are used to copy and compare array:
#define arrcmp(a,b) memcmp(a, b, sizeof(a))
#define arrcpy(a,b) memcpy(a, b, sizeof(a))
If left operand is formal parameter sizeof operator is applied to it's type:
foo(str5 s) {
memcpy(s, "12345", sizeof(str5));
}
When string is passed as actual parameter for conformant array macro array(s) is used to calculate bounds of string:
#define array(s) 1, sizeof(s)-1, (s)
PtoC provides special type zero_terminated_string for passing zero terminated strings to C functions. Consider for example the following definition of function:
function putenv(s : zero_terminated_string); external;
This function can be called in the following way:
procedure foo(a : array [1..10] of char);
begin
putenv('VERSION=1');
putenv(a);
end;
==>
void foo(array<1,10,char> a)
{
putenv("VERSION=1");
putenv(lpsz(a));
}
Function lpsz(a) converts array to zero terminated string. This function use static circular buffers for coping array elements to it and appending '\0' symbols at the end.
In C++ arrays are implemented by following template C++ classes:
array<low_bound, high_bound, type>
conf_array<type>
matrix<low_1, high_1, low_2, high_2, type>
conf_matrix<type>
See Passing in Parameters in C++ for more details about passing of array parameters.
Methods of classes array, conf_array, matrix, conf_matrix performs bounds checking by means of assert() statement. If, for example, array index is out of bounds, assertion will fail and program will be abnormally terminated. To avoid asserts overhead you should define NDEBUG macro (pass -DNDEBUG options to the C++ compiler).
Sets
By default converter use single set type for all Pascal sets. This set type can handle sets with card up to 256 elements (consequently size of set is 256/8 = 32 bytes). Following functions implement Pascal operations with sets:
boolean subset(set a, set b); /* if a is subset of b */
boolean inset(SetElemType elem, set s);
boolean equivalent(set a, set b);
set join(set a, set b);
set difference(set a, set b);
set intersect(set a, set b);
There is a special constructor for set constants: setof(). This function takes varying number of arguments each of them is either set element or range of elements, defined by macro range(a,b). List of elements should be terminated with eos (end of set) constant:
s := [' ', '!', '?', '_', '0'..'9', 'a'..'z'];
==>
s = setof(' ', '!', '?', '_', range('0','9'), range('a','z'), eos);
In C++ work with Pascal sets is encapsulated in set_template class. Instantiation of this class with constant MAX_SET_CARD as parameter is used as standard Pascal set. Sets of enumerations are created by set_of_enum(e) macro:
typedef set_template<MAX_SET_CARD> set;
#define set_of_enum(e) set_template<last_##e>
Mathematical functions
This is one to one correspondence between Pascal and C mathematical functions:
Pascal |
C |
| Sin | sin |
| Cos | cos |
| Tan | tan |
| Arctan | atan |
| Ln | log |
| Sqrt | sqrt |
Functions trunc(), pred(), succ(), bitsize(), odd(), chr(), ord() are implemented by macros in the following way:
#define trunc(x) ((integer)(x))
#define pred(type,x) ((type)((x) - 1))
#define succ(type,x) ((type)((x) + 1))
#define bitsize(x) (sizeof(x)*8)
#define odd(x) ((x) & 1)
#define chr(n) ((char)(n))
#define ord(c) ((int)(unsigned char)(c))
Round function is implemented as
trunc(x+0.5) x >= 0
round(x) =
trunc(x-0.5) x < 0
Pascal function size(x) is replaced with C operator sizeof(x).
Input/Output
We use standard C io library to emulate Pascal input/output. Pascal file type is emulated by C macro file(r) which defines structure containing file descriptor and current record. There are following fields in file descriptor: pointer to C FILE structure, file name, last IO-operation error status, open mode, file status (and FAB pointer for OpenVMS system). Special macros are used for all Pascal file accessing procedures which scatter file structure fields and call corresponding functions. Bellow there is a table specifying mapping between Pascal functions, macros and C functions:
Mapping between Pascal and C I/O functions |
||
Pascal |
C macro |
C function |
| Rewrite | rewrite | pio_rewrite_file |
| Reset | reset | pio_reset_file |
| Get | get | pio_get_record |
| Put | put | pio_put_record |
| Eof | eof | pio_check_end_of_file |
| Read | sread | pio_read_record (1) |
| Write | swrite | pio_write_record (1) |
| Close | close | pio_close |
| Seek | seek | pio_seek_file |
| Rename | rename | pio_rename_file |
| Break | flush | pio_flush_file (2) |
| Delete | delete_file | pio_delete_file (2) |
| Iostatus | iostatus | pio_iostatus (2) |
| ioerror | ioerror | pio_ioerror (2) |
| noioerror | noioerror | pio_ignore_error (2) |
| - | scopy | pio_copy_record (3) |
| - | access | pio_access_recprd(3) |
| - | store | pio_store_record (3) |
Let the following variable are defined:
type rec = record ... end;
var f, g : file of rec;
r : rec;
Then translation translation for the following construction will be:
r := f^;
f^ := r;
f^ := g^;
==>
r = *access(f);
store(f, r);
scopy(f, g);
We use lazy evaluation strategy for implementing Pascal file access. Current record is red from disc only when it is accessed. To handle current state of current record two flags are used: fs_record_defined and fs_next_pos. First flag is used to mark record which is either red from disk or was assigned a value. Flag fs_next_pos is set when pointer in file is moved to position after current record. There is the following invariant: flag fs_next_pos is set only when flag fs_record_defined is set. A table below shows state of this flags after execution of some functions:
Flags settings |
||
function |
fs_next_pos |
fs_record_defined |
| pio_rewrite_file | 0 | 0 |
| pio_reset_file | 0 | 0 |
| pio_get_record | 0 | 0 |
| pio_put_record | 0 | 0 |
| pio_read_record | 0 | 0 |
| pio_write_record | 0 | 0 |
| pio_seek_file | 0 | 0 |
| pio_copy_record | 0 | 0 |
| pio_access_record | 1 | 1 |
| pio_store_record | 0 | 1 |
Pascal write and read procedures working with text files are replaced with twrite and tread C functions when first parameter is file and cwrite and cread when console input/output is used. These functions receive printf-like format string and varying number of arguments. Format string can include arbitrary text and format specifiers:
Format specifiers for read and write operations |
||
type |
read |
write |
| integer | %i | %i %<width>i |
| real | %f | %f %<width>f %<width>.<precision>f |
| char[] (array of char) |
%s | %s %<width>s |
| char* const ok := 'ok' (zero terminated string) |
- | %z %<width>z |
| short unsigned short [-32768..32767] [0..65535] |
%W | - |
| char unsigned char [-128..127] [0..255] |
%B | - |
where <width> and <precision> is either literal specified in format string either symbol '*'. In former case value of this qualifiers is specified AFTER (unlike C) corresponding parameter:
write(x:5:2); ==> cwrite("%5.2f",x);
write(x:w:p); ==> cwrite("%*.*f",x,w,p);
For symbols not preceding with % action performed by read and write are:
read |
write |
| if character is '\n' then skip all
input character until newline character is reached; otherwise character is compared with next input character and if not equal read operation fails |
just output character |
To output % character this symbol should be included in the format string twice.
Procedures writeln() and readln() are implemented by the same functions but symbol '\n' is appended to the formating string.
Array arguments are passed to read and write function with low and high bound specified before array pointer:
procedure foo(str : packed array [1..100] of char);
begin
writeln('str = ',str);
end;
==>
void foo(char str[100]) {
writec("str = %s\n", 1, 100, str);
}
In C++ template class file<type> is used as wrapper for these C pio_ functions. PtoC uses streamio like interface for converting Pascal IO operation in C++:
type
rec = record
code : integer;
name : array [1..10] of char;
end;
const
pi = 3.14;
var
f : file of rec;
r : rec;
begin
writeln('Hello world');
writeln('pi = ', pi:10:5);
write(f, r);
read(f, r);
r := f^;
f^ := r;
readln;
end.
==>
struct rec {
integer code;
array<1,10,char> name;
};
const real pi = 3.14;
file f;
rec r;
main()
{
output << "Hello world" << NL;
output << "pi = " << format(pi, 10, 5) << NL;
f << r;
f >> r;
r = *f;
store(f, r);
input >> NL;
return EXOT_SUCCESS;
}
The following table summirize rules of translation Pascal IO constructions to C++:
Pascal construction |
C++ construction |
| write(expr1, expr2, ..., exprN) writeln(expr1, ..., exprN) write(f, expr1, ..., exprN) writeln(f, expr1, ..., exprN) |
output << expr1 << expr2
<< ... << exprN; output << expr1 << ... << exprN << NL; f << expr1 << ... << exprN; f << expr1 << ... << exprN << NL; |
| read(lvalue1, lvalue2, ..., lvalueN) readln(lvalue1, ..., lvalueN) read(f, lvalue1, ..., lvalueN) readln(f, lvalue1, ..., lvalueN) |
input >> lvalue1 >> lvalue2
>> ... >> lvalueN; input >> lvalue1 >> ... >> lvalueN >> NL; f >> lvalue1 >> ... >> lvalueN; f >> lvalue1 >> ... >> lvalueN >> NL; |
| write(string_expr:width) write(integer_expr:width) write(real_expr:width:precision) |
output << format(string_expr,
width); output << format(integer_expr, width); output << format(real_expr, width, precision); |
| file_variable^ | *file_variable |
| file_variable^ := expr | store(file_variable, expr); |
Nested functions and call graph analysis
Converter can perform global call graph analyze in order to recognize non-recursive functions and making static variables of such functions which are accessed by nested functions. If you specify -analyze option, converter appends to file "call.grp" information about callers and callees. After conversion of all files special utility cganal can be used to produced transitive closure of call graph and output list of recursive procedures in file "recur.prc". When you run converter once again (with -analyze option) information from this file is used to mark recursive procedures. This approach greatly increases readability of program as no extra arguments need to be passed to nested functions.
Resolving name conflicts
Resolving of names conflicts is controlled by file ptoc.cfg which is loaded by converter at startup. This file specifies reserved symbols (C and C++ keywords), names of functions from C standard library, names of macros defined by converter, and mapping of names for some functions from pascal runtime.
Passing parameters in C
When converter produces C code, it doesn't copy arrays which are passed by value. Instead of this converter declare such arrays as const, so any attempt to modify contents of such array cause C compiler warning or error. It seems to me, that there are usually few places in program where procedure modifies array which is passed by value. As a rule absence of VAR qualifier means that procedure only access but not modify contents of the array. So we decide that efficient generation of this most common is more important then some amount of manual job which is necessary to correct places where array has to be copied. You should only rename formal parameter, create local variable with original name and copy value to it:
foo(str20 const name) {
...
}
=>
foo(str20 name_) {
str20 name;
memcpy(name, name_, sizeof(name));
...
}
Passing parameters in C++
As far as Pascal arrays are represented in C++ by special class, there is no problem with array parameter passing as in C. But as far as arrays passed by value are very rarely modified in called procedure, PtoC optimizes passing of arrays by value. If array parameter, passed by value, is not changed in procedure and is not passed by reference to another procedure, then PtoC doesn't create copy of the array. This optimization can be suppressed by -copy option. When this option is specified, PtoC strictly emulates Pascal call semantic for arrays passed by value (always create copy of the array). I don't know reasons of using this options.
PtoC implements conformant array by template class conf_array. PtoC uses special macro copy_conformant_array() to create copy of conformant array passed by value when this array is modified within procedure or option -copy is specified. This macro uses alloca() function from C library, which allocates space from the system stack.
PtoC uses macro as(type, string-constant) for passing string constant to the parameter of array of char type. As far as PtoC wants to make it possible to initialize arrays by means of C aggregate construction {...}, it is impossible for array class to have constructor. That is why macro as(), which calls method array::make(char const* str), is used for passing string constant as parameter. If size of string constant is less than size of target parameter, then string is padded with spaces. If size of string constant is greater than size of target parameter, then string is truncated to the size of parameter.
Calling of C functions
Sometimes C functions need to be called from Pascal code. Sun Pascal has special "EXTERNAL C" qualifier for C procedures called from Pascal. PtoC recognize this qualifier and treat it as C (not C++) function declaration. There are some specific items of conversion of declarations and calls of such functions:
The following examples illustrate these items:
type smallstr = array [1..64] of char;
procedure foo(var x : integer; a : smallstr; b : zero_terminated_string);
external c;
var
i : integer;
s : smallstr;
begin
foo(i, s, s);
foo(i, 'abc', 'xyz');
end.
----------------------------------------------
typedef array<1,64,char> smallstr;
extern "C" void foo(integer* x, char* a, char* b);
int main()
{
integer i;
smallstr s;
foo(&i, s.body(), lpsz(s));
foo(&i, "abc", "xyz");
return EXIT_SUCCESS;
}
Array assignments
Some C++ compilers don't allow classes with any assignment operators to be members of unions (for correct implementation it is only necessary that such classes should not redefine DEFAULT assignment operator). As far as arrays can be members of variant components in Pascal, converter can generate code without using of assignment operator for string and character constants. If your specify -assign option, converter will use assign(str) method of array instead of operator = for assignment of string and character constants to array. But PtoC still use default operator= generated by compiler for assignment of one array to another. To compile code produced with -assign option, pass -DNO_ARRAY_ASSIGN_OPERATOR option to C++ compiler.
PtoC translates assignment of string constant to variable of fixed array type by means of as(type, string-constant) macro, which performs conversion of string constant to the type of destination variable. If size of string constant is less than the size of the destination variable, then string is padded with spaces. If size of string constant is greater than size of the destination variable, then the string is truncated to the size of destination variable,
Conversion of Turbo Pascal strings
For conversion of Turbo Pascal classes string and varying_string were designed to represent correspondent Pascal types. This classes have constructors and assignment operators and so they can not be initialized using C aggregates notation {} and can not be used as components of unions (GCC allows to initialize classes with constructors with {}). To avoid this problem either manually replace such places with C arrays or use option -cstring of compiler. When this option is set converter replaces type of string component of records or arrays with C pointer to zero terminated string const char*. This approach works well only when this types are used to declare constants.
Some porting problems
Representation of integer type
When your are porting application from 16-bit architecture platform you may want to preserve integer size (2 bytes). In this case you can face with two problems: one is that pointers will not more fit into such integers. Converter can't help your in this case. You should change types of some variables and records fields. And second problem is less obvious. In language C short and char operands are converted to int type before operation takes place. So if you you compare for equality variables of signed and unsigned type declared in Pascal as
type
word : -32768..32767
uword : 0..65535
var
v1 : word;
v2 : uword;
containing the same value (for example 40000) then result will be false (unlike original application) ! This is because variable with signed type will be converted to integer with sign extension while variable with unsigned type - without sign extension. To help to deal with this problem converter provides option -unsigned, which force converter to insert explicit type conversion in such operations. Lets look at the translation the following Pascal construction with and without -unsigned option:
Pascal |
C without -unsigned |
C with -unsigned |
| if v1 = v2 then ... | if (v1 == v2) ... | if ((uword)v1 == v2) ... |
It is not recommended to use 2-byte C types for representing INTEGER and WORD Turbo Pascal types, because structures and functions of BGI emulation library deal with original C int type. Also I see no much sense in using short types for converted Turbo Pascal applications because in this case you can not receive benefits of 32-bit architecture, it is better to run original application.
Implementation of Pascal sets
Sometimes it is necessary to preserve original size of data structure. For example if structure is mapped to another structure by means of union (record with variants in Pascal) or is extracted from file. There are two options in converter which can help you in this case. First option is -intset, which order converter to generate short sets (2 or 4 bytes) for sets of enumeration types, Operations with short sets are implemented by macros using bit arithmetic. (so they are significantly faster than operations with universal sets). Disadvantage of using short sets is that adding elements to enumeration may cause problems in future.
Enumeration types
And another option is -smallenum. The problem is that enum type in C is treated by many compilers as integers and there are no ways to make compiler use less bytes for their representation. When you specify option -smallenum converter replaces original enumeration type definition with unsigned char or unsigned short definitions according to number of elements in enumeration. So construction
colors = (red, green, blue);
will be translated to
typedef unsigned char colors;
enum {red, green, blue};
Size of colors type will be 1. And without -smallenum size of colors type depends on compiler and usually will be equal to the size of integer (4):
enum colors {red, green, blue};
Style of converted sources
As was mentioned above converter tries to preserve original indentation of converted sources. But if Pascal sources are not properly aligned you can reformat produced C code using some indentation utility, for example GNU indent, which is freely distributed (GNU indent has one interesting bug: it fails to work with files with empty comments /**/ which are produced from popular Pascal {} comments. To avoid this problem just replace such comments with /* */ or something else).
Includes in ANSII Pascal
PtoC supports %include operator (used in Oregon Pascal). This operator works like C #include directive, performing text substitution. This operator can be used in one of the following forms:
%include file { "file.pas" will be included }
%include '../../file.inc' { "../../file.inc" will be included }
%include file.con ; { "file.con" will be included }
The statements above will be converted to
#include "file.h"
#include "../../file_inc.h"
#include "file_con.h"
If several files include the file with variables declarations, then these variables will be multiple defined. This problem can be solved either by passing option to linker to merge symbols with the name (most linkers have such option), either by using -extern option. Specifying -extern option tells the converter to prepend each variable declaration in included file by EXTERN qualifier. EXTERN is defined as extern in ptoc.h and is redefined to "" (empty string) if:
PtoC options
-I [.]
-in
-out
-suf [.cxx]
-c
-assign
Array assignments.
-analyze
-intset
-init
-smallenum
-unsigned
-h
-turbo
-cstring
-nological
-extern
-preserve
-nested
-copy
-pascall []
-comment_tags
-namespace
unit foo;
interface
var
a : integer;
implementation
end.
---------------------------------------------
namespace foo {
integer a;
}
unsing namespace foo;
Known bugs
PtoC distribution
PtoC is shareware and is distributed in the hope to be useful. You are free to use this converter, modify the sources and do with this converter everything else you want.. Shareware status doesn't mean lack of support. I will do my best to fix all reported bugs and will help you to tune PtoC to fit your requirements. Also e-mail support is guaranteed.