@database beginner.guide @Master beginner @Width 75 This is the AmigaGuideŽ file beginner.guide, produced by Makeinfo-1.55 from the input file beginner. @NODE "main" "Introduction to Amiga E" @Next "Understanding a Simple Program" @Prev "Contents.guide/main" @Toc "Contents.guide/main" Introduction to Amiga E *********************** To interact with your Amiga you need to speak a language it understands. Luckily, there is a wide choice of such languages, each of which fits a particular need. For instance, BASIC (in most of its flavours) is simple and easy to learn, and so is ideal for beginners. Assembly, on the other hand, requires a lot of effort and is quite tedious, but can produce the fastest programs so is generally used by commercial programmers. These are two extremes and most businesses and colleges use C or Pascal/Modula-2, which try to strike a balance between simplicity and speed. E programs look very much like Pascal or Modula-2 programs, but E is based more closely on C. Anyone familiar with these languages will easily learn E, only really needing to get to grips with E's unique features and those borrowed from other languages. This guide is aimed at people who haven't done much programming and may be too trivial for competent programmers, who should find the `E Reference Manual' more than adequate (although some of the later sections offer different explanations to the `Reference Manual', which may prove useful). Part One (this part) goes through some of the basics of the E language and programming in general. Part Two delves deeper into E, covering the more complex topics and the unique features of E. Part Three goes through a few example programs, which are a bit longer than the examples in the other Parts. Finally, Part Four contains the Appendices, which is where you'll find some other, miscellaneous information. @{" A Simple Program " Link "A Simple Program" } @ENDNODE @NODE "A Simple Program" "A Simple Program" @Toc "main" A Simple Program ================ If you're still reading you're probably desperate to do some programming in E but you don't know how to start. We'll therefore jump straight in the deep end with a small example. You'll need to know two things before we start: how to use a text editor and the Shell/CLI. @{" The code " Link "The code" } @{" Compilation " Link "Compilation" } @{" Execution " Link "Execution" } @ENDNODE @NODE "The code" "The code" @Next "Compilation" @Toc "A Simple Program" The code -------- Enter the following lines of code into a text editor and save it as the file @{b }simple.e@{ub } (taking care to copy each line accurately). (Just type the characters shown, and at the end of each line press the RETURN or ENTER key.) PROC main() WriteF('My first program') ENDPROC Don't try to do anything different to the code, yet: the case of the letters in each word is significant and the funny characters are important. If you're a real beginner you might have difficulty finding the ' character. On my GB keyboard it's on the big key in the top left-hand corner directly below the ESC key. On a US and most European keyboards it's two to the right of the L key, next to the ; key. (If you don't have your keyboard set up properly then you find that keys don't produce the same characters that are printed on them--especially when use use the shift key. In this case it will probably behave like a US keyboard, although you should really fix this and set it up properly--see the manuals that came with your Amiga.) @ENDNODE @NODE "Compilation" "Compilation" @Next "Execution" @Prev "The code" @Toc "A Simple Program" Compilation ----------- Once the file is saved (preferably in the RAM disk, since it's only a small program), you can use the E compiler to turn it into an executable program. All you need is the file @{b }ec@{ub } in your @{b }C:@{ub } directory or somewhere else on your search path (advanced users note: we don't need the @{b }Emodules:@{ub } assignment because we aren't using any modules). Assuming you have this and you have a Shell/CLI running, enter the following at the prompt after changing directory to where you saved your new file: ec simple If all's well you should be greeted, briefly, by the E compiler. If anything went wrong then double-check the contents of the file @{b }simple.e@{ub }, that your CLI is in the same directory as this file, and that the program @{b }ec@{ub } is in your @{b }C:@{ub } directory (or on your search path). @ENDNODE @NODE "Execution" "Execution" @Prev "Compilation" @Toc "A Simple Program" Execution --------- Once everything is working you can run your first program by entering the following at the CLI prompt: simple As a help here's the complete transcript of the whole compilation and execution process (the CLI prompt, below, is the bit of text beginning with @{b }1.@{ub } and ending in @{b }>@{ub }): 1.System3.0:> cd ram: 1.Ram Disk:> ec simple Amiga E Compiler/Assembler/Linker/PP v3.2e registered (c) '91-95 Wouter lexical analysing ... parsing and compiling ... no errors 1.Ram Disk:> simple My first program1.Ram Disk:> Your display should be something similar if it's all worked. Notice how the output from the program runs into the prompt (the last line). We'll fix this soon. @ENDNODE @NODE "Understanding a Simple Program" "Understanding a Simple Program" @Next "Variables and Expressions" @Prev "main" @Toc "Contents.guide/main" Understanding a Simple Program ****************************** To understand the example program we need to understand quite a few things. The observant amongst you will have noticed that all it does is print out a message, and that message was part of a line we wrote in the program. The first thing to do is see how to change this message. @{" Changing the Message " Link "Changing the Message" } @{" Procedures " Link "Procedures" } @{" Parameters " Link "Parameters" } @{" Strings " Link "Strings" } @{" Style Reuse and Readability " Link "Style Reuse and Readability" } @{" The Simple Program " Link "The Simple Program" } @ENDNODE @NODE "Changing the Message" "Changing the Message" @Next "Procedures" @Toc "Understanding a Simple Program" Changing the Message ==================== Edit the file so that line contains a different message between the two ' characters and compile it again using the same procedure as before. Don't use any ' characters except those around the message. If all went well, when you run the program again it should produce a different message. If something went wrong, compare the contents of your file with the original and make sure the only difference is the message between the ' characters. @{" Tinkering with the example " Link "Tinkering with the example" } @{" Brief overview " Link "Brief overview" } @ENDNODE @NODE "Tinkering with the example" "Tinkering with the example" @Next "Brief overview" @Toc "Changing the Message" Tinkering with the example -------------------------- Simple tinkering is a good way to learn for yourself so it is encouraged on these simple examples. Don't stray too far, though, and if you start getting confused return to the proper example pretty sharpish! @ENDNODE @NODE "Brief overview" "Brief overview" @Prev "Tinkering with the example" @Toc "Changing the Message" Brief overview -------------- We'll look in detail at the important parts of the program in the following sections, but we need first to get a glimpse of the whole picture. Here's a brief description of some fundamental concepts: @{b }*@{ub } @{i }Procedures:@{ui } We defined a procedure called @{b }main@{ub } and used the (built-in) procedure @{b }WriteF@{ub }. A procedure can be thought of as a small program with a name. @{b }*@{ub } @{i }Parameters:@{ui } The message in parentheses after @{b }WriteF@{ub } in our program is the parameter to @{b }WriteF@{ub }. This is the data which the procedure should use. @{b }*@{ub } @{i }Strings:@{ui } The message we passed to @{b }WriteF@{ub } was a series of characters enclosed in ' characters. This is known as a @{fg shine }string@{fg text }. @ENDNODE @NODE "Procedures" "Procedures" @Next "Parameters" @Prev "Changing the Message" @Toc "Understanding a Simple Program" Procedures ========== As mentioned above, a procedure can be thought of as a small program with a name. In fact, when an E program is run the procedure called @{b }main@{ub } is executed. Therefore, if your E program is going to do anything you must define a @{b }main@{ub } procedure. Other (built-in or user-defined) procedures may be run (or @{fg shine }called@{fg text }) from this procedure (as we did @{b }WriteF@{ub } in the example). For instance, if the procedure @{b }fred@{ub } calls the procedure @{b }barney@{ub } the code (or mini-program) associated with @{b }barney@{ub } is executed. This may involve calls to other procedures, and when the execution of this code is complete the next piece of code in the procedure @{b }fred@{ub } is executed (and this is generally the next line of the procedure). When the end of the procedure @{b }main@{ub } has been reached the program has finished. However, lots can happen between the beginning and end of a procedure, and sometimes the program may never get to finish. Alternatively, the program may @{fg shine }crash@{fg text }, causing strange things to happen to your computer. @{" Procedure Definition " Link "Procedure Definition" } @{" Procedure Execution " Link "Procedure Execution" } @{" Extending the example " Link "Extending the example" } @ENDNODE @NODE "Procedure Definition" "Procedure Definition" @Next "Procedure Execution" @Toc "Procedures" Procedure Definition -------------------- Procedures are defined using the keyword @{b }PROC@{ub }, followed by the new procedure's name (starting with a lowercase letter), a description of the parameters it takes (in parentheses), a series of lines forming the code of the procedure and then the keyword @{b }ENDPROC@{ub }. Look at the example program again to identify the various parts. See @{"The code" Link "The code" }. @ENDNODE @NODE "Procedure Execution" "Procedure Execution" @Next "Extending the example" @Prev "Procedure Definition" @Toc "Procedures" Procedure Execution ------------------- Procedures can be called (or executed) from within the code part of another procedure. You do this by giving its name, followed by some data in parentheses. Look at the call to @{b }WriteF@{ub } in the example program. See @{"The code" Link "The code" }. @ENDNODE @NODE "Extending the example" "Extending the example" @Prev "Procedure Execution" @Toc "Procedures" Extending the example --------------------- Here's how we could change the example program to define another procedure: PROC main() WriteF('My first program') fred() ENDPROC PROC fred() WriteF('...slightly improved') ENDPROC This may seem complicated, but in fact it's very simple. All we've done is define a second procedure called @{b }fred@{ub } which is just like the original program--it outputs a message. We've @{fg shine }called@{fg text } this procedure in the @{b }main@{ub } procedure just after the line which outputs the original message. Therefore, the message in @{b }fred@{ub } is output after this message. Compile the program as before and run it so you don't have to take my word for it. @ENDNODE @NODE "Parameters" "Parameters" @Next "Strings" @Prev "Procedures" @Toc "Understanding a Simple Program" Parameters ========== Generally we want procedures to work with particular data. In our example we wanted the @{b }WriteF@{ub } procedure to work on a particular message. We passed the message as a @{fg shine }parameter@{fg text } (or @{fg shine }argument@{fg text }) to @{b }WriteF@{ub } by putting it between the parentheses (the @{b }(@{ub } and @{b })@{ub } characters) that follow the procedure name. When we called the @{b }fred@{ub } procedure, however, we did not require it to use any data so the parentheses were left empty. When defining a procedure we define how much and what type of data we want it to work on, and when calling a procedure we give the specific data it should use. Notice that the procedure @{b }fred@{ub } (like the procedure @{b }main@{ub }) has empty parentheses in its definition. This means that the procedure cannot be given any data as parameters when it is called. Before we can define our own procedure that takes parameters we must learn about variables. We'll do this in the next chapter. See @{"Global and local variables" Link "Global and local variables" }. @ENDNODE @NODE "Strings" "Strings" @Next "Style Reuse and Readability" @Prev "Parameters" @Toc "Understanding a Simple Program" Strings ======= A series of characters between two ' characters is known as a string. Almost any character can be used in a string, although the \\ and ' characters have a special meaning. For instance, a linefeed is denoted by the two characters @{b }\\n@{ub }. We now know how to stop the message running into the prompt. Change the program to be: PROC main() WriteF('My first program\\n') fred() ENDPROC PROC fred() WriteF('...slightly improved\\n') ENDPROC Compile it as before, and run it. You should notice that the messages now appear on lines by themselves, and the second message is separated from the prompt which follows it. We have therefore cured the linefeed problem we spotted earlier (see @{"Execution" Link "Execution" }). @ENDNODE @NODE "Style Reuse and Readability" "Style Reuse and Readability" @Next "The Simple Program" @Prev "Strings" @Toc "Understanding a Simple Program" Style, Reuse and Readability ============================ The example has grown into two procedures, one called @{b }main@{ub } and one called @{b }fred@{ub }. However, we could get by with only one procedure: PROC main() WriteF('My first program\\n') WriteF('...slightly improved\\n') ENDPROC What we've done is replace the call to the procedure @{b }fred@{ub } with the code it represents (this is called @{fg shine }inlining@{fg text } the procedure). In fact, almost all programs can be easily re-written to eliminate all but the @{b }main@{ub } procedure. However, splitting a program up using procedures normally results in more readable code. It is also helpful to name your procedures so that their function is apparent, so our procedure @{b }fred@{ub } should probably have been named @{b }message@{ub } or something similar. A well-written program in this style can read just like English (or any other spoken language). Another reason for having procedures is to reuse code, rather than having to write it out every time you use it. Imagine you wanted to print the same, long message fairly often in your program--you'd either have to write it all out every time, or you could write it once in a procedure and call this procedure when you wanted the message printed. Using a procedure also has the benefit of having only one copy of the message to change, should it ever need changing. @ENDNODE @NODE "The Simple Program" "The Simple Program" @Prev "Style Reuse and Readability" @Toc "Understanding a Simple Program" The Simple Program ================== The simple program should now (hopefully) seem simple. The only bit that hasn't been explained is the built-in procedure @{b }WriteF@{ub }. E has many built-in procedures and later we'll meet some of them in detail. The first thing we need to do, though, is manipulate data. This is really what a computer does all the time--it accepts data from some source (possibly the user), manipulates it in some way (possibly storing it somewhere, too) and outputs new data (usually to a screen or printer). The simple example program did all this, except the first two stages were rather trivial. You told the computer to execute the compiled program (this was some user input) and the real data (the message to be printed) was retrieved from the program. This data was manipulated by passing it as a parameter to @{b }WriteF@{ub }, which then did some clever stuff to print it on the screen. To do our own manipulation of data we need to learn about variables and expressions. @ENDNODE @NODE "Variables and Expressions" "Variables and Expressions" @Next "Program Flow Control" @Prev "Understanding a Simple Program" @Toc "Contents.guide/main" Variables and Expressions ************************* Anybody who's done any school algebra will probably know what a variable is--it's just a named piece of data. In algebra the data is usually a number, but in E it can be all sorts of things (e.g., a string). The manipulation of data like the addition of two numbers is known as an @{fg shine }expression@{fg text }. The result of an expression can be used to build bigger expressions. For instance, @{b }1+2@{ub } is an expression, and so is @{b }6-(1+2)@{ub }. The good thing is you can use variables in place of data in expressions, so if @{b }x@{ub } represents the number 1 and @{b }y@{ub } represents 5, then the expression @{b }y-x@{ub } represents the number 4. In the next two sections we'll look at what kind of variables you can define and what the different sorts of expressions are. @{" Variables " Link "Variables" } @{" Expressions " Link "Expressions" } @ENDNODE @NODE "Variables" "Variables" @Next "Expressions" @Toc "Variables and Expressions" Variables ========= Variables in E can hold many different kinds of data (called @{fg shine }types@{fg text }). However, before a variable can be used it must be defined, and this is known as @{fg shine }declaring@{fg text } the variable. A variable declaration also decides whether the variable is available for the whole program or just during the code of a procedure (i.e., whether the variable is @{fg shine }global@{fg text } or @{fg shine }local@{fg text }). Finally, the data stored in a variable can be changed using @{fg shine }assignments@{fg text }. The following sections discuss these topics in slightly more detail. @{" Variable types " Link "Variable types" } @{" Variable declaration " Link "Variable declaration" } @{" Assignment " Link "Assignment" } @{" Global and local variables " Link "Global and local variables" } @{" Changing the example " Link "Changing the example" } @ENDNODE @NODE "Variable types" "Variable types" @Next "Variable declaration" @Toc "Variables" Variable types -------------- In E a variable is a storage place for data (and this storage is part of the Amiga's RAM). Different kinds of data may require different amounts of storage. However, data can be grouped together in @{fg shine }types@{fg text }, and two pieces of data from the same type require the same amount of storage. Every variable has an associated type and this dictates the maximum amount of storage it uses. Most commonly, variables in E store data from the type @{b }LONG@{ub }. This type contains the integers from -2,147,483,648 to 2,147,483,647, so is normally more than sufficient. There are other types, such as @{b }INT@{ub } and @{b }LIST@{ub }, and more complex things to do with types, but for now knowing about @{b }LONG@{ub } is enough. @ENDNODE @NODE "Variable declaration" "Variable declaration" @Next "Assignment" @Prev "Variable types" @Toc "Variables" Variable declaration -------------------- Variables must be declared before they can be used. They are declared using the @{b }DEF@{ub } keyword followed by a (comma-separated) list of the names of the variables to be declared. These variables will all have type @{b }LONG@{ub } (later we will see how to declare variables with other types). Some examples will hopefully make things clearer: DEF x DEF a, b, c The first line declares the single variable @{b }x@{ub }, whilst the second declares the variables @{b }a@{ub }, @{b }b@{ub } and @{b }c@{ub } all in one go. @ENDNODE @NODE "Assignment" "Assignment" @Next "Global and local variables" @Prev "Variable declaration" @Toc "Variables" Assignment ---------- The data stored by variables can be changed and this is normally done using @{fg shine }assignments@{fg text }. An assignment is formed using the variable's name and an expression denoting the new data it is to store. The symbol @{b }:=@{ub } separates the variable from the expression. For example, the following code stores the number two in the variable @{b }x@{ub }. The left-hand side of the @{b }:=@{ub } is the name of the variable to be affected (@{b }x@{ub } in this case) and the right-hand side is an expression denoting the new value (simply the number two in this case). x := 2 The following, more complex example uses the value stored in the variable before the assignment as part of the expression for the new data. The value of the expression on the right-hand side of the @{b }:=@{ub } is the value stored in the variable @{b }x@{ub } plus one. This value is then stored in @{b }x@{ub }, over-writing the previous data. (So, the overall effect is that @{b }x@{ub } is incremented.) x := x + 1 This may be clearer in the next example which does not change the data stored in @{b }x@{ub }. In fact, this piece of code is just a waste of CPU time, since all it does is look up the value stored in @{b }x@{ub } and store it back there! x := x @ENDNODE @NODE "Global and local variables" "Global and local variables" @Next "Changing the example" @Prev "Assignment" @Toc "Variables" Global and local variables (and procedure parameters) ----------------------------------------------------- There are two kinds of variable: @{fg shine }global@{fg text } and @{fg shine }local@{fg text }. Data stored by global variables can be read and changed by all procedures, but data stored by local variables can only be accessed by the procedure to which they are local. Global variables must be declared before the first procedure definition. Local variables are declared within the procedure to which they are local (i.e., between the @{b }PROC@{ub } and @{b }ENDPROC@{ub }). For example, the following code declares a global variable @{b }w@{ub } and local variables @{b }x@{ub } and @{b }y@{ub }. DEF w PROC main() DEF x x:=2 w:=1 fred() ENDPROC PROC fred() DEF y y:=3 w:=2 ENDPROC The variable @{b }x@{ub } is local to the procedure @{b }main@{ub }, and @{b }y@{ub } is local to @{b }fred@{ub }. The procedures @{b }main@{ub } and @{b }fred@{ub } can read and alter the value of the global variable @{b }w@{ub }, but @{b }fred@{ub } cannot read or alter the value of @{b }x@{ub } (since that variable is local to @{b }main@{ub }). Similarly, @{b }main@{ub } cannot read or alter @{b }y@{ub }. The local variables of one procedure are, therefore, completely different to the local variables of another procedure. For this reason they can share the same names without confusion. So, in the above example, the local variable @{b }y@{ub } in @{b }fred@{ub } could have been called @{b }x@{ub } and the program would have done exactly the same thing. DEF w PROC main() DEF x x:=2 w:=1 fred() ENDPROC PROC fred() DEF x x:=3 w:=2 ENDPROC This works because the @{b }x@{ub } in the assignment in @{b }fred@{ub } can refer only to the local variable @{b }x@{ub } of @{b }fred@{ub } (the @{b }x@{ub } in @{b }main@{ub } is local to @{b }main@{ub } so cannot be accessed from @{b }fred@{ub }). If a local variable for a procedure has the same name as a global variable then in the rest of the procedure the name refers only to the local variable. Therefore, the global variable cannot be accessed in the procedure, and this is called @{fg shine }descoping@{fg text } the global variable. The parameters of a procedure are local variables for that procedure. We've seen how to pass values as parameters when a procedure is called (the use of @{b }WriteF@{ub } in the example), but until now we haven't been able to define a procedure which takes parameters. Now we know a bit about variables we can have a go: DEF y PROC onemore(x) y:=x+1 ENDPROC This isn't a complete program so don't try to compile it. Basically, we've declared a variable @{b }y@{ub } (which will be of type @{b }LONG@{ub }) and a procedure @{b }onemore@{ub }. The procedure is defined with a parameter @{b }x@{ub }, and this is just like a (local) variable declaration. When @{b }onemore@{ub } is called a parameter must be supplied, and this value is stored in the (local) variable @{b }x@{ub } before execution of @{b }onemore@{ub }'s code. The code stores the value of @{b }x@{ub } plus one in the (global) variable @{b }y@{ub }. The following are some examples of calling @{b }onemore@{ub }: onemore(120) onemore(52+34) onemore(y) A procedure can be defined to take any number of parameters. Below, the procedure @{b }addthem@{ub } is defined to take two parameters, @{b }a@{ub } and @{b }b@{ub }, so it must therefore be called with two parameters. Notice that values stored by the parameter variables (@{b }a@{ub } and @{b }b@{ub }) can be changed within the code of the procedure, since they are just like local variables for the procedure. (The only real difference between local and parameter variables is that parameter variables are initialised with the values supplied as parameters when the procedure is called.) DEF y PROC addthem(a, b) a:=a+2 y:=a*b ENDPROC The following are some examples of calling @{b }addthem@{ub }: addthem(120,-20) addthem(52,34) addthem(y,y) Global variables are, by default, initialised to zero. Parameter variables are, of course, initialised by the actual values passed as parameters when a procedure is called. However, local variables are not initialised. This means that a local variable will contain a fairly random value when the code of a procedure is first executed. It is the responsibility of the programmer to ensure no assumptions are made about the value of local variables before they have been initialised. The obvious way to initialise a local variable is using an assignment, but there is also a way of giving an initialisation value as part of the declaration (see @{"Initialised Declarations" Link "MoreExpressions.guide/Initialised Declarations" }). Initialisation of variables is often very important, and is a common reason why programs go wrong. @ENDNODE @NODE "Changing the example" "Changing the example" @Prev "Global and local variables" @Toc "Variables" Changing the example -------------------- Before we change the example we must learn something about @{b }WriteF@{ub }. We already know that the characters @{b }\\n@{ub } in a string mean a linefeed. However, there are several other important combinations of characters in a string, and some are special to procedures like @{b }WriteF@{ub }. One such combination is @{b }\\d@{ub }, which is easier to describe after we've seen the changed example. PROC main() WriteF('My first program\\n') fred() ENDPROC PROC fred() WriteF('...brought to you by the number \\d\\n', 236) ENDPROC You might be able to guess what happens, but compile it and try it out anyway. If everything's worked you should see that the second message prints out the number that was passed as the second parameter to @{b }WriteF@{ub }. That's what the @{b }\\d@{ub } combination does--it marks the place in the string where the number should be printed. Here's the output the example should generate: My first program ...brought to you by the number 236 Try this next change: PROC main() WriteF('My first program\\n') fred() ENDPROC PROC fred() WriteF('...the number \\d is quite nice\\n', 16) ENDPROC This is very similar, and just shows that the @{b }\\d@{ub } really does mark the place where the number is printed. Again, here's the output it should generate: My first program ...the number 16 is quite nice We'll now try printing two numbers. PROC main() WriteF('My first program\\n') fred() ENDPROC PROC fred() WriteF('...brought to you by the numbers \\d and \\d\\n', 16, 236) ENDPROC Because we're printing two numbers we need two lots of @{b }\\d@{ub }, and we need to supply two numbers as parameters in the order in which we want them to be printed. The number 16 will therefore be printed before the word `and' and before the number 236. Here's the output: My first program ...brought to you by the numbers 16 and 236 We can now make a big step forward and pass the numbers as parameters to the procedure @{b }fred@{ub }. Just look at the differences between this next example and the previous one. PROC main() WriteF('My first program\\n') fred(16, 236) ENDPROC PROC fred(a,b) WriteF('...brought to you by the numbers \\d and \\d\\n', a,b) ENDPROC This time we pass the (local) variables @{b }a@{ub } and @{b }b@{ub } to @{b }WriteF@{ub }. This is exactly the same as passing the values they store (which is what the previous example did), and so the output will be the same. In the next section we'll manipulate the variables by doing some arithmetic with @{b }a@{ub } and @{b }b@{ub }, and get @{b }WriteF@{ub } to print the results. @ENDNODE @NODE "Expressions" "Expressions" @Prev "Variables" @Toc "Variables and Expressions" Expressions =========== The E language includes the normal mathematical and logical operators. These operators are combined with values (usually in variables) to give @{fg shine }expressions@{fg text } which yield new values. The following sections discuss this topic in more detail. @{" Mathematics " Link "Mathematics" } @{" Logic and comparison " Link "Logic and comparison" } @{" Precedence and grouping " Link "Precedence and grouping" } @ENDNODE @NODE "Mathematics" "Mathematics" @Next "Logic and comparison" @Toc "Expressions" Mathematics ----------- All the standard mathematical operators are supported in E. You can do addition, subtraction, multiplication and division. Other functions such as sine, modulus and square-root can also be used as they are part of the Amiga system libraries, but we only need to know about simple mathematics at the moment. The @{b }+@{ub } character is used for addition, @{b }-@{ub } for subtraction, @{b }*@{ub } for multiplication (it's the closest you can get to a multiplication sign on a keyboard without using the letter @{b }x@{ub }), and @{b }/@{ub } for division (be careful not to confuse the @{b }\\@{ub} used in strings with @{b }/@{ub } used for division). The following are examples of expressions: 1+2+3+4 15-5 5*2 330/33 -10+20 3*3+1 Each of these expressions yields ten as its result. The last example is very carefully written to get the precedence correct (see @{"Precedence and grouping" Link "Precedence and grouping" }). All the above expressions use integer operators, so they manipulate integers, giving integers as results. @{fg shine }Floating-point@{fg text } numbers are also supported by E, but using them is quite complicated (see @{"Floating-Point Numbers" Link "FloatingPoint.guide/main" }). (Floating-point numbers can represent both very small fractions and very large integers, but they have a limited accuracy, i.e., a limited number of @{i }significant@{ui } digits.) @ENDNODE @NODE "Logic and comparison" "Logic and comparison" @Next "Precedence and grouping" @Prev "Mathematics" @Toc "Expressions" Logic and comparison -------------------- Logic lies at the very heart of a computer. They rarely guess what to do next; instead they rely on hard facts and precise reasoning. Consider the password protection on most games. The computer must decide whether you entered the correct number or word before it lets you play the game. When you play the game it's constantly making decisions: did your laser hit the alien?, have you got any lives left?, etc. Logic controls the operation of a program. In E, the constants @{b }TRUE@{ub } and @{b }FALSE@{ub } represent the truth values true and false (respectively), and the operators @{b }AND@{ub } and @{b }OR@{ub } are the standard logic operators. The comparison operators are @{b }=@{ub } (equal to), @{b }>@{ub } (greater than), @{b }<@{ub } (less than), @{b }>=@{ub } (greater than or equal to), @{b }<=@{ub } (less than or equal to) and @{b }<>@{ub } (not equal to). All the following expressions are true: TRUE TRUE AND TRUE TRUE OR FALSE 1=1 2>1 3<>0 And these are all false: FALSE TRUE AND FALSE FALSE OR FALSE 0=2 2<1 (2<1) AND (-1=0) The last example must use parentheses. We'll see why in the next section (it's to do with precedence, again). The truth values @{b }TRUE@{ub } and @{b }FALSE@{ub } are actually numbers. This is how the logic system works in E. @{b }TRUE@{ub } is the number -1 and @{b }FALSE@{ub } is zero. The logic operators @{b }AND@{ub } and @{b }OR@{ub } expect such numbers as their parameters. In fact, the @{b }AND@{ub } and @{b }OR@{ub } operators are really bit-wise operators (see @{"Bitwise AND and OR" Link "MoreExpressions.guide/Bitwise AND and OR" }), so most of the time any non-zero number is taken to be @{b }TRUE@{ub }. It can sometimes be convenient to rely on this knowledge, although most of the time it is preferable (and more readable) to use a slightly more explicit form. Also, these facts can cause a few subtle problems as we shall see in the next section. @ENDNODE @NODE "Precedence and grouping" "Precedence and grouping" @Prev "Logic and comparison" @Toc "Expressions" Precedence and grouping ----------------------- At school most of us are taught that multiplications must be done before additions in a sum. In E it's different--there is no operator precedence, and the normal order in which the operations are performed is left-to-right, just like the expression is written. This means that expressions like @{b }1+3*3@{ub } do not give the results a mathematician might expect. In fact, @{b }1+3*3@{ub } represents the number 12 in E. This is because the addition, @{b }1+3@{ub }, is done before the multiplication, since it occurs before the multiplication. If the multiplication were written before the addition it would be done first (like we would normally expect). Therefore, @{b }3*3+1@{ub } represents the number 10 in E and in school mathematics. To overcome this difference we can use parentheses to group the expression. If we'd written @{b }1+(3*3)@{ub } the result would be 10. This is because we've forced E to do the multiplication first. Although this may seem troublesome to begin with, it's actually a lot better than learning a lot of rules for deciding which operator is done first (in C this can be a real pain, and you usually end up writing the brackets in just to be sure!). The logic examples above contained the expression: (2<1) AND (-1=0) This expression was false. If we'd left the parentheses out, it would have been: 2<1 AND -1=0 This is actually interpreted the same as: ((2<1) AND -1) = 0 Now the number -1 shouldn't really be used to represent a truth value with @{b }AND@{ub }, but we do know that @{b }TRUE@{ub } is the number -1, so E will make sense of this and the E compiler won't complain. We will soon see how @{b }AND@{ub } and @{b }OR@{ub } really work (see @{"Bitwise AND and OR" Link "MoreExpressions.guide/Bitwise AND and OR" }), but for now we'll just work out what E would calculate for this expression: 1. Two is not less than one so @{b }2<1@{ub } can be replaced by @{b }FALSE@{ub }. (FALSE AND -1) = 0 2. @{b }TRUE@{ub } is -1 so we can replace -1 by @{b }TRUE@{ub }. (FALSE AND TRUE) = 0 3. @{b }FALSE AND TRUE@{ub } is @{b }FALSE@{ub }. (FALSE) = 0 4. @{b }FALSE@{ub } is really the number zero, so we can replace it with zero. 0 = 0 5. Zero is equal to zero, so the expression is @{b }TRUE@{ub }. TRUE So E calculates the expression to be true. But the original expression (with parentheses) was false. Bracketing is therefore very important! It is also very easy to do correctly. @ENDNODE @NODE "Program Flow Control" "Program Flow Control" @Next "Summary" @Prev "Variables and Expressions" @Toc "Contents.guide/main" Program Flow Control ******************** A computer program often needs to repeatedly execute a series of statements or execute different statements according to the result of some decision. For example, a program to print all the numbers between one and a thousand would be very long and tedious to write if each print statement had to be given individually--it would be much better to use a variable and repeatedly print its value and increment it. Another aspect of flow control is choosing between different pieces of code to execute. For instance, if something goes wrong a program may need to decide whether to continue or print an error message and stop--this part of a program is a typical example of a conditional block. @{" Conditional Block " Link "Conditional Block" } @{" Loops " Link "Loops" } @ENDNODE @NODE "Conditional Block" "Conditional Block" @Next "Loops" @Toc "Program Flow Control" Conditional Block ================= There are two kinds of conditional block: @{b }IF@{ub } and @{b }SELECT@{ub }. Examples of these blocks are given below as fragments of E code (i.e., the examples are not complete E programs). IF x>0 x:=x+1 WriteF('Increment: x is now \\d\\n', x) ELSEIF x<0 x:=x-1 WriteF('Decrement: x is now \\d\\n', x) ELSE WriteF('Zero: x is 0\\n') ENDIF In the above @{b }IF@{ub } block, the first part checks if the value of @{b }x@{ub } is greater than zero, and, if it is, @{b }x@{ub } is incremented and the new value is printed (with a message saying it was incremented). The program will then skip the rest of the block, and will execute the statements which follow the @{b }ENDIF@{ub }. If, however, @{b }x@{ub } it is not greater than zero the @{b }ELSEIF@{ub } part is checked, so if @{b }x@{ub } is less than zero it will be decremented and printed, and the rest of the block is skipped. If @{b }x@{ub } is not greater than zero and not less than zero the statements in the @{b }ELSE@{ub } part are executed, so a message saying @{b }x@{ub } is zero is printed. The @{b }IF@{ub } conditional is described in more detail below. @{" IF block " Link "IF block" } @{" IF expression " Link "IF expression" } SELECT x CASE 0 WriteF('x is zero\\n') CASE 10 WriteF('x is ten\\n') CASE -2 WriteF('x is -2\\n') DEFAULT WriteF('x is not zero, ten or -2\\n') ENDSELECT The @{b }SELECT@{ub } block is similar to the @{b }IF@{ub } block--it does different things depending on the value of @{b }x@{ub }. However, @{b }x@{ub } is only checked against specific values, given in the series of @{b }CASE@{ub } statements. If it is not any of these values the @{b }DEFAULT@{ub } part is executed. There's also a variation on the @{b }SELECT@{ub } block (known as the @{b }SELECT..OF@{ub } block) which matches ranges of values and is quite fast. The two kinds of @{b }SELECT@{ub } block are described in more detail below. @{" SELECT block " Link "SELECT block" } @{" SELECT..OF block " Link "SELECT..OF block" } @ENDNODE @NODE "IF block" "IF block" @Next "IF expression" @Toc "Conditional Block" @{b }IF@{ub } block -------- The @{b }IF@{ub } block has the following form (the bits like @{fg shine }expression@{fg text } are descriptions of the kinds of E code which is allowed at that point--they are not proper E code): IF @{fg shine }expressionA@{fg text } @{fg shine }statementsA@{fg text } ELSEIF @{fg shine }expressionB@{fg text } @{fg shine }statementsB@{fg text } ELSE @{fg shine }statementsC@{fg text } ENDIF This block means: @{b }*@{ub } If @{fg shine }expressionA@{fg text } is true (i.e., represents @{b }TRUE@{ub } or any non-zero number) the code denoted by @{fg shine }statementsA@{fg text } is executed. @{b }*@{ub } If @{fg shine }expressionA@{fg text } is false (i.e., represents @{b }FALSE@{ub } or zero) and @{fg shine }expressionB@{fg text } is true the @{fg shine }statementsB@{fg text } part is executed. @{b }*@{ub } If both @{fg shine }expressionA@{fg text } and @{fg shine }expressionB@{fg text } are false the @{fg shine }statementsC@{fg text } part is executed. There does not need to be an @{b }ELSE@{ub } part but if one is present it must be the last part (immediately before the @{b }ENDIF@{ub }). Also, there can be any number of @{b }ELSEIF@{ub } parts between the @{b }IF@{ub } and @{b }ELSE@{ub } parts. An alternative to this vertical form (where each part is on a separate line) is the horizontal form: IF @{fg shine }expression@{fg text } THEN @{fg shine }statementA@{fg text } ELSE @{fg shine }statementB@{fg text } This has the disadvantage of no @{b }ELSEIF@{ub } parts and having to cram everything onto a single line. Notice the presence of the @{b }THEN@{ub } keyword to separate the @{fg shine }expression@{fg text } and @{fg shine }statementA@{fg text }. This horizontal form is closely related to the @{b }IF@{ub } expression, which is described below (see @{"IF expression" Link "IF expression" }). To help make things clearer here are a number of E code fragments which illustrate the allowable @{b }IF@{ub } blocks: IF x>0 THEN x:=x+1 ELSE x:=0 IF x>0 x:=x+1 ELSE x:=0 ENDIF IF x=0 THEN WriteF('x is zero\\n') IF x=0 WriteF('x is zero\\n') ENDIF IF x<0 Write('Negative x\\n') ELSEIF x>2000 Write('Too big x\\n') ELSEIF (x=2000) OR (x=0) Write('Worrying x\\n') ENDIF IF x>0 IF x>2000 WriteF('Big x\\n') ELSE WriteF('OK x\\n') ENDIF ELSE IF x<-800 THEN WriteF('Small x\\n') ELSE Write('Negative OK x') ENDIF In the last example there are @{fg shine }nested@{fg text } @{b }IF@{ub } blocks (i.e., an @{b }IF@{ub } block within an @{b }IF@{ub } block). There is no ambiguity in which @{b }ELSE@{ub } or @{b }ELSEIF@{ub } parts belong to which @{b }IF@{ub } block because the beginning and end of the @{b }IF@{ub } blocks are clearly marked. For instance, the first @{b }ELSE@{ub } line can be interpreted only as being part of the innermost @{b }IF@{ub } block. As a matter of style the conditions on the @{b }IF@{ub } and @{b }ELSEIF@{ub } parts should not @{fg shine }overlap@{fg text } (i.e., at most one of the conditions should be true). If they do, however, the first one will take precedence. Therefore, the following two fragments of E code do the same thing: IF x>0 WriteF('x is bigger than zero\\n') ELSEIF x>200 WriteF('x is bigger than 200\\n') ELSE WriteF('x is too small\\n') ENDIF IF x>0 WriteF('x is bigger than zero\\n') ELSE WriteF('x is too small\\n') ENDIF The @{b }ELSEIF@{ub } part of the first fragment checks whether @{b }x@{ub } is greater than 200. But, if it is, the check in the @{b }IF@{ub } part would have been true (@{b }x@{ub } is certainly greater than zero if it's greater than 200), and so only the code in the @{b }IF@{ub } part is executed. The whole @{b }IF@{ub } block behaves as if the @{b }ELSEIF@{ub } was not there. @ENDNODE @NODE "IF expression" "IF expression" @Next "SELECT block" @Prev "IF block" @Toc "Conditional Block" @{b }IF@{ub } expression ------------- @{b }IF@{ub } is such a commonly used construction that there is also an @{b }IF@{ub } expression. The @{b }IF@{ub } block is a statement and it controls which lines of code are executed, whereas the @{b }IF@{ub } expression is an expression and it controls its own value. For example, the following @{b }IF@{ub } block: IF x>0 y:=x+1 ELSE y:=0 ENDIF can be written more succinctly using an @{b }IF@{ub } expression: y:=(IF x>0 THEN x+1 ELSE 0) The parentheses are unnecessary but they help to make the example more readable. Since the @{b }IF@{ub } block is just choosing between two assignments to @{b }y@{ub } it isn't really the lines of code that are different (they are both assignments), rather it is the values that are assigned to @{b }y@{ub } that are different. The @{b }IF@{ub } expression makes this similarity very clear. It chooses the @{i }value@{ui } to be assigned in just the same way that the @{b }IF@{ub } block choose the @{i }assignment@{ui }. The @{b }IF@{ub } expression has the following form: IF @{fg shine }exp@{fg text } THEN @{fg shine }expA@{fg text } ELSE @{fg shine }expB@{fg text } As you can see, @{b }IF@{ub } expressions are written like the horizontal form of the @{b }IF@{ub } block. However, there must be an @{b }ELSE@{ub } part and there can be no @{b }ELSEIF@{ub } parts. This means that the expression will always have a value (either @{fg shine }expA@{fg text } or @{fg shine }expB@{fg text }, depending on the value of @{fg shine }exp@{fg text }), and it isn't cluttered with lots of cases. Don't worry too much about @{b }IF@{ub } expressions, since there are only useful in a handful of cases and can always be rewritten as a more wordy @{b }IF@{ub } block. Having said that they are very elegant and a lot more readable than the equivalent @{b }IF@{ub } block. @ENDNODE @NODE "SELECT block" "SELECT block" @Next "SELECT..OF block" @Prev "IF expression" @Toc "Conditional Block" @{b }SELECT@{ub } block ------------ The @{b }SELECT@{ub } block has the following form: SELECT @{fg shine }variable@{fg text } CASE @{fg shine }expressionA@{fg text } @{fg shine }statementsA@{fg text } CASE @{fg shine }expressionB@{fg text } @{fg shine }statementsB@{fg text } DEFAULT @{fg shine }statementsC@{fg text } ENDSELECT The value of the selection variable (denoted by @{fg shine }variable@{fg text } in the @{b }SELECT@{ub } part) is compared with the value of the expression in each of the @{b }CASE@{ub } parts in turn. If there's a match, the statements in the (first) matching @{b }CASE@{ub } part are executed. There can be any number of @{b }CASE@{ub } parts between the @{b }SELECT@{ub } and @{b }DEFAULT@{ub } parts. If there is no match, the statements in the @{b }DEFAULT@{ub } part are executed. There does not need to be a @{b }DEFAULT@{ub } part but if one is present it must be the last part (immediately before the @{b }ENDSELECT@{ub }). It should be clear that @{b }SELECT@{ub } blocks can be rewritten as @{b }IF@{ub } blocks, with the checks on the @{b }IF@{ub } and @{b }ELSEIF@{ub } parts being equality checks on the selection variable. For example, the following code fragments are equivalent: SELECT x CASE 22 WriteF('x is 22\\n') CASE (y+z)/2 WriteF('x is (y+x)/2\\n') DEFAULT WriteF('x isn't anything significant\\n') ENDSELECT IF x=22 WriteF('x is 22\\n') ELSEIF x=(y+z)/2 WriteF('x is (y+x)/2\\n') ELSE WriteF('x isn't anything significant\\n') ENDIF Notice that the @{b }IF@{ub } and @{b }ELSEIF@{ub } parts come from the @{b }CASE@{ub } parts, the @{b }ELSE@{ub } part comes from the @{b }DEFAULT@{ub } part, and the order of the parts is preserved. The advantage of the @{b }SELECT@{ub } block is that it's much easier to see that the value of @{b }x@{ub } is being tested all the time, and also we don't have to keep writing @{b }x=@{ub } in the checks. @ENDNODE @NODE "SELECT..OF block" "SELECT..OF block" @Prev "SELECT block" @Toc "Conditional Block" @{b }SELECT..OF@{ub } block ---------------- The @{b }SELECT..OF@{ub } block is a bit more complicated than the normal @{b }SELECT@{ub } block, but can be very useful. It has the following form: SELECT @{fg shine }maxrange@{fg text } OF @{fg shine }expression@{fg text } CASE @{fg shine }constA@{fg text } @{fg shine }statementsA@{fg text } CASE @{fg shine }constB1@{fg text } TO @{fg shine }constB2@{fg text } @{fg shine }statementsB@{fg text } CASE @{fg shine }range1@{fg text }, @{fg shine }range2@{fg text } @{fg shine }statementsC@{fg text } DEFAULT @{fg shine }statementsD@{fg text } ENDSELECT The value to be matched is @{fg shine }expression@{fg text }, which can be any expression, not just a variable like in the normal @{b }SELECT@{ub } block. However, the @{fg shine }maxrange@{fg text }, @{fg shine }constA@{fg text }, @{fg shine }constB1@{fg text } and @{fg shine }constB2@{fg text } must all be explicit numbers, i.e., constants (see @{"Constants" Link "Constants.guide/main" }). @{fg shine }maxrange@{fg text } must be a positive constant and the other constants must all be between zero and @{fg shine }maxrange@{fg text } (including zero but excluding @{fg shine }maxrange@{fg text }). The @{b }CASE@{ub } values to be matched are specified using @{fg shine }ranges@{fg text }. A simple range is a single constant (the first @{b }CASE@{ub } above). The more general range is shown in the second @{b }CASE@{ub }, using the @{b }TO@{ub } keyword (@{fg shine }constB2@{fg text } must be greater than @{fg shine }constB1@{fg text }). A general @{b }CASE@{ub } in the @{b }SELECT..OF@{ub } block can specify a number of possible ranges to match against by separating each range with a comma, as in the third @{b }CASE@{ub } above. For example, the following @{b }CASE@{ub } lines are equivalent and can be used to match any number from one to five (inclusive): CASE 1 TO 5 CASE 1, 2, 3, 4, 5 CASE 1 TO 3, 3 TO 5 CASE 1, 2 TO 3, 4, 5 CASE 1, 5, 2, 4, 3 CASE 2 TO 3, 5, 1, 4 If the value of the @{fg shine }expression@{fg text } is less than zero, greater than or equal to @{fg shine }maxrange@{fg text }, or it does not match any of the constants in the @{b }CASE@{ub } ranges, then the statements in the @{b }DEFAULT@{ub } part are executed. Otherwise the statements in the first matching @{b }CASE@{ub } part are executed. As in the normal @{b }SELECT@{ub } block, there does not need to be a @{b }DEFAULT@{ub } part. The following @{b }SELECT..OF@{ub } block prints the (numeric) day of the month nicely: SELECT 32 OF day CASE 1, 21, 31 WriteF('The \\dst day of the month\\n', day) CASE 2, 22 WriteF('The \\dnd day of the month\\n', day) CASE 3, 23 WriteF('The \\drd day of the month\\n', day) CASE 4 TO 20, 24 TO 30 WriteF('The \\dth day of the month\\n', day) DEFAULT WriteF('Error: invalid day=\\d\\n', day) ENDSELECT The @{fg shine }maxrange@{fg text } for this block is 32, since 31 is the maximum of the values used in the @{b }CASE@{ub } parts. If the value of @{b }day@{ub } was 100, for instance, then the statements in the @{b }DEFAULT@{ub } part would be executed, signalling an invalid day. This example can be rewritten as an @{b }IF@{ub } block: IF (day=1) OR (day=21) OR (day=31) WriteF('The \\dst day of the month\\n', day) ELSEIF (day=2) OR (day=22) WriteF('The \\dnd day of the month\\n', day) ELSEIF (day=3) OR (day=23) WriteF('The \\drd day of the month\\n', day) ELSEIF ((4<=day) AND (day<=20)) OR ((24<=day) AND (day<=30)) WriteF('The \\dth day of the month\\n', day) ELSE WriteF('Error: invalid day=\\d\\n', day) ENDIF The comma separating two ranges in the @{b }CASE@{ub } part has been replaced by an @{b }OR@{ub } of two comparison expressions, and the @{b }TO@{ub } range has been replaced by an @{b }AND@{ub } of two comparisons. (It is worth noticing the careful bracketing of the resulting expressions.) Clearly, the @{b }SELECT..OF@{ub } block is much more readable than the equivalent @{b }IF@{ub } block. It is also a lot faster, mainly because none of the comparisons present in @{b }IF@{ub } block have to be done in the @{b }SELECT..OF@{ub } version. Instead the value to be matched is used to immediately locate the correct @{b }CASE@{ub } part. However, it's not all good news: the @{fg shine }maxrange@{fg text } value directly affects the size of compiled executable, so it is recommended that @{b }SELECT..OF@{ub } blocks be used only with small @{fg shine }maxrange@{fg text } values. See the `Reference Manual' for more details. @ENDNODE @NODE "Loops" "Loops" @Prev "Conditional Block" @Toc "Program Flow Control" Loops ===== Loops are all about making a program execute a series of statements over and over again. Probably the simplest loop to understand is the @{b }FOR@{ub } loop. There are other kinds of loops, but they are easier to understand once we know how to use a @{b }FOR@{ub } loop. @{" FOR loop " Link "FOR loop" } @{" WHILE loop " Link "WHILE loop" } @{" REPEAT..UNTIL loop " Link "REPEAT..UNTIL loop" } @ENDNODE @NODE "FOR loop" "FOR loop" @Next "WHILE loop" @Toc "Loops" @{b }FOR@{ub } loop -------- If you want to write a program to print the numbers one to 100 you can either type each number and wear out your fingers, or you can use a single variable and a small @{b }FOR@{ub } loop. Try compiling this E program (the space after the @{b }\\d@{ub } in the string is needed to separate the printed numbers): PROC main() DEF x FOR x:=1 TO 100 WriteF('\\d ', x) ENDFOR WriteF('\\n') ENDPROC When you run this you'll get all the numbers from one to 100 printed, just like we wanted. It works by using the (local) variable @{b }x@{ub } to hold the number to be printed. The @{b }FOR@{ub } loop starts off by setting the value of @{b }x@{ub } to one (the bit that looks like an assignment). Then the statements between the @{b }FOR@{ub } and @{b }ENDFOR@{ub } lines are executed (so the value of @{b }x@{ub } gets printed). When the program reaches the @{b }ENDFOR@{ub } it increments @{b }x@{ub } and checks to see if it is bigger than 100 (the limit we set with the @{b }TO@{ub } part). If it is, the loop is finished and the statements after the @{b }ENDFOR@{ub } are executed. If, however, it wasn't bigger than 100, the statements between the @{b }FOR@{ub } and @{b }ENDFOR@{ub } lines are executed all over again, and this time @{b }x@{ub } is one bigger since it has been incremented. In fact, this program does exactly the same as the following program (the @{b }...@{ub } is not E code--it stands for the 97 other @{b }WriteF@{ub } statements): PROC main() WriteF('\\d ', 1) WriteF('\\d ', 2) ... WriteF('\\d ', 100) WriteF('\\n') ENDPROC The general form of the @{b }FOR@{ub } loop is as follows: FOR @{fg shine }var@{fg text } := @{fg shine }expressionA@{fg text } TO @{fg shine }expressionB@{fg text } STEP @{fg shine }number@{fg text } @{fg shine }statements@{fg text } ENDFOR The @{fg shine }var@{fg text } bit stands for the loop variable (in the example above this was @{b }x@{ub }). The @{fg shine }expressionA@{fg text } bit gives the start value for the loop variable and the @{fg shine }expressionB@{fg text } bit gives the last allowable value for it. The @{b }STEP@{ub } part allows you to specify the value (given by @{fg shine }number@{fg text }) which is added to the loop variable on each loop. Unlike the values given for the start and end (which can be arbitrary expressions), the @{b }STEP@{ub } value must be a constant (see @{"Constants" Link "Constants.guide/main" }). The @{b }STEP@{ub } value defaults to one if the @{b }STEP@{ub } part is omitted (as in our example). Negative @{b }STEP@{ub } values are allowed, but in this case the check used at the end of each loop is whether the loop variable is @{i }less than@{ui } the value in the @{b }TO@{ub } part. Zero is not allowed as the @{b }STEP@{ub } value. As with the @{b }IF@{ub } block there is a horizontal form of a @{b }FOR@{ub } loop: FOR @{fg shine }var@{fg text } := @{fg shine }expA@{fg text } TO @{fg shine }expB@{fg text } STEP @{fg shine }expC@{fg text } DO @{fg shine }statement@{fg text } @ENDNODE @NODE "WHILE loop" "WHILE loop" @Next "REPEAT..UNTIL loop" @Prev "FOR loop" @Toc "Loops" @{b }WHILE@{ub } loop ---------- The @{b }FOR@{ub } loop used a loop variable and checked whether that variable had gone past its limit. A @{b }WHILE@{ub } loop allows you to specify your own loop check. For instance, this program does the same as the program in the previous section: PROC main() DEF x x:=1 WHILE x<=100 WriteF('\\d ', x) x:=x+1 ENDWHILE WriteF('\\n') ENDPROC We've replaced the @{b }FOR@{ub } loop with an initialisation of @{b }x@{ub } and a @{b }WHILE@{ub } loop with an extra statement to increment @{b }x@{ub }. We can now see the inner workings of the @{b }FOR@{ub } loop and, in fact, this is exactly how the @{b }FOR@{ub } loop works. It is important to know that our check, @{b }x<=100@{ub }, is done before the loop statements are executed. This means that the loop statements might not even be executed once. For instance, if we'd made the check @{b }x>=100@{ub } it would be false at the beginning of the loop (since @{b }x@{ub } is initialised to one in the assignment before the loop). Therefore, the loop would have terminated immediately and execution would pass straight to the statements after the @{b }ENDWHILE@{ub }. Here's a more complicated example: PROC main() DEF x,y x:=1 y:=2 WHILE (x<10) AND (y<10) WriteF('x is \\d and y is \\d\\n', x, y) x:=x+2 y:=y+2 ENDWHILE ENDPROC We've used two (local) variables this time. As soon as one of them is ten or more the loop is terminated. A bit of inspection of the code reveals that @{b }x@{ub } is initialised to one, and keeps having two added to it. It will, therefore, always be an odd number. Similarly, @{b }y@{ub } will always be even. The @{b }WHILE@{ub } check shows that it won't print any numbers which are greater than or equal to ten. From this and the fact that @{b }x@{ub } starts at one and @{b }y@{ub } at two we can decide that the last pair of numbers will be seven and eight. Run the program to confirm this. It should produce the following output: x is 1 and y is 2 x is 3 and y is 4 x is 5 and y is 6 x is 7 and y is 8 Like the @{b }FOR@{ub } loop, there is a horizontal form of the @{b }WHILE@{ub } loop: WHILE @{fg shine }expression@{fg text } DO @{fg shine }statement@{fg text } Loop termination is always a big problem. @{b }FOR@{ub } loops are guaranteed to eventually reach their limit (if you don't mess with the loop variable, that is). However, @{b }WHILE@{ub } loops (and all other loops) may go on forever and never terminate. For example, if the loop check were @{b }1<2@{ub } it would always be true and nothing the loop could do would prevent it being true! You must therefore take care that your loops terminate in some way if you want to program to finish. There is a sneaky way of terminating loops using the @{b }JUMP@{ub } statement, but we'll ignore that for now. @ENDNODE @NODE "REPEAT..UNTIL loop" "REPEAT..UNTIL loop" @Prev "WHILE loop" @Toc "Loops" @{b }REPEAT..UNTIL@{ub } loop ------------------ A @{b }REPEAT..UNTIL@{ub } loop is very similar to a @{b }WHILE@{ub } loop. The only difference is where you specify the loop check, and when and how the check is performed. To illustrate this, here's the program from the previous two sections rewritten using a @{b }REPEAT..UNTIL@{ub } loop (try to spot the subtle differences): PROC main() DEF x x:=1 REPEAT WriteF('\\d ', x) x:=x+1 UNTIL x>100 WriteF('\\n') ENDPROC Just as in the @{b }WHILE@{ub } loop version we've got an initialisation of @{b }x@{ub } and an extra statement in the loop to increment @{b }x@{ub }. However, this time the loop check is specified at the end of the loop (in the @{b }UNTIL@{ub } part), and the check is only performed at the end of each loop. This difference means that the code in a @{b }REPEAT..UNTIL@{ub } loop will be executed at least once, whereas the code in a @{b }WHILE@{ub } loop may never be executed. Also, the logical sense of the check follows the English: a @{b }REPEAT..UNTIL@{ub } loop executes @{i }until@{ui } the check is true, whereas the @{b }WHILE@{ub } loop executes @{i }while@{ui } the check is true. Therefore, the @{b }REPEAT..UNTIL@{ub } loop executes while the check is false! This may seem confusing at first, but just remember to read the code as if it were English and you'll get the correct interpretation. @ENDNODE @NODE "Summary" "Summary" @Next "Format.guide/main" @Prev "Program Flow Control" @Toc "Contents.guide/main" Summary ******* This is the end of Part One, which was hopefully enough to get you started. If you've grasped the main concepts you are good position to attack Part Two, which covers the E language in more detail. This is probably a good time to look at the different parts of one of the examples from the previous sections, since we've now used quite a bit of E. The following examination uses the @{b }WHILE@{ub } loop example. Just to make things easier to follow, each line has been numbered (don't try to compile it with the line numbers on!). 1. PROC main() 2. DEF x,y 3. x:=1 4. y:=2 5. WHILE (x<10) AND (y<10) 6. WriteF('x is \\d and y is \\d\\n', x, y) 7. x:=x+2 8. y:=y+2 9. ENDWHILE 10. ENDPROC Hopefully, you should be able to recognise all the features listed in the table below. If you don't then you might need to go back over the previous chapters, or find a much better programming guide than this! @{i }Line(s)@{ui } @{i }Observation@{ui } --------------------------------------------------------- 1-10 The procedure definition. 1 The declaration of the procedure @{b }main@{ub }, with no parameters. 2 The declaration of local variables @{b }x@{ub } and @{b }y@{ub }. 3, 4 Initialisation of @{b }x@{ub } and @{b }y@{ub } using assignment statements. 5-9 The @{b }WHILE@{ub } loop. 5 The loop check for the @{b }WHILE@{ub } loop using the logical operator @{b }AND@{ub }, the comparison operator @{b }<@{ub }, and parentheses to group the expression. 6 The call to the (built-in) procedure @{b }WriteF@{ub } using parameters. Notice the string, the place holders for numbers, @{b }\\d@{ub }, and the linefeed, @{b }\\n@{ub }. 7, 8 Assignments to @{b }x@{ub } and @{b }y@{ub }, adding two to their values. 9 The marker for the end of the @{b }WHILE@{ub } loop. 10 The marker for the end of the procedure. @ENDNODE