A Beginners Guide To Euphoria

Introduction To Library Routines
 
The chapters you have read so far have helped teach you how to accomplish the primary purpose of a program: the processing of data. You know the two types of data, you know how to declare variables to hold data, and you know how to use assignment statements in order to initialize and change values in variables. However, this is only the core. The declaration and assignment statements alone are not enough to make a program that actually does something useful. Euphoria has something called "library routines" that allow you to do things beyond the power of simple assignment statements. When requested by the programs that you will write, they allow you to access specific features of your computer. This allows you to write software like games, office and home applications, or system utilties. Euphoria (Version 1.5) has grouped all library routines into the following categories:
 
  1. Predefined Types - library routines that test the type of a data
  2. Sequence Manipulation - library routines that offer advanced sequence handling features
  3. Searching and Sorting - library routines that compare, look for, and sort data objects
  4. Pattern Matching - library routines that can convert alphabet case and allow pattern matching in a string of characters
  5. Math - library routines that handle advanced mathematic formulas far beyond the power of the math operators +,-,/, and *
  6. Bitwise Logical Operators - library routines that handle binary bits
  7. File And Device I/O - library routines that let programs use the hard and floppy drives, screen, keyboard, and other computer hardware.
  8. Mouse Support - library routines that let your programs use the  features of the mouse, like clicking buttons and pointer movement.
  9. Operating System - library routines that handle your program's relationship with the operating system on your computer. 
  10.  Special Machine Dependent Routines - library routines that allow direct access to computer resources normally accessed by Euphoria.
  11.  Debugging - library routines that lets you do diagnostics on the program while it runs.
  12. Graphics And Sound - library routines that lets your programs perform multimedia features like graphics and sound.
  13. Machine Level Interface - library routines that allow access to low level (machine language) features of the computer
We will cover most of the library routines in this tutorial, except those that involve machine language work. These are for the advanced programmer who has machine language experience. The library routines will be introduced based on the subject in each of the next chapters ahead. Where are the library routines stored? Well, they are defined in one of two places. Some are written in machine language and are defined in the interpreter EX.EXE. The rest are written in the Euphoria programming language. Library routines written in Euphoria require a special Euphoria programming statement called an "include" statement. The syntax is listed below: 
 
    include (file name).e

To use library routines written in Euphoria, an include statement MUST be at the top of your program or they will not work. When you use EX.EXE to interpret your program, or BIND.BAT to create a machine language replica, (file name) is referenced to get the programming statements of  (file name).e, the library routine(s) stored in there. Include files always end in an .e extension. The name of the include file will depend on the Euphoria-coded library routine you are using. Euphoria (Version 1.5) has 8 include files, listed below with a brief description of each:
 
   GRAPHICS.E - Graphics And Sound
   SORT.E - Sorting Routine
   GET.E - Input And Conversion Routines
   MOUSE.E - Mouse Routines
   FILE.E - Random Access File Operations And Directory Functions
   MACHINE.E - Machine Level Programming For 386's And Higher
   WILDCARD.E - Wildcard String Matching And Conversion
   IMAGE.E - Graphical Image Routines
 
When a new Euphoria-coded routine is introduced in the tutorial, the proper include file will be shown, so don't worry about remembering all these include file names. There are two kinds of library routines: procedures and functions. The only difference between a procedure and a function is that a function will return a value after it finishes running, while a procedure does not. To call a library routine that is a procedure, here is the following syntax below:
 
   procedure name (parameters)
 
the procedure name is followed by a pair of brackets that contain a list of values to be sent to the procedure for processing called parametersParameters can be data object values, variable names, or expressions that work out to a value. Procedures can have no parameters (if they are not required), or an endless list of parameters. If more than one parameter is passed to a procedure, each parameter is separated by commas. If no parameters are passed, then the brackets are placed back-to-back with no spaces in-between, like "()". The following are actual Euphoria procedures that you will be learning ,about later. They are being presented here for you to see how each accepts parameters. Don't worry about what they do just yet.

    print(1,"Hey, now!")
    clear_screen()
    pixel({BRIGHT_BLUE,BRIGHT_RED,BRIGHT_GREEN},{100,150})

Functions are identical in appearance to procedures, so the syntax of a function isn't hard to learn if you already understand the syntax of procedures. However, a function requires a slight addition to its syntax in order to return a value, as previously mentioned. The syntax of a function is on the next page.

   receiving variable = function name (parameters) 

