OpenEuphoria: Euphoria v4.1

# OpenEuphoria Manual

## Euphoria v4.1

routine or constant name search

| Up |  |  | TOC | Index
<< 3 Using Euphoria | < 4.3 Assignment statement | Up: 4 Language Reference | 4.5 Loop statements > | 5 Formal Syntax >>

## 4.4 Branching Statements

### 4.4.1 if statement

An if statement tests a condition to see whether it is true or false, and then depending on the result of that test, executes the appropriate set of statements.

The syntax of `if` is

    IFSTMT  ==:  IFTEST [ ELSIF ...] [ELSE] ENDIF
     IFTEST  ==:  if ATOMEXPR [ LABEL ] then [ STMTBLOCK ]
     ELSIF   ==:  elsif ATOMEXPR then [ STMTBLOCK ]
     ELSE    ==:  else [ STMTBLOCK ]
     ENDIF   ==:  end if

Description of syntax

- An if statement consists of the keyword `if`, followed by an expression that evaluates to an atom, optionally followed by a label clause, followed by the keyword `then`. Next is a set of zero or more statements. This is followed by zero or more elsif clauses. Next is an optional else clause and finally there is the keyword `end` followed by the keyword `if`.
- An elsif clause consists of the key word `elsif`, followed by an expression that evaluates to an atom, followed by the keyword `then`. Next is a set of zero or more statements.
- An else clause consists of the keyword `else` followed by a set of zero or more statements.

In Euphoria, false is represented by an atom whose value is zero and true is represented by an atom that has any non-zero value.

- When an expression being tested is true, Euphoria executes the statements immediately following the `then` keyword after the expression, up to the corresponding `elsif` or `else`, whichever comes next, then skips down to the corresponding `end if`.
- When an expression is false, Euphoria skips over any statements until it comes to the next corresponding `elsif` or `else`, whichever comes next. If this is an `elsif` then its expression is tested otherwise any statements following the `else` are executed.

For example:

    if a < b then
        x = 1
    end if

    if a = 9 and find(0, s) then
        x = 4
        y = 5
    else
        z = 8
    end if

    if char = 'a' then
        x = 1
    elsif char = 'b' or char = 'B' then
        x = 2
    elsif char = 'c' then
        x = 3
    else
        x = -1
    end if

Notice that `elsif` is a contraction of else if, but it's cleaner because it does not require an `end if` to go with it. There is just one `end if` for the entire if statement, even when there are many `elsif` clauses contained in it.

The `if` and `elsif` expressions are tested using short_circuit evaluation.

An if statement can have a label clause just before the first `then` keyword. See the section on Header Labels. Note that an elsif clause can not have a label.

### 4.4.2 switch statement

The switch statement is used to run a specific set of statements, depending on the value of an expression. It often replaces a set of if-elsif statements due to it's ability to be highly optimized, thus much greater performance. There are some key differences, however. A switch statement operates upon the value of a single expression, and the program flow continues based upon defined cases. The syntax of a switch statement:

    switch <expr> [with fallthru] [label "<label name>"] do
        case <val>[, <val2>, ...] then
            [code block]
            [[break [label]]|fallthru]
        case <val>[, <val2>, ...] then
            [code block]
            [[break [label]]|fallthru]
        case <val>[, <val2>, ...] then
            [code block]
            [[break [label]]|fallthru]
        ...

        [case else]
            [code block]
            [[break [label]]|fallthru]
    end switch

The above example could be written with `if` statements like this ..

    object temp = expression
    object breaking = false
    if equal(temp, val1) then
        [code block 1]
        [breaking = true]
    end if
    if not breaking and equal(temp, val2) then
        [code block 2]
        [breaking = true]
    end if
    if not breaking and equal(temp, val3) then
        [code block 3]
        [breaking = true]
    end if
     ...
    if not breaking then
        [code block 4]
        [breaking = true]
    end if

