home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
euphoria
/
refman.doc
< prev
next >
Wrap
Text File
|
1994-03-10
|
79KB
|
2,008 lines
Euphoria Programming Language
version 1.2
Reference Manual
(c) 1994 Rapid Deployment Software
Permission is freely granted to anyone
to copy this manual.
TABLE OF CONTENTS
=================
1. Introduction
1.1 Example Program
1.2 Installation
1.3 How To Run A Program
1.4 How To Edit A Program
1.5 How To Distribute A Program
2. Core Language
2.1 Objects
2.2 Expressions
2.3 Declarations
2.4 Statements
2.5 Top-Level Commands
3. Debugging
4. Built-in Routines
4.1 Predefined Types
4.2 Sequence Manipulation
4.3 Searching and Sorting
4.4 Math
4.5 File and Device I/O
4.6 Mouse Support
4.7 Operating System
4.8 Debugging
4.9 Graphics & Sound
4.10 Machine Level Interface
1. Introduction
===============
Euphoria is a new programming language with the following advantages over
conventional languages:
o a remarkably simple, flexible, powerful language
definition that is extremely easy to learn and use.
o dynamic storage allocation. Variables grow or shrink
without the programmer having to worry about allocating
and freeing chunks of memory. Objects of any size can be
assigned to an element of a Euphoria sequence (array).
o lightning-fast pre-compilation. Your program is checked
for syntax and converted into an efficient internal form at
over 12,000 lines per second on a 486-50.
o a high-performance, state-of-the-art interpreter that is
10 to 20 times faster than conventional interpreters such as
Microsoft QBasic.
o extensive run-time checking for: out-of-bounds subscripts,
uninitialized variables, bad parameter values for built-in
functions, illegal value assigned to a variable and many
more. There are no mysterious machine exceptions -- you
will always get a full English description of any problem
that occurs with your program at run-time, along with a
call-stack trace-back and a dump of all of your variable
values. Programs can be debugged quickly, easily and
more thoroughly.
o features of the underlying hardware are completely
hidden. Programs are not aware of word-lengths,
underlying bit-level representation of values, byte-order
etc. Euphoria programs are therefore highly portable from
one machine to another.
o a full-screen source debugger and an execution profiler
are included, along with a full-screen editor. On a color
monitor, the editor displays Euphoria programs in
multiple colors, to highlight comments, reserved words,
built-in functions, strings, and level of nesting of brackets.
It optionally performs auto-completion of statements,
saving you typing effort and reducing syntax errors. This
editor is written in Euphoria, and the source code is
provided to you without restrictions. You are free to
modify it, add features, and redistribute it as you wish.
o Euphoria programs run under MS-DOS (or Windows), but are not
subject to any 64K or 640K memory limitations. You can
create programs that use the full multi-megabyte memory
of your computer. You can even set up a swap file for
programs that need more memory than exists on your machine.
A least-recently-used paging algorithm will automatically
shuffle pages of virtual memory in and out as your program
executes.
o Euphoria routines are naturally generic. The example
program below shows a single routine that will sort any
type of data -- integers, floating-point numbers, strings
etc. Euphoria is not an "Object-Oriented" language in the
usual sense, yet it achieves many of the benefits of these
languages in a much simpler way.
1.1 Example Program
-------------------
The following is an example of a complete Euphoria program.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sequence list, sorted_list
function merge_sort(sequence x)
-- put x into ascending order using a recursive merge sort
integer n, mid
sequence merged, x1, x2
n = length(x)
if n = 0 or n = 1 then
return x -- trivial case
end if
mid = floor(n/2)
x1 = merge_sort(x[1..mid]) -- sort first half of x
x2 = merge_sort(x[mid+1..n]) -- sort second half of x
-- merge the two sorted halves into one
merged = {}
while length(x1) > 0 and length(x2) > 0 do
if compare(x1[1], x2[1]) < 0 then
merged = append(merged, x1[1])
x1 = x1[2..length(x1)]
else
merged = append(merged, x2[1])
x2 = x2[2..length(x2)]
end if
end while
return merged & x1 & x2 -- merged data plus leftovers
end function
procedure print_sorted_list()
-- generate sorted_list from list
list = {9, 10, 3, 1, 4, 5, 8, 7, 6, 2}
sorted_list = merge_sort(list)
? sorted_list
end procedure
print_sorted_list() -- this command starts the program
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The above example contains 4 separate commands that are processed in order.
The first declares two variables: list and sorted_list to be sequences.
The second defines a function merge_sort(). The third defines a procedure
print_sorted_list(). The final command calls procedure print_sorted_list().
The output from the program will be:
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.
merge_sort() will just as easily sort {1.5, -9, 1e6, 100} or
{"oranges", "apples", "bananas"} .
This example is stored as euphoria\demo\example.ex. This is not the fastest
way to sort in Euphoria. Go into the euphoria\demo directory and type
"ex allsorts" to see timings on several different sorting algorithms for
increasing numbers of objects. For a quick tutorial on Euphoria programming
see euphoria\demo\bench\filesort.ex.
1.2 Installation
----------------
To install Euphoria on your machine, first read the file install.doc.
Installation simply involves copying the euphoria files to your hard disk
under a directory named "EUPHORIA", and then modifying your autoexec.bat file
so that EUPHORIA\BIN is on your search path, and the environment variable
EUDIR is set to the EUPHORIA directory. An automatic install program,
"install.bat" is provided for this purpose. For the latest details, please
read the instructions in install.doc before you run install.bat.
When installed, the euphoria directory will look something like this:
euphoria
readme.doc
\bin
ex.exe, dos4gw.exe, ed.bat, other utilities
\include
standard include files, e.g. graphics.e
\doc
ed.doc, refman.doc etc.
\demo
demo programs, e.g. ttt.ex, mset.ex, plot3d.ex
\learn
test your knowledge of Euphoria
\langwar
language war game, lw.ex
\bench
benchmark programs
1.3 How to Run a Program
------------------------
Euphoria programs are executed by typing "ex", followed by the name of the
main (or only) file. By convention, main Euphoria files have an extension of
".ex". Other Euphoria files, that are meant to be included in a larger
program, end in ".e". To save typing, you can leave off the ".ex", and
the ex command will supply it for you automatically. If the file can't be
found in the current directory, your PATH will be searched. There are no
command-line options for ex itself, but your program can call the built-in
function command_line() to read the ex command-line. You can redirect
standard input and standard output when you run a Euphoria program,
for example:
ex filesort.ex < raw > sorted
or simply,
ex filesort < raw > sorted
For frequently-used programs you might want to make a small .bat file
containing something like:
@echo off
ex myprog.ex %1 %2
where myprog.ex expects two command-line arguments. This will save you
from typing ex all the time.
ex.exe is in the euphoria\bin directory which must be on your search path.
The file dos4gw.exe must also be present in the bin directory. Some
Euphoria programs expect the environment variable EUDIR to be set to the
main Euphoria directory.
Running Under Windows
---------------------
You can run Euphoria programs directly from the Windows environment, or from
a DOS shell that you have opened from Windows. By "associating" .ex files
with ex.exe, you can simply double-click on a .ex file to run it. It
is possible to have several Euphoria programs active in different windows.
You can resize these windows, move them around, change to a different font,
run things in the background, copy and paste between windows etc. See your
Windows manual. The Euphoria editor is available. You might want to
associate .e, .pro and other text files with ed.bat. Also, the File-menu/
Run-command will let you type in ex or ed command lines.
Use of a swap file
------------------
If your program requires more memory than is physically present on your
machine, you can easily set up a swap file to provide extra "virtual"
memory under DOS. All you have to do is define the environment variable
DOS4GVM. The MS-DOS command: set DOS4GVM=1 will allow a 16 megabyte swap
file to be created, which will be used as virtual memory. Portions of your
program and data that have not been recently used will be copied out to this
file to create room in physical memory for actively-used information. If you
can't afford the default 16 megabytes of disk space, you could:
set DOS4GVM=virtualsize#8192 to get an 8 megabyte swap file, or specify a
smaller number if this is still too much. To turn off virtual memory,
you can: set DOS4GVM= after your program has finished executing. The swap
file can be deleted after execution, but leaving it in place will let your
next program start up faster.
When you run under Windows, virtual memory swapping will be performed
by Windows itself, and you may actually get more memory than DOS (without
swap file) would provide.
1.4 How to Edit a Program
-------------------------
You can use any text editor to edit a Euphoria program. However, Euphoria
comes with its own special editor which is written entirely in Euphoria.
Type ed followed by the complete name of the file you wish to edit (the
.ex extension is not assumed). You can use this editor to edit any kind of
text file. When you edit a .e or .ex file some extra features, such as color
syntax highlighting and auto-completion of certain statements, are available
to make your job easier.
Whenever you run a Euphoria program and get an error message, during
compilation or execution, you can simply type ed with no file name and you
will be automatically positioned in the file containing the error, at
the correct line and column, and with the error message displayed at the
top of the screen.
Under Windows you can associate ed.bat with various kinds of text files
that you want to edit.
Most keys that you type are inserted into the file at the cursor position.
Hit the Esc key once to get a menu bar of special commands. The arrow keys,
and the Insert/Delete/Home/End/PageUp/PageDown keys are also active. See
the file euphoria\doc\ed.doc for a complete description of the editing
commands. Esc h (help) will let you view ed.doc from your editing session.
If you need to understand or modify any detail of the editor's operation,
you can edit the file ed.ex in euphoria\bin (be sure to make a backup
copy so you don't lose your ability to edit). If the name ed conflicts
with some other command on your system, simply rename the file
euphoria\bin\ed.bat to something else. Because this editor is written
in Euphoria, it is remarkably concise and easy to understand. The same
functionality implemented in a language like C, would take far more
lines of code.
1.5 How To Distribute A Program
-------------------------------
Your customer needs to have the 2 files: ex.exe and dos4gw.exe somewhere
on the search path. You are free to supply anyone with the Public Domain
Edition of ex.exe, as well as dos4gw.exe to support it.
Your program can be distributed in source form or in shrouded form. In source
form you supply your Euphoria files plus any standard include files that are
required. To deliver a program in shrouded form, you run the Euphoria source
code shrouder, bin\shroud.ex, against your main Euphoria file. The shrouder
pulls together all included files into a single compact file that is
virtually unreadable. You then ship this one file plus a copy of ex.exe and
dos4gw.exe. One copy of ex.exe and dos4gw.exe on a machine is sufficient to
run any number of Euphoria programs. Comments in bin\shroud.ex tell you how
to run it, and what it does to obscure or "shroud" your source.
2. The Core Language
====================
2.1 Objects
-----------
All data objects in Euphoria are either atoms or sequences. An atom is a
single numeric value. A sequence is an ordered list of data objects.
The objects contained in a sequence can be an arbitrary mix of atoms or
sequences. A sequence is represented by a list of objects in brace brackets,
separated by commas. Atoms can have any double-precision floating point value.
This is approximately -1e300 to +1e300 with 15 decimal digits of accuracy.
Here are some Euphoria objects:
-- examples of atoms:
0
1000
98.6
-1e6
-- examples of sequences:
{2, 3, 5, 7, 11, 13, 17, 19} -- 8-element sequence
{1, 2, {3, 3, 3}, 4, {5, {6}}} -- 5-element sequence
{{"jon", "smith"}, 52389, 97.25} -- 3-element sequence
{} -- 0-element sequence
Numbers can also be entered in hexadecimal. For example:
#FE -- 254
#A000 -- 40960
#FFFF00008 -- 68718428168
-#10 -- -16
Sequences can be nested to any depth. Brace brackets are used to construct
sequences out of a list of expressions. These expressions are evaluated at
run-time. e.g.
{x+6, 9, y*w+2, sin(0.5)}
Performance Note: Although atoms can have any double-precision value,
integer atoms are generally stored and manipulated as machine integers
to save time and space.
Character Strings
-----------------
Character strings may be entered using quotes e.g.
"ABCDEFG"
Strings are just sequences of characters, and may be manipulated and
operated upon just like any other sequences. For example the above
string is equivalent to the sequence
{65, 66, 67, 68, 69, 70, 71}
which contains the corresponding ASCII codes. Individual characters may be
entered using single quotes if it is desired that they be treated as
individual numbers (atoms) and not length-1 sequences. e.g.
'B' -- equivalent to the atom 66
"B" -- equivalent to the sequence {66}
Note that an atom is not equivalent to a one-element sequence containing
the same value, although there are a few built-in routines that choose
to treat them similarly.
Special characters may be entered using a back-slash:
\n newline
\r carriage return
\t tab
\\ backslash
\" double quote
\' single quote
For example, "Hello, World!\n", or '\\'. The Euphoria editor displays
character strings in brown.
Comments
--------
Comments are started by two dashes and extend to the end of the current line.
e.g.
-- this is a comment
Comments are ignored by the compiler and have no effect on execution speed.
The editor displays comments in red. In this manual we use italics.
2.2 Expressions
---------------
Objects can be combined into expressions using binary and unary operators as
well as built-in and user-defined functions. For example,
{1,2,3} + 5
is an expression that adds the sequence {1,2,3} and the atom 5 to get the
resulting sequence {6,7,8}. Besides + there are many other operators. The
precedence of operators is as follows:
highest precedence: function/type calls
unary - not
* /
+ -
&
< > <= >= = !=
lowest precedence: and, or
Thus 2+6*3 means 2+(6*3), not (2+6)*3. Operators on the same line above have
equal precedence and are evaluated left to right.
Relational & Logical Operators
------------------------------
The relational operators, <, >, <=, >=, = , != each produce a 1 (true) or a
0 (false) result. These results can be used by the logical operators 'and',
'or', and 'not' to determine an overall truth value. e.g.
b > 0 and b != 100 or not (c <= 5)
where b and c are the names of variables.
Subscripting of Sequences
-------------------------
A single element of a sequence may be selected by giving the element number
in square brackets. Element numbers start at 1. Non-integer subscripts are
rounded down to an integer.
For example, if x contains {5, 7, 9, 11, 13} then x[2] is 7. Suppose we
assign something different to x[2]:
x[2] = {11,22,33}
Then x becomes: {5, {11,22,33}, 9, 11, 13}. Now if we ask for x[2] we get
{11,22,33} and if we ask for x[2][3] we get the atom 33. If you try to
subscript with a number that is outside of the range 1 to the number of
elements, you will get a subscript error. For example x[0], x[-99] or
x[6] will cause errors. So will x[1][3] since x[1] is not a sequence. There
is no limit to the number of subscripts that may follow a variable, but
the variable must contain sequences that are nested deeply enough. The
two dimensional array, common in other languages, can be easily simulated
with a sequence of sequences:
{ {5, 6, 7, 8, 9},
{1, 2, 3, 4, 5},
{0, 1, 0, 1, 0} }
An expression of the form x[i][j] can be used to access any element. The two
dimensions are not symmetric however, since an entire "row" can be selected
with x[i], but there is no simple expression to select an entire column.
Other logical structures, such as n-dimensional arrays, arrays of strings,
arrays of structures etc. can also be handled easily and flexibly.
Note that expressions in general may not be subscripted, just variables. For
example: {5,6,7,8}[3] is not supported.
Slicing of Sequences
--------------------
A sequence of consecutive elements may be selected by giving the starting and
ending element numbers. For example if x is {1, 1, 2, 2, 2, 1, 1, 1} then
x[3..5] is the sequence {2, 2, 2}. x[3..3] is the sequence {2}. x[3..2] is
also allowed. It evaluates to the length-0 sequence {}. If y has the value:
{"fred", "george", "mary"} then y[1..2] is {"fred", "george"}.
We can also use slices for overwriting portions of variables. After x[3..5] =
{9, 9, 9} x would be {1, 1, 9, 9, 9, 1, 1, 1}. We could also have said
x[3..5] = 9 with the same effect. Suppose y is {0, "Euphoria", 1, 1}.
Then y[2][1..4] is "Euph". If we say y[2][1..4]="ABCD" then y will
become {0, "ABCDoria", 1, 1}.
We need to be a bit more precise in defining the rules for empty slices.
Consider a slice s[i..j] where s is of length n. A slice from i to j,
where j = i-1 and i >= 1 produces the empty sequence, even if i = n+1.
Thus 1..0 and n+1..n and everything in between are legal (empty) slices.
Empty slices are quite useful in many algorithms. A slice from i to j where
j < i - 1 is illegal , i.e. "reverse" slices such as s[5..3] are not allowed.
Concatenation of Sequences and Atoms
------------------------------------
Any two objects may be concatenated using the & operator. The result is a
sequence with a length equal to the sum of the lengths of the concatenated
objects (where atoms are considered to have length 1). e.g.
{1, 2, 3} & 4 -- result is {1, 2, 3, 4}
4 & 5 -- result is {4, 5}
{{1, 1}, 2, 3} & {4, 5} -- result is {{1, 1}, 2, 3, 4, 5}
x = {}
y = {1, 2}
y = y & x -- y is still {1, 2}
Arithmetic Operations on Sequences
----------------------------------
Any binary or unary arithmetic operation, including any of the built-in
math routines, may be applied to entire sequences as well as to single
numbers.
When applied to a sequence, a unary operator is actually applied to each
element in the sequence to yield a sequence of results of the same length.
If one of these elements is itself a sequence then the same rule is applied
recursively. e.g.
x = -{1, 2, 3, {4, 5}} -- x is {-1, -2, -3, {-4, -5}}
If a binary operator has operands which are both sequences then the two
sequences must be of the same length. The binary operation is then applied
to corresponding elements taken from the two sequences to get a sequence of
results. e.g.
x = {5, 6, 7 {1, 1}} + {10, 10, 20, 100}
-- x is {15, 16, 27, {101, 101}}
If a binary operator has one operand which is a sequence while the other is a
single number (atom) then the single number is effectively repeated to
form a sequence of equal length to the sequence operand. The rules for
operating on two sequences then apply. Some examples:
y = {4, 5, 6}
w = 5 * y -- w is {20, 25, 30}
x = {1, 2, 3}
z = x + y -- z is {5, 7, 9}
z = x < y -- z is {1, 1, 1}
w = {{1, 2}, {3, 4}, {5}}
w = w * y -- w is {{4, 8}, {15, 20}, {30}}
Comparison of Euphoria Objects with Other Languages
---------------------------------------------------
By basing Euphoria on this one, simple, general, recursive data structure,
a tremendous amount of the complexity normally found in programming languages
has been avoided. The arrays, record structures, unions, arrays of records,
multidimensional arrays, etc. of other languages can all be easily
simulated in Euphoria with sequences. So can higher-level structures such
as lists, stacks, queues, trees etc.
Furthermore, in Euphoria you can have sequences of mixed type; you can
assign any object to an element of a sequence; and sequences easily grow or
shrink in length without your having to worry about storage allocation issues.
The exact layout of a data structure does not have to be declared in advance,
and can change dynamically as required. It is easy to write generic code,
where, for instance, you push or pop a mix of various kinds of data
objects using a single stack.
Data structure manipulations are very efficient since Euphoria will point to
large data objects rather than copy them.
Programming in Euphoria is based entirely on creating and manipulating
flexible, dynamic sequences of data. Sequences are it - there are no
other data structures to learn. You operate in a simple, safe, elastic world
of *values*, that is far removed from the rigid, tedious, dangerous world
of bits, bytes, pointers and machine crashes.
Unlike other languages such as LISP and Smalltalk, Euphoria's
"garbage collection" of unused storage is a continuous process that never
causes random delays in execution of a program, and does not pre-allocate
huge regions of memory.
The language definitions of conventional languages such as C, C++, Ada, etc.
are very complex. Most programmers become fluent in only a subset of the
language. The ANSI standards for these languages read like complex legal
documents.
You are forced to write different code for different data types simply to
copy the data, ask for its current length, concatenate it, compare it etc.
The manuals for those languages are packed with routines such as "strcpy",
"strncpy", "memcpy", "strcat", "strlen", "strcmp", "memcmp", etc. that
each only work on one of the many types of data.
Much of the complexity surrounds issues of data type. How do you define
new types? Which types of data can be mixed? How do you convert one type
into another in a way that will keep the compiler happy? When you need to
do something requiring flexibility at runtime, you frequently find yourself
trying to fake out the compiler.
In these languages the numeric value 4 (for example) can have a different
meaning depending on whether it is an int, a char, a short, a double, an
int * etc.. In Euphoria, 4 is the atom 4, period. Euphoria has something
called types as we shall see later, but it is a much simpler concept.
Issues of dynamic storage allocation and deallocation consume a great deal
of programmer coding time and debugging time in these other languages, and
make the resulting programs much harder to understand.
Pointer variables are extensively used. The pointer has been called the
"go to" of data structures. It forces programmers to think of data as
being bound to a fixed memory location where it can be manipulated in all
sorts of low-level non-portable, tricky ways. A picture of the actual
hardware that your program will run on is never far from your mind. Euphoria
does not have pointers and does not need them.
2.3 Declarations
----------------
Identifiers
-----------
Variable names and other user-defined symbols (identifiers) may be of any
length. Upper and lower case are distinct. Identifiers must start with a
letter and then be followed by letters, digits or underscores. The
following reserved words have special meaning in Euphoria and may not be
used as identifiers:
and end include then
by exit not to
constant for or type
do function procedure while
else global profile with
elsif if return without
The Euphoria editor displays these words in blue. In this manual we use
boldface.
The following kinds of user-defined symbols may be declared in a program:
o procedures
These perform some computation and may have a list of parameters,
e.g.
procedure empty()
end procedure
procedure plot(integer x, integer y)
position(x, y)
puts(1, '*')
end procedure
There are a fixed number of named parameters, but this is not
restrictive since any parameter could be a variable-length sequence
of arbitrary objects. In many languages variable-length parameter
lists are impossible. In C, you must set up strange mechanisms that
are complex enough that the average programmer cannot do it without
consulting a manual or a local guru.
A copy of the value of each argument is passed in. The formal
parameter variables may be modified inside the procedure but this does
not affect the value of the arguments.
Performance Note: The interpreter does not copy sequences unless it
becomes necessary. For example,
y = {1,2,3,4,5,6,7}
x = y
The statement x = y does not cause the value of y to be copied.
Both x and y will simply "point" to the same data.
If we later perform x[3] = 9, then x will be given its own separate
copy. The same thing applies to "copies" of arguments passed in to
subroutines.
o functions
These are just like procedures, but they return a value, and can be
used in an expression, e.g.
function max(atom a, atom b)
if a >= b then
return a
else
return b
end if
end function
Any Euphoria object can be returned. You can, in effect, have
multiple return values, by returning a sequence of objects. e.g.
return {quotient, remainder}
We will use the general term "subroutine", or simply "routine" when a
remark is applicable to both procedures and functions.
o types
These are special functions that may be used in declaring the allowed
values for a variable. A type must have exactly one parameter and
should return an atom that is either TRUE (non-zero) or FALSE (zero).
Types can also be called just like other functions. They are discussed
in more detail below.
o variables
These may be assigned values during execution e.g.
integer x
x = 25
object a, b, c
a = {}
b = a
c = 0
o constants
These are variables that are assigned an initial value that can
never change e.g.
constant MAX = 100
constant upper = MAX - 10, lower = 5
The result of any expression can be assigned to a constant,
even one involving calls to previously defined functions, but once
the assignment is made the value of the constant variable is
"locked in".
Scope
-----
Every symbol must be declared before it is used. This is restrictive, but it
has benefits. It means you always know in which direction to look for the
definition of a subroutine or variable that is used at some point in the
program. When looking at a subroutine definition, you know that there could
not be a call to this routine from any routine defined earlier. In general,
it forces you to organize your program into a hierarchy where there are
distinct, "layers" of low-level , followed by higher-level routines. You
can replace a layer without disrupting any lower layers.
A symbol is defined from the point where it is declared to the end of its
scope. The scope of a variable declared inside a procedure or function (a
private variable) ends at the end of the procedure or function. The scope
of all other constants, procedures, functions and variables ends at the end
of the source file in which they are declared and they are referred to as
local, unless the word global precedes their declaration, in which case their
scope extends indefinitely. Procedures and functions can call themselves
recursively.
Constant declarations must be outside of any function or procedure.
A special case is that of the controlling variable used in a for-loop. It is
automatically declared at the beginning of the loop, and its scope ends at
the end of the for loop. If the loop is inside a function or procedure, the
loop variable is a private variable and may not have the same name as any
other private variable. When the loop is at the top level, outside of any
function or procedure, the loop variable is a local variable and may not have
the same name as any other global or local variable in that file. You do not
declare loop variables as you would other variables. The range of values
specified in the for statement defines the legal values of the loop variable
- specifying a type would be redundant and is not allowed..
Specifying the type of a variable
---------------------------------
Variable declarations consist of a type name followed by a list of
the variables being declared. For example,
global integer x, y, z
object a
procedure fred(sequence q, sequence r)
In a parameter list like the one above, the type name may only be followed by
a single variable name.
The types: object, sequence, atom and integer are predefined. Variables of
type object may take on any value. Those declared with type sequence must be
always be sequences. Those declared with type atom must always be atoms. Those
declared with type integer must be atoms with integer values from -1073709056
to +1073709055 inclusive. You can perform exact calculations on larger integer
values, up to about 15 decimal digits, but declare them as atom, rather than
integer.
Performance Note: Calculations using integer variables will usually be
somewhat faster than calculations involving atom variables. If your
machine has floating-point hardware, Euphoria will use it to manipulate
atoms that aren't representable as integers, otherwise floating-point
emulation routines contained in ex.exe are used.
To augment the predefined types, you can create new types. All you have to
do is define a single-parameter function, but declare it with
type ... end type instead of function ... end function. For example,
type hour(integer x)
return x >= 0 and x <= 23
end type
hour h1, h2
This guarantees that variables h1 and h2 can only be assigned integer values
in the range 0 to 23 inclusive. After an assignment to h1 or h2 the
interpreter will call "hour()", passing the new value. The parameter x will
first be checked to see if it is an integer. If it is, the return statement
will be executed to test the value of x (i.e. the new value of h1 or h2).
If "hour" returns true, execution continues normally. If "hour" returns false
then the program is aborted with a suitable diagnostic message.
procedure set_time(hour h)
set_time() above can only be called with a reasonable value for parameter h.
A variable's type will be checked after each assignment to the variable
(except where the compiler can predetermine that a check will not be
necessary), and the program will terminate immediately if the type function
returns false. Subroutine parameter types are checked when the subroutine
is called. This checking guarantees that a variable can never have a value
that does not belong to the type of that variable.
Unlike other languages, the type of a variable does not affect any
calculations on the variable. Only the value of the variable matters in an
expression. The type just serves as an error check to prevent any "corruption"
of the variable.
Type checking can be turned off or on in between subroutines using the
with type_check or without type_check commands. It is initially on by default.
Note to Benchmarkers: When comparing the speed of Euphoria programs against
programs written in other languages, you should specify without type_check
at the top of the file, unless the other language provides a comparable
amount of run-time checking. This gives Euphoria permission to skip runtime
type checks, thereby saving some execution time. When type_check is off, all
other checks are still performed, e.g. subscript checking, uninitialized
variable checking etc. Even when you turn off type checking, Euphoria
reserves the right to make checks at strategic places, since this can
actually allow it to run your program faster in many cases. So you may
still get a type check failure even when you have turned off type checking.
With or without type_check, you will never get a machine-level exception.
You will always get a meaningful message from Euphoria when something goes
wrong.
Euphoria's method of defining types is much simpler than what you will find
in other languages, yet Euphoria provides the programmer with greater
flexibility in defining the legal values for a type of data. Any algorithm
can be used to include or exclude values. You can even declare a variable
to be of type object which will allow it to take on any value. Routines can
be written to work with very specific types, or very general types.
Strict type definitions can greatly aid the process of debugging. Logic
errors are caught close to their source and are not allowed to propagate in
subtle ways through the rest of the program. Furthermore, it is much easier
to reason about the misbehavior of a section of code when you are guaranteed
that the variables involved always had a legal value, if not the desired
value.
Types also provide meaningful, machine-checkable documentation about your
program, making it easier for you or others to understand your code at a
later date. Combined with the subscript checking, uninitialized variable
checking, and other checking that is always present, strict run-time type
checking makes debugging much easier in Euphoria than in most other
languages. It also increases the reliability of the final program since
many latent bugs that would have survived the testing phase in other
languages will have been caught by Euphoria.
Anecdote 1: In porting a large C program to Euphoria, a number
of latent bugs were discovered. Although this C program was believed to be
totally "correct", we found: a situation where an uninitialized variable
was being read; a place where element number "-1" of an array was routinely
written and read; and a situation where something was written just off the
screen. These problems resulted in errors that weren't easily visible to a
casual observer, so they had survived testing of the C code.
Anecdote 2: The Quick Sort algorithm presented on page 117 of Writing
Efficient Programs by Jon Bentley has a subscript error! The algorithm will
sometimes read the element just before the beginning of the array to be
sorted, and will sometimes read the element just after the end of the array.
Whatever garbage is read, the algorithm will still work - this is probably
why the bug was never caught. But what if there isn't any (virtual) memory
just before or just after the array? Bentley later modifies the algorithm
such that this bug goes away -- but he presented this version as being
correct. Even the experts need subscript checking!
Performance Note: When typical user-defined types are used extensively, type
checking adds only 20 to 40 percent to execution time. Leave it on unless
you really need the extra speed. You might also consider turning it off for
just a few heavily-executed routines. Profiling can help with this decision.
2.4 Statements
--------------
The following kinds of executable statements are available:
o assignment statement
o procedure call
o if statement
o while statement
o for statement
o return statement
o exit statement
Semicolons are not used in Euphoria, but you are free to put as many
statements as you like on one line, or to split a single statement across
many lines. You may not split a statement in the middle of a variable name,
string, number or keyword.
An assignment statement assigns the value of an expression to a simple
variable, or to a subscript or slice of a variable. e.g.
x = a + b
y[i] = y[i] + 1
y[i..j] = {1, 2, 3}
The previous value of the variable, or element(s) of the subscripted or
sliced variable are discarded. For example, suppose x was a 1000-element
sequence that we had initialized with:
object x
x = repeat(0, 1000) -- repeat 0, 1000 times
and then later we assigned an atom to x with:
x = 7
This is perfectly legal since x is declared as an object. The previous value
of x, namely the 1000-element sequence, would simply disappear. Actually,
the space consumed by the 1000-element sequence will be automatically
recycled due to Euphoria's dynamic storage allocation.
A procedure call starts execution of a procedure, passing it an optional list
of argument values. e.g.
plot(x, 23)
An if statement tests an expression to see if it is 0 (false) or non-zero
(true) and then executes the appropriate series of statements. There may
be optional elsif and else clauses. e.g.
if a < b then
x = 1
end if
if a = 9 then
x = 4
y = 5
else
z = 8
end if
if char = 'a' then
x = 1
elsif char = 'b' then
x = 2
elsif char = 'c' then
x = 3
else
x = -1
end if
A while statement tests an expression to see if it is non-zero (true),
and while it is true a loop is executed. e.g.
while x > 0 do
a = a * 2
x = x - 1
end while
A for statement sets up a special loop with a controlling loop variable
that runs from an initial value up or down to some final value. e.g.
for i = 1 to 10 do
print(1, i)
end for
for i = 10 to 20 by 2 do
for j = 20 to 10 by -2 do
print(1, {i, j})
end for
end for
The loop variable is declared automatically and exists until the end of the
loop. Outside of the loop the variable has no value and is not even declared.
If you need its final value, copy it into another variable before leaving
the loop. The compiler will not allow any assignments to a loop variable. The
initial value, loop limit and increment must all be atoms. If no increment
is specified then +1 is assumed. The limit and increment values are
established when the loop is entered, and are not affected by anything that
happens during the execution of the loop.
A return statement returns from a subroutine. If the subroutine is a function
or type then a value must also be returned. e.g.
return
return {50, "FRED", {}}
An exit statement may appear inside a while loop or a for loop. It causes
immediate termination of the loop, with control passing to the first statement
after the loop. e.g.
for i = 1 to 100 do
if a[i] = x then
location = i
exit
end if
end for
It is also quite common to see something like this:
while TRUE do
...
if some_condition then
exit
end if
...
end while
i.e. an "infinite" while loop that actually terminates via an exit statement
at some arbitrary point in the body of the loop.
2.5 Top-Level Commands
----------------------
Euphoria processes your .ex file in one pass, starting at the first line and
proceeding through to the last line. When a procedure or function definition
is encountered, the routine is checked for syntax and converted into an
internal form, but no execution takes place. When a statement that is outside
of any routine is encountered, it is checked for syntax, converted into an
internal form and then immediately executed. If your .ex file contains only
routine definitions, but no immediate execution statements, then nothing will
happen when you try to run it (other than syntax checking). You need to have
an immediate statement to call your main routine (see the example program in
section 1.1). It is quite possible to have a .ex file with nothing but
immediate statements, for example you might want to use Euphoria as a
desk calculator, typing in just one print (or ? see below) statement into a
file, and then executing it. The langwar demo program
(euphoria\demo\langwar\lw.ex) quickly reads in and displays a file on the
screen, before the rest of the program is compiled (on a 486 this makes
little difference as the compiler takes less than a second to finish
compiling the whole program). Another common practice is to immediately
initialize a global variable, just after its declaration.
The following special commands may only appear at the top level i.e.
outside of any function or procedure. As we have seen, it is also
possible to use any Euphoria statement, including for loops, while loops,
if statements etc. (but not return), at the top level.
include filename - reads in (compiles) a Euphoria source file in the presence
of any global symbols that have already been defined.
Global symbols defined in the included file remain visible
in the remainder of the program. If an absolute pathname
is given, Euphoria will use it. When a relative pathname
is given, Euphoria will first look for filename in the
same directory as the main file given on the ex command
line. If it's not there, it will look in %EUDIR%\include,
where EUDIR is the environment variable that must be set
when using Euphoria. This directory contains the standard
Euphoria include files.
profile - outputs an execution profile showing the number of times
each statement was executed. Only statements compiled
with profile will be shown. The output is placed in the
file ex.pro. View this file with the Euphoria editor to
see a color display.
with - turns on one of the compile options: profile, trace,
warning or type_check. Options warning and type_check are
initially on, while profile and trace are initially off.
These are global options. For example if you have:
without type_check
include graphics.e
then type checking will be turned off inside graphics.e as
well as in the current file.
without - turns off one of the above options. Note that each of
these options may be turned on or off between subroutines
but not inside of a subroutine.
Redirecting Standard Input and Standard Output
----------------------------------------------
Routines such as gets() and puts() can use standard input (file #0),
standard output (file #1), and standard error output (file #2). Standard
input and output can then be redirected as in:
ex myprog < myinput > myoutput
See section 4.5 for more details.
3. Debugging
============
Debugging in Euphoria is much easier than in most other programming languages.
The extensive runtime checking provided at all times by Euphoria automatically
catches many bugs that in other languages might take hours of your time to
track down. When Euphoria catches an error, you will always get a brief
report on your screen, and a detailed report in a file called "ex.err".
These reports always include a full English description of what happened,
along with a call-stack traceback. The file ex.err will also have a dump of
all variable values, and optionally a list of the most recently executed
statements. For extremely large sequences, only a partial dump is shown.
In addition, you are able to create user-defined types that precisely
determine the set of legal values for each of your variables. An error
report will occur the moment that one your variables is assigned an illegal
value.
Sometimes a program will misbehave without failing any runtime checks. In
any programming language it may be a good idea to simply study the source
code and rethink the algorithm that you have coded. It may also be useful
to insert print statements at strategic locations in order to monitor the
internal logic of the program. This approach is particularly convenient in
an interpreted language like Euphoria since you can simply edit the source
and rerun the program without waiting for a recompile/relink.
Euphoria provides you with additional powerful tools for debugging. You
can trace the execution of your program source code on one screen while
you witness the output of your program on another. with trace / without trace
commands select the subroutines in your program that are available for tracing.
Often you will simply insert a with trace command at the very beginning of
your source code to make it all traceable. Sometimes it is better to place
the first with trace after all of your user-defined types, so you don't
trace into these routines after each assignment to a variable. At other times,
you may know exactly which routine or routines you are interested in tracing,
and you will want to select only these ones. Of course, once you are in the
trace window you can interactively skip over the execution of any routine by
pressing down-arrow on the keyboard rather than Enter.
Only traceable lines can appear in ex.err as "most-recently-executed lines"
should a runtime error occur. If you want this information and didn't get it,
you should insert a with trace and then rerun your program. Execution will
be a bit slower when lines compiled with trace are executed.
After you have predetermined the lines that are traceable, your program must
then dynamically cause the trace facility to be activated by executing a
trace(1) statement. Again, you could simply say:
with trace
trace(1)
at the top of your program, so you can start tracing from the beginning of
execution. More commonly, you will want to trigger tracing when a certain
routine is entered, or when some condition arises. e.g.
if x < 0 then
trace(1)
end if
You can turn off tracing by executing a trace(0) statement. You can also
turn it off interactively by typing 'q' to quit tracing. Remember that
with trace must appear outside of any routine, whereas trace(1) and
trace(0) can appear inside a routine or outside.
You might want to turn on tracing from within a type. Suppose you run
your program and it fails, with the ex.err file showing that one of your
variables has been set to a strange, although not illegal value, and you
wonder how it could have happened. Simply create a type for that variable
that executes trace(1) if the value being assigned to the variable is the
strange one that you are interested in.
e.g.
type positive_int(integer x)
if x = 99 then
trace(1) -- how can this be???
return 1 -- keep going
else
return x > 0
end if
end type
You will then be able to see the exact statement that caused your variable
to be set to the strange value, and you will be able to check the values
of other variables. You will also be able to check the output screen to
see what has been happening up to this precise moment. If you make your
special type return 0 for the strange value instead of 1, you can force a
dump into ex.err.
The Trace Screen
----------------
When a trace(1) statement is executed, your main output screen is saved and
a trace screen appears. It shows a view of your program with the statement
that will be executed next highlighted, and several statements before and
after showing as well. Several lines at the bottom of the screen are
reserved for displaying variable names and values. The top line shows the
commands that you can enter at this point:
F1 - display main output screen - take a look at your program's output so far
F2 - redisplay trace screen. Press this key while viewing the main output
screen to return to the trace display.
Enter - execute the currently-highlighted statement only
down-arrow - continue execution and break when any statement coming after
this one in the source listing is executed. This lets you skip
over subroutine calls. It also lets you force your way out of
repetitive loops.
? - display the value of a variable. Many variables are displayed
automatically as they are assigned a value, but sometimes you will have
to explicitly ask for one that is not on display. After hitting ?
you will be prompted for the name of the variable. Variables that are
not defined at this point cannot be shown. Variables that have not yet
been initialized will have <NO VALUE> beside their name.
q - quit tracing and resume normal execution. Tracing will start again when
the next trace(1) is executed.
! - this will abort execution of your program. A traceback and dump of
variable values will go to ex.err.
As you trace your program, variable names and values appear automatically in
the bottom portion of the screen. Whenever a variable is assigned-to you will
see its name and new value appear at the bottom. This value is always kept
up-to-date. Private variables are automatically cleared from the screen
when their routine returns. When the variable display area is full,
least-recently referenced variables will be discarded to make room for
new variables.
The trace screen adopts the same graphics mode as the main output screen.
This makes flipping between them quicker and easier.
When a traced program requests keyboard input, the main output screen will
appear, to let you type your input as you normally would. This works fine for
gets() input. When get_key() (quickly samples the keyboard) is called you
will be given 10 seconds to type a character otherwise it is assumed that
there is no input for this call to get_key(). This allows you to test the
case of input and also the case of no input for get_key().
4. Built-in Routines
====================
Many built-in procedures and functions are provided. The names of these
routines are not reserved. If a user-defined symbol has the same name, it
will override the built-in routine until the end of scope of the
user-defined symbol (you will get a suppressible warning about this). Some
routines are written in Euphoria and you must include one of the .e files in
euphoria\include to use them. Where this is the case, the appropriate include
file is noted. The editor displays in magenta those routines that are part
of the interpreter, ex.exe, and require no include file.
To indicate what kind of object may be passed in and returned, the following
prefixes are used:
fn - an integer used as a file number
a - an atom
i - an integer
s - a sequence
st - a string sequence, or single-character atom
x - a general object (atom or sequence)
An error will result if an illegal argument value is passed to any of these
routines.
4.1 Predefined Types
--------------------
As well as declaring variables with these types, you can also call them
just like ordinary functions, in order to test if a value is a certain type.
i = integer(x)
Return 1 if x is an integer in the range -1073709056 to +1073709055.
Otherwise return 0.
i = atom(x)
Return 1 if x is an atom else return 0.
i = sequence(x)
Return 1 if x is a sequence else return 0.
4.2 Sequence Manipulating Routines
----------------------------------
i = length(s)
Return the length of s. s must be a sequence. e.g.
length({{1,2}, {3,4}, {5,6}}) -- 3
length("") -- 0
length({}) -- 0
Notice that {} and "" both represent the same empty sequence.
As a matter of style, use "" when you are thinking of it as an
empty string of characters. Use {} when it is an empty sequence
in general.
s = repeat(x, a)
Create a sequence of length a where each element is x.
e.g.
repeat(0, 10) -- {0,0,0,0,0,0,0,0,0,0}
repeat("JOHN", 4) -- {"JOHN", "JOHN", "JOHN", "JOHN"}
s2 = append(s1, x)
Append x to the end of sequence s1. The length of s2 will be
length(s1) + 1. If x is an atom this is the same as
s2 = s1 & x. If x is a sequence it is definitely not the same.
You can use append to dynamically grow a sequence e.g.
x = {}
for i = 1 to 10 do
x = append(x, i)
end for
-- x is now {1,2,3,4,5,6,7,8,9,10}
The necessary storage is allocated automatically (and quite
efficiently) with Euphoria's dynamic storage allocation. See also
the gets() example in section 4.5.
s2 = prepend(s1, x)
Prepend x to the start of sequence s1. The length of s2 will be
length(s1) + 1. If x is an atom this is the same as s2 = x & s1.
If x is a sequence it is definitely not the same. e.g.
prepend({1,2,3}, {0,0}) -- {{0,0}, 1, 2, 3}
{0,0} & {1,2,3} -- {0, 0, 1, 2, 3}
4.3 Searching and Sorting
-------------------------
i = compare(x1, x2)
Return 0 if objects x1 and x2 are identical, 1 if x1 is greater
than x2, -1 if x1 is less than x2. Atoms are considered to be less
than sequences. Sequences are compared "alphabetically" starting
with the first element until a difference is found. e.g.
compare("ABC", "ABCD") -- -1
i = find(x, s)
Find x as an element of s. If successful, return the first element
number of s where there is a match. If unsuccessful return 0.
Example:
location = find(11, {5, 8, 11, 2, 3})
-- location is set to 3
names = {"fred", "rob", "george", "mary", ""}
location = find("mary", names)
-- location is set to 4
i = match(s1, s2)
Try to match s1 against some slice of s2. If successful, return
the element number of s2 where the (first) matching slice begins,
else return 0. Example:
location = match("pho", "Euphoria")
-- location is set to 3
include sort.e
x2 = sort(x1)
Sort x into ascending order using a fast sorting algorithm. By
defining your own compare function to override the built-in
compare(), you can change the ordering of values from sort(), and
perhaps choose a field or element number on which to base the sort.
Define your compare() as a global function before including sort.e.
Example:
x = 0 & sort({7,5,3,8}) & 0
-- x is set to {0, 3, 5, 7, 8, 0}
4.4 Math
--------
These routines can be applied to individual atoms or to sequences of values.
See "Arithmetic Operations on Sequences" in chapter 2.
x2 = sqrt(x1)
Calculate the square root of x1.
x2 = rand(x1)
Return a random integer from 1 to x1 where x1 is from 1 to 32767.
x2 = sin(x1)
Return the sin of x1, where x1 is in radians.
x2 = cos(x1)
Return the cos of x1, where x1 is in radians.
x2 = tan(x1)
Return the tan of x1, where x1 is in radians.
x2 = log(x1)
Return the natural log of x1
x2 = floor(x1)
Return the greatest integer less than or equal to x1.
x3 = remainder(x1, x2)
Compute the remainder after dividing x1 by x2. The result will have
the same sign as x1, and the magnitude of the result will be less
than the magnitude of x2.
x3 = power(x1, x2)
Raise x1 to the power x2
Examples:
sin_x = sin({.5, .9, .11}) -- {.479, .783, .110}
? power({5, 4, 3.5}, {2, 1, -0.5}) -- {25, 4, 0.534522}
? remainder({81, -3.5, -9, 5.5}, {8, -1.7, 2, -4})
-- {1, -0.1, -1, 1.5}
4.5 File and Device I/O
-----------------------
To do input or output on a file or device you must first open the file or
device, then use the routines below to read or write to it, then close
the file or device. open() will give you a file number to use as the first
argument of the other I/O routines. Certain files/devices are opened for you
automatically:
0 - standard input
1 - standard output
2 - standard error
Unless you redirect them on the command line, standard input comes from
the keyboard, standard output and standard error go to the screen. When
you write something to the screen it is written immediately without
buffering. If you write to a file, your characters are put into a buffer
until there are enough of them to write out efficiently. When you close
the file or device, any remaining characters are written out. When your
program terminates, any files that are still open will be closed for you
automatically.
fn = open(s1, s2)
Open a file or device, to get the file number. -1 is returned if the
open fails. s1 is the path name of the file or device. s2 is the
mode in which the file is to be opened. Possible modes are:
"r" - open text file for reading
"rb" - open binary file for reading
"w" - create text file for writing
"wb" - create binary file for writing
"u" - open text file for update (reading and writing)
"ub" - open binary file for update
"a" - open text file for appending
"ab" - open binary file for appending
Files opened for read or update must already exist. Files opened
for write or append will be created if necessary. A file opened
for write will be set to 0 bytes. Output to a file opened for
append will start at the end of file.
Output to text files will have carriage-return characters
automatically added before linefeed characters. On input, these
carriage-return characters are removed. Null characters (0) are
removed from output. I/O to binary files is not modified in any way.
Some typical devices that you can open are:
"CON" the console (screen)
"AUX" the serial auxiliary port
"COM1" serial port 1
"COM2" serial port 2
"PRN" the printer on the parallel port
"NUL" a non-existent device that accepts and discards output
close(fn)
Close a file or device and flush out any still-buffered characters.
print(fn, x)
Print an object x with braces { , , , } to show the structure.
If you want to see a string of characters, rather than just the
ASCII codes, you need to use puts or printf below. e.g.
print(1, "ABC") -- output is: {65, 66, 67}
puts(1, "ABC") -- output is: ABC
? x This is just a shorthand way of saying print(1, x), i.e. printing
the value of an expression to the standard output. For example
? {1, 2} + {3, 4} would display {4, 6}. ? adds new-lines to
make the output more readable on your screen (or standard output).
printf(fn, st, x)
Print x using format string st. If x is an atom then a single
value will be printed. If x is a sequence, then formats from st
are applied to successive elements of x. Thus printf always takes
exactly 3 arguments. Only the length of the last argument,
containing the values to be printed, will vary. The basic formats
are:
%d - print an atom as a decimal integer
%x - print an atom as a hexadecimal integer
%o - print an atom as an octal integer
%s - print a sequence as a string of characters
%e - print an atom as a floating point number with exponential
notation
%f - print an atom as a floating-point number with a decimal point
but no exponent
%g - print an atom as a floating point number using either
the %f or %e format, whichever seems more appropriate
%% - print '%' character
Field widths can be added to the basic formats, e.g. %5d, or %8.2f.
The number before the decimal point is the minimum field width to be
used. The number after the decimal point is the precision to be used.
If the field width is negative, e.g. %-5d then the value will be
left-justified within the field. Normally it will be right-justified.
If the field width starts with a leading 0 e.g. %08d then leading
zeros will be supplied to fill up the field. If the field width
starts with a '+' e.g. %+7d then a plus sign will be printed for
positive values.
Examples:
rate = 7.75
printf(myfile, "The interest rate is: %8.2f\n", rate)
name="John Smith"
score=97
printf(1, "%15s, %5d\n", {name, score})
Watch out for the following common mistake:
printf(1, "%15s", name)
This will print only the first character of name, as each element
of name is taken to be a separate value to be formatted. You must
say this instead:
printf(1, "%15s", {name})
puts(fn, x)
Print a single character (atom) or sequence of characters as bytes
of text. e.g.
puts(screen, "Enter your first name: ")
puts(output, 'A') -- the single byte 65 will be sent to output
i = getc(fn)
Get the next character (byte) from file fn. -1 is returned at end
of file
x = gets(fn)
Get a sequence (one line, including \n) of characters from text
file fn. The atom -1 is returned on end of file. Example:
-- read a text file into a sequence
buffer = {}
while 1 do
line = gets(0)
if atom(line) then
exit -- end of file
end if
buffer = append(buffer, line)
end while
i = get_key()
Return the key that was pressed by the user, without waiting for
carriage return, or return -1 if no key was pressed
include get.e
s = get(fn)
Read the next representation of a Euphoria object from file fn, and
convert it into the value of that object. s will be a 2-element
sequence {error status, value}. Error status values are:
GET_SUCCESS -- object was read successfully
GET_EOF -- end of file before object was read
GET_FAIL -- object is not syntactically correct
As a simple example, suppose your program asks the user to enter
a number from the keyboard. If he types 77.5, get(0) will return
an atom with numeric value 77.5. gets(0) would return the string
"77.5\n".
get() can read arbitrarily complicated Euphoria objects. You could
have a long sequence of values in braces and separated by commas,
e.g. {23, {49, 57}, 0.5, -1, 99, 'A', "john"}. A single call to
get() will read in this entire sequence and return it's value as
a result. The combination of print() and get() can be used to
save any Euphoria object to disk and later read it back. This
technique could be used to implement a database as one or more
large Euphoria sequences stored in disk files. The sequences
could be read into memory, updated and then written back to disk
after each series of transactions is complete. See demo\mydata.ex.
Each call to get() picks up where the previous call left off. For
instance, a series of 5 calls to get() would be needed to read in:
99 5.2 {1,2,3} "Hello" -1.
include file.e for the following:
i1 = seek(fn, i2)
Seek (move) to any byte position in the file fn or to the end of file
if i2 is -1. For each open file there is a current byte position
that is updated as a result of I/O operations on the file. The
initial file position is 0 for files opened for read, write or update.
The initial position is the end of file for files opened for append.
The value returned by seek() is 0 if the seek was successful, and
non-zero if it was unsuccessful. It is possible to seek past the
end of a file. In this case undefined bytes will be added to the file
to make it long enough for the seek.
i = where(fn)
This function returns the current byte position in the file fn.
This position is updated by reads, writes and seeks on the file.
It is the place in the file where the next byte will be read from,
or written to.
s = current_dir()
Return the name of the current working directory.
x = dir(st)
Return directory information for the file or directory named by st.
If there is no file or directory with this name then -1 is returned.
This information is similar to what you would get from the DOS dir
command. A sequence is returned where each element is a sequence
that describes one file or subdirectory. For example:
{
{".", "d", 0 1994, 1, 18, 9, 30, 02},
{"..", "d", 0 1994, 1, 18, 9, 20, 14},
{"fred", "ra", 2350, 1994, 1, 22, 17, 22, 40},
{"sub", "d" , 0, 1993, 9, 20, 8, 50, 12}
}
If st names a directory you will have entries for "." and "..", just
as with the DOS dir command. If st names a file then x will have just
one entry, i.e. length(x) will be 1. Each entry contains the name,
attributes and file size as well as the year, month, day, hour, minute
and second of the last modification. The attributes element is a
string sequence containing characters chosen from:
d - directory
r - read only file
h - hidden file
s - system file
v - volume-id entry
a - archive file
A normal file without special attributes would just have an empty
string, "", in this field. See bin\walkdir.ex for an example that
uses the dir() function.
4.6 Mouse Support
-----------------
include mouse.e -- required for these routines
x = get_mouse()
Return the last mouse event in the form {event, x, y}, or return -1
if there has not been a mouse event since the last time get_mouse()
was called.
Constants have been defined in mouse.e for the possible mouse events.
x and y are the coordinates of the mouse pointer at the time that
the event occurred. e.g. {2, 100, 50} would indicate that the
left button was pressed down while the mouse pointer was at position
x=100, y=50 on the screen. get_mouse() returns immediately with
either a -1 or a mouse event. It does not wait for an event to occur.
You must check it frequently enough to avoid missing an event. When
the next event occurs, the current event will be lost, if you haven't
read it. In practice it is not hard to catch almost all events, and
the ones that are lost are usually lost at a lower level in the
system, beyond the control of your program. Losing a MOVE event is
generally not too serious, as the next MOVE will tell you where the
mouse pointer is.
You can use get_mouse() in most text and graphics modes. (The SVGA
modes may not work fully under MS-DOS).
mouse_events(i)
Use this procedure to select the mouse events that you want
get_mouse() to report. For example, mouse_events(LEFT_DOWN+LEFT_UP+
RIGHT_DOWN) will restrict get_mouse() to reporting the left button
being pressed down or released, and the right button being pressed
down. All other events will be ignored. By default, get_mouse()
will report all events. It is good practice to ignore events that
you are not interested in, particularly the very frequent MOVE event,
in order to reduce the chance that you will miss a significant event.
mouse_events() can be called at various stages of the execution of
your program, as the need to detect events changes.
mouse_pointer(i)
If i is 0 hide the mouse pointer, otherwise turn on the mouse pointer.
It may be necessary to hide the mouse pointer temporarily when you
update the screen. Multiple calls to hide the pointer will require
multiple calls to turn it back on. The first call to either get_mouse()
or mouse_events() above, will also turn the pointer on (once).
4.7 Operating System
--------------------
a = time()
Return the number of seconds since some fixed point in the past. The
resolution on MS-DOS is about 0.05 seconds.
s = date()
Return a sequence with the following information:
{ year (since 1900),
month (January = 1),
day (day of month, starting at 1),
hour (0 to 23),
minute (0 to 59),
second (0 to 59),
day of the week (Sunday = 1),
day of the year (January 1 = 1) }
s = command_line()
Return a sequence of strings, where each string is a word from the
ex command line that started Euphoria. The first word will be the
path to the Euphoria executable. The next word is the name of
your Euphoria .ex file. After that will come any extra words typed
by the user. You can use these words in your program. Euphoria
does not have any command line options.
x = getenv(s)
Return the value of an environment variable. If the variable is
undefined return -1. e.g.
getenv("EUDIR") -- returns "C:\EUPHORIA" -- or D: etc.
system(s, a)
Pass a command string s to the operating system command interpreter
for execution. The argument a indicates the manner in which to
return from the system call.
value of a return action
0 - restore previous graphics mode
(clears the screen)
1 - make a beep sound, wait for a
key press, then restore the
graphics mode
2 - do not restore graphics mode
Action 2 should only be used when it is known that the system call
will not change the graphics mode.
abort(a)
Abort execution of the program. The argument a is an integer status
value to be returned to the operating system. A value of 0 indicates
successful completion of the program. Other values can indicate
various kinds of errors. Euphoria for MS-DOS currently ignores this
argument and always returns 0. abort() is useful when a program is
many levels deep in subroutine calls, and execution must end
immediately, perhaps due to a severe error that has been detected.
Machine Dependent Routines
--------------------------
x = machine_func(a, x)
machine_proc(a, x)
These routines perform machine-specific operations such as graphics
and sound effects. They are meant to be called indirectly via one of
the support routines, written in Euphoria. A direct call can cause
a machine exception if done incorrectly. Calls to these routines
may not be portable across all machines and operating systems that
Euphoria is implemented on.
4.8 Debugging
-------------
trace(x)
If x is 1 then turn on full-screen statement tracing. If x is 0 then
turn off tracing. Tracing only occurs in subroutines that were
compiled with trace. See the section on Debugging.
4.9 Graphics & Sound
--------------------
clear_screen()
Clear the screen using the current background color.
position(a1, a2)
Set the cursor to line a1, column a2, where the top left corner
is position(1,1).
include graphics.e -- for the following routines:
i1 = graphics_mode(i2)
Select graphics mode i2. See graphics.e for a list of valid
graphics modes. If successful, i1 is set to 0, otherwise i1
is set to 1.
s = video_config()
Return a sequence of values describing the current video
configuration:
{color monitor?, graphics mode, text rows, text columns,
xpixels, ypixels, number of colors}
scroll(i)
Scroll the screen up (i positive) or down (i negative) by i lines.
wrap(i)
Allow text to wrap at right margin (i = 1) or get truncated (i = 0).
cursor(i)
Select a style of cursor. See graphics.e for some choices.
text_color(i)
Set the foreground text color. Add 16 to get blinking text
in some modes. See graphics.e for a list of possible colors.
bk_color(i)
Set the background color - text or graphics modes.
x = palette(i, s)
Change the color for color number i to s, where s is a sequence of
color intensities: {red, green, blue}. Each value in s can be from
0 to 63. If successful, a 3-element sequence containing the
previous color for i will be returned, and all pixels on the screen
with value i will be set to the new color. If unsuccessful, the
atom -1 will be returned.
i2 = text_rows(i1)
Set the number of text rows on the screen to i1 if possible.
i2 will be set to the actual new number of rows.
pixel(i, s)
Set a pixel using color i at point s, where s is a 2-element screen
coordinate {x, y}.
i = get_pixel(s)
Read the color number for a pixel on the screen at point s,
where s is a 2-element screen coordinate {x, y}. -1 is returned
for points that are off the screen.
draw_line(i, s)
Draw a line connecting the points in s, using color i.
Example:
draw_line(7, {{100, 100}, {200, 200}, {900, 700}})
would connect the three points in the sequence using a thin white
line, i.e. a line would be drawn from {100, 100} to {200, 200} and
another line would be drawn from {200, 200} to {900, 700}.
polygon(i1, i2, s)
Draw a polygon with 3 or more vertices given in s, using a certain
color i1. Fill the area if i2 is 1. Don't fill if i2 is 0. Example:
polygon(7, 1, {{100, 100}, {200, 200}, {900, 700}})
would make a solid white triangle.
ellipse(i1, i2, s1, s2)
Draw an ellipse with color i1. The ellipse neatly fits inside
the rectangle defined by diagonal points s1 {x1, y1} and s2
{x2, y2}. If the rectangle is a square then the ellipse will be a
circle. Fill the ellipse when i2 is 1. Don't fill when i2 is 0.
Example:
ellipse(5, 0, {10, 10}, {20, 20})
would make a magenta colored circle just fitting inside the square
{10, 10}, {10, 20}, {20, 20}, {20, 10}.
sound(i)
Turn on the PC speaker at the specified frequency. If i is 0
the speaker will be turned off.
4.10 Machine Level Interface
----------------------------
With this low-level machine interface you can read and write to memory. You
can also set up your own 386+ machine language routines and call them. The
usual guarantee that Euphoria will protect you from machine-level errors
does *not* apply when you use these routines. There are only some simple
checks to catch the use of memory addresses that are negative or zero.
(Theoretically address 0 is acceptable, but in practice it is usually an
error so we catch it.)
Most Euphoria programmers will never use this interface, but it is
important because it makes it possible to get into every nook and cranny
of the hardware or operating system. For those with very specialized
applications this could be crucial.
Machine code routines can be written by hand, or taken from the disassembled
output of a compiler for C or some other language. Remember that your
machine code will be running in 32-bit protected mode. See demo\callmach.ex
for an example.
i = peek(a)
Read a single byte value in the range 0 to 255 from machine address a.
poke(a, i)
Write a single byte value, i, to memory address a. remainder(i, 256)
is actually written.
Writing to the screen memory with poke() can be much faster than using
puts() or printf(), but the programming is more difficult and less
portable. In most cases the speed is not needed. For example, the
Euphoria editor never uses poke().
call(a)
Call a machine language routine that starts at location a. This
routine must execute a RET instruction #C3 to return control
to Euphoria. The routine should save and restore any registers
that it uses. You can allocate a block of memory for the routine
and then poke in the bytes of machine code. You might allocate
other blocks of memory for data and parameters that the machine
code can operate on. The addresses of these blocks could be poked
into the machine code.
include machine.e
a = allocate(i)
Allocate i contiguous bytes of memory. Return the address of the
block of memory, or return 0 if the memory can't be allocated.
free(a)
Free up a previously allocated block of memory by specifying the
address of the start of the block, i.e. the address that was
returned by allocate().
s = int_to_bytes(a)
Convert an integer into a sequence of 4 bytes. These bytes are in
the order expected on the 386+, i.e. least significant byte first.
You would use this routine prior to poking the bytes into memory.
a = bytes_to_int(s)
Convert a sequence of 4 bytes into an integer value. The result could
be greater than the integer type allows, so assign it to an atom.
--- THE END ---