-
Notifications
You must be signed in to change notification settings - Fork 4
EVO Wiki
Amiga E-VO v3.6.1 Amiga E-VO v3.6.1
+-----------------------------------------------+
| |
| Amiga E-VO v3.6.1 |
| Compiler for The E Language |
| Originally By Wouter van Oortmerssen |
| Updates By GRIO 1999-2003 |
| and Darren Coles 2022 / 2023 |
| |
+-----------------------------------------------+
Contents:
0. compiler and introduction
[A. introduction](#ch_0a)
[B. the distribution](#ch_0b)
[C. installing/using the compiler](#ch_0c)
[D. changes / history / new features](#ch_0d)
[E. additional information](#ch_0e)
1. format
[A. tabs,lf etc.](#ch_1a)
[B. comments](#ch_1b)
[C. identifiers and types](#ch_1c)
2. immediate values
[A. decimal (1)](#ch_2a)
[B. hexadecimal ($1)](#ch_2b)
[C. binary (%1)](#ch_2c)
[D. float (1.0)](#ch_2d)
[E. character](#ch_2e)
[F. strings ('bla')](#ch_2f)
[G. lists (\[1,2,3\]) and typed lists](#ch_2g)
[H. lisp-cells (<a|b>)](#ch_2h)
3. expressions
[A. format](#ch_3a)
[B. precedence and grouping](#ch_3b)
[C. types of expressions](#ch_3c)
[D. function calls](#ch_3d)
4. operators
[A. math (+ - \* /)](#ch_4a)
[B. comparison (= <> > < >= <=)](#ch_4b)
[C. logical and bitwise (AND OR NOT << and >>)](#ch_4c)
[D. unary (SIZEOF OFFSETOF \` ^ {} ++ -- -)](#ch_4d)
[E. triple (IF THEN ELSE)](#ch_4e)
[F. structure (.)](#ch_4f)
[G. array (\[\])](#ch_4g)
[H. float operator (!)](#ch_4h)
[I. assignments expressions (:=)](#ch_4i)
[J. sequencing (BUT)](#ch_4j)
[K. dynamic memory allocation (NEW)](#ch_4k)
[L. unification (<=>)](#ch_4l)
[M. pointer typing (::)](#ch_4m)
[N. swap (:=:)](#ch_4n)
[O. quick compare (==)](#ch_4o)
[P. increment decrement etc (+= -= etc)](#ch_4p)
5. statements
[A. format (;)](#ch_5a)
[B. statement labels and gotos (JUMP)](#ch_5b)
[C. assignment (:=)](#ch_5c)
[D. assembly mnemonics](#ch_5d)
[E. conditional statement (IF)](#ch_5e)
[F. for-statement (FOR)](#ch_5f)
[G. while-statement (WHILE)](#ch_5g)
[H. repeat-statement (REPEAT)](#ch_5h)
[I. loop-statement (LOOP)](#ch_5i)
[J. select-case-statement (SELECT)](#ch_5j)
[K. increase statement (INC/DEC)](#ch_5k)
[L. void expressions (VOID)](#ch_5l)
[M. memory deallocation (END)](#ch_5m)
[N. generate an error or warning (FATAL/WARN)](#ch_5n)
[O. code and data sections (SECTION)](#ch_5o)
[P. Negative conditions/loops (IFN/ELSEIFN etc)](#ch_5p)
6. function definitions and declarations
[A. proc definition and arguments (PROC)](#ch_6a)
[B. local and global definitions: scope (DEF)](#ch_6b)
[C. endproc/return](#ch_6c)
[D. the 'main' function](#ch_6d)
[E. built-in system variables](#ch_6e)
[F. default arguments to functions](#ch_6f)
[G. multiple return values](#ch_6g)
[H. function values](#ch_6h)
7. declaration of constants
[A. const (CONST)](#ch_7a)
[B. enumerations (ENUM)](#ch_7b)
[C. sets (SET)](#ch_7c)
[D. built-in constants](#ch_7d)
8. types
[A. about the 'type' system](#ch_8a)
[B. the basic type (LONG/PTR)](#ch_8b)
[C. the simple type (CHAR/INT/LONG)](#ch_8c)
[D. the array type (ARRAY)](#ch_8d)
[E. the complex type (STRING/LIST)](#ch_8e)
[F. the compound type (OBJECT)](#ch_8f)
[G. initialisation](#ch_8g)
[H. the essentials of the E typesystem](#ch_8h)
9. built-in functions
[A. io functions](#ch_9a)
[B. strings and string functions](#ch_9b)
[C. lists and list functions](#ch_9c)
[D. intuition support functions](#ch_9d)
[E. graphics support functions](#ch_9e)
[F. system support functions](#ch_9f)
[G. math and other functions](#ch_9g)
[H. string and list linking functions](#ch_9h)
[I. lisp-cells and cell functions](#ch_9i)
[J. boopsi functions](#ch_9j)
10. library functions and modules
[A. built-in library calls](#ch_10a)
[B. interfacing to the amiga system with the v47 modules](#ch_10b)
[C. compiling own modules](#ch_10c)
[D. the modulecache](#ch_10d)
11. quoted expressions
[A. quoting and scope](#ch_11a)
[B. Eval()](#ch_11b)
[C. built-in functions](#ch_11c)
12. floating point support
[A. float values](#ch_12a)
[B. computing with floats](#ch_12b)
[C. builtin float functions](#ch_12c)
[D. float implementation issues](#ch_12d)
13. Exception handling
[A. defining exception handlers (TRY/CATCH or HANDLE/EXCEPT)](#ch_13a)
[B. using the Raise() function](#ch_13b)
[C. defining exceptions for built-in functions (RAISE/IF)](#ch_13c)
[D. use of exception-ID's](#ch_13d)
14. OO programming
[A. OO features in E](#ch_14a)
[B. object inheritance](#ch_14b)
[C. data hiding (EXPORT/PRIVATE/PUBLIC)](#ch_14c)
[D. methods and virtual methods](#ch_14d)
[E. Constructors, Destructors and Super-Methods (NEW,END,SUPER)](#ch_14e)
[F. Unions](#ch_14f)
15. inline assembly
[A. identifier sharing](#ch_15a)
[B. the inline assembler compared to a macro assembler](#ch_15b)
[C. ways using binary data (INCBIN/CHAR..)](#ch_15c)
[D. OPT ASM](#ch_15d)
[E. Inline asm and register variables](#ch_15e)
16. technical and implementation issues
[A. the OPT keyword](#ch_16a)
[B. small/large model](#ch_16b)
[C. stack organisation](#ch_16c)
[D. hardcoded limits](#ch_16d)
[E. error messages, warnings and the unreferenced check](#ch_16e)
[F. compiler buffer organisation and allocation](#ch_16f)
[G. register allocation](#ch_16g)
17. Essential E Utilities / Applications
[A. ShowModule](#ch_17a)
[B. ShowHunk](#ch_17b)
[C. Pragma2Module / Iconvert](#ch_17c)
[D. ShowCache / FlushCache](#ch_17d)
[E. ecompile.rexx](#ch_17e)
[F. o2m](#ch_17f)
[G. E-Yacc](#ch_17g)
[H. SrcGen](#ch_17h)
[I. EBuild](#ch_17i)
[J. EE / Aprof](#ch_17j)
[K. EDBG](#ch_17k)
[L. E-VO PreProcessor](#ch_17l)
[M. E-VO Library mode](#ch_17m)
18. Appendices
[A. E Grammar description](#ch_18a)
[B. Tutorial](#ch_18b)
[C. Mapping E to C/C++/Pascal/Ada/Lisp etc.](#ch_18c)
[D. Amiga E FAQ](#ch_18d)
[E. TODO/BUG list](#ch_18e)
0A. introduction
Contents | < Browse | Browse >
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 v47 (OS3.2) 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
0B. the distribution
Contents | < Browse | Browse >
The distribution includes:
Bin/ contains the compiler E-VO and the support utilities Modules/ directory containing all Amiga v47 E modules and useful tools as linkable modules Docs/ documentation on E Src/ example sources in E
This distribution of E-VO Amiga E is FreeWare, and may be freely copied. It contains the compiler and modules, example code and various additional tools originally distributed as part of the original Amiga E package which were also released into the public domain.
Distributing copies of E-VO for more than the price of media+postage, or any other way of distribution with only the slightest aim of making profit, is not allowed.
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.
0C. installing/using the compiler
Contents | < Browse | Browse >
To install E-VO 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 which includes the latest OS 3.2 modules and compiler.
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,COMPILESTACK/N/K, IC=IGNORECACHE/S,HOLD/S,WB/S,LINEDEBUG/S,OPTI/S,LEGACY/S,LEGACYINT/S, DEBUG/S,DEBUG50/S,NILCHECK/S,SHOWFNAME/S,DEFINE/M',0
for 1.2 to 2.03:
evo [-opts]
As an example we'll compile the program 'HelloWorld.e'. The compiler will produce an executable 'HelloWorld'.
1> cd e:src E:Src> evo helloworld
E-VO E Compiler/Assembler/Linker/PP v3.4.0-rc1 (14.09.2021) developing as "EC" '1991-97 by Wouter van Oortmerssen developing as "GEC" '1999/2003 by grio developing as "E-VO" '2021 by Darren Coles 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 'EVO ?'. 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 , OPT LARGE) ASM -a puts E-VO into assembler mode (see 15D , OPT ASM). NOWARN -n suppresses warnings (see 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 ). 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, E-VO won't output anything. WB -w puts wb to front (for scripts) SHOWBUF -b shows buffer memory usage information ADDBUF=X -mX forces E-VO to allocate more memory for its buffers. X ranges 1..9, the minimum number of 100k blocks to allocate. default is 1 (never needed). COMPILESTACK Allows you to override the size of the compilestack in case you get workspace full errors. 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 on the commandline before exiting E-VO. OPTI enable all optimisations (currently equals REG=5 and STRMERGE on) LEGACY enable legacy mode - outputs module files in version 10 format and disable the new constants and built in functions that may cause naming clashes with older code. LEGACYINT see OPT LEGACYINT. Enables backward compability with Int() function DEBUG attach debug infos to executable/module (see 17K ) DEBUG50 attach debug infos to executable/module in a way that allows debugging under OS 4.x (see 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) SHOWFNAME Displays the filename of the E file being compiled. DEFINE Equivalent to #DEFINE in your code, defines a macro
example: evo large blabla compiles blabla.e with large model. NOTE: in most cases you won't need to use any of these options
0D. changes / history / new features
Contents | < Browse | Browse >
New in E-VO (3.6.1)
- add checks for object going over 64k
- fix 020 optimised address calculation for array of object (or ptr to
object) indexing
- added EXENAME and DESTDIR command line arguments to override the
output file name.
- fix casing for IeeeDPFloor and IeeeDPCeil
- Extend lists to handle 64k items instead of 32k.
- Fix #else preprocessor parsing issues
New in E-VO (3.6.0) - EDBG: add support for ECX/EEC compiled executables - fix issue with memfill that could cause odd address exception on 68000 - add TRY..CATCH..ENDTRY allowing more control over exception handling - Add UNTILN, WHILEN ELSEWHILEN, IFN, ELSEIFN, EXITN, CONTN which are inverse of their counterparts eg. UNTILN x is equal to UNTIL Not(x) - Add WORD and BYTE types (unsigned 16 bit and signed 8 bit variable types) and Word() Byte() functions as well as PutByte() and PutWord() - Allow SIZEOF object and SIZEOF object.member - Fix issue where SIZEOF x didnt throw a compile error if x was not defined - Added EVO_3_6_0 define - Added Compare, Ucompare, Fcompare, StrCompare functions - Fixed Int() function to sign extend result - Added OPT LEGACYINT to restore old version of Int() command - Added command line switch LEGACYINT/S which does the same as the above OPT - Allow BYTE and WORD types in modules, increase module version to 13. - FindModule: fix error in memory deallocation - FindModule: update for BYTE and WORD types (module version 13) - ShowModule: update for BYTE and WORD types (module version 13)
New in E-VO (3.5.1) - fix issue with short jump calculations being done incorrect in certain circumstances, causing crashes. - fix casing of math ieee functions to match original E modules - disable OPT POOL and OPT UTILLIB in legacy mode - disable THISTASK, __POOL and UTILITYBASE in legacy mode - reinstate [] in immediate list to previous E functionality (a blank 0 item list) - fix optimisation issue where MOVE.B (A0),D0 and MOVE.W (A0),D0 followed by TST.L would be optimised incorrectly. - fix issue with immediate lists, ListMax() could not be used as the max size was not populated. - allow 0 length lists to be created eg List(0) - add showhunk source New in E-VO (3.5.0) - Added ac020 object with pad int to allow optimised 32 bit writes on 020+ courtesy of Samuel D. Crow - When compiling in debug, disable the optimisation that removes LINK, UNLK in procs where not needed (EDBG doesnt step properly when these aren't present) - EDBG: Add case insensitive search - EDBG: Added search next - EDBG: Display variable values in both hex and decimal - Fixed: Clears an extra byte in the String() initialisation causing memory corruption in the case of String(0) - EDBG: Enhanced breakpoints - multiple can now be set and can be seen/set in a column next to the code - EDBG: Support for multiple breakpoints and debugging while code is running. - EDBG: Added support for debugging under OS4.x (eg without using CPU exceptions) - EDBG: Added support for adding watch variables by entering name - Added support for DEBUG50 mode which allows debugging using JSR instructions instead of causing cpu exceptions which allows debugging under OS 4.x - Added support for multi-dimensional arrays - Added support for PTR TO PTR variables - Added EVO_3_5_0 define - Added ? operator (ported from CreativE) - Added OPT FPEXP (fpu floating point support - ported from CreativE) - Added :=: swap operator (ported form CreativE) - Added OPT INLINE (ported from CreativE) - Added OPT NOSTARTUP (ported form CreativE) - Added OPT UTILLIB (ported from CreativE) - Added NOREGS option (ported from CreativE) - Updated module format (now version 12) to account for multi-dimension arrays and PTR TO PTR type in object members - Ported some optimisation code from CreativE - Added ENDPROC WITH function (ported from CreativE) - Added == operator (range check - ported from CreativE) - Added new system constants TAG_DONE, TAG_END, TAG_INORE, TAG_MORE, TAG_SKIP, TAG_USER, OFFSET_BEGINNING, OFFSET_BEGINING, OFFSET_CURRENT, OFFSET_END (ported from CreativE) - Added new utilitybase variable - Added 020+ optimised MOD function - Added support for += -= etc operators (ported from CreativE) - ShowModule: Added support for new module format (version 12) - Added improved LONG,INT,CHAR routines from CreativE which allow expressions - Added improved SIZEOF command that allows SIZEOF objectvar - Allow xNN for hex char in strings - Add ELSEWHILE and ALWAYS extension to WHILE LOOP (from CreativE) - Improvements to SELECT x to allow expressions (from CreativE) - Added LEGACY option to output module format version 10 and disable newer features - Fix: 2 << obj.val was generating corrupt code - Add SECTION (code and data) support - Fix: ReadStr return value was not completely compatible with EC (fix Pragma2Module bug) - ShowModule: Add -e parameter which will force output into a form that can be directly recompiled - Added a scanf module in the other folder - Allow string consts in modules - Allow float CONST definitions - Remove some unused startup code when OPT ASM in use. - ShowModule: Added support for string CONSTs - Added OFFSETOF to get the offset of a member of an object - Added MemFill, AstrClone, ListInsItem, ListRemItem, ListSwapItem functions - EDBG: Support for sections - FindModule: Coded new FindModule application from scratch with support for latest module format. - Make second parameter of SetStr optional and if not specified it will set the calculate the string length automatically - Added EndsWith function eg EndsWith(s,'A') returns TRUE if a string1 ends with string2 - Fix: CHAR '' or CONST STR='' would cause memory corruption - Fix "3*SIZEOF LONG" error - added AstpCopy and UsedStack and StackSize functions New in E-VO (3.4.1) - Fix: Optimisation error where multiple variables set to the same value were not set correctly. - Made the select source file window wider in EDBG to account for the full path being displayed. New in E-VO (3.4.0) - Name change to E-VO - New version numbering 3.4.0 - Add CONT command, similar to EXIT but continues the loop at the top instead of dropping out. - Make default cpu 68000 - Make stack buffer configurable (and default it to 50k) - Fix file handle leak - Fixed breaking defect MIDSTR command - Allow CONT and EXIT commands to be called inside an IF or SELECT - Add ANDALSO and ORELSE short circuit versions of AND and OR - Optimised IF/WHILE/REPEAT expressions and removed TST immediately following MOVEQ - Fixed KickVersion not returning true when version number is exactly equal to the requested version - Allow String(0) which was valid in E v3.3a - Show line numbers for unused variables/labels/procs - Allow new formatting characters u (unsigned long) q (double quote) ! (ascii bell) v (vertical tab) xHH (hex char) (taken from CreativE) - Allow macros to be defined by passing in DEFINE command line parameter (taken from CreativE / ECX) - Allow string consts (taken from CreativE) - Added #date preprocessor (taken from CreativE but enhanced with hours minutes and seconds options) - Added DoMethod, DoMethodA, CoerceMethod, CoerceMethodA, DoSuperMethod, DoSuperMethodA, Set, Sets, Get, Gets, CtrlD, CtrlE, CtrlF, Chk, Eof, Fopen, Fclose, Alloc, Free, PutF, ReadB, WriteB, Size, Lsl, Lsr functions (all taken from CreativE) - We now have optimised versions of WriteF, PrintF, PutF when targetting OS 37. - Added SHOWFNAME command line parameter. - Reverted order of efunctab back to how it was in E to restore compatibility with pre-compiled E modules that are using Mul Div or WriteF. - Added the following functions: LowerChar(c), UpperChar(c) - takes a char and returns a char StrAddChar(s,c) - Adds a single character to an estring ListAddItem(l,i) - Adds a single item to an elist - Immediate assignments for values -256 to 254 for even numbers converted to MOVEQ followed by ADD.L D0,D0 - Optimised NEW [] list creation to remove duplicate moves into D0 eg. NEW [0,0,0,0] - Added OPT PURE to check for non pure code (eg variables in static lists/objects and GetA4 call) - Optimisation: Removed TST.L D0 after EXT.L D0 in IF/WHILE/REPEAT etc conditions. - Optimisation for default values for reg vars into single MOVE instruction. - Optimisation for reg var assignments where moveq is used into a single MOVE instruction where possible. - Added warning when compiling a library that OPT PURE is recommended - Optimisation for reg var assignments where moveq+add is used. - Optimisations with FOR loops to remove redundant move instructions. - Added FATAL 'string' command which generates no code but will fail the compilation (useful for use with #ifdef) - Added WARN 'string' command which generates no code but will show - a warning during compilation - Added GEC_3_4_0 auto generated #define macro which will allow you to check for a minimum version of the compiler. New defines will be additionally created for each new version of the software. - Added #ELSE #ELIFDEF #ELIFNDEF preprocessor commands - Added #UNDEF preprocessor command - Added 020 optimised versions of Long, Int and Char functions - Added StrIns(estring,estring,pos) - insert string function - Added StrRem(estring,pos,len) - remove substring function - Store full path of source files when generating debug hunk (allows EDBG to find the files if they are not all located in the same folder). - v47 (OS3.2) emodules, including all the new reaction interfaces - ARRAY OF CHAR no longer word aligned in OBJECTS - NOT operator works correctly with immediate values (eg "NOT 0" did not work correctly. - MUL/DIV optimisations also working for 020 generated code. - NOALIGN now requires 020 as accessing unaligned INT or LONG will result in exception if you don't have 020. - Fixed optimiser bug in library calls where IF function was used in a parameter that prevented the correct value from being passed. - Fix: x AND 0 was returning incorrect value
New features: (3.3g) - Improved fake stack setting , now is more os compatible ( used exec SwapStack() for OS v37 else old things ). - Added PRIVATE methods in OOP ( PROC bla() OF abc PRIVATE IS EMPTY ) - member can be used as method ( OBJECT abc;a,b;ENDOBJECT -> abc.a(1,2) ) - possible set PROC/label address in static list like this [1,2,main] ( old mode [1,2,{main}] ; is still working ,but code is longer ) - registers can be used as normal variable ( very beta ) - EXTRA keyword for LIBRARY mode (idea taken from CreativE) : LIBRARY namestring,version,revision,versionstring EXTRA value IS ...; now library base can have more variables - PREPROCESS'ing is on at beginnig , OPT PREPROCESS leaved for down compatibility. - Added shift expresion (exp. CONST ABC=1<<4; x:=y<<3 ). - Function added AstringF - like StringF but for normal string's
Fixed Bugs: (3.3g) - Another bugs fixed in "get address " sequence (ex.: {a.c[a]} ). - Problem with definition static OBJECT's and UNION's members. - Small bug in reg exp
New features: (3.3f) -New functions: StrClone(estr) - duplicate estring ListClone(elist) - only long size elements GetA4() - ... -Keyword SAFE for PROC's ( ex: PROC SAFE bla(a,b) ) : result push REG's D2-D7/A2-A6 on the stack . -OPT RUNBG - run background ( 2 vers 4 kick<v37 & kick>=v37 ) . -Modified startup code (to work with or without RUNBG , and fake stack is setup ). -New global var "thistask" . -Modified LIBRARY startup/Open/Close ; tasks never be hash-tabled but tabled by dynamic allocated one linked list . - "//" is a comment . -Added exporting in MODULE arg name for method's ; optional can be switched off by new keyword OLD ( ex: OPT MODULE OLD ); update ShowModule tool for display "new" module .
Fixed Bugs: (3.3f) - Bad reporting in case: OBJECT bla; a,b,c ;PROC zzz() ( or CONST ...) error message "label expected" - now "incorrect object definition" . - Small fixes in head code in LIBRARY output . - Raise() in LIBRARY if PROC isn't have HANDLE'r JUMP to $0 address . - Small bug in geting var address . - Agrrh... nasty bug in close library routine . Resolved. - Fake stack maker bug ( in setting Upper/Lower pointers) .
Optimisations: (3.3f) Some improvements in EAEXP.
New features: (3.3e) -Modified startup code . -REGS var in HANDLER PROC ( only if you define them , no in optimisation ).Becarefull with that this maybe very dangerous if you use REGS var in HANDLE'r. -Using LONG label -> we get reloc of entered label . -Short version ARRAY definition in PROC/OBJECT ( DEF a[10]:CHAR ) . -NOALIGN keyword for OBJECT definition , it's turn off auto align odd members (example : OBJECT blah NOALIGN ). -INC/DEC extension : INC x,20 ( x:=x+20 ) . -ASM mnemonics: MUL(U/S) AND DIV(U/S) - long size EXTB.L -NOT keyword for IF/WHILE/UNTIL expresion . -UNION keyword for OBJECT's definitions : OBJECT abc a:LONG UNION [b:INT,c,d] [ e:CHAR f:LONG g:INT ] ENDUNION k:PTR TO LONG -> or something like that : UNION [ [b:INT,c,d], [e:CHAR,f:LONG, g:INT] ] ENDOBJECT -Added operators : & eq AND || eq OR ~ eq NOT . -Possible assigment in member/list elements ([a.e:=0]) .
Fixed Bugs: (3.3e) -Getting address member if he not defined . -Bad brach set in LIBRARY if OPT CPU not used . -Things like: IF IF x THEN x ELSE x=y THEN y ELSE 0 generating bad code . -"IF" optimiser cut TST after functions/procedures; Example: IF CtrlC() THEN Raise(ERR_BREAK) CODE: BSR CtrlC BEQ xxx .
New features: (3.3d) -In definition OBJECT or var array size can be defined by [4*3+1] -DEF a:REG abc now accepted . -"=>" and ">=" it's the same , "=<" and "<=" it's the same . -Things like that : Func()::blah.a now is possible . -Using OPT 020+ we get improved code : 1. to the startup compiler put test code for entered CPU 2. inline mul/div (*;/) working for long sizes 3. function Mul()/Div() is patched for 020+ 4. EXT.W;EXT.L is changed to EXTB.L .
Fixed Bugs: (3.3d) -Get address member then a first is dereference : {a::abc.c} .
Optimisations: (3.3d) Removed LINK A5,#-XXXX/UNLK A5: if PROC haven't args and vars
Put element to the list:
Was: Is:
LEA xx(PC),A0 PEA xx(PC)
MOVE.L A0,D0
MOVE.L D0,-(A7)
Was: Is:
MOVE.L #xx,D0 PEA xx.(W/L)
MOVE.L D0,-(A7)
Was: Is:
MOVE.L xx(A4/A5)/Dx,D0 MOVE.L xx(A4/A5)/Dx,-(A7)
MOVE.L D0,-(A7)
New features: (3.3c) -String joining in the list by "+" . -Get/set list elements by 1+a . -Function InStri() -> like InStr() but it case sensitive . -Geting address of object member/method: {abc.a[3].z} . -Line number report in branches . -OPT STRMERGE for string merging . -EXPORT ignored if not used in MODULE .
Fixed Bugs: (3.3c) -Bad object reference: OBJECT bla brr ENDOBJECT PROC main() DEF i:bla WriteF('dn',i.brr.brr) ENDPROC -MOVEQ #-128,D0 now accepted . -Default member in object now is PTR TO CHAR . -RAISE error report . -Error report without "]" in DEF a[ . -Library base showed unref if called in other module . -EOR.L #3,D0 changed to EORI.L #3,D0 .
Optimisations: (3.3c) Short instruction for interval (-128;127): Was: Is: MOVE.L xxxx,D0 CMP.L #0,D0 BEQ xxx or TST.L D0 BEQ xxx Was: Is: CMP.L #1,D0 MOVEQ #1,D1 BEQ xxx CMP.L D1,D0 BEQ xxx
Was: Is:
OR.L #-1,D0 MOVEQ #-1,D1
OR.L D1,D0
Was: Is:
SUB.L #25,D0 MOVEQ #25,D1
SUB.L D1,D0
Was: Is:
... ...
MOVEM instrunction:
Was: Is:
MOVEM.L D3,-(A7) MOVE.L D3,-(A7)
MOVEM.L (A7)+,A5 MOVEA.L (A7)+,A5
Start value for variable:
Was: Is:
MOVEQ #0,D0 MOVEQ #0,D0
MOVE.L D0,-$0224(A4) MOVE.L D0,-$0224(A4)
MOVEQ #0,D0 MOVE.L D0,-$0228(A4)
MOVE.L D0,-$0228(A4)
NEW \[1,x,10000\]:
Was: Is:
MOVE.L D0,(A7) MOVE.L D0,(A7)
MOVEQ #1,D0 MOVEA.L D0,A6
MOVE.L (A7),A6 MOVEQ #1,D0
MOVE.L D0,4(A6) MOVE.L D0,4(A6)
MOVE.L x(A5),D0 MOVE.L x(A5),D0
MOVE.L (A7),A6 MOVE.L D0,8(A6)
MOVE.L D0,8(A6) MOVE.L #10000,D0
MOVE.L #10000,D0 MOVE.L D0,12(A6)
MOVE.L (A7),A6
MOVE.L D0,12(A6)
Zero offset to Ax register:
Was: Is:
MOVE.L (A7)+,$0000(A0) MOVE.L (A7)+,(A0)
MOVE.L D0,$0000(A7) MOVE.L D0,(A7)
New features: (3.3b) -Possible to use EXIT in LOOP/ENDLOOP and REPEAT/UNTIL . -Improved functions: StrCopy() StrCmp() StrAdd() MidStr() ForAll() Exitst() SelectList() FileLength() ReadStr() -> much faster MapList() ListCmp() ListAdd() ListCopy() String() OstrCmp() . -New function StriCmp() -> case sensitive version of StrCmp() .
Fixed Bugs: (3.3b) -Functions: Facos() Fsinh() ... .
Optimisations: (3.3b) Better ELSEIF: Was: Is: Scc D0 Bcc xxx EXT.W D0 EXT.L D0 BEQ xxx
Testing data in D0:
Was: Is:
MOVE.L xx(Ax),D0
OR
MOVE.L Dx,D0
TST.L D0 Bcc xxx
Bcc xxx
Stack back:
Was: Is:
JSR xxxPROC/EFUNC
LEA 4(A7),A7 ADDQ.L #4,A7
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 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 ) (!)
- 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 )
- new EDBG with a massive amount of new features! (see 17K )
- enhanced EasyGUI (have a look)
- NEW and END now work on subobjects as well (see 4K ).
- NILCHECK/S, will insert code to check all "." and "[]" dereferences for NIL pointers (see 0C )
- 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 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.
v3.3b 20.11.1999 -> 12.12.1999 v3.3c 15.12.1999 -> 12.01.2000 v3.3d 21.01.2000 -> 02.03.2000 v3.3e 05.04.2000 -> 22.06.2000 v3.3f 03.01.2001 -> 15.10.2001 v3.3g 04.01.2002 -> 16.06.2003 v3.4.0 09.06.2021 -> 07.10.2021 v3.4.1 19.10.2021 -> 23.10.2021 v3.5.0 04.11.2021 -> 14.07.2022 v3.6.0 12.09.2022 -> 19.04.2023 v3.6.1 24.04.2023 -> 16.06.2023
0E. additional information
Contents | < Browse | Browse >
The original 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 (and now E-VO) was programmed (as you might have guessed) in 100% assembly, using the AsmOne assembler. 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ö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...
1A. tabs,lf etc.
Contents | < Browse | Browse >
E-sources are pure ascii format files, with the linefeed 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 (see 5A for line endings). Any lexical element in a source code file may be separated from another by any number of spaces, tabs etc.
1B. comments
Contents | < Browse | Browse >
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 .
In addition single line comments using // are also supported which were not originally part of the Amiga E language specification.
/* this, is a comment */ -> this one too. // another comment
1C. identifiers and types
Contents | < Browse | Browse >
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()
2A. decimal (1)
Contents | < Browse | Browse >
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
2B. hexadecimal ($1)
Contents | < Browse | Browse >
A hexadecimal value uses the additional characters "A" .. "F" (or "a" .. "f") and is preceded by a "$" character. Examples: $FC, $DFF180, -$ABCD
2C. binary (%1)
Contents | < Browse | Browse >
Binary numbers start with a "%" character and use only "1" and "0" to form a value. Examples: %111, %1010100001, -%10101
2D. float (1.0)
Contents | < Browse | Browse >
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 for more information on floats). Examples: 3.14159, .1 (=0.1), 1. (=1.0)
2E. character
Contents | < Browse | Browse >
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).
2F. string ('bla')
Contents | < Browse | Browse >
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) \xHH print a character with the hex code HH \! bell character (ascii 7)
Additionally, when used with formatting functions:
\d print a decimal number \u print an unsigned 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 :
'this specifically long string ' + 'is separated over two lines'
2G. lists ([1,2,3]) and typed lists
Contents | < Browse | Browse >
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 on list-functions for a discussion on typed-immediate lists and detailed information).
2H. lisp-cells ()
Contents | < Browse | Browse >
(see 9I to read about cells in detail)
3A. format
Contents | < Browse | Browse >
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 ,...)
- operators (see 4A ,...)
- function calls (see 3D )
- parentheses () (see 3B )
- variables or variable-expressions (see 3C )
examples of expressions: 1 'hello' $ABCD+(2*6)+Abs(a) (a<1) OR (b>=100)
3B. precedence and grouping
Contents | < Browse | Browse >
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 */
3C. types of expressions
Contents | < Browse | Browse >
There are three types of expressions that may be used for different purposes;
- , consisting of just a variable
- , consisting of a variable, possibly with unary operators with it, like "++" (increment) "." (member selection) or [] (array operator). (see 4D , 4G). It denotes a modifiable expression, like Lvalues in C. Note that those (unary) operators are not part of any precedence.
- . This includes and , and any other expression.
3D. function calls
Contents | < Browse | Browse >
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 ) on how to make your own functions, (see 9A and 10A) on built-in functions. Examples:
foo(1,2) Gadget(buffer,glist,2,0,40,80+offset,100,'Cancel') Close(handle)
4A. math (+ - * /)
Contents | < Browse | Browse >
These infix operators combine an expression with another value to yield a new value. Examples:
1+2, MAX-1*5
(see 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 , Mul())
If you have selected 020 cpu (OPT 020) then these will be swapped out with 020+ optimised versions which can handle 32 bit operations.
4B. comparison (= <> > < >= <=)
Contents | < Browse | Browse >
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.
4C. logical and bitwise (AND OR NOT << and >>)
Contents | < Browse | Browse >
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 */
a:=NOT b (bitwise invert)
a:=1 << 3 /* bitwise shift left */ a:=128 >> 3 /* bitwise shift right */
4D. unary (SIZEOF OFFSETOF ^ {} ++ -- `)
Contents | < Browse | Browse >
-
SIZEOF or SIZEOF simply returns the size of a certain object or CHAR/INT/LONG. Example: SIZEOF newscreen + SIZEOF INT
-
OFFSETOF returns the offset from the base address of the object for that member
-
{} 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]
-
^ [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 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 */
-
++ and -- Increases (++) or decreases (--) the pointer that is denoted by 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 , -- 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.
-
` This is called a quoted expression, from LISP. 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
4E. triple (IF THEN ELSE)
Contents | < Browse | Browse >
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.
IF THEN ELSE
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')
You can also use the (less readable but more concise) C style syntax - x?y:z eg.
b:= a<1 ? 2 : 3
The bitwise AND, OR can be used in IF THEN ELSE constructs and will work as you might expect
IF (a<1) OR (a>10) THEN b:=2 ELSE b:=3
but because they are simply operating on a bitwise level there is no short circuit evalaution. So you cannot do this for example:
IF (obj<>NIL) AND (obj.myval=3) THEN b:=2 ELSE b:=3
because both sides of the AND operator will always be executed and this will result in an invalid memory access when obj.myval is referenced if obj = NIL.
To address this E-VO also has ANDALSO and ORELSE constructs which perform the same operation as AND and OR but will not perform the second part of the test if the outcome is already determined by the first part. eg x AND y will always be false if x is false and x OR y will always be true if x is not false.
They are only designed for use in IF statements although you can use them in a bitwise way the results are not defined. It is best to stick to just using them with boolean expressions and expecting them to return FALSE or TRUE.
IF (obj<>NIL) ANDALSO (obj.myval=3) THEN b:=2 ELSE b:=3
This is perfectly acceptable with ANDALSO since the (abj.myval=3) won't be executed if (obj<>NIL) is false, so there will be no invalid memory referencing.
4F. structure (.)
Contents | < Browse | Browse >
. builds a The pointer has to be declared as PTR TO or ARRAY OF (see 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 , you may use "." and "[]" to dereference further values. If you select an ARRAY or substructure in an OBJECT, the result is again a PTR.
4G. array ([])
Contents | < Browse | Browse >
[] (is a ) This operator reads the value from the array points to, with index . 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 */
4H. float operator (!)
Contents | < Browse | Browse >
! Converts expressions from integer to float and back, and overloads operators + - * / = <> < > <= >= with float equivalents. (see 12A to read all about floats and this operator).
4I. assignments expressions (:=)
Contents | < Browse | Browse >
Assignments (setting a variable to a value) exist as statement and as expression. The only difference is that the statement version has the form := and the expression :=. The latter has the value of as result. Note that as := 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.
4J. sequencing (BUT)
Contents | < Browse | Browse >
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:
BUT
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.
4K. dynamic memory allocation (NEW)
Contents | < Browse | Browse >
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 "MEM" 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 , 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 ).
(see 5M , END) (see 9F for the fast memory allocation functions NEW makes use of)
4L. unification (<=>)
Contents | < Browse | Browse >
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, E-VO 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 for that). The future will see this expanded with strings, typed-lists/objects, and even expressions [!]
4M. pointer typing (::)
Contents | < Browse | Browse >
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.
4N. swap (:=:)
Contents | < Browse | Browse >
using the swap operator you can exchange the contents of two variables without needing to use an interim variable.
DEF a,b
a:=1 b:=2
a:=:b
will swap the contents of a and b.
The results will be stored in the last-used variables (i.e. in a+b/c*d, "d" is the last one). Multiple use of it is possible but the variables are set after each two (i.e. when You write sth like:
a:=:b:=:c
first a is swapped with b and then b is swapped with c)
IMPORTANT NOTE!
This "thing" is only for variables, NOT for objects and members. SO! If you do sth. like:
a.b.c:=:d.e.f
then ONLY POINTERS are set with expression values! In this case it is equal to sth. like:
t:=a a:=d.e.f d:=t.b.c
4O. quick compare (==)
Contents | < Browse | Browse >
This sequence lets You compare a single expression against any bounds or values [or even expressions, if needed] in a very fast way. The main use is:
== [, TO ,...]
What is a good thing about it? Well, I think it's the speed. The output code is much shorter and the check procedure exits as soon as one comparison is true [for example, if an array has 100 entries and the first entry matches the given expression, it exits immediately with TRUE]. It might be very useful together with IF sentence.
Example use:
PROC checkrange(value)
IF value\*16-2 == \[2, 10 TO 20, xyz()-blah+3\] THEN
DisplayBeep(NIL)
ENDPROC
is equivalent to:
PROC checkrange(value)
IF (value\*16-2=2) OR ((value\*16-2>10) AND (value\*16-2<20)) OR
(value\*16-2=(xyz()-blah+3)) THEN DisplayBeep(NIL)
ENDPROC
4P. increment decrement etc (+= -= etc)
Contents | < Browse | Browse >
That works mostly same as in C. You can easily omit the sequence such as
x:=x....
and start it without writing the above. It is especially useful when You want to perform an action on some long-named fields in Your object identifier or the variables with quite big names. The main sequence is:
<function>=
where may be one of those: +, -, *, /, AND, &, OR, ||, <<, >>
example use:
DEF object:objectname ... PROC increase(value) object.member+=value // same as: object.member:=object.member+value ENDPROC
5A. format (;)
Contents | < Browse | Browse >
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 -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.
5B. statement labels and gotos (JUMP)
Contents | < Browse | Browse >
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 , 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 continues execution at . 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:
5C. assignment (:=)
Contents | < Browse | Browse >
The basic format of an assignment is: := Examples: a:=1, a:=myfunc(), a:=b*3
In E multiple variables can be assigned at once, when is a function that returns multiple returnvalues. (see 6G )
5D. assembly mnemonics
Contents | < Browse | Browse >
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 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 */
5E. conditional statement (IF)
Contents | < Browse | Browse >
IF, THEN, ELSE, ELSEIF, ENDIF
syntax: IF THEN [ ELSE ] or: IF [ ELSEIF /* multiple elseifs may occur */ ] [ ELSE ] ENDIF
builds a conditional block. Note that there are two general forms of this statement, a single-line and a multiple-line version.
5F. for-statement (FOR)
Contents | < Browse | Browse >
FOR, TO, STEP, DO, ENDFOR
syntax: FOR := TO STEP DO or: FOR := TO STEP ENDFOR
builds a for-block, note the two general forms. 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 CONT and EXIT statements with the following syntax:
CONT
Will continue executing at the next iteration of the loop of the boolexp is true.
EXIT
Which allows you to exit the loop if boolexp is true.
5G. while-statement (WHILE)
Contents | < Browse | Browse >
WHILE, DO, ENDWHILE
syntax: WHILE DO or: WHILE ENDWHILE
builds a while-loop, which is repeated as long as is TRUE. Note the one-line/one-statement version and the multiple line version.
WHILE may also contain CONT and EXIT statements, like FOR.
This great idea was brought by Martin Kuchinka - author of PowerD compiler0. The WHILE-ENDWHILE loop is expanded with following keywords:
ELSEWHILE
Alternative loop part. Any number of ELSEWHIlE keywords can be used in each WHILE loop. Usage is same as for WHILE:
ELSEWHILE
with only one exception from the rule. As for now it DOESN'T support "DO" keyword (it means: You cannot write ELSEWHILE x DO y as it will cause an error)
Every time the WHILE loop is executed, program searches for a condition that returns true and executes proper part of program. In other case it returns the loop
ALWAYS
This keyword is designed especially to be used together with ELSEWHILE. It declares part of program that will be called every time the loop is executed and at last one condition is true.
Sample code (as short as possible :):
PROC main()
DEF a=10
WHILE a=10
a:=a+1
WriteF('Incrementing "a" by 1\\n');
ELSEWHILE a=11
a:=0
WriteF('Setting "a" to zero\\n');
ALWAYS
WriteF('Loop is called again now\\n');
ENDWHILE
WriteF('a=\\d\\n',a)
ENDPROC
will return:
Incrementing "a" by 1 Loop is called again now Setting "a" to zero Loop is called again now a=0
NOTES
- The infinite loops are much easier to create :))
- Please don't use ALWAYS keyword without ELSEWHILE
5H. repeat-statement (REPEAT)
Contents | < Browse | Browse >
REPEAT, UNTIL
syntax: REPEAT UNTIL
builds a repeat-until block: it will continue to loop this block until =TRUE. Example:
REPEAT WriteF('Do you really, really wish to exit this program?\n') ReadStr(stdout,s) UNTIL StrCmp(s,'yes please!')
REPEAT..UNTIL may also contain CONT and EXIT statements, like FOR be aware though that executing a CONT on a REPEAT will not re-evaluate the UNTIL expression so you could end up in an infinite loop.
5I. loop-statement (LOOP)
Contents | < Browse | Browse >
LOOP, ENDLOOP
syntax: LOOP ENDLOOP
builds an infinite loop (unless you use EXIT to break the loop)
5J. select-case-statement (SELECT)
Contents | < Browse | Browse >
SELECT, CASE, DEFAULT, ENDSELECT
syntax: SELECT [ CASE ] [ CASE ] /* any number of these blocks */ [ DEFAULT ] 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 which works on expressions in the CASE statements, E has a SELECT OF 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* bytes, 256 in this case.
5K. increase statement (INC/DEC)
Contents | < Browse | Browse >
INC, DEC
syntax: INC ,n DEC ,n
short for :=+n and :=-n.
The second part of the instruction (,n) can be omitted and defaults to 1. In this case they are equivalent to var++ and var-- and the only difference is that these are statements, and do not return a value.
5L. void expressions (VOID)
Contents | < Browse | Browse >
VOID
syntax: VOID
[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.
5M. memory deallocation (END)
Contents | < Browse | Browse >
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 for details).
5N. Generate an error or warning (FATAL/WARN)
Contents | < Browse | Browse >
You may wish to include warnings or error message into your code (usually based on conditional compilation). The following statements will generate an error or warning during compilation.
FATAL 'this is a fatal error'
and
WARN 'this is a warning'
Neither of these strings will end up in the compiled code and wil simply be displayed to the user when compiling. A useful use case for this is to display an error or warning when using the wrong E compiler.
#ifndef EVO_3_4_0 FATAL 'This code requires at least E-VO compiler v3.4.0' #endif
5O. code and data sections (SECTION)
Contents | < Browse | Browse >
USAGE SECTION type,mem
PARAMETERS Second parameter is optional. First one decides about the section type [CODE/DATA], second - memory type [none=PUBLIC, FAST or CHIP]
ABOUT This keyword allows a programmer creating programs splitted up to more than one hunk
NOTE
- OPT LARGE should be set if You want to use it.
- Although SECTION DATA allows storing code, please don't do it.
- it is allowed only in executable files, not in modules
and a small example - don't blame me for it, I want to make it as simple as possible ;)
MODULE 'tools/pt', 'dos/dos'
PROC main() pt_play({module}) Wait(SIGBREAKF_CTRL_C) pt_stop() ENDPROC
SECTION DATA,CHIP
module: INCBIN 'pt_module'
5P. Negative conditions/loops (IFN/ELSEIFN etc)
Contents | < Browse | Browse >
While it is perfectly possible to use the Not() operator in loops or conditions to indicate the negative condition eg.
REPEAT .. UNTIL Not(failed)
There are a set of commands which are shorter and generate more efficient code. These commands are:
UNTILN
WHILEN
ELSEWHILEN
IFN
ELSEIFN
EXITN
CONTN
They are used in exactly the same way as their counterparts eg.
REPEAT
..
UNTILN failed
or
IFN v
..
ENDIF
Rather than inverting the results of the operation (which is what adding a Not() would do and thus adding extra steps to the calculation these commands simply alter the expected branch condition so its direct opposite and so are more efficient.
6A. proc definition and arguments (PROC)
Contents | < Browse | Browse >
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 ( , ... ) ENDPROC , ...
defines a procedure with any number of arguments. Arguments are of type LONG or optionally of type PTR TO (see 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
You can also denote a procedure as SAFE
PROC SAFE add(x,y)..
This will ensure that registers D2-D7/A2-A6 are all saved on the stack at the start of the procedure and restored at the end. Normally E will automatically save any registers used by E generated code (register variables or those allocated to registers by the optmimiser). If you write your own ASM code however you may want tell E to store the registers on the stack - of course you can do this in your own ASM code but this is an easier way.
NOREGS - This is an extra option you can add on the end of a proc statement
eg.
PROC sub1() NOREGS
This will override the OPT REG in use and ensure that no registers are used as variable storage during the duratioon of the procedure definition. This may be useful if you have inline asm code that needs the registers to be available.
There is also an extension of the ENDPROC command that allows you to control what assembly instruction is used to return from the routine.
ENDPROC WITH RTR or ENDPROC WITH RTE
If you don't understand what this is for then you will never need this. It is used to return from a system exception handler.
6B. local and global definitions: scope (DEF)
Contents | < Browse | Browse >
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 ,...
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 for more examples, as that is where the types are introduced). For now, we'll use the form. Arguments to functions are restricted to basic types; (see 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.
6C. endproc/return
Contents | < Browse | Browse >
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 [] /* 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 ( , ... ) IS
("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
6D. the 'main' function
Contents | < Browse | Browse >
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)
6E. built-in system variables
Contents | < Browse | Browse >
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 , 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. exception exception ID used in exception handling exceptioninfo additional information used in exception handling thistask address of this task __pool address of the memory pool (OS 3.0+ only and when pooling is in use). utilitybase base reference for utility.library (only initialised if OPT UTILIB is in use).
6F. default arguments to functions
Contents | < Browse | Browse >
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.
6G. multiple return values
Contents | < Browse | Browse >
In E you can return any number of return values (max 3 in Amiga E because of implementation reasons). How?
RETURN ,, (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:
, ... :=
where 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))
6H. function values
Contents | < Browse | Browse >
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.
7A. const (CONST)
Contents | < Browse | Browse >
syntax: CONST ,...
Enables you to declare a constant. A declaration looks like: = constants must be uppercase, and will in the rest of the program be treated as . Example:
CONST MAX_LINES=100, ER_NOMEM=1, ER_NOFILE=2
CONST STR='test' CONST F=3.5
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 E-VO also allows you to put them between two PROCs, for example.
7B. enumerations (ENUM)
Contents | < Browse | Browse >
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 '=' 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
7C. sets (SET)
Contents | < Browse | Browse >
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 */
7D. built-in constants
Contents | < Browse | Browse >
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 , Gadget()) [obsolete] OLDFILE,NEWFILE,READWRITE 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 \*/
The following built in constants are new and not active if
you are using LEGACY mode.
TAG_DONE,TAG_END,TAG_IGNORE, TAG_MORE,TAG_SKIP,TAG_USER Used with tag lists OFFSET_BEGINNING,OFFSET_CURRENT,OFFSET_END Used with the dos.library Seek function
8A. about the 'type' system
Contents | < Browse | Browse >
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
8B. the basic type (LONG/PTR)
Contents | < Browse | Browse >
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 :PTR TO
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
8C. the simple type (BYTE/CHAR/INT/WORD/LONG)
Contents | < Browse | Browse >
in the E language basic (single) variables cannot be 8 or 16 bit types (BYTE/CHAR/INT/WORD); 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.
The types are defined as follows:
BYTE - Signed 8 bit values (-128 to 127)
CHAR - Unsigned 8 bit values (0 to 255)
INT - Signed 16 bit values (-32768 to 32767)
WORD - Unsigned 16 bit values (0-65535)
LONG - Signed 32 bit values (-2147483648 to 2147483647)
8D. the array type (ARRAY)
Contents | < Browse | Browse >
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: []
You can also just skip the "ARRAY OF" and use the following shortcut syntax:
DEF x[100]:LONG DEF b[255]:CHAR
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 is compatible with PTR TO , with the only difference that the variable that is an ARRAY is already initialised.
Multi-dimensional arrays are also allowed and declared as follows:
DEF f[5][8][10]:ARRAY OF CHAR
The number of dimensions is not limited however the maximum size of the array is 32767 elements in total (eg when all the dimensions are multiplied together).
Accessing a multi-dimensional array is done using the same syntax:
f[1][3][5]:="a"
8E. the complex type (STRING/LIST)
Contents | < Browse | Browse >
-
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 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 and 2G) for more on this.
8F. the compound type (OBJECT)
Contents | < Browse | Browse >
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 [ : ] /* any number of these */ ENDOBJECT
where type is one of the following:
CHAR/INT/LONG/ PTR TO CHAR/INT/LONG/ ARRAY OF CHAR/INT/LONG/
(ARRAY with no type is short for ARRAY OF CHAR)
like DEF declarations, omitting the type means :LONG.
Note that 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 and other parts of chapter 4 for these)
INT's or LONG ARRAYs in objects are by default put on even offsets and ARRAY sizes are rounded to an even size:
OBJECT myobject len:CHAR, data[9]:ARRAY OF INT a:CHAR ENDOBJECT
SIZEOF myobject is 22, and "data" starts at offset 2.
You can use the NOALIGN option on the object to disable this feature
OBJECT mystring NOALIGN len:CHAR, data[9]:ARRAY ENDOBJECT
This will allow items to be placed on odd boundaries. This requires you have chosen OPT 020 since the 68000 will crash if you try to access INT or LONG items on odd boundaries.
'PTR TO' is the only type in OBJECTs that may refer to yet undeclared other objects.
(see 14A for all other OBJECT features that are somehow OO related)
8G. initialisation
Contents | < Browse | Browse >
- 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.
- Initialised to '' and [] resp.
- global and local STRINGs
- global and local LISTs
- Not initialised
- local variables (unless explicitly stated)
- global and local ARRAYs
- global and local OBJECTs
8H. the essentials of the E typesystem
Contents | < Browse | Browse >
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 , where 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.
It is also possible to declare something as a PTR to a PTR. So for example
DEF c: PTR TO PTR TO CHAR
So c or c[n] would be a PTR but c[n][n] would be a CHAR
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 is just another way of writing PTR TO , 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 , (are equal to) PTR TO
- 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 is equivalent to [1]:ARRAY OF . both represent an initialised PTR TO .
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 .
"[]" 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...
9A. io functions
Contents | < Browse | Browse >
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 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 . 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 )
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.
bool:=Eof(filehandle)
Checks if we are at the end of the file.
filehandle:=Fopen(name,mode)
Opens a file and returns the file handle. All open file handles are stored in a global list that will automatically be closed when you program ends.
Fclose(filehandle)
Closes and open file and removes the handle from the internal list. You should always use this rather than the OS Close() when using Fopen.
PutF(filehandle, formatstr, args...)
Used in the same way as WriteF but writes the output to a filehandle rather than to the standard output.
blks:=ReadB(filehandle, blksize, numblocks, mem)
Read from an open file into memory. Reads a number of blocks of a given block size and returns the number of blocks read.
blks:=WriteB(filehandle, blksize, numblocks, mem)
Write to an open file from memory. Writes a number of blocks of a given block size and returns the number of blocks written.
len:=Size(filehandle)
Returns the length of a currently open file.
9B. strings and string functions
Contents | < Browse | Browse >
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(80)
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 ))
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.
StrAddChar(estring,char)
Same as StrAdd but adds a single character onto the end of an 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.
AstringF(string,fmtstring,args,...)
array string version of StringF.. So no length checking is done so make sure the output string is long enough or you will end up with memory corruption.
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.
foundpos:=InStri(string1,string2,startpos=0)
Same as InStr but with a case insensitive comparison.
newstringadr:=TrimStr(string)
returns the *address* of the first character in a string, i.e., after leading spaces, tabs etc.
res:=EndsWith(string1,string2)
returns TRUE if a string1 ends with string2 otherwise returns FALSE
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.
ch:=UpperChar(char) and ch:=LowerChar(char)
uppercase and lowercase functions that work on a single character.
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=ALL)
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.
The second parameter is optional and defaults to ALL which will automatically calculate the length of a zero-terminated string. For example, after using a function that just puts a zero-terminated string at the address of estring, use SetStr(mystr) to make it manipulatable again. If the string is too long, SetStr does nothing (this should always be prevented).
newstr:=StrClone(estring)
Creates a new estring that is a copy of the estring passed in. This is a shorthand way of creating a new string and copying the contents of the original estring.
newstr:=AstrClone(string,len=ALL)
Similar to StrClone but creates a new estring that is a copy of the string passed in. A partial string copy can be performed by passing in a len value. This is a shorthand way of creating a new estring from an existing string.
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.
AstpCopy(string1,string2,size)
This is based on the C stpcpy function. It is very similar to AstrCopy except that AstpCopy() returns the address of the end of the string dest (that is, the address of the terminating zero byte) rather than the beginning.
It is therefore optimised for concatenating several strings tgogether using multiple calls to the function.
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.
StrIns(estring1,estring2,pos)
Inserts estring2 into estring1 at the specified pos. Returns the estring
StrRem(estring,pos,len=ALL)
Removes a sub-string within an estring. len characters are removed starting at position pos. The estring is returned.
StrCompare(string1,string2,len=ALL)
Compares string1 and string2 for upto "len" characters and returns -1 if string1<string2. 0 if string1 = string2 and +1 is string1 > string2
for string linking functions (see 9H )
9C. lists and list functions
Contents | < Browse | Browse >
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:
[,,... ] immediate list (of LONGs, use with listfuncs) [,,... ]: typed list (just to build data structures)
If is a simple type like INT or CHAR, you'll just have the initialised equivalent of ARRAY OF , if is an object-name, you'll be building initialised objects, or ARRAY OF , 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.
ListAddItem(listvar,value)
Adds a single item to the tail of listvar. returns listvar.
ListInsItem(listvar,value,pos)
Inserts a single item in a specific position of listvar. returns listvar.
ListRemItem(listvar,pos)
Remove a single item at a specific position of listvar. returns listvar.
ListSwapItem(listvar,pos1,pos2)
Swaps a two single itemd at specific positiond 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])
list:=ListClone(oldlist)
creates a new list with the same specifications as the old list and copies across the values in the old list. The values are just blindly treated as LONG's so if they are references to objects then the new list will reference the same objects.
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 ). for list linking functions (see 9H ).
9D. intuition support functions
Contents | < Browse | Browse >
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
9E. graphics support functions
Contents | < Browse | Browse >
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 , 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.
9F. system support functions
Contents | < Browse | Browse >
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 , 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 on how E organizes its stack. If you don't do heavy recursion, you need not worry about your free stack space.
Stack space can also be monitored further using these calls
amount:=UsedStack()
amount:=TotalStack()
which will give details of the total stack space available and used by the current task. The total stack will not match the value specified in the code exactly as some stack space is reserved automatically on top of the size requested.
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.
Also CtrlD(), CtrlE(), CtrlF() work the same way but for each of the possible break conditions.
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:
- "MEM" 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.
GetA4()
The register A4 is used in E for accessing global variables. If A4 gets corrupted by any code (particularly inline assembler) then it will not be possible to access any global variables. GetA4() will restore the correct value in A4. However the value is stored in a way that is not re-entrant so it is not suitable for code that needs to be made resident (eg library code). Using this function will cause a compiler error if you have OPT PURE turned on.
mem:=Alloc(size)
This is used in conjuntion with OPT POOL to allocate memory from the pool. Memory pools are available only in OS v39 and above. 0 will be returned in when not enough free pool space is available. The size of the pool is set in the OPT POOL command.
Free(mem)
Releases memory allocated with the Alloc command back to the pool.
MemFill(addr,size,value)
Fills the memory at addr to addr+size-1 with value. The value can be 0-255.
9G. math and other functions
Contents | < Browse | Browse >
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).
x:=Lsl(y,num) x:=Lsr(y,num)
logically shifts y num bits to left or right (set new bits to 0).
a:=Long(adr) a:=Int(adr) a:=Word(adr)
a:=Char(adr) a:=Byte(adr)
peeks into memory at some address, and returns the value found. This works with 32, 16 and 8 bit values and signed or unsigned according to the respective types. 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 would only make a program more complex or less efficient. You are _not_ encouraged to use these functions.
NOTE: In E-VO versions prior to 3.6.0 (and other E languages based directly from the original E source) Int() did not sign extend the results. This has been corrected in v3.6.0 of E-VO but you can restore the old functioning by specifying OPT LEGACYINT
PutLong(adr,a) PutInt(adr,a) PutChar(adr,a)
Pokes value 'a' into memory. See: Long() in addition:
PutByte(adr,a) and PutWord(adr,a)
are also defined but are just aliases for PutChar and PutInt since there is no actual difference in functionality when the value is signed or not, the value is simply saved to that address.
y:=Bounds(x,a,b)
makes sure x lies between bounds a and b, and adjust acordingly if necessary.
It equals: y:=IF xb THEN b ELSE x
b:=Chk(x)
Returns FALSE if x is 0 and TRUE for any other value.
r:=Compare(a,b)
Does a comparison between a and b and returns -1 if a < b, 0 if a=b and +1 if a>b
r:=Ucompare(a,b)
As above but treating a and b as unsigned 32 bit values
r:=Fcompare(a,b)
As above but treating a and b as floating point values
r:=StrCompare(a,b,len)
As above but performing a string comparison on a and b. (see 9B on string functions for more details)
9H. string and list linking functions
Contents | < Browse | Browse >
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
9I. lisp-cells and cell functions
Contents | < Browse | Browse >
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 ). 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, "MEM" 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.
9J. Boopsi functions
Contents | < Browse | Browse >
E-VO Provides a number of functions that are useful when working with Boopsi objects. These are normally found in amiga.lib but since E-VO doesn't provide any way to link with amiga.lib these have been implemented as function calls.
These are particularly useful when working with the new Reaction objects that are introduced in OS3.2
All of these boopsi functions are only available when targeting OS 37 or above.
res:=DoMethod(object, methodid, ...)
res:=DoMethodA(object, message)
These invoke the specific message on the given object. The only difference is the way the parameters are passed. DoMethod expects a stream of parameters and DoMethodA expects the message to be a taglist starting with the methodid code and followed by any additional parameters and ending with TAG_DONE
res:=CoerceMethod(class, object, methodid, ...)
res:=CoerceMethodA(class, object, message)
Function invokes the supplied message on the specific object as though it were the specified class
res:=DoSuperMethod(class, object, methodid, ...)
res:=DoSuperMethodA(class, object, message)
Function invokes the supplied message on the specified object though as it were the superclass of the specified class
Set(object, attr, value, ...)
Sets(object, attr, value)
Assign a value assigned to specified attribute of the object
res:=Get(object, attr, store)
res:=Gets(object, attr)
Ask specified object for a value assigned to specified attribute
10A. built-in library calls
Contents | < Browse | Browse >
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.
10B. interfacing to the amiga system with the v47 modules
Contents | < Browse | Browse >
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 ) 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 ,...
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) WriteF('File: "\s" in "\s"\n',req.file,req.drawer) ENDIF 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 , 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.
10C. compiling own modules
Contents | < Browse | Browse >
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 E-VO 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 ) [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. E-VO 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 ).
Try out the new ShowModule (see 17A ) to see what E-VO 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 (E-VO 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).
10D. the modulecache
Contents | < Browse | Browse >
(see 17D , ShowCache/FlushCache about this).
11A. quoting and scope
Contents | < Browse | Browse >
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.
11B. Eval()
Contents | < Browse | Browse >
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.
11C. built-in functions
Contents | < Browse | Browse >
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 ), 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.
12A. float values
Contents | < Browse | Browse >
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)
12B. computing with floats
Contents | < Browse | Browse >
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.
12C. builtin float functions
Contents | < Browse | Browse >
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'
12D. float implementation issues
Contents | < Browse | Browse >
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 E 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 E-VO 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, E-VO 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.
Another suitable patch is for example util/boot/PatchMathSB10.lha on Aminet (or CD #7). As far as E-VO itself is concerned, if E-VO has to use one of these operations (for example for float constants) it will warn you that you are not running the patch.
13A. defining exception handlers (TRY/CATCH or HANDLE/EXCEPT)
Contents | < Browse | Browse >
E now has two styles of exception handling which you can happily mix and match according to your needs.
The first (and original) 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.
The second style of exception handling is more flexible and allows for more than one exception handler in a single PROC. It is more akin to the exception handling in more modern languages. The syntax for this is
TRY .. do something .. CATCH .. handle the exception .. ENDTRY
So you can define a block of code anywhere within a procedure that can handle exceptions raised within the scope of that block. Exception blocks can be nested and in effect work exactly the same but this gives the developer a greater level of control.
13B. using the Raise() function
Contents | < Browse | Browse >
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.
13C. defining exceptions for built-in functions (RAISE/IF)
Contents | < Browse | Browse >
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 IF , ...
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". 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.
13D. use of exception-ID's
Contents | < Browse | Browse >
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.
14A. OO features in E
Contents | < Browse | Browse >
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.
14B. object inheritance
Contents | < Browse | Browse >
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.
14C. data hiding (EXPORT/PRIVATE/PUBLIC)
Contents | < Browse | Browse >
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.
14D. methods and virtual methods
Contents | < Browse | Browse >
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.
14E. Constructors, Destructors and Super-Methods
Contents | < Browse | Browse >
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 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).
14F. Unions
Contents | < Browse | Browse >
Within an object a member will always have a single specific type. However sometimes it is useful to be able to access one or members in different context specific ways. So for example you may have an object that stores data but the type of that data can vary at runtime. UNION's allow you to define different structures within your object that can all reference the same data. eg.
OBJECT myObject name:PTR TO CHAR priority:INT type:INT UNION [ v1:CHAR v2:CHAR ] [ v:INT ] ENDUNION
With this object you can either access v1 and v2 or v depending on whether you wish to store two CHAR values or an INT. The space reserved in the object will only be 2 bytes and both v1 and v will point to the same memory area.
15A. identifier sharing
Contents | < Browse | Browse >
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 is (A4) (or A5) */
MOVE.L dosbase,A6 /* library call identifiers */ JSR Output(A6)
MOVEQ #TRUE,D0 /* constants */
E-VO'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 E-VO:
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:
#
-
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.
15B. the inline assembler compared to a macro assembler
Contents | < Browse | Browse >
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).
15C. ways using binary data (INCBIN/CHAR..)
Contents | < Browse | Browse >
INCBIN
syntax: INCBIN
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 ,... INT ,... CHAR ,...
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.
15D. OPT ASM
Contents | < Browse | Browse >
OPT ASM is discussed also in chapter 16A. It allows you to operate 'E-VO' as an assembler. There's no good reason to use E-VO 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 E-VO, because of the differences as described in chapter 15B. If you want to write assembly programs with E-VO, and want to keep your sources compatible with other assemblers, simply precede all E-specific elements with a ";", E-VO 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 E-VO
15E. Inline asm and register variables
Contents | < Browse | Browse >
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, E-VO 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
16A. the OPT keyword
Contents | < Browse | Browse >
OPT, LARGE, STACK, ASM, NOWARN, DIR, OSVERSION, MODULE, EXPORT, RTD, REG
syntax: OPT ,...
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 0C , 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 ).
ASM Set the compiler to assembly mode. From there on, only
assembly instructions are allowed, and no initialisation
code is generated. (see 15D , inline assembly)
NOWARN Shut down warnings. The compiler will warn you if it
*thinks* your program is incorrect, but still syntactically
ok. (see 0C , -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 )
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,882,040,060
generate code for these CPUs. Currently only opmises for 020
so fpu and higher CPU's have no additional effect (except in
the case of FPEXP below).
REG=n use n register for register-allocation.
RUNBG Compiles the program with a modified startup code that
detaches from the current shell process
PURE Checks to make sure the code generated is pure (re-entrant)
so no use of variables in immedate lists is allowed and no
use of GetA4(). Doing either of these will result in an
error.
Library code should be pure since it may be held in memory
and called from multiple applications at the same time.
STRMERGE Add an extra step into the compilation process where all
constant strings values are de-duped.
POOL(memtype, puddlesize, threshsize)
create pool that can be used later in
your program using Alloc() and Free(). Parameters are
optional and default to $10001 (MEMF_CLEAR|MEMF_PUBLIC),
10240 and 256.
Pools are only availablein OS v39 and above.
FPEXP Tells the compiler to generate FPU based floating point math
code. You also need to specify either the cpu like 040/060
or the fpu like 881/882 to switch this on.
INLINE The compiler will create inline code for many of the smaller
E-VO functions each time they are used instaad of calling a
subroutine each time. This results in larger code that will
run slightly faster.
NOSTARTUP Disables as much of the startup code as possible. No
libraries will be automatically opened.
UTILLIB Automatically opens UTILITY.LIBRARY in addition to the
usual libraries that E-VO will open. The library pointer
will be placed in UTILBASE
LEGACYINT The Int() function did not sign extend the value. This is
not in line with the definition of INT variable type which
is signed. This has been corrected in v3.6.0 of E-VO and
this option will revert that function back to the previous
way of working for backwards compatibility.
example:
OPT STACK=20000,NOWARN,DIR='df1:modules',OSVERSION=39,REG=3
16B. small/large model
Contents | < Browse | Browse >
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 E-VO starts complaining that it can't squeeze your code into 32k anymore. To compile a source with LARGE model:
1> evo large sizy.e
or better yet, put the statement
OPT LARGE
at the top of your code.
16C. stack organisation
Contents | < Browse | Browse >
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
16D. hardcoded limits
Contents | < Browse | Browse >
Note these signs: (+-) just about, depends on situation, (n.l.) no clear limit, but this seems reasonable.
value datatype BYTE -128 .. 127 value datatype CHAR 0 .. 255 value datatype INT -32768 .. +32767 value datatype WORD 0 .. 65535 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 gb LARGE model current practical limit (may extend in future) 2-5 mb (v2.1) / 10 mb (v2.5)
buffersize of generated code and identifiers relative to source buffersize of labels/branches and intermediate independently (re)allocated
16E. error messages, warnings and the unreferenced check
Contents | < Browse | Browse >
Sometimes, when compiling your source with E-VO, you get a message of the sort UNREFERENCED: , , ... 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=. The compiler will simply match your against its own estimate (see 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 := ... etc.
-
'value does not fit in 32 bit' In specifying a constant value (see 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 'EVO -opt source' you wrote something for '-opt' that is not a legal option to E-VO.
-
'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 , 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 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 'evo 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: evo [-opts] ('.e' is added)" You get this by just typing 'evo' 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 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' in INCBIN could not be read.
-
'"}" expected' You started an expression with "{" , 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 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 E-VO 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:
- You were running other programs in multitasking. Leave them and try again.
- You were low on memory anyway and your memory was fragmented. Try rebooting.
- 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 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 ), 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 ).
-
'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 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 E-VO for this :-)' You probably use a module that was compiled with a newer version of E-VO 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 E-VO's PP.
-
'illegal macro definition' You made a syntax error in typing your #define (see 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 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.
16F. compiler buffer organisation and allocation
Contents | < Browse | Browse >
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 E-VO organizes its buffers.
A compiler, and in this case E-VO needs buffers to keep track of all sorts of things, like identifiers etc., and it needs a buffer to keep the generated code in. E-VO 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, E-VO 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: E-VO needs to make a pretty good estimate of this buffersize to be able to compile small and large sources alike. To do this, E-VO 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, E-VO 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 0C ) to see how this works in practice.
16G. register allocation
Contents | < Browse | Browse >
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 EVO REG=3 bla.e, (max=5, currently), E-VO 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 E-VO to pick the remaining 4 by itself.
The default is REG=0, so E-VO 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 E-VO is currently was tested to be pretty reliable, but you still check that behaviour is the same as in non-allocated code.
- E-VO 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 ). 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
17A. bin/showmodule
Contents | < Browse | Browse >
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
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 .
when given the option -c, ShowModule will output modules with library declarations in .fd format, constants in #define format.
The -e option will make ShowModule format the output as an E file that can be recompiled.
17B. sources/utilities/showhunk.e, bin/showhunk
Contents | < Browse | Browse >
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 E-VO, 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
like: 1> showhunk helloworld 1> showhunk disasm dpaint
17C. bin/iconvert, bin/pragma2module
Contents | < Browse | Browse >
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
EQU <any_expression>
STRUCTURE ,0 ; if <>0, then _SIZEOF ULONG _ BPTR _ ; etc. LABEL _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).
17D. bin/ShowCache, bin/FlushCache
Contents | < Browse | Browse >
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, E-VO will load it from disk and put it in the cache. the second time and on, E-VO will find it in cache and doesn't have to load anything from disk. If E-VO 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 E-VO, 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 E-VO option 'IGNORECACHE/S' to compile a source without using the cache. Whether the cache is full or empty, E-VO will load all from disk, and store nothing new in cache.
If two E-VO's try to access the cache simultaneously in multitasking, the second E-VO acts as if its IGNORECACHE flag were set.
17E. rexx/ecompile.rexx
Contents | < Browse | Browse >
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 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
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 E-VO to ram: for systems with a slower SYS: device, you may want to disable this if you have a fast HD.
17F. bin/o2m
Contents | < Browse | Browse >
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).
17G. bin/EYacc
Contents | < Browse | Browse >
This file is not included in the E-VO package. It can be obtained in the original E compiler files from the Aminet http://aminet.net/package/dev/e/amigae33a
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> evo 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 [email protected]. 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. "
17H. Src/Various/SrcGen.e
Contents | < Browse | Browse >
This source file is not included in the E-VO package. It can be obtained in the original E compiler files from the Aminet http://aminet.net/package/dev/e/amigae33a
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> EVO 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.
17I. bin/EBuild
Contents | < Browse | Browse >
This file is not included in the E-VO package. It can be obtained from the Aminet
http://aminet.net/package/dev/e/ebuild
17J. Aprof
Contents | < Browse | Browse >
This file is not included in the E-VO package. It can be obtained in the original E compiler files from the Aminet http://aminet.net/package/dev/e/amigae33a
17K. EDBG
Contents | < Browse | Browse >
Updates to v3.6.1
- Setting breakpoint caused crashes on certain versions of graphics.library
Updates to v3.6.0:
- add support for ECX/EEC compiled executables
Updates to v3.5.0:
- Added case insensitive searches
- Added search next option
- Display variables in both hex and decimal
- Multiple breakpoints
- Support for debugging while the code is running
- Add watch variables by name
- Support for debugging under OS 4.x without using CPU exceptions
- Section support
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
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]
17L. E-VO PreProcessor
Contents | < Browse | Browse >
E-VO 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 E-VO for speed and flexibility.
Note that OPT PREPROCESS is not required as the pre-processor is now enabled by default. It is still supported but does nothing.
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 E-VO 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)
If you try to define the same macro twice you will get an error message. So there is also a command to undefine a macro if you wish to redefine it.
#undef MACRONAME
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
We can also create more complicated conditional compilation blocks with #else #elifdef and #elifndef
#ifdef HEAVYDEBUG WriteF('now dumping memory...\n') /* ... */ #elifdef DEBUG WriteF('now entering bla() with x = \d\n',x) #else WriteF('bla()') #endif
E-VO also defines a default MACRO that you can use to test that you are compiling with E-VO rather than any other E compiler or even for a specific version of E-VO.
The currently defined version macro's are
EVO\_3\_4\_0
EVO\_3\_5\_0
EVO\_3\_6\_0
So if this is not defined then you are not compiling under E-VO. Further defines will be added for each new revision of the E-VO compiler so you can test for a specific minimum version.
#date - allows you to insert the current date into a a macro.
This is useful if you want to define a version string in your code. Acts like #define but allows the use of date specifiers which are populated with the compile date/time.
#date Version '$VER: myApp v0.0 (%d.%aM.%Y) by me'
You can then use CHAR to insert that string directly into the executable eg.
CHAR Version,0
The options for the date specifiers are:
%d - day nr
%m - month nr
%y - year nr (4 digits)
%D - day (name)
%M - month (name)
%Y - year nr (2 digits)
%h - hour (0-23)
%n - minute (0-59)
%s - second (0-59)
%aD - day (abbreviated name)
%aM - month (abbreviated name)
17M. E-VO Library mode
Contents | < Browse | Browse >
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!
E-VO 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 EXTRA size 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.
The EXTRA parameter is optional and reserves an extra amount of space in the library base struction that you can use for whatever you need. The value shoudl be 0 - 32000 and also must be even.
If all went ok, E-VO 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
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 E-VO library link feature. If a lot of people require this feature I will build it in.
18A. The E grammar
Contents | < Browse | Browse >
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.
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 :-)
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 ""
program = opts globalpart localpart
globalpart = ( modulestat | defstat | objdecl | constdecl | raisedecl )* localpart = ( procdecl | constdecl )+
modulestat = "MODULE" { conststring "," }+ eol defstat = "DEF" vardecllist eol objdecl = "OBJECT" ident [ "NOALIGN" ] [ "OF" ident ] eol ( vardecllist eol )+ [ "UNION" "[" ( vardecllist ) "]" [" ( vardecllist ) "[" "ENDUNION" ] "ENDOBJECT" eol constdecl = "CONST" { ( constant "=" constexp ) "," }+ | "ENUM" { ( constant | constant "=" constexp ) "," }+ | "SET" { constant "," }+ procdecl = [ "EXPORT" ] "PROC" [ "SAFE" ] ident "(" argdecllist ")" [ "OF" ident ] ["NOREGS"] [ "HANDLE" ] ( ( "RETURN" | "IS" ) { exp "," }* | eol defstat* stats [ "EXCEPT" eol stats ] "ENDPROC" { exp "," }* [WITH ("RTR" | "RTE" ]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" | ptrtype ) argdecllist = { argdecl "," }+ argdecl = ident [ "=" defaultarg ] [ ":" ( "LONG" | "REAL" | "PTR" "TO" ptrtype ) ] ptrtype = objtype | simpletype simpletype = BYTE | CHAR | INT | WORD | LONG objtype = ident
stats = ( ( onelinestat | multlinestat ) eol )* onelinestat = exp | lval ":=" exp | { var "," }+ ":=" exp | lval :=: lval "IF[N]" exp "THEN" onelinestat "ELSE" onelinestat | "FOR" var ":=" exp "TO" exp [ "STEP" num ] "DO" onelinestat | "WHILE[N]" exp "DO" onelinestat | "RETURN" { exp "," }* | "JUMP" ident | "EXIT[N]" exp | "CONT[N]" exp | "WARN" stringconst | "FATAL" stringconst | "SECTION" ("CODE" | "DATA") ["CHIP"] ["FAST"] | ( "INC" | "DEC" ) var [ "," num]| ; nearly obsolete asm_mnemonic { operand "," }* | ; machine dependant "INCBIN" stringconst | ; inline asm support simpletype { num "," }+ | "VOID" exp ; obsolete multlinestat = "IF[N]" exp eol stats [ ( "ELSEIF[N]" exp eol stats )* ] [ "ELSE" eol stats ] "ENDIF" | "FOR" var ":=" exp "TO" exp [ "STEP" num ] eol stats "ENDPROC" | "WHILE[N]" exp eol stats ["ELSEWHILE[N]" exp eol stats] ["ALWAYS" eol stats] "ENDWHILE" | "REPEAT" eol stats "UNTIL[N]" exp | "SELECT" var eol ( "CASE" exp eol stats )+ [ "DEFAULT" eol stats ] "ENDSELECT" | "LOOP" eol stats "ENDLOOP"
explist = { exp "," }+ exp = [ "-" ] { [ "NOT" ] item binop }+ | exp "BUT" exp item = num | fnum | lval | stringconst | charconst | "SIZEOF" objident | "OFFSETOF objident.ident | "IF[N]" exp "THEN" exp "ELSE" exp | exp ? exp : exp | exp == [ { (exp | exp "TO" exp) ","}+] "[" explist "]" [ ":" ptrtype ] | ( builtin | ident ) "(" explist ")" | var ":=" exp | "{" ident "}" | "`" exp | binop = mathop | compop | logop mathop = "+" | "-" | "*" | "/" | "<<" | ">>" | "+=" | "-=" | "*=" | "/=" | "AND=" | "OR=" | "||=" | ">>=" | "<<=" compop = "=" | "<>" | ">" | "<" | ">=" | "<=" | "=>" | "=<" logop = "AND" | "OR" | "ANDALSO" | "ORELSE" | " constexp = [ "-" ] { num ( "+" | "-" | "*" | "/" ) }+ lval = var ( "[" [ exp ] "]" | "." ident )* [ "++" | "--" ] | "^" var [ "++" | "--" ] | var = ident defaultarg = num
18B. Tutorial
Contents | < Browse | Browse >
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 (http://aminet.net/package/dev/e/BeginnersGuideUpd) that you'd definitely want to take a look at.
18C. Mapping E to C/C++/Pascal/Ada/Lisp etc.
Contents | < Browse | Browse >
[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 - - (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 (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.
18D. Amiga E FAQ
Contents | < Browse | Browse >
[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 E-VO 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 E-VO 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 far 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.). Use OPT PURE if you want to ensure these are not present in your code.
-
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, E-VO 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 E-VO resident?
no, currently not. E-VO itself is still written in old-fashioned assembly style, and cannot be made resident ;-)
-
Can we have an option for E-VO to generate assembly source instead of an executable?
No. There's no intermediate level of assembly in E-VO, it generates code directly. Using a disassembler is as close as you will get.
-
I have been disassembling some code generated by E-VO, 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 E-VO is not an assembly programmer. Doing really very high quality optimisations would require a complete rewrite of E-VO, 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 E-VO 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.
E-VO does now have a SECTION command which allows this.
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 '[email protected]' (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 '[email protected]' (Fabio Rotondo, the list administrator). The old list at [email protected] 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?
The wikipedia page is probably the best starting point. It has some useful links:
-
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: [email protected]).
-
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
-
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?
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.
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. 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?
- 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 LONG?
- Multiple statements in macros?
- Multiple error messages?
- Dereferenced variables in lefthandsides of assignment expressions,
- multiple returnvalue assignments, and the {} operator?
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?
Included in E-VO 3.5.0
-
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...
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. * 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 E-VO 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
Wild Ideas (aka Big Todo's)
* plug a general back-end onto E-VO (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--------------------------------------
Converted using GuideML 3.17