Chapter 3 THE SIMPLE MODULA-2 DATA TYPES TYPES ARE IMPORTANT ______________________________________________________________ The material in this chapter is extremely important to you as you strive to become a good Modula-2 programmer, but you may also find it to be somewhat tedious because it contains so many facts. This material is needed in order to develop the topics in the next few chapters, but all of the details are not necessarily required. For that reason, you may wish to go through it rather rapidly picking up the high points and come back to this chapter for the details later when they will be much more meaningful. Do not completely pass over this material at this time or the next few chapters will be meaningless. If you are already highly experienced in other programming languages, you may be able to survive the next few chapters without this material, but keep in mind that Modula- 2 has a few unique constructs. The most important basic principle in this chapter is the introduction of the data type. The type of a variable defines a range of values the variable can have assigned to it and it also defines a set of operations that are available for use with the variable. By the proper application of data typing, the system checks your usage of values to see that you use them in a reasonable manner. MODULA-2 IS CHANGING ______________________________________________________________ Because Modula-2 is changing based on experience with use, some of these programs may not compile correctly. Every effort was made to make the example programs as error free as possible but due to slight changes in the language, you may have a few compilation problems. We will say more about this after we develop a few topics so we can talk more intelligently about it. Of course, due to slight variations in compilers you may have a few problems also. Modula-2 is a well designed and defined language, but there is room for slight differences in compilers and the way they interpret various constructs. A PROGRAM WITH VARIABLES ______________________________________________________________ The program named INTVAR.MOD is our first program with some variables in use. This program begins in the usual way since it has a module header and the import list. Next we come to 3-1 Chapter 3 - The Simple Data Types a new reserved word, VAR. This word is ================ used to indicate to the compiler that we INTVAR.MOD wish to define one or more variables. In ================ Modula-2, there is a rule that says you can use nothing until it is defined. If we wish to use a variable in the program, we must first define that it will exist, and what kind of a variable it is, or to be more specific, what type it is. After that, it can be used in the program to do what needs to be done. Following the reserved word VAR in line 16, we have the variable named Count defined. The reserved word INTEGER following the colon states that the variable named Count will be of type INTEGER. This means that it can store any whole number between -32768 to 32767 on most microcomputers, and a considerably larger range on larger computers. Don't worry too much about this yet, the next program will completely define what an integer type variable is. It is important to recognize that after we have defined the variable named Count, it still doesn't have a value stored in it, that will come later. Line 7 has two more variables defined, namely x, and y. Once the reserved word VAR is mentioned, as many variables as desired can be defined. They are also integer type variables and do not have a value stored in them yet. You can think of the three variables as three empty boxes, each capable of storing a number but with no number in them yet. It would be perfectly permissible to put all three variables on one line, or to have separated them such that each was on a separate line. At this point, the program doesn't know that there is any difference between them, because there isn't any. The fact that one will contain the sum of the other two has no meaning yet, the comments are only for us, not the computer. Note that Modula-2 gives you no way to initialize a variable when it is declared. This is permissible in many other languages. USING VARIABLES IN A PROGRAM ______________________________________________________________ Now we will examine the program itself. Line 11 sets the variable named x to the value 12, in effect putting the number 12 in the box mentioned earlier. The sign := is the Modula-2 symbol for assignment. It is most meaningful to read the symbol "gets the value of" since it is not really stating a mathematical equality but is saying in effect, "assign the value of this to the variable at the left." The entire line can be read as "x gets the value of 12." There is now a value assigned to the variable x declared in the header. The next statement assigns the value of 13 to the variable named y. Finally the value of the data stored in the variable named x 3-2 Chapter 3 - The Simple Data Types is added to the value of the data stored in the variable named y, and the sum is stored in the variable named Count. We have therefore done our first calculation in Modula-2, but we will do many more before this tutorial is completed. The observant student will notice that each statement is terminated with a semicolon, a Modula-2 requirement. The three variables are then displayed on the monitor with appropriate prose to identify them in lines 17 through 25. The only new statement here is the WriteInt procedure that needs a little explanation. This procedure is used to output an integer type variable to the monitor or whatever device is being used. By definition, it contains two quantities within the parentheses, the first being the variable name and the second being the number of columns the output should fill. If there are not enough columns specified to output the data, more will be used so that no digits will be truncated. If all are not needed, leading blanks will be output. If the variable named x had the value of 1234 when we came to program line 18, all four digits would be output in spite of the request for three. Since the variable x has the value of 12, only two columns will be used and one leading blank will be output. In like manner, the value of the variable named y is allotted 4 columns and the value of Count is to be output in 6 columns. Lines 27 and 28 of the program assign new values to two of the variables. The variable x is assigned the value of FF hexadecimal which is 255 decimal, and y is assigned the value of 177 octal which is 127 decimal. Note that the hexadecimal number must have a leading digit. In this case, a zero had to be prepended. This is only done as an illustration to you of how it is done. If you don't understand these two numbering systems, simply ignore this until you have a need for it. Compile and run this program to see if it does what you expect it to do. The important thing to notice in this program is the variable definition in the definition part of the module and the variable assignment in the program part. It should be obvious, but it would be well to mention that the definition part of the module extends from the module name to the reserved word BEGIN and is where all definitions are put. Likewise, the program part of the module includes all statements from the begin statement to the end statement. SIMPLE VARIABLE TYPES ______________________________________________________________ Modula-2 has several predefined data types that you can use in your programs. You also have the ability to define any number of complex types built up from the simple types but we will not discuss this until we get to chapter 6 of this 3-3 Chapter 3 - The Simple Data Types tutorial. The simple types are INTEGER, CARDINAL, REAL, BOOLEAN, and CHAR. Each has its own purpose and its own peculiarities and we will cover each type one at a time. A few other types may be available with your particular compiler, but they are not universal, so you must check your documentation to see if they are available with your system. The types LONGINT, LONGCARD, and LONGREAL are additional types that represent long integer, long cardinal, and long real types respectively. In each case more storage is required, but numbers covering a larger range can be stored in variables of these types. After you understand the basic types, these new types will be easy for you to understand, so they will not be illustrated in this tutorial. THE SIMPLE VARIABLE - INTEGER ______________________________________________________________ Examine the program named INTMATH.MOD for =============== a few examples of integer math operations. INTMATH.MOD In the declaration part of the program =============== (the part prior to the BEGIN) we have 7 integer type variables defined for use in the program which we will use to illustrate integer arithmetic in the Modula-2 programming language. An integer variable, can store any whole number between -32768 and 32767 on most microcomputers. An attempt to store any other value in an integer type variable should produce an error by your compiler but it may produce some other result. Some compilers may store a -32769, which is one count too low, as a 32767 which is at the top end of the range. This is due to the two's complement arithmetic that you don't need to understand at this point. It will be left to you to determine what your compiler does in such a case. Line 10 of this program is nothing new to you, it simply assigns the variable named A the value of 9. Line 11 adds 4 to the value stored in the variable A and the result, 13, is stored in the variable named B. Next, the values stored in the variables named A and B are added together and the sum, which is 13, is stored in the variable named IntSum. Continuing in the same manner, the difference and the product are calculated and stored. When we come to integer division, we are breaking new ground because the result is truncated to the largest whole number resulting from the division. Thus 13 DIV 9 results in 1 because the remainder is simply dropped. The next construct, B MOD A results in the remainder of the division, which is 4 in this case. Note that the modulo (MOD) operator can only be used with a positive denominator in Modula-2. Any attempt to use the modulo operator with a negative denominator will result in a fatal run time error. 3-4 Chapter 3 - The simple Data Types You will find these operations very useful as you progress as a Modula-2 programmer. The intent of the next line is to illustrate that you can do several math operations in a statement if you are careful to put the parentheses in the proper places. The rules about operator precedence are similar to those for other programming languages, and they are well defined, but I recommend that you use lots of parentheses to remove all doubt as to what the results will be. THE INC AND DEC OPERATIONS ______________________________________________________________ The results of the operations are each displayed in 6 columns and we move on to several new operations. The first new operation is the INC which is short for increment. This simply increments the variable contained within the parentheses and if a second argument is given, the variable is incremented by the value of that variable. In like manner, the DEC procedure decrements the variable in the parentheses by one unless a second argument is given in which case the variable is decremented by the value of that variable. It may not be clear at this point, but the second variable itself may be another variable name or even a composite of several as long as it results in an integer type variable. This is illustrated in the program in line 33. THE MIN AND MAX OPERATIONS ______________________________________________________________ Finally, we come to the last two procedures in this program, the MIN and the MAX. These two function procedures are built into the language and therefore do not need to be imported. They are properly called function procedures because each returns a value, but it is still proper to refer to them simply as procedures. These two procedures will return the value of the smallest possible integer, -32768 and the largest possible integer, 32767 for most microcomputers. These are the values returned for a 16 bit computer. It is possible that you are using a computer and compiler combination that uses more than 16 bits. If this is the case, MIN and MAX will return larger numbers. Compile and execute this program and observe the output. If your compiler results in errors, you may have to make some changes in order to compile it. Some of the more popular Modula-2 compilers do not implement the MIN and MAX procedures, so you may need to remove or comment out lines 35 and 36 of this program prior to compilation. The min and max 3-5 Chapter 3 - The Simple Data Types procedures are relatively new additions to Modula-2 which explains why they are not yet available with all compilers. THE SIMPLE VARIABLE - CARDINAL ______________________________________________________________ Examine the file named CARDMATH.MOD for a ================ few examples of cardinal mathematics and CARDMATH.MOD output. In this file, 7 variables are ================ defined as type CARDINAL and one more as type INTEGER. A cardinal variable can store any whole number from 0 to 65535 in a 16 bit microcomputer, although the range may be different if you are using a computer and compiler combination that uses more bits. The first few lines are the same as the last program so very little needs to be said about them except for the subtraction example. In this case, the result of the subtraction would be negative if it were carried out as in the last program so A is subtracted from B. It is an error to attempt to store a negative number in a cardinal type variable. For that reason, a cardinal should not be used if there is any chance that it will be required to go negative. Programming experience will be the best teacher when it comes to deciding what variable types to use in each situation. In this program the variables are once again displayed, but now the procedure named WriteCard is used for output because the variables to be output are of the cardinal type. The next two statements indicate that integer and cardinal variables are "assignment compatible", meaning that they can be assigned to each other with the := operator. They cannot however, be mixed in calculations. Constants in an expression are assumed to be of the same type as the variables in the expression and they must agree. For that reason, the expression in line 36 is invalid because (-112) is a negative constant and therefore not cardinal. In the prior line it is permissible to subtract the positive number 112 from the value of A as long as the result is still positive. As an exercise, change line 34 such that a number less than 112 is assigned to the variable named A. The program will compile without error but when you run it, you should get a runtime error because the cardinal assignment is out of range. Notice that the constant value of -112 is permissible for use as an integer constant. The remaining statements in the program are the same as the last program so additional explanation is unneeded. It would be good to point out that in the case of cardinal, the MIN and MAX procedures will return values of 0 and 65535 for most 16 bit implementations. Computers using additional bits will result in a much larger value for MAX. Compile and run this 3-6 Chapter 3 - The Simple Data Types program remembering that it may be necessary to comment out the MIN and MAX statements to get a successful compilation, since they may not be available with your compiler. HOW NEW IS YOUR COMPILER? ______________________________________________________________ Modula-2, as mentioned near the beginning of this chapter, is a dynamic language that is changing as the base of experience dictates weaknesses. This is true of any good modern programming language. As originally defined, Modula-2 used the cardinal type for most operations, assuming cardinal if the type was not specifically stated and if the type could be either integer or cardinal. In his Fourth edition of "Programming in Modula-2", Niklaus Wirth has changed this to favor the integer in some situations. The language will now be more similar to a few other modern languages, many of which do not have a cardinal representation. This change makes this tutorial difficult to write, since all compilers will not comply with this change for several years. I will try to point out the places where the changes are taking place, but I will not be able to find them all. You may be required to do a little searching in your compiler documentation for some of the changes as they come up in the example programs. THE SIMPLE VARIABLE - REAL ______________________________________________________________ Examine the program named REALMATH.MOD for ================ a demonstration of the use of the data REALMATH.MOD type REAL. The definition part of this ================ program is similar to the last with some additions to the import list. Your compiler may use different names for some of the procedures here, so if you get a compile error you will need to modify these. We will study the import (and export) list in detail later, so be patient. Several real variables and one each of the integer and cardinal types are defined for use in the program. The real type variable can contain numbers in a wide range and with fractional parts included. The exact range, and the accuracy will vary widely depending on your implementation. It will be up to you to check your reference manual for the limits on your computer and compiler. A real type number is defined as one with a decimal point, and in Modula-2, there must be at least one digit prior to the decimal point, but none are required following it. The mathematics are the same as with the other two except that the division symbol is the slash (/). There is no MOD for real type numbers because there is 3-7 Chapter 3 - The Simple Data Types theoretically no remainder, since a fractional part is computed as part of the calculation. The four results are displayed on the monitor with 12 columns allowed for each result and two extra blanks displayed between each number. Unfortunately, we have no control over how many digits will be displayed following the decimal point. This would be nice for outputting data in a financial model where we would like to have two digits following the decimal point. When we get to the advanced part of this tutorial, we will write our own procedure for outputting real values in such a way that we can call it from any program just like we call these output procedures. CONVERSION BETWEEN DATA TYPES ______________________________________________________________ Beginning in line 33, we assign the integer and cardinal variables some values and convert the values to type real by using the procedure named FLOAT. We then convert the variable Sum to integer and cardinal by use of the procedure named TRUNC. The fractional part, if any, will simply be thrown away. These procedures will be very useful in many of your programs. Lines 40 and 41 once again use the MIN and MAX functions to return the value of the largest positive real number and the smallest positive real number for your implementation. Once again, your compiler may not support these two functions and they may have to be commented out in order to compile. Be sure to compile and execute this program. THE REAL MATHEMATICS LIBRARY ______________________________________________________________ Besides the standard arithmetic ================ operations, Modula-2 provides a library of REALTRIG.MOD standard geometric and trigonometric ================ functions in the library module MathLib0. These must be imported in order to use them as illustrated in the example program REALTRIG.MOD which you should examine at this time. The program itself should be self explanatory with the exception of lines 24 and 25 which illustrate the real and entier functions which convert data between the real and integer types. The word entier is French for integer. Note that if the real value will not fit into the integer range of values, the result is undefined by the definition of Modula- 2, but your compiler documentation should define the result in this case. 3-8 Chapter 3 - The Simple Data Types THE SIMPLE VARIABLE - BOOLEAN ______________________________________________________________ Examine the file named BOOLMATH.MOD for an ================ example of boolean variables and some BOOLMATH.MOD boolean operations. A boolean variable ================ can only have one of two possible values, TRUE or FALSE. The values TRUE and FALSE are standard identifiers which are defined by the Modula-2 system and available for your use. These variables cannot be printed directly but can be used to control other print statements to print out a representation of their value. We will see how later. We define 3 boolean variables and 3 integer variables and assign values to the 3 integer variables in the program for use in these illustrations. In line 13 the boolean expression "A = 22" is true, therefore the boolean variable named IsIt is assigned the value TRUE. The variable IsIt could be used later in the program to make a decision, by a yet undefined method, to do something or bypass it. In like manner, the next statement assigns IsIt the value FALSE because A is not equal to 23. The remainder of the allowed boolean expressions are defined in the next few lines and are left for your inspection and study. Lines 20 and 21 illustrate forms of the "not equal" operator. According to the latest definition of Modula-2, the <> is no longer legal for use and must be changed to the # form. You may need to comment out line 21 to compile this program. Beginning in line 25, composite boolean expressions are illustrated. As many boolean expressions as desired can be combined with AND and OR operators. If two or more boolean expressions are combined with the AND, the result is true if all expressions are true. If two or more boolean expressions are combined with the OR, the result is true if any of them are true. The NOT operator inverts the sense of what it modifies, it turns a TRUE to FALSE and vice-versa. Finally, a couple of composite boolean expressions are given for illustration of the amount of complexity that is allowed, although there is no real limit as to how far you can go with the complexity. Good programming practice would dictate that you keep it simple and understandable. The ampersand (&) can be used in place of the AND operator, and the tilde (~) can be used in place of the OR operator, but the use of the words is considerably clearer. Their use is illustrated in lines 35 and 36. 3-9 Chapter 3 - The Simple Data Types TWO RULES FOR BOOLEAN EVALUATION ______________________________________________________________ First it is important that you use the same type of variables within a boolean expression. Real type variables can be compared to other reals and integers to integers, but reals cannot be compared to integers. Cardinal and char types can also be compared to their own types, but none of the four can be compared directly to each other. Secondly, Modula-2 uses a short circuit evaluation technique for boolean evaluation. Evaluation proceeds from left to right and if it finds a result which will positively determine the outcome, evaluation stops. For example, if it is evaluating a string of 5 comparisons all combined with an AND, and it finds that the second term is false, evaluation stops there. Since all terms must be true for the result to be true, it makes no difference what values the last three are, the result will be false because of the second term. Be sure to compile and execute this program even though it has no output. You should verify for your own information that this program will compile and execute correctly with your system. THE SIMPLE VARIABLE - CHAR ______________________________________________________________ Examine the program named CHARDEMO.MOD for ================ an illustration of use of the last simple CHARDEMO.MOD variable type, CHAR. Text data is stored ================ in a computer in a format utilizing the char data type. Although there are exceptions, such as when text is stored in some form of a packed mode, this is nearly always true. This tutorial was written with a word processor that uses a char type for text storage, and few word processors use any other method. Although there are many different ways to store text, only two are used to any level of significance, EBCDIC and ASCII. This merely refers to the way the characters of the alphabet and all other characters are represented in the computer. The ASCII standard defines that the value of 65 will be the letter A, 66 will be the letter B, etc. If everyone uses the same standard, transfer of data from one computer to another is greatly simplified. The program named CHARDEMO.MOD has the usual header, imports the procedure named Write for use in displaying the char type data, then defines 4 char type variables for use in the program. An integer type variable is also defined. In the program itself, we begin in lines 11 and 12 by assigning two of the variables some char data. Since a char variable is 3-10 Chapter 3 - THe Simple Data Types capable of storing one letter, numeral, or special character, each variable is assigned one letter. The single or double quotes are used as an indication to the compiler that you intend for it to use the letter as a char type variable rather than as another variable name. Of course if you wanted to use A as a variable name, you would have to define it in the definition part of the module. TWO SPECIAL CHAR PROCEDURES ______________________________________________________________ The next instruction gets the ordinal value of the letter A, adds 2 to it, and assigns that value to the variable named Index, which must be an integer (although it could have been defined as a cardinal type variable). Refer to the documentation that came with your computer and you will find an ASCII table that will define the letter A as 65. Finally, the char type variable Dog3 is assigned the character value of Index. Your ASCII table should define 67 as the letter C. It is important to understand that the char variable Dog3 contains the character representation of the letter C, and the integer variable Index contains the numerical value of the ASCII representation of the letter C. It would be perfectly alright to use the variable Index for any desired numerical calculations, but not to display the letter C. On the other hand, it would be alright to use the variable Dog3 to display the letter C on the monitor but it could not be used for any calculations. The purpose therefore, of the two procedures named ORD and CHR, is to translate from one type to the other. The variable Cat4 is assigned the double quote by enclosing it in the single quotes, and the characters are output in a funny order to spell "CAT". The variable Char1 is assigned the value of S, and the word is completed resulting in the full word "CATS" on the monitor after the program is compiled and run. If this were the only way to use the char type variable, it would be very tedious and frustrating, but there are other methods to use the char type that are far more useful as you will see later in this tutorial. Next, an additional means of assigning a char type variable is given. By assigning the char variable the value 65C, it is the same as writing CHR(65), resulting in the variable having the internal value A. A number less than 256 followed by a C is defined by Modula-2 as a char type constant. Finally, the variable Char1 is assigned the letter a and it is converted to upper case A with the procedure named CAP. This procedure will convert the argument to its upper case equivalent if it is a lower case letter. If its argument is an upper case letter or any other character, it will do nothing to it. Be sure to compile and execute CHARDEMO.MOD and observe the output. 3-11 Chapter 3 - The Simple Data Types One final point should be made about the char type variable. There are no arithmetic operations that can be done on a char type variable. To increment a char type variable for example, it must be converted to a numerical type, incremented, then converted back to char. Note that the actual increment is performed on the numerical type, not the char type. USING THE TRANSFER PROCEDURES ______________________________________________________________ Examine the file named TRANSFER.MOD for ================ several examples of transferring data TRANSFER.MOD between the various simple data types. ================ The transfer functions given here may not seem too important at this time, but some time spent here will help to reduce the frustration later when you get seemingly wrong errors that say you are using incompatible types in a statement. All of the program will not be discussed, only those statements that use some of the more unusual capabilities of Modula-2. In line 13, the calculations are done in integer format, but due to the assignment compatibility of integer and cardinal, the result is converted to cardinal across the assignment operator. In lines 14 and 15, the integer type Int1 is converted to cardinal and the mathematics are completed in the cardinal type. This is an example of type coercion, which will be explained in a couple of paragraphs. Line 16 is an illustration of mixed mathematics using the transfer procedure INTEGER. Line 20 is the first example of nested transfer procedures which must be done because FLOAT can only be used with cardinal for an argument. (Keep in mind that Modula-2 is changing such that integer is the preferred type, and some of this may change.) The expression in line 22 is an error because the procedure named TRUNC results in a cardinal which cannot be added to an integer type variable. Either of the next two lines fix the problem by making the addition type-compatible then making use of the assignment compatibility between integer and cardinal for line number 23. The same error occurs in line 26 and is fixed the same way in either of the next two lines. Once again, in line 31, the incompatible type error occurs and is fixed in either of two ways in the next two lines. Lines 35 and 36 illustrate converting char data to first cardinal then real which requires nested transfer procedure calls. The last line of the program is a nest of procedures which converts a character from char to cardinal, then to real, back to cardinal, and finally back to the original char variable. It does nothing except act as a good illustration to you of what can be done. 3-12 Chapter 3 - The Simple Data Types TYPE COERCION OR TYPE CONVERSION ______________________________________________________________ All examples in this example program use type conversion except for the INTEGER and CARDINAL conversions, which are actually type coercions. A type conversion is a call to a function which actually modifies the bit pattern to conform to the new type, whereas a type transformation, also called a type coercion, simply copies the bit pattern from one type to the other with no regard for the meaning of the bits. Check your compiler documentation for a complete list of type conversions. The definition of Modula-2 requires that CHR, FLOAT, ORD, TRUNC, and VAL be available. A type transformation requires that the two types involved use the same amount of storage. This implies that you must have some knowledge of the machine representation of the types. The only type transformations you should make at this point are between integer and cardinal types, since it would be difficult for a compiler writer to use a different bit pattern for each of these types. More will be said about this later. Until then, you should use type transformations cautiously, if at all. Conversion between types is very important. You will use these techniques often so it is important to know how they work. A very simple yet helpful memory aid is to remember that any simple type can be converted to cardinal and cardinal can be converted to any type. Most other conversions require two steps to get from one to the other. Remember that the latest update to Modula-2 is changing this. Don't worry about this change too much, since you may not even notice the change as you program. Compiler writers will try to keep updates compatible with older versions and will inform you of any changes. Chapter 14 will readdress this topic with even more extensive type transfer procedures. PROGRAMMING EXERCISES ______________________________________________________________ 1. Write a program in which you define, using the char type variable, the letters "a" and "z", and the numbers "0" and "9". Convert them to cardinal, and display the four characters and their ASCII (numerical) values. 2. Write a program that you can easily modify to experiment with conversions between the various types that result in incorrect conversions to see the results on your compiler. For example, convert a -1 as an integer to a cardinal. 3-13