Because a function returns a value after processing is complete, it must be used in an assignment statement with a variable on the left side of the equal sign to receive the returned value. You could say a function is very similiar to an expression because, like an expression, it returns a single value. Functions can return a value of any data object type (an atom or sequence). Some Euphoria functions can return both data object types. For this reason, it is advisable to have a receiving variable of type object to handle both types of return values. You may remember, in our discussion on declaring variables, we mentioned object type variables are used when the type of data being returned from a program process is unknown. Now you know why object type variables are important when dealing with functions! Because a function's syntax requires the use of an assignment statement, and because it returns a single value, it can be part of an expression that is itself evaluated to a single value. Here are some examples of actual Euphoria functions, the last of which is being used as part of an expression. Again, don't try to understand the meaning of each function.

   index_bitmap = read_bitmap("index.bmp")
   pressed_key = get_key()
   computed_result = sqrt(25) * (30 + units)
 
While a function can modify the original value of a receiving variable, both procedures and functions do not modify the parameter values that are passed to them. Also, procedure and function names follow the same rules that variable names must adhere to. The following abbreviations will be used every time a new library routine is introduced to you. Take a moment to look them over:
 
   a           - either an atom data object or a variable of type atom
   i           - either an integer data object or a variable of type integer
   o           - either an object data object or a variable of type object
   s           - either a sequence data object or a variable of type sequence
   ra,ri,ro,rs - receiving variable, the second letter means variable type.

If any of these abbreviations are used more than once, a number will follow the letter (i.e. s1, s2, s3 or o1, o2) to separate the parameters apart.
The next chapter will begin your learning of Euphoria library routines!

ToC

Displaying Data On The Screen

Now that you have reached this current level of understanding about the Euphoria programming language, the tutorial's style of teaching is going to change. Instead of reading program examples on the screen, you will now be able to execute actual Euphoria programs and view their source code. This approach is necessary, as these advanced features of Euphoria need more than reading text in the tutorial in order to be clearly understood. The program examples you have studied through the past chapter have one thing in common. All the data they process is stored in the body of the program, in the form of assignment statements. In real life, this is not the case. Today's programs do not keep data as part of the source code, but instead obtain it from elsewhere. The idea of retrieving and storing data outside the program makes good sense, for three reasons. First of all, the data can be edited without the program itself being changed. Second, if more than one program uses the same kind of data (like an employee list), there is no data duplication. The programs share the same data instead of each program having its own copy of the data. Finally, having the ability to send data outside the program means the data can be made presentable to human eyes.

Up to now, you have taken the word of this tutorial and its author that what is stored in the variables of example programs was the case. In real life, people need more than that in the form of printed reports and spreadsheet figures on the screen. Data that is accepted by a program is called "input". Data that is produced by a program is called "output". Programs send data to and receive data from components of your computer called "devices". These device can either be "input devices" that send data to a program, or "output devices" that receive data from a program. There is a third type of device called an "I-O device", which is both an input and an output device, but this type will be covered in a later chapter. The keyboard and mouse are examples of input devices. The computer screen and printer are examples of output devices. When a Euphoria program starts running, some devices are automatically allocated for use. These three devices are listed below:

   0 or standard input, the keyboard by default
   1 or standard output, the screen by default
   2 or standard error, the screen by default
 
Devices 1 and 2 are actually the same, but are defined separate in case there is a reason one type of screen output should be made distinct from the rest. Notice the phrase "by default". This implies we can make the numbers mean something else other than keyboard and screen. In a later chapter, you will be shown exactly how to do this. To keep it simple for now, understand that 0 means "keyboard", and 1 means "screen". This chapter will introduce screen output, while a future chapter will focus on keyboard input. Now that you are familiar with Euphoria's device numbers, let's introduce you to your very first Euphoria library routine, called print():

   print(1,o)

print() displays a Euphoria data object on the screen. The object is displayed at the current cursor position. print() displays data on the screen "as is", meaning what is displayed on the screen is the actual value of the data object. With sequences, you will be able to see the braces and commas, even if you used a character string to represent the value. If you click the demo button on the remote, you will be able to run and view the source of demo programs that use print().

Demo program 1
atom some_atom_value
some_atom_value = 134.45
print(1,some_atom_value)
Demo program 2
sequence some_sequence_value
some_sequence_value = {1,2,3,4,3,2,1}

print(1,some_sequence_value)

Demo program 3
print(1,36/2)

Demo program 4
sequence my_name
my_name = "David Gay"

print(1,my_name)

One minor drawback with print() is that it displays the actual values of atom and sequence data objects. As a result, any data object values meant to be shown on the screen as ASCII characters cannot be displayed using print(). However, Euphoria has another library routine that can display screen output in human-readable form:
 
    puts(1,o) 

Because puts() displays data as text characters instead of actual values, cursor control codes such as line feed (10), carriage return (13), and tab control (9) can be utilized. You can have text strings separated by lines or formatted in different tab columns. This makes puts() useful in print control as a result. Two notes about puts(). First of all, this library routine can only print one-dimensional sequences (those composed of atoms as elements). This makes sense, as text character strings are really a representation of one dimensional sequences. Also, any attempt to print an atom value larger than 255 will result in an incorrect character value being displayed. Again, this makes sense because the ASCII code set goes from 0 to 255. Some demo programs are available from this screen to show how puts() works.

Demo program 5
atom a_character
a_character = 'A'

puts(1,a_character)

Demo program 6
sequence a_string
a_string = "Utter Nonsense"
puts(1,a_string)

Demo program 7
puts(1,"Column 1\tColumn 2\tColumn 3\n\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")
puts(1,"********\t********\t********\n")

It is sometimes necessary to display additional screen output to help clarify information. This process is called "print formatting". Examples of print formatting are adding commas and dollar signs to financial figures or to limit the number of decimal places shown in scientific data. We can use the library routine printf() to display formatted screen output:
 
   printf(1,s,o) 
 
printf() prints data (shown as o) as a character string on the screen just like puts(). Before it does this, however, it formats the data using a format string (shown as s). The format string must contain special format codes that will edit the way the data will appear on the screen, and optionally, some extra text. The data to be displayed can either be a single atom value, or a sequence that contains a list of values to be edited using the format string. The number of format codes used will depend on the number of values that are to be edited and then printed. Let's look more closely at the format codes available for the programmer to use:

%d   - this will edit any atom values to appear as a decimal integer.
 
Decimal integers are the numbers we are all familiar with, using a numbering system of 10 digits from 0 to 9.

%x   - this will edit any atom values to appear as a hexadecimal integer.
 
Hexadecimal integers are numbers that are made up of digits from 0 to 9, A, B, C, D, E, and F. It is also known as a "base-16" numbering system (our decimal system is a "base-10" numbering system).

%o   - this will edit any atom values to appear as an octal integer.
 
Octal integers are numbers that are made up of digits from 0 to 7. It is also known as a "base-8" numbering system.

%s    - this will edit any sequence values to appear as character strings.
 
This works just like puts() but with extra formatting options.

%e    - this will edit any atom values to appear as a floating point number using exponential (or standard) notation.

%f     - this will edit any atom values to appear as a floating point number,but not using exponential notation.
 
%g    - this will edit any atom values to appear as a floating point number, using either the format offered by %e or %f, whichever works best for the
            display of the value.
%%  - this will display the percentage character.
 
Format codes can have a field width number added to control the number of displayed characters. For example, %6d means a minimum display size of 6 digits. If the number ends up smaller than 6 digits in this example, it will be right-justified with spaces. Adding a - in front of the number, like %-6d, will make it left-justified. If a zero is placed in front of the number, like %06d, zeroes instead of blanks will be used to fill up any leftmost field positions not used by the value. Placing a plus sign (+) in front of the number, like %+6d, will make positive values appear with a leading plus sign. Normally, positive values are displayed without a sign.The addition of a field size is not limited to decimal integers alone. You can even add them to other format codes like %s or %f (though using to fill any unused leftmost positions of a string with zeroes is unusual, even if it is considered valid). With floating point format codes (%f), the field size can be a decimal number. The number to the LEFT of the decimal point is the field size, while the number to the RIGHT of the decimal point is the precision. Precision means how many digits of the decimal fraction you want to have shown. For example, %7.3f will show the floating point value 13.1705 as 13.170 with a leading blank ( 13.170). The decimal fractions are not cut off, however: they are rounded up. This means %6.2f will display the value 899.987 as 899.99 with no leading blanks. Remember when we stated the data to be displayed in printf() is either a single atom value or a sequence representing a list of values to be displayed? For this reason, if you are attempting to display a SINGLE sequence value as a string, you must make it the only element of a sequence. This step is necessary, otherwise the sequence itself will be treated as a list of values and only the first character will be printed:
 
   printf(1,"%s\n","Euphoria")        - Only "E" is printed
   printf(1,"%s\n",{"Euphoria"})      - "Euphoria" is printed

A series of demo programs have been assigned to this page to help clarify printf().

Demo program 8
atom     atom_value
integer  integer_value

atom_value = 1233.14
integer_value = 255

print(1,atom_value)
printf(1,"\n  is shown as %d when using %%d", atom_value)
printf(1,"\n  is shown as %e when using %%e", atom_value)
printf(1,"\n  is shown as %f when using %%f\n\n", atom_value)

print(1,integer_value)
printf(1,"\n  is shown as %x when using %%x", integer_value)
printf(1,"\n  is shown as %o when using %%o\n\n", integer_value)

atom_value = 'A'
print(1,atom_value)
printf(1,"\n  is shown as %s when using %%s\n", atom_value)

Demo program 9
puts(1,"Field Width Using %9s\n")
puts(1,".........\n")
printf(1,"%9s", {"CAT"})
puts(1,"\n\n")

puts(1,"Field Width Using %-9s\n")
puts(1,".........\n")
printf(1,"%-9s", {"CAT"})
puts(1,"\n\n")

puts(1,"Field Width Using %4d\n")
puts(1,"....\n")
printf(1,"%4d", 123)
puts(1,"\n\n")

puts(1,"Field Width Using %04d\n")
puts(1,"....\n")
printf(1,"%04d", 123)
puts(1,"\n\n")

puts(1,"Field Width Using %+4d\n")
puts(1,"....\n")
printf(1,"%+4d", 123)
puts(1,"\n\n")

Demo program 10
puts(1, "How Decimal Precision Affects Floating Point Values\n\n")
printf(1,"%f  <- Using Straight %%f\n", 123.7651)
puts(1,".......\n")
printf(1,"%7.3f  <- %%7.3f\n", 123.7651)
printf(1,"%7.2f  <- %%7.2f\n", 123.7651)
printf(1,"%7.1f  <- %%7.1f\n", 123.7651)
printf(1,"%7.0f  <- %%7.0f\n", 123.7651)

We would normally explore the other side of screen output by introducing library routines that accept data from the keyboard. However, because some keyboard library routines require a program to execute a block of statements based on a condition or for a set number of times in order to work properly, a detour is needed. The next chapter will show you how to change the way your program executes.

ToC

Program Branching And Looping
 
The demonstration programs in this tutorial, while helpful in understanding Euphoria, are not good models of how the majority of programs work today. Program execution is not a straight line from program start to program end. A program may execute a group of statements meant to handle an anticipated condition, or repeat those statements until a condition is met. In short, program execution is a series of backtracks and detours. A group of statements that runs only when a specific condition is met is called a program branch. A group of statements that is meant to repeat while a condition is being met is called a program loop.
Let's begin our learning by introducing the "if" statement, which is the workhorse of program branching:
 
   if expression then
       one or more Euphoria statements
   end if 
 
The "if" statement will execute one or more Euphoria statements only if expression evaluates to be true. If the expression evaluates to to be false, program control will skip the conditioned statements and then resume with the first Euphoria statement following the end if line. The expression of an "if" statement can be any arithmetic, logical, or relational expression, or even a constant value. Most of the time, however, an expression is either a simple logical expression or a larger relational expression linking logical expressions together. A demo program is available to demonstrate a series of "if" statement examples. But before you run the demo, view the source to see if you can guess which of the puts() lines will be printed.

Demo program 11
atom first, second, sum
first = 36
second = 1.5
sum = first * second

if second >= 1.4 and second <= 1.6 then
     puts(1,"Condition 1 is true\n")
end if

if sum = 54 then
     puts(1,"Condition 2 is true\n")
     first = 63
end if

if first = 36 then
     puts(1,"Condition 3 is true\n")
end if

The "if" statement can be used to execute a group of statements for both false and true outcomes of a condition. One way to do this is to write a second "if" statement that checks for the opposite of the first "if" statement.
 
   if speed > 65 then
        puts(1,"You are driving too fast!\n")
   end if
   if speed <= 65 then
        puts(1,"Thank you for driving within the speed limit!\n")
   end if

The use of two "if" statements works, but it is inefficient because both "if" statements are checked even though only one of two outcomes will happen. You can't have a condition that is both true and false. It would be nice to have the "if" statement work like a toggle switch, executing the right block of statements with only one condition check. Fortunately, the "if" statement can be modified to handle two outcomes without using two "if" statements:
 
   if expression then
       one or more statements that will only run if expression is true
   else
       one or more statements that will only run if expression is false
   end if 

When the "if" statement has an "else" option added, only one of two things will happen. If the expression portion of the "if" statement works out to be true, the statements immediately following below are executed, and the statements under the "else" line are ignored. If the expression works out to be false, it's the reverse that happens. The statements under the "if" line are ignored, but the statements under the "else" line are executed. Whatever the outcome of the expression, the first statement following "end if" is always executed once the "if" statement completes. A demo is available on this screen to show the use of "else".

Demo program 12
atom character

character = 'a'

if character = 'a' then
     puts(1,"********************************\n")
     puts(1,"* This line should be printed! *\n")
     puts(1,"********************************\n")
else
     puts(1,"This line should not be printed!\n")
end if

character = 'b'

if character = 'a' then
     puts(1,"This line should not be printed!\n")
else
     puts(1,"********************************\n")
     puts(1,"* This line should be printed! *\n")
     puts(1,"********************************\n")
end if

Using an "if-else" combination can handle situations that result in only one of two outcomes. For situations that can result in more than two outcomes, we replace "else" with "elsif".

   if  expression 1 then
        one or more statements that execute if expression 1 is true
   elsif expression 2 then
        one or more statements that execute if expression 2 is true
   elsif expression 3 then
        one or more statements that execute if expression 3 is true
   elsif expression 4 then
        one or more statements that execute if expression 4 is true
   end if 
 
Using "elsif" with "if" creates a decision chain, where each of the expressions are checked one after another, starting with the first one, expression 1. Once an expression is found to be true, the statements conditioned to that expression are run. After this, all other expressions that follow in the "if-elsif" chain are skipped, and the first statement following "end if" is executed. Using "if-elsif" allows you to check for a specific set of outcomes to a situation, rather then using "either this or the other" kind of logic. It also lets you analyze a problem from a step-by-step approach, eliminating the need for designing complex expressions and typing out many "if" statements. A demo program is available that shows one use of "if-elsif" chains.

Demo program 13
atom character

character = 'M'

if character >= 'A' and character <= 'J' then
     puts(1, "This line should not be printed\n")
elsif character >= 'K' and character <= 'T' then
     puts(1, "********************************\n")
     puts(1, "* This line should be printed! *\n")
     puts(1, "********************************\n")
elsif character >= 'U' and character <= 'Z' then
     puts(1, "This line should not be printed\n")
end if

You can have what is called nested "if" statements, where one "if" statement leads to a second "if" statement when its expression is true: 
 
   if expression 1 then
        if expression 2 then
             one or more statements that run only if expression 2 is true
       end if
   end if 
 
In the case of the example on the previous page, it is the same as:

   if expression 1 and expression 2 then
        one or more expressions to be run only if both expressions are true
   end if 
 
However, using nested "if" statements allow you more flexibility, particularly when you want to mix "if" statements, library routine calls, and  assignment statements in a block of conditioned statements to be run. It also gives you the option of avoiding the use of "if" statements that have complex relational expressions made up of smaller expressions. When using nested "if" statements, make sure each "if" has a matching "end if" for each "if" level, just like the example you saw earlier. Make sure that any optional "else" and "elsif" used are on the correct level as the "if" statement they are associated with.

You've probably noticed, in all of the "if" statement examples we have shown, we used expressions that involved atom variables and values. The reason for this is because "if" statements cannot handle direct comparisons using sequence data objects. However, there is a library routine that can help you get around this problem:
 
   ri  = compare(o1,o2)
 
compare() takes two data objects, o1 and o2, and compares them, returning an integer value that represents the result of the comparison. If o1 is equal to o2, 0 is returned. If o2 is smaller than o1 , -1 is returned. If o2  is larger than o1, 1 is returned. compare() can compare two atoms, two sequences, or a sequence and an atom. Atoms are always assumed to be smaller than sequences in value. Where sequence comparison is concerned, it's an element by element comparison until a difference is found, either in the length of the sequences or a different element value. So, to compare one sequence with another, you simply follow the format shown in this example below:
 
   sequence my_name
   my_name = "David Gay"
   if compare(my_name, "David Gay") = 0 then
        puts(1,"Hi, David!\n")
   else
        puts(1,"Who the heck are you?\n")
   end if
 
A program demo is available to help clarify compare() if needed.

Demo program 14
sequence animal1, animal2

animal1 = "cat"
animal2 = animal1

if compare(animal1,animal2) = 0 then
     puts(1, "These strings are the same\n")
else
     puts(1, "These strings are unequal\n")
end if

animal2 = "CAT"

if compare(animal1,animal2) = 0 then
     puts(1, "These strings are the same\n")
else
     puts(1, "These strings are unequal\n")
end if

Program branching is only one part of changing the way your program runs. You can also make a group of statements repeat for as long as an expression remains true. This is done by using the "while" statement: 
 
   while expression do
        one or more statements that are executed while condition is true
   end while 
 
When the "while" statement first starts, it checks expression to see if it is true. If it is not, the next programming statement following end while is executed. If the expression is true, the "while" statement will continue to repeat the statements between "while" and "end while" until the expression portion of  the "while" statement becomes false. This tells you that the repeating statements must be able to make the expression change to false, or the program will be stuck in an endless "while" loop. A demo program using a "while" statement is available.

Demo program 15
atom count
puts(1,"Hello, I'm Sparky I, The Counting Euphoria Program\n")
puts(1,"Watch me count to 10!\n\n")
count = 1
while count < 11 do
     printf(1,"%d\n",count)
     count = count + 1
end while

If you want to repeat a group of statements for a specific number of times, Euphoria offers the "for" statement:
 
   for ra = start value to end value by increment do
        one or more statements to repeat until ra > end value
   end for 

When the "for" statement starts, it declares a temporary index variable, ra, assigning it a start value. It then processes the statements down to the end for line. Once it reaches that point, it changes ra's value by adding the increment. The program then loops back up to check if ra is larger than the end value. If not, the statements are processed again, and ra is changed by adding the value increment. This loop will keep repeating until ra is larger than end value. When this occurs, ra is "undeclared", and the program continues with the next statement after the "end for" line. The increment portion of the "for" statement is an optional feature. If left out, the default increment is 1. You can have a negative increment value, so the "for" statement counts down instead of up, but set the end value of the "for" statement to be smaller than the start value. Unlike the "while" statement, the "for" statement cannot be locked into an endless loop, because the expression change is handled automatically. Also, the index variable cannot be changed by any statements within the loop, but it can be referenced for use (such as an element index in a sequence). If the value in the index variable is required for later use, you should save it in a variable before the loop ends, because both the index variable and its value will vanish when the "for" statement ends. This temporary index variable has an attribute called "scope", meaning it is accessible only for a specific duration in the program . This topic will be revisited later in this tutorial. In the meantime, run a program demo now to demonstrate how the "for" statement can be used.

Demo program 16
puts(1,"Hello, I'm Sparky II, The Counting Euphoria Program\n")
puts(1,"Watch me count to 5, then back again to 1!\n\n")
for count = 1 to 5 do
     printf(1,"%d\n",count)
end for
puts(1,"\n")
for count = 5 to 1 by -1 do
     printf(1,"%d\n",count)
end for

Both the "for" and "while" statements can be nested, meaning that you can have smaller loops within larger ones. However, the nested "while" and "for" statement levels should have correctly matching "end while" and "end for" statements. You can force both a "while" and a "for" statement to end early using the "exit" statement. The loop ends at the moment the "exit" statement is executed. The program will then resume at the next statement following "end for" or "end while". The "exit" statement is best used as either an "emergancy brake" when something unexpected comes up, or for creating a loop that needs to end for a set of reasons uniquely separate from each other. A program demo shows the "exit" statement in action. You have now learned everything you need to know to control the course of program execution. These tools will be valuable in the next chapter, when you learn how to accept data from the keyboard.

Demo program 17
atom pet_index
sequence pets
puts(1,"Examples Of Animals You Can Keep As Pets\n")
puts(1,"========================================\n\n")
pets = {"Cat",
        "Dog",
        "Hamster",
        "Rat",
        "Snake",
        "Parrot",
        "Budgie",
        "Unelected Politician",
        "*END*"}
pet_index = 1

while 1 do
     if compare(pets[pet_index],"*END*") = 0 then
          exit
     else
          puts(1,pets[pet_index])
          puts(1,"\n")
     end if
     pet_index = pet_index + 1
end while
puts(1,"\nList Completed!\n")

Displaying data on the screen is only one side of program-human interaction. While it is great that we can now write programs that present text on the
screen, our programs can be even more useful if they can accept data for processing later. A program could be written to accept keyed-in monthly expenses and compute either a surplus or a deficit in your household budget. Euphoria has a set of library routines to handle keyboard input. Here's the first one listed below:
 
   include get.e
   ri = wait_key() 

wait_key() causes the program to pause until a key is pressed, then stores that value in an integer variable to the left of the equal sign. You will notice that wait_key() is an example of a library routine that is externally defined in an include file, called get.e. get.e must be present at the top of your program in order to use this library routine. If a key generating a displayable character (alphabet, numbers, symbols, and punctuation) or cursor control (tab, linefeed, backspace, etc) is pressed, the value stored in the receiving integer value is the ASCII code for that value. This means you can use puts() or print() to display the value on the screen as a text character, or move the cursor. Keys like the function keys from F1 through F12, arrow keys, insert and delete keys, and any key pressed while the ALT key is pressed down generate a value higher than 255. These keys are meant to be defined for the use of the programmer and do not generate a displayable screen character. A demo program showing how wait_key() is used is available on this page.

Demo program 18
include get.e
integer keystroke

puts(1,"Please press a key on the keyboard\n")
keystroke = wait_key()
puts(1,"\nThank you!\n")
printf(1,"You pressed the %s key!\n",keystroke)

It is sometimes necessary to read in an entire character string instead of a single character. Euphoria has a library routine that performs this:
 
    ro = gets(0) 
 
Like wait_key(), gets() pauses the program run for keyboard data. Unlike wait_key(), it is not a single value but a string of data that is stored in a variable at the left of the equal sign. However, notice that the receiving variable is an object type variable. This is necessary because a value of -1 (meaning that no keyboard data string was retrieved) may also be returned. It's also important to note that the Enter key that you pressed to end the string to send to gets() is a part of the string, at the very end as value 10. The receiving object variable will contain a sequence composed of atoms that represent ASCII codes. This means puts() and printf() can be used to display the data received by gets(). A demo program is available to show one use of gets() from this screen.

Demo program 19
object name, city
puts(1, "Hello, what is your name?\n")
name = gets(0)
printf(1, "\nHello, %sWhat city are you from?\n",{name})
city = gets(0)
printf(1, "\nI've always wanted to go to %s", {city})

All the library routines introduced so far have one thing in common, and that is the accepted keyboard data is treated like a character string to be redisplayed as text later. This means if we entered a value of 16.13 using gets(), it will not be stored as an atom value of 16.13, but a 5 element long character string "16.13". This makes the library routines we have mentioned unsuitable for handling numeric data. However, Euphoria does have a more sophisticated routine that can accept both numeric and character data:
 
    include get.e
    rs = get(0)
 
get() accepts a Euphoria data object and stores it into a receiving sequence variable. This means you can enter any atom value, character string representing a sequence, or even a very complex multi-dimensional sequence value. get() converts keyboard input to an actual Euphoria data object and then stores it as the second element of a two sequence value. The receiving sequence variable is assigned this sequence value. The first element of this sequence value serves as an error code on whether or not the string of characters you typed is a valid Euphoria data object. The error code is an atom value, and can be any one of the values listed below: 
 
    0 - Accepted value is a valid Euphoria data object.
   -1 - End of data hit before any data objects were read.
    1 - Accepted value is not a valid Euphoria data object.
 
It's important to monitor the error code, as it tells you if the data that was keyed in is valid. Using the "if" statement to check the error code can help you ensure you do not receive any garbage input. While actual atom and sequence values can be keyed in as is (like 45.1 or {123,{6,7,-1},17}), character strings must be enclosed with the double quotation mark or ". get() has many advantages over gets() in a number of ways. First of all, you can enter both numeric and character data in any format. Second, you can choose to send a list of data objects on a single line separated by spaces or tabs, or enter them one at a time using the Enter key. However, get() only reads these values one at a time. As a result, whether you place four values on one line and press Enter, or enter one on each line and press Enter, you need four get()'s in your program to read them. Finally, get() does not include the Enter key, space, or tab values as part of the keyboard data.
A demo is available on this page to demonstrate how get() accepts both character and numeric data.
 
Demo program 20
include get.e

object input
atom value1, value2, value3, average, error_code
sequence name

error_code = 999

puts(1,"Hello! Enter your first name in quotes below, like \"John\":\n")

while error_code != 0 do
     input = get(0)
     error_code = input[1]
end while

name = input[2]

puts(1, "\nThank you! Now please enter any three numeric values below,\n")
puts(1, "and you will see both your name and the average of the three\n")
puts(1, "numbers you entered. Separate the numbers with the space bar:\n")

error_code = 999
while error_code != 0 do
     input = get(0)
     error_code = input[1]
end while
puts(1, "\nGot first number!\n")
value1 = input[2]

error_code = 999
while error_code != 0 do
     input = get(0)
     error_code = input[1]
end while
puts(1, "\nGot second number!\n")
value2 = input[2]

error_code = 999
while error_code != 0 do
     input = get(0)
     error_code = input[1]
end while
puts(1, "\nGot third number!\n")
value3 = input[2]

average = (value1+value2+value3)/3
printf(1,"\nHello, %s, your computed average is %4.2f\n",{name, average})

You may have played some games that are able to accept data from the keyboard without pausing. Actually, they do pause, but only for a tiny fraction of time to check the keyboard buffer for a pressed keystroke. Because computers can operate at such high speeds, this delay goes unnoticed. The next library routine would be of interest to game designers:
 
   ri = get_key() 
 
get_key() stores each keystroke waiting in the keyboard buffer into a receiving varible. Multiple get_key()'s are required to read more than one waiting keystroke. If there are no keystrokes waiting, a -1 is returned instead. Unlike wait_key(), get_key() does not pause the program. get_key() will also accept keystrokes from special keys like the function keys and arrow keys. A demo is ready to show how get_key() can interrupt a countdown from 100000 to 1. When you view the demo program's source code, see how get_key() is located in a program loop, like the "while" statement. This makes sense, as the keyboard buffer should be repeatedly checked for any waiting keystrokes.

Demo program 21
integer keystroke, counter

keystroke = -1
counter = 10000
while keystroke = -1 and counter > 0 do
     keystroke = get_key()
     printf(1,"%5d\n",counter)
     counter = counter - 1
end while
if counter = 0 then
     puts(1, "\nHey, why didn't you press any of the keys?\n")
end if

The one keystroke that cannot be accepted by a Euphoria program is Ctrl-C, generated by either pressing the c or break key while holding down the Ctrl key at the same time. If you press these combination of keys, the operating system will abruptly stop the program. In the case of larger, more complex programs, this can result in an unexpected loss of data. It would be preferable for the program to know that Ctrl-C has been pressed and perform an orderly shutdown. First of all, we need to disable Ctrl-C so it cannot kill the program outright. This is done by using the allow_break() library routine:
 
   include file.e
   allow_break(i) 
 
Setting the parameter i to 1 will allow Ctrl-C to abruptly halt the program, while 0 would prevent usage of Ctrl-C to halt the program. The most obvious benefits of allow_break() are in applications that offer menu options to users based on their security level. If a hacker could disable a menu program using Ctrl-C, s/he then could access the program of interest directly, bypassing security. However, having a program issue allow_break(0) at the start of its run would prevent this. But remember that a program cannot directly accept a value of Ctrl-C, allow_break() or not. How can it know when to perform an orderly shutdown if it can't accept that value? Well, while it cannot accept Ctrl-C, it can DETECT it using this library routine:
 
   include file.e
   ri = check_break()
 
check_break() returns the number of times Ctrl-C has been pressed since the start of the program, or since the last time check_break() was issued. This does not return the actual Ctrl-C code, it just returns a value that indicates the number of times Ctrl-C was detected by the program. You must make sure you are using get_key() to read the keyboard buffer, in order for check_break() to work properly, even though it cannot read in the Ctrl-C code. A demo program has been made available from this page to show how allow_break() and check_break() are used to prevent Ctrl-C from stopping the program. You need to hit Ctrl-C or Ctrl-break three times to stop the demo from running.

Demo program 22
include file.e

integer control_break_counter, pressed_key
allow_break(0)

puts(1, "***********************************\n")
puts(1, "* The Program That Would Not Die! *\n")
puts(1, "***********************************\n")
puts(1,"\n")

control_break_counter = 0
while control_break_counter < 3 do
     pressed_key = get_key()
     if check_break() then
          control_break_counter = control_break_counter + 1
          if control_break_counter <= 2 then
               puts(1,"Neener-neener, can't kill meeeeeeee!\n")
          end if
     end if
end while

puts(1, "\n")
puts(1, "***********************************\n")
puts(1, "* Okay, I'll be nice and stop.... *\n")
puts(1, "***********************************\n")

Now that you have learned how to display data on the screen, and how to accept data from the keyboard, let's learn some powerful library routines that show you what you can really do with this data.

ToC