The <val> in a `case` must be either an atom, literal string, constant or enum. Multiple values for a single `case` can be specified by separating the values by commas. The same symbol (or literal) may not be used multiple times as a `case` for the same `switch`. If two different symbols used as `case` values happen to have the same value, they must be in the same `case...then` statement, or an error will occur. If the parser can determine all values when the `switch` is parsed, then a compile time error will be thrown. Otherwise, the error will occur the first time that the switch is encountered. Likewise, when translating code, if the parser cannot determine all values at the time when the `case` values are parsed, the compilation will fail due to mulitple `case` values in the emitted C code (it is assumed that the programmer should work out this sort of bug in interpreted mode).

By default, control flows to the end of the `switch` block when the next `case` is encountered. The default behavior can be modified in two ways. The default for a particular `switch` block can be changed so that control passes to the next executable statement whenever a new case is encountered by using `with fallthru` in the `switch` statement:

    switch x with fallthru do
        case 1 then
            ? 1
        case 2 then
            ? 2
            break
        case else
            ? 0
    end switch

Note that when `with fallthru` is used, the `break` statement can be used to jump out of the `switch` block. The behavior of individual `case`s can be changed by using the `fallthru` statement:

    switch x do
        case 1 then
            ? 1
            fallthru
        case 2 then
            ? 2
        case else
            ? 0
    end switch

Note that the `break` statement before `case else` was omitted, because the equivalent action is taken automatically by default.

    switch length(x) do
        case 1 then
            -- do something
            fallthru
        case 2 then
            -- do something extra
        case 3 then
            -- do something usual

        case else
            -- do something else
    end switch

The `label "name"` is optional and if used it gives a name to the switch block. This name can be used in nested switch `break` statements to break out of an enclosing switch rather than just the owning switch.
Example:

    switch opt label "LBLa" do
        case 1, 5, 8 then
            FuncA()

        case 4, 2, 7 then
            FuncB()
            switch alt label "LBLb" do
               case "X" then
                    FuncC()
                    break "LBLa"

               case "Y" then
                    FuncD()

               case else
                    FuncE()
           end switch
           FuncF()

        case 3 then
           FuncG()
           break

        case else
            FuncH()
    end switch
    FuncM()

In the above, if opt is 2 and alt is "X" then it runs...

FuncB() FuncC() FuncM()

But if opt is 2 and alt is "Y" then it runs ...

FuncB() FuncD() FuncF() FuncM()

In other words, the `break "LBLa"` skips to the end of the switch called "LBLa" rather than the switch called "LBLb".

### 4.4.3 ifdef statement

The `ifdef` statement has a similar syntax to the `if` statement.

    ifdef SOME_WORD then
     --... zero or more statements
    elsifdef SOME_OTHER_WORD then
     --... zero or more statements
    elsedef
     --... zero or more statements
    end ifdef

Of course, the `elsifdef` and `elsedef` clauses are optional, just like `elsif` and `else` are option in an `if` statement.

The major differences between and `if` and `ifdef` statement are that `ifdef` is executed at parse time not runtime, and `ifdef` can only test for the existence of a defined word whereas `if` can test any boolean expression.

Note that since the `ifdef` statement executes at parse time, run-time values cannot be checked, only words defined by the `-D` command line switch, or by the `with define` directive, or one of the special predefined words.

The purpose of `ifdef` is to allow you to change the way your program operates in a very efficient manner. Rather than testing for a specific condition repeatedly during the running of a program, `ifdef` tests for it once during parsing and then generates the precise IL code to handle the condition.

For example, assume you have some debugging code in your application that displays information to the screen. Normally you would not want to see this display so you set a condition so it only displays during a 'debug' session. The first example below shows how would could do this just using the `if` statement, and the second example shows the same thing but using the `idef` statement.

    -- Example 1. --
    if find("-DEBUG", command_line()) then
        writefln("Debug x=[], y=[]", {x,y})
    end if

    -- Example 1. --
    ifdef DEBUG then
        writefln("Debug x=[], y=[]", {x,y})
    end ifdef

As you can see, they are almost identical. However, in the first example, everytime the program gets to this point in the code, it tests the command line for the -DEBUG switch before deciding to display the information or not. But in the second example, the existence of DEBUG is tested once at parse time, and if it exists then, Euphoria generates the IL code to do the display. Thus when the program is running then everytime it gets to this point in the code, it does not check that DEBUG exists, instead it already knows it does so it just does the display. If however, DEBUG did not exist at parse time, then the IL code for the display would simply be omitted, meaning that during the running of the program, when it gets to this point in the code, it does not recheck for DEBUG, instead it already knows it doesn't exist and the IL code to do the display also doesn't exist so nothing is displayed. This can be a much needed performance boost for a program.

Euphoria predefines some words itself:

#### 4.4.3.1 Euphoria Version Definitions

- EU4 - Major Euphoria Version
- EU4_1 - Major and Minor Euphoria Version
- EU4_1_0 - Major, Minor and Release Euphoria Version

Euphoria is released with the common version scheme of Major, Minor and Release version identifiers in the form of major.minor.release. When 4.1.1 is released, `EU4_1_1` will be defined and `EU4_1` will still be defined, but `EU4_1_0` will no longer be defined. When 4.2 is released, `EU4_1` will no longer be defined, but `EU4_2` will be defined. Finally, when 5.0 is released, `EU4` will no longer be defined, but `EU5` will be defined.

#### 4.4.3.2 Platform Definitions

- CONSOLE - Euphoria is being executed with the Console version of the interpreter (on windows, eui.exe, others are eui)
- GUI - Platform is Windows and is being executed with the GUI version of the interpreter (euiw.exe)
- WINDOWS - Platform is Windows (GUI or Console)
- LINUX - Platform is Linux
- OSX - Platform is Mac OS X
- FREEBSD - Platform is FreeBSD
- OPENBSD - Platform is OpenBSD
- NETBSD - Platform is NetBSD
- BSD - Platform is a BSD variant (FreeBSD, OpenBSD, NetBSD and OS X)
- UNIX - Platform is any Unix

#### 4.4.3.3 Architecture Definitions

Chip architecture:

- X86
- X86_64
- ARM

Size of pointers and euphoria objects. This information can be derived from the chip architecture, but is provided for convenience.

- BITS32
- BITS64

Size of long integers. On Windows, long integers are always 32 bits. On other platforms, long integers are the same size as pointers. This information can also be derived from a combination of other architecture and platform ifdefs, but is provided for convenience.

- LONG32
- LONG64

#### 4.4.3.4 Application Definitions

- EUI - Application is being interpreted by `eui`.
- EUC - Application is being translated by `euc`.
- EUC_DLL - Application is being translated by `euc` into a DLL file.
- EUB - Application is being converted to a bound program by `eub`.
- EUB_SHROUD - Application is being converted to a shrouded program by `eub`.
- CONSOLE - Application is being translated, or converted to a bound console program by `euc` or `eub`, respectively.
- GUI - Application is being converted to a bound Windows GUI program by `eub`.

#### 4.4.3.5 Library Definitions

- DATA_EXECUTE - Application will always get executable memory from `allocate` even when the system has Data Execute Protection enabled for the Euphoria Interpreter.
- SAFE - Enables safe runtime checks for operations for routines found in `machine.e` and `dll.e`
- UCSTYPE_DEBUG - Found in `include/std/ucstypes.e`
- CRASH - Found in `include/std/unittest.e`

More examples

    -- file: myproj.ex
    puts(1, "Hello, I am ")
    ifdef EUC then
        puts(1, "a translated")
    end ifdef
    ifdef EUI then
        puts(1, "an interpreted")
    end ifdef
    ifdef EUB then
        puts(1, "a bound")
    end ifdef
    ifdef EUB_SHROUD then
        puts(1, ", shrouded")
    end ifdef
    puts(1, " program.\n")

    C:\myproj> eui myproj.ex
    Hello, I am an interpreted program.
    C:\myproj> euc -con myprog.ex
    ... translating ...
    ... compiling ...
    C:\myproj> myprog.exe
    Hello, I am a translated program.
    C:\myproj> bind myprog.ex
    ...
    C:\myproj> myprog.exe
    Hello, I am a bound program.
    C:\myproj> shroud myprog.ex
    ...
    C:\myproj> eub myprog.il
    Hello, I am a bound, shrouded program.

It is possible for one or more of the above definitions to be true at the same time. For instance, `EUC` and `EUC_DLL` will both be true when the source file has been translated to a DLL. If you wish to know if your source file is translated and not a DLL, then you can

    ifdef EUC and not EUC_DLL then
        -- translated to an application
    end ifdef

#### 4.4.3.6 Using ifdef

You can define your own words either in source:

    with define MY_WORD       -- defines
    without define OTHER_WORD -- undefines

or by command line:

    eui -D MY_WORD myprog.ex

This can handle many tasks such as change the behavior of your application when running on Linux vs. Windows, enable or disable debug style code or possibly work differently in demo/shareware applications vs. registered applications.

You should surround code that is not portable with `ifdef` like:

    ifdef WINDOWS then
       -- Windows specific code.
    elsedef
       include std/error.e
       crash("This program must be run with the Windows interpreter.")
    end ifdef

When writing include files that you cannot run on some platform, issue a crash call in the include file. Yet make sure that public constants and procedures are defined for the unsupported platform as well.

    ifdef UNIX then
         include std/bash.e
    end ifdef

    -- define exported and public constants and procedures for
    -- OSX as well
    ifdef WINDOWS or OSX then
        -- OSX is not supported but we define public symbols for it anyhow.

The reason for doing this is so that the user that includes your include file sees an "OS not supported" message instead of an "undefined reference" message.

Defined words must follow the same character set of an identifier, that is, it must start with either a letter or underscore and contain any mixture of letters, numbers and underscores. It is common for defined words to be in all upper case, however, it is not required.

A few examples:

    for a = 1 to length(lines) do
        ifdef DEBUG then
            printf(1, "Line %i is %i characters long\n", {a, length(lines[a])})
        end ifdef
    end for

    sequence os_name
    ifdef UNIX then
        include unix_ftp.e
    elsifdef WINDOWS then
        include win32_ftp.e
    elsedef
        crash("Operating system is not supported")
    end ifdef

    ifdef SHAREWARE then
      if record_count > 100 then
         message("Shareware version can only contain 100 records. Please register")
         abort(1)
      end if
    end ifdef

The `ifdef` statement is very efficient in that it makes the decision only once during parse time and only emits the `TRUE` portions of code to the resulting interpreter. Thus, in loops that are iterated many times there is zero performance hit when making the decision. Example:

    while 1 do
        ifdef DEBUG then
            puts(1, "Hello, I am a debug message\n")
        end ifdef
        -- more code
    end while

If `DEBUG` is defined, then the interpreter/translator actually sees the code as being:

    while 1 do
        puts(1, "Hello, I am a debug message\n")
        -- more code
    end while

Now, if `DEBUG` is not defined, then the code the interpreter/translator sees is:

    while 1 do
        -- more code
    end while

Do be careful to put the numbers after the platform names for Windows:

    -- This puts() routine will never be called
    -- even when run by the Windows interpreter!
    ifdef WINDOWS then
         puts(1,"I am on Windows\n")
    end ifdef

| Up |  |  | TOC | Index
<< 3 Using Euphoria | < 4.3 Assignment statement | Up: 4 Language Reference | 4.5 Loop statements > | 5 Formal Syntax >>