6981 lines
240 KiB
Plaintext
6981 lines
240 KiB
Plaintext
@database "e.guide"
|
||
@node MAIN
|
||
@title "Amiga E v3.0a"
|
||
|
||
+-----------------------------------------------+
|
||
| |
|
||
| Amiga E v3.3a |
|
||
| Compiler for The E Language |
|
||
| By Wouter van Oortmerssen |
|
||
| |
|
||
+-----------------------------------------------+
|
||
|
||
|
||
|
||
Contents:
|
||
|
||
0. compiler and introduction
|
||
@{" A. introduction " link CH_0A }
|
||
@{" B. the distribution " link CH_0B }
|
||
@{" C. demo restrictions, registration and sites " link CH_0C }
|
||
@{" D. installing/using the compiler " link CH_0D }
|
||
@{" E. changes / history / new features " link CH_0E }
|
||
@{" F. additional information " link CH_0F }
|
||
|
||
1. format
|
||
@{" A. tabs,lf etc. " link CH_1A }
|
||
@{" B. comments " link CH_1B }
|
||
@{" C. identifiers and types " link CH_1C }
|
||
|
||
2. immediate values
|
||
@{" A. decimal (1) " link CH_2A }
|
||
@{" B. hexadecimal ($1) " link CH_2B }
|
||
@{" C. binary (%1) " link CH_2C }
|
||
@{" D. float (1.0) " link CH_2D }
|
||
@{" E. character " link CH_2E }
|
||
@{" F. strings ('bla') " link CH_2F }
|
||
@{" G. lists ([1,2,3]) and typed lists " link CH_2G }
|
||
@{" H. lisp-cells (<a|b>) " link CH_2H }
|
||
|
||
3. expressions
|
||
@{" A. format " link CH_3A }
|
||
@{" B. precedence and grouping " link CH_3B }
|
||
@{" C. types of expressions " link CH_3C }
|
||
@{" D. function calls " link CH_3D }
|
||
|
||
4. operators
|
||
@{" A. math (+ - * /) " link CH_4A }
|
||
@{" B. comparison (= <> > < >= <=) " link CH_4B }
|
||
@{" C. logical and bitwise (AND OR) " link CH_4C }
|
||
@{" D. unary (SIZEOF ` ^ {} ++ -- -) " link CH_4D }
|
||
@{" E. triple (IF THEN ELSE) " link CH_4E }
|
||
@{" F. structure (.) " link CH_4F }
|
||
@{" G. array ([]) " link CH_4G }
|
||
@{" H. float operator (!) " link CH_4H }
|
||
@{" I. assignments expressions (:=) " link CH_4I }
|
||
@{" J. sequencing (BUT) " link CH_4J }
|
||
@{" K. dynamic memory allocation (NEW) " link CH_4K }
|
||
@{" L. unification (<=>) " link CH_4L }
|
||
@{" M. pointer typing (::) " link CH_4M }
|
||
|
||
5. statements
|
||
@{" A. format (;) " link CH_5A }
|
||
@{" B. statement labels and gotos (JUMP) " link CH_5B }
|
||
@{" C. assignment (:=) " link CH_5C }
|
||
@{" D. assembly mnemonics " link CH_5D }
|
||
@{" E. conditional statement (IF) " link CH_5E }
|
||
@{" F. for-statement (FOR) " link CH_5F }
|
||
@{" G. while-statement (WHILE) " link CH_5G }
|
||
@{" H. repeat-statement (REPEAT) " link CH_5H }
|
||
@{" I. loop-statement (LOOP) " link CH_5I }
|
||
@{" J. select-case-statement (SELECT) " link CH_5J }
|
||
@{" K. increase statement (INC/DEC) " link CH_5K }
|
||
@{" L. void expressions (VOID) " link CH_5L }
|
||
@{" M. memory deallocation (END) " link CH_5M }
|
||
|
||
6. function definitions and declarations
|
||
@{" A. proc definition and arguments (PROC) " link CH_6A }
|
||
@{" B. local and global definitions: scope (DEF) " link CH_6B }
|
||
@{" C. endproc/return " link CH_6C }
|
||
@{" D. the `main' function " link CH_6D }
|
||
@{" E. built-in system variables " link CH_6E }
|
||
@{" F. default arguments to functions " link CH_6F }
|
||
@{" G. multiple return values " link CH_6G }
|
||
@{" H. function values " link CH_6H }
|
||
|
||
7. declaration of constants
|
||
@{" A. const (CONST) " link CH_7A }
|
||
@{" B. enumerations (ENUM) " link CH_7B }
|
||
@{" C. sets (SET) " link CH_7C }
|
||
@{" D. built-in constants " link CH_7D }
|
||
|
||
8. types
|
||
@{" A. about the `type' system " link CH_8A }
|
||
@{" B. the basic type (LONG/PTR) " link CH_8B }
|
||
@{" C. the simple type (CHAR/INT/LONG) " link CH_8C }
|
||
@{" D. the array type (ARRAY) " link CH_8D }
|
||
@{" E. the complex type (STRING/LIST) " link CH_8E }
|
||
@{" F. the compound type (OBJECT) " link CH_8F }
|
||
@{" G. initialisation " link CH_8G }
|
||
@{" H. the essentials of the E typesystem " link CH_8H }
|
||
|
||
9. built-in functions
|
||
@{" A. io functions " link CH_9A }
|
||
@{" B. strings and string functions " link CH_9B }
|
||
@{" C. lists and list functions " link CH_9C }
|
||
@{" D. intuition support functions " link CH_9D }
|
||
@{" E. graphics support functions " link CH_9E }
|
||
@{" F. system support functions " link CH_9F }
|
||
@{" G. math and other functions " link CH_9G }
|
||
@{" H. string and list linking functions " link CH_9H }
|
||
@{" I. lisp-cells and cell functions " link CH_9I }
|
||
|
||
10. library functions and modules
|
||
@{" A. built-in library calls " link CH_10A }
|
||
@{" B. interfacing to the amiga system with the v40 modules " link CH_10B }
|
||
@{" C. compiling own modules " link CH_10C }
|
||
@{" D. the modulecache " link CH_10D }
|
||
|
||
11. quoted expressions
|
||
@{" A. quoting and scope " link CH_11A }
|
||
@{" B. Eval() " link CH_11B }
|
||
@{" C. built-in functions " link CH_11C }
|
||
|
||
12. floating point support
|
||
@{" A. float values " link CH_12A }
|
||
@{" B. computing with floats " link CH_12B }
|
||
@{" C. builtin float functions " link CH_12C }
|
||
@{" D. float implementation issues " link CH_12D }
|
||
|
||
13. Exception handling
|
||
@{" A. defining exception handlers (HANDLE/EXCEPT) " link CH_13A }
|
||
@{" B. using the Raise() function " link CH_13B }
|
||
@{" C. defining exceptions for built-in functions (RAISE/IF) " link CH_13C }
|
||
@{" D. use of exception-ID's " link CH_13D }
|
||
|
||
14. OO programming
|
||
@{" A. OO features in E " link CH_14A }
|
||
@{" B. object inheritance " link CH_14B }
|
||
@{" C. data hiding (EXPORT/PRIVATE/PUBLIC) " link CH_14C }
|
||
@{" D. methods and virtual methods " link CH_14D }
|
||
@{" E. Constructors, Destructors and Super-Methods (NEW,END,SUPER) " link CH_14E }
|
||
|
||
15. inline assembly
|
||
@{" A. identifier sharing " link CH_15A }
|
||
@{" B. the inline assembler compared to a macro assembler " link CH_15B }
|
||
@{" C. ways using binary data (INCBIN/CHAR..) " link CH_15C }
|
||
@{" D. OPT ASM " link CH_15D }
|
||
@{" E. Inline asm and register variables " link CH_15E }
|
||
|
||
16. technical and implementation issues
|
||
@{" A. the OPT keyword " link CH_16A }
|
||
@{" B. small/large model " link CH_16B }
|
||
@{" C. stack organisation " link CH_16C }
|
||
@{" D. hardcoded limits " link CH_16D }
|
||
@{" E. error messages, warnings and the unreferenced check " link CH_16E }
|
||
@{" F. compiler buffer organisation and allocation " link CH_16F }
|
||
@{" G. register allocation " link CH_16G }
|
||
|
||
17. Essential E Utilities / Applications
|
||
@{" A. ShowModule " link CH_17A }
|
||
@{" B. ShowHunk " link CH_17B }
|
||
@{" C. Pragma2Module / Iconvert " link CH_17C }
|
||
@{" D. ShowCache / FlushCache " link CH_17D }
|
||
@{" E. ecompile.rexx " link CH_17E }
|
||
@{" F. o2m " link CH_17F }
|
||
@{" G. E-Yacc " link CH_17G }
|
||
@{" H. SrcGen " link CH_17H }
|
||
@{" I. EBuild " link CH_17I }
|
||
@{" J. EE / Aprof " link CH_17J }
|
||
@{" K. EDBG " link CH_17K }
|
||
@{" L. EC PreProcessor " link CH_17L }
|
||
@{" M. EC Library mode " link CH_17M }
|
||
|
||
18. Appendices
|
||
@{" A. E Grammar description " link CH_18A }
|
||
@{" B. Tutorial " link CH_18B }
|
||
@{" C. Mapping E to C/C++/Pascal/Ada/Lisp etc. " link CH_18C }
|
||
@{" D. Amiga E FAQ " link CH_18D }
|
||
@{" E. TODO/BUG list " link CH_18E }
|
||
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_0A
|
||
@title "0A. introduction"
|
||
0A. introduction
|
||
----------------
|
||
E is an object oriented / procedural / unpure functional higher programming
|
||
language, mainly influenced by languages such as C++, Ada, Lisp etc. It is a
|
||
general-purpose programming language, and the Amiga implementation is
|
||
specifically targeted at programming applications. Features of the language
|
||
include: speed of >20000 lines/minute on a vanilla A500, inline assembler and
|
||
linker integrated into compiler, large set of integrated functions, great
|
||
module concept with v39 includes as modules, flexible type-system, quoted
|
||
expressions, immediate and typed lists, parametric and inclusion polymorphism,
|
||
exception handling, inheritance, data-hiding, methods, multiple return values,
|
||
default arguments, register allocation, fast memory management, unification,
|
||
LISP-Cells, gui-toolkit, (macro-) preprocessor, very intuitive and powerful
|
||
source-level debugger, easy .library linking, and much more...
|
||
|
||
This is what 'HelloWorld' looks like in E:
|
||
|
||
/* nominated for Most Boring Example */
|
||
|
||
PROC main()
|
||
WriteF('Hello, World!\\n')
|
||
ENDPROC
|
||
|
||
|
||
@endnode
|
||
@node CH_0B
|
||
@title "0B. the distribution"
|
||
0B. the distribution
|
||
--------------------
|
||
The distribution includes:
|
||
|
||
Bin/ contains the compiler EC and the support utilities
|
||
Modules/ directory containing all Amiga v40 E modules and
|
||
useful tools as linkable modules
|
||
Docs/ documentation on E
|
||
Src/ example sources in E
|
||
|
||
This distribution of Amiga E including the demo version of the compiler
|
||
is FreeWare, and may be freely copied. The compiler archive some of you may
|
||
have received together with this archive, however, is NOT FreeWare and should
|
||
NOT be copied for anyone but yourself.
|
||
|
||
Distributing copies of E for more than the price of disk+postage (generally
|
||
<3$), or any other way of distribution with only the slightest aim of making
|
||
profit, is not allowed. I explicitly forbid Serge Hammouche or his company
|
||
"France Festival Distribution" to distribute E in any shape or form. He has
|
||
done so in the past without my permission, misusing my name and stealing money
|
||
from me on the backs of french E programmers. If you paid him money, claim
|
||
it back and spread the word. A french translation of this document authorized
|
||
by me (made by Olivier Anh) is available on the aminet.
|
||
|
||
This distribution should always be distributed as a whole, i.e. in the form
|
||
of the original .lha archive. No additions, modifications, translations,
|
||
partial distributions or whatever are allowed without my explicit permission.
|
||
|
||
No guarantees, no warranty. If you manage to drown your pet goldfish using E,
|
||
or E fails to be suitable for ordering pizzas, that's entirely your problem.
|
||
Whatever happens, don't blame it on me.
|
||
|
||
Fred Fish has special permission to distribute E on his CD-ROM's, distribution
|
||
on Aminet CDROMs is also permitted.
|
||
|
||
|
||
@endnode
|
||
@node CH_0C
|
||
@title "0C. demo restrictions, registration and sites"
|
||
0C. demo restrictions, registration and sites
|
||
---------------------------------------------
|
||
The Amiga E distribution is PD, and contains the demo version of the compiler.
|
||
registered programmers obtain the full compiler as a separate archive, with
|
||
only "EC" in it.
|
||
|
||
If you have received the registered EC already, you may wish to put it in the
|
||
bin directory. The distribution archive may be freely copied for anyone (see
|
||
0B on that), but please make sure you do not distribute your registered copy
|
||
of EC to anyone, not even to friends. I haven't serialised these copies
|
||
because of the confidence I have in my registered users, so don't break it.
|
||
Registration is very affordable, and v2.1b is still available, so there's no
|
||
excuse left for pirating E.
|
||
|
||
So what's "demo" about the included compiler? The compiler will only spit out
|
||
executables <8k, above that it will present you with a "workspace full" error.
|
||
Other than that it's fully functional. [To the ignorant pirates: if you feel
|
||
like removing this protection make sure you do it right: all attempts I've
|
||
see sofar only remove the first level of protection and won't allow compilation
|
||
of large executables].
|
||
|
||
You are allowed to evaluate this demo for _two weeks_, after which you decide
|
||
either to register or quit using this demo version, even if you only write
|
||
programs <8k. [this used to be 12k, yes. Guess why it has changed.]
|
||
|
||
E registrations are personal, i.e. you may use it on any number of machines.
|
||
it also means you can't sell it to someone else if you don't want it anymore.
|
||
|
||
updates.
|
||
distribution updates can be found in the PD as normal, and updates to the
|
||
registered EC will be distributed as patches, also in the PD.
|
||
|
||
ordering a copy.
|
||
The basic price is, was, and will always be 65 dutch guilders (approxx. 40$).
|
||
subtract 5 guilders if you are prepared to receive your registered EC per
|
||
uuencoded E-mail instead of on disk. This is the fastest way to receive a
|
||
registered version (you only receive the EC executable).
|
||
|
||
I will allow payment in some other currencies as well, to simplify payment for
|
||
some people. No rare currencies please. If you send money in your currency,
|
||
make sure it's well above 65 guilders, according to _current_ exchange rates.
|
||
|
||
Getting the money to me. Not only has the registration-site situation changed
|
||
radically (since previous releases of E), my own situation has as well (read
|
||
the bit about my new address in 0F).
|
||
|
||
These are the authorized E registration sites at the moment:
|
||
|
||
England: Jason R. Hulance
|
||
Germany: Gregor Goldbach
|
||
|
||
[NOTE: the one in sweden was closed down a while ago, the ones in the USA and
|
||
Australia are in the process of being closed down (if you can provide any
|
||
information of the current whereabouts of either Barry Wills or Rob Nottage,
|
||
please contact me). The site in germany changed from J<>rg Wach to Gregor
|
||
Goldbach, The one in Italy is having troubles at the moment so please use
|
||
other sites until further notice. And my own site, Holland, has gone into
|
||
low-power mode since I live in England now, so I'd advice you not to use it
|
||
(you risk waiting up to 4 months for your registration)]
|
||
|
||
Below is some information from the various sites, for more info contact them
|
||
personally. Residents in nearby countries may choose at their option to
|
||
registrate at these sites as well (England is the "main" site for the moment).
|
||
Whatever your method of payment, make sure the correct amount is received by
|
||
the site.
|
||
|
||
|
||
ENGLAND
|
||
---------------
|
||
name: Jason R. Hulance (Dept. E)
|
||
address: Formal Systems (Europe) Ltd, 3 Alfred Street, Oxford OX1 4EH, ENGLAND
|
||
telephone: (0869) 324350, out of office hours.
|
||
email: jason@fsel.com, m88jrh@ecs.ox.ac.uk
|
||
price: 26 UK pounds (on disk) or 23 UK pounds (via E-mail)
|
||
"Beginner's Guide to Amiga E" (182 pages) is available in AmigaGuide, TeX,
|
||
PostScript and printed forms with a large index. This costs 5 UK pounds
|
||
(non-printed forms) or 10 UK pounds (printed form).
|
||
payment: For safety, please make payment by cheque (payable to "Jason Hulance"),
|
||
although postal orders/cash/bank transfers will also be accepted
|
||
|
||
|
||
GERMANY
|
||
---------------
|
||
name: Gregor Goldbach
|
||
address: Gruener Weg 10, 21423 Pattensen, Deutschland
|
||
phone: 04173/511267
|
||
email: glauschwuffel@amt.comlink.de
|
||
price: 60 DM (54 DM if sent by email)
|
||
payment: direct transfer to bank account (preferred, mail me for
|
||
details)
|
||
cash (at your risk)
|
||
|
||
|
||
HOLLAND
|
||
---------------
|
||
address: (see @{" 0F " link CH_0F }) please use one of the other sites above instead
|
||
price: 65 dutch guilders
|
||
payment: cash (preferred), mandat de poste international, eurocheque
|
||
(in DFL), bank (427875951 ABN-AMRO bank, the netherlands,
|
||
add 20,-), post-giro (6030387, PostBank, the netherlands,
|
||
add 7,-), no cheques please.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_0D
|
||
@title "0D. installing/using the compiler"
|
||
0D. installing/using the compiler
|
||
----------------------
|
||
|
||
To install Amiga E on your system, just copy the whole distribution to some
|
||
place in your system, extend your path to the BIN directory, and assign
|
||
EMODULES: to the `Modules' directory. If you're upgrading from a previous
|
||
version, please reinstall this release, and use your older registrated
|
||
compiler (v3.0a, v3.1a or v3.2a) in combination with the corresponding patch
|
||
(in the Bin/ directory) to create the v3.3a EC executable.
|
||
|
||
syntax of the compiler (2.04+):
|
||
|
||
SOURCE/A,REG=NUMREGALLOC/N/K,LARGE/S,SYM=SYMBOLHUNK/S,NOWARN/S,
|
||
QUIET/S,ASM/S,ERRLINE/S,ERRBYTE/S,SHOWBUF/S,ADDBUF/N/K,IGNORECACHE/S,
|
||
HOLD/S,WB/S,LINEDEBUG/S,OPTI/S,DEBUG/S,NILCHECK/S:
|
||
|
||
for 1.2 to 2.03:
|
||
|
||
EC [-opts] <sourcefile>
|
||
|
||
As an example we'll compile the program 'HelloWorld.e'. The compiler will
|
||
produce an executable 'HelloWorld'.
|
||
|
||
1> cd e:src
|
||
E:Src> ec helloworld
|
||
Amiga<EFBFBD>E<EFBFBD>Compiler/Assembler/Linker<65>v2.4f<EFBFBD>(c)<29>91/92/93<39>$#%!
|
||
lexical analysing ...
|
||
parsing and compiling ...
|
||
no errors
|
||
E:Src> helloworld
|
||
Hello, World!
|
||
E:Src> list
|
||
HelloWorld.e 89 ----rwed Oggi 17:37:00
|
||
helloworld 656 ----rwed Oggi 17:37:00
|
||
2 files - 4 blocks used
|
||
E:Src>
|
||
|
||
note: the compiler cannot be made resident
|
||
|
||
Options.
|
||
These are standard AmigaDos options. You can see them at any time by typing
|
||
'EC ?'. For 1.2 options the old unix-like switches are used (need to be
|
||
written together, preceded by a "-"):
|
||
|
||
LARGE -l compiles with large code/data model (see @{" 16A " link CH_16A }, OPT LARGE).
|
||
ASM -a puts EC into assembler mode (see @{" 15D " link CH_15D }, OPT ASM).
|
||
NOWARN -n suppresses warnings (see @{" 16A " link CH_16A }, OPT NOWARN)
|
||
SYM -s adds a symbolhunk to the executable, for use with
|
||
profilers, debuggers, disassemblers etc.
|
||
LINEDEBUG -L adds a "LINE" debughunk to the executable, for use
|
||
with profilers, debuggers etc. (see @{" 17K " link CH_17K }).
|
||
REG=N -rN uses N regs per PROC for register-allocation.
|
||
(default is 0 for the time being, read elsewhere
|
||
about how to use this!)
|
||
QUIET -q if there are no errors or warnings, EC won't output
|
||
anything.
|
||
WB -w puts wb to front (for scripts)
|
||
SHOWBUF -b shows buffer memory usage information
|
||
ADDBUF=X -mX forces EC to allocate more memory for its buffers.
|
||
X ranges 1..9, the minimum number of 100k blocks to allocate.
|
||
default is 1 (never needed).
|
||
ERRLINE -e give the line# the error was on as returnvalue
|
||
ERRBYTE -E give the byte-offset in the file the error was on
|
||
as returnvalue
|
||
IGNORECACHE -c don't use the module-cache in any way
|
||
HOLD -h wait for a <return> on the commandline before
|
||
exiting EC.
|
||
OPTI enable all optimisations (currently equals REG=5)
|
||
DEBUG attach debug infos to executable/module (see @{" 17K " link CH_17K })
|
||
NILCHECK insert code at every "." or "[]" dereference to check
|
||
for a NIL pointer. Will throw an exception of the form
|
||
Throw("NIL",source_line_number)
|
||
|
||
example: ec large blabla
|
||
compiles blabla.e with large model.
|
||
NOTE: in most cases you won't need to use any of these options
|
||
|
||
|
||
@endnode
|
||
@node CH_0E
|
||
@title "0E. changes / history / new features"
|
||
0E. changes / history / new features
|
||
------------------------------------
|
||
New in v3.3a:
|
||
- significantly improved version of EasyGUI, including the SOURCE!
|
||
- new version of EDBG which a.o. now cooperates with Explorer
|
||
(see @{" 17K " link CH_17K } for a full list of new features and bugs fixed).
|
||
Now comes with source code as well!
|
||
- new version of EBuild
|
||
- various new examples, e.g. a texturemapper
|
||
- new versions of many modules and examples
|
||
- revised docs, e.g. a new "known-bugs" list.
|
||
- some new builtins, e.g. the Arc float functions.
|
||
- LIBRARY mode now has a PROC close() to match main()
|
||
- two (!) new class libraries now part of the distribution,
|
||
Gregor's OO modules and Fabio's AFC.
|
||
|
||
Bugs fixed in v3.3a:
|
||
- EC now saves debug information for "self", and EDBG makes use of it.
|
||
- OPT RTD removed.
|
||
- ENUM now works with negative numbers
|
||
- module-cache flushing now definitely fool proof.
|
||
- and many smaller bugs...
|
||
|
||
New in v3.2i:
|
||
- make sure you read about the bug in mathieeesingbas.library (see @{" 12D " link CH_12D }) (!)
|
||
- You can explicitly assign registers in own libraries (e.g. `myfun(A0)').
|
||
- `find' in EDBG
|
||
- Iconvert supports PhxAss (with "-p")
|
||
- quite a few things in the distribution
|
||
|
||
Bugs fixed in v3.2i:
|
||
- methods with an exception handler could cause problems in the register
|
||
allocator.
|
||
- various bugs in EDBG (scroller etc.)
|
||
- double declaration of object or label identifiers could go unnoticed
|
||
- EC would have problems flushing modules from different directories (now it
|
||
only looks for the file-part and flushes it).
|
||
- problems could occur with method being called before their OBJECT declarations.
|
||
- and various smaller bugs...
|
||
|
||
New features in v3.2e:
|
||
- better EasyGUI (plugins)
|
||
- better EDBG (Arexx port and config saving)
|
||
|
||
Bugs fixed in v3.2e:
|
||
- procident.member gave weird errors
|
||
- preprocessor failed on 68000 in particular situations
|
||
- preprocessor didn't handle international characters
|
||
- '\\0' in strings would cut them off
|
||
- EC would Lock() with an illegal mode from other dirs
|
||
- $FFFF as default arg became $FFFF0000
|
||
- macro errors could cause garbage to be printed on screen
|
||
- /* */ in comments failed
|
||
- inheriting from yourself wasn't detected
|
||
- didn't accept 0(A3,a) or 0(A3,a.L) or lab(PC,a) etc. where a:REG
|
||
- AND.L D0,a where a:REG changed src-dest
|
||
|
||
New features in v3.2a:
|
||
- EC can now output .library files!!! (see @{" 17M " link CH_17M })
|
||
- new EDBG with a massive amount of new features! (see @{" 17K " link CH_17K })
|
||
- enhanced EasyGUI (have a look)
|
||
- NEW and END now work on subobjects as well (see @{" 4K " link CH_4K }).
|
||
- NILCHECK/S, will insert code to check all "." and "[]" dereferences for
|
||
NIL pointers (see @{" 0D " link CH_0D })
|
||
- v40 emodules, with types!
|
||
- new modules (Src/Class/ScrollWin/ and others)
|
||
- new docs / beginners guide
|
||
- extended distribution with Jason's RKRM examples etc.
|
||
|
||
Bugs fixed in v3.2a:
|
||
- IGNORECACHE was broken
|
||
- silly bug in /2
|
||
- asm -xx(Ax,Dx.w) addressing mode assembled wrong
|
||
- a-- where a:PTR TO o where SIZEOF o>8 translated into ADDI ipv SUBI
|
||
- globals in modules used in inline asm were not exported / offsets adjusted
|
||
- exp*globvar in module not compiled ok
|
||
- macros of other module would be saved again in current module
|
||
- and quite a few smaller ones...
|
||
|
||
|
||
|
||
a version history:
|
||
|
||
vers date EC k comment
|
||
|
||
v0.3 sep 91 5 first version that outputs executables
|
||
v1.0 may 92 20 builtins and library calls, usable for small tools
|
||
v2.0 okt 92 38 v37 modules usable, language pretty "complete"
|
||
v2.1a jan 93 44 first public release
|
||
v2.1b apr 93 45 bug fix, few new features. current version for >1 year
|
||
v3.0a jul 94 82 enormous amount of new features. E gets a price tag.
|
||
v3.0b sep 94 82 bug fix
|
||
v3.0e nov 94 83 bug fix
|
||
v3.1a dec 94 85 EDBG, preprocessor etc.
|
||
v3.1i jun 95 83 new EDBG etc.
|
||
v3.2a jul 95 85 .library mode
|
||
v3.2e nov 95 85 new EDBG, EasyGUI, bugfixes.
|
||
v3.2i nov 96 86 bugfixes, various
|
||
v3.3a okt 97 86 bugfixes, new EDBG/EasyGUI/modules/classes/sources etc.
|
||
|
||
@endnode
|
||
@node CH_0F
|
||
@title "0F. additional information"
|
||
0F. additional information
|
||
--------------------------
|
||
|
||
The Amiga E Compiler was developed over the course of more than six years,
|
||
after the author's idea of a good programming language, and a quality amiga
|
||
specific compiler for it. It started out as quite a small project and evolved
|
||
over the years into the large development environment it is now. EC was
|
||
programmed (as you might have guessed) in 100% assembly, using the AsmOne
|
||
assembler v1.02. All other support programs and tools (such as EDBG) were
|
||
written in E itself.
|
||
|
||
Special thanks go to the following people:
|
||
|
||
Jason Hulance - for 'The Beginners Guide', modules/examples, helping out
|
||
with EasyGUI/EDBG, betatesting, his registration site, and practically
|
||
being the coauthor of the Amiga E package.
|
||
Gregor Goldbach - for his registration site and general help.
|
||
J<EFBFBD>rg Wach - for EPD and general support.
|
||
Rob Verver - for comments/inspiration.
|
||
Barry Wills - for being such a good betatester.
|
||
|
||
I also would like to thank the following people for various reasons
|
||
(no particular order):
|
||
|
||
Raymond Hoving, Erwin van Breemen, Michael Zucchi, James Cooper, Jens Gelhar,
|
||
Paolo Silvera, Sergio Ruocco, Jeroen Vermeulen, Jan van den Baard,
|
||
Norman Kraft, Urban Mueller, Charles McCreary, Olivier Anh, Lionel
|
||
Vintenat, Rob Nottage, Mirko Lalli, Jonas Tehler, Paul Nolan, Antonio J.
|
||
Gomez Glez, Fabio Rotondo and many more...
|
||
|
||
This compiler was programmed with great care towards reliability, and even
|
||
more so the code it generates, additionally it has been tested and debugged
|
||
for a long period. However, it is not impossible that it contains bugs. if
|
||
you happen to find one, or have other comments/questions, write me at the
|
||
address below: I _strongly_ prefer E-mail above conventional mail.
|
||
|
||
NOTE WELL: due to the immense popularity (?) of Amiga E, I get an almost
|
||
unreplyable amount of Email, some of which (>90%) are questions that would not
|
||
have been necessary if people read all the docs carefully. What I mean to say
|
||
is that I don't mind receiving Email, and I generally answer all questions and
|
||
help people out with their programming problems, but be sure to check all other
|
||
information at your disposal (like the Amiga E docs or the RKRM's) to see if
|
||
your question is relevant, before mailing me. Especially questions that are
|
||
not E specific but Amiga specific should not be directed to me. And try and
|
||
be gentle on me with comments like "I think E should have feature X...", and
|
||
compiler bug reports that are really about bugs in your own program (see
|
||
18D, the FAQ).
|
||
|
||
registrations (see @{" 0C " link CH_0C }) and donations are welcome at the following address:
|
||
|
||
Note that I've moved to England. For the time being the following address is
|
||
safe to use (my parents address, will be valid for quite a while, but it may
|
||
take quite a while for me to collect my mail there):
|
||
|
||
Wouter van Oortmerssen
|
||
Singel 524
|
||
3311 HP Dordrecht
|
||
HOLLAND
|
||
|
||
If its about registrations it is wiser to just use another registration
|
||
site instead for the time being (the english one is preferred).
|
||
|
||
The old address (in Leiden, Holland) is not valid anymore, please stop sending
|
||
mail there.
|
||
|
||
You can try my current address in England, but it is subject to immediate
|
||
change (better email me first):
|
||
|
||
Wouter van Oortmerssen
|
||
Montefiore House III / L246
|
||
Wessex Lane
|
||
Swaythling
|
||
Southampton
|
||
SO18 2NU
|
||
UK
|
||
|
||
What this boils down to is that the only way to reach me without problems
|
||
is via email:
|
||
|
||
wvo96r@ecs.soton.ac.uk (strongly preferred)
|
||
wouter@ai.let.uva.nl (new, forward)
|
||
wouter@mars.let.uva.nl (still valid, forward)
|
||
wvo@acm.org (new, forward)
|
||
wvo@soton.ac.uk (new, not read)
|
||
w.v.oortmerssen@let.uva.nl (probably still works, forward)
|
||
oortmers@fwi.uva.nl (probably invalid by now)
|
||
wouter@alf.let.uva.nl (likely invalid)
|
||
root@wouter.ecs.soton.ac.uk (emergency cases, not read)
|
||
m.scha@zadkine.nl (at my parents, emergency only)
|
||
|
||
or visit me on the www (updated infrequently):
|
||
|
||
http://www.dsse.ecs.soton.ac.uk/~wvo96r/ (primary)
|
||
http://ecs.soton.ac.uk/~wvo96r/ (alternatively?)
|
||
http://earth.let.uva.nl/~wouter/ (older copy)
|
||
http://mars.let.uva.nl/wouter/ (might go)
|
||
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_1A
|
||
@title "1A. tabs,lf etc."
|
||
1A. tabs,lf etc.
|
||
----------------
|
||
E-sources are pure ascii format files, with the linefeed <lf> and
|
||
semicolon ";" being the separators for two statements. Statements
|
||
that have particularly many arguments, may be spread over several lines
|
||
by ending a line with a comma (or any other lexical element that can
|
||
normally never occur at the end of a line), thus ignoring the following
|
||
<lf> (see @{" 5A " link CH_5A } for line endings). Any lexical element in a source code
|
||
file may be separated from another by any number of spaces, tabs etc.
|
||
|
||
|
||
@endnode
|
||
@node CH_1B
|
||
@title "1B. comments"
|
||
1B. comments
|
||
------------
|
||
comments may be placed anywhere in a source file where normally a space
|
||
would have been correct. They start with '/*' and end with '*/'
|
||
and may be nested infinitely. The same goes for one-line comments,
|
||
which start with a '->' and end at the first <lf>.
|
||
|
||
/* this, is a comment */
|
||
-> this one too.
|
||
|
||
|
||
@endnode
|
||
@node CH_1C
|
||
@title "1C. identifiers and types"
|
||
1C. identifiers and types
|
||
-------------------------
|
||
identifiers are strings that the programmer uses to denote certain
|
||
objects, in most cases variables, or even keywords or function names
|
||
predefined by the compiler. An identifier may consist of:
|
||
|
||
- upper and lowercase characters
|
||
- "0" .. "9" (except as first character)
|
||
- "_" (the underscore)
|
||
|
||
All characters are significant, but the compiler just looks at the
|
||
first two to identify the type of identifier it is dealing with:
|
||
|
||
both uppercase: - keyword like IF, PROC etc.
|
||
- constant, like MAX_LENGTH
|
||
- assembly mnemonic, like MOVE
|
||
first lowercase: - identifier of variable/label/object etc.
|
||
first upper and second lower: - E system function like: WriteF()
|
||
- library call: OpenWindow()
|
||
|
||
Note that all identifiers obey this syntax, for example:
|
||
WBenchToFront() becomes WbenchToFront()
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_2A
|
||
@title "2A. decimal (1)"
|
||
2A. decimal (1)
|
||
---------------
|
||
Immediate values in E all evaluate to a 32bit result; the only
|
||
difference among these values (A-G) is either their internal
|
||
representation, or the fact that they return a pointer rather than
|
||
a value.
|
||
|
||
A decimal value is a sequence of characters "0" .. "9", possibly
|
||
preceded by a minus "-" sign to denote negative numbers.
|
||
examples: 1, 100, -12, 1024
|
||
|
||
@endnode
|
||
@node CH_2B
|
||
@title "2B. hexadecimal ($1)"
|
||
2B. hexadecimal ($1)
|
||
--------------------
|
||
A hexadecimal value uses the additional characters "A" .. "F" (or
|
||
"a" .. "f") and is preceded by a "$" character.
|
||
Examples: $FC, $DFF180, -$ABCD
|
||
|
||
@endnode
|
||
@node CH_2C
|
||
@title "2C. binary (%1)"
|
||
2C. binary (%1)
|
||
---------------
|
||
Binary numbers start with a "%" character and use only "1" and "0"
|
||
to form a value.
|
||
Examples: %111, %1010100001, -%10101
|
||
|
||
@endnode
|
||
@node CH_2D
|
||
@title "2D. float (1.0)"
|
||
2D. float (1.0)
|
||
---------------
|
||
Floats differ only from normal decimal numbers in that they have
|
||
a "." to separate their two parts. Either one may be omitted,
|
||
not both. Note that floats have a different internal 32bit (IEEE)
|
||
representation (see @{" 12A " link CH_12A } for more information on floats).
|
||
Examples: 3.14159, .1 (=0.1), 1. (=1.0)
|
||
|
||
@endnode
|
||
@node CH_2E
|
||
@title "2E. character"
|
||
2E. character
|
||
-------------------
|
||
The value of a character (enclosed in double "" quotes) is its
|
||
ascii value, i.e. "A" = 65. In E, character immediate values
|
||
may be a short string up to 4 characters, for example "FORM",
|
||
where the first character "F" will be the MSB of the 32bit
|
||
representation, and "M" the LSB (least significant byte).
|
||
|
||
@endnode
|
||
@node CH_2F
|
||
@title "2F. string ('bla')"
|
||
2F. string ('bla')
|
||
------------------
|
||
Strings are any ascii representation, enclosed in single '' quotes.
|
||
The value of such a string is a pointer to the first character of it.
|
||
More specific: 'bla' yields a 32bit pointer to a memory area where
|
||
we find the bytes "b", "l" and "a". ALL strings in E are terminated
|
||
by a zero byte.
|
||
Strings may contain format signs introduced by a slash "\\", either
|
||
to introduce characters to the string that are for some reason
|
||
not displayable, or for use with string formatting functions
|
||
like WriteF(), TextF() and StringF(), or kick2 Vprintf().
|
||
|
||
\\n a linefeed (ascii 10)
|
||
\\a or '' an apostrophe ' (the one used for enclosing the string)
|
||
\\q a doublequote: "
|
||
\\e escape (ascii 27)
|
||
\\t tab (ascii 9)
|
||
\\\\ a backslash
|
||
\\0 a zero byte. Of rare use, as ALL strings are 0-terminated
|
||
\\b a carriage return (ascii 13)
|
||
|
||
Additionally, when used with formatting functions:
|
||
|
||
\\d print a decimal number
|
||
\\h print a hexadecimal
|
||
\\s print a string
|
||
\\c print a character
|
||
\\z set fill byte to '0' character
|
||
\\l format to left of field
|
||
\\r format to right of field (these last two act as toggles)
|
||
|
||
Field specifiers may follow the \\d,\\h and \\s codes:
|
||
|
||
[x] specify exact field width x
|
||
(x,y) specify minimum x and maximum y (strings only)
|
||
|
||
Example: print a hexadecimal number with 8 positions and leading zeroes:
|
||
WriteF('\\z\\h[8]\\n',num)
|
||
|
||
A string may be extended over several lines by trailing them with a "+"
|
||
sign and a <lf>:
|
||
|
||
'this specifically long string ' +
|
||
'is separated over two lines'
|
||
|
||
|
||
@endnode
|
||
@node CH_2G
|
||
@title "2G. lists ([1,2,3]) and typed lists"
|
||
2G. lists ([1,2,3]) and typed lists
|
||
-----------------------------------
|
||
An immediate list is the constant counterpart of the LIST datatype,
|
||
just as a 'string' is the constant counterpart for the STRING or
|
||
ARRAY OF CHAR datatype. Example:
|
||
|
||
[3,2,1,4]
|
||
|
||
is an expression that has as value a PTR to an already initialised list,
|
||
a list as a representation in memory is compatible with an ARRAY OF LONG,
|
||
with some extra length information at a negative offset. You may use these
|
||
immediate lists anywhere a function expects a PTR to an array of
|
||
32bits values, or a list. Examples:
|
||
|
||
['string',1.0,2.1]
|
||
[WA_FLAGS,1,WA_IDCMP,$200,WA_WIDTH,120,WA_HEIGHT,150,TAG_DONE]
|
||
|
||
(see @{" 9C " link CH_9C } on list-functions for a discussion on typed-immediate
|
||
lists and detailed information).
|
||
|
||
|
||
@endnode
|
||
@node CH_2H
|
||
@title "2H. lisp-cells (<a|b>)"
|
||
2H. lisp-cells (<a|b>)
|
||
----------------------
|
||
(see @{" 9I " link CH_9I } to read about cells in detail)
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_3A
|
||
@title "3A. format"
|
||
3A. format
|
||
----------
|
||
An expression is a piece of code held together by operators, functions
|
||
and parentheses to form a value.
|
||
They mostly consist of:
|
||
|
||
- immediate values (see @{" 2A " link CH_2A },...)
|
||
- operators (see @{" 4A " link CH_4A },...)
|
||
- function calls (see @{" 3D " link CH_3D })
|
||
- parentheses () (see @{" 3B " link CH_3B })
|
||
- variables or variable-expressions (see @{" 3C " link CH_3C })
|
||
|
||
examples of expressions:
|
||
1
|
||
'hello'
|
||
$ABCD+(2*6)+Abs(a)
|
||
(a<1) OR (b>=100)
|
||
|
||
@endnode
|
||
@node CH_3B
|
||
@title "3B. precedence and grouping"
|
||
3B. precedence and grouping
|
||
---------------------------
|
||
The E language has _no_ precedence whatsoever. This means that
|
||
expressions are evaluated left to right. You may change
|
||
precedence by parenthesizing some (sub-)expression:
|
||
1+2*3 /* =9 */ 1+(2*3) /* =7 */ 2*3+1 /* =7 */
|
||
|
||
@endnode
|
||
@node CH_3C
|
||
@title "3C. types of expressions"
|
||
3C. types of expressions
|
||
------------------------
|
||
There are three types of expressions that may be used for different
|
||
purposes;
|
||
|
||
- <var>, consisting of just a variable
|
||
- <varexp>, consisting of a variable, possibly with unary operators
|
||
with it, like "++" (increment) "." (member selection) or [] (array
|
||
operator). (see @{" 4D " link CH_4D }, 4G). It denotes a modifiable expression, like
|
||
Lvalues in C.
|
||
Note that those (unary) operators are not part of any precedence.
|
||
- <exp>. This includes <var> and <varexp>, and any other expression.
|
||
|
||
@endnode
|
||
@node CH_3D
|
||
@title "3D. function calls"
|
||
3D. function calls
|
||
------------------
|
||
A function call is a temporary suspension of the current code for a
|
||
jump to a function, this may be either a self-written function (PROC),
|
||
or a function provided by the system. The format of a function call
|
||
is the name of the function, followed by two parentheses () enclosing
|
||
zero to unlimited arguments, separated by commas ",". Note that arguments
|
||
to functions are again expressions. (see @{" 6A " link CH_6A }) on how to make your own
|
||
functions, (see @{" 9A " link CH_9A } and 10A)
|
||
on built-in functions. Examples:
|
||
|
||
foo(1,2)
|
||
Gadget(buffer,glist,2,0,40,80+offset,100,'Cancel')
|
||
Close(handle)
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_4A
|
||
@title "4A. math (+ - * /)"
|
||
4A. math (+ - * /)
|
||
------------------
|
||
These infix operators combine an expression with another value
|
||
to yield a new value. Examples:
|
||
|
||
1+2, MAX-1*5
|
||
|
||
(see @{" 12A " link CH_12A } on how to overload these operators for use with floats).
|
||
"-" may be used as the first part of an expression, with an implied 0,
|
||
i.e. -a or -b+1 is legal.
|
||
Note that * and / are by default 16bit operators: (see @{" 9G " link CH_9G }, Mul())
|
||
|
||
@endnode
|
||
@node CH_4B
|
||
@title "4B. comparison (= <> > < >= <=)"
|
||
4B. comparison (= <> > < >= <=)
|
||
-------------------------------
|
||
Equal to math operators, with the difference that they result in either
|
||
TRUE (32bit value -1), or FALSE. These can also be overloaded for floats.
|
||
|
||
@endnode
|
||
@node CH_4C
|
||
@title "4C. logical and bitwise (AND OR)"
|
||
4C. logical and bitwise (AND OR)
|
||
--------------------------------
|
||
These operators either combine truth values to new ones, or perform
|
||
bitwise AND and OR operations. Examples:
|
||
(a>1) AND ((b=2) OR (c>=3)) /* logical */
|
||
a:=b AND $FF /* bitwise */
|
||
|
||
@endnode
|
||
@node CH_4D
|
||
@title "4D. unary (SIZEOF ^ {} ++ -- `)"
|
||
4D. unary (SIZEOF ^ {} ++ -- `)
|
||
---------------------------------
|
||
- SIZEOF <objectident>
|
||
simply returns the size of a certain object or CHAR/INT/LONG.
|
||
Example: SIZEOF newscreen + SIZEOF INT
|
||
- {<var>}
|
||
Returns the address of a variable or label. This is the operator
|
||
you would use to give a variable as argument to a function by
|
||
reference, instead of by value, which is default in E. See "^".
|
||
[a bit obsolete for variables. use multiple returnvalues instead]
|
||
- ^<var>
|
||
[a bit obsolete. you should be using the [] operator or multiple
|
||
returnvalues instead]
|
||
The counterpart of {}, writes or reads variables that were given by
|
||
reference, examples: ^a:=1 b:=^a
|
||
it may also be used to plainly "peek" or "poke" LONG values from
|
||
memory, if <var> is pointer to such a value.
|
||
Example for {} and ^: write your own assignment function;
|
||
|
||
PROC set(var,exp)
|
||
^var:=exp
|
||
ENDPROC
|
||
|
||
and call it with: set({a},1) /* equals a:=1 */
|
||
- <varexp>++ and <varexp>--
|
||
Increases (++) or decreases (--) the pointer that is denoted by
|
||
<varexp> by the size of the data it points to. This has the effect
|
||
that that pointer points to the next or previous item. When used
|
||
on variables that are not pointers, these will simply be changed
|
||
by one. Note that ++ always takes effect _after_ the calculation
|
||
of <varexp>, -- always _before_. Examples:
|
||
|
||
a++ /* return value of a, then increase by one */
|
||
sp[]-- /* decrease pointer sp by 4 (if it were an array of long),
|
||
and read value pointed at by sp */
|
||
|
||
Note that this is quite different from C, also, expressions like
|
||
p.i++ work on p, not i.
|
||
- `<exp>
|
||
This is called a quoted expression, from LISP. <exp> is not evaluated,
|
||
but instead returns the address of the expression, which can later be
|
||
evaluated when required. More on this special feature in chapter 11
|
||
|
||
@endnode
|
||
@node CH_4E
|
||
@title "4E. triple (IF THEN ELSE)"
|
||
4E. triple (IF THEN ELSE)
|
||
-------------------------
|
||
The IF operator has quite the same function as the IF statement, only
|
||
it selects between two expressions instead of two statements or blocks
|
||
of statements. It equals the x?y:z operator in C.
|
||
|
||
IF <boolexp> THEN <exp1> ELSE <exp2>
|
||
|
||
returns exp1 or exp2, according to boolexp. For example, instead of:
|
||
|
||
IF a<1 THEN b:=2 ELSE b:=3
|
||
IF x=3 THEN WriteF('x is 3\\n') ELSE WriteF('x is something else\\n')
|
||
|
||
write:
|
||
|
||
b:=IF a<1 THEN 2 ELSE 3
|
||
WriteF(IF x=3 THEN 'x is 3\\n' ELSE 'x is something else\\n')
|
||
|
||
@endnode
|
||
@node CH_4F
|
||
@title "4F. structure (.)"
|
||
4F. structure (.)
|
||
-----------------
|
||
<ptr2object>.<memberofobject> builds a <varexp>
|
||
The pointer has to be declared as PTR TO <object> or ARRAY OF <object>
|
||
(see @{" 8D " link CH_8D } for these), and the member has to be a legal
|
||
object identifier. Note that reading a subobject in an object this
|
||
way results in a pointer to that object. Examples:
|
||
|
||
thistask.userdata:=1
|
||
rast:=myscreen.rastport
|
||
|
||
If the member you select is of type PTR TO <type>, you may use "."
|
||
and "[]" to dereference further values. If you select an ARRAY or
|
||
substructure in an OBJECT, the result is again a PTR.
|
||
|
||
@endnode
|
||
@node CH_4G
|
||
@title "4G. array ([])"
|
||
4G. array ([])
|
||
--------------
|
||
<var>[<indexexp>] (is a <varexp>)
|
||
This operator reads the value from the array <var> points to, with
|
||
index <indexexp>. The index may be just about any expression.
|
||
Note1: "[]" is a shortcut for "[0]"
|
||
Note2: with an array of n elements, the index is 0 .. n-1
|
||
Examples:
|
||
|
||
a[1]:=10 /* sets second element to 10 */
|
||
x:=table[y*4+1] /* reads from array */
|
||
x.y[3] /* accesses array in an object */
|
||
|
||
@endnode
|
||
@node CH_4H
|
||
@title "4H. float operator (!)"
|
||
4H. float operator (!)
|
||
----------------------
|
||
<exp>!<exp>
|
||
Converts expressions from integer to float and back, and
|
||
overloads operators + - * / = <> < > <= >= with float equivalents.
|
||
(see @{" 12A " link CH_12A } to read all about floats and this operator).
|
||
|
||
@endnode
|
||
@node CH_4I
|
||
@title "4I. assignments expressions (:=)"
|
||
4I. assignments expressions (:=)
|
||
--------------------------------
|
||
Assignments (setting a variable to a value) exist as statement
|
||
and as expression. The only difference is that the statement
|
||
version has the form <varexp>:=<exp> and the expression <var>:=<exp>.
|
||
The latter has the value of <exp> as result.
|
||
Note that as <var>:= takes on an expression, you will often need
|
||
parentheses to force correct interpretation, like:
|
||
|
||
IF mem:=New(100)=NIL THEN error()
|
||
|
||
is interpreted like:
|
||
|
||
IF mem:=(New(100)=NIL) THEN error()
|
||
|
||
which is not what you mean: mem should be a pointer, not a boolean.
|
||
but you should write:
|
||
|
||
IF (mem:=New(100))=NIL THEN error()
|
||
|
||
it's a good habit to parenthesize any assignment expression that is
|
||
part of another one, if not already disambiguated by other constructs
|
||
such as "bla(a:=1)", "b:=a:=1" etc.
|
||
|
||
@endnode
|
||
@node CH_4J
|
||
@title "4J. sequencing (BUT)"
|
||
4J. sequencing (BUT)
|
||
--------------------
|
||
The sequencing operator "BUT" allows two expressions to be written
|
||
in a construction that allows for only one. Often in writing
|
||
complex expressions/function calls, it would be nice to do a second
|
||
thing on the spot, like an assignment. Syntax:
|
||
|
||
<exp1> BUT <exp1>
|
||
|
||
this says: evaluate exp1, but return the value of exp2.
|
||
Example:
|
||
|
||
myfunc((x:=2) BUT x*x)
|
||
|
||
assign 2 to x and then calls myfunc with x*x. The () around the
|
||
assignment are again needed to prevent the := operator from taking
|
||
(2 BUT x*x) as an expression.
|
||
|
||
@endnode
|
||
@node CH_4K
|
||
@title "4K. dynamic memory allocation (NEW)"
|
||
4K. dynamic memory allocation (NEW)
|
||
-----------------------------------
|
||
the NEW operator is a powerful operator for dynamic memory allocation.
|
||
it has various forms:
|
||
|
||
(assuming DEF p:PTR TO whateverobj, q:PTR TO INT)
|
||
|
||
NEW p
|
||
|
||
is an expression that will allocate zero-ised memory for the object-size
|
||
p points to. the resulting pointer will be put in p, and is also
|
||
the value of the expression. if NEW fails to get memory, it raises
|
||
a "NEW" exception. 'NEW p' is therefore roughly equivalent with:
|
||
|
||
IF (p:=New(SIZEOF whateverobj))=NIL THEN Raise("MEM")
|
||
|
||
with the difference that p never gets any value if an exception
|
||
is raised, and that the former is of course an expression, not
|
||
a statement
|
||
|
||
but there's more: it's also possible to allocate an array dynamically:
|
||
|
||
NEW p[10] /* array of 10 objects */
|
||
|
||
NEW q[a+1] /* array of INT, size is runtime computed */
|
||
|
||
(this doesn't work when instantiating classes)
|
||
A problem with list [1,2,3] expressions sometimes is that they are static, and
|
||
when using these to build large datastructures they would need to be created
|
||
at run-time. The dynamic equivalent to static lists may then be used:
|
||
|
||
p:=[1,2,3]:whateverobj /* static structure */
|
||
|
||
p:=NEW [1,2,3]:whateverobj /* dynamicly allocated! */
|
||
|
||
this works with both lists and typed-lists, and also with arrays:
|
||
|
||
NEW [1,2,3] /* constant-list, dyn. (note: not a like a listvar!) */
|
||
NEW [1,2,3]:obj /* object */
|
||
NEW [1,2,3]:INT /* array of INTs */
|
||
|
||
freeing memory allocated with any variations of NEW is done automatically
|
||
at the end of the program, or by hand with END or FastDispose().
|
||
(note: the only exception is NEW <list>, use FastDisposeList())
|
||
|
||
If you use NEW [...]:obj, and the number of fields is less than the one
|
||
present in the object, additional 0/NIL/"\\0" whatever fields will be added.
|
||
this allows extending objects without getting into trouble with allocations
|
||
like these. (note that this is different from the static [...]:obj)
|
||
|
||
when you use NEW as a statement, multiple pointers may follow, i.e.:
|
||
|
||
NEW a,b.create(),c
|
||
|
||
is valid.
|
||
|
||
special forms of NEW and END allow to use a PTR within an object, so you can
|
||
write things like
|
||
|
||
NEW a.b.create()
|
||
NEW a.b[50]
|
||
END a.b
|
||
|
||
in the examples, the pointer `a' is not affected, the pointer `b' in the `a'
|
||
object is used.
|
||
|
||
NEW allocated lists are not to be used with the Link() functions (see @{" 9H " link CH_9H }).
|
||
|
||
(see @{" 5M " link CH_5M }, END)
|
||
(see @{" 9F " link CH_9F } for the fast memory allocation functions NEW makes use of)
|
||
|
||
@endnode
|
||
@node CH_4L
|
||
@title "4L. unification (<=>)"
|
||
4L. unification (<=>)
|
||
---------------------
|
||
Unification allows a totally different style of programming
|
||
that will be familiar to you if you're used to either Logic
|
||
(ProLog), equational or functional (Miranda/Gofer/Haskell)
|
||
programming. Most of us when using structures/arrays whatever
|
||
are used to get values from it by selection ("." and "[]"),
|
||
in these languages however pattern matching is used.
|
||
|
||
in E:
|
||
|
||
exp <=> uni_exp
|
||
|
||
exp can be any expression, but in v3 it's only really useful
|
||
if it somehow is a pointer to a list. uni_exp is the pattern
|
||
that is used to match exp. All constants in uni_exp much
|
||
match to values in exp, variables are set to their respective
|
||
values. if something doesn't match, no variables get a value,
|
||
and the expression has the result FALSE. otherwise TRUE.
|
||
example:
|
||
|
||
a:=[1,2,3]
|
||
...
|
||
IF a <=> [1,x,y] THEN ...
|
||
|
||
this will succeed with x=2 and y=3. If list a were to be another
|
||
lenght than 3, the match would fail too. The fact that a is a list
|
||
in the first place is something you need to assure by yourself, EC
|
||
simply tries to fit the uni_exp on whatever value it gets as exp.
|
||
examples of FALSE:
|
||
|
||
a <=> [1,x] -> wrong list-len
|
||
a <=> [1,4,x] -> 4=2 fails
|
||
'bla' <=> [1,2] -> unpredictable result / crash ?
|
||
|
||
The fun thing with unification is that you can do very complex matches,
|
||
and that if you take the first field or so as a constant telling what the
|
||
structure is, you have a nice form of dynamic typing. And, all the time
|
||
without using PTRs!
|
||
|
||
a slightly nicer example:
|
||
|
||
[BLA,[1,'burp'],['bla',"bla"]] <=> [BLA,[1,x],y]
|
||
|
||
binds:
|
||
|
||
x='burp', y=['bla',"bla"]
|
||
|
||
or even:
|
||
|
||
IF myexp <=> [PLUS,[MUL,a,1],[SUBS,[PLUS,c,d],e]] THEN RETURN a+c+d-e
|
||
|
||
maybe a silly example, but one could imagine doing complex stuff
|
||
like this in a compiler's code-optimizer. it equals this traditional code:
|
||
|
||
IF ListLen(myexp)=3
|
||
IF myexp[]=PLUS
|
||
IF ListLen(dummy:=myexp[1])=3
|
||
IF (dummy[]=MUL) AND (dummy[2]=1)
|
||
a:=dummy[1]
|
||
IF ListLen(dummy:=myexp[2])=3
|
||
IF dummy[]=SUBS
|
||
e:=dummy[2]
|
||
IF ListLen(dummy2:=dummy[1])=3
|
||
IF dummy2[]=PLUS
|
||
c:=dummy2[1]
|
||
d:=dummy2[2]
|
||
RETURN a+c+d-e
|
||
ENDIF
|
||
ENDIF
|
||
ENDIF
|
||
ENDIF
|
||
ENDIF
|
||
ENDIF
|
||
ENDIF
|
||
ENDIF
|
||
|
||
only then a bit more optimal. As you see there's a lot of expressive
|
||
power involved in unification as compared to traditional selection-based
|
||
programming.
|
||
|
||
For now, the only thing allowed in unification are untyped lists,
|
||
(integer) constants, variables and LISP-Cells (see @{" 9I " link CH_9I } for that).
|
||
The future will see this expanded with strings, typed-lists/objects,
|
||
and even expressions [!]
|
||
|
||
|
||
@endnode
|
||
@node CH_4M
|
||
@title "4M. pointer typing (::)"
|
||
4M. pointer typing (::)
|
||
-----------------------
|
||
when you write an expression like 'p' where p is a PTR TO object
|
||
(and only objects), this allows you to dereference it as such.
|
||
The pointer typing operator allows you to change such a type on the fly:
|
||
|
||
DEF p:PTR TO mp -> message port
|
||
|
||
p.sigbit -> access it
|
||
p::ln.name -> access pointer as if it were a node
|
||
|
||
this is very useful for pointers that can point to different sorts
|
||
of things or objects that are part of each other, where of course
|
||
only one declaration is possible.
|
||
|
||
A second handy use is in OBJECTs that have members declared as LONG,
|
||
which are actually pointers. You may type it on the fly to dereference
|
||
it anyway:
|
||
|
||
mywindow.userport::mp.sigbit
|
||
|
||
here the window OBJECT has userport defined as LONG, so you wouldn't
|
||
be able to dereference it normally.
|
||
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_5A
|
||
@title "5A. format (;)"
|
||
5A. format (;)
|
||
--------------
|
||
As suggested in chapter 1A, a statement generally stands in its
|
||
own line, but several of them may be put together on one line
|
||
by separating them with semicolon, or one may be spread over
|
||
more than one line by ending each line in a comma ",". Examples:
|
||
|
||
a:=1; WriteF('hello!\\n')
|
||
DEF a,b,c,d, /* too many args for one line (faked) */
|
||
e,f,g
|
||
|
||
statements may be:
|
||
- assignments
|
||
- conditional statements, for statements and the like, (see @{" 5E " link CH_5E }-5K)
|
||
- void expressions
|
||
- labels
|
||
- assembly instructions
|
||
|
||
The comma is the primary character to show that you do not wish to
|
||
end the statement with the next linefeed, but from v3 on, any token
|
||
that cannot legally be the end of a line causes the statement to
|
||
continue. Furthermore, if not all "[" and "(" occuring in a statement
|
||
have been closed off, a statement will continue also.
|
||
examples of such tokens:
|
||
|
||
+ - * / =
|
||
< >= <=
|
||
:= . <=> ::
|
||
{ [ (
|
||
AND OR BUT THEN IS -> others too, but these are most useful
|
||
|
||
example of bracketing:
|
||
|
||
a:=[
|
||
[1,2],
|
||
[3,4]
|
||
] -> assignment ends here.
|
||
|
||
@endnode
|
||
@node CH_5B
|
||
@title "5B. statement labels and gotos (JUMP)"
|
||
5B. statement labels and gotos (JUMP)
|
||
-------------------------------------
|
||
Labels are global-scoped identifiers with a ':' added to them, as in:
|
||
|
||
mylabel:
|
||
|
||
they may be used by instructions such as JUMP, and to reference
|
||
static data. They may be used to jump out of all types of loops (although
|
||
this technique is not encouraged, (see @{" 5F " link CH_5F }, EXIT), but not out of
|
||
procedures. In normal E programs they are mostly used with inline assembly.
|
||
Labels are always globally visible.
|
||
|
||
Usage of JUMP: JUMP <label>
|
||
continues execution at <label>. You are not encouraged to use this
|
||
instruction, it's there for situations that would otherwise increase
|
||
the complexity of the program. Example:
|
||
|
||
IF done THEN JUMP stopnow
|
||
|
||
/* other parts of program */
|
||
|
||
stopnow:
|
||
|
||
@endnode
|
||
@node CH_5C
|
||
@title "5C. assignment (:=)"
|
||
5C. assignment (:=)
|
||
-------------------
|
||
The basic format of an assignment is: <var> := <exp>
|
||
Examples: a:=1, a:=myfunc(), a:=b*3
|
||
|
||
In E multiple variables can be assigned at once, when <exp> is
|
||
a function that returns multiple returnvalues. (see @{" 6G " link CH_6G })
|
||
|
||
@endnode
|
||
@node CH_5D
|
||
@title "5D. assembly mnemonics"
|
||
5D. assembly mnemonics
|
||
----------------------
|
||
In E, inline assembly is a true part of the language, they need not
|
||
be enclosed in special "ASM" blocks or the like, as is usual in
|
||
other languages, nor are separate assemblers necessary to assemble
|
||
the code. This also means that it obeys the E syntax rules, etc.
|
||
(see @{" 15A " link CH_15A } to read all about the inline assembler). Example:
|
||
|
||
DEF a,b
|
||
b:=2
|
||
MOVEQ #1,D0 /* just use some assembly statements */
|
||
MOVE.L D0,a /* a:=1+b */
|
||
ADD.L b,a
|
||
WriteF('a=\\d\\n',a) /* a will be 3 */
|
||
|
||
@endnode
|
||
@node CH_5E
|
||
@title "5E. conditional statement (IF)"
|
||
5E. conditional statement (IF)
|
||
------------------------------
|
||
IF, THEN, ELSE, ELSEIF, ENDIF
|
||
|
||
syntax: IF <exp> THEN <statement> [ ELSE <statement> ]
|
||
or: IF <exp>
|
||
<statements>
|
||
[ ELSEIF <exp> /* multiple elseifs may occur */
|
||
<statements> ]
|
||
[ ELSE ]
|
||
<statements>
|
||
ENDIF
|
||
|
||
builds a conditional block. Note that there are two general forms of
|
||
this statement, a single-line and a multiple-line version.
|
||
|
||
|
||
@endnode
|
||
@node CH_5F
|
||
@title "5F. for-statement (FOR)"
|
||
5F. for-statement (FOR)
|
||
-----------------------
|
||
FOR, TO, STEP, DO, ENDFOR
|
||
|
||
syntax: FOR <var> := <exp> TO <exp> STEP <step> DO <statement>
|
||
or: FOR <var> := <exp> TO <exp> STEP <step>
|
||
<statements>
|
||
ENDFOR
|
||
|
||
builds a for-block, note the two general forms. <step> may be
|
||
any positive or negative constant, excluding 0, and is optional. Example:
|
||
|
||
FOR a:=1 TO 10 DO WriteF('\\d\\n',a)
|
||
|
||
The body of FOR may contain EXIT statements with the following syntax:
|
||
|
||
EXIT <boolexp>
|
||
|
||
Which allows you to exit the loop if boolexp is true.
|
||
|
||
|
||
@endnode
|
||
@node CH_5G
|
||
@title "5G. while-statement (WHILE)"
|
||
5G. while-statement (WHILE)
|
||
---------------------------
|
||
WHILE, DO, ENDWHILE
|
||
|
||
syntax: WHILE <exp> DO <statement>
|
||
or: WHILE <exp>
|
||
<statements>
|
||
ENDWHILE
|
||
|
||
builds a while-loop, which is repeated as long as <exp> is TRUE. Note
|
||
the one-line/one-statement version and the multiple line version.
|
||
|
||
WHILE may also contain EXIT statements, like FOR.
|
||
|
||
|
||
@endnode
|
||
@node CH_5H
|
||
@title "5H. repeat-statement (REPEAT)"
|
||
5H. repeat-statement (REPEAT)
|
||
-----------------------------
|
||
REPEAT, UNTIL
|
||
|
||
syntax: REPEAT
|
||
UNTIL <exp>
|
||
|
||
builds a repeat-until block: it will continue to loop this block until
|
||
<exp>=TRUE. Example:
|
||
|
||
REPEAT
|
||
WriteF('Do you really, really wish to exit this program?\\n')
|
||
ReadStr(stdout,s)
|
||
UNTIL StrCmp(s,'yes please!')
|
||
|
||
|
||
@endnode
|
||
@node CH_5I
|
||
@title "5I. loop-statement (LOOP)"
|
||
5I. loop-statement (LOOP)
|
||
-------------------------
|
||
LOOP, ENDLOOP
|
||
|
||
syntax: LOOP
|
||
<statements>
|
||
ENDLOOP
|
||
|
||
builds an infinite loop.
|
||
|
||
|
||
@endnode
|
||
@node CH_5J
|
||
@title "5J. select-case-statement (SELECT)"
|
||
5J. select-case-statement (SELECT)
|
||
---------------------------------
|
||
SELECT, CASE, DEFAULT, ENDSELECT
|
||
|
||
syntax: SELECT <var>
|
||
[ CASE <exp>
|
||
<statements> ]
|
||
[ CASE <exp>
|
||
<statements> ] /* any number of these blocks */
|
||
[ DEFAULT
|
||
<statements> ]
|
||
ENDSELECT
|
||
|
||
builds a select-case block. Various expressions will be matched against
|
||
the variable, and only the first matching block executed. If nothing matches,
|
||
a default block may be executed.
|
||
|
||
SELECT character
|
||
CASE 10
|
||
WriteF('Gee, I just found a linefeed\\n')
|
||
CASE 9
|
||
WriteF('Wow, this must be a tab!\\n')
|
||
DEFAULT
|
||
WriteF('Do you know this one: "\\c" ?\\n',character)
|
||
ENDSELECT
|
||
|
||
next to the old SELECT <var> which works on expressions in the CASE
|
||
statements, E has a SELECT <maxrange> OF <exp> which works with constants
|
||
and or ranges of constants in a CASE. not only is this for many applications
|
||
more powerful, it is also much faster for large numbers of cases (>5
|
||
usually), or if cases are equally probable. It assumes however, that all
|
||
CASEs lie within a small integer range from 0 TO n-1, where n is something
|
||
reasonable, for example 10, or 256 for characters.
|
||
|
||
example:
|
||
|
||
SELECT 128 OF FgetC(stream)
|
||
CASE "\\n","\\b"
|
||
WriteF('line ending\\n')
|
||
CASE "\\t"," "
|
||
WriteF('whitepace\\n')
|
||
CASE "0" TO "9"
|
||
WriteF('Integer\\n')
|
||
CASE "A" TO "Z", "a" TO "z", "_"
|
||
WriteF('Identifier\\n')
|
||
DEFAULT
|
||
WriteF('some other character\\n')
|
||
ENDSELECT
|
||
|
||
DEFAULT will be hit not only for those for which there is no CASE,
|
||
but also for the chars that fall out of the range, i.e. 128 TO 255
|
||
(and >255, and <0).
|
||
note that speed costs: because this SELECT uses a jump-table
|
||
to quickly get at the right CASE, it'll use 2*<maxrange> bytes,
|
||
256 in this case.
|
||
|
||
|
||
@endnode
|
||
@node CH_5K
|
||
@title "5K. increase statement (INC/DEC)"
|
||
5K. increase statement (INC/DEC)
|
||
--------------------------------
|
||
INC, DEC
|
||
|
||
syntax: INC <var>
|
||
DEC <var>
|
||
|
||
[obsolete]
|
||
short for <var>:=<var>+1 and <var>:=<var>-1. Only difference with
|
||
var++ and var-- is that these are statements, and do not return a value.
|
||
|
||
|
||
@endnode
|
||
@node CH_5L
|
||
@title "5L. void expressions (VOID)"
|
||
5L. void expressions (VOID)
|
||
---------------------------
|
||
VOID
|
||
|
||
syntax: VOID <exp>
|
||
|
||
[obsolete: only for backwards compatability]
|
||
calculates the expression without the result going anywhere. Only useful
|
||
for a clearer syntax, as expressions may be used as statements without
|
||
VOID in E anyway. This may cause subtle bugs though, as "a:=1" assigns
|
||
a the value 1, but "a=1" as a statement will do nothing. E will give you
|
||
a warning if this happens.
|
||
|
||
|
||
@endnode
|
||
@node CH_5M
|
||
@title "5M. memory deallocation (END)"
|
||
5M. memory deallocation (END)
|
||
-----------------------------
|
||
END is the complement to NEW. any pointer obtained should be
|
||
deallocated (and only!) with END
|
||
|
||
END a
|
||
|
||
or even
|
||
|
||
END a,b,c
|
||
|
||
where the arguments are PTRs to some type. END frees the amount of space
|
||
that is being pointed to, so if a is PTR TO LONG it will only free 4
|
||
bytes. so if you allocated a with NEW a[NUM], free it with
|
||
|
||
END a[NUM]
|
||
|
||
NIL-pointers may safely be passed to NEW. Pointers are also
|
||
nuked to NIL afterwards.
|
||
If a is a PTR to an object that is an instance of a class, END
|
||
will dynamically get the size to be freed from the class-object,
|
||
so if you have an object of class b which is 32 bytes, but the
|
||
pointer you're freeing with is a baseclass pointer (only 24
|
||
bytes), END will correctly deallocate 32 bytes. this only works
|
||
for classes.
|
||
|
||
You can imagine END p as a macro for:
|
||
|
||
IF p
|
||
p.end()
|
||
FastDispose(p,p.classobject.virtuallen)
|
||
p:=NIL
|
||
ENDIF
|
||
|
||
If you don't call END on memory allocated by NEW, it will be deallocated
|
||
automatically at the end of the program, however no destructor methods
|
||
(.end()) will be called at this stage (i.e. if this is necessary you'll have
|
||
to do it yourself).
|
||
|
||
note that the NEW and END make use of the FastNew() and FastDispose()
|
||
functions (described elsewhere) which work quite differently from
|
||
NewR() and Dispose(). (see @{" 9F " link CH_9F } for details).
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_6A
|
||
@title "6A. proc definition and arguments (PROC)"
|
||
6A. proc definition and arguments (PROC)
|
||
----------------------------------------
|
||
|
||
You may use PROC and ENDPROC to collect statements into your own functions.
|
||
Such a function may have any number of arguments, and several return values.
|
||
|
||
PROC, ENDPROC
|
||
|
||
syntax: PROC <label> ( <args> , ... )
|
||
ENDPROC <returnvalue>, ...
|
||
|
||
defines a procedure with any number of arguments. Arguments are of type LONG
|
||
or optionally of type PTR TO <type> (see @{" 8B " link CH_8B }) and need no further
|
||
declaration. The end of a procedure is designated by ENDPROC. If no
|
||
return value is given, 0 is returned. Example: write a function that
|
||
returns the sum of two arguments:
|
||
|
||
PROC add(x,y) /* x and y are local variables */
|
||
ENDPROC x+y /* return the result */
|
||
|
||
a short version:
|
||
|
||
PROC add(x,y) IS x+y
|
||
|
||
|
||
@endnode
|
||
@node CH_6B
|
||
@title "6B. local and global definitions: scope (DEF)"
|
||
6B. local and global definitions: scope (DEF)
|
||
---------------------------------------------
|
||
You may define additional local variables besides those which are
|
||
arguments with the DEF statement. The easiest way is simply like:
|
||
|
||
DEF a,b,c
|
||
|
||
declares the identifiers a, b and c as variables of your function.
|
||
Note that such declarations should be at the start of your function.
|
||
|
||
DEF
|
||
|
||
syntax: DEF <declarations>,...
|
||
|
||
declares variables. A declaration has one of the forms:
|
||
|
||
<var>
|
||
<var>:<type> where <type>=LONG,<objectident>,...
|
||
<var>[<size>]:<type> where <type>=ARRAY,STRING,LIST
|
||
|
||
(see @{" 8A " link CH_8A } for more examples, as that is where the types are introduced).
|
||
For now, we'll use the <var> form.
|
||
Arguments to functions are restricted to basic types; (see @{" 8B " link CH_8B }).
|
||
A declaration of a basic type can have an initialisation, in the current
|
||
version this must be an integer (not an expression):
|
||
|
||
DEF a=1,b=2
|
||
|
||
A program consists of a set of functions, called procedures, PROCs. Each
|
||
procedure may have Local variables, and the program as a whole may have
|
||
Global variables. At least one procedure should be the PROC main(), as
|
||
this is the module where execution begins. A simple program could look like:
|
||
|
||
DEF a, b /* definition of global vars */
|
||
|
||
PROC main() /* all functions in random order */
|
||
bla(1)
|
||
ENDPROC
|
||
|
||
PROC bla(x)
|
||
DEF y,z /* possibly with own local vars */
|
||
ENDPROC
|
||
|
||
To summarize, local definitions are the ones you make at the start of
|
||
procedures, and which are only visible within that function. Global
|
||
definitions are made before the first PROC, at the start of your
|
||
source code, and they are globally visible. Global and local variables
|
||
(and of course local variables of two different functions) may have the
|
||
same name, local variables always have priority.
|
||
|
||
@endnode
|
||
@node CH_6C
|
||
@title "6C. endproc/return"
|
||
6C. endproc/return
|
||
------------------
|
||
As stated before, ENDPROC marks the end of a function definition, and may
|
||
return a value. Optionally RETURN may be used at any point in the function
|
||
to exit, if used in main(), it will exit the program.
|
||
|
||
RETURN [<returnvalue>] /* optional */
|
||
|
||
Example:
|
||
|
||
PROC getresources()
|
||
/* ... */
|
||
IF error THEN RETURN FALSE /* something went wrong, so exit and fail */
|
||
/* ... */
|
||
ENDPROC TRUE /* we got this far, so return TRUE */
|
||
|
||
a very short version of a function definition is:
|
||
|
||
PROC <label> ( <arg> , ... ) IS <exp>
|
||
|
||
("RETURN" instead of "IS" is allowed, but obsolete)
|
||
These are function definitions that only make small computations, like
|
||
faculty functions and the like: (one-liners :-)
|
||
|
||
PROC fac(n) IS IF n=1 THEN 1 ELSE fac(n-1)*n
|
||
|
||
|
||
@endnode
|
||
@node CH_6D
|
||
@title "6D. the `main' function"
|
||
6D. the `main' function
|
||
-----------------------
|
||
The PROC called main is only of importance because it is called as first
|
||
function; it behaves exactly the same as other functions, and may also
|
||
have local variables. Main has no arguments: the command-line arguments
|
||
are supplied in the system-variable "arg", or can be checked with
|
||
ReadArgs() (dos.library function)
|
||
|
||
@endnode
|
||
@node CH_6E
|
||
@title "6E. built-in system variables"
|
||
6E. built-in system variables
|
||
-----------------------------
|
||
Following global variables are always available in your program,
|
||
they're called system variables.
|
||
|
||
arg As discussed above, contains a pointer to a zero-terminated
|
||
string, containing the command-line arguments. Don't use this
|
||
variable if you wish to use ReadArgs() instead.
|
||
stdout Contains a file-handle to the standard output (and input).
|
||
If your program was started from the workbench, so no
|
||
shell-output is available, WriteF() will open a
|
||
CON: window for you and put its file handle here.
|
||
stdin file-handle of standard input
|
||
conout This is where that file handle is kept, and the console
|
||
window will be automatically closed upon exit of your
|
||
program. (see @{" 9A " link CH_9A }, WriteF(), on how to use these two variables
|
||
properly).
|
||
execbase, These four variables are always provided with their
|
||
dosbase, correct values.
|
||
gfxbase,
|
||
intuitionbase
|
||
stdrast Pointer to standard rastport in use with your program,
|
||
or NIL. The built-in graphics functions like Line()
|
||
make use of this variable.
|
||
wbmessage Contains a ptr to a message you got if you started
|
||
from wb, else NIL. May be used as a boolean to detect
|
||
if you started from workbench, or even check any
|
||
arguments that were shift-selected with your icon.
|
||
See WbArgs.e in the Src/Args dir how to
|
||
make good use of wbmessage.
|
||
|
||
@endnode
|
||
@node CH_6F
|
||
@title "6F. default arguments to functions"
|
||
6F. default arguments to functions
|
||
----------------------------------
|
||
|
||
default arguments allows you to specify for one or more
|
||
arguments of a procedure which is the default value, if the
|
||
procedure is called with less args than parameters. for example,
|
||
a procedure like:
|
||
|
||
PROC bla(a,b=1,c=NIL)
|
||
|
||
can be called like: is equivalent with:
|
||
|
||
bla(a,b,c) bla(a,b,c)
|
||
bla(a,b) bla(a,b,NIL)
|
||
bla(a) bla(a,1,NIL)
|
||
|
||
This can be useful and also express something about the
|
||
procedures function, i.e. that most of the time one would
|
||
call it with NIL anyway, so why not leave it out for clarity.
|
||
That's also why you should not overdo it with D.A.: do not
|
||
start specifying non-sensical values for procedures
|
||
out of pure laziness, if you feel a certain parameter really
|
||
has no default value.
|
||
|
||
to make calls with fewer args unambiguous, D.A. declarations
|
||
can only apply to the last 0..n parameters of a PROC of n parameters.
|
||
|
||
for example, illegal is: PROC bla(a,b=1,c)
|
||
|
||
(you should then simply reorder the parameters, of course).
|
||
arguments supplied in a call are filled in from left to right,
|
||
missing arguments are added with D.A.'s as needed.
|
||
|
||
@endnode
|
||
@node CH_6G
|
||
@title "6G. multiple return values"
|
||
6G. multiple return values
|
||
--------------------------
|
||
In E you can return any number of return values (max 3 in
|
||
Amiga E because of implementation reasons). How?
|
||
|
||
RETURN <exp>,<exp>,<exp> (or ENDPROC, of course)
|
||
|
||
example:
|
||
|
||
PROC sincos(rad)
|
||
DEF sin,cos
|
||
/* whatever computation is needed */
|
||
ENDPROC sin,cos
|
||
|
||
call with:
|
||
|
||
s,c:=sincos(3.14)
|
||
s:=sincos(1.00)
|
||
|
||
as you can see, there's a new statement of the form:
|
||
|
||
<var> , ... := <exp>
|
||
|
||
where <exp> makes mostly only sense as function call.
|
||
note two things:
|
||
- you can decide yourself how many values you wish to receive.
|
||
this makes sense when the first retval is the main one,
|
||
and the second/third optional infos, which might only be
|
||
important to some callers.
|
||
- this form is a _statement_. this means that when you would
|
||
call sincos() as part of another expression, only the
|
||
first (the regular) return value is used: fun(sincos(1.0))
|
||
|
||
|
||
@endnode
|
||
@node CH_6H
|
||
@title "6H. function values"
|
||
6H. function values
|
||
-------------------
|
||
With v3, you can also have functions as values, and pass these
|
||
freely around. they're different from quoted expression, since
|
||
they're called just like normal PROCs. example:
|
||
|
||
fun:={myproc} -> get PROC ptr
|
||
|
||
...
|
||
|
||
fun(1,2,3) -> apply to args, just like normal PROC
|
||
|
||
notes:
|
||
- you have to be sure that the PROC you have a ptr to and the
|
||
number of args are the same. the compiler can't check this for you.
|
||
- even worse: you have to be sure the ptr is a PROC at all.
|
||
there is a compiler warning that may help you with this.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_7A
|
||
@title "7A. const (CONST)"
|
||
7A. const (CONST)
|
||
-----------------
|
||
syntax: CONST <declarations>,...
|
||
|
||
Enables you to declare a constant. A declaration looks like:
|
||
<ident>=<value>
|
||
constants must be uppercase, and will in the rest of the program be
|
||
treated as <value>. Example:
|
||
|
||
CONST MAX_LINES=100, ER_NOMEM=1, ER_NOFILE=2
|
||
|
||
You cannot declare constants in terms of others that are being
|
||
declared in the same CONST statement: put these in the next.
|
||
|
||
CONST, ENUM and SET declarations are always global, i.e. it is not
|
||
possible to declare constants local to a PROC. The best place for
|
||
constant declarations is at the top of your source, but EC also
|
||
allows you to put them between two PROCs, for example.
|
||
|
||
@endnode
|
||
@node CH_7B
|
||
@title "7B. enumerations (ENUM)"
|
||
7B. enumerations (ENUM)
|
||
-----------------------
|
||
Enumerations are a specific type of constant that need not be given values,
|
||
as they simply range from 0 .. n, the first being 0. At any given point
|
||
in an enumeration, you may use the '=<value>' notation to set or reset
|
||
the counter value. Example:
|
||
|
||
ENUM ZERO, ONE, TWO, THREE, MONDAY=1, TUESDAY, WEDNESDAY
|
||
|
||
ENUM ER_NOFILE=100, ER_NOMEM, ER_NOWINDOW
|
||
|
||
@endnode
|
||
@node CH_7C
|
||
@title "7C. sets (SET)"
|
||
7C. sets (SET)
|
||
--------------
|
||
Sets are again like enumerations, with the difference that instead of
|
||
increasing a value (0,1,2,...) they increase a bitnumber (0,1,2,...) and
|
||
thus have values like (1,2,4,8,...). This has the added advantage that
|
||
they may be used as sets of flags, as the keyword says.
|
||
Suppose a set like the one below to describe properties of a window:
|
||
|
||
SET SIZEGAD,CLOSEGAD,SCROLLBAR,DEPTH
|
||
|
||
to initialise a variable to properties DEPTH and SIZEGAD:
|
||
|
||
winflags:=DEPTH OR SIZEGAD
|
||
|
||
to set an additional SCROLLBAR flag:
|
||
|
||
winflags:=winflags OR SCROLLBAR
|
||
|
||
and to test if either of both of two properties hold:
|
||
|
||
IF winflags AND (SCROLLBAR OR DEPTH) THEN /* whatever */
|
||
|
||
|
||
@endnode
|
||
@node CH_7D
|
||
@title "7D. built-in constants"
|
||
7D. built-in constants
|
||
---------------------
|
||
Following are built-in constants that may be used:
|
||
|
||
TRUE,FALSE Represent the boolean values (-1,0)
|
||
NIL (=0), the uninitialised pointer.
|
||
ALL Used with string functions like StrCopy() to copy all characters
|
||
GADGETSIZE Minimum size in bytes to hold one gadget (see @{" 9D " link CH_9D }, Gadget()) [obsolete]
|
||
OLDFILE,NEWFILE Mode-parameters for use with Open()
|
||
EMPTY used with methods (might be keyword in the future)
|
||
STRLEN Always has the value of the length of the last immediate
|
||
string used. Example:
|
||
|
||
Write(handle,'hi folks!',STRLEN) /* =9 */
|
||
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_8A
|
||
@title "8A. about the `type' system"
|
||
8A. about the `type' system
|
||
---------------------------
|
||
E doesn't have a rigid type-system like Pascal or Modula2, it's even more
|
||
flexible than C's type system: you might as well call it a datatype-system.
|
||
This goes hand in hand with the philosophy that in E all datatypes are
|
||
equal: all basic small values like characters, integers etc. All have
|
||
the same 32bit size, and all other datatypes like arrays and strings
|
||
are represented by 32bit pointers to them. This way, the e compiler can
|
||
generate code in a very polymorphic way.
|
||
The (dis)advantages are obvious:
|
||
|
||
disadvantages of the E-type system
|
||
- less compiler checking on silly errors you make
|
||
|
||
advantages:
|
||
- low-level polymorphism, easier to make powerful generic functions.
|
||
- flexible way of programming: no problem that some types of return values
|
||
don't match, no superfluous "casts" etc., no unnecessary errormessages.
|
||
- no hard to find errors when mixing data of different sizes in expressions
|
||
|
||
|
||
@endnode
|
||
@node CH_8B
|
||
@title "8B. the basic type (LONG/PTR)"
|
||
8B. the basic type (LONG/PTR)
|
||
-----------------------------
|
||
There's only one basic, non-complex variable type in E, which is the
|
||
32bit type LONG. As this is the default type, it may be declared as:
|
||
|
||
DEF a:LONG or just: DEF a
|
||
|
||
This variable type may hold what's known as CHAR/INT/PTR/LONG types in other
|
||
languages. A special variation of LONG is the PTR type. This type
|
||
is compatible with LONG, with the only difference that it specifies
|
||
to what type it is a pointer. By default, the type LONG is specified
|
||
as PTR TO CHAR. Syntax:
|
||
|
||
DEF <var>:PTR TO <type>
|
||
|
||
where type is either a simple type or a compound type. Example:
|
||
|
||
DEF x:PTR TO INT, myscreen:PTR TO screen
|
||
|
||
Note that 'screen' is the name of an object as defined in intuition/screens.m
|
||
For example, if you open your own screen with:
|
||
|
||
myscreen:=OpenS(... etc.
|
||
|
||
you may use the pointer myscreen as in 'myscreen.rastport'. However,
|
||
if you do not wish to do anything with the variable until you call
|
||
CloseS(myscreen), you may simply declare it as
|
||
|
||
DEF myscreen
|
||
|
||
Variable declarations may have optional initialisations, but only
|
||
integer constants, i.e. no full expression:
|
||
|
||
DEF a=1, b=NIL:PTR TO textfont
|
||
|
||
|
||
@endnode
|
||
@node CH_8C
|
||
@title "8C. the simple type (CHAR/INT/LONG)"
|
||
8C. the simple type (CHAR/INT/LONG)
|
||
-----------------------------------
|
||
The simple types CHAR (8bit) and INT (16bit) may not be used as types
|
||
for a basic (single) variable; the reason for this must be clear by now.
|
||
However they may be used as data type to build ARRAYs from, set PTRs to,
|
||
use in the definition of OBJECTs etc. See those for examples.
|
||
|
||
|
||
@endnode
|
||
@node CH_8D
|
||
@title "8D. the array type (ARRAY)"
|
||
8D. the array type (ARRAY)
|
||
--------------------------
|
||
ARRAYs are declared by specifying their length (in elements):
|
||
|
||
DEF b[100]:ARRAY
|
||
|
||
this defines an array of 100 bytes. Internally, 'b' is a variable of
|
||
type LONG and a PTR to this memory area.
|
||
Default type of an array-element is CHAR, it may be anything by specifying:
|
||
|
||
DEF x[100]:ARRAY OF LONG
|
||
DEF mymenus[10]:ARRAY OF newmenu
|
||
|
||
where "newmenu" is an example of a structure, called OBJECTs in E.
|
||
Array access is very easy with: <var>[<sexp>]
|
||
|
||
b[1]:="a"
|
||
z:=mymenus[a+1].mutualexclude
|
||
|
||
Note that the index of an array of size n ranges from 0 to n-1,
|
||
and not from 1 to n.
|
||
Note that ARRAY OF <type> is compatible with PTR TO <type>, with the
|
||
only difference that the variable that is an ARRAY is already
|
||
initialised.
|
||
|
||
|
||
@endnode
|
||
@node CH_8E
|
||
@title "8E. the complex type (STRING/LIST)"
|
||
8E. the complex type (STRING/LIST)
|
||
----------------------------------
|
||
- STRINGs. Similar to arrays, but different in the respect that they may
|
||
only be changed by using E string functions, and that they contain
|
||
length and maxlength information, so string functions may alter them in a
|
||
safe fashion, i.e: the string can never grow bigger than the memory
|
||
area it is in. Definition:
|
||
|
||
DEF s[80]:STRING
|
||
|
||
The STRING datatype (called an estring) is backwards compatible with
|
||
PTR TO CHAR and of course ARRAY OF CHAR, but not the other way around.
|
||
(see @{" 9B " link CH_9B } on string functions for more details).
|
||
|
||
- LISTs. These may be interpreted as a mix between a STRING and an ARRAY
|
||
OF LONG. I.e: this data structure holds a list of LONG variables which may
|
||
be extended and shortened like STRINGs. Definition:
|
||
|
||
DEF x[100]:LIST
|
||
|
||
A powerful addition to this datatype is that it also has a 'constant'
|
||
equivalent [], like STRINGs have ''. LIST is backward compatible with
|
||
PTR TO LONG and of course ARRAY OF LONG, but not the other way around.
|
||
(see @{" 9C " link CH_9C } and 2G) for more on this.
|
||
|
||
|
||
@endnode
|
||
@node CH_8F
|
||
@title "8F. the compound type (OBJECT)"
|
||
8F. the compound type (OBJECT)
|
||
------------------------------
|
||
OBJECTs are like a struct/class in C/C++ or a RECORD in pascal. Example:
|
||
|
||
OBJECT myobj
|
||
a:LONG
|
||
b:CHAR
|
||
c:INT
|
||
ENDOBJECT
|
||
|
||
This defines a data structure consisting of three elements. Syntax:
|
||
|
||
OBJECT <objname>
|
||
<membername> [ : <type> ] /* any number of these */
|
||
ENDOBJECT
|
||
|
||
where type is one of the following:
|
||
|
||
CHAR/INT/LONG/<object>
|
||
PTR TO CHAR/INT/LONG/<object>
|
||
ARRAY OF CHAR/INT/LONG/<object>
|
||
|
||
(ARRAY is short for ARRAY OF CHAR)
|
||
|
||
like DEF declarations, omitting the type means :LONG.
|
||
|
||
Note that <membername> need not be a unique identifier,
|
||
it may be in other objects too. There are lots of ways to use objects:
|
||
|
||
DEF x:myobj /* x is a structure */
|
||
DEF y:PTR TO myobj /* y is just a pointer to it */
|
||
DEF z[10]:ARRAY OF myobj
|
||
|
||
y:=[-1,"a",100]:myobj /* typed lists */
|
||
|
||
IF y.b="a" THEN /* ... */
|
||
|
||
z[4].c:=z[d+1].b++
|
||
|
||
(see @{" 4F " link CH_4F } and other parts of chapter 4 for these)
|
||
|
||
ARRAYs in objects are always rounded to even sizes, and put on
|
||
even offsets:
|
||
|
||
OBJECT mystring
|
||
len:CHAR, data[9]:ARRAY
|
||
ENDOBJECT
|
||
|
||
SIZEOF mystring is 12, and "data" starts at offset 2.
|
||
|
||
'PTR TO' is the only type in OBJECTs that may refer to yet undeclared
|
||
other objects.
|
||
|
||
(see @{" 14A " link CH_14A } for all other OBJECT features that are somehow OO related)
|
||
|
||
@endnode
|
||
@node CH_8G
|
||
@title "8G. initialisation"
|
||
8G. initialisation
|
||
------------------
|
||
|
||
1. Always initialised to NIL (or else, if explicitly stated)
|
||
- global variables
|
||
NOTE: for documentation purposes, it's always nicer if you
|
||
write =NIL in the definitions of variables that you expect to be NIL.
|
||
2. Initialised to '' and [] resp.
|
||
- global and local STRINGs
|
||
- global and local LISTs
|
||
3. Not initialised
|
||
- local variables (unless explicitly stated)
|
||
- global and local ARRAYs
|
||
- global and local OBJECTs
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_8H
|
||
@title "8H. the essentials of the E typesystem"
|
||
8H. the essentials of the E typesystem
|
||
--------------------------------------
|
||
This section tries to explain how the E typesystem works from
|
||
another perspective.
|
||
|
||
Most problems people have while programming in E stem from their incorrect
|
||
view of how the E type-system works, Also, many people have an idea how types
|
||
work from their previous programming language, and try to apply this to E,
|
||
which is often fatal, because E is quite different when it come to types.
|
||
|
||
The Type System.
|
||
but E is in essence a TYPELESS language. Indeed, variables may have a type,
|
||
but this is only used as a specification how to dereference a variable when
|
||
it is used as a pointer. In almost ALL other language constructions,
|
||
variables are treated as all being of the same type, namely the 32bit
|
||
typeless value.
|
||
|
||
In practise this means that for example in expressions with the exception
|
||
of the ".", "[]" and "++" operators etc., all operators and functions work
|
||
on 32bit values, regardless of whether they represent booleans, integers,
|
||
reals or pointers to something.
|
||
|
||
Pointer Types.
|
||
In the E type-system only 4 types exist, PTR TO CHAR, PTR TO INT,
|
||
PTR TO LONG and PTR TO <object>, where <object> is a name of a previously
|
||
defined OBJECT. When a variable (or an object member, as we'll see later)
|
||
is declared as being of this type, It means that if the variable contains
|
||
a value that is a legal pointer, this is how it should be dereferenced.
|
||
|
||
LONG, ARRAY etc.
|
||
All other types one may see in a DEF declaration are not really types, as
|
||
they really are only other ways of writing one of the above four. As an
|
||
example, ARRAY OF <type> is just another way of writing PTR TO <type>, with
|
||
the only difference that the former is automatically assigned the address
|
||
of an area of stackspace which is big enough to hold data for the #of
|
||
elements specified in square brackets.
|
||
|
||
Here's a table that shows all E 'types' in terms of the basic four:
|
||
|
||
ARRAY OF CHAR, ARRAY, STRING, LONG (are equal to) PTR TO CHAR
|
||
ARRAY OF INT (is equal to) PTR TO INT
|
||
ARRAY OF LONG, LIST (are equal to) PTR TO LONG
|
||
ARRAY OF <object>, <object> (are equal to) PTR TO <object>
|
||
|
||
- LONG is for variables that are not intended to be used as a pointer,
|
||
i.e integers. Its equivalence with PTR TO CHAR is quite logical, as
|
||
conceptually both talk about things that are measured in units of 1.
|
||
(for example, "++" has the same effect on both)
|
||
- LIST and STRING are the same as their ARRAY equivalents, in respect
|
||
to the fact that they're initialised to a piece of stack-space, but
|
||
their stack representation is a little more complex to facilitate
|
||
runtime bounds-checking (when used with the correct functions).
|
||
- an <object> is equivalent to [1]:ARRAY OF <object>. both represent
|
||
an initialised PTR TO <object>.
|
||
|
||
In an OBJECT one can have the same declarations, with the addition of CHAR
|
||
and INT (similar to LONG), and the ommission of LIST and STRING, as these
|
||
are complex objects in their own right, and cannot be part of an object.
|
||
|
||
Deferencing.
|
||
Given a pointer p of some type,
|
||
|
||
"[]" may index other elements that are sequentially ordered next to
|
||
the element it is currently pointing to. note that this allows for
|
||
both positive and negative indices, and also no assumptions are made
|
||
about where and how many elements are actually allocated.
|
||
|
||
"++" sets the pointer to the next element in memory, "--" to the previous
|
||
one. note that these operators always operate on the pointer and
|
||
never on the the element the pointer is pointing to.
|
||
|
||
"." works similar to "[]", only now indexes the pointer by name, i.e. the
|
||
pointer must be a PTR TO <object>.
|
||
|
||
"[]" and "." may be concatenated to a pointer p in any sequence, given the
|
||
fact that the previous resulting value again is known to be of a "PTR TO"
|
||
type.
|
||
|
||
One does not need to write out a de-reference in total, as in other
|
||
languages, e.g. if p is an ARRAY OF obj, instead of having to write
|
||
p[index].member you can write just p[index], which logically results
|
||
in the address of that object. This also explains why p[].member
|
||
is equivalent to p.member, since p[] is the same as p when it points
|
||
to an object.
|
||
|
||
Reference Semantics.
|
||
Another type-related issue that makes E somewhat different from other
|
||
languages and thus harder to grasp is it's accent on Reference Semantics
|
||
rather than Value Semantics. I'll try to argue why that's good here.
|
||
|
||
Informally, Reference Semantics means that objects in a language (mostly
|
||
other than the simple ones like LONGs) are represented by pointers, while
|
||
Value Semantics treats these objects as just being themselves. An example
|
||
of a language that has only Value Semantics is BASIC, examples of
|
||
languages that have them both are the C/C++ and Pascal type-of languages,
|
||
and examples of Reference only are newer Object Oriented languages,
|
||
functional languages like LISP and of course E.
|
||
|
||
Using Reference Semantics doesn't mean being occupied with pointers
|
||
all the time, rather you're worrying about them a lot less then in the
|
||
mixed case or the Value-only case, especially since in real life programs
|
||
most non-trivial data-structures get allocated dynamically which implies
|
||
pointers. The best example of this is LISP, where one programs heavily
|
||
with pointers without noticing. In E, one could easily forget STRING
|
||
is a pointer, given the easy by which one can pass it around to other
|
||
functions; in C often lots of "&" are needed where in the equivalent E
|
||
case none are, and the Oberon equivalent of bla('hallo') looks like
|
||
bla(sys.ADR('hallo')) because the string doesn't represent a pointer,
|
||
but a value as a whole...
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_9A
|
||
@title "9A. io functions"
|
||
9A. io functions
|
||
----------------
|
||
|
||
WriteF(formatstring,args,...)
|
||
PrintF(formatstring,args,...)
|
||
|
||
prints a string (which may contain formatting codes) to stdout. Zero
|
||
to unlimited arguments may be added. Note that, as formatstrings may
|
||
be created dynamically, no check on the correct number of arguments
|
||
is (can be) made. Examples:
|
||
|
||
WriteF('Hello, World!\\n') /* just write a lf terminated string */
|
||
|
||
WriteF('a = \\d \\n',a) /* writes: "a = 123", if a was 123 */
|
||
|
||
(see @{" 2F " link CH_2F } about strings for more).
|
||
NOTE: if stdout=NIL, for example if your program was started from the
|
||
Workbench, WriteF() will create an output window, and put the handle
|
||
in conout and stdout. This window will automatically be closed on
|
||
exit of the program, after the user typed a <return>. WriteF() is the
|
||
only function that will open this window, so if you want to do IO
|
||
on stdout, and want to be sure stdout<>NIL, perform a "WriteF('')"
|
||
as first instruction of your program to ensure output. If you want
|
||
to open a console window yourself, you may do so by placing the resulting
|
||
file handle in the 'stdout' and 'conout' variables, as your window will
|
||
then be closed automatically upon exit. If you wish to close this window
|
||
manually, make sure to set 'conout' back to NIL, to signal E that there's
|
||
no console window to be closed. PrintF() is the same as WriteF only
|
||
uses the v37+ buffered IO.
|
||
both return the length of the string that was printed.
|
||
|
||
Out(filehandle,char) and char:=Inp(filehandle)
|
||
|
||
Either write or read one single byte to some file or stdout
|
||
if char=-1 then an EOF was reached, or an error occurred.
|
||
Out returns the number of bytes actually written (<>1 is error).
|
||
The functions use dos.library unbuffered IO, and as such are
|
||
slow for large amounts of data.
|
||
|
||
len:=FileLength(namestring)
|
||
|
||
lets you determine the length of a file you *may* wish to load, and
|
||
also, if it exists (returns -1 upon error/file not found).
|
||
|
||
ok:=ReadStr(filehandle,estring)
|
||
|
||
(see @{" 9B " link CH_9B })
|
||
|
||
oldout:=SetStdOut(newstdout)
|
||
oldin:=SetStdIn(newstdin)
|
||
|
||
Sets the standard output variable 'stdout' (not the value
|
||
returned from Input() and Output()!). Equivalent for:
|
||
oldout:=stdout; stdout:=newstdout. Same goes for the stdin
|
||
variable.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_9B
|
||
@title "9B. strings and string functions"
|
||
9B. strings and string functions
|
||
--------------------------------
|
||
|
||
E has a datatype STRING. This is a string, from now on called 'Estring',
|
||
that may be modified and changed in size, as opposed to normal 'strings',
|
||
which will be used here for any zero-terminated sequence. Estrings are
|
||
downward compatible with strings, but not the other way around, so if an
|
||
argument requests a normal string, it can be either of them. If an Estring
|
||
is requested, don't use normal strings. Example of usage:
|
||
|
||
DEF s[80]:STRING, n -> s is an estring with a maxlen of 80
|
||
ReadStr(stdout,s) -> read input from the console
|
||
n:=Val(s) -> get a number out of it
|
||
-> etc.
|
||
|
||
Note that all string functions will handle cases where string tends to
|
||
get longer than the maximum length correctly;
|
||
|
||
DEF s[5]:STRING
|
||
StrAdd(s,'this string is longer than 5 characters',ALL)
|
||
|
||
s will contain just 'this '.
|
||
A string may also be allocated dynamically from system memory
|
||
with the function String(), (note: the pointer returned from this function
|
||
must always be checked against NIL)
|
||
|
||
s:=String(maxlen)
|
||
|
||
DEF s[80]:STRING is equivalent to DEF s and s:=String(10)
|
||
|
||
|
||
bool:=StrCmp(string,string,len=ALL)
|
||
|
||
compares two strings. len must be the number of bytes to compare,
|
||
or 'ALL' if the full length is to be compared. Returns TRUE or FALSE
|
||
(len is a default argument (see @{" 6F " link CH_6F }))
|
||
|
||
StrCopy(estring,string,len=ALL)
|
||
|
||
copies the string into the estring. If len=ALL, all will be copied.
|
||
returns the estring.
|
||
|
||
StrAdd(estring,string,len=ALL)
|
||
|
||
same as StrCopy(), only now the string is concatenated to the end.
|
||
returns the estring.
|
||
|
||
len:=StrLen(string)
|
||
|
||
calculates the length of any zero-terminated string
|
||
|
||
len:=EstrLen(estring)
|
||
|
||
returns the length of an estring
|
||
|
||
max:=StrMax(estring)
|
||
|
||
returns the maximum length of a estring
|
||
|
||
StringF(estring,fmtstring,args,...)
|
||
|
||
similar to WriteF, only now output goes to estring instead of stdout.
|
||
example:
|
||
|
||
StringF(s,'result: \\d\\n',123)
|
||
|
||
's' will be 'result: 123\\n'
|
||
returns the estring, and length as second returnvalue.
|
||
|
||
RightStr(estring,estring,n)
|
||
|
||
fills estring with the last n characters of the second estring
|
||
returns the estring.
|
||
|
||
MidStr(estring,string,pos,len=ALL)
|
||
|
||
copies any number of characters (including all if len=ALL) from
|
||
position pos in string to estring
|
||
NOTEZ BIEN: in all string related functions where a position in a
|
||
string is used, the first character in a string has position 0,
|
||
not 1, as is common in languages like BASIC.
|
||
returns the estring.
|
||
|
||
value,read:=Val(string,read=NIL)
|
||
|
||
finds an integer encoded in ascii out of a string. Leading spaces/tabs
|
||
etc. will be skipped, and also hexadecimal numbers (1234567890ABCDEFabcdef)
|
||
and binary numbers (01) may be read this way if they are preceded by a
|
||
"$" or a "%" sign respectively. A minus "-" may indicate a negative integer.
|
||
Val() returns the number of characters read in the second argument, which
|
||
must be given by reference (<-!!!), or can be received as second returnvalue.
|
||
If "read" returns 0 (value will be 0 too) then the string did not contain an
|
||
integer, or the value was too sizy to fit in 32bit. "read" may be NIL.
|
||
|
||
examples of strings that would be parsed correctly:
|
||
'-12345', '%10101010', ' -$ABcd12'
|
||
|
||
these would return both as "value" and in read a 0:
|
||
'', 'hello!'
|
||
|
||
|
||
foundpos:=InStr(string1,string2,startpos=0)
|
||
|
||
searches string1 for the occurrence of string2, possibly starting from
|
||
another position than 0. Returned is the offset at which the substring
|
||
was found, else -1.
|
||
|
||
newstringadr:=TrimStr(string)
|
||
|
||
returns the *address* of the first character in a string, i.e., after
|
||
leading spaces, tabs etc.
|
||
|
||
UpperStr(string) and LowerStr(string)
|
||
|
||
changes the case of a string.
|
||
TAKE NOTE: these functions modify the contents of 'string', so they may
|
||
only be used on estrings, and strings that are part of your programs data.
|
||
Effectively this means that if you obtain the address of a string through
|
||
some amiga-system function, you must first StrCopy() it to a string of
|
||
your program, then use these functions.
|
||
they return the string.
|
||
|
||
ok:=ReadStr(filehandle,estring)
|
||
|
||
will read a string (ending in ascii 10) from any file or stdout.
|
||
ok contains -1 if an error occurred, or an EOF was reached.
|
||
Note: the contents of the string read so far is still valid.
|
||
Also note that, like Inp() Out(), etc. this function makes use
|
||
of unbuffered 1.3 style IO, and thus may be slow. The dos.library
|
||
Fgets() function forms a nice alternative.
|
||
|
||
SetStr(estring,newlen)
|
||
|
||
manually sets the length of a string. This is only handy when you read
|
||
data into the estring by a function other then an E string function,
|
||
and want to continue using it as an Estring. For example, after
|
||
using a function that just puts a zero-terminated string at the
|
||
address of estring, use SetStr(mystr,StrLen(mystr)) to make
|
||
it manipulatable again. If the string is too long, SetStr does nothing
|
||
(this should always be prevented).
|
||
|
||
AstrCopy(string1,string2,size)
|
||
|
||
'Array String Copy' copies string2 into the memory area denoted by string1.
|
||
string1 is typically not an estring but an ARRAY. size is the total #of chars
|
||
string1 can hold, i.e. if you write 5 and string2='helloworld', string1 will
|
||
be 'hell' + 0termination.
|
||
|
||
order:=OstrCmp(string1,string2,max=ALL)
|
||
|
||
'Ordered String Compare' returns 1 if string2>string1, 0 for equal and -1
|
||
for less. only max chars are compared.
|
||
|
||
for string linking functions (see @{" 9H " link CH_9H })
|
||
|
||
|
||
@endnode
|
||
@node CH_9C
|
||
@title "9C. lists and list functions"
|
||
9C. lists and list functions
|
||
----------------------------
|
||
|
||
Lists are like strings, only they consist of LONGs, not CHARs.
|
||
They may also be allocated either global, local or dynamic:
|
||
|
||
DEF mylist[100]:LIST /* local or global */
|
||
DEF a
|
||
a:=List(10) /* dynamic */
|
||
|
||
(note that in the latter case, pointer 'a' may contain NIL)
|
||
Just as strings may be represented as constants in expressions, lists
|
||
have their constant equivalent:
|
||
|
||
[1,2,3,4]
|
||
|
||
The value of such an expression is a pointer to an already initialised list.
|
||
Special feature is that they may have dynamic parts, i.e, which will
|
||
be filled in at runtime:
|
||
|
||
a:=3
|
||
[1,2,a,4]
|
||
|
||
moreover, lists may have some other type than the default LONG, like:
|
||
|
||
[1,2,3]:INT
|
||
[65,66,67,0]:CHAR /* equivalent with 'ABC' */
|
||
['topaz.font',8,0,0]:textattr
|
||
OpenScreenTagList(NIL,[SA_TITLE,'MyScreen',TAG_DONE])
|
||
|
||
As shown in the latter examples, lists are extremely useful with
|
||
system functions: they are downward compatible with an ARRAY OF LONG,
|
||
and object-typed ones can be used wherever a system function needs
|
||
a pointer to some structure, or an array of those.
|
||
Taglists and vararg functions may also be used this way.
|
||
NOTEZ BIEN: all list functions only work with LONG lists, typed-lists
|
||
are only convenient in building complex data structures and expressions.
|
||
|
||
As with strings, a certain hierarchy holds:
|
||
list variables -> constant lists -> array of long/ptr to long
|
||
When a function needs an array of long you might just as well give a list
|
||
as argument, but when a function needs a listvar, or a constant list,
|
||
then an array of long won't do.
|
||
|
||
It's important that one understands the power of lists and in particular
|
||
typed-lists: these can save you lots of trouble when building just
|
||
about any data-structure. Try to use these lists in your own programs,
|
||
and see what function they have in the example-programs.
|
||
|
||
summary:
|
||
|
||
[<item>,<item>,... ] immediate list (of LONGs, use with listfuncs)
|
||
[<item>,<item>,... ]:<type> typed list (just to build data structures)
|
||
|
||
If <type> is a simple type like INT or CHAR, you'll just have the
|
||
initialised equivalent of ARRAY OF <type>, if <type> is an object-name,
|
||
you'll be building initialised objects, or ARRAY OF <object>, depending
|
||
on the length of the list.
|
||
|
||
If you write [1,2,3]:INT you'll create a data structure of 6 bytes,
|
||
of 3 16bit values to be precise. The value of this expression then
|
||
is a pointer to that memory area. Same works if, for example, you have
|
||
an object like:
|
||
|
||
OBJECT myobject
|
||
a:LONG, b:CHAR, c:INT
|
||
ENDOBJECT
|
||
|
||
writing [1,2,3]:myobject would then mean creating a data structure
|
||
in memory of 8 bytes, with the first four bytes being a LONG with value 1,
|
||
the following byte a CHAR with value 2, then a pad byte, and the last
|
||
two bytes an INT (2 bytes) with value 3. you could also write:
|
||
|
||
[1,2,3,4,5,6,7,8,9]:myobject
|
||
|
||
you would be creating an ARRAY OF myobject with size 3. Note that such
|
||
lists don't have to be complete (3,6,9 and so on elements), you may
|
||
create partial objects with lists of any size
|
||
|
||
One last note on data size: on the amiga, you may rely on the fact that
|
||
a structure like 'myobject' has size 8, and that it has a pad byte
|
||
to have word (16bit) alignment. It is however very likely that an
|
||
E-compiler for 80x86 architectures will not use the pad byte and make
|
||
it a 7byte structure, and that an E-compiler for a sun-sparc architecture
|
||
(if I'm not mistaken) will try to align on 32bit boundaries, thus make
|
||
it a 10 or 12 byte structure. Some microprocessors (they are rare, but
|
||
they exist) even use (36:18:9) as numbers of bits for their types
|
||
(LONG:INT:CHAR), instead of (32:16:8) as we're used to. So don't make too
|
||
great an assumption on the structure of OBJECTs and LISTs if you want to
|
||
write code that stands a chance of being portable or doesn't rely on side
|
||
effects.
|
||
|
||
ListCopy(listvar,list,num=ALL)
|
||
|
||
Copies num elements from list to listvar. example:
|
||
DEF mylist[10]:LIST
|
||
ListCopy(mylist,[1,2,3,4,5],ALL)
|
||
returns listvar.
|
||
|
||
ListAdd(listvar,list,num=ALL)
|
||
|
||
Copies num items of list to the tail of listvar.
|
||
returns listvar.
|
||
|
||
ListCmp(list,list,num=ALL)
|
||
|
||
Compares two lists, or some part of them.
|
||
|
||
len:=ListLen(list)
|
||
|
||
Returns length of list, like ListLen([a,b,c]) would return 3
|
||
|
||
max:=ListMax(listvar)
|
||
|
||
returns maximum possible length of a listvar.
|
||
|
||
value:=ListItem(list,index)
|
||
|
||
functions as value:=list[index] with the difference that
|
||
list may also be a constant value instead of a pointer. This is
|
||
very useful in situations like this where we directly want to
|
||
use a list of values:
|
||
|
||
WriteF(ListItem(['ok!','no mem!','no file!'],error))
|
||
|
||
this prints an errormessage according to "error". it's similar to:
|
||
|
||
DEF dummy:PTR TO LONG
|
||
dummy:=['ok!','no mem!','no file!']
|
||
WriteF(dummy[error])
|
||
|
||
SetList(listvar,newlen)
|
||
|
||
manually sets the length of a list. This will only be useful when you read
|
||
data into the list by a function other then a list-specific function,
|
||
and want to continue using it as a true list.
|
||
|
||
for list functions that make use of quoted expressions (see @{" 11C " link CH_11C }).
|
||
for list linking functions (see @{" 9H " link CH_9H }).
|
||
|
||
|
||
@endnode
|
||
@node CH_9D
|
||
@title "9D. intuition support functions"
|
||
9D. intuition support functions
|
||
-------------------------------
|
||
|
||
wptr:=OpenW(x,y,width,height,IDCMP,wflags,title,
|
||
screen,sflags,gadgets,taglist=NIL)
|
||
|
||
creates a window where wflags are flags for window layout
|
||
(like BACKDROP, SIMPLEREFRESH e.d, usually $F) and sflags are
|
||
for specifying the type of screen to open on (1=wb,15=custom).
|
||
screen must only be valid if sflags=15, else NIL will do.
|
||
gadgets may point to a glist structure, which you can easily
|
||
create with the Gadget() function, else NIL.
|
||
|
||
CloseW(wptr)
|
||
|
||
closes that screen again. Only difference from CloseWindow()
|
||
is that it accepts NIL-pointers and sets stdrast back to NIL.
|
||
|
||
sptr:=OpenS(width,height,depth,sflags,title,taglist=NIL)
|
||
|
||
opens a custom screen for you. depth is number of bitplanes (1-6, 1-8 AGA).
|
||
|
||
CloseS(sptr)
|
||
|
||
as CloseW(), now for screens.
|
||
|
||
nextbuffer:=Gadget(buffer,glist,id,flags,x,y,width,string)
|
||
|
||
[obsolete]
|
||
This function creates a list of gadgets, which can then be put in your
|
||
window by giving them as an argument to OpenW(), or afterwards with
|
||
intuition functions like AddGlist().
|
||
buffer is mostly an ARRAY of at least GADGETSIZE bytes to hold all the
|
||
structures associated with one gadget, id is any number that may help you
|
||
remember which gadget was pressed when an IntuiMessage arrives.
|
||
Flags are: 0=normal gadget, 1=boolean gadget, 3=boolean gadget that is
|
||
selected. Width is width in pixels, that should be large enough to hold
|
||
the string, which will be auto-centered. glist should be NIL for the first
|
||
gadget, and glistvar for all others, so E may link all gadgets.
|
||
The function returns a pointer to the next buffer (=buffer+GADGETSIZE).
|
||
Example for three gadgets:
|
||
|
||
CONST MAXGADGETS=GADGETSIZE*3
|
||
|
||
DEF buf[MAXGADGETS]:ARRAY, next, wptr
|
||
|
||
next:=Gadget(buf,NIL,1,0,10,20,80,'bla') /* the 1st gadget */
|
||
next:=Gadget(next,buf,... )
|
||
next:=Gadget(next,buf,... ) /* any amount linked 2 1st */
|
||
|
||
wptr:=OpenW( ...,buf)
|
||
|
||
code:=Mouse()
|
||
|
||
gives you the current state of all 2 or 3 mouse buttons; left=1,
|
||
right=2 and middle=4. If for example code=3 then left and right were
|
||
pressed together.
|
||
NOTEZ BIEN: this is not a real intuition function, if you want to
|
||
know about mouse-events the proper way, you'll have to check the
|
||
intuimessages that your window receives. This is the only E
|
||
function that directly checks the hardware, and thus only useful
|
||
in demo-like programs and for testing. (DO NOT USE THIS FUNCTION
|
||
IN PROGRAMS THAT ARE SUPPOSED TO WORK UNDER THE OS)
|
||
|
||
bool:=LeftMouse(win) WaitLeftMouse(win)
|
||
|
||
intuition Mouse() alternatives for programs that only want to test for
|
||
a mouseclick.
|
||
|
||
x:=MouseX(win) y:=MouseY(win)
|
||
|
||
[obsolete]
|
||
enables you to read the mouse coordinates. win is the window
|
||
they need to be relative to.
|
||
|
||
class:=WaitIMessage(window)
|
||
|
||
This function makes it easier to just wait for a window event. It simply
|
||
waits until a intuimessage arrives, and returns the class of the event.
|
||
It stores other variables like code and qualifiers as private global
|
||
variables, for access with functions described below.
|
||
WaitIMessage() represents the following code:
|
||
|
||
PROC waitimessage(win:PTR TO window)
|
||
DEF port,mes:PTR TO intuimessage,class,code,qual,iaddr
|
||
port:=win.userport
|
||
IF (mes:=GetMsg(port))=NIL
|
||
REPEAT
|
||
WaitPort(port)
|
||
UNTIL (mes:=GetMsg(port))<>NIL
|
||
ENDIF
|
||
class:=mes.class
|
||
code:=mes.code /* stored internally */
|
||
qual:=mes.qualifier
|
||
iaddr:=mes.iaddress
|
||
ReplyMsg(mes)
|
||
ENDPROC class
|
||
|
||
as you see, it gets exactly one message, and does not forget about
|
||
multiple messages arriving in one event, if called more than once.
|
||
For example, say you opened a window that displays something and just
|
||
waits for a closegadget (you specified IDCMP_CLOSEWINDOW only):
|
||
|
||
WaitIMessage(mywindow)
|
||
|
||
or, you have a program that waits for more types of events, handles
|
||
them in a loop, and ends on a closewindow event:
|
||
|
||
WHILE (class:=WaitIMessage(win))<>IDCMP_CLOSEWINDOW
|
||
/* handle other classes */
|
||
ENDWHILE
|
||
|
||
code:=MsgCode() qual:=MsgQualifier() iaddr:=MsgIaddr()
|
||
|
||
These all supply you with the private global variables as mentioned
|
||
before. the values returned are all defined by the most recent call
|
||
to WaitIMessage(). Example:
|
||
|
||
IF class:=IDCMP_GADGETUP
|
||
mygadget:=MsgIaddr()
|
||
IF mygadget.userdata=1 THEN /* ... user pressed gadget #1 */
|
||
ENDIF
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_9E
|
||
@title "9E. graphics support functions"
|
||
9E. graphics support functions
|
||
------------------------------
|
||
|
||
All graphics support functions that do not explicitly ask for a rastport,
|
||
make use of the system-variable 'stdrast'. It is automatically defined by
|
||
the last call to OpenW() or OpenS(), and is set to NIL by CloseW() and
|
||
CloseS(). Calling these routines while stdrast is still NIL is legal.
|
||
stdrast may be manually set by SetStdRast() or stdrast:=myrast
|
||
|
||
Plot(x,y,colour=1)
|
||
|
||
Draws a single dot on your screen/window in one of the colours available.
|
||
colour ranges from 0-255, or 0-31 on pre-AGA machines.
|
||
|
||
Line(x1,y1,x2,y2,colour=1)
|
||
|
||
Draws a line
|
||
|
||
Box(x1,y1,x2,y2,colour=1)
|
||
|
||
Draws a box
|
||
|
||
Colour(foreground,background=0)
|
||
|
||
sets the colours for all graphics functions (from the library) that
|
||
do not take a colour as argument. This is the colour *register*
|
||
(i.e 0-31) and not colour *value*
|
||
NOTE: functions that have "colour" as an argument, change the Apen
|
||
of stdrast.
|
||
|
||
TextF(x,y,formatstring,args,...)
|
||
|
||
exactly the same function as WriteF(), only outputs to some (x,y) on
|
||
your stdrast, instead of stdout. (see @{" 9A " link CH_9A }, WriteF() and strings in the language
|
||
reference). returns the length of the resulting string.
|
||
|
||
oldrast:=SetStdRast(newrast)
|
||
|
||
changes the output rastport of the E graphics functions
|
||
|
||
SetTopaz(size=8)
|
||
|
||
[obsolete: don't use]
|
||
lets you set the font of the rastport "stdrast" to topaz. size is 8 or 9.
|
||
Only to be used as last resort if you can't support font-sensitivity.
|
||
|
||
SetColour(screen,colourreg,r,g,b)
|
||
|
||
set colour register (0..255) of screen to certain RGB values.
|
||
each rgb value has a range of 0..255, i.e. 24bit colour. this
|
||
function will automatically rescale to 12bit colour if no AGA
|
||
is present, and also use the correct function.
|
||
|
||
|
||
@endnode
|
||
@node CH_9F
|
||
@title "9F. system support functions"
|
||
9F. system support functions
|
||
----------------------------
|
||
|
||
bool:=KickVersion(vers)
|
||
|
||
Will give TRUE if the kickstart in the machine your program is running
|
||
on is equal or higher than vers, else FALSE
|
||
|
||
mem:=New(n)
|
||
|
||
This dynamically creates an array (or memory area, if you wish) of
|
||
n bytes. Difference with AllocMem() is that it is called automatically
|
||
with flags $10000 (i.e cleared mem, any type) and that no calls to
|
||
Dispose() are necessary, as it is linked to a memory list that is
|
||
automatically de-allocated upon exit of your program.
|
||
(see @{" 4K " link CH_4K }, also)
|
||
|
||
mem:=NewR(n)
|
||
|
||
same as New(), only now automatically raises the "MEM" exception
|
||
instead of return if no memory could be allocated.
|
||
|
||
mem:=NewM(n,flags)
|
||
|
||
same as NewR(), but also allows you to specify flags (MEMF_CHIP etc.)
|
||
|
||
Dispose(mem)
|
||
|
||
Frees any mem allocated by New(). You only have to use this function
|
||
if you explicitly wish to free memory _during_ your program, as all
|
||
is freed at the end anyway.
|
||
|
||
CleanUp(returnvalue=0)
|
||
|
||
[obsolete: use exception handling]
|
||
Exits the program from any point. It is the replacement for the DOS
|
||
call Exit(): never use it! instead use CleanUp(), which allows
|
||
for the deallocation of memory, closing libraries correctly etc.
|
||
The return value will be given to dos as returncode.
|
||
Cleanup() is ONLY necessary if you have to exit at a point different
|
||
from ENDPROC in main.
|
||
|
||
amount:=FreeStack()
|
||
|
||
returns the amount of free stack space left. This should always be 1000 or
|
||
more. (see @{" 16C " link CH_16C } on how E organizes its stack. If you don't do heavy recursion,
|
||
you need not worry about your free stack space.
|
||
|
||
bool:=CtrlC()
|
||
|
||
Returns TRUE if Ctrl-C was pressed since you last checked, else FALSE.
|
||
This only works for programs running on a console, i.e. cli-programs.
|
||
|
||
Example of how these last three functions may be used:
|
||
|
||
/* calculate faculty from command-line argument */
|
||
|
||
OPT STACK=100000
|
||
|
||
PROC main()
|
||
DEF num,r
|
||
num:=Val(arg,{r})
|
||
IF r=0 THEN WriteF('bad args.\\n') ELSE WriteF('result: \\d\\n',fac(num))
|
||
ENDPROC
|
||
|
||
PROC fac(n)
|
||
DEF r
|
||
IF FreeStack()<1000 OR CtrlC() THEN CleanUp(5) /* xtra check */
|
||
IF n=1 THEN r:=1 ELSE r:=fac(n-1)*n
|
||
ENDPROC r
|
||
|
||
Of course, this recursion will hardly run out of stack space, and when it
|
||
does, it's halted by FreeStack() so fast you won't have time to press
|
||
CtrlC, but it's the idea that counts here.
|
||
A definition of fac(n) like:
|
||
|
||
PROC fac(n) IS IF n=1 THEN 1 ELSE fac(n-1)*n
|
||
|
||
would be less safe.
|
||
|
||
|
||
mem:=FastNew(size) FastDispose(mem,size) FastDisposeList(list)
|
||
|
||
FastNew() and FastDispose() are replacements for NewR(size) and
|
||
Dispose(ptr) (they are used by NEW and END). this is what they have
|
||
in common:
|
||
- "NEW" exceptions may be Raised
|
||
- memory is always cleared
|
||
- auto-dealloc at end of program
|
||
- NIL accepted in deallocation
|
||
but the following differences should be noted (positive):
|
||
- they are varying from 10 to 50 times faster (!)
|
||
- they use way less memory for small objects
|
||
- they do not fragment memory
|
||
[all this is for objects <=256 bytes, for bigger ones NewR() and
|
||
Dispose() are used].
|
||
negative:
|
||
- they do not free mem, but recycle it.
|
||
- FastDispose() needs exact size of allocation. END also.
|
||
|
||
List allocated with NEW need the function FastDisposeList(). Because
|
||
Lists have a length, the size parameter isn't needed.
|
||
|
||
|
||
@endnode
|
||
@node CH_9G
|
||
@title "9G. math and other functions"
|
||
9G. math and other functions
|
||
----------------------------
|
||
|
||
a:=And(b,c) a:=Or(b,c) a:=Not(b)
|
||
a:=Eor(b,c)
|
||
|
||
These work with the usual operations, boolean as well as arithmetic.
|
||
Note that for And() and Or() an operator exists.
|
||
|
||
a:=Mul(b,c) a:=Div(a,b)
|
||
|
||
Performs the same operation as the '*' and '/' operators, but now in
|
||
full 32bit. For speed reasons, normal operations are 16bit*16bit=32bit
|
||
and 32bit/16bit=16bit. This is sufficient for nearly all calculations,
|
||
and where it's not, you may use Mul() and Div(). NOTE: in the Div
|
||
case, a is divided by b, not b by a.
|
||
|
||
bool:=Odd(x) bool:=Even(x)
|
||
|
||
Return TRUE or FALSE if some expression is Odd or Even
|
||
|
||
Min(a,b) Max(a,b)
|
||
|
||
compute min and max of the two given ints.
|
||
|
||
randnum:=Rnd(max) seed:=RndQ(seed)
|
||
|
||
Rnd() computes a random number from an internal seed in range 0 .. max-1.
|
||
For example, Rnd(1000) returns an integer from 0..999
|
||
To initialise the internal seed, call Rnd() with a negative value;
|
||
the Abs() of that value will be the initial seed.
|
||
|
||
RndQ() computes a random number "Q"uicker than Rnd() does, but returns
|
||
only full range 32bit random numbers. Use the result as the seed for
|
||
the next call, and for startseed, use any large value, like $A6F87EC1
|
||
|
||
absvalue:=Abs(value)
|
||
|
||
computes the absolute value.
|
||
|
||
s:=Sign(v)
|
||
|
||
computes the sign of v, i.e. returns -1,0,1
|
||
|
||
a,b:=Mod(c,d)
|
||
|
||
Divides 32bit c by 16bit d and returns 16bit modulo a and optionally
|
||
a 16bit result of the division b. If these 16bit limits are exceeded,
|
||
Mod() will not give correct results.
|
||
|
||
x:=Shl(y,num) x:=Shr(y,num)
|
||
|
||
arithmatically shifts y num bits to left or right (set new bits to 0).
|
||
|
||
a:=Long(adr) a:=Int(adr) a:=Char(adr)
|
||
|
||
peeks into memory at some address, and returns the value found. This
|
||
works with 32, 16 and 8 bit values respectively. Note that the compiler does
|
||
not check if 'adr' is valid. These functions are available in E for
|
||
those cases where reading and writing in memory with PTR TO <type>
|
||
would only make a program more complex or less efficient. You are _not_
|
||
encouraged to use these functions.
|
||
|
||
PutLong(adr,a) PutInt(adr,a) PutChar(adr,a)
|
||
|
||
Pokes value 'a' into memory. See: Long()
|
||
|
||
y:=Bounds(x,a,b)
|
||
|
||
makes sure x lies between bounds a and b, and adjust acordingly if necessary.
|
||
It equals: y:=IF x<a THEN a ELSE IF x>b THEN b ELSE x
|
||
|
||
|
||
@endnode
|
||
@node CH_9H
|
||
@title "9H. string and list linking functions"
|
||
9H. string and list linking functions
|
||
-------------------------------------
|
||
E provides for a set of functions that allows the creation of
|
||
linked list with the STRING and LIST datatype, or strings and lists
|
||
that were created with String() and List() respectively. As you may
|
||
know by now, strings and lists, complex datatypes, are pointers
|
||
to their respective data, and have extra fields to a negative offset
|
||
of that pointer specifying their current length and maxlength. the
|
||
offsets of these fields are PRIVATE. as an addition to those two,
|
||
any complex datatype has a 'next' field, which is set to NIL by
|
||
default, which may be used to build linked list of strings, for example.
|
||
in the following, I will use 'complex' to denote a ptr to a STRING
|
||
or LIST, and 'tail' to denote another such pointer, or one that already
|
||
has other strings linked to it. 'tail' may also be a NIL pointer,
|
||
denoting the end of a linked list.
|
||
[note: these String-list functions have nothing to do with E-lists or
|
||
Lisp-Cell lists]
|
||
The following functions may be used:
|
||
|
||
complex:=Link(complex,tail)
|
||
|
||
puts the value tail into the 'next' field of complex. returns again complex.
|
||
example:
|
||
|
||
DEF s[10]:STRING, t[10]:STRING
|
||
Link(s,t)
|
||
|
||
creates a linked list like: s --> t --> NIL
|
||
|
||
tail:=Next(complex)
|
||
|
||
reads the 'next' field of var complex. this may of course be NIL, or
|
||
a complete linked list. Calling Next(NIL) will result in NIL, so it's
|
||
safe to call Next when you're not sure if you're at the end of a linked list.
|
||
|
||
tail:=Forward(complex,num)
|
||
|
||
same as Next(), only goes forward num links, instead of one, thus:
|
||
|
||
Next(c) = Forward(c,1)
|
||
|
||
You may safely call Forward() with a num that is way too large;
|
||
Forward will stop if it encounters NIL while searching links, and
|
||
will return NIL.
|
||
|
||
DisposeLink(complex)
|
||
|
||
same as Dispose(), with two differences: it's only for strings and
|
||
lists allocated with String() or List(), and will automatically
|
||
de-allocate the tail of complex too. Note that large linked lists
|
||
containing strings allocated with String() as well as some allocated
|
||
locally or globally with STRING may also be de-allocated this way.
|
||
|
||
For a good example of how linked lists of strings may be put to
|
||
good use in real-life programs, see Src/Utils/D.e
|
||
|
||
|
||
@endnode
|
||
@node CH_9I
|
||
@title "9I. lisp-cells and cell functions"
|
||
9I. lisp-cells and cell functions
|
||
---------------------------------
|
||
yep. that's right. you thought LISP was fun, then try E now.
|
||
[or: the story about why E is a better LISP than LISP itself :-)]
|
||
|
||
Starting from v3, E has the cell datatype, almost identical to cells
|
||
in the LISP language. more technically, E has:
|
||
|
||
`Conservative Mark and Sweep Garbage-Collected Lisp-Cells'
|
||
|
||
basically this amounts to being able to allocate LISP-cells,
|
||
which are pairs of two values:
|
||
|
||
x:=<a|b>
|
||
|
||
which is much like NEW [a,b]:LONG, only now E will automatically
|
||
deallocate the 8 bytes in question itself when it finds out it needs
|
||
memory and no pointers are pointing to the cell. In practise this
|
||
means that you can freely have functions create cells as temporaries,
|
||
without worrying about freeing them. And any LISP-programmer will
|
||
be able to explain to you that with cells you can build any
|
||
data-structure (most notably trees and lists). [note: this text
|
||
does not thorougly explain how to make full use of cells, since
|
||
dozens of LISP books have been written about this]
|
||
|
||
Selecting the values can easily be done using Car(x) and Cdr(x),
|
||
two LISP-functions which select head and tail (first and second)
|
||
element of the cell. if x is a PTR TO LONG, even x[0] and x[1]
|
||
are allowed.
|
||
|
||
One can also write lists of cells:
|
||
|
||
<a,b,c>
|
||
|
||
(note the commas) as short for
|
||
|
||
<a|<b|<c|NIL>>>
|
||
|
||
An alternative for selection with Car/Cdr is E's unification:
|
||
|
||
x <=> <a,b|c>
|
||
a+b+c
|
||
|
||
instead of:
|
||
|
||
Car(x)+Car(Cdr(x))+Cdr(Cdr(x))
|
||
|
||
lisp-cell unification resembles E-list unification (see @{" 4L " link CH_4L }). for
|
||
example:
|
||
|
||
x <=> <1,2|a>
|
||
|
||
equals:
|
||
|
||
IF Car(x)=1
|
||
IF Car(Cdr(x))=2
|
||
a:=Cdr(Cdr(x))
|
||
...
|
||
|
||
|
||
A lisp-nil value is available "<>", which equals "NIL" and "0", but not
|
||
"[]".
|
||
|
||
some functions are available (note that Cons() is _only_ available
|
||
through <...>)
|
||
|
||
h:=Car(c) t:=Cdr(c)
|
||
|
||
get the head and the tail value of a cell c
|
||
|
||
bool:=Cell(c)
|
||
|
||
gives a bool for whether or not c points at a cell, so Cell(<1>)=TRUE,
|
||
and Cell(3.14)=FALSE. This is not a fast function.
|
||
|
||
n:=FreeCells()
|
||
|
||
tell you about the amount of free cells available. very slow function.
|
||
there should be no need to call this function other than curiosity.
|
||
|
||
SetChunkSize(k)
|
||
|
||
Sets the chunksize to allocate for cells to k kilobyte. default is 128k.
|
||
This function can only be called once, and only before the first cons
|
||
(<..>) allocation takes place. Thereafter it has no effect.
|
||
|
||
In general, get a good book about lisp to understand more about
|
||
programming with cells.
|
||
|
||
One can write any LISP-functions in E, with exactly the same functionality:
|
||
|
||
PROC append(x,y) IS IF x THEN <Car(x)|append(Cdr(x),y)> ELSE y
|
||
PROC nrev(x) IS IF x THEN append(nrev(Cdr(x)),<Car(x)>) ELSE NIL
|
||
PROC sum(x) IS IF x THEN Car(x)+sum(Cdr(x)) ELSE 0
|
||
|
||
using a destructive implementation for functions like these is
|
||
also allowed.
|
||
|
||
techy stuff:
|
||
E's garbage collector implements a conservative mark and sweep algorithm
|
||
that was tested to be 5 to 25 times faster than several logical and
|
||
functional language implementations on the Amiga. Conservative
|
||
means that in case of doubt, the GC will not deallocate a cell. this
|
||
is necessary since in a typeless language such as E, the GC can
|
||
easily bump into a value that is not a valid pointer etc.
|
||
|
||
The GC allocates big chunks (default 128k), in which it allocates
|
||
cells. if out of cells, it will collect garbage by scanning the
|
||
stack and registers for pointers into the cellspace, and recursively
|
||
mark them. after that, all unmarked cells are reused, and if the
|
||
gain after a collection was only small, a new chunk is allocated
|
||
(if this fails, "NEW" is raised).
|
||
|
||
interaction with other E values:
|
||
- storing other values in cells is no problem whatsoever. objects,
|
||
strings, floats, anything can be put into a cell without confusing
|
||
the GC too much.
|
||
- storing cells in other values, for example a ptr to a cell in a
|
||
dynamic object, is problematic, since the GC won't be able to find
|
||
it there. a solution for this will be provided. However I think this
|
||
case will seldom occur.
|
||
ptrs to cells can safely be stored in global and local variables,
|
||
even registers, and any stack datastructures.
|
||
[and most importantly, in other cells!]
|
||
|
||
caveats:
|
||
- The GC currently can't collect cells that have a Car-list >1000
|
||
long or so, i.e. <<<NIL:a>:b>:c>, but then 1000 instead of 3
|
||
entries. this will hardly ever occur since lists like this
|
||
are usually formed as Cdr-lists, which the GC can handle into
|
||
infinity. (it will raise "STCK" if this fails")
|
||
- inline assembly code should never push stuff on the stack that
|
||
is not LONG aligned. this was already necessary in v2.1b,
|
||
but now is even more essential.
|
||
|
||
There's a trade off in chunk-size between time and space.
|
||
Allocating small chunks obviously is nice since you won't waste
|
||
any memory, however, when collecting garbage, the effort for
|
||
each pointer to trace is almost proportional to the number of
|
||
spaces. therefore:
|
||
- if speed is most important tune the chunkspacesize such that
|
||
that only one space is needed. if the top cell-memory usage at a
|
||
certain time is 50k, a chunkspace of 100k or 150k will give
|
||
optimal performance.
|
||
- if memory usage is more important, in the example above a
|
||
chunksize of 20k or 30k will be quite optimal for memory.
|
||
In general, time a heavy usage of your cell-algorithm with
|
||
different sizes to see what trade-off suits you best.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_10A
|
||
@title "10A. built-in library calls"
|
||
10A. built-in library calls
|
||
---------------------------
|
||
As you may have noticed from previous sections, the piece of code
|
||
automatically linked to the start of your code, called the "initialisation code",
|
||
always opens the three libraries intuition, dos, graphics and (and sometimes
|
||
mathieeesingbas), and because of this, the compiler has all the calls to those
|
||
four libraries (including exec) integrated in the compiler (there are a few hundred
|
||
of them). These are up to AmigaDos v3.1 (v40). To call Open() from the dos library,
|
||
simply say:
|
||
|
||
handle:=Open('myfile',OLDFILE)
|
||
|
||
or AddDisplayInfo() from the graphics library:
|
||
|
||
AddDisplayInfo(mydispinfo)
|
||
|
||
it's as simple as that.
|
||
|
||
|
||
@endnode
|
||
@node CH_10B
|
||
@title "10B. interfacing to the amiga system with the v40 modules"
|
||
10B. interfacing to the amiga system with the v40 modules
|
||
----------------------------------------------------------
|
||
To use any other library than the five in the previous section, you'll
|
||
need to resort to modules. Also, if you wish to use some OBJECT or CONST
|
||
definition from the Amiga includes as is usual in C or assembler,
|
||
you'll need modules. Modules are binary files which may include constant,
|
||
object, library and function (code) definitions. The fact that they're
|
||
binary has the advantage over ascii (as in C and assembly), that they
|
||
need not be compiled over and over again, each time your program is
|
||
compiled. The disadvantage is that they cannot simply be viewed; they
|
||
need a utility like ShowModule (see @{" 17A " link CH_17A }) to make their contents
|
||
visible. The modules that contain the library definitions (i.e the calls)
|
||
are in the root of emodules: (the modules dir in the distribution), the
|
||
constant/object definitions are in the subdirectories, structured just
|
||
like the originals from Commodore.
|
||
|
||
MODULE
|
||
|
||
syntax: MODULE <modulenames>,...
|
||
|
||
Loads a module. A module is a binary file containing information on libraries,
|
||
constants, and sometimes functions. Using modules enables you to use
|
||
libraries and functions previously unknown to the compiler.
|
||
|
||
Now for an example, below is a short version of the source/examples/asldemo.e
|
||
source that uses modules to put up a filerequester from the 2.0 Asl.library.
|
||
|
||
|
||
MODULE 'Asl', 'libraries/Asl'
|
||
|
||
PROC main()
|
||
DEF req:PTR TO filerequester
|
||
IF aslbase:=OpenLibrary('asl.library',37)
|
||
IF req:=AllocFileRequest()
|
||
IF RequestFile(req) THEN WriteF('File: "\\s" in "\\s"\\n',req.file,req.drawer)
|
||
FreeFileRequest(req)
|
||
ENDIF
|
||
CloseLibrary(aslbase)
|
||
ENDIF
|
||
ENDPROC
|
||
|
||
|
||
From the modules 'asl', the compiler takes asl-function definitions like
|
||
RequestFile(), and the global variable 'aslbase', which only needs to be
|
||
initialised by the programmer (i.e it should not be defined by the
|
||
programmer). From 'libraries/Asl', it takes the definition of the
|
||
filerequester object, which we use to read the file the user picked. Well,
|
||
that wasn't all that hard: did you think it was that easy to program a
|
||
filerequester in E?
|
||
|
||
Note that when objects imported from modules contain references to objects
|
||
from yet other modules, with an object member of type PTR TO <object>, this
|
||
type will only be set correctly if the object in question is already
|
||
available, otherwise it is set to LONG. The only way to influence this is
|
||
rearranging the order of modules in the MODULE statement.
|
||
|
||
|
||
@endnode
|
||
@node CH_10C
|
||
@title "10C. compiling own modules"
|
||
10C. compiling own modules
|
||
--------------------------
|
||
with v3, you can gather all PROCs, CONSTs, OBJECTs and to
|
||
some extent also global variables that you feel somehow belong
|
||
together in one source, write "OPT MODULE" to signal EC that
|
||
this is supposed to be a module, and then compile all to a
|
||
.m file to be used in the main program, just like you used to
|
||
do with the old modules.
|
||
|
||
by default, all elements in a module are PRIVATE, i.e. not accessable
|
||
to the code that imports the .m file. to show which elememts
|
||
you wish to be visible in the module, simply write EXPORT
|
||
before it:
|
||
|
||
EXPORT ENUM TESTING,ONE,TWO,THREE,FOUR
|
||
|
||
EXPORT DEF important_glob_var, bla:PTR TO x
|
||
|
||
EXPORT OBJECT x
|
||
next, index, term
|
||
ENDOBJECT
|
||
|
||
EXPORT PROC burp()
|
||
/* whatever */
|
||
ENDPROC
|
||
|
||
EXPORT lab:
|
||
|
||
"EXPORT" is useful in making a distinction between private and
|
||
public, especially when all functions of an OBJECT can be accessed
|
||
via PROCs, you may wish to keep OBJECT private as an effective
|
||
method of data hiding. more on this topic, (see @{" 14C " link CH_14C })
|
||
[EXPORT can be written on any line, doesn't affect everything though.]
|
||
|
||
If in a module _all_ elememts need to be exported (for example
|
||
one with only constants), a 'OPT EXPORT' will export all, without
|
||
the need for individual EXPORT keywords.
|
||
|
||
global variables require extra attention:
|
||
- try to avoid global variables. having lots of globals
|
||
in modules makes projects messy and error-prone
|
||
- globals in a module cannot have initialisations directly
|
||
in the DEF statement (reason for this will become clear
|
||
below). for example:
|
||
DEF a not DEF a=1
|
||
DEF a:PTR TO x not DEF a[10]:ARRAY OF x
|
||
- globals in a module which are not exported function as
|
||
local for the module, i.e. they'll never clash with globals
|
||
in other modules. those who _are_ exported though, are
|
||
combined with the others, i.e. if in both the main program
|
||
and in a module a variable with the same name are used,
|
||
this will be one and the same variable. that's why
|
||
one can write DEF a[10]:ARRAY OF x in the main program,
|
||
and EXPORT DEF a:PTR TO x in the module, to share the
|
||
array. Also, if both use for example 'gadtools.m',
|
||
only one of the two needs to initialise 'gadtoolsbase'
|
||
for both to be able to make calls to the library.
|
||
If you do not want librarybases to be shared (i.e. you
|
||
want to have a local, private library base), simply
|
||
redeclare it in a DEF that is not EXPORTed in the module.
|
||
If you export a variable in a general purpose
|
||
module, make sure to give it a pretty unique name.
|
||
variables are combined in the main program, if you want to
|
||
reference an exported variable from another module,
|
||
you will have to export it there too.
|
||
- using globals in modules which provide general purpose
|
||
datatypes needs special attention, as the module may be
|
||
in use from more than one other module, in which case
|
||
it may be unclear who is responsable for resources.
|
||
take good care of this.
|
||
|
||
Using modules in modules
|
||
|
||
This requires little extra attention. If the module (B) you include
|
||
in your own module (A) is one that only declares CONSTs, LIBRARYs and
|
||
OBJECTs (without code) nothing special happens, however if B includes
|
||
PROCs, then it's obvious this code needs to be linked later to the
|
||
main program when A is linked. Therefore if a main program uses A,
|
||
B will need to be present for compilation. The fact that A needs
|
||
B is stored in A, and can be viewed with ShowModule. This chain
|
||
of uses may grow out to a tree of dependencies, which has the result
|
||
that if you use just one module in your program, a lot of others
|
||
are automatically linked to it. Thus, E's module system automatically
|
||
keeps track of dependancies that other languages need makefiles for.
|
||
EC also allows for circular inclusions, and loads/links modules at most
|
||
once (i.e. doesn't link unused modules). One thing E's module system
|
||
doesn't automatically do for you is recompile dependant modules. If
|
||
you change B, it is often necessary to recompile A too, since it
|
||
might be referring to offsets etc. in the old version of B, which
|
||
might cause code to crash. If this gets too complex in your project,
|
||
use a utility such as E-Build (see @{" 17I " link CH_17I }).
|
||
|
||
|
||
Try out the new ShowModule (see @{" 17A " link CH_17A }) to see what EC puts in modules.
|
||
|
||
|
||
Including modules from other directories.
|
||
|
||
By default, a module name is prefixed by 'emodules:' to obtain
|
||
the actual file. Now you can prefix the name with a '*' to
|
||
denote the directory the source is in, so:
|
||
|
||
MODULE 'bla', '*bla'
|
||
|
||
if this statement would be in source 'WORK:E/burp.e', these two
|
||
modules would be 'emodules:bla.m' and 'WORK:E/bla.m'.
|
||
|
||
This is naturally the way to include components of your app into
|
||
other parts. If you write modules that you use in many of your programs
|
||
it would be handy to store them in the emodules hierarchy, and the
|
||
place for this is the 'emodules:other/' dir.
|
||
|
||
|
||
Circular dependancies.
|
||
|
||
When writing sets of own modules, one should try to avoid
|
||
circular dependancies (EC does not enforce this), as in some
|
||
cases this can result in chicken and egg problems (you can't
|
||
recompile everything from scratch, you always need atleast
|
||
one readily compiled module).
|
||
|
||
|
||
@endnode
|
||
@node CH_10D
|
||
@title "10D. the modulecache"
|
||
10D. the modulecache
|
||
--------------------
|
||
(see @{" 17D " link CH_17D }, ShowCache/FlushCache about this).
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_11A
|
||
@title "11A. quoting and scope"
|
||
11A. quoting and scope
|
||
----------------------
|
||
Quoted expressions start with a backquote. The value of a quoted
|
||
expression is not the result from the computation of the expression,
|
||
but the address of the code. This result may then be passed on as
|
||
a normal variable, or as an argument to certain functions.
|
||
example:
|
||
|
||
myfunc:=`x*x*x
|
||
|
||
myfunc is now a pointer to a `function' that computes x^3 when evaluated.
|
||
These pointers to functions are very different from normal PROCs, and
|
||
you should never mix the two up. The biggest differences are that a
|
||
quoted expression is just a simple expression, and thus cannot have its
|
||
own local variables. In our example, "x" is just a local or global variable.
|
||
That's where we have to be cautious:
|
||
if we evaluate myfunc somewhat later in the same PROC, x may be local,
|
||
but if myfunc is given as parameter to another PROC, and then evaluated,
|
||
x needs of course to be global. There's no scope checking on this.
|
||
|
||
@endnode
|
||
@node CH_11B
|
||
@title "11B. Eval()"
|
||
11B. Eval()
|
||
-----------
|
||
Eval(func)
|
||
|
||
simply evaluates a quoted expression (exp = Eval(`exp)).
|
||
|
||
NOTE: because E is a somewhat typeless language, accidentally writing
|
||
"Eval(x*x)" instead of "Eval(`x*x)" will go unnoticed by the
|
||
compiler, and will give you big runtime problems: the value of x*x
|
||
will be used as a pointer to code.
|
||
|
||
To understand why 'quoted expressions' is a powerful feature think of the
|
||
following cases: if you were to perform a set of actions on a set of different
|
||
variables, you'd normally write a function, and call that function with
|
||
different arguments. But what happens when the element that you want to give
|
||
as argument is a piece of code? in traditional languages this would not be
|
||
possible, so you would have to 'copy' the blocks of code representing your
|
||
function, and put the expression in it. Not in E. say you wanted to write
|
||
a program that times the execution time of different expressions. In E you
|
||
would simply write:
|
||
|
||
PROC timing(func,title)
|
||
/* do all sorts of things to initialise time */
|
||
Eval(func)
|
||
/* and the rest */
|
||
WriteF('time measured for \\s was \\d\\n',title,t)
|
||
ENDPROC
|
||
|
||
and then call it with:
|
||
|
||
timing(`x*x*x,'multiplication')
|
||
timing(`sizycalc(),'large calculation')
|
||
|
||
|
||
in any other imperative language, you would have to write out
|
||
copies of timing() for every call to it, or you would have to
|
||
put each expression in a separate function. This is just a simple
|
||
example: think about what you could do with data structures (LISTs)
|
||
filled with unevaluated code:
|
||
|
||
drawfuncs:=[`Plot(x,y,c),`Line(x,y,x+10,y+10,c),`Box(x,y,x+20,y+20,c)]
|
||
|
||
Note that this idea of functions as normal variables/values is not new
|
||
in E, quoted expressions are literally from LISP, which also has the
|
||
somewhat more powerful so-called Lambda function, which can also be
|
||
given as argument to functions; E's quoted expressions can also be
|
||
seen as parameterless (or global parameter only) lambdas.
|
||
|
||
|
||
@endnode
|
||
@node CH_11C
|
||
@title "11C. built-in functions"
|
||
11C. built-in functions
|
||
-----------------------
|
||
|
||
MapList(varadr,list,listvar,func)
|
||
|
||
performs some function on all elements of list and returns all
|
||
results in listvar. func must be a quoted expression (see @{" 11A " link CH_11A }),
|
||
and var (which ranges over the list) must be given by reference. Example:
|
||
|
||
MapList({x},[1,2,3,4,5],r,`x*x) results r in: [1,4,9,16,25]
|
||
|
||
returns listvar.
|
||
|
||
ForAll(varadr,list,func)
|
||
|
||
Returns TRUE if for all elements in the list the function (quoted
|
||
expression) evaluates to TRUE, else FALSE. May also be used to perform
|
||
a certain function for all elements of a list:
|
||
|
||
ForAll({x},['one','two','three'],`WriteF('example: \\s\\n',x))
|
||
|
||
Exists(varadr,list,func)
|
||
|
||
As ForAll(), only this one returns TRUE if for any element the function
|
||
evaluates to TRUE (<>0). note that ForAll() always evaluates all elements,
|
||
but Exists() possibly does not.
|
||
|
||
SelectList(v,list,listvar,quotedexp)
|
||
|
||
Much like MapList(), only now doesn't store the result from quotedexp,
|
||
it uses it as a boolean value, and only those values for which it is
|
||
true are stored in listvar (which should be capable of holding
|
||
the same amount of elements as list. example:
|
||
|
||
SelectList({x},[1,2,0,3,NIl],r,`x<>0)
|
||
|
||
results in r being [1,2,3].
|
||
returns length of listvar.
|
||
|
||
Example of how to use these functions in a practical fashion:
|
||
we allocate different sizes of memory in one statement, check them
|
||
all together at once, and free them all, but still only those that
|
||
succeeded. (example is v37+)
|
||
|
||
|
||
PROC main()
|
||
DEF mem[4]:LIST,x
|
||
MapList({x},[200,80,10,2500],mem,`AllocVec(x,0)) -> alloc some
|
||
WriteF(IF ForAll({x},mem,`x) THEN 'Yes!\\n' ELSE 'No!\\n') -> suxxes ?
|
||
ForAll({x},mem,`IF x THEN FreeVec(x) ELSE NIL) -> free only those <>NIL
|
||
ENDPROC
|
||
|
||
|
||
Note the absence of iteration in this code. Just try to rewrite this
|
||
example in any other language to see why this is special.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_12A
|
||
@title "12A. float values"
|
||
12A. float values
|
||
-----------------
|
||
REALs (or FLOATs, whatever) are very different in E than in other
|
||
languages. This mainly has to do with the fact that E doesn't
|
||
really discriminate between types of values. One is advised to
|
||
understand these chapters _well_ before attempting to use floats.
|
||
|
||
In E, a float is just another 32bit value. The E compiler treats
|
||
them just like integers or pointers, with the difference that
|
||
their bit representation means something different. The E float
|
||
format is the IEEEsingle standard.
|
||
|
||
A float value looks like an integer value with the exception that
|
||
somewhere a "." is present. for example, the following are valid
|
||
floats:
|
||
|
||
3.14159 .1 1. -12345.6
|
||
|
||
these aren't:
|
||
|
||
. 1234
|
||
|
||
(i.e. atleast one "." and one "0-9" char must be present).
|
||
|
||
You can use these values at almost all places where LONG values
|
||
are legal, i.e. if you have have a function or datastructure that
|
||
handles arbitrary LONG values, it will also handle floats.
|
||
|
||
DEF f=3.14
|
||
myobj.x:=.1
|
||
fun(f,2.73)
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_12B
|
||
@title "12B. computing with floats"
|
||
12B. computing with floats
|
||
--------------------------
|
||
Because to E a float will seem like just another LONG, it will happily
|
||
apply integer math to it when used in an expression, which is mostly
|
||
not what you want. Also, one would like to be able to convert to
|
||
integer and vice-versa. The float operator "!" handles all this.
|
||
|
||
assume in the following examples that a,b,c contain integer values,
|
||
and x,y,z float values.
|
||
|
||
By default, an expression in E is considered an integer expression.
|
||
what the "!" does when it occurs in an expression is the following:
|
||
- changes the expression from int to float. any operators following
|
||
(+ * - / = <> > < >= <=) will be float operations. "!" may occur
|
||
any number of times in an expression, changing from and to float
|
||
again and again.
|
||
- the expression that did occur before the "!", if any, is converted
|
||
to the appropriate type.
|
||
|
||
examples:
|
||
|
||
x:=a!
|
||
|
||
converts "a" to float, and stores the result in x. "a" is an integer exp,
|
||
which is then toggled to float, which implies a conversion.
|
||
|
||
a:=!x!
|
||
|
||
converts "x" to integer and stores the result in a.
|
||
|
||
x:=y
|
||
|
||
x:=Ftan(y)
|
||
|
||
no "!" is needed here since no operator-math or conversions are necessary.
|
||
|
||
x:=!y*z
|
||
|
||
the "*" acts on y and z as floats, since "!" denotes the whole as
|
||
a float-exp. the float result is stored in x
|
||
|
||
a:=b!*x+y!
|
||
|
||
a more complex example: the int "b" is converted to float, then x
|
||
and y are float-multiplied and float-added to it. The result is converted
|
||
to int and stored in the int "a"
|
||
|
||
x:=!y*z-z*y+(a!)+z/z
|
||
|
||
z:=!x*Fsin(!x*y)
|
||
|
||
all (+ * - /) are computed as float, and the int "a" is converted
|
||
to float somewhere in the middle. since "(" ")" denotes a new expression,
|
||
it has it's own status of "!". Same idea for the function below.
|
||
|
||
IF !x<0.1 THEN WriteF('Float value too small!\\n')
|
||
|
||
as you can see, "!" also works on the six comparison operators.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_12C
|
||
@title "12C. builtin float functions"
|
||
12C. builtin float functions
|
||
----------------------------
|
||
|
||
Some trans-math functions are present, more will probably follow.
|
||
|
||
x:=Fsin(y) x:=Fcos(y) x:=Ftan(y)
|
||
|
||
usual sin() etc. functions. they work with radians.
|
||
New in v3.3 are Fatan() Fasin() Facos() Fsincos()
|
||
Fsinh() Fcosh() and Ftanh() which work analogously to
|
||
the functions above. Ftieee() and Ffieee() convert FFP
|
||
floats to and from ieee floats (as used by E).
|
||
|
||
x:=Fabs(y)
|
||
|
||
compute absolute value of y
|
||
|
||
x:=Ffloor(y) x:=Fceil(y)
|
||
|
||
compute lowest and highest whole-number float value near y
|
||
|
||
x:=Fexp(y) x:=Flog(y) Flog10(y) x:=Fpow(y,z) Fsqrt(y)
|
||
|
||
compute e^y, ln(y), log base 10, z^y and square root of y, respectively.
|
||
|
||
x,n:=RealVal(s)
|
||
|
||
parses string "s" to produce float value x. will skip leading spaces and tabs.
|
||
n is the number of characters parsed from the start of the string, or 0
|
||
if it couldn't be parsed as a float value. "x" will then be 0.0.
|
||
accepts negative numbers (and number without a ".", even).
|
||
example:
|
||
|
||
RealVal(' 3.14 ') results in 3.14, 5
|
||
RealVal('blabla') results in 0.0, 0
|
||
|
||
s:=RealF(s,x,n)
|
||
|
||
Format a float value x to the estring s, with n positions after the ".".
|
||
max for "n" is 8, even less if you have lots of digits leading the ".".
|
||
an "n" of 0 will denote no fraction. The string is returned as result,
|
||
so it can be reused in a WriteF() for example:
|
||
|
||
WriteF('float = \\s\\n',RealF(s,3.14159),4) results in 'float = 3.1416\\n'
|
||
|
||
RealF() tries hard to make sensible roundings for a certain "n", as
|
||
the example shows. negative numbers are also handled properly.
|
||
|
||
RealF(s,-3.14159,0) results in '-3'
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_12D
|
||
@title "12D. float implementation issues"
|
||
12D. float implementation issues
|
||
--------------------------------
|
||
As said before, the E float format is IEEE (single), this means that
|
||
older float code using the FFP format with the SpXxx functions will
|
||
have to be rewritten (as stated in the v2.1b docs). The mathffp
|
||
library is no longer directly supported by EC starting from v3.0, and
|
||
you'll have to open this library like all others if you want to use it.
|
||
|
||
single IEEE's were chosen because:
|
||
- double IEEE's don't fit in a LONG
|
||
- the FFP format routines do not make use of a 68881 if present,
|
||
the IEEE ones do. Furthermore the FFP format is incompatible
|
||
with the 68881, which also uses IEEE format.
|
||
- IEEE is the worldwide float-format standard, which encourages
|
||
data-file compatability among software/platforms.
|
||
|
||
E's float routines use the mathieeesingbas.library and the
|
||
mathieeesingtrans.library, which are not by default supplied
|
||
with the ancient v1.3 of the OS. This means that if you want
|
||
to write under / support 1.3 AND you want to use the _builtin_
|
||
floats, you have to make sure these libraries are present (they
|
||
seem to be available, maybe through commodore?).
|
||
|
||
Both EC and the programs it generates do not open these libraries
|
||
as long as no float-features are used.
|
||
|
||
If all else fails, one can always use other floatlibraries to use
|
||
floats with 1.3. I might recommend the tools/longreal.m module
|
||
which uses doubles.
|
||
|
||
In the future, EC will probably allow mathieee library calls to
|
||
be replaced with inline 68881 code transparently.
|
||
|
||
|
||
|
||
The bug in mathieeesingbas.library.
|
||
|
||
v3.1 (v40) of the amiga operating system is known to contain a bug in the IEEE
|
||
code of the divide and multiply functions. If you're using E floats under
|
||
this version of the OS you will need to run a patch that fixes this otherwise
|
||
you may provoke crashes. Even worse, even if you program under another
|
||
version of the OS, if your program uses these float operations, you will have
|
||
to notify your users to use this patch, otherwise your programs may fail on
|
||
other peoples machines. I've included SetPatch v43.6 in this distribution
|
||
which fixes the problem (Bin/SetPatch/). Another suitable patch is for
|
||
example util/boot/PatchMathSB10.lha on Aminet (or CD #7). As far as EC
|
||
itself is concerned, if EC has to use one of these operations (for example
|
||
for float constants) it will warn you that you are not running the patch.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_13A
|
||
@title "13A. defining exception handlers (HANDLE/EXCEPT)"
|
||
13A. defining exception handlers (HANDLE/EXCEPT)
|
||
------------------------------------------------
|
||
The exception mechanism in E is basically the same as in ADA; it
|
||
provides for flexible reaction on errors in your program and
|
||
complex resource management. NOTE: the term 'exception' in E has
|
||
very little to do with exceptions caused directly by 680x0 processors.
|
||
|
||
An exception handler is a piece of program code that will be invoked
|
||
when runtime errors occurs, such as windows that fail to open or
|
||
memory that is not available. You, or the runtime system itself,
|
||
may signal that something is wrong (this is called "raising an
|
||
exception"), and then the runtime-system will try and find the
|
||
appropriate exception handler. I say "appropriate" because a program
|
||
can have more than one exception handler, on all levels of a program.
|
||
A normal function definition may (as we all know) look like this:
|
||
|
||
PROC bla()
|
||
/* ... */
|
||
ENDPROC
|
||
|
||
a function with an exception handler looks like this:
|
||
|
||
PROC bla() HANDLE
|
||
/* ... */
|
||
EXCEPT
|
||
/* ... */
|
||
ENDPROC
|
||
|
||
The block between PROC and EXCEPT is executed as normal, and if no
|
||
exception occurs, the block between EXCEPT and ENDPROC is skipped, and
|
||
the procedure is left at ENDPROC. If an exception is raised, either
|
||
in the PROC part, or in any function that is called in this block,
|
||
an exception handler is invoked.
|
||
|
||
@endnode
|
||
@node CH_13B
|
||
@title "13B. using the Raise() function"
|
||
13B. using the Raise() function
|
||
-------------------------------
|
||
There are many ways to actually "raise" an exception, the simplest
|
||
is through the function Raise():
|
||
|
||
Raise(exceptionID=0)
|
||
|
||
the exception ID is simply a constant that defines the type of
|
||
exception, and is used by handlers to determine what went wrong.
|
||
Example:
|
||
|
||
ENUM NOMEM,NOFILE /* and others */
|
||
|
||
PROC bla() HANDLE
|
||
DEF mem
|
||
IF (mem:=New(10))=NIL THEN Raise(NOMEM)
|
||
myfunc()
|
||
EXCEPT
|
||
SELECT exception
|
||
CASE NOMEM
|
||
WriteF('No memory!\\n')
|
||
/* ... and others */
|
||
ENDSELECT
|
||
ENDPROC
|
||
|
||
PROC myfunc()
|
||
DEF mem
|
||
IF (mem:=New(10))=NIL THEN Raise(NOMEM)
|
||
ENDPROC
|
||
|
||
The "exception" variable in the handler always contains the value of
|
||
the argument to the Raise() call that invoked it.
|
||
In both New() cases, the Raise() function invokes the handler of
|
||
function bla(), and then exits it correctly to the caller of bla().
|
||
If myfunc() had its own exception-handler, that one would be invoked
|
||
for the New() call in myfunc(). The scope of a handler is from the start
|
||
of the PROC in which it is defined until the EXCEPT keyword, including
|
||
all calls made from there.
|
||
|
||
This has three consequences:
|
||
A. handlers are organized in a recursive fashion, and which handler is
|
||
actually invoked is dependant on which function calls which at runtime;
|
||
B. if an exception is raised within a handler, the handler of a lower
|
||
level is invoked. This characteristic of handlers may be used
|
||
to implement complex recursive resource allocation schemes with
|
||
great ease, as we'll see shortly.
|
||
C. If an exception is raised on a level where no lower-level handler
|
||
is available (or in a program that hasn't got any handlers at all),
|
||
the program is terminated. (i.e: Raise(x) has the same effect as
|
||
CleanUp(0))
|
||
|
||
other functions:
|
||
|
||
Throw(exceptionID,value)
|
||
|
||
same as Raise(), only now it takes an arbitrary value with it. in
|
||
a handler one can then scrutinize this value with the variable
|
||
'exceptioninfo'
|
||
|
||
ReThrow()
|
||
|
||
has no args. simply does a Throw() on the current exception value,
|
||
IFF it is <>0.
|
||
|
||
|
||
@endnode
|
||
@node CH_13C
|
||
@title "13C. defining exceptions for built-in functions (RAISE/IF)"
|
||
13C. defining exceptions for built-in functions (RAISE/IF)
|
||
---------------------------------------------------------
|
||
With exceptions like before, we have made a major gain over the
|
||
old way of defining our own "error()" function, but still it is
|
||
a lot of typing to have to check for NIL with every call to New().
|
||
|
||
The E exception handling system allows for definition of exceptions
|
||
for all E functions (like New(), OpenW() etc.), and for all Library
|
||
functions (OpenLibrary(), AllocMem() etc.), even for those
|
||
included by modules. Syntax:
|
||
|
||
RAISE <exceptionId> IF <func> <comp> <value> , ...
|
||
|
||
the part after RAISE may be repeated with a ",".
|
||
Example:
|
||
|
||
RAISE NOMEM IF New()=NIL,
|
||
NOLIBRARY IF OpenLibrary()=NIL
|
||
|
||
the first line says something like: "whenever a call to New() results
|
||
in NIL, automatically raise the NOMEM exception".
|
||
<comp> may be any of = <> > < >= <=
|
||
After this definition, we may write all through our programs:
|
||
|
||
mem:=New(size)
|
||
|
||
without having to write:
|
||
|
||
IF mem=NIL THEN Raise(NOMEM)
|
||
|
||
Note that the only difference is that 'mem' never gets any value
|
||
if the runtime system invokes the handler: code is generated for
|
||
every call to New() to check directly after New() returns and call
|
||
Raise() when necessary.
|
||
|
||
We'll now be implementing a small example that would be complex to solve
|
||
without exception handling: we call a function recursively, and in each
|
||
we allocate a resource (in this case memory), which we allocate before,
|
||
and release after the recursive call. What happens when somewhere high
|
||
in the recursion a severe error occurs, and we have to leave the program?
|
||
right: we would (in a conventional language) be unable to free all the
|
||
resources lower in the recursion while leaving the program, because all
|
||
pointers to those memory areas are stored in unreachable local variables.
|
||
In E, we can simply raise an exception, and from the end of the handler
|
||
again raise an exception, thus recursively calling all handlers and
|
||
releasing all resources. Example:
|
||
|
||
|
||
CONST SIZE=100000
|
||
ENUM NOMEM /* ,... */
|
||
|
||
RAISE NOMEM IF AllocMem()=NIL
|
||
|
||
PROC main()
|
||
alloc()
|
||
ENDPROC
|
||
|
||
PROC alloc() HANDLE
|
||
DEF mem
|
||
mem:=AllocMem(SIZE,0) /* see how many blocks we can get */
|
||
alloc() /* do recursion */
|
||
EXCEPT DO
|
||
IF mem THEN FreeMem(mem,SIZE)
|
||
ReThrow() /* recursively call all handlers */
|
||
ENDPROC
|
||
|
||
|
||
This is of course a simulation of a natural programming problem that
|
||
is usually far more complex, and thus the need for exception handling
|
||
becomes far more obvious. For a real-life example program whose error
|
||
handling would have become very difficult without exception
|
||
handlers, see the 'D.e' utility source.
|
||
|
||
The "DO" after an EXCEPT means that instead of jumping to ENDPROC,
|
||
the main code will simply continue execution in the handler as soon
|
||
as it gets there. it also sets exception to 0.
|
||
This is handy if you free resources local to a PROC in the handler.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_13D
|
||
@title "13D. use of exception-ID's"
|
||
13D. use of exception-ID's
|
||
--------------------------
|
||
In real life an exception-ID is of course a normal 32-bit value,
|
||
and you may pass just about anything to an exception handler: for
|
||
example, some use it to pass error-description strings
|
||
|
||
Raise('Could not open "gadtools.library"!')
|
||
|
||
However, if you want to use exceptions in expandabele fashion and you
|
||
want to be able to use future modules that raise exceptions not defined
|
||
by your program, follow the following guidelines:
|
||
|
||
- Use and define ID 0 as "no error" (i.e. normal termination)
|
||
|
||
- For exceptions specific to your program, use the ID's 1-10000.
|
||
Define these in the usual fashion with ENUM:
|
||
|
||
ENUM OK,NOMEM,NOFILE,...
|
||
|
||
(OK will be 0, and others will be 1+)
|
||
|
||
- ID's 12336 to 2054847098 (these are all identifiers
|
||
consisting of upper/lowercase letters and digits of length 2,3 or 4
|
||
enclosed in "") are reserved as common exceptions. A common exception
|
||
is an exception that need not be defined in your program, and that
|
||
may be used by implementors of modules (with functions in them) to
|
||
raise exceptions: for example, if you design a set of procedures that
|
||
perform a certain task, you may want to raise exceptions. As you would
|
||
want to use those functions in various programs, it would be
|
||
unpractical to have to coordinate the IDs with the main program,
|
||
furthermore, if you use more than one set of functions (in a module,
|
||
in the future) and every module would have a different ID for
|
||
'no memory!', things could get out of hand.
|
||
This is where common exceptions come in: the common out-of-memory
|
||
ID is "MEM" (including the quotes): any implementor can now simply
|
||
|
||
Raise("MEM")
|
||
|
||
from all different procedures, and the programmer that uses the module
|
||
only needs to suply an exception handler that understands "MEM"
|
||
|
||
future modules that contain sets of functions will specify what
|
||
exception a certain procedure may raise, and if these overlap
|
||
with the IDs of other procedures, the task of the programmer
|
||
that has to deal with the exceptions will be greatly simplified.
|
||
|
||
examples:
|
||
|
||
(system)
|
||
|
||
"MEM" out of memory
|
||
"FLOW" (nearly) stack overflow
|
||
"STCK" garbage collector has stack problems
|
||
"^C" Control-C break
|
||
"ARGS" bad args
|
||
|
||
(exec/libraries)
|
||
|
||
"SIG" could not allocate signal
|
||
"PORT" could not create messageport
|
||
"LIB" library not available
|
||
"ASL" no asl.library
|
||
"UTIL" no utility.library
|
||
"LOC" no locale.library
|
||
"REQ" no req.library
|
||
"RT" no reqtools.library
|
||
"GT" no gadtools.library (similar for others)
|
||
|
||
(intuition/gadtools/asl/gfx)
|
||
|
||
"WIN" failed to open window
|
||
"SCR" failed to open screen
|
||
"REQ" could not open requester
|
||
"FREQ" could not open filerequester
|
||
"GAD" could not create gadget
|
||
"MENU" could not create menu(s)
|
||
"FONT" problem getting font
|
||
|
||
(dos)
|
||
|
||
"OPEN" could not open a file / file does not exist
|
||
"OUT" problems while writing
|
||
"IN" problems while reading
|
||
"EOF" unexpected end of file
|
||
"FORM" input format error
|
||
"SEG" loadseg problems
|
||
|
||
The general tendency is:
|
||
* all uppercase for general system exceptions,
|
||
* mixed case for exceptions used by >1 app, but not general enough.
|
||
* all lowercase for exceptions raised within your own
|
||
multi-module application
|
||
|
||
- all others (including all negative IDs) remain reserved.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_14A
|
||
@title "14A. OO features in E"
|
||
14A. OO features in E
|
||
---------------------
|
||
The features descibed here in this chapter are grouped as such
|
||
since they constitute what is generally seen as the three essential
|
||
main components that make a language 'Object Oriented' (i.e.
|
||
inheritance - data hiding - polymorhism). However in E they are
|
||
by no means a 'separate chapter' since each can be used in any
|
||
way with other E features.
|
||
|
||
|
||
@endnode
|
||
@node CH_14B
|
||
@title "14B. object inheritance"
|
||
14B. object inheritance
|
||
-----------------------
|
||
it's always annoying not being able to express dependencies between
|
||
OBJECTs, or reuse code that works on a particular OBJECT with a bigger
|
||
OBJECT that encapsulates the first. Object Inheritance allows you
|
||
to do just that in E. when you have an object a:
|
||
|
||
OBJECT a
|
||
next, index, term
|
||
ENDOBJECT
|
||
|
||
you can make a new object b that has the same properties
|
||
as a (and is compatible with code for a):
|
||
|
||
OBJECT b OF a
|
||
bla, x, burp
|
||
ENDOBJECT
|
||
|
||
is equivalent to:
|
||
|
||
OBJECT b
|
||
next, index, term /* from a */
|
||
bla, x, burp
|
||
ENDOBJECT
|
||
|
||
with DEF p:b, you can directly not only access p.bla as usual,
|
||
but also p.next.
|
||
|
||
as an example, if one would have a module with an OBJECT to
|
||
implement a certain datatype (for example a doubly-linked-list),
|
||
and PROCs to support it, one could simply inherit from it, adding
|
||
own data to the object, and use the _existing_ functions to
|
||
manipulate the list. However, it's only in combination with
|
||
methods (descibed below), inheritance can show its real power.
|
||
|
||
|
||
@endnode
|
||
@node CH_14C
|
||
@title "14C. data hiding (EXPORT/PRIVATE/PUBLIC)"
|
||
14C. data hiding (EXPORT/PRIVATE/PUBLIC)
|
||
----------------------------------------
|
||
E has a very handy data-hiding mechanism. Other languages, like C++,
|
||
use data-hiding on classes, which raises the need for kludges (like
|
||
'friends'), and makes datahiding insecure (Eiffel). E's datahiding
|
||
works on the module-level, which can model class-level datahiding,
|
||
but enables more intelligent schemes also.
|
||
|
||
PRIVATE and PUBLIC let you declare a section of an object as visible
|
||
to the outer world or not; the outer world here is all code outside
|
||
the module. for the code within a module, everything is always visible.
|
||
example:
|
||
|
||
|
||
OBJECT mydata PRIVATE -> whole object is private
|
||
bla:PTR TO mydata, burp, grrr:INT
|
||
ENDOBJECT
|
||
|
||
OBJECT aaargh
|
||
blerk:PTR TO aaargh -> public
|
||
PRIVATE
|
||
x:INT, y:INT, z:INT -> private
|
||
PUBLIC
|
||
hmpf[10]:ARRAY OF mydata -> public again
|
||
ENDOBJECT
|
||
|
||
|
||
an object is by default public, an occurring PRIVATE or PUBLIC
|
||
acts as a toggle-switch to the objects current visibility. In the
|
||
first object, all is private. The second object has only (x,y,z)
|
||
as private. PRIVATE and PUBLIC keywords may occur:
|
||
- in the object header line
|
||
- as a line on itself in the object-def
|
||
- preceding decls in an object-def
|
||
(i.e virtually anywhere)
|
||
|
||
Why datahiding?
|
||
If you want to know why datahiding is a good technique, you'd
|
||
probably want to read a good book on OO. But in short: it is
|
||
generally assumed that lots of problems in maintaining and
|
||
enhancing large pieces of software is the fact that it's hard to change
|
||
things because lots of code start to depend on certain structures
|
||
in your program. if you datahide an object, only the code within a
|
||
module will rely on the format of objects, and you can easily change
|
||
both representation of an object (for example changing a stack
|
||
implementation from ARRAY to a linked list) and the code that
|
||
works with it. If a lot of code of a large app depend on the fact
|
||
that the stack is an ARRAY, you won't be able to simply change
|
||
it, which will lead to kludges. In general, try to datahide as
|
||
much as possible without becoming too restrictive on the use
|
||
of your object. Using methods (below) will often enable you to
|
||
keep the whole object private.
|
||
|
||
|
||
@endnode
|
||
@node CH_14D
|
||
@title "14D. methods and virtual methods"
|
||
14D. methods and virtual methods
|
||
--------------------------------
|
||
A method is much like a PROC, only now it's part of an OBJECT. It
|
||
also allows you to exploit Polymorhism on objects, as we'll see
|
||
below. definition of a method:
|
||
|
||
|
||
OBJECT blerk PRIVATE
|
||
x:PTR TO blerk, y:INT, z
|
||
ENDOBJECT
|
||
|
||
PROC getx() OF blerk IS self.x
|
||
|
||
|
||
the 'OF blerk' part tells the compiler it belongs to the object
|
||
'blerk'. Note that apart from the 'OF' the syntax is completely
|
||
like a 'PROC', however, it can't be invoked as one, and also
|
||
functions quite differently.
|
||
|
||
'self' is a local variable that is available in every method, and
|
||
is a pointer to the object that the method belongs to (in this
|
||
case 'self:PTR TO blerk'). This function just returns the value
|
||
of the x field of blerk, which actually makes sense, given that
|
||
this allows you to later change whatever x represents.
|
||
|
||
we may call methods similar to object selections ".":
|
||
|
||
|
||
DEF a:PTR TO blerk
|
||
NEW a
|
||
...
|
||
a.getx() -> invoke method getx() on object a
|
||
|
||
|
||
in this example, upon invocation `a' becomes the value of `self'
|
||
during the execution of getx().
|
||
|
||
so far the use of methods has been nice, but hasn't show us its
|
||
real power, which only comes when used with inheritance.
|
||
|
||
If I inherit an object that has methods, I automatically get
|
||
those in the new object:
|
||
|
||
|
||
OBJECT burp OF blerk PRIVATE -> same as blerk, + extra field
|
||
prut:INT
|
||
ENDOBJECT
|
||
|
||
DEF b:PTR TO burp
|
||
NEW b
|
||
...
|
||
b.getx() -> same method
|
||
|
||
|
||
The interesting thing is now, that instead of inheriting a method,
|
||
you may also redefine it:
|
||
|
||
|
||
PROC getx() OF burp IS self.x+1
|
||
|
||
|
||
(it goes without saying that we may also add new methods)
|
||
so where appropriate, we can choose to modify slightly the
|
||
behaviour of methods we get from other objects, while the
|
||
interface to it (i.e. 'getx()') stays the same. Not only
|
||
does this allow us to reuse code selectively, we can also make use
|
||
of polymorhism:
|
||
|
||
|
||
PROC dosomething(o:PTR TO blerk)
|
||
...
|
||
o.getx()
|
||
...
|
||
ENDPROC
|
||
|
||
dosomething(a)
|
||
dosomething(b)
|
||
|
||
|
||
we may call that PROC with both a and b, since both are compatible
|
||
with a blerk object. But which of the two method implementations of
|
||
getx() is invoked at o.getx()? Answer: both are. Method calls in E
|
||
are what virtual method calls are in other languages: they dynamically
|
||
act on the real type of an object (o) and call the appropriate method.
|
||
|
||
A more clear example:
|
||
|
||
|
||
-> classical OO polymorphic example
|
||
|
||
OBJECT loc
|
||
PRIVATE xpos:INT, ypos:INT
|
||
ENDOBJECT
|
||
|
||
OBJECT point OF loc
|
||
PRIVATE colour:INT
|
||
ENDOBJECT
|
||
|
||
OBJECT circle OF point
|
||
PRIVATE radius:INT
|
||
ENDOBJECT
|
||
|
||
PROC show() OF loc IS WriteF('I''m a Location!\\n')
|
||
PROC show() OF point IS WriteF('I''m a Point!\\n')
|
||
PROC show() OF circle IS WriteF('I''m a Circle!\\n')
|
||
|
||
PROC main()
|
||
DEF x:PTR TO loc,l:PTR TO loc,p:PTR TO point,c:PTR TO circle
|
||
ForAll({x},[NEW l,NEW p,NEW c],`x.show())
|
||
ENDPROC
|
||
|
||
|
||
In the above, x is a PTR TO loc, so many would expect x.show() to
|
||
write 'I'm a Location' three times, but instead it writes the
|
||
right string for each object.
|
||
|
||
If one would write this example in a non-OO language, one would need
|
||
a SELECT for every operation like show(), testing some value
|
||
present in the object to see what it is. If I would add a new shape
|
||
to this, say:
|
||
|
||
OBJECT ellipse OF circle
|
||
|
||
I would need to change all SELECTs throughout my app to account for
|
||
it. With object-polymorhism, I can just write a show() method, and
|
||
ALL code throughout my app that calls x.show() will act correctly
|
||
when x is a PTR TO ellipse, even without recompilation!
|
||
|
||
|
||
It's difficult to show why this is powerful in a few examples, and
|
||
best to discover this is using it in real life apps. and: like I said,
|
||
read a book on it.
|
||
|
||
|
||
How does polymorphism work?
|
||
In the above examples, it's clear the compiler can't know
|
||
what method it's going to call. that's why E uses a 'class
|
||
object', and every object created gets a ptr to this object.
|
||
In the class object, all information is stored that is common
|
||
for all objects of that type, such as pointers to methods.
|
||
when the E compiler sees a call like x.show(), instead of
|
||
looking directly at the show() that belongs to the type of
|
||
x (i.e. loc), it will generate code to retrieve the pointer
|
||
to the show() method from loc's class object. since the
|
||
class object for point looks the same as loc (only maybe
|
||
slightly larger), that code will automatically call point's
|
||
show(), when x is really a point object. This is sometimes
|
||
called runtime binding.
|
||
|
||
Objects that have methods therefore always are 4 bytes larger
|
||
than you expect them to be, since they contain a class object
|
||
pointer. This pointer is automatically installed by NEW, which
|
||
is the reason _currently_ NEW is the only way to create such
|
||
an object (!).
|
||
|
||
If a method is declared with the sole purpose of enabling
|
||
subclasses to redefine it (this type of class is known as
|
||
a virtual baseclass in some languages), one may use EMPTY:
|
||
|
||
PROC bla() OF obj IS EMPTY
|
||
|
||
it may then be redefined in subclasses. Since a programmer
|
||
might not implement all methods at once, it is not an error
|
||
when the above method is executed. it will just return 0 or NIL.
|
||
|
||
|
||
One may effectively add methods to system OBJECTs:
|
||
|
||
OBJECT mygadget OF gadget -> from intuition!
|
||
-> extra fields here
|
||
ENDOBJECT
|
||
|
||
PROC creategadget() OF mygadget IS ...
|
||
|
||
A pointer to an object such as mygadget above is then compatible
|
||
with a normal gadget pointer, i.e. it may be added directly to a
|
||
window etc.
|
||
|
||
|
||
@endnode
|
||
@node CH_14E
|
||
@title "14E. Constructors, Destructors and Super-Methods"
|
||
14E. Constructors, Destructors and Super-Methods
|
||
------------------------------------------------
|
||
|
||
The constructor name may be anything, but it is usually given the same name
|
||
as the class. One may even have multiple constructors for one class.
|
||
A constructor is called directly on the object created with NEW (and only
|
||
NEW!):
|
||
|
||
NEW obj.stack()
|
||
|
||
Destructors however have to be named "end". An object is destroyed like this:
|
||
|
||
END obj
|
||
|
||
If 'obj' has a .end() method, it is automatically called. end() shouldn't have
|
||
any arguments, and it is of no use returning a value. (see @{" 5M " link CH_5M } for a desciption
|
||
of END's precise functioning).
|
||
|
||
The super-method of a method is the method by the same name of its super
|
||
class. Sometimes it's handy to call this method because you might want to
|
||
add its behaviour to the implementation of your class, however, since
|
||
you're redefined it, calling the super-method by that name will just
|
||
call yourself (!). The SUPER keyword allows you to call any method of your
|
||
superclass (or someone else's superclass):
|
||
|
||
SUPER obj.method()
|
||
|
||
This piece of code above can be used as expression and statement.
|
||
Care has to be taken though, since if your supermethod calls another
|
||
method of that object, it will call the redefined version, not the
|
||
one at its own 'level', so to speak (generally this is what one wants).
|
||
Also, the compiler looks at the static type of 'obj' to find the
|
||
superclass, not the dynamic type (though it may still have that behaviour).
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_15A
|
||
@title "15A. identifier sharing"
|
||
15A. identifier sharing
|
||
-----------------------
|
||
As you've probably guessed from the example in chapter 5D, assembly
|
||
instructions may be freely mixed with E code. The big secret is, that
|
||
a complete assembler has been built in to the compiler.
|
||
Apart from normal assembly addressing modes, you may use the following
|
||
identifiers from E:
|
||
|
||
mylabel:
|
||
LEA mylabel(PC),A1 /* labels */
|
||
|
||
DEF a /* variables */
|
||
MOVE.L (A0)+,a /* note that <var> is <offset>(A4) (or A5) */
|
||
|
||
MOVE.L dosbase,A6 /* library call identifiers */
|
||
JSR Output(A6)
|
||
|
||
MOVEQ #TRUE,D0 /* constants */
|
||
|
||
|
||
EC's assembler supports following constructs,
|
||
|
||
where
|
||
n = registernum
|
||
x = index
|
||
lab = label, from: "label:" or "PROC label()"
|
||
abs = absolute addressing
|
||
s = size. L, W or B where appropriate.
|
||
|
||
- addressing modes supported by EC:
|
||
|
||
Dn, An, (An), (An)+, -(An), x(An), x(An,Dn.s),
|
||
lab(PC), lab(PC,Dn.s), abs, abs.W
|
||
|
||
(note: write abs.W in hexadecimal, to not confuse it with a float
|
||
value, i.e. write MOVE.L $4.W,A6 )
|
||
|
||
- supported partially:
|
||
|
||
#<constexp>
|
||
|
||
- not supported:
|
||
|
||
lab (same as abs), #lab
|
||
use LEA lab(PC),An instead.
|
||
|
||
- extra modes:
|
||
|
||
var.s (transfers contents of var. optionally size is ".W" or ".B",
|
||
default is ".L")
|
||
|
||
LibraryFunction(A6)
|
||
|
||
example:
|
||
|
||
...
|
||
MOVE.W myvar.W,D0 -> move lowword of 'myvar'
|
||
...
|
||
MOVE.L dosbase,A6
|
||
JSR Write(A6)
|
||
|
||
|
||
|
||
As an extra, E allows to directly return registers from a function:
|
||
|
||
ENDPROC D0
|
||
|
||
You may even interpret this as multiple return values, i.e. D0/D1/D2.
|
||
|
||
|
||
@endnode
|
||
@node CH_15B
|
||
@title "15B. the inline assembler compared to a macro assembler"
|
||
15B. the inline assembler compared to a macro assembler
|
||
-------------------------------------------------------
|
||
The inline assembler differs somewhat from your average macro-assembler,
|
||
and this is caused mainly by the fact that it is an extension to E,
|
||
and thus it obeys E-syntax. Main differences:
|
||
|
||
- comments are with /* */ and not with ";", they have a different meaning.
|
||
- keywords and registers are in uppercase, everything is case sensitive
|
||
- no macros and other luxury assembler stuff (well, there's the complete
|
||
E language to make up for that ...)
|
||
- You should be aware that registers A4/A5 may not be trashed by inline
|
||
assembly code, as these are used by E code. Also, if your code
|
||
can be called by code that is register-allocated, you should preserve
|
||
D3-D7. an instruction like
|
||
|
||
MOVEM.L D3-D7,-(A7); /* inline asm */; MOVEM.L (A7)+,D3-D7
|
||
|
||
should help if problems occur.
|
||
- no support for LARGE model/reloc-hunks in assembly _YET_.
|
||
This means practically that you have to use (PC)-relative addressing
|
||
for now (which is faster anyway).
|
||
|
||
|
||
@endnode
|
||
@node CH_15C
|
||
@title "15C. ways using binary data (INCBIN/CHAR..)"
|
||
15C. ways using binary data (INCBIN/CHAR..)
|
||
-------------------------------------------
|
||
|
||
INCBIN
|
||
|
||
syntax: INCBIN <filename>
|
||
|
||
includes a binary file at the exact spot of the statement, should
|
||
therefore be separate from the code. Example:
|
||
|
||
mytab: INCBIN 'df1:data/blabla.bin'
|
||
|
||
LONG, INT, CHAR
|
||
|
||
syntax: LONG <values>,...
|
||
INT <values>,...
|
||
CHAR <values>,...
|
||
|
||
Allows you to place binary data directly in your program. Functions much
|
||
like DC.x in assembly. Note that the CHAR statement also takes strings,
|
||
and will always be aligned to an even word-boundary. Example:
|
||
|
||
mydata: LONG 1,2; CHAR 3,4,'hi folks!',0,1
|
||
|
||
|
||
INCBIN, LONG and in general inline assembly can also be written
|
||
between PROCs in an E source (so it doesn't stand in the way of normal
|
||
E control flow). Notice however that an E source needs atleast
|
||
one PROC before any inline assembly.
|
||
|
||
|
||
@endnode
|
||
@node CH_15D
|
||
@title "15D. OPT ASM"
|
||
15D. OPT ASM
|
||
------------
|
||
OPT ASM is discussed also in chapter 16A. It allows you to operate
|
||
'EC' as an assembler. There's no good reason to use EC over some
|
||
macro-assembler, except that it is significantly faster than for example
|
||
A68k, equals DevPac and loses of AsmOne (sob 8-{). You will also have
|
||
a hard time trying to squeeze your disks of old seka-sources through EC,
|
||
because of the differences as described in chapter 15B. If you want to write
|
||
assembly programs with EC, and want to keep your sources compatible with
|
||
other assemblers, simply precede all E-specific elements with a ";",
|
||
EC will use them, and any other assembler will see them as a comment.
|
||
Start all regular comments with a smiley (";->").
|
||
Example:
|
||
|
||
; OPT ASM
|
||
|
||
start: MOVEQ #1,D0 ;-> do something silly
|
||
RTS ;-> and exit
|
||
|
||
this will be assembled by any assembler, including EC
|
||
|
||
|
||
@endnode
|
||
@node CH_15E
|
||
@title "15E. Inline asm and register variables"
|
||
15E. Inline asm and register variables
|
||
---------------------------------------
|
||
register variables are a great companion to inline assembly, as
|
||
they function just as registers, but at the same time have clear
|
||
identifiers instead of Dx, and also are automatically saved and
|
||
restored by E code. example:
|
||
|
||
PROC bla()
|
||
DEF count:REG
|
||
MOVEQ #10,count
|
||
loop: WriteF('count=\\d\\n',count)
|
||
DBRA count,loop
|
||
ENDPROC
|
||
|
||
all instruction that can work with a Dx EA, work with register
|
||
variables. examples:
|
||
|
||
MOVEQ #1,a
|
||
MOVEM.L D0/D1/a/b/A0,-(A7)
|
||
LSL.L a,b
|
||
|
||
etc.
|
||
|
||
as may be known, EC uses D3-D7 for these register variables. If you wish
|
||
to write code that freely mixes assembly with E, it's advisable to
|
||
keep longer-term values in register variables, and temporaries in
|
||
D0-D2/A0-A3/A6
|
||
|
||
|
||
@endnode
|
||
@node CH_16A
|
||
@title "16A. the OPT keyword"
|
||
16A. the OPT keyword
|
||
--------------------
|
||
|
||
OPT, LARGE, STACK, ASM, NOWARN, DIR, OSVERSION, MODULE, EXPORT, RTD, REG
|
||
|
||
syntax: OPT <options>,...
|
||
|
||
allows you to change some compiler settings:
|
||
|
||
LARGE Sets code and data model to large. Default is small;
|
||
the compiler generates mostly pc-relative code, with a
|
||
max-size of 32k. With LARGE, there are no such limits,
|
||
and reloc-hunks are generated (see @{" 0D " link CH_0D }, LARGE).
|
||
STACK=x Set stacksize to x bytes yourself. Only if you know what
|
||
you are doing. Normally the compiler makes a very good
|
||
guess itself at the required stack space (see @{" 16C " link CH_16C }).
|
||
ASM Set the compiler to assembly mode. From there on, only
|
||
assembly instructions are allowed, and no initialisation
|
||
code is generated. (see @{" 15D " link CH_15D }, inline assembly)
|
||
NOWARN Shut down warnings. The compiler will warn you if it
|
||
*thinks* your program is incorrect, but still syntactically
|
||
ok. (see @{" 0D " link CH_0D }, -n)
|
||
DIR=moduledir Sets the directory where the compiler searches for modules.
|
||
default='emodules:'
|
||
OSVERSION=vers Default=33 (v1.2). Sets the minimum version of the kickstart
|
||
(like 37 for v2.04) your program runs on. That way, your
|
||
program simply fails while the dos.library is being opened
|
||
in the initialisation code when running on an older machine.
|
||
However, checking the version yourself and giving an
|
||
appropriate error-message is more helpful for the user.
|
||
MODULE denotes this source to be a module. (see @{" 10C " link CH_10C })
|
||
EXPORT automatically export all declarations in a module
|
||
RTD generates RTD's instead of RTS in the main source.
|
||
020+ only. [experimental optimisation]
|
||
020,881,040 generate code for these CPUs. not really usable yet.
|
||
REG=n use n register for register-allocation.
|
||
|
||
example:
|
||
|
||
OPT STACK=20000,NOWARN,DIR='df1:modules',OSVERSION=39,REG=3
|
||
|
||
|
||
@endnode
|
||
@node CH_16B
|
||
@title "16B. small/large model"
|
||
16B. small/large model
|
||
----------------------
|
||
Amiga E lets you choose between SMALL and LARGE code/data model.
|
||
Note that most of the programs you'll write (especially if you just
|
||
started with E) will fit into 32k when compiled: you won't have to
|
||
bother setting some code-generation model. You'll recognise the
|
||
need for LARGE model as soon as EC starts complaining that it can't
|
||
squeeze your code into 32k anymore. To compile a source with LARGE model:
|
||
|
||
1> ec large sizy.e
|
||
|
||
or better yet, put the statement
|
||
|
||
OPT LARGE
|
||
|
||
at the top of your code.
|
||
|
||
|
||
@endnode
|
||
@node CH_16C
|
||
@title "16C. stack organisation"
|
||
16C. stack organisation
|
||
-----------------------
|
||
To store all local and global variables, the run-time system of an
|
||
executable generated by Amiga E allocates a chunk of memory,
|
||
from which it takes some fixed part to store all global variables.
|
||
The rest will be dynamically used as functions get called.
|
||
as a function is called in E, space on the stack is reserved
|
||
to store all local data, which is released upon exit of the function.
|
||
That is why having large arrays of local data can be dangerous when
|
||
used recursively: all data of previous calls to the same function
|
||
still resides on the stack and eats up large parts of the free stack
|
||
space. However, if PROCs are called in a linear fashion, there's
|
||
no way the stack will overflow.
|
||
Example:
|
||
|
||
global data: 10k (arrays etc.)
|
||
local data PROC #1: 1k
|
||
local data PROC #1: 3k
|
||
|
||
the runtime system always reserves an extra 10k over this for normal
|
||
recursion (for example with small local-arrays) and additional buffers/
|
||
system spaces, thus will allocate a total of 24k stack space
|
||
|
||
|
||
@endnode
|
||
@node CH_16D
|
||
@title "16D. hardcoded limits"
|
||
16D. hardcoded limits
|
||
---------------------
|
||
|
||
Note these signs: (+-) just about, depends on situation,
|
||
(n.l.) no clear limit, but this seems reasonable.
|
||
|
||
--------------------------------------------------------------------------
|
||
OBJECT/ITEM SIZE/AMOUNT/MAX
|
||
--------------------------------------------------------------------------
|
||
|
||
value datatype CHAR 0 .. 255
|
||
value datatype INT -32 k .. +32 k
|
||
value datatype LONG/PTR -2 gig .. +2 gig
|
||
|
||
identifierlength 100 bytes (n.l.)
|
||
length of one source line 2000 lexical tokens (+-)
|
||
source length 2 gig (theoretically)
|
||
constant lists few hundred elements (+-)
|
||
constant strings 1000 chars (n.l.)
|
||
max. nesting depth of loops (IF, FOR etc.) 500 deep
|
||
max. nesting depth of comments infinite
|
||
|
||
#of local variables per procedure 8000
|
||
#of global variables 7500
|
||
#of arguments to own functions 8000 (together with locals)
|
||
#of arguments to E-varargs functions (WriteF()) 64 (v2.1) / 1024 (v2.5)
|
||
max length of string processed by WriteF() etc. 5000 (depends on stack)
|
||
|
||
one object (allocated local/global or dyn.) 8 k
|
||
one array, list or string (local or global) 32 k
|
||
one string (dynamically) 32 k
|
||
one list (dynamically) 128 k
|
||
one array (dynamically) 2 gig
|
||
objects with NEW 64k elem.
|
||
CHAR/INT/LONG with NEW 2 gig.
|
||
|
||
local data per procedure 250 meg
|
||
global data 250 meg
|
||
|
||
code size of one procedure 32 k
|
||
code size of executable 32 k SMALL, 2 gig LARGE model
|
||
current practical limit (may extend in future) 2-5 meg (v2.1) / 10 meg (v2.5)
|
||
|
||
buffersize of generated code and identifiers relative to source
|
||
buffersize of labels/branches and intermediate independently (re)allocated
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_16E
|
||
@title "16E. error messages, warnings and the unreferenced check"
|
||
16E. error messages, warnings and the unreferenced check
|
||
--------------------------------------------------------
|
||
Sometimes, when compiling your source with EC, you get a message
|
||
of the sort UNREFERENCED: <ident>, <ident>, ...
|
||
This is the case when you have declared variables, functions or labels,
|
||
but did not use them. This is an extra service rendered to you by the
|
||
compiler to help you find out about those hard to find errors.
|
||
|
||
There are several warnings that the compiler issues to notify you that
|
||
something might be wrong, but is not really an error.
|
||
|
||
|
||
- "A4/A5 used in inline assembly"
|
||
This is the warning you'll get if you use registers A4 or A5 in your
|
||
assembly code. The reason for this is that those registers are used
|
||
internally by E to address the global and local variables respectively.
|
||
Of course there might be a good reason to use these, like doing
|
||
a MOVEM.L A4/A5,-(A7) before a large part of inline assembly code
|
||
|
||
- "keep an eye on your stacksize"
|
||
- "stack is definitely too small"
|
||
Both these may be issued when you use OPT STACK=<size>. The compiler
|
||
will simply match your <size> against its own estimate (see @{" 16C " link CH_16C }),
|
||
and issue the former warning if it thinks it's ok but a bit on the small
|
||
side, and the latter if it's probably too small.
|
||
|
||
- 'suspicious use of "=" in void expressions (s). (line %d)'
|
||
This warning is issued if you write expressions like 'a=1' as a
|
||
statement. One reason for this is the fact that a comparison doesn't
|
||
make much sense as a statement, but the main reason is that it could be
|
||
an often occurring typo for 'a:=1'. Forgetting those ":" may be hard to
|
||
find, and it may have disastrous consequences.
|
||
|
||
- 'module changed OPT settings'
|
||
If you use a module that has OPT OSVERSION=37, this changes the
|
||
OPT for the main program too. this warning serves to make you aware
|
||
of this. put such an OPT in the main program too to get rid of it.
|
||
|
||
- 'variable used as function'
|
||
in v3, arbitrary variables may be used as function. this function
|
||
is there to warn you so you don't accidentally do this.
|
||
|
||
- 'code outside PROCs'
|
||
You wrote E code in between PROCs, which is only rarely useful.
|
||
|
||
Errors.
|
||
The compiler will print the source-code-line that caused the error below it,
|
||
with a cursor at the exact spot. The cursor denotes the spot the compiler was
|
||
when it _discovered_ the error, it is thus likely that the token that caused
|
||
the error is the one or two tokens just _before_ the cursor, maybe even on
|
||
the previous line (i.e. the cursor is always _after_ the error).
|
||
|
||
- 'syntax error'
|
||
Most common error. This error is issued either when no other
|
||
error is appropriate or your way of ordering code in your sources
|
||
is too abnormal.
|
||
|
||
- 'unknown keyword/const'
|
||
You have used an identifier in uppercase (like "IF" or "TRUE"), and
|
||
the compiler could not find a definition for it. Causes:
|
||
* mispelled keyword
|
||
* you used a constant, but forgot to define it in a CONST statement
|
||
* you forgot to specify the module where your constant is defined
|
||
|
||
- '":=" expected'
|
||
You have written a FOR statement or an assignment, and put something
|
||
other than ":=" in its place.
|
||
|
||
- 'unexpected characters in line'
|
||
You used characters that have no syntactic meaning in E outside of
|
||
a string. examples: "@!&\\~"
|
||
|
||
- 'label expected'
|
||
At some places, for example after the PROC or JUMP keyword,
|
||
a label identifier is required. You wrote something else.
|
||
|
||
- '"," expected'
|
||
In specifying a list of items (for example a parameter list)
|
||
you wrote something else instead of a comma.
|
||
|
||
- 'variable expected'
|
||
This construction requires a variable, example:
|
||
FOR <var>:= ... etc.
|
||
|
||
- 'value does not fit in 32 bit'
|
||
In specifying a constant value (see @{" 2A " link CH_2A }-2E) you wrote too
|
||
large a number, examples: $FFFFFFFFF, "abcdef".
|
||
Also occurs when you define a SET of more than 32 elements.
|
||
|
||
- 'missing apostrophe/quote'
|
||
You forgot the ' at the other end of a string.
|
||
|
||
- 'incoherent program structure'
|
||
* you started a new PROC before ending the last one
|
||
* you don't nest your loops properly, for example:
|
||
FOR
|
||
IF
|
||
ENDFOR
|
||
ENDIF
|
||
|
||
- 'illegal command-line option'
|
||
In specifying 'EC -opt source' you wrote something for '-opt'
|
||
that is not a legal option to EC.
|
||
|
||
- 'division and multiplication 16bit only'
|
||
The compiler detected that you were about to use 32bits
|
||
for * or /. This would not have the desired result at runtime.
|
||
(see @{" 9G " link CH_9G }, Mul() and Div()).
|
||
|
||
- 'superfluous items in expression/statement'
|
||
After the compiler already compiled your statement, it still found
|
||
lexical tokens instead of an end of line. You probably forgot
|
||
the <lf> or ";" to separate two statements.
|
||
|
||
- 'procedure "main" not available'
|
||
Your program does not include a main procedure !
|
||
|
||
- 'double declaration of label'
|
||
You declared a label twice, for example:
|
||
label:
|
||
PROC label()
|
||
|
||
- 'unsafe use of "*" or "/"'
|
||
This again has to do with 16bit instead of 32bit * and /.
|
||
See 'division and multiplication 16bit only'.
|
||
|
||
- "reading sourcefile didn't succeed"
|
||
Check your source spec. that you gave with 'ec mysource'
|
||
make sure the file ends in '.e'
|
||
|
||
- "writing executable didn't succeed"
|
||
Trying to write the generated code as an executable caused a dos
|
||
error. For example, the executable that did already exist could
|
||
not be overwritten.
|
||
|
||
- 'no args'
|
||
"USAGE: ec [-opts] <sourcecodefilename> (`.e' is added)"
|
||
You get this by just typing 'ec' without any arguments.
|
||
|
||
- 'unknown/illegal addressing mode'
|
||
This error is reported only by the inline assembler. Possible causes are:
|
||
* you used some addressing mode that does not exist on the 68000
|
||
* the addressing mode exists, but not for this instruction.
|
||
not all assembly instructions support all combinations of
|
||
effective addresses for source and destination.
|
||
|
||
- 'unmatched parentheses'
|
||
Your statement has more "(" than ")" or the other way around
|
||
|
||
- 'double declaration'
|
||
One identifier is used in two or more declarations.
|
||
|
||
- 'unknown identifier'
|
||
An identifier is not used in any declaration; it is unknown.
|
||
You probably forgot to put it in a DEF statement.
|
||
|
||
- 'incorrect #of args or use of ()'
|
||
* You forgot to put "(" or ")" at the right spot
|
||
* you supplied the incorrect #of arguments to some function
|
||
|
||
- 'unknown e/library function'
|
||
You wrote an identifier with the first character in uppercase, and
|
||
the second in lowercase, but the compiler could not find a definition.
|
||
Possible causes:
|
||
* Misspelled name of function
|
||
* You forgot to include the module that defines this library call.
|
||
|
||
- 'illegal function call'
|
||
Rarely occurs. You get this one if you try to construct weird
|
||
function calls like nested WriteF()'s. Example:
|
||
WriteF(WriteF('hi!'))
|
||
|
||
- 'unknown format code following "\\"'
|
||
You specified a format code in a string which is illegal.
|
||
(see @{" 2F " link CH_2F } for a listing of format codes)
|
||
|
||
- '/* not properly nested comment structure */'
|
||
The #of '/*' is unequal to the #of '*/', or is placed in a funny order.
|
||
|
||
- 'could not load binary'
|
||
<filespec> in INCBIN <filespec> could not be read.
|
||
|
||
- '"}" expected'
|
||
You started an expression with "{<var>" , but forgot the "}"
|
||
|
||
- 'immediate value expected'
|
||
Some constructions require an immediate value instead of an expression.
|
||
Example:
|
||
DEF s[x*y]:STRING /* wrong: only something like s[100]:STRING is legal */
|
||
|
||
- 'incorrect size of value'
|
||
You specified an unacceptably large (or small) value for some construction.
|
||
Examples:
|
||
DEF s[-1]:STRING, t[1000000]:STRING /* needs to be 0..32000 */
|
||
MOVEQ #1000,D2 /* needs to be -128..127 */
|
||
|
||
- 'no e code allowed in assembly modus'
|
||
You wish to operate the compiler as an assembler by writing 'OPT ASM',
|
||
but, by accident, wrote some E code.
|
||
|
||
- 'illegal/inappropriate type'
|
||
At someplace where a <type> spec. was needed, you wrote something
|
||
inappropriate. Examples:
|
||
DEF a:PTR TO ARRAY /* no such type */
|
||
[1,2,3]:STRING
|
||
|
||
- '"]" expected'
|
||
You started with "[", but never ended with "]"
|
||
|
||
- 'statement out of local/global scope'
|
||
A breakpoint of scope is the first PROC statement. before that,
|
||
only global definitions (DEF,CONST,MODULE etc.) are allowed, and no code.
|
||
In the second part, only code and function definitions are legal, no
|
||
global definitions.
|
||
|
||
- 'could not read module correctly'
|
||
A dos error occurred while trying to read a module from a MODULE
|
||
statement. Causes:
|
||
* emodules: was not assigned properly
|
||
* module name was misspelled, or did not exist in the first place
|
||
* you wrote MODULE 'bla.m' instead of MODULE 'bla'
|
||
|
||
- 'workspace full!'
|
||
Rarely occurs. If it does, you'll need the '-m' (ADDBUF) option to
|
||
manually force EC to make a bigger estimate on the needed amount of
|
||
memory. Try compiling with -m2, then -m3 until the error disappears.
|
||
You'll probably be writing huge applications with giant amounts
|
||
of data just to even possibly get this error.
|
||
|
||
- 'not enough memory while (re-)allocating'
|
||
Just like that. Possible solutions:
|
||
1. You were running other programs in multitasking. Leave them and try again.
|
||
2. You were low on memory anyway and your memory was fragmented.
|
||
Try rebooting.
|
||
3. None of 1-2. Buy a memory expansion (um).
|
||
|
||
- 'incorrect object definition'
|
||
You were being silly while writing the definitions between OBJECT and
|
||
ENDOBJECT. (see @{" 8F " link CH_8F } to find out how to do it right).
|
||
|
||
- 'illegal use of/reference to object'
|
||
If you use expressions like ptr.member, member needs to be a legal
|
||
member of the object ptr is pointing to.
|
||
|
||
- 'incomplete if-then-else expression'
|
||
If you use IF as an operator (see @{" 4E " link CH_4E }), then an ELSE part
|
||
needs to be present: an expression with an IF in it always needs to
|
||
return a value, while a statement with an IF in it can just 'do nothing'
|
||
if no ELSE part is present.
|
||
|
||
- 'unknown object identifier'
|
||
You used an identifier that was recognised by the compiler as being
|
||
part of some object, but you forgot to declare it. Causes:
|
||
* misspelled name
|
||
* missing module
|
||
* the identifier in the module is spelled not like you expected
|
||
from the RKRM's. Check with ShowModule.
|
||
Note that amiga-system-objects inherit from assembly identifiers,
|
||
not from C. Second: identifiers obey E-syntax.
|
||
|
||
- 'double declaration of object identifier'
|
||
One identifier used in two object definitions
|
||
|
||
- 'reference(s) out of 32k range: switch to LARGE model'
|
||
Your program is growing larger than 32k. Simply put 'OPT LARGE'
|
||
in your source and code on. (see @{" 16B " link CH_16B }).
|
||
|
||
- 'reference(s) out of 256 byte range'
|
||
You probably wrote BRA.S or Bcc.S over too great a distance.
|
||
|
||
- 'too sizy expression'
|
||
You used a string '' or list [], possibly recursive [[]], that is too sizy.
|
||
|
||
- 'incomplete exception handler definition'
|
||
You probably used EXCEPT without HANDLE, or the other way round
|
||
(see @{" 13A " link CH_13A } on exception handling).
|
||
|
||
- 'not allowed in a module'
|
||
You're doing one of the few things you can't do in a module,
|
||
such as global variables with initialisations.
|
||
|
||
- 'allowed in modules only'
|
||
you probably use EXPORT in your main source
|
||
|
||
- 'this doesn't make sense'
|
||
general error.
|
||
|
||
- 'you need a newer version of EC for this :-)'
|
||
You probably use a module that was compiled with a newer
|
||
version of EC than you currently have.
|
||
|
||
- 'no matching "["'
|
||
within a statement, a "]" was found, without a matching "]".
|
||
|
||
- 'this instruction needs a better CPU/FPU (see
|
||
You use a construction (probably an asm instruction) that
|
||
requires an OPT 020 or the like.
|
||
|
||
- 'object doesn't understand this method'
|
||
you invoke a method on a object that wasn't defined
|
||
for its type.
|
||
|
||
- 'method doesn't have same #of args as method of baseclass'
|
||
If you redefine a method, you have to make sure the new one
|
||
has the same #of args as the original.
|
||
|
||
- 'too many register variables in this function'
|
||
If you use :REG to assign register variables yourself,
|
||
you can't use more than 5, currently.
|
||
|
||
- 'Linker can't find all symbols'
|
||
If you use a module A that uses again a module B,
|
||
B also needs to linked. A relies on certain PROCs to
|
||
be present in B, and if B was recompiled with those
|
||
PROCs removed, the linker has trouble putting your
|
||
exe together.
|
||
|
||
- 'could not open "mathieeesingbas.library"'
|
||
If you use float code, the compiler itself may need
|
||
float functions to be able to generate code.
|
||
|
||
- 'illegal destructor definition'
|
||
You defined an end() method with arguments (or with a returnvalue)
|
||
|
||
- 'implicit initialisation of private members'
|
||
You write a [...] or NEW [...] expression that has private parts.
|
||
|
||
- 'double method declaration'
|
||
You defined a method on this object twice.
|
||
|
||
- 'object referenced by other object not found'
|
||
You probably inherited from some other object in another module, and
|
||
then changed it (its name for example) without changing/recompiling
|
||
other dependant modules too.
|
||
|
||
- 'unknown preprocessor keyword'
|
||
only #define, #ifdef, #ifndef and #endif are known by EC's PP.
|
||
|
||
- 'illegal macro definition'
|
||
You made a syntax error in typing your #define (see @{" 17L " link CH_17L })
|
||
|
||
- 'incoherent #ifdef/#ifndef nesting'
|
||
You forgot to close with #endif or similar
|
||
|
||
- 'macro redefinition'
|
||
You can't use the same macro-name-identifier twice.
|
||
|
||
- 'syntax error in #ifdef/#ifndef/#else/#endif'
|
||
(see @{" 17L " link CH_17L } for the correct way to write conditionally compiled code)
|
||
|
||
- 'macro(s) nested too deep'
|
||
You will get this if macros expand to other macros which expand to yet
|
||
others... such that the amount of memory needed for this is getting
|
||
out of hand. More likely you just defined a recursive macro (which will
|
||
want to expand forever).
|
||
|
||
- 'method definition out of object/module scope'
|
||
You can only define methods for an object in the same module/source
|
||
where that object is defined.
|
||
|
||
- 'library definition problem'
|
||
Generally when there's something wrong with the procs you specified in
|
||
the LIBRARY declaration, i.e.: one doesn't exist, is not a PROC or is
|
||
a method etc.
|
||
|
||
- 'object not known at this point'
|
||
You are calling a method on an object whose type is not fully known
|
||
at that point in the source. Better place the OBJECT at the top of
|
||
your source.
|
||
|
||
|
||
@endnode
|
||
@node CH_16F
|
||
@title "16F. compiler buffer organisation and allocation"
|
||
16F. compiler buffer organisation and allocation
|
||
------------------------------------------------
|
||
When you get the error 'workspace full' (very unlikely), or want
|
||
to know what really happens when your program is compiled, it's useful
|
||
to know how EC organizes its buffers.
|
||
|
||
A compiler, and in this case EC needs buffers to keep track of all sorts
|
||
of things, like identifiers etc., and it needs a buffer to keep the
|
||
generated code in. EC doesn't know how big these buffers need to be.
|
||
for most buffers, like the one for various strcutures, this is no
|
||
problem: if the buffer is full while compiling, EC just allocates a
|
||
new piece of memory and continues. Other buffers, like the one for
|
||
the generated code, need to be a continuous block of memory that doesn't
|
||
move while compiling: EC needs to make a pretty good estimate of
|
||
this buffersize to be able to compile small and large sources alike.
|
||
To do this, EC computes the needed memory relative to the size of
|
||
your source code, and adds a nice amount to it. This way, in 99% of the
|
||
cases, EC will have allocated enough memory to compile just about any
|
||
source, in other cases, you'll get the error and have to specify more
|
||
memory with the '-m' (ADDBUF) option.
|
||
|
||
Experiment with different types and sizes of example-sources in combination
|
||
with the '-b' (SHOWBUF) option (see @{" 0D " link CH_0D }) to see how this works in practice.
|
||
|
||
|
||
@endnode
|
||
@node CH_16G
|
||
@title "16G. register allocation"
|
||
16G. register allocation
|
||
------------------------
|
||
E v3 supports a register allocation, which is a technique to keep
|
||
variables in registers instead of on the stack. For normal code
|
||
that uses OS-routines you won't notice the difference very much,
|
||
but for tight computation-loops, this optimisation can make a big
|
||
difference. There are two ways to use register allocation:
|
||
|
||
- with the option REG.
|
||
If you write for example EC REG=3 bla.e, (max=5, currently),
|
||
EC will compute for each PROC the 3 most-used variables in
|
||
registers. Register allocation is a technique that tries to be
|
||
intelligent: it will compute for each var a weight, and will
|
||
use heuristics to increase that weight, for example a var used
|
||
in a FOR loop gets relatively a higher weight than one outside
|
||
it, and one in an IF gets an even lower weight. These weights
|
||
are combined, so a WHILE in a FOR gets quite a high weight.
|
||
|
||
- DIY: you can put the keyword REG in front of any type in a
|
||
declaration, for example:
|
||
|
||
DEF x:REG, s[4]:REG LIST
|
||
|
||
you can do this if you don't trust the register-allocator,
|
||
or if you want to fine-tune just one PROC. You can even use
|
||
both together: if in a PROC you have one var with :REG,
|
||
compiling with REG=5 will allow EC to pick the remaining 4
|
||
by itself.
|
||
|
||
The default is REG=0, so EC works much like the older versions.
|
||
|
||
The variables that CAN be allocated are only local variables
|
||
that are not parameters. also, if you take the address of
|
||
a variable with {} it can't be put in a register either (guess
|
||
why...). registers can't be allocated in PROCs that have
|
||
an exception handler, for now.
|
||
|
||
There are a few things to note when using registers:
|
||
- this part of EC is currently (E v3.0a) was tested to
|
||
be pretty reliable, but you still check that behaviour is
|
||
the same as in non-allocated code.
|
||
- EC uses registers D7..D3 for variables, so if you use
|
||
inline assembly, you need to check that PROCs that use
|
||
register-allocation or :REG don't trash these (see @{" 15E " link CH_15E }).
|
||
The code generated for a PROC automatically saves the
|
||
registers it uses (callee save) to protect the code that
|
||
called it.
|
||
- hint: compiling with REG=5 is not inherently fastest,
|
||
since variable saving on function/library calls also
|
||
incurs an overhead. REG=3 may be better for some
|
||
cases. Also if _all_ code in question deals with
|
||
library calls instead of pure computation, expect no
|
||
gain from registers.
|
||
|
||
-> register allocation will easily make this program twice as fast
|
||
|
||
PROC main()
|
||
DEF a,b=10,c=20,d
|
||
FOR a:=1 TO 1000000 DO d:=b+c
|
||
ENDPROC
|
||
|
||
-> at most 5% faster when using register allocation
|
||
|
||
PROC main()
|
||
DEF a,s[100]:STRING,t
|
||
t:='putting "a" in a reg won''t give that much of a speedup, I think.'
|
||
FOR a:=1 TO 100000 DO StrCopy(s,t)
|
||
ENDPROC
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_17A
|
||
@title "17A. bin/showmodule"
|
||
17A. bin/showmodule
|
||
-------------------
|
||
As you might have noticed, E's equivalent for "includes", modules, are binary
|
||
files, much like those usually suplied with Modula2 compilers, for example.
|
||
To display the contents of such a file in readable ascii form, you may use
|
||
showmodule:
|
||
|
||
showmodule <modulespec>
|
||
|
||
examples:
|
||
|
||
1> showmodule emodules:intuition/intuition
|
||
1> showmodule >gadtools.txt emodules:gadtools
|
||
|
||
note that showmodule by default outputs to stdout, and may be interrupted at
|
||
any point by <ctrlc>.
|
||
|
||
when given the option -c, ShowModule will output modules with library
|
||
declarations in .fd format, constants in #define format.
|
||
|
||
|
||
@endnode
|
||
@node CH_17B
|
||
@title "17B. sources/utilities/showhunk.e, bin/showhunk"
|
||
17B. sources/utilities/showhunk.e, bin/showhunk
|
||
-----------------------------------------------
|
||
Displays all types of executable files, and also object ".o" files as
|
||
generated by (other) compilers/assemblers. will show you the (very simple)
|
||
structure of executables generated by EC, but also supports complex
|
||
overlay-files. also dumps labels (like XREF's and XDEF's).
|
||
|
||
most important of all, ShowHunk features a disassembler for
|
||
code-hunks. use the option 'DISASM/S'
|
||
|
||
showhunk <exefile>
|
||
|
||
like: 1> showhunk helloworld
|
||
1> showhunk disasm dpaint
|
||
|
||
|
||
@endnode
|
||
@node CH_17C
|
||
@title "17C. bin/iconvert, bin/pragma2module"
|
||
17C. bin/iconvert, bin/pragma2module
|
||
------------------------------------
|
||
These two utilities are for advanced E-programmers only. if
|
||
you don't feel like one (yet), skip this part.
|
||
|
||
[NOTE: like the showmodule utility, the sources to these utilities
|
||
have been removed from the distribution, because people misused
|
||
their knowledge of the .m module format. It is PRIVATE. Contact
|
||
me first if you want to do something with it.]
|
||
|
||
Iconvert will convert structure and constant definitions in
|
||
assembly ".i" files to E modules, and pragma2module will do the
|
||
same for SAS/C pragma library definition files. Of course, all
|
||
commodores includes have already been converted this way, but
|
||
say you find a nice PD library that you may want to use with E,
|
||
you will need these utilities.
|
||
|
||
most libraries come with various includes defining, most obvious,
|
||
the library calls of the library, as well as the constants
|
||
and structures (OBJECTs in E) that it uses. say that it is
|
||
called "tools.library", then it will probably feature:
|
||
|
||
pragmas/tools_pragmas.h
|
||
includes/tools.i
|
||
|
||
then do:
|
||
|
||
1> pragma2module tools_pragmas.h
|
||
|
||
rename the resulting "tools_pragmas.m" to "tools.m" and put it
|
||
in emodules:, check with ShowModule if all went well.
|
||
|
||
Now, in your program you may use tools.library:
|
||
|
||
MODULE 'tools'
|
||
|
||
PROC main()
|
||
IF (toolsbase:=Openlibrary('tools.library',37))=NIL THEN error()
|
||
|
||
ToolsFunc()
|
||
|
||
...etc.
|
||
|
||
convert tools.i with Iconvert to another tools.m, which you place in
|
||
emodules:libraries, for example. Iconvert needs an assembler like
|
||
the PD A68k or PhxAss (use option "-p") to do the hard work of understanding
|
||
the actual assembly.
|
||
|
||
1> iconvert tools.i
|
||
|
||
see with showmodule what became of the ".i" file. use in your
|
||
program with:
|
||
|
||
MODULE 'libraries/tools'
|
||
|
||
DEF x:toolsobj, y=TOOLS_CONST
|
||
|
||
converting with Iconvert may require some assembly expertise, as
|
||
Iconvert relies on the correct format of the ".i" file, just like
|
||
commodores assembly includes. About 10% of the ".i" files need
|
||
to be patched by hand to be "convertable". definitions that
|
||
Iconvert judges correctly are amongst others
|
||
|
||
<label> EQU <any_expression>
|
||
|
||
STRUCTURE <sname>,0 ; if <>0, then <struct>_SIZEOF
|
||
ULONG <sname>_<label>
|
||
BPTR <sname>_<label>
|
||
; etc.
|
||
LABEL <sname>_SIZEOF ; or "_SIZE"
|
||
|
||
to get an idea what kind of assembly-expression Iconvert can
|
||
handle, take a look at commodores assembly includes and compare
|
||
them to the equivalent modules (for example intuition.i).
|
||
|
||
|
||
@endnode
|
||
@node CH_17D
|
||
@title "17D. bin/ShowCache, bin/FlushCache"
|
||
17D. bin/ShowCache, bin/FlushCache
|
||
----------------------------------
|
||
The E Module Cache is a piece of memory that is able to hold modules (.m)
|
||
between compiles. The first time you use a certain module, EC will load it
|
||
from disk and put it in the cache. the second time and on, EC will find it in
|
||
cache and doesn't have to load anything from disk. If EC compiles a module of
|
||
which an old version is present in cache, it will flush it. One can imagine
|
||
that this a tremendous speedup, even for people with HD's when they use a lot
|
||
of modules and recompile often.
|
||
|
||
To see what's currently stored in the cache (and how much memory
|
||
it's wasteing :-), type:
|
||
|
||
1> ShowCache
|
||
|
||
A second utility, FlushCache, allows you to selectively remove modules
|
||
from the cache. Reasons for this can be:
|
||
- you can't afford the memory it uses
|
||
- you created a new .m, with a tool other than EC, so you need to flush
|
||
by hand (in all other cases it's NOT necessary to flush!).
|
||
The argument to Flushcache is the substring that needs to occur in
|
||
a module name for it to be flushed. no args means flush all.
|
||
|
||
1> FlushCache ; empty whole cache
|
||
1> FlushCache intuition/ ; flush all intuition-related modules
|
||
|
||
You can use the EC option 'IGNORECACHE/S' to compile a source without
|
||
using the cache. Whether the cache is full or empty, EC will load
|
||
all from disk, and store nothing new in cache.
|
||
|
||
If two EC's try to access the cache simultaneously in multitasking,
|
||
the second EC acts as if its IGNORECACHE flag were set.
|
||
|
||
|
||
@endnode
|
||
@node CH_17E
|
||
@title "17E. rexx/ecompile.rexx"
|
||
17E. rexx/ecompile.rexx
|
||
-----------------------
|
||
This is a rexx-script for CygnusEd (tm), and enables you to compile E programs
|
||
from the editor. Just assign this script a function key in the editor with
|
||
"Install Dos/Arexx command ..." (check your CED-manual if you're not sure how
|
||
to do this). Now write your programs, and press Fx if you wish to compile.
|
||
Your source will be saved if necessary, the compiler will be invoked on a
|
||
separate console window, and the program is run on the same console. When
|
||
your program is done, you may press <return> to return to the editor
|
||
(CED-screen to back and front is automatically done by the script). If an
|
||
error occurred during compilation, the script will let CED jump to the line of
|
||
error after you pressed <return>
|
||
|
||
Note: in the script there is a path name as to where the compiler can be
|
||
found. You probably need to change this. Also, the script copies EC to ram:
|
||
for systems with a slower SYS: device, you may want to disable this if you
|
||
have a fast HD.
|
||
|
||
|
||
@endnode
|
||
@node CH_17F
|
||
@title "17F. bin/o2m"
|
||
17F. bin/o2m
|
||
------------
|
||
If you have large pieces of assembly source that you'd like to use it would be
|
||
tedious at best to convert them all by hand to E's inline assembly. o2m
|
||
allows you to simply have your favourite macro-assembler assemble it all to a
|
||
.o file, and o2, then will turn this .o file into a .m file for use with E.
|
||
If you have a file bla.o:
|
||
|
||
1> o2m bla
|
||
|
||
will produce bla.m. However, the .o file will have to obey certain rules. It
|
||
should consist of just one code-hunk with external definitions (XDEFs) for
|
||
each symbol you wish to reference from E, and no XREFs. typically,
|
||
|
||
your source would look like:
|
||
|
||
XDEF add__ii
|
||
|
||
add__ii:
|
||
move.l 4(a7),d0
|
||
add.l 8(a7),d0
|
||
rts
|
||
|
||
this example shows a bit of assembly code that gets two arguments (hence
|
||
the two "i" for integer). arguments can be found on the stack, where
|
||
4(a7) is the last arg, 8(a7) the one before that etc.
|
||
|
||
Showhunk shows you this:
|
||
|
||
hunk_unit:
|
||
HUNK -1 hunk_name:
|
||
hunk_code: 12 bytes
|
||
hunk_ext
|
||
add__ii = $0
|
||
|
||
this type of .o file is easily transformed to .m by o2m:
|
||
|
||
/* this module contains 12 bytes of code! */
|
||
|
||
PROC add(a,b)
|
||
|
||
|
||
there are a couple of things to note:
|
||
- if your asm code uses D3-D7/A4/A5 you should probably save it.
|
||
- if a label doesn't have the "__" with an "i" for each function,
|
||
it becomes a parameterless function. Don't worry if the label
|
||
actually references data, you can simply get the address of this
|
||
'proc' with {}, and use it as a ptr to your data.
|
||
|
||
theoretically, o2m could be used to link C code to E programs, however in
|
||
practise this is often not feasable. If your C compiler allows you to 'tune'
|
||
the resulting .o files a bit, this might work.
|
||
|
||
some problems are:
|
||
- reference of C functions, for example _printf()
|
||
- reference of globals vars created by C startup code. C code
|
||
may reference "DOSBase" as an XREF, whereas E's startup code makes
|
||
this value available somewhere on the stack
|
||
- call/register conventions.
|
||
|
||
I did manage to link a small C function to E that only does some computation,
|
||
and call it succesfully (this was done using MaxonC++, whose linker uses the
|
||
__ii convention for parameters also).
|
||
|
||
|
||
@endnode
|
||
@node CH_17G
|
||
@title "17G. bin/EYacc"
|
||
17G. bin/EYacc
|
||
--------------
|
||
This is a port of the famous Yacc utility for unix, which now produces E code
|
||
instead of C code. It is only a first version, so don't expect too much from
|
||
it. If you have no clue what Yacc does, read a text on it, since I'm not
|
||
going to explain that in full here.
|
||
|
||
Basically you can write .y sources as normal, only where actions used to be
|
||
written in C, now you can write E. See the Src/Yacc/bcalc.y example.
|
||
|
||
1> eyacc bcalc.y
|
||
|
||
produces a file 'yyparse.e'
|
||
|
||
1> ec yyparse
|
||
|
||
will get you a module, which contains only the function yyparse(). The rest
|
||
of 'how to interface with Yacc' should be analoguous to C.
|
||
|
||
[note: I'm halfway through a translation of Lex to E-Lex, but not done yet.]
|
||
|
||
further info.
|
||
E-Yacc is a modification of Berkeley Yacc 1.8, originally by
|
||
corbett@berkeley.edu. The inclusion of this modified version in the E
|
||
distribution is totally legal, as the author states in the original BYacc1.8
|
||
README:
|
||
|
||
" Berkeley Yacc is in the public domain. The data structures and algorithms
|
||
used in Berkeley Yacc are all either taken from documents available to the
|
||
general public or are inventions of the author. Anyone may freely distribute
|
||
source or binary forms of Berkeley Yacc whether unchanged or modified.
|
||
Distributers may charge whatever fees they can obtain for Berkeley Yacc.
|
||
Programs generated by Berkeley Yacc may be distributed freely. "
|
||
|
||
|
||
@endnode
|
||
@node CH_17H
|
||
@title "17H. Src/Various/SrcGen.e"
|
||
17H. Src/Various/SrcGen.e
|
||
-------------------------
|
||
SrcGen GadToolBox source generator for E: beta version
|
||
[note: this is now very much obsolete. Have a look at EasyGUI]
|
||
|
||
You'll be needing GadToolBox v2.0 or higher, and have the
|
||
gadtoolsbox.library that comes with it in your LIBS:. Now, with GTB,
|
||
make some simple example (just a window with a few gadgets/menus etc.),
|
||
save it as "bla" (filename will be "bla.gui"), and type:
|
||
|
||
1> SrcGen bla
|
||
1> EC bla
|
||
1> bla
|
||
|
||
"bla.e" contains the routines for opening your interface, as well
|
||
as some routines to handle idcmpmessages, errors etc., and a dummy
|
||
"main" that just waits for one selection. here you can put in
|
||
your own code. see the commandline template how to stop SrcGen from
|
||
generating these routines.
|
||
|
||
That's all there's to it. If you have problems, just check the
|
||
source that has been generated.
|
||
|
||
|
||
@endnode
|
||
@node CH_17I
|
||
@title "17I. bin/EBuild"
|
||
17I. bin/EBuild
|
||
---------------
|
||
|
||
now also described by its own documentation in the
|
||
Src/Utils/Build directory.
|
||
|
||
|
||
@endnode
|
||
@node CH_17J
|
||
@title "17J. Aprof"
|
||
17J. Aprof
|
||
---------------
|
||
|
||
described in its own documentation in the Bin/Tools directory.
|
||
|
||
|
||
@endnode
|
||
@node CH_17K
|
||
@title "17K. EDBG"
|
||
17K. EDBG
|
||
---------
|
||
|
||
bugs fixed for v3.3a:
|
||
- Double-clicking on window frames no longer sets breakpoints, etc.
|
||
- 'ARG/K' argument now works (sets the arguments for the program being
|
||
debugged)
|
||
- More than 16 windows allowed now, without crashing!
|
||
- Safer closing of public screen
|
||
- Unary negation now works as an expression
|
||
- No longer ignores some identifiers with "_"
|
||
- Serious (but benign?) bugs in initial breakpoint setup code removed
|
||
|
||
features new for v3.3a:
|
||
- Integrated calls to Explorer to debug objects (extracts type
|
||
information from declarations)
|
||
- Can now debug standard global variables
|
||
- Can now debug "self" variable (requires EC v3.3a+)
|
||
- "Follow Step" mode added (and configurable)
|
||
- "Repeat Step" mode added (useful for debugging loops)
|
||
- "^var" expression now handled
|
||
- Source location of offsets added (useful for finding Enforcer hits)
|
||
- Registers now marked with whether they have changed since last step
|
||
- Status/flags register now expressed more clearly
|
||
|
||
|
||
Explorer
|
||
--------
|
||
|
||
To examine/watch a variable (in the current scope) double-click on it.
|
||
To see it in Explorer, press Shift while you double-click. If
|
||
Explorer is not running it will be automatically started (as
|
||
"explorer" or whatever you set in the options, including any
|
||
arguments). It will be instructed to open on the same screen as EDBG,
|
||
and will quit when EDBG quits (but only if EDBG started it). Explorer
|
||
ought to be run in Auto-Reply mode, and you need Explorer v2.2j+.
|
||
|
||
|
||
|
||
EDBG is the E sourcelevel debugger. To use it, compile your source with
|
||
the DEBUG flag (this works on both main program an modules).
|
||
This will add debug infos to your executable/module.
|
||
|
||
NOTE: Do NOT distribute a program of which any part is compiled with DEBUG/S.
|
||
(You can check this with ShowHunk, it should not contain any hunk_debug).
|
||
Programs with debug-infos are compiled with extra NOPs to facilitate
|
||
debugging, which isn't wanted in the final code.
|
||
|
||
Always make sure sources and compiled are in the current directory, then
|
||
fire up EDBG with:
|
||
|
||
1> EDBG exename
|
||
|
||
template:
|
||
|
||
EXECUTABLE/A,PUBSCREEN/K:
|
||
|
||
with for example PUBSCREEN=Workbench you can run EDBG anywhere. By default
|
||
it opens it own screen, which is a clone of the workbench in size and
|
||
display mode. EDBG works fine on the graphics-cards etc.
|
||
|
||
EDBG opens a window for every source/module in your project, and it will start
|
||
with the one that contains 'main'. From you can step through your code, and
|
||
windows will automatically open when necessary. When code doesn't have a
|
||
source attached, it can be executed only (which is sometimes handy, if it
|
||
doesn't need to be debugged).
|
||
|
||
From here all is pretty intuitive. Major buttons are the first two pictures
|
||
which are step over/in. Step in follows the code every step as it is
|
||
executed, step over does the same but does not enter subroutines.
|
||
[Just try it, the debugger is quite intuitive]
|
||
|
||
Other functions show memory or register windows, and various other functions.
|
||
An important one is double-clicking on variable-names: this will show their
|
||
contents in variable view window. Double clicking somewhere not on a variable
|
||
allows you to set a breakpoint.
|
||
|
||
For the memory views, memory breakpoints, exception raising features a subset
|
||
of the E expression language can be used to denote addresses and values:
|
||
|
||
values: 123, $ABC, %010101, "FORM"
|
||
variables: a, {a}
|
||
operators: +, -, *, /, ()
|
||
|
||
For example:
|
||
- to check when variable `a' is modified, use `{a}' as a memory breakpoint.
|
||
- to view the memory pointed to by array `a' (of LONGs) at offset `b', use
|
||
`a+(b*4)' with a memory view.
|
||
- to change the contents of variable `a' into `b*2' click on the variable
|
||
in the variable-view and select 'Modify'.
|
||
|
||
Note that when raising exceptions, that the handler for the current PROC
|
||
is installed only at the first statement after the local DEFs, i.e. you will
|
||
want to step past there first before raising an exception that should
|
||
go to that handler.
|
||
|
||
To quit a debugging session in the middle of a program while still freeing
|
||
all resources, or to test the capacity of your program to deal with failures,
|
||
raise an exception.
|
||
|
||
Caveats:
|
||
- The debugged program runs on the same task as EDBG. this means that
|
||
all code that does something special to the task will have to be
|
||
careful. An example is Forbid() etc.
|
||
- A special case is ReadArgs(). because EDBG already read the args, a
|
||
call from the debugged program will cause a read from the console.
|
||
So you can conveniently type the args to your program on the EDBG
|
||
output console and press return.
|
||
|
||
Know problems:
|
||
- The variable view treats scopes on basis of variable names
|
||
|
||
Future:
|
||
- editing in variable and memory views
|
||
- more extensive E expression language support
|
||
- E types support (OBJECTs in particular)
|
||
- cooperation with enforcer (as soon as I can run it)
|
||
|
||
|
||
The LINEDEBUG and SYM options.
|
||
The LINEDEBUG option adds linedebug info to your executable (for each line of
|
||
code inside a PROC). This option is necessary for EDBG, but the DEBUG switch
|
||
automatically turns it on. LINEDEBUG is partially compatible with the "LINE"
|
||
HUNK_DEBUG produced by other compilers/assemblers, so it can be useful with
|
||
other debug-tools as well. The SYM option is not necessary for EDBG, but
|
||
can be useful for others, such as AProf or disassemblers.
|
||
|
||
|
||
The EDBG Arexx port (name: "EDBG")
|
||
|
||
EDBG's prefs saving works by saving an arexx-script, and running it again on
|
||
startup. It will save specific prefs for every project, remember window
|
||
positions (if you want to), and also variables you were watching!
|
||
|
||
Select 'Settings...', and after that 'Save Settings'. You may add optional
|
||
Arexx-scripts that can be launched directly from EDBG's 'Rexx' menu. The
|
||
saved script is '.edbg-startup.rexx' in the current dir, which you are
|
||
encouraged to examine and modify.
|
||
|
||
currently supported EDBG Arexx commands:
|
||
|
||
QUIT
|
||
|
||
emergency exit (raising an exception is friendlier).
|
||
|
||
STEPIN
|
||
STEPOVER
|
||
RUN
|
||
|
||
step in/over and run to breakpoint. only actually performed when
|
||
control returns to the debugged program.
|
||
|
||
EVAL exp
|
||
|
||
allows you to evaluate an E expression. You may use variables
|
||
from the scope that is currently being debugged (much like
|
||
'Eval E Expression' from the menu). Nearly all Arexx commands
|
||
that expect integer arguments can use similar expressions to EVAL.
|
||
This is the only command that returns something in the rexx
|
||
variable "rc" (the value of the expression). example:
|
||
|
||
'eval {a}+(b*4)'
|
||
say 'rc =' rc
|
||
|
||
you might need to fiddle with "options failat". Note closely
|
||
how these to get their variables from entirely different scopes:
|
||
|
||
'eval a+b'
|
||
'eval' a+b
|
||
|
||
(from Arexx (!))
|
||
|
||
BREAKPOINT linenum
|
||
|
||
sets the breakpoint to that linenumber (in the current view).
|
||
|
||
MEMORYBREAKPOINT addr
|
||
|
||
sets a memory breakpoint on that address. example:
|
||
|
||
'memorybreakpoint {a}'
|
||
'run'
|
||
|
||
runs to the spot where variable "a" gets modified.
|
||
|
||
RAISE exception exceptioninfo
|
||
|
||
raises that exception in the debugged program.
|
||
|
||
ASSIGN varname exp
|
||
|
||
modifies a variable in the current scope. example:
|
||
|
||
'assign a a+1'
|
||
|
||
increases "a" by one.
|
||
|
||
WATCH varname varname ...
|
||
|
||
may be followed by any number of variable names. these will be
|
||
inserted into the list of watched variables, which will then
|
||
automatically be shown whenever you get to the scope that
|
||
contains those variables.
|
||
|
||
MEMORY exp
|
||
|
||
opens a memory window on the address denoted by exp. doesn't
|
||
check for validity of the address.
|
||
|
||
VARIABLES left top width height
|
||
|
||
opens the variable view window on that particular spot on the
|
||
EDBG screen. If it was already open, it will resize it if
|
||
necessary.
|
||
|
||
SRCWINDOW srcname left top width height
|
||
|
||
same for the source view of source "srcname"
|
||
|
||
NOABOUT
|
||
|
||
prevents the about window from popping up
|
||
|
||
NOREFRESH
|
||
|
||
will not refresh memory windows etc. at every step
|
||
|
||
REXX n script
|
||
|
||
sets up the arexx script "script" for launching within EDBG,
|
||
from menu item "n".
|
||
|
||
|
||
[note: if you want EDBG to make debugging steps during the
|
||
execution of your scripts, you should launch them with 'rx',
|
||
as those launched from EDBG execute the whole script before
|
||
returning to the debugged program]
|
||
|
||
|
||
@endnode
|
||
@node CH_17L
|
||
@title "17L. EC PreProcessor"
|
||
17L. EC PreProcessor
|
||
--------------------
|
||
|
||
EC has an internal preprocessor which features macro substitution and
|
||
conditional compilation. These are not features of the E language,
|
||
rather it has been integrated with EC for speed and flexibility.
|
||
|
||
Activating the preprocessor.
|
||
Until you type:
|
||
|
||
OPT PREPROCESS
|
||
|
||
EC will behave as normal. This OPT is necessary for any preprocessor
|
||
related feature.
|
||
|
||
Macros.
|
||
The macropreprocessor is mostly compatible with Mac2E and the C language PP
|
||
(see
|
||
|
||
You can define macros as follows:
|
||
|
||
#define MACRONAME
|
||
#define MACRONAME BODY
|
||
#define MACRONAME(ARG,...) BODY
|
||
#define MACRONAME(ARG,...) BODY \\
|
||
REST OF BODY
|
||
|
||
MACRONAME and ARG may be ANY case, and may contain "_" and 0-9 as usual.
|
||
Whitespace may be added everywhere, except between MACRONAME and "(", because
|
||
otherwise EC can't see the difference between arguments and a body. The BODY
|
||
may contain occurances of the ARGs. A macro may continue on the next line by
|
||
preceding the end_of_line with a "\\". A body should not extend over more than
|
||
one statement. (a macroname with no body is useful in combination with
|
||
conditional compilation).
|
||
|
||
Macro identifiers have precedence over other identifiers.
|
||
|
||
Macro definitions defined in a module will be saved in the module only if
|
||
OPT EXPORT is on (#define can't be preceded with EXPORT). If that's a problem,
|
||
keep macros together in their own modules. Macros in modules can be used in
|
||
other code simply by importing them with MODULE and OPT PREPROCESS.
|
||
|
||
|
||
Using a macro.
|
||
Using MACRONAME anywhere in the program will insert the body of the macro
|
||
at that spot. Note that this substitution is text substitution, and has
|
||
little to do with actual E syntax. If the macros have arguments they will
|
||
be inserted at their respective places in the macrobody. If the body (or
|
||
the arguments) contain futher macros, these will be expanded after that.
|
||
|
||
example:
|
||
|
||
#define MAX(x,y) (IF x>y THEN x ELSE y)
|
||
|
||
WriteF('biggest = \\d\\n',MAX(10,a))
|
||
|
||
will equal writing:
|
||
|
||
WriteF('biggest = \\d\\n',(IF 10>a THEN 10 ELSE a))
|
||
|
||
This immediately shows a danger of macros: since it simply copies
|
||
textually, writing MAX(large_computation(),1) will generate code
|
||
that executes large_computation() twice. Be aware of this.
|
||
|
||
|
||
Differences of the E PP from the C PP and Mac2E
|
||
* it doesn't support 0-arg macros, i.e. MACRONAME()
|
||
* comments part of a macro definition are not removed
|
||
* macro arguments in string constants are replaced as well. This
|
||
can actually be quite handy, to write macros such as:
|
||
#define debug(x) (WriteF('now evaluating: x\\n') BUT x)
|
||
|
||
|
||
Conditional compilation.
|
||
This can be useful if you wish to decide at compile time which part of
|
||
your code to use. syntax:
|
||
|
||
#ifdef MACRONAME
|
||
|
||
or
|
||
|
||
#ifndef MACRONAME
|
||
|
||
the piece of source following this will or won't be compiled depending on
|
||
whether MACRONAME was defined. you can simply do this with:
|
||
|
||
#define MYFLAG
|
||
|
||
or somesuch. End a conditionally compiled block with:
|
||
|
||
#endif
|
||
|
||
Blocks like these may be nested. example:
|
||
|
||
#define DEBUG
|
||
->#define HEAVYDEBUG
|
||
|
||
#ifdef DEBUG
|
||
WriteF('now entering bla() with x = \\d\\n',x)
|
||
#ifdef HEAVYDEBUG
|
||
WriteF('now dumping memory...\\n')
|
||
/* ... */
|
||
#endif
|
||
#endif
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_17M
|
||
@title "17M. EC Library mode"
|
||
17M. EC Library mode
|
||
--------------------
|
||
|
||
NOTE: there are quite a couple of things you need to know before you can
|
||
rush off and create a library. please read this WHOLE chapter before
|
||
proceeding!
|
||
|
||
EC can generate shared library files that can be called from any programming
|
||
language, and from any number of tasks at the same time. Additionally it
|
||
supports almost all E language constructs in one way or another.
|
||
|
||
How?
|
||
|
||
You can create a library by writing a LIBRARY declaration at the top of the
|
||
sourcefile you want to become the library. syntax:
|
||
|
||
LIBRARY name,version,revision,versionstring IS function,...
|
||
|
||
for example:
|
||
|
||
LIBRARY 'mytools.library',37,1,'MyTools 37.1 (2.7.95)' IS
|
||
opengui,closegui,...
|
||
|
||
The function names are simply those of the PROCs (no methods!) you wish to
|
||
make available as functions in the library. The order in which they appear is
|
||
important, because it denotes at which offsets they will be in the library
|
||
(starting from -30). You can explicitly assign registers (e.g. `myfun(A0)').
|
||
|
||
|
||
A source with a declaration like this is in many respects similar to a normal
|
||
E source (i.e. not a module). You may also put names of PROCs from included
|
||
modules in the LIBRARY declaration.
|
||
|
||
If all went ok, EC will compile your source to two files: the library file
|
||
(with the name from the LIBRARY declaration, not the source file!), and a
|
||
module (".library" replaced by ".m") which contains information for other E
|
||
programs on how to use your library (see
|
||
of procedures have their first char converted to uppercase.
|
||
|
||
You may now use your library from another E program as follows:
|
||
|
||
MODULE '*mymodule'
|
||
|
||
PROC main()
|
||
IF mymodulebase:=OpenLibrary('mymodule.library',37)
|
||
/* ... use libfunctions here! ... */
|
||
CloseLibrary(mymodulebase)
|
||
ENDIF
|
||
ENDPROC
|
||
|
||
or similar. This is all the E syntax need to create and use libraries,
|
||
however, there are quite a number of things one needs to know about what kind
|
||
of E code may be put into libraries.
|
||
|
||
|
||
What's so special about libraries then?
|
||
|
||
Well, imagine that the code in a library is completely separate from the
|
||
calling program, and that it may be executed at the same time by two different
|
||
tasks. This weren't so problematic if it weren't for the fact that code in
|
||
most languages needs a global environment, not only for storing global
|
||
variables, but in E also for quite a lot of features including exception
|
||
handling, memory, io, library bases, etc. (In E, the A4 register is used for
|
||
this). It is safe to say that E code without this specific global environment
|
||
is almost impossible.
|
||
|
||
This environment can't be provided by the program that calls the library,
|
||
since this program might be written in C (even for E it would be a problem).
|
||
It can't be stored in the library base, since two tasks might be using the
|
||
library at the same time. So what do E libraries do? They create such an
|
||
environment for every task that calls them. When the library is opened by a
|
||
task, a small E environment for that task is created. When the task makes a
|
||
call, the environment belonging to the task is looked up (and put in A4).
|
||
|
||
In practise this means that libraries created by E should be opened/closed by
|
||
any one task EXACTLY ONCE, not more, not less (this shouldn't pose any
|
||
problems though). If you put your library in the PD, you will want to put a
|
||
programmers notice in your docs mentioning this. Strange things WILL happen
|
||
if you don't obey this rule.
|
||
|
||
|
||
features and problems/suggestions, hints and tips:
|
||
- If you use global variables, or in general the global environment, remember
|
||
that you have a set of global vars for each task that uses your library.
|
||
Global arrays are initialised as normal. (this is what you want, in
|
||
general).
|
||
- A procedure main() still needs to be present. It will be called each time
|
||
when a task makes an OpenLibrary() call to you library. Use it only to make
|
||
minor initialisations, i.e. setting global vars to certain values, otherwise
|
||
leave it empty. The code in main() is executed within a Forbid()/Permit().
|
||
If the source for a LIBRARY contains a PROC close(), it will be called
|
||
when the user of the library calls CloseLibrary() (i.e. it is the
|
||
counterpart to main()). [close still mentioned as unref]
|
||
- Lists with variable bits in them, i.e [1,2,a]. A problem when >1 task uses
|
||
the library. This is a bit of a problem for example in taglists. For now
|
||
one should probably prefix those with a NEW and FastDisposeList() them
|
||
afterwards (hopefully a better solution will be found sometime later).
|
||
- Exceptions can be raised and caught like normal, only NO exception should
|
||
be thrown out of a library call EVER (strange things might happen). If one
|
||
of your library calls uses code that might throw an exception, you have to
|
||
handle it at the bottom level and turn it into an error code or somesuch.
|
||
- In general, and specifically with NEW, if you allocate resources in the
|
||
library, free them in the library. If you feel you have to leave memory
|
||
behind from or to the library, use AllocMem. Also, don't deallocate stuff
|
||
that the calling task allocated for you.
|
||
- If you need callbacks, use hooks from the OS. Using pointers to PROCs
|
||
like for example EasyGUI does won't do, as these then will be executed
|
||
within the libraries' environment (i.e. the calling task will not be
|
||
able to reach its own A4 (or equivalent in another language).
|
||
- LISP Cells make use of tricky garbage collection techniques on the stack,
|
||
and generally will not work in libraries. This could be made to work, I
|
||
guess...
|
||
- stdin, stdout, conout, arg, wbmessage will all be NIL. If you wish to
|
||
use for example WriteF(), it's best to open (and close!) your own console,
|
||
and place its handle in stdout.
|
||
- libraries will stay in memory after use, so make you flush them regularly,
|
||
otherwise debugging becomes particularly hard (you'll be using an old
|
||
version: "didn't I just change that?"). Also close the library in all cases,
|
||
otherwise you'll have to reboot to flush it. A trick to flush directly
|
||
after testing the library is writing `AllocMem(100000000,0)' or similar
|
||
directly after the CloseLibrary() call.
|
||
- memory allocation like New() and NEW, and other resources that E startup
|
||
code may arrange, are freed when the calling task closes your library,
|
||
i.e. not when your library is removed. Remember that your code might be
|
||
dealing (hopefully transparently) with different sets of global variables,
|
||
NEW memory pools, resources etc.
|
||
- If you want to use libraries with other languages like C, note that
|
||
ShowModule has a "-c" option that shows a library module in .fd format,
|
||
which can easily be converted to pragmas for a specific compiler.
|
||
- Do not "convert" old code to libraries without looking at it first,
|
||
especially don't put code you don't have the source to in libraries
|
||
without thinking first (it might have static lists, exceptions at weird
|
||
spots etc.). (easygui.m is an example of this).
|
||
- Test your code under different circumstances. Have two programs use
|
||
your library at the same time, and close in different orders to see
|
||
wether it has problematic code in it. Try letting it fall over in any
|
||
combination of EDBG, NILCHECK/S, enforcer/mungwall etc. Test your
|
||
code first as normal exe or module before turning it into a library.
|
||
- In general, If you do tricky stuff involving the task or global environment,
|
||
remember that you're in a different position from normal E code.
|
||
|
||
Have a look at the examples in the Src/Library dir.
|
||
|
||
It is currently not possible to create devices directly with EC library
|
||
link feature. If a lot of people require this feature I will build it in.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_18A
|
||
@title "18A. The E grammar"
|
||
18A. The E grammar
|
||
------------------
|
||
|
||
|
||
This is a grammar of E for those who are interested. Don't expect
|
||
it to be up to date or otherwise complete/correct though (it should
|
||
be quite ok for E upto v2.1b atleast).
|
||
|
||
lex syntax: regular expressions
|
||
parse syntax: own ASF/SDF adaption;
|
||
|
||
name = grammar ident
|
||
"name" = constant
|
||
() = grouping
|
||
| = or
|
||
e* = 0 or more of e
|
||
e+ = 1 or more of e
|
||
{e s}* = 0 or more of e separated by s
|
||
{e s}+ = 1 or more of e separated by s
|
||
[e] = e is optional
|
||
; e = e is comment :-)
|
||
|
||
|
||
|
||
LEX
|
||
---
|
||
|
||
|
||
|
||
whitespace = [ \\t] ; also \\n if last token is [,+-*/] or similar
|
||
anything between "/*" and "*/"
|
||
from "->" to \\n
|
||
eol = [;\\n]
|
||
|
||
constant = [A-Z] ( [A-Z] [A-Za-z0-9_]* )?
|
||
builtin = [A-Z] [a-z] [A-Za-z0-9_]*
|
||
ident,objident = [a-z] [a-zA-Z0-9_]*
|
||
|
||
num = [0-9]+ ; "-" is separate token
|
||
$[0-9A-Fa-f]+
|
||
%[01]+
|
||
fnum = [0-9]*.[0-9]*
|
||
|
||
stringconst = anything in ''
|
||
charconst = anything in ""
|
||
|
||
|
||
|
||
PARSE
|
||
-----
|
||
|
||
|
||
program = opts globalpart localpart
|
||
|
||
globalpart = ( modulestat | defstat | objdecl | constdecl | raisedecl )*
|
||
localpart = ( procdecl | constdecl )+
|
||
|
||
modulestat = "MODULE" { conststring "," }+ eol
|
||
defstat = "DEF" vardecllist eol
|
||
objdecl = "OBJECT" ident [ "OF" ident ] eol
|
||
( vardecllist eol )+
|
||
"ENDOBJECT" eol
|
||
constdecl = "CONST" { ( constant "=" constexp ) "," }+ |
|
||
"ENUM" { ( constant | constant "=" constexp ) "," }+ |
|
||
"SET" { constant "," }+
|
||
procdecl = [ "EXPORT" ] "PROC" ident "(" argdecllist ")"
|
||
[ "OF" ident ] [ "HANDLE" ]
|
||
( ( "RETURN" | "IS" ) { exp "," }* |
|
||
eol defstat* stats
|
||
[ "EXCEPT" eol stats ]
|
||
"ENDPROC" { exp "," }* eol )
|
||
raisedecl = "RAISE" { ( constant "IF" builtin "()" compop num ) "," }+
|
||
opts = ( "OPT" { setting "," }+ )* ; machine dependant
|
||
|
||
vardecllist = { vardecl "," }+
|
||
vardecl = ident [ "=" num ]
|
||
[ ":" ( "LONG" | "REAL" | "PTR" "TO" ptrtype ) ] |
|
||
ident ":" objtype |
|
||
ident "[" num "]" ":"
|
||
( "ARRAY" |
|
||
"ARRAY" "OF" ptrtype |
|
||
"STRING" |
|
||
"LIST" )
|
||
argdecllist = { argdecl "," }+
|
||
argdecl = ident [ "=" defaultarg ]
|
||
[ ":" ( "LONG" | "REAL" | "PTR" "TO" ptrtype ) ]
|
||
ptrtype = objtype | simpletype
|
||
simpletype = CHAR | INT | LONG
|
||
objtype = ident
|
||
|
||
stats = ( ( onelinestat | multlinestat ) eol )*
|
||
onelinestat = exp |
|
||
lval ":=" exp |
|
||
{ var "," }+ ":=" exp |
|
||
"IF" exp "THEN" onelinestat "ELSE" onelinestat |
|
||
"FOR" var ":=" exp "TO" exp [ "STEP" num ]
|
||
"DO" onelinestat |
|
||
"WHILE" exp "DO" onelinestat |
|
||
"RETURN" { exp "," }* |
|
||
"JUMP" ident |
|
||
( "INC" | "DEC" ) var | ; nearly obsolete
|
||
asm_mnemonic { operand "," }* | ; machine dependant
|
||
"INCBIN" stringconst | ; inline asm support
|
||
simpletype { num "," }+ |
|
||
"VOID" exp ; obsolete
|
||
multlinestat = "IF" exp eol stats
|
||
[ ( "ELSEIF" exp eol stats )* ]
|
||
[ "ELSE" eol stats ]
|
||
"ENDIF" |
|
||
"FOR" var ":=" exp "TO" exp [ "STEP" num ] eol
|
||
stats "ENDPROC" |
|
||
"WHILE" exp eol stats "ENDWHILE" |
|
||
"REPEAT" eol stats "UNTIL" exp |
|
||
"SELECT" var eol
|
||
( "CASE" exp eol stats )+
|
||
[ "DEFAULT" eol stats ]
|
||
"ENDSELECT" |
|
||
"LOOP" eol stats "ENDLOOP"
|
||
|
||
explist = { exp "," }+
|
||
exp = [ "-" ] { item binop }+ |
|
||
exp "BUT" exp
|
||
item = num | fnum | lval | stringconst | charconst |
|
||
"SIZEOF" objident |
|
||
"IF" exp "THEN" exp "ELSE" exp |
|
||
"[" explist "]" [ ":" ptrtype ] |
|
||
( builtin | ident ) "(" explist ")" |
|
||
var ":=" exp |
|
||
"{" ident "}" |
|
||
"`" exp |
|
||
binop = mathop | compop | logop
|
||
mathop = "+" | "-" | "*" | "/"
|
||
compop = "=" | "<>" | ">" | "<" | ">=" | "<="
|
||
logop = "AND" | "OR"
|
||
constexp = [ "-" ] { num ( "+" | "-" | "*" | "/" ) }+
|
||
lval = var ( "[" [ exp ] "]" | "." ident )* [ "++" | "--" ] |
|
||
"^" var [ "++" | "--" ] |
|
||
var = ident
|
||
defaultarg = num
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_18B
|
||
@title "18B. Tutorial"
|
||
18B. Tutorial
|
||
-------------
|
||
|
||
|
||
NOTE: the original E v2.1b tutorial has been removed, since it
|
||
was not a very useful tutorial, IMHO. Instead, Jason Hulance made
|
||
an extensive E tutorial (Docs/BeginnersGuide/) that you'd definitely
|
||
want to take a look at.
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_18C
|
||
@title "18C. Mapping E to C/C++/Pascal/Ada/Lisp etc."
|
||
18C. Mapping E to C/C++/Pascal/Ada/Lisp etc.
|
||
--------------------------------------------
|
||
|
||
[some new stuff has been added here]
|
||
|
||
In the first/second column I will match E against AnsiC/C++, the third
|
||
column is reserved for a third language. I will mainly use Pascal here,
|
||
but where a feature asks for it, I will use others (for example, LISP
|
||
with quoted expression, Ada with exceptions etc).
|
||
|
||
note well: take these tables with a grain of salt. I'll try to denote
|
||
syntactic equivalences, and semantic properties as well as possible,
|
||
but different languages still need their own evaluation.
|
||
|
||
usage of signs:
|
||
|
||
- = feature not available in language in question.
|
||
? = author has no clue what this feature translates to.
|
||
(or atleast he's not sure).
|
||
... = feature may be available, but no appropriate 1:1 translation
|
||
possible to make it interesting.
|
||
x,y,z = arbitrary identifiers
|
||
e,f,g = arbitrary expressions
|
||
s,t,u = arbitrary statements
|
||
i,j,k = arbitrary integers
|
||
etc.
|
||
|
||
|
||
-----------------------------------------------------------------------
|
||
STRUCTURE/STATEMENTS
|
||
|
||
E C/C++ Pascal
|
||
----------------------- ----------------------- -----------------------
|
||
PROC x() int x() { FUNCTION x:INTEGER;
|
||
PROC x(y,z) int x(y,z) { FUNCTION x(y,z:INTEGER):INTEGER;
|
||
PROC x(y=1) int x(y=1) { -
|
||
ENDPROC return 0; }; x:=0; END;
|
||
ENDPROC e return e; }; x:=e; END;
|
||
ENDPROC e,f,g - -
|
||
RETURN e return e; ?
|
||
|
||
IF e if(e) { IF e THEN BEGIN
|
||
ELSEIF e } else if(e) { END ELSE IF e THEN BEGIN
|
||
ELSE } else { END ELSE BEGIN
|
||
ENDIF }; END;
|
||
IF e THEN s if(e) s; IF e THEN s;
|
||
IF e THEN s ELSE t if(e) s else t; IF e THEN s ELSE t;
|
||
|
||
FOR x:=e TO f - (1) FOR x:=e TO f DO BEGIN
|
||
FOR x:=e TO f STEP i - - (2)
|
||
EXIT e if(e) break; -
|
||
ENDFOR - END;
|
||
FOR x:=e TO f DO s - FOR x:=e TO f DO s;
|
||
|
||
WHILE e while(e) { WHILE e DO BEGIN
|
||
EXIT e if(e) break; -
|
||
ENDWHILE }; END;
|
||
WHILE e DO s while(e) s; WHILE e DO s;
|
||
|
||
s; WHILE e for(s;e;u) { s; WHILE e DO BEGIN
|
||
t; u t; t; u
|
||
ENDWHILE }; END;
|
||
|
||
REPEAT do { REPEAT
|
||
UNTIL e } while(!e); UNTIL e;
|
||
|
||
LOOP for(;;) { WHILE TRUE DO BEGIN (?)
|
||
ENDLOOP }; END;
|
||
|
||
SELECT x switch(x) { CASE x OF
|
||
SELECT x OF y switch(x) { CASE x OF
|
||
CASE 1; s... case 1: s...; break 1: BEGIN s... END
|
||
CASE a+1 - -
|
||
CASE 1,2,3 case 1: case 2: case3: 1,2,3:
|
||
CASE "a".."z" - -
|
||
ENDSELECT }; END
|
||
|
||
INC x x++; x:=x+1; (INC())
|
||
DEC x x--; x:=x-1; (DEC())
|
||
JUMP lab goto lab; GOTO lab;
|
||
x:=e x=e; x:=e;
|
||
|
||
/* */ /* */ { }
|
||
-> // -
|
||
|
||
(1) see WHILE; C has no FOR, "for" in C is another way of writing "while"
|
||
(2) only STEP -1 as DOWNTO
|
||
|
||
|
||
-----------------------------------------------------------------------
|
||
VALUES
|
||
|
||
E C/C++ Pascal
|
||
----------------------- ----------------------- -----------------------
|
||
1 1 1
|
||
1.0 1.0 1.0
|
||
$1 0x1 ?
|
||
%1 ? ?
|
||
"a" 'a' chr(97) (?)
|
||
'blabla' "blabla" 'blabla'
|
||
[1,2,3] - (1) -
|
||
[1,2,3]:INT - -
|
||
|
||
(1) in translating from E to C, you can often simulate them with:
|
||
|
||
myfunc([1,2,3])
|
||
|
||
becomes:
|
||
|
||
int dummy [] = {1,2,3};
|
||
myfunc(dummy);
|
||
|
||
|
||
-----------------------------------------------------------------------
|
||
OPERATORS
|
||
|
||
E C/C++ Pascal
|
||
----------------------- ----------------------- -----------------------
|
||
+ - * / + - * / + - * DIV
|
||
= <> > < >= <= == != > < >= <= = <> > < >= <=
|
||
AND OR (log) && || and or
|
||
AND OR (bit) & | ?
|
||
SIZEOF x sizeof(x) -
|
||
`e - - (1)
|
||
^x (4) *x ...
|
||
{x} &x ...
|
||
x++ x++ -
|
||
x-- --x -
|
||
-x -x -x
|
||
IF e THEN f ELSE g e ? f : g -
|
||
x.y x->y x^.y
|
||
- x.y x.y
|
||
x.y.z x->y->z x^.y^.z
|
||
x:=e x=e -
|
||
e BUT f (e,f) -
|
||
x[] x[0] *x (2) x[0]
|
||
x[1] x[1] x[1]
|
||
x[1] (3) &x[1] ?
|
||
x[1].y x[1]->y x[1]^.y
|
||
x[]++ *x++ -
|
||
x[1].y++ *(x+1)++ -
|
||
x::y.a ((y *)x)->a -
|
||
x.y::z.a ((z *)x->y)->a -
|
||
|
||
(1) see QUOTED EXPRESSIONS
|
||
(2) also for others, equivalences between *(x+e) and x[e] hold.
|
||
(3) if ARRAY OF <object>
|
||
(4) ONLY for giving by reference. otherwise: "[]"
|
||
|
||
-----------------------------------------------------------------------
|
||
CONSTANTS/TYPES
|
||
|
||
E C/C++ Pascal
|
||
----------------------- ----------------------- -----------------------
|
||
CONST X=1 #define X 1 CONST X=1;
|
||
const int X=1;
|
||
ENUM X,Y,Z #define X 0 (etc.) TYPE x=(X,Y,Z);
|
||
enum x{X,Y,Z};
|
||
SET X,Y,Z - TYPE x=SET OF (X,Y,Z);
|
||
|
||
DEF VAR
|
||
x int x; (or: long x;) x:INTEGER;
|
||
x:LONG int x; x:INTEGER;
|
||
x:PTR TO y struct y* x; x:^y;
|
||
x:y struct y x; x:y;
|
||
x[10]:ARRAY OF y struct y x[10]; x:ARRAY [0..9] OF y;
|
||
x[10]:STRING - (1) x:STRING[10]; (2)
|
||
x[10]:LIST - (1) - (1)
|
||
|
||
x:REG register int x;
|
||
|
||
OBJECT x struct x { (3) TYPE x = RECORD
|
||
y:CHAR,z:INT char y; short z; y:CHAR; z:INTEGER;
|
||
ENDOBJECT }; END;
|
||
|
||
(1) when translating from E to C, simulate with an array of char/int resp.,
|
||
and do your own range-checking etc.
|
||
(2) no Wirth Pascal, but available in all popular dialects.
|
||
(3) or public class.
|
||
|
||
|
||
-----------------------------------------------------------------------
|
||
QUOTED EXPRESSIONS
|
||
|
||
E LISP MIRANDA
|
||
----------------------- --------------------------- -------------------
|
||
`e (QUOTE e) 'e (3)
|
||
(LAMBDA () e) (1)
|
||
`x+y '(+ x y)
|
||
Eval(`e) (EVAL `e)
|
||
ForAll(v,l,`e) - (2)
|
||
MapList(v,l,l,`e) (MAPCAR (LAMBDA (V) E) L) map (\\v->e) l
|
||
|
||
example:
|
||
|
||
E: MapList({x},[1,2,3,4],a,`x*x)
|
||
MIRANDA: map (\\x->x*x) [1,2,3,4]
|
||
LISP: (MAPCAR (LAMBDA (X) (* X X) `(1 2 3 4))
|
||
|
||
(1) really QUOTE, but sometimes used where in LISP LAMBDA would be
|
||
used, like in MapList()
|
||
(2) not even in ProLog, see other logical languages.
|
||
(3) lazyness would be used instead here
|
||
|
||
-----------------------------------------------------------------------
|
||
UNIFICATION AND LISP CELLS
|
||
|
||
E LISP PROLOG
|
||
----------------------- ----------------------- -----------------------
|
||
<1|2> (1 . 2) [1|2]
|
||
<1,2,3> (1 2 3) [1,2,3]
|
||
<1,2|3> (1 2 . 3) [1,2|3]
|
||
|
||
E HASKELL PROLOG
|
||
----------------------- ----------------------- -----------------------
|
||
e <=> <x|y> (x:y) = e e = [X|Y]
|
||
e <=> <1,2,x> [1,2,x] = e e = [1,2,X]
|
||
e <=> [1,x] - -
|
||
|
||
|
||
-----------------------------------------------------------------------
|
||
EXCEPTIONS
|
||
|
||
E C++ ADA
|
||
----------------------- ----------------------- -----------------------
|
||
PROC x() HANDLE int x() { try { function x is begin
|
||
EXCEPT } catch (exc) { (1) exception
|
||
EXCEPT DO - -
|
||
ENDPROC }}; end x;
|
||
|
||
Raise(e) throw e; raise e;
|
||
Throw(e,f) ? -
|
||
ReThrow() throw e; raise e;
|
||
RAISE "MEM" IF New()=0 - - (2)
|
||
|
||
|
||
(1) catch handles only one specific exception, it's quite different
|
||
from general exception handlers as used in E.
|
||
(2) the runtime system does raise some exceptions, but I'm not sure
|
||
whether automatically raised exceptions can be _defined_ in Ada.
|
||
|
||
|
||
-----------------------------------------------------------------------
|
||
OBJECT ORIENTED PROGRAMMING
|
||
|
||
E C++
|
||
------------------------------- -----------------------
|
||
OBJECT x class x {
|
||
OBJECT x OF y class x : y {
|
||
self.i this->i
|
||
PROC a OF x IS self.i virtual int x::a() { return i; }
|
||
- int x::a() { return i; }
|
||
PROC a OF x IS EMPTY virtual int x::a() =0
|
||
PUBLIC public:
|
||
a.method(1) a->method(1)
|
||
SUPER a.method(1) a->supername::method(1)
|
||
|
||
[see also next part under NEW]
|
||
|
||
-----------------------------------------------------------------------
|
||
BUILTIN FUNCTIONS AND MEMORY ALLOCATION
|
||
(only a few are presented here, as an example)
|
||
|
||
E C/C++ Pascal
|
||
----------------------- ----------------------- -----------------------
|
||
WriteF(fs,...) printf(fs,...); WriteLn(a,b,...);
|
||
cout << a << b ... ;
|
||
|
||
ReadStr(f,s) scanf(fs,...) ReadLn(s)
|
||
Val(s) Val()
|
||
cin >> s;
|
||
|
||
StrCopy(s,s,n) (1) strcpy(s,s) s:=s; (2)
|
||
|
||
Mod(e,e) e%e e MOD e
|
||
Shl(e,n) e<<n Shl()
|
||
Long(e) - -
|
||
|
||
|
||
p:=New(e) p=malloc(e); New(p);
|
||
NEW p p=new type;
|
||
NEW p.constr() p=new constr() -
|
||
NEW [e,f,g] - -
|
||
Dispose(p) free(p); Dispose(p);
|
||
END p delete p;
|
||
|
||
|
||
(1) when translating from C, make sure you turn the arrays of char into
|
||
proper STRINGs.
|
||
(2) dunno what function is needed in the pointer case.
|
||
|
||
|
||
@endnode
|
||
@node CH_18D
|
||
@title "18D. Amiga E FAQ"
|
||
18D. Amiga E FAQ
|
||
----------------
|
||
[94_10_1..94_12_1]
|
||
|
||
This FAQ-list (Frequently Asked Questions) was compiled by looking through
|
||
old email and gathering those questions which keep popping up.
|
||
|
||
|
||
|
||
Compiler/Linking/Executables
|
||
|
||
- Will EC generate code for PowerPC Amiga's, if they become available?
|
||
|
||
Likely, though don't pin me down on this. I have an idea on how to
|
||
adapt EC for PPC, and maybe other processors/languages as well.
|
||
When, where and how this is going to happen depends, and you'll just
|
||
have to wait. A project like this easily requires a year's worth
|
||
of hard work, so if I'm to go through with it, it'd better be worth it.
|
||
That of course depends heavily on the future of the Amiga.
|
||
|
||
- How can I link E code with other languages? / use standard object (.o) format?
|
||
|
||
Converting between .m and .o really isn't a huge problem, as one can see
|
||
from the o2m utility, which allows cooperation between E and Macro-Assembly
|
||
quite marvelously. The problem with C is in the compiled code, not the file
|
||
format. Anything but trivial C will reference things like _DOSBase, link-
|
||
library functions, stack-bases or other things from startup-code that E
|
||
provides in a quite different way, for example E's startup code is very
|
||
different from that of C compilers, and dosbase in E is placed on the stack,
|
||
not in the exe as with C. Even two C compilers may have these problems.
|
||
If you can get your C compiler to deliver a .o without external references
|
||
etc., you can link it with E (I did this once with MaxonC++). If you need
|
||
to program a project in both E and another language, you may wish to look
|
||
at E's (or the other language's) library linking facilities. E is really
|
||
quite a bit different from other languages (e.g. C), and providing .o
|
||
linking facilities only makes sense if the code contained in the .o
|
||
files can cooperate, which isn't the case with E.
|
||
|
||
- Can I link amiga.lib?
|
||
|
||
Currently no. To a lesser extend this has the same problems as .o files.
|
||
Many functions from amiga.lib have already been "converted" to E modules.
|
||
(see
|
||
|
||
- Can E code be made resident?
|
||
|
||
Nearly ALL E code is already resident-able without the help of the
|
||
programmer. As fas as I know, the only E construct that can violate this is
|
||
a static list [] with an expression in it (so [a], for example. [1] or
|
||
NEW [a] is ok.).
|
||
|
||
- I have this application with pieces of inline asm, and after
|
||
OPTI/S it behaves weird. what is going on?
|
||
|
||
Inline asm may use the same registers as the register optimizer does.
|
||
check E.doc for this.
|
||
|
||
- I wrote X in both C and E, and the E version is Y times slower/faster.
|
||
how come?
|
||
|
||
It's not easy to compare both in a fair way. Often it may be the case that
|
||
one of the two has more optimized routines for something (string handling,
|
||
I/O etc.). Use of a profiler can reveal this. If the code in question only
|
||
does calculations, there's no reason why any of the two should be
|
||
significantly slower than the other, if used properly.
|
||
|
||
- I have written this nice 'myutils.m', only when I use one function
|
||
from it, EC links all. I don't like that.
|
||
|
||
The style of writing modules in E is to keep them as relatively small units,
|
||
with only related code and data. (This makes more sense if you know that the
|
||
module is the unit of datahiding in E). Split it up!
|
||
|
||
- Can I make EC resident?
|
||
|
||
no, currently not. EC itself is still written in old-fashioned assembly
|
||
style, and cannot be made resident ;-)
|
||
|
||
- Can we have an option for EC to generate assembly source instead of an
|
||
executable?
|
||
|
||
No. There's no intermediate level of assembly in EC, it generates code
|
||
directly. Using a disassembler is as close as you will get.
|
||
|
||
- I have been disassembling some code generated by EC, and I think this
|
||
bit of code here can be optimized to this bit of code.
|
||
|
||
Sure it can, I can see that too. But EC is not an assembly programmer.
|
||
Doing really very high quality optimisations would require a complete
|
||
rewrite of EC, and would make it a lot slower, even with optimisation off.
|
||
Writing a good optimizer for a compiler is quite a non-trivial operation,
|
||
and if some EC generated code is sub-optimal or downright silly doesn't
|
||
mean I must be amateur assembly programmer too, and you need to explain me
|
||
how its done.
|
||
|
||
- E should have a CHIP keyword or somesuch, to put data areas in chip memory.
|
||
|
||
I don't find this worth it. If the amount of data is small (a gadget image),
|
||
it's no waste doing a CopyMem(). If it's large (an iff picture, a sample),
|
||
what's it doing in the executable in the first place? load it directly to
|
||
chip mem from disk.
|
||
|
||
|
||
Resources
|
||
|
||
- There's no explanation in the docs of all those handy functions from
|
||
intuition.library etc. How come?
|
||
|
||
The functions are not part of E, rather they are part of the amiga OS.
|
||
As such, they are described by Commodore's documents, not by the E documents.
|
||
"The AMIGA ROM Kernal Reference Manuals" are a series of books published
|
||
by Addison Wesley. A good place to start is for example "Libraries", ISBN
|
||
0-201-56774-1. Other books are worth considering too, an example is "The
|
||
Amiga Guru Book" by Ralph Babel. All the C code you come across in the
|
||
RKRMs is available in E form in Src/RKRM/
|
||
|
||
- I'd like to read more source code than what comes with the distribution
|
||
where should I look?
|
||
|
||
There are lots of places too look, but the best is without doubt Aminet
|
||
(A collection of FTP-sites on the internet), for example ftp.luth.se.
|
||
In the directory 'pub/aminet/dev/e' you'll find all sorts of E related
|
||
stuff. Some BBS'es outside of the internet also carry Aminet, and there
|
||
are even CDROMs available. Another good place to pick up sources and
|
||
to talk with fellow E programmers is the E mailing list. Send an email
|
||
with 'SUBSCRIBE' in the subject to 'amigae-list@intercom.it' (or
|
||
'UNSUBSCRIBE' if you want get off it again), and any future messages to
|
||
the same address. If you have any problems with the list email
|
||
'fsoft@intercom.it' (Fabio Rotondo, the list administrator). The old list
|
||
at amigae-request@bkhouse.cts.com is no longer operational.
|
||
There are E discussion on other nets as well (FidoNet, AmigaNet),
|
||
public domain disk series (EPD in europe/germany, same address as the german
|
||
registration site), furthermore there are heaps of user-clubs, BBS's etc.
|
||
supporting E these days. just look around in your area.
|
||
|
||
- I have troubles understanding your incomprehensible english. Can I read
|
||
about E in my own language?
|
||
|
||
There are translations of E.guide (some older versions) in german, french,
|
||
spanish and italian, the first three are available on Aminet.
|
||
|
||
- Where can I find any E related sites on the WWW?
|
||
|
||
You can take my home page (http://ecs.soton.ac.uk/~wvo96r/) as a starting
|
||
point. it has an E homepage which has links to a few other E sites.
|
||
|
||
- I have heard about this mailing list. is it any good?
|
||
|
||
Find out for yourself. If you're serious about E it's definitly a must, also
|
||
because it's the place where news on E is generally released first.
|
||
Many good E programmers are on it as well, so you are sure to get the
|
||
right answers to your questions.
|
||
|
||
- I have a hard time getting on the mailing list. Could you take care of that
|
||
for me?
|
||
|
||
I personally don't have anything to do with the administration of the list,
|
||
so I can't help out any better than anyone else can. Remember that the
|
||
server is an automated process, so you need to be very precise in what you
|
||
send there. Don't send administrative post to the list at large, instead
|
||
try and get hold of the administrator (Fabio Rotondo: fsoft@intercom.it).
|
||
|
||
- I'm new to internet. Could you help me get on the mailing list /
|
||
FTP files from Aminet etc...?
|
||
|
||
Stuff like that is way beyond my service. Please try and contact someone
|
||
locally.
|
||
|
||
- How do I know what is the latest version / How do I receive it?
|
||
|
||
If you're on the mailing list you'll be the first to know. Aminet is the
|
||
place where releases and updates are uploaded first.
|
||
|
||
- Can you send me email as soon as a new version is out?
|
||
|
||
No. I used to send all registered users email with new versions, but can't
|
||
do so anymore as the number is too large (last time I tried I jammed our
|
||
network and crashed various workstations).
|
||
|
||
- How do I register?
|
||
|
||
please read E.doc on this.
|
||
|
||
- Does E exist for other platforms?
|
||
|
||
Not Yet. Personally I have undertaken small projects to make portable
|
||
compilers/translators, and several others have E compiler projects on other
|
||
platforms, but nothing has come out of this so far.
|
||
Portability of some form might be available as a spin-off of the PPC
|
||
backend project (see
|
||
|
||
- Can I become an E registration site for country X?
|
||
|
||
Generally I pick sites myself, when I feel the necessity, and know someone
|
||
in that country pretty well.
|
||
|
||
- Can I start an E support BBS / an E programming club...?
|
||
|
||
You can always do that, without asking me. I of course enjoy hearing about
|
||
efforts like this...
|
||
|
||
- Just out of curiosity, how many registered E programmers are there?
|
||
|
||
This changes all the time. Inquire within :-)
|
||
|
||
|
||
Programming
|
||
|
||
- Can I do X in E? (where X = {games,dtp-packages,...})
|
||
|
||
E is a general purpose programming language, so there shouldn't be any type
|
||
of program that can't be done in E (with few rare exceptions, which are covered
|
||
by E's inline asm). This doesn't mean E is especially equipped for certain
|
||
types of programs, i.e. E has no special functions for games (though its
|
||
extensibility makes it easy to add them).
|
||
|
||
- How do I create X in E (where X = {window, interupt handler,...})
|
||
|
||
as with the last question, E just opens the possibilities to write anything,
|
||
and there isn't always a specific function available. In the worst case this
|
||
means having to use difficult functions from spooky libraries, but it's
|
||
worth it getting to grips with that. If you're lucky some E programmer
|
||
already has done something similar which you can learn from.
|
||
|
||
- Please write me an example how to do X in E.
|
||
|
||
Please try to figure it out yourself first, or ask other E programmers to
|
||
help out.
|
||
|
||
- The compiler refuses to compile things like myscreen.rastport.bitmap,
|
||
eventhough x.y.z is generally possible. Why is that?
|
||
|
||
Thanks to jasons v40 modules this should not be a problem anymore.
|
||
The only case where this can still occur is if you do not include the
|
||
module that defines the object "z" is in.
|
||
|
||
- I'm writing code like this:
|
||
|
||
DEF s[100]:STRING
|
||
s:='my beautiful string'
|
||
|
||
which is allowed by the compiler, but later gives me problems. What can possibly
|
||
be wrong with this?
|
||
|
||
If you're used to for example BASIC, you're used to handle strings in the
|
||
same way as integers, as they are both _values_. In E however there are no
|
||
real string variables in the BASIC sense, the DEF above creates a piece of
|
||
memory to hold a string, then sets the variable as a pointer to this memory.
|
||
The pointer and the memory are implementation-wise two unrelated entities.
|
||
From that DEF on, all operation that directly access 's' access the pointer,
|
||
which is just an integer which tells us were to find the actual string.
|
||
The assignment thus puts the address of 'my beautiful string' into 's',
|
||
overwriting the old value. The string-memory created by the DEF sits there
|
||
unaltered, and now unaccessable because no pointer points to it. Functions
|
||
like StrCopy() can use this pointer to find the real memory, and can fill
|
||
it:
|
||
|
||
StrCopy(s,'my beautiful string')
|
||
|
||
is correct. Of course assigning strings as pointers also has a use, for
|
||
example if you just want to read the string data and do nothing else with
|
||
it. then:
|
||
|
||
DEF s:PTR TO CHAR
|
||
|
||
allocates no memory for a string, only the pointer. the assignment above
|
||
would now make sense. This all boils down to the differences between
|
||
pointers and values (or value/reference semantics), and is important to
|
||
understand to succesfully program in E.
|
||
|
||
- How do I return a STRING / OBJECT etc. from a function?
|
||
|
||
depends. If you just wish to use it as temporary return value, give a string
|
||
as argument and let the routine fill it. If you need to create a NEW string/
|
||
object, allocate it dynamically within the function, and return the pointer.
|
||
|
||
- can I create an array of typed pointers, eg ARRAY OF PTR TO LONG
|
||
|
||
um... no. you'll have to use an ARRAY OF LONG instead.
|
||
|
||
|
||
<more of these specific questions are to be added>
|
||
|
||
|
||
|
||
Bugs
|
||
|
||
- I remember programming something when suddenly the compiler crashed/
|
||
misbehaved/produced an internal error/gave wrong error messages. shouldn't
|
||
you be doing something about this?
|
||
|
||
If I have no clue where to look for a bug I can't fix it. If you stumble
|
||
across something that you're sure of is a compiler bug, make a copy of
|
||
the source that reproduces the bug (possibly cut the source down while
|
||
making sure it still shows the bug) and send it to me with as much info
|
||
as possible. EC-enforcer hits for example are very useful.
|
||
|
||
- I wrote program X, but it crashes. I'm positive I made no mistakes,
|
||
so certainly this must be a compiler bug.
|
||
|
||
due to E's untyped-ness, you can never be quite sure you're code is
|
||
correct. Most code send to me with comments like the above later proved
|
||
to be errors in the code, mostly due to lack of E knowledge. Read the
|
||
docs, and use EDBG!
|
||
|
||
|
||
|
||
Future Features
|
||
|
||
Will E have...
|
||
- Initialisation for globals in modules?
|
||
- Unaligned object members?
|
||
- Bitfields?
|
||
- Startup code that doesn't open intuition etc.?
|
||
- A feature to automatically include all modules used by a module?
|
||
- Access to object members in methods without the `self.' prefix?
|
||
- DEFs halfway PROCs or local to loops etc.?
|
||
- Disassembly mixed mode for EDBG?
|
||
- Sourcecode level #include?
|
||
- Unsigned INT/LONG or signed CHARs?
|
||
- Multiple statements in macros?
|
||
- Multiple error messages?
|
||
- Dereferenced variables in lefthandsides of assignment expressions,
|
||
multiple returnvalue assignments, and the {} operator?
|
||
- Inlined code for small functions (e.g. Shl())?
|
||
|
||
No. Or atleast very unlikely.
|
||
|
||
- Registerised arguments?
|
||
|
||
It surely is possible but like many things it's not a priority for me.
|
||
|
||
- Library linker?
|
||
|
||
Yes, it's included in this version.
|
||
|
||
- Multiple Inheritance?
|
||
|
||
Not possible in E. MI breaks with structural compatability between objects,
|
||
and needs relatively strong typing to resolve this.
|
||
|
||
- PROCs inside PROCs?
|
||
|
||
Not likely.
|
||
|
||
- multidimensional arrays?
|
||
|
||
Not likely either. In E there isn't really something like an array, just
|
||
pointers that point to a large amounts of equally sized objects. To have
|
||
twodimensional arrays, one needs to have the concept of 'size' of an array,
|
||
which E doesn't have.
|
||
|
||
- Overloading
|
||
|
||
No. Overloading requires quite strong typing to distinguish which function
|
||
to call. Besides, overloading is 100% syntactic sugaring, it doesn't provide
|
||
any added functionality whatsoever.
|
||
|
||
- more 020/881 support?
|
||
|
||
Yes, eventually. It just isn't at the top of my TODO-list.
|
||
|
||
- assembly source output?
|
||
|
||
No. Unlike other compilers, E never passes an assembly stage, E code is
|
||
compiled directly to opcodes. Providing this feature would boil down
|
||
to doing a disassembly. E's inline assembler is not used for generating
|
||
code for E constructs. (see
|
||
|
||
- C compatible syntax?
|
||
|
||
Often people are used to some type of language (mostly C), and don't
|
||
understand why a programming language design isn't as configurable as a
|
||
directory utility or text editor:
|
||
'I like "=="/"=" better than "="/":="'
|
||
'Can't you switch """ and "'" ?'
|
||
'Why doesn't "--" work as in C?'
|
||
'I hate typing CAPS for keywords!'
|
||
'No precedence sucks!'
|
||
The features 'referenced' above will never change, and you'd better get use
|
||
to them. All design decisions have good reasons, and unlike some rumours
|
||
they were not done for compiler speed (infact implementing them more
|
||
traditionally wouldn't make the compiler any slower).
|
||
|
||
- Why is feature X in E not like language Y, which I find better.
|
||
|
||
Like above, the design of a language isn't changed just because of
|
||
personal preference. If you have strong ideas of what a language should
|
||
look like according to you, and those ideas stem from language Y, then
|
||
use Y. If you can't find your ideas in any existing language, then it's
|
||
time to design and implement your own! (I'm not kidding, it's worth the
|
||
trouble! and you can always start with E... :-)
|
||
|
||
- X maybe?
|
||
|
||
Having seen literally hundreds of programming languages, your chances of
|
||
suggesting a new feature for E which I haven't thought of before are not
|
||
too bright. Many things don't 'fit' into E, and in general time is limited
|
||
to add more fancy stuff. (If you look closely, the amount of features in
|
||
E is already quite high for an average programming language). I have quite
|
||
a huge list of possible features for E, and eventually some of these will
|
||
be implemented. just wait and see...
|
||
|
||
|
||
|
||
|
||
@endnode
|
||
@node CH_18E
|
||
@title "18E. TODO/BUG list"
|
||
18E. TODO/BUG list
|
||
------------------
|
||
By popular demand: This is an extract from my todo/bugs list for E. The real
|
||
list is much longer, since I left all the real details/unimportant stuff out.
|
||
Don't go and ask why any of these items haven't been done yet; the answer will
|
||
generally vary from the the tactical "due to the internal structure of the
|
||
compiler fixing this bug is more work than the collective trouble it will ever
|
||
cause" to the honest "I'm too lazy: I can't be bothered" :) Don't go and ask
|
||
me "when?" either.
|
||
|
||
|
||
Bugs I haven't been able to reproduce:
|
||
* compilation problems on 060
|
||
* broken Lock in combination with MCX
|
||
|
||
Bugs still not fixed:
|
||
* dereferencing an untyped OBJECT member is not properly interpreted
|
||
as "PTR TO CHAR". Easy workaround: Give the member a type.
|
||
* debug hunks don't contain directory information for projects
|
||
split up over sub directories.
|
||
* error reports can sometimes be off by several lines.
|
||
* error reports for inline assembly often doesn't work.
|
||
* unreferenced check too conservative in some cases.
|
||
* doesn't detect two library functions with the same names.
|
||
* constant folding optimizer often misbehaves, disallowing
|
||
you to do things like `1+SIZEOF o' in constant expressions
|
||
or 1+a as list-elements.
|
||
* internal linker problem doesn't allow for private methods.
|
||
* strings in lists cannot be joined by "+" (optimizer problem).
|
||
* StrCopy/MidStr don't fill dest with empty-string if len=0
|
||
* methods and procs in one module cannot have the same name
|
||
(other than by inheritance).
|
||
* Exists() evaluates all regardless.
|
||
* SUPER doesn't check wether the superclass actually has such
|
||
a method.
|
||
* recompiling a module that is being used at the same time
|
||
by another invocation of EC can give problems.
|
||
|
||
Future Todo's
|
||
* fix above bugs :)
|
||
* extend CONST expressions with floats etc.
|
||
* find a way to split up executables in more hunks
|
||
* add symbols for methods
|
||
* lots of extensions to the optimizer
|
||
* proper 020+ support
|
||
* improve error reporting
|
||
* class variables
|
||
* short-circuit AND / OR
|
||
|
||
Wild Ideas (aka Big Todo's)
|
||
* plug a general back-end onto EC (to allow for code
|
||
generation to other cpus (e.g. PPC) or even languages
|
||
(e.g. C) or better optimizers etc.)
|
||
* extend the language with lambda's / higher order
|
||
functions / lazyness etc.
|
||
* concurrency system based on Linda
|
||
* better debugging / analysis / type inference tools
|
||
* full visual language on top of E
|
||
|
||
|
||
|
||
-------------------------------EOF--------------------------------------
|
||
@endnode
|