trunk
Alona EM 2022-05-29 21:03:47 +01:00
parent 64fe6180a8
commit 59290ba718
85 changed files with 43941 additions and 0 deletions

Binary file not shown.

BIN
amigae33a/E_v3.3a/Bin/AProf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
amigae33a/E_v3.3a/Bin/EYacc Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,6 @@
These fonts were especially designed to make E sources look good :-),
E/11 (and ELSE/11, the proportional version) were designed for non-interlaced
resolutions on 1084-type (fuzzy) monitors.
end/10 and 11 (monospaced) and except/12 and 13 (proportional) were designed
for square-pixel resolutions (such as DBLPAL) on a multisync/VGA type
monitor. Sizes 10 and 12 are relatively thin.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
amigae33a/E_v3.3a/Bin/O2M Normal file

Binary file not shown.

View File

@ -0,0 +1,4 @@
Here you'll find most of the executables that come with E. Documentation for
the functioning of most of these can be found in 'Docs/E.doc'. The Tools
'Explorer', 'Aprof' and 'EYacc' have their respective directories in the
Tools directory.

View File

@ -0,0 +1,37 @@
/* Ecompile.rexx: run E compiler from ced.
thanks to Rick Younie for improvements. */
epath = 'e:bin/' /* homedir of EC */
OPTIONS RESULTS
ADDRESS COMMAND
IF ~EXISTS('ram:ec') THEN 'copy 'epath'ec ram:' /* for slow sys: devices */
ADDRESS 'rexx_ced'
'status 19' /* ask ced filename */
file = result
'status 18'
IF result ~= 0 THEN DO /* save if changed */
'save' file
SAY 'saving changes..'
END
ELSE SAY 'no changes..'
PARSE VAR file comparg '.e' /* strip the extension */
SAY 'invoking E compiler with file' comparg'.e'
ADDRESS
OPTIONS FAILAT 1000000
'ram:ec -E' comparg /* run compiler */
ebyte = rc
IF EXISTS(comparg) THEN comparg /* run exe */
ADDRESS
pull /* wait for a <cr> */
'cedtofront'
IF ebyte>0 THEN 'jump to byte' ebyte /* jump to spot of error */
exit 0

View File

@ -0,0 +1,101 @@
/* $VER: CompileE.ged v0.5 (8.8.94)
by Leon Woestenberg (leon@stack.urc.tue.nl)
This is an ARexx script for GoldED (© Dietmar Eilert), and enables
you to compile E programs from the editor. Just add a menu item,
and in the 'Command' window select this ARexx script. Also, set
the output to 'NIL:'. You might as well add keybindings, window
gadgets, a shortcut key or even API to call this script.
Now just edit your source in the active window, and select the
menu item (or key, gadget etc.) you added. The compiler will be
invoked, and feedback will be showed in the status line of the
active window. If necessary, unsaved changes will be saved first.
After compilation, the status line will show the result. If there
was an error, the cursor will jump exactly to the spot of error,
and the error message of the compiler will be shown in the status
line. Warnings or unreferences (not both) will also be shown but
will not jump, nor beep, as this would get annoying very soon.
Also, cursor slot (bookmark) 9 will contain the spot of trouble.
*/
/* adjust your EC filename here, especially after registering :) */
ec='ECDEMO'
OPTIONS RESULTS
IF (LEFT(ADDRESS(),6)~="GOLDED") THEN ADDRESS 'GOLDED.1'
'LOCK CURRENT QUIET'
IF rc THEN EXIT
options='ERRBYTE' 'QUIET'
'QUERY ANYTEXT'
IF (result='TRUE') THEN
DO
'QUERY DOC VAR FILEPATH'
IF (UPPER(RIGHT(result,2))='.E') THEN
DO
'QUERY MODIFY'
IF (result='TRUE') THEN
DO
'REQUEST STATUS="Saving changes..."'
'SAVE ALL'
END
'REQUEST STATUS="Compiling source..."'
ADDRESS COMMAND ec filepath options '>T:EC_Report'
errorbyte=rc
IF errorbyte>0 THEN
DO
'FOLD OPEN=TRUE ALL'
'GOTO UNFOLD=TRUE BYTE=' || errorbyte
'PING 9'
END
IF OPEN(filehandle,'T:EC_Report','Read')~=0 THEN
DO
importance=0
DO WHILE ~EOF(filehandle) & importance~='ERROR'
lastline=READLN(filehandle)
/* messages ordered in accending importance */
IF (INDEX(lastline,'UNREFERENCED:')~=0) & (importance<1) THEN
DO
importance=1
message=lastline
END
IF (INDEX(lastline,'WARNING:')~=0) & (importance<2) THEN
DO
importance=2
message=lastline
END
IF (INDEX(lastline,'ERROR:')~=0) & (importance<3) THEN
DO
importance=3
message=lastline
END
IF (INDEX(lastline,'EC INTERNAL ERROR')~=0) & (importance<4) THEN
DO
importance=4
message=lastline
END
END
ok=CLOSE(filehandle)
IF importance=0 THEN message='Compilation succesful.'
IF importance>=3 THEN 'BEEP'
message=TRANSLATE(message,'''','"')
'FIX VAR message'
'REQUEST STATUS="' || message ||'"'
END
END
ELSE
DO
'REQUEST STATUS="Source has no .e extension!"'
END
END
ELSE
DO
'REQUEST STATUS="Say what?! Try typing some e source first :)"'
END
'UNLOCK'
EXIT

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,867 @@
@DATABASE "AProf.guide"
@remark Profiler Dokumentation für Version 3.3
@remark
@remark $RCSfile: AProf.guide $
@remark
@remark $Revision: 1.7 $ $Date: 1994/10/04 11:28:35 $
@remark ******************
@remark * Main
@remark ******************
@NODE Main "AProf.guide"
Documentation for Amiga Profiler V3.30
© 1993,94 Michael G. Binz
@{" What is a profiler? " link l-what}
@{" System requirements " link l-sysrequ }
@{" Installation procedure " link l-install }
@{" Starting the profiler " link l-start }
@{" User interface " link l-ui }
@{" Configurability " link l-config }
@{" Notes " link l-notes }
@{" Tested Systems " link l-systeme }
@{" Caveats " link l-caveats }
@{" StripB " link l-stripb }
@{" Feedback " link l-feedback }
@{" AProf needs you..." link l-enforcer-blah }
@{" Copyright " link l-copyright}
@{" Changes " link l-changes }
@{" Q+A " link frageantwort }
@ENDNODE
@remark ******************
@remark * Was isses???
@remark ******************
@NODE l-what "AProf:What is a profiler"
A profiler is a development tool supporting the developer in code
optimization. A specified program is executed by the profiler
which collects several informations (ie. call counts, time values)
while execution. This data can be used to locate the so-called
hot spots in your code - functions where most of the execution time
is spent and optimization will gain most effect.
The following articles supply further information on profilers:
o Dr. Dobb's Journal, January 1993
Joseph Newcomer, 'Profiling for Performance', pp. 80
o Dr. Dobb's Journal, November 1993
Michael R. Dunlavey, 'Performance Tuning: Slugging it out', pp. 18
@ENDNODE
@remark ******************
@remark * Installation & Systemanforderungen
@remark ******************
@NODE l-sysrequ "AProf: System requirements"
o Hardware requirements
The profiler needs at least Workbench/Kickstart 2.04 and a minimum
of 500K available memory (depends on profilees sizes).
It has been tested successfully with Amiga 500, 2000, 3000, 4000
and OS versions 2.04, 2.1, 3.0.
o Software requirements
Your compiler must be able to create Amiga symbol hunks. Code
symbols included in this hunks should only address the starting
addresses of functions in your code. No intermediate jump
labels must be defined. (See @{ "Clean symbol tables" link frageantwort}, @{ "Tested compilers" link l-systeme })
@ENDNODE
@NODE l-install "AProf:Installation procedure"
Copy all files included in AProf3_30.lha into a directory and add
this directory to your PATH.
Do not delete AProf's icon (AProf.info), since configuration data
is located in this file (See @{"Configurability" link l-config}).
@ENDNODE
@remark ******************
@remark * Start & Oberfläche
@remark ******************
@NODE l-start "AProf: Starting the profiler"
From CLI the profiler is started with
1.> AProf [application]
The profiler starts up, displays its user interface, loads the
symbol tables from the file 'application' if specified and waits
for your actions.
From Workbench, doubleclick the AProf icon.
If no filename was specified or if started from workbench, you can
load a file from the menu @{ "[Files/Open]" link m-files }.
See also: @{ "User interface" link l-ui }
@ENDNODE
@NODE l-ui "AProf:User interface"
Title: Name of loaded profilee
Menus: @{ " Files " link m-files } @{ " Action " link m-action } @{ " Data " link m-data } @{ " Move " link m-move } @{ " Misc " link m-misc }
Displays: @{ " Mode " link gad-inex } @{ " Time unit " link gad-percmil } @{ " Sort " link gad-sort } @{ " Time " link gad-xtime }
Buttons: [ Shortcuts for often needed menu entrys ]
Description of table entrys:
Function HitCnt Per Call Over Min/Max
| | | | |
Symbol names* | | | |
| | | |
Call count** | | |
| | |
Average execution time per call | |
| |
Overall execution time for that function |
|
Minimum/maximum execution time per call
* The symbol *ENTRY*, if displayed, is generated by AProf if the symbol
table of the profilee contains no symbol sitting at the very first
executed address of its seglist.
** A call count value of -1 is shown for symbols being no function entrys.
Safe profiling must be active.
[ Status and error messages are displayed in the bottom window border ]
@ENDNODE
@NODE m-files "AProf:Menu: Files"
Menu: Files
Reading and writing profiler data.
Open: Read a new file (also accessable via button)
Save: Save report to file with same basename and suffix .pro
Save as: Save report to selectable file
Reset: Reset all timing values
Print: Print report
Exit: Leave the program
@ENDNODE
@NODE m-action "AProf:Menu: Action"
Menu: Action
Start the profiling process.
Start: Starts the profile run. If you restart a profilee, then times
are added.
If you want to run a profilee several times without adding the
times and hit counts, you must reset the timer and hitcounts
with @{ " Files/Reset " link m-files }.
(also accessable via button row)
@ENDNODE
@NODE m-data "AProf:Menu: Data"
Menu: Data
Runtime configuration and execution control.
Exec details:
Provide a command line and stack size for the profilee
See: @{ "Exec dialog" link dialog-exec }
Preferences:
Configure the profiler.
See: @{ "Preferences dialog" link dialog-preferences }, @{ "Configurability" link l-config }.
@ENDNODE
@NODE m-move "AProf:Menu: Move"
Menu: Move
Moving in the display area.
Find: Enter a search string and start search
Find next: Search for next occurence
Top: Go to top of symbol table
Bottom: Go to bottom of symbol table
Page up/down: One page up/down
@ENDNODE
@NODE m-misc "AProf:Menu: Misc"
Menu: Misc
Other functions
Help:
Start the AmigaGuide(TM) hypertext help system (if available)
Refresh Window:
Redisplay symbol table
About:
Display version and copyright information
@ENDNODE
@NODE gad-inex "AProf: Profiler mode: Separate/Combined"
Separate/Combined: (Mode)
Displays how function timing values are accumulated. If combined
is selected, the time value for a function includes the times of
all functions that are called by this function. Separate excludes
the times of called functions.
Note that if your code includes recursive procedure you must use
separate mode. If combined is used, the time for the recursive
procedure is wrong.
Example:
int main( void )
{
foo( 2 ); /* foo needs 2secs execution time */
bar( 3 ); /* bar needs 3secs execution time */
...burn 1sec proc. time in main()...
return 0;
}
If 'Separate' is selected, the profile list will look like this:
Symbol HitCnt Per Call
main 1 1000 The timing values are updated for
foo 1 2000 each funktion separate.
bar 1 3000
Now if 'Combined' is selected:
Symbol HitCnt Per Call
main 1 6000 The timing values for foo() and
foo 1 2000 bar() are added to main(), since
bar 1 3000 main() calls both.
@ENDNODE
@NODE gad-percmil "AProf: Cycle: Percentual/Millisecond timevalues"
Millisecond/Percentual time units: (Units)
Displays the units used for for timing values. If percentual is
active, times shown are fractions of overall execution time in
percent.
@ENDNODE
@NODE gad-xtime "AProf: XTime"
Overall execution time of profilee. Units are seconds or milli-
seconds depending on execution time. Times longer than 1000 ms are
displayed in seconds (s), below in milliseconds (ms).
@ENDNODE
@NODE l-config "AProf: Configurability"
All configurable items of AProf must be set as ToolTypes in AProf's
.info file, which must reside in the same directory as the profiler.
+------------------------------------------------------------------+
| Never delete AProf.info! All configuration data will be |
| written to this file! |
+------------------------------------------------------------------+
Beginning with version 3.30 one can save AProf's current settings
with the 'Save' button in 'Preferences'. It is recommended to use
this instead of setting the ToolTypes manually.
o WINDIM=left/top/width/height
Use this to set size and position for the profiler window.
o TUNITS=(PERCENTUAL | MILLISECOND)
Default time units.
o PMODE=(COMBINED|SEPARATE)
Profiling mode
o PATTERN=(AmigaPattern)
Amiga pattern for symbols to hide.
Look in your Amiga User Manual for a description of pattern syntax
o SORT=(NONE | NAME | HIT | AVERAGE | OVER)
Specify the sort order you prefer.
o SAFE=(TRUE | FALSE)
Safe profiling on or off
@ENDNODE
@REMARK ****************
@REMARK * Allgemein Blah...
@REMARK ****************
@NODE l-notes "AProf: Notes"
Implementation
--------------
AProf is an active profiler. This means, hit counts and timing values
are measured by employing a sophisticated breakpoint scheme.
Before a profilee is executed by AProf, all function entry points
are marked with breakpoints.
After execution is started, AProf receives the breakpoint hits and
places additional breakpoints at the function return points.
The address for function return points is derived from the stack. This
should make it clear why AProf needs a 'clean' symbol table: There is
no foolproof way to figure out the function return address if there
are any function local data on top of stack.
Actually some testing can be undergone to ensure the correct values
are read from the stack. This can be activated in AProf versions
> 3.22 by the checkmark 'safe profiling' in AProf preferences.
Although this is not foolproof as stated, it works unexpected good
with most compilers. The disadvatage of this technique is the
additional time spent in the checking routines.
Profilee environment
--------------------
Wether AProf is started from CLI or workbench, the profilee's
environment is always a shell. If started from wb, a shell window will
be supplied for profilee IO.
ARexx port
----------
In the current version AProf provides no additional functions for
ARexx hosts.
@ENDNODE
@NODE l-caveats "AProf: Caveats"
Here is a list of known constructs which can result in problems if
you try to profile programs including them
o Non-standard startup code
-------------------------
The profilees must be able to run in the same process environment
as the profiler. It's not possible to use special startup codes for
detaching a program or making it resident.
o recursive procedures
--------------------
Use only '@{ "separate" link gad-inex }' profiling mode if your code includes recursive
procedures. In combined mode the time for the recursive procedure
is wrong.
o setjmp()/longjmp()
------------------
If setjmp()/longjmp() combinations are used in the profilee, the
time span between calling longjmp() and the next RTS instruction
will add to the function calling longjmp().
o signal()/raise()
----------------
Most compiler libraries contain signal()/raise() functions which
are not compatible with AProf.
o CIA Timer
---------
CIA timers are not available for profilees.
o Overlays
--------
Profiling of overlayed programs is not possible.
o Runtime limitation
------------------
Profiler timers can measure maximum time spans of about 99 mins.
o Static functions
----------------
Functions to be measured must be in the symbol table. This is not
the case for static (module local) functions in most programming
environments.
o Traphandlers
------------
If your program uses a private trap handler, traps not handled must
be propagated to the previous handler. Used traps must be allocated
with AllocTrap().
Profiling is NOT possible if you are using trace traps (#9).
o Switch- and Launchfunctions
---------------------------
Profilees using members tc_Switch and tc_Launch in Exec's Task
structure must propagate execution to previous defined handlers.
@ENDNODE
@NODE l-stripb "AProf: StripB"
StripB is a command line utility which can be used to remove all
HUNK_SYMBOL- and HUNK_DEBUG-hunks from an Amiga executable.
Command line: StripB infile outfile
@ENDNODE
@NODE l-systeme "AProf: Tested systems"
This is a list of systems I have tested the profiler with. If you
find an error or if you have tested a system not included here,
send a message please.
@{ " Amiga E " link amiga-e }
@{ " Aztec C " link manx-c }
@{ " DICE C " link dice-c }
@{ " GNU C/C++ " link gnu-cpp}
@{ " SAS C 6.3 " link sas-c }
@{ " Maxon C++ V1.2.1 " link maxon-cpp }
@{ " Assemblers " link assemblers }
@{ " Other systems... " link other }
@ENDNODE
@NODE amiga-e "AProf: Tested systems: Amiga E"
AProf works with all versions of Amiga E >2.1b.
Problems:
Profiling of programs using exeptions leads to meaningless results
Creation of symbol hunks:
Use -s switch
@ENDNODE
@NODE manx-c "AProf: Tested systems: Aztec C"
Manx Aztec C V3.4 - V5.2b
Creation of symbol hunks:
Use option -w for the linker
Problems:
o Remove symbols named '_H#[0-9]_org'
o Don't use detach.o with programs you want to profile.
o ANSI C functions signal() and raise() don't handle traps
the way they should. (see @{"Caveats" link l-caveats}, custom trap-handlers)
@ENDNODE
@NODE sas-c "AProf: Tested systems: SAS C"
SAS C ?
@ENDNODE
@NODE maxon-cpp "AProf: Tested systems: Maxon C++"
Maxon C++ V1.2.1
Problems:
Code and data symbols in link libraries reside in the same hunk. According
to Jens Gelhar this will change in future versions of the library. The
beta version of the updated compiler writes symbol hunks as needed by Aprof
so the library problem requires a simple recompile.
Compiler is adding labels named L'num' to symbol table. This must be
removed (Pattern: L#[0-9]) or you must activate 'save profiling'.
Creation of symbol hunks:
For command line compiler use option -bs.
In the integrated environment use menu 'Compiler options'.
Other:
Test the procedure rexx/maxoncpp.aprof for unmangling of c++ symbol names.
@ENDNODE
@NODE dice-c "AProf: Tested systems: DICE C"
DICE C V2.06.21 unregistered version
Problems:
Dice generates dirty symbol tables, activate 'Safe profiling'.
Creation of symbol hunks:
Use option -s for DCC.
Information about the commercial DICE system welcome.
@ENDNODE
@NODE gnu-cpp "AProf: Tested systems: GNU C/C++"
GNU gcc - 2.3.3
Problems:
Profiling programs using ixemul.library is not possible, use static linking.
Activate 'Safe profiling'.
Creation of symbol hunks:
Always created, to not create them use -s or @{ " StripB " link l-stripb }, which is
included in this AProf distribution.
@ENDNODE
@NODE assemblers "AProf: Tested systems: Assemblers"
AProf is tested with a number of assemblers (Aztec as, asm68, DevPack).
There seem to be no problems as long as the following rules are obeyed:
o If you export code symbols other than function labels, activate
'Safe profiling' in [Data/Prefs].
o Don't be too creative with your control flow, eg. several starting
points for functions etc. Although this will not crash AProf, the
results provided will be a bit random :)
o Do not mix data and code in one hunk.
o Do not use self modifying code.
@ENDNODE
@NODE other "AProf: Tested systems: Other..."
Here is a little strategy to test if your system is able to cooperate
with AProf.
Step 0.
-------
First, try to find out if your system can generate executables with
Amiga symbol hunks. You should find this information in your compiler
documentation.
Then after successfully creating a executable version of the program
to test, first check if your program is at least stable enough to run
stand-alone without crashing the machine.
Close all applications so that if a crash happens no data is lost!
Step 1.
-------
Start AProf and load your program. Then specify in [Data/Prefs] the
symbol pattern '#?' (without quotation marks). This removes the complete
symbol table, which results in a reduced protocol between AProf and
your program.
When this is done, close your eyes and start your program (if you
have problems in finding the right keys you can keep your eyes open).
If your machine crashed, then Aprof is definitely incompatible with
this programming system. The only thing to do is to reboot your machine
an delete AProf. If there was no problems and your program is running,
take all steps needed to stop your program.
Step 2.
-------
Next step is to remove all symbols but one, which should be generated by
you. If your programm contains a function 'foo' written by you then
open the prefs request and insert ~(foo) as symbol pattern. After selecting
'use', this should be the only symbol displayed (it is possible, that
there is an second symbol called *ENTRY* - this is used by AProf, ignore
it).
Start execution. If everything works, then again stop your program or
wait for termination. Now Aprof should display the function timings.
A crash is a sign for incompatibility. Forget AProf.
Step 3.
-------
Now remove the pattern in [Data/Prefs] and activate 'Save profiling'.
Then take a look at the symbol list. Remove temporary labels or
line number labels.
Examples:
_L0001, _L0002, _L0003, _L0004, ...
_H0_org, _H0_end, _H1_org, _H1_end, ...
@0001, @0002, @0003, ...
Then again start execution of your program. If everything works as it
should you did it. Exit your program and AProf will generate the
timing report. AProf seems to be compatible with your programming
system. Save the pattern created in the preceding step and use it
in future profiling sessions.
If your program crashed, the hard work begins. As we saw in Step 2,
your programming system basically should work with AProf. So there
must be one or several data objects in your code hunks. You will
have to find and remove them.
Step 4.
-------
Save all settings.
@ENDNODE
@NODE l-feedback "AProf: Feedback"
Contact me under
EMail: michab@informatik.fh-augsburg.de
If it's not possible to use EMail, write to:
Michael Binz
Bahnhofstr. 11
D-86459 Gessertshausen
If you made tests with a compiler not listed in 'systems', please
send me a small demo source file, the translated executable (with
symbol hunk) and some information about creating symbol hunks with
your compiler. If needed, send a description of problems and actions
you performed to solve them, too.
@ENDNODE
@NODE l-enforcer-blah "AProf needs you..."
... if you have access to an Amiga with MMU!
Since I'm developing AProf on an Amiga 2000 with 68000 I can't check
for ENFORCER HITS! Although much care was taken to keep this nasty
sort of bugs out of my code, there must be some. Anywhere.
So man, read the following!
if ( found_enforcer_hit() )
{
if ( have_access_to_email() )
Send_Micha_An_Email_Report( ); /* Really! */
else if ( !too_lazy() )
Send_Micha_SnailMail_Report(); /* Cool... */
else
GuruForever(); /* Gnah... */
}
@{ " Micha's address? " link l-feedback }
@ENDNODE
@NODE l-copyright "AProf: Copyright"
This software is freeware. You are allowed to copy, distribute and
use it, as long as you don't change the software and distribute only
the packed file (AProf3_30.lha).
You are not allowed to charge a fee higher than Fred Fish's for copy-
ing and distributing.
These files and their related documentation, utilities and examples
are provided "AS-IS" and subject to change without notice; no warran-
ties are made. All use is at your own risk. No liability or respon-
sibility is assumed.
This software, the included utilities and documentation are
© 1993-94 Michael G. Binz
@ENDNODE
@remark *************
@remark * Abgesang
@remark *************
@NODE l-changes "AProf: Changes"
V 3.34
Bugs fixed:
o Screen updates caused enforcer hits
o Shell display of command line caused enforcer hit
Changes:
o Console window opened when started from WB now has close gadget
o XTime display uses [s] if [ms] value exceeds 1000
V 3.30
Future:
o Passive (interrupt controlled) profiling mode
Known Problems:
o AmigaGuide needs 'topaz' or maybe another fixed width font
to be the system default font. If another font is used,
AmigaGuide fails silently.
Changes:
o If run from WB the profiler acts as shell for profilees
o System default font is used
o Added sorting functions
o Extended configurability
o Maximum command line length is now 256 chars
o Special 'safe' profiling mode added
o ARexx Port for custom symbol name unmangling
Bugs fixed:
o Use of other fonts than topaz.8 trashed window
o Catched some enforcer hits
o Initial window size was wrong
o Syntax error in a pattern led to an endless loop
V 3.20
o First released
@ENDNODE
@NODE frageantwort "AProf: Question and Answer"
o What is an 'clean' symbol table?
Clean symbol tables include only function entry addresses. Neither
intermediate labels are defined nor data symbols in code hunks.
@ENDNODE
@remark *******
@remark * Hilfeseiten für Dialoge. Aufruf erfolgt direkt aus AProf
@remark *
@remark * Texte werden in diesem Hilfedokument mehrfach verwendet.
@remark *******
@NODE dialog-preferences "AProf: Preferences dialog"
APROF: PREFERENCES DIALOG @{ " Exit help " close }
-------------------------
Symbol pattern:
Provide a regular expression for symbols you want to exclude from
profiling. For a description of Amiga regular expressions see
your dos manual.
Patterns can be used to remove compiler generated intermediate
labels or functions you aren't interested in. One advantage of
removing functions from the symbol table is faster profiling.
Example: 'L#[0-9]' removes symbols starting with an 'L' followed
by any number of digits (L1 L00 L4711).
Separate/Combined: (Mode)
Specifies how function timing values are accumulated. If combined
is selected, the time value for a function includes the times of
all functions that are called by this function. Separate excludes
the times of called functions.
Note that if your code includes recursive procedure you must use
separate mode. If combined is used, the time for the recursive
procedure is wrong.
See @{ "example" link gad-inex }.
Millisecond/Percentual time units: (Units)
Selects the units used for for timing values. If percentual is
active, times shown are fractions of overall execution time in
percent.
Safe profiling:
If safe profiling is on (selected), then while profiling, every
breakpoint must pass additional tests to ensure that it belongs
to a function entry or exit (See @{ "dirty symbol tables" link frageantwort }).
If off, this tests are not done, resulting in faster profiling.
Since it is hard to notice the time spent in doing the tests, it
is recommended to activate safe profiling.
Rexx Unmangler:
Specifiy the filename of the Rexx procedure to be used for name
unmangling.
Some programming languages (eg. C++) offer 'type-safe linking',
which is often implemented by coding the argument types into the
symbol names (this process is called symbol mangling). Since for
humans this symbol names are then hard to read, it is possible
to specify a Rexx procedure for translating the symbol back in
a more readable form.
See @{ "example Rexx unmangler" link rexxample }.
Sort order:
Specify the preferred sort order.
@ENDNODE
@NODE rexxample "AProf: Rexx example"
/* Dummy C Unmangler
*
* This demonstrates the basics for AProf unmanglers written in Rexx
*
* Don't use this in real life, since this is included in AProf
* as standard unmangler (No mangler selected in preferences)
*
* This unmangler removes a leading '_', if one exists
*/
/* Get the symbol name from AProf and put it in 'symnam' */
parse arg symnam
/* Check if there is a leading '_' */
if "_" = left( symnam, 1 ) then
/* Remove first char */
symnam = right( symnam, length( symnam ) -1 )
/* Return result */
exit symnam
@ENDNODE
@NODE dialog-exec "AProf: Exec dialog"
APROF: EXEC DIALOG @{ " Exit help " close }
------------------
Command line:
A command line for the profilee can be specified. The command
name must not be given.
Example: You have written a program named 'foo', which needs one
argument 'bar' to be specified on the command line. The following
steps are needed to get an execution profile of 'foo':
o Start AProf: aprof foo
o Select 'Data...' and insert 'bar' as command line
o Select 'Use'
o Select 'Start'
Stack size:
Provide a stack size for the profilee.
@ENDNODE

Binary file not shown.

View File

@ -0,0 +1,33 @@
AProf has been included in the Amiga E distribution with the
approval of the author, Michael G. Binz. Thanks Michael!
[the AProf executable itself can be found in the bin directory]
usage of AProf with Amiga E is dead simple:
compile your source (say, 'bla.e') with the '-s' switch (SYM/S)
(= add symbols):
1> ec sym bla
as you can check with ShowHunk, bla now has a symbolhunk
added to it. Now, load 'bla' into AProf:
1> aprof bla
if everything went ok, you'll be seeing a GUI with some
buttons. You'll probably want to toggle 'inclusive subroutines'
off. now start the profile by pressing 'Start'. After your
program has finished, you'll see the amount of time spend
in each PROC. Especially the 'Average' column is interesting.
This will give you a clue which part of your program needs
optimising.
notes:
- as you might have noticed, E system functions CAN be profiled
seperatedly, but library calls CANNOT, i.e. the'll always
be added to the PROC that calls them. Be aware of this.
- AProf is not always reliable when the executation time
of the whole program is very short.
For further infos, check AProf.guide

View File

@ -0,0 +1,5 @@
Here you'll find the distributions/docs of currently only
Aprof (a profiler), and the docs for Explorer (an object browser)
and EFindHit (an enforcer tool). Mac2E has been removed due to the
new internal macro-preprocessor in E v3.1a, EE is not being updated
anymore, and the EYacc stuff has moved to the Src/Lang directory.

View File

@ -0,0 +1,197 @@
Short: EFindHit v1.2 (for Enforcer output)
Type: dev/e
Author: jason@fsel.com (Jason R. Hulance)
EFindHit can be used to find the line number of an Enforcer hit in an
E program, given the offset information that Enforcer produces.
Unlike normal FindHit programs, EFindHit reports the line numbers of
hits in E modules, too (i.e., not just the main program). And it can
also tell you which function the code was in.
Usage
-----
To use EFindHit, the main source file *must* be compiled with the
LINEDEBUG or DEBUG switches. And if you want to track hits in modules
used by the program, they too must be compiled with one of these
switches.
If you want to see the names of functions as well, then you need to
use the SYM switch, too, but only on the main source file. This is
especially useful for tracking hits in E internal functions. If you
don't use the SYM switch when compiling your source then EFindHit
cannot reliably identify hits that occur in E internal functions (it
may report them as coming from the last line of one of the sources,
and flag those offsets that appear to be too far beyond the last
line).
EFindHit takes the following arguments:
EXECUTABLE/A,DEBUG/S,OFFSETS/M
So, you can supply a list of offsets. Hexadecimal offsets can be
specified using a leading '$' or '0x'. Without these prefixes the
offset is interpreted as decimal.
New to v1.2 is the DEBUG switch: see below for an illustrative
example.
Example
-------
For example, consider the following output from Enforcer (when
SegTracker is running):
BYTE-READ from 00000000 PC: 7833432C
USP: 7834B4B0 SR: 0004 SW: 0751 (U0)(-)(-) TCB: 7828F188
Data: 00000000 0000000D 78348FB8 7829016C 00000001 1E0CCF39 78348FB8 00000001
Addr: 00000000 7828F1E4 783343AE 7829016C 7834B6C0 7834B4B4 78019864 --------
Stck: 00000000 7829016C 78334306 783341F2 00000000 00000000 00000000 00000000
Stck: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
----> 7833432C - "xxx" Hunk 0000 Offset 00000244
Name: "Shell Process" CLI: "xxx" Hunk 0000 Offset 00000244
The interested lines are the first and last. The first line says the
hit was a byte read from $00000000 (probably from dereferencing a NIL
pointer). The last line says that the culprit was the program "xxx"
and that the hit happened at offset $00000244. (You might also
consider using the STACKCHECK option of Enforcer to get the backtrace
of the call stack.)
If "xxx" has been compiled with LINEDEBUG (or DEBUG) then you can run
EFindHit on it to find the line number of the hit in one of the source
files:
1.System:> efindhit xxx $244
EFindHit works better if you compile with the SYM option
Offset $244: "xxx.e" line 5 [$242]
The first line of output is a reminder that using SYM makes EFindHit
work better, then the information about the offset. The trailing
"[$242]" indicates the offset of the start of the code for line 5.
If you've compiled "xxx" with the SYM option too, then things get a
bit better:
1.System:> efindhit xxx $244
Offset $244: "xxx.e" line 5 [$242], in "fun()" [$238]
This also states the function the hit occurred in, and gives the
offset of the start of code for that function ($238).
If you don't trust the output of EFindHit (???), then you can use the
DEBUG switch to dump all the information from the executable that
EFindHit uses. (If this switch is specified then any supplied offsets
are ignored.) For example:
1.System:> efindhit xxx debug
$00000000 *** Startup1 ***
$00000196 *** End of startup1 ***
$00000374 *** End of code ***
$00000236 fun()
$000001D2 zzz()
$00000196 external()
$00000284 main()
$000002DA WriteF()
$0000036A Char()
$0000023A 14 xxx.e
$00000240 15 xxx.e
$00000244 16 xxx.e
$00000288 22 xxx.e
$0000028E 23 xxx.e
$000001D6 4 zzz.e
$0000019A 8 xyz.e
$000001EE *** Startup2 ***
$00000200 *** End of Startup2 ***
The first column is the offset, the second (if present) is the line
number and the last column is the name of the file, the function or
another significant part of the executable. You could sort this
output if you have pipes set up:
1.System:> efindhit xxx debug | sort in: out:
$00000000 *** Startup1 ***
$00000196 *** End of startup1 ***
$00000196 external()
$0000019A 8 xyz.e
$000001D2 zzz()
$000001D6 4 zzz.e
$000001EE *** Startup2 ***
$00000200 *** End of Startup2 ***
$00000236 fun()
$0000023A 14 xxx.e
$00000240 15 xxx.e
$00000244 16 xxx.e
$00000284 main()
$00000288 22 xxx.e
$0000028E 23 xxx.e
$000002DA WriteF()
$0000036A Char()
$00000374 *** End of code ***
Now it's really easy to see where any particular offset lies...
Possible output
---------------
As well as complaining about errors (like you forgot to compile with
LINEDEBUG), EFindHit might report:
1) Offset $244: "xxx.e" line 5 [$242], in "fun()" [$238]
The offset is in the identified function in the main source.
2) Offset $200: "xyz.e" line 11 [$1E8], near EXPORTed "external()" [$196]
The line number information for code in modules is more accurate than
the function information, since only EXPORTed functions can be
followed. So EFindHit reminds you that the code at the offset is only
"near" the EXPORTed function (i.e., some point after it but before the
next EXPORTed function).
3) Offset $280: E internal function "Char()"
The hit has occurred in an E internal function. You probably want the
backtrace in this case (the STACKCHECK option of Enforcer), so you can
spot which function this was called from.
4) Offset $190: E startup code
The hit has occurred outside user code, either when starting or
cleaning up. This didn't ought to happen unless your code has
scribbled all over the place. (These offsets will show up in the
backtrace produced by the STACKCHECK option of Enforcer, as E code
always starts in the startup code!)
5) Invalid offset $330: not in code part
The hit didn't occur within the code of the executable being analysed.
This might happen if you're running EFindHit on the wrong program or a
different version from that which produced the hit.
6) No line number for offset $200
EFindHit couldn't find a line number (but might still be able to
identify a function, nonetheless, and this will be reported as "in" or
"near" as appropriate [assuming you've compiled with the SYM option]).
Limitations
-----------
There's no symbol information (currently?) for any methods or for
functions which are not EXPORTed from modules. So it's not possible
to put names to offsets in these bits of code (the line numbers should
be sufficicient in these cases). And offsets in methods/INCBIN/other
data in the main source file will be reported as being "in" the
nearest function before it.
The startup code recognition is pretty much specific to EC v3.2e.
It's likely to change in future...
Have fun!

View File

@ -0,0 +1,249 @@
Short: Amazing System Browser and Debugger
Type: dev/e
Author: jason@fsel.com (Jason R. Hulance)
Explorer 2.2j
=============
Allows you to browse around memory as E objects (which are like C structs).
Basic Function
--------------
You enter an address and then select the object which is (supposed to be?)
at that address. You can then see the value that each element of the
object takes, and follow pointers etc. by double-clicking on the element (a
single click changes the address to the element's address, which is an
offset from the address you specified for the object).
A double-click may therefore change the object being browsed, so there is a
way of returning to the original object via the "Back Up" button. As you
double-click and select new objects your choices are recorded. The current
depth of this path is shown as the 'Depth:' text. The action of the "Back
Up" button is duplicated by the "Back Up" menu item on the "Selections"
menu. There is also an option to clear the current path on this menu.
The list of objects may be created in several ways (these are found on the
"Project" menu):
1) "Open" Opens a previously saved config file, containing any number of
objects. By default, when Explorer starts it tries to load
objects from "explorer.config" in the current directory, and
then "s:explorer.config" if that fails. The supplied
"explorer.config" file contains all the standard Amiga OS
objects. (This file was created by using "Load Modules" on the
"Emodules:" directory and then saving the config using the next
option.)
2) "Save" Saves the current list of objects as an Explorer config file.
3) "Load Modules" Scan the selected module or all the modules in the
selected directory and its sub-directories. The found objects
will replace any current list of objects. This operation can
take a long time, especially if you try to load all the modules
in "Emodules:". You can interrupt it at any time by closing the
status window.
4) "Merge Modules" Same as 3) but any objects found are added to the
current list of objects rather than replacing them.
5) "Load Cache" Same as 3) but the current E module cache is scanned.
This is most useful when Explorer is used to help debug your
programs.
The address and object can also be specified via ARexx, making Explorer an
extremely useful debugging tool as well as a system browser! In fact, EDBG
v3.3a+ makes use of Explorer to analyse typed variables. (A module is
supplied to show how easy it is to integrate the appropriate ARexx call
into your program.)
Object Layout
-------------
The elements of the selected object are listed in order. If you choose to
see the value of element it will be displayed in hex in parenthesis after
the element, and if you choose to see the element address (an offset from
the object address) it will be displayed after an '@'. (Use the
"Selections" menu to decide which, if any, you want to see.)
Where to Start
--------------
Explorer starts off with the address of an internal hail and copyright
string. The items on the "Presets" menu give some other, interesting
starting points:
1) "Dos" Views the address stored in the 'dosbase' global variable as a
'doslibrary' object. The dos.library is one of the fundamental
Amiga system libraries, and 'dosbase' is its corresponding
library base object.
2) "Exec" Same as 1) but for the 'execbase' variable and object for the
exec.library (the most fundamental Amiga system library).
3) "Graphics" Same as 1) but for the 'gfxbase' variable and object for the
graphics.library.
4) "Intuition." Same as 1) but for the 'intuitionbase' variable and
object for the intuition.library.
5) "This Process." Views the 'process' object for the running Explorer
program!
For a typical Explorer session, you would probably start at one of these
points and then double-click on elements and use the "Back Up" button to
browse around. You never really need to set the address explicitly on the
GUI (at worst you'd do it by ARexx), but the option is there if you really
want to...
Simple Types
------------
The "CHAR", "INT" and "LONG" buttons view the contents of the address as if
it were a pointer to the corresponding type, and fills in the 'Hex:',
'Decimal:' and 'String:' texts with the value(s) being pointed at.
"BSTR" is similar to "CHAR" but it first converts the address from a BCPL
pointer and then skips over the length byte. "BADDR" just converts the
address to a BCPL pointer.
"--" and "++" treat the address as if it were pointing to an array of the
type last requested (i.e., an object or simple type). "--" moves the
address back to the previous element in the array, and "++" moves it on to
the next. The size of the last requested type is shown as the 'Size:'
text. (Note: "--" can be activated also by the "p" key and "++" by the "n"
key.)
Display Beeps!
--------------
The screen will flash (or you'll get a beep or whatever your Workbench is
set up to do) if you do one of the following:
1) Select something when the address is NIL (zero). A NIL address is not
valid (although Explorer will behave as if it were).
2) Select an element which is a NIL pointer or a LONG that is NIL.
3) Select an element with object or PTR TO object type, where the object
is not in the current list of objects.
4) Try to backup past a depth of zero.
Command Line Options
--------------------
The command line template is:
OBJECT,ADDR,ALL/S,CACHE/S,NONE/S,CONFIG/K,SCREEN/K,NOAUTOREPLY/S
1) OBJECT Specify an initial object to be displayed (to be useful, the
appropriate module must also be loaded using 'ALL' or 'CACHE').
2) ADDR Specify an initial address to be displayed (NIL is ignored!).
3) ALL Load all objects from the modules in Emodules: and its
sub-directories (overrides 'CACHE' and 'CONFIG').
4) CACHE Load all objects from the modules in the current E module cache
(overrides 'CONFIG').
5) NONE Start with no objects (overrides 'ALL', 'CACHE' and 'CONFIG').
6) CONFIG Specify the config file to load. If this load fails then
Explorer will try "s:explorer.config".
7) SCREEN Specify the public screen to open on. Normally Explorer will
open on the default public screen.
8) NOAUTOREPLY Specifies that the "Auto Reply" option (described below)
should start as off rather than on.
By default, Explorer will try to load the "explorer.config" in the current
directory (as if you'd used the CONFIG option -- so it will fall back to
trying "s:explorer.config"). The starting address will be that of an
internal hail and copyright string (which will be displayed as the
'String:' text).
ARexx
-----
The "Reply" menu item on the "ARexx" menu manually replies to an ARexx
message that may have been received. You can also let Explorer
automatically reply to these messages by checking the "Auto Reply" option,
but the manual method is best since you can be sure that the pointers
remain valid while you browse (since the calling program is halted). Once
you've replied to a message the calling program is free to scribble all
over the memory at the address it just gave you...
A module 'sendexplorer' is supplied to simplify the debugging facilities of
Explorer. It defines several functions:
1) PROC sendExplorer(addr,obj=NIL,repPort=NIL,message=NIL,quiet=FALSE)
'addr' is the address you wish to use.
'obj' is the name of the object you are interested in.
'repPort' is a message port for replies. If you don't supply this then a
temporary port is created for the duration of the function call.
(The purpose of this parameter is to allow you to re-use the
same port during your program if you call 'sendExplorer' a lot.)
'message' is a short string to be displayed in the 'Message:' part of
Explorer. Use this to communicate some information about the
address and object (EDBG uses this to give the name, type and
scope of the variable being explored).
'quiet' is a boolean to say whether Explorer should complain if the
object is not loaded.
The result is TRUE if the message was successfully delivered and did
not cause an ARexx error, and FALSE otherwise.
An example call is:
sendExplorer(packet, 'dospacket')
To browse using your own objects you'd separate them out into modules and
load these modules in your program. Compiling your program would add these
to the E module cache, which can then be used to set-up Explorer (using the
"Load Cache" menu item). Alternatively, you could "Load" or "Merge" the
appropriate module directly, or use a config file if the objects don't
change very often.
2) PROC quitExplorer(repPort=NIL)
'repPort' is as above.
This sends a 'QUIT' message to Explorer. Again, it returns TRUE if
everything succeeded.
3) PROC isExplorerRunning()
Returns TRUE if Explorer is running. It works this out by testing for
the presence of Explorer's ARexx port, so it may be fooled..
Altering Data
-------------
By popular demand, if you hold down the shift key when you click on an
element of an object or on the 'CHAR', 'INT' or 'LONG' buttons, then a
small window pops up which allows you to edit the appropriate value. It's
not very safe to randomly edit data (especially system data), so use of
this feature is discouraged unless you really know what you're doing...
Example Program
---------------
To show how useful Explorer can be, there is a modified version of the
'empty' handler supplied. This uses the above call to 'sendExplorer' to
communicate the value of the packet currently being processed. Since the
handler is not a full process (it's just a task) there would normally be no
simple way of debugging it or examining the packets as they arrive (none of
the DOS functions, such as printing text, are available to simple tasks).
However, the handler can send messages to ARexx and thus to Explorer.
To try it out:
1) Make sure ARexx is up (by running RexxMast, if necessary).
2) Start up Explorer using,
run explorer all
3) Now copy the 'empty-handler' to L:,
copy empty-handler L:
4) Mount it using,
mount empty: from empty.mountlist
5) Use the handler,
type empty:10
6) Look at Explorer. It will be displaying the first packet. Browse
around it, remembering that most elements will be BCPL strings.
7) Select "Reply" from the ARexx menu (or press RAmiga-R) to reply to this
message.
8) Another packet arrives and the message is sent to Explorer.
9) Repeat 6)-8) until the 'type' command finishes and you get the Shell
prompt back.
EDBG
----
EDBG v3.3a+ communicates data about a variable if you hold down the shift
key and double-click on a variable that's within the current scope (EDBG
also adds it to the list of watched variables, if necessary).

BIN
amigae33a/E_v3.3a/Bin/build Normal file

Binary file not shown.

BIN
amigae33a/E_v3.3a/Bin/d Normal file

Binary file not shown.

BIN
amigae33a/E_v3.3a/Bin/edbg Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,497 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Common Problems"
@Next "Other Information"
@Prev "Examples.guide/Recursion Example"
@Toc "Contents.guide/main"
Common Problems
***************
If you are new to programming or the Amiga E language then you might
appreciate some help locating problems (or @{fg shine }bugs@{fg text }) in your programs. This
Appendix details some of the most common mistakes people make.
@{" Assignment and Copying " Link "Assignment and Copying" }
@{" Pointers and Memory Allocation " Link "Pointers and Memory Allocation" }
@{" String and List Misuse " Link "String and List Misuse" }
@{" Initialising Data " Link "Initialising Data" }
@{" Freeing Resources " Link "Freeing Resources" }
@{" Pointers and Dereferencing " Link "Pointers and Dereferencing" }
@{" Mathematics Functions " Link "Mathematics Functions" }
@{" Signed and Unsigned Values " Link "Signed and Unsigned Values" }
@ENDNODE
@NODE "Assignment and Copying" "Assignment and Copying"
@Next "Pointers and Memory Allocation"
@Toc "main"
Assignment and Copying
======================
This is probably the most common problem encountered by people who are
used to languages like BASIC. Strings, lists, arrays and objects cannot
be initialised using an assignment statement: data must be copied. Unlike
BASIC, this kind of data is represented by a pointer (see @{"PTR Type" Link "Types.guide/PTR Type" }), so
only the pointer would be copied by an assignment statement, not the data
it points to. The following examples all copy a pointer rather than the
data, and so the memory for the data is shared (and this is probably not
what was intended).
DEF s[30]:STRING, t[30]:STRING,
l[10]:LIST, m[10]:LIST,
x:myobj, y:myobj,
a[25]:ARRAY OF INT, b[25]:ARRAY OF INT
/* You probably don't want to do any of these */
s:='Some text in a string'
l:=[-6,4,-9]
x:=[1,2,3]:myobj
a:=[1,-3,8,7]:INT
t:=s
m:=l
y:=x
b:=a
All the declarations allocate memory for the appropriate data. The first
four assignments replace the pointers to this memory with pointers to some
statically allocated memory. The memory allocated by the declarations is
probably now unreachable, because the only pointers to it have been
over-written. BASIC programmers might expect, say, the assignment to @{b }s@{ub } to
have copied the string into the memory allocated for @{b }s@{ub } by its declaration,
but this is not the case (only the pointer to the string is copied).
For the E-string, @{b }s@{ub }, and E-list, @{b }l@{ub }, there is another, disastrous
side-effect. The assignment to @{b }s@{ub }, for example, means that @{b }s@{ub } will point to
a normal string, not an E-string. So, @{b }s@{ub } can no longer be used with any of
the E-string functions. The same considerations apply to the E-list, @{b }l@{ub },
as well.
The final four assignments also copy only the pointers. This means
that @{b }s@{ub } and @{b }t@{ub } will point to exactly the same memory. So they will
represent exactly the same string, and any change to one of them (by a
@{b }StrAdd@{ub }, for example) will appear to change both (of course, only one lump
of memory is being changed, but there are two references to it). This is
called memory @{fg shine }sharing@{fg text }, and is only a problem if you didn't intend to do
it!
To get the result that a BASIC programmer might have intended you need
to copy the appropriate data. For E-strings and E-lists the functions to
use are, respectively, @{b }StrCopy@{ub } and @{b }ListCopy@{ub }. All other data must be
copied using a function like @{b }CopyMem@{ub } (an Amiga system function from the
Exec library). (Normal strings can be copied using @{b }AstrCopy@{ub } built-in
function, see the `Reference Manual'.) Here's the revised forms of the
above assignments:
DEF s[30]:STRING, t[30]:STRING,
l[10]:LIST, m[10]:LIST,
x:myobj, y:myobj,
a[25]:ARRAY OF INT, b[25]:ARRAY OF INT
StrCopy(s, 'Some text in a string') /* Defaults to ALL */
ListCopy(l, [-6,4,-9]) /* Defaults to ALL */
CopyMem([1,2,3]:myobj, x, SIZEOF myobj)
CopyMem([1,-3,8,7]:INT, a, 4*SIZEOF INT)
StrCopy(t, s) /* Defaults to ALL */
ListCopy(m, l) /* Defaults to ALL */
CopyMem(x, y, SIZEOF myobj)
CopyMem(a, b, 4*SIZEOF INT)
Notice that you need to supply the size (in bytes) of the data being
copied when you use @{b }CopyMem@{ub }. The parameters are also given in a slightly
different order to the E-string and E-list copying functions (i.e., the
source must be the first parameter and the destination the second). The
@{b }CopyMem@{ub } function does a byte-by-byte copy, something like this:
PROC copymem(src, dest, size)
DEF i
FOR i:=1 TO size DO dest[]++:=src[]++
ENDPROC
Of course, you can use string constants and lists to give initialised
arrays, but in this case you should be initialising an appropriately typed
pointer. You must also be careful not to run into a static data problem
(see @{"Static data" Link "Types.guide/Static data" }).
DEF s:PTR TO CHAR, l:PTR TO LONG, x:PTR TO myobj, a:PTR TO INT
s:='Some text in a string'
l:=[-6,4,-9]
x:=[1,2,3]:myobj
a:=[1,-3,8,7]:INT
@ENDNODE
@NODE "Pointers and Memory Allocation" "Pointers and Memory Allocation"
@Next "String and List Misuse"
@Prev "Assignment and Copying"
@Toc "main"
Pointers and Memory Allocation
==============================
Another common error is to declare a pointer (usually a pointer to an
object) and then use it without the memory for the target data being
allocated.
/* You don't want to do this */
DEF p:PTR TO object
p.element:=99
There are two ways of correcting this: either dynamically allocate the
memory using @{b }NEW@{ub } or, more simply, let an appropriate declaration allocate
it. See @{"Memory Allocation" Link "Memory.guide/main" }.
DEF p:PTR TO object
NEW p
p.element:=99
DEF p:object
p.element:=99
@ENDNODE
@NODE "String and List Misuse" "String and List Misuse"
@Next "Initialising Data"
@Prev "Pointers and Memory Allocation"
@Toc "main"
String and List Misuse
======================
Some of the string functions can only be used with E-strings.
Generally, these are the ones that might extend the string. If you use a
normal string instead you can run into some serious (but subtle) problems.
Commonly misused functions are @{b }ReadStr@{ub }, @{b }MidStr@{ub } and @{b }RightStr@{ub }. Similar
problems can arise by using a list when an E-list is required by a list
function.
String constants and normal lists are static data, so you shouldn't try
to alter their contents unless you know what you're doing (see
@{"Static data" Link "Types.guide/Static data" }).
@ENDNODE
@NODE "Initialising Data" "Initialising Data"
@Next "Freeing Resources"
@Prev "String and List Misuse"
@Toc "main"
Initialising Data
=================
Probably one of the most common mistakes that even seasoned programmers
make is to forget to initialise variables (especially pointers). The
rules in the `Reference Manual' state which declarations initialise
variables to zero values, but it is often wise to make even these explicit
(using initialised declarations). Variable initialisation becomes even
more important when using automatic exceptions.
@ENDNODE
@NODE "Freeing Resources" "Freeing Resources"
@Next "Pointers and Dereferencing"
@Prev "Initialising Data"
@Toc "main"
Freeing Resources
=================
Unlike a Unix operating system, the Amiga operating system requires the
programmer to release or free any resources used by a program. In
practice, this means that all windows, screens, libraries, etc., that are
successfully opened must be closed before the program terminates. Amiga E
provides some help, though: the four most commonly used libraries (Dos,
Exec, Graphics and Intuition) are opened before the start of an E program
and closed at the end (or when @{b }CleanUp@{ub } is called). Also, memory allocated
using any of @{b }List@{ub }, @{b }String@{ub }, @{b }New@{ub }, @{b }NEW@{ub }, @{b }NewR@{ub }, @{b }NewM@{ub } and @{b }FastNew@{ub } is
automatically freed at the end of a program.
@ENDNODE
@NODE "Pointers and Dereferencing" "Pointers and Dereferencing"
@Next "Mathematics Functions"
@Prev "Freeing Resources"
@Toc "main"
Pointers and Dereferencing
==========================
C programmers may think that the @{b } ^@{ub }@{fg shine }var@{fg text }@{b }@{ub } and @{b }{@{ub }@{fg shine }var@{fg text } @{b } }@{ub } expressions
are the direct equivalent of C's @{b } &@{ub }@{fg shine }var@{fg text }@{b }@{ub } and @{b } *@{ub }@{fg shine }var@{fg text }@{b }@{ub } expressions.
However, in E dereferencing is normally achieved using array and object
element selection, and pointers to large amounts of data (like E-strings
or objects) are made by declarations. This means that the @{b } ^@{ub }@{fg shine }var@{fg text }@{b }@{ub } and
@{b }{@{ub }@{fg shine }var@{fg text } @{b } }@{ub } expressions are rarely used, whilst @{b }@{ub }@{fg shine }var@{fg text }@{b }[]@{ub } is very
common.
@ENDNODE
@NODE "Mathematics Functions" "Mathematics Functions"
@Next "Signed and Unsigned Values"
@Prev "Pointers and Dereferencing"
@Toc "main"
Mathematics Functions
=====================
The standard mathematical operators @{b }/@{ub } and @{b }*@{ub } do not use full 32-bit
values in their calculations, as noted previously (see
@{"Maths and logic functions" Link "BuiltIns.guide/Maths and logic functions" }). A common problem is to forget this and use
them where the values will exceed the 16-bit limit. A typical example is
the position calculations used with proportional gadgets. See
@{"Signed and Unsigned Values" Link "Signed and Unsigned Values" }.
@ENDNODE
@NODE "Signed and Unsigned Values" "Signed and Unsigned Values"
@Prev "Mathematics Functions"
@Toc "main"
Signed and Unsigned Values
==========================
This is a quite advanced topic, but might be the cause of some strange
bugs in your programs. Basically, E does not have a way of
differentiating signed and unsigned values from, say, the @{b }LONG@{ub } type. That
is, all values from the 32-bit, @{b }LONG@{ub } type are considered to be signed
values, so the range of values is from -2,147,483,648 to 2,147,483,647.
If the values from this type were taken to be unsigned then no negative
values would be allowed but more positive values would be possible (i.e.,
the range of values would be from zero to 4,294,967,295). This
distinction would also affect the mathematical operators.
In practice, though, it is not the @{b }LONG@{ub } type that can cause problems.
Instead, it is the 16-bit, @{b }INT@{ub } type, which again is considered to be
signed. This means that the range of values is -32,768 to 32,767.
However, the Amiga system objects contain a number of 16-bit, @{b }INT@{ub } elements
which are actually interpreted as unsigned, ranging from zero to 65,535.
A prominent example is the proportional gadget which forms a part of a
scroll-bar on a window (for example, a drawer window on Workbench). This
works with unsigned 16-bit values, which is at odds with the @{b }INT@{ub } type in E.
These values are commonly used in calculations to determine the position
of something displayed in a window, and if the @{b }INT@{ub } type is used without
taking into account this signed/unsigned problem the results can be quite
wrong. Luckily it is very simple to convert the signed @{b }INT@{ub } values into
unsigned values if they are part of some expression, since the value of
any expression is taken from the @{b }LONG@{ub } type (and unsigned @{b }INT@{ub } values fit
well within the range of even signed @{b }LONG@{ub } values).
PROC unsigned_int(x) IS x AND $FFFF
The function @{b }unsigned_int@{ub }, above, is very specific to the way the Amiga
handles values internally, so to understand how it works is beyond the
scope of this Guide. It should be used wherever an unsigned 16-bit value
is stored in an @{b }INT@{ub } element of, say, an Amiga system object. For example,
the position of the top of a (vertical) proportional gadget as a
percentage (zero to one hundred) of its size can be calculated like this:
/* propinfo is from the module 'intuition/intuition' */
DEF gad:PTR TO propinfo, pct
/* Set up gad... */
/* Calculate percentage (MAXPOT is from 'intuition/intuition') */
pct:=Div(Mul(100,unsigned_int(gad.vertpot)),MAXPOT)
Notice that the full 32-bit functions @{b }Div@{ub } and @{b }Mul@{ub } need to be used since
the arithmetic may be well over the normal 16-bits used in the @{b }/@{ub } and @{b }*@{ub }
operators.
The remaining type, @{b }CHAR@{ub }, is not, in practice, a problem. It is the
only unsigned type, with a range of values from zero to 255. There is a
fairly simple way to convert these values to signed values (and again this
is particular to the way the Amiga stores values internally). One good
example of a signed @{b }CHAR@{ub } value is the priority value associated with a
node of an Amiga list (i.e., the @{b }pri@{ub } element of an @{b }ln@{ub } object from the
module @{b }exec/nodes@{ub }).
PROC signed_char(x) IS IF x<128 THEN x ELSE x-256
@ENDNODE
@NODE "Other Information" "Other Information"
@Next "EIndex.guide/main"
@Prev "main"
@Toc "Contents.guide/main"
Other Information
*****************
This Appendix contains some useful, miscellaneous information.
@{" Amiga E Versions " Link "Amiga E Versions" }
@{" Further Reading " Link "Further Reading" }
@{" Amiga E Author " Link "Amiga E Author" }
@{" Guide Author " Link "Guide Author" }
@ENDNODE
@NODE "Amiga E Versions" "Amiga E Versions"
@Next "Further Reading"
@Toc "Other Information"
Amiga E Versions
================
As I write, the current version of Amiga E is version 3.2e (which is
minor update of v3.2a). This edition of the Guide is based primarily on
that version, but the majority still applies to the older versions,
including the last Public Domain version (v2.1b). Version 3.3 is
imminent, and the new `Reference Manual' will contain details of the new
features and changes.
Please note that, as of v3.0a, Amiga E is a commercial product so you
must pay a fee to get a version of the full compiler (which will be
registered to you). The Public Domain distribution contains only a
demonstration version of the compiler, with limited functionality. See
the `Reference Manual' for more details.
@ENDNODE
@NODE "Further Reading" "Further Reading"
@Next "Amiga E Author"
@Prev "Amiga E Versions"
@Toc "Other Information"
Further Reading
===============
`Amiga E Language Reference'
Referred to as the `Reference Manual' in this Guide. This is one of
the documents that comes with the Amiga E package, and is essential
reading since it was written by Wouter (the author of Amiga E). It
contains a lot of extra information.
`Rom Kernel Reference Manual' (Addison-Wesley)
This is the official Commodore documentation on the Amiga system
functions and is a must if you want to use these functions properly.
At the time of writing the Third Edition is the most current and it
covers the Amiga system functions up to Release 2 (i.e., AmigaDOS
2.04 and KickStart 37). Because there is so much information it
comes in three separate volumes: `Libraries', `Includes and
Autodocs', and `Devices'. The `Libraries' volume is probably the
most useful as it contains many examples and a lot of tutorial
material. However, the examples are written mainly in C (the
remainder are in Assembly). To alleviate this problem I have
undertaken to re-code them in E, and Part One and Part Two of this
effort should be available from Aminet or good PD houses (the archive
names will be something like @{b }JRH-RKRM-1@{ub } and @{b }JRH-RKRM-2@{ub }).
(Unfortunately, it seems that the actual manuals may be hard to find
since there are now out-of-print.)
`The AmigaDOS Manual' (Bantam Books)
This is the companion to the `Rom Kernel Reference Manual' and is the
official Commodore book on AmigaDOS (both the AmigaDOS programs and
the DOS library functions). Again, the Third Edition is the most
current.
Example sources
Amiga E comes with a large collection of example programs. When
you're familiar with the language you should be able to learn quite a
bit from these. There are a lot of small, tutorial programs and a
few large, complicated programs.
@ENDNODE
@NODE "Amiga E Author" "Amiga E Author"
@Next "Guide Author"
@Prev "Further Reading"
@Toc "Other Information"
Amiga E Author
==============
In case you didn't know the author and creator of Amiga E is Wouter van
Oortmerssen (or @{b }$#%!@{ub }). You can reach him by normal mail at the following
address:
Wouter van Oortmerssen (@{b }$#%!@{ub })
Levendaal 87
2311 JG Leiden
HOLLAND
However, he much prefers to chat by E-mail, and you can reach him at the
following addresses:
@{b }Wouter@alf.let.uva.nl@{ub } (E-programming support)
@{b }Wouter@mars.let.uva.nl@{ub } (personal)
@{b }Oortmers@gene.fwi.uva.nl@{ub } (other)
Better still, if your problem or enquiry is of general interest to Amiga E
users you may find it useful joining the Amiga E mailing list. Wouter
regularly contributes to this list and there are a number of good
programmers who are at hand to help or discuss problems. To join send a
message to:
@{b }amigae-request@bkhouse.cts.com@{ub }
Once you're subscribed, you will receive a copy of each message mailed to
the list. You will also receive a message telling you how you can
contribute (i.e., ask questions!).
@ENDNODE
@NODE "Guide Author" "Guide Author"
@Prev "Amiga E Author"
@Toc "Other Information"
Guide Author
============
This Guide was written by Jason Hulance, with a lot of help and
guidance from Wouter. The original aim was to produce something that
might be a useful introduction to Amiga E for beginners, so that the
language could (rightly) become more widespread. The hidden agenda was to
free Wouter from such a task so that he could concentrate his efforts on
improving Amiga E.
You can reach me by normal mail most easily at the following (work)
address:
Jason R. Hulance
Formal Systems (Europe) Ltd.
3 Alfred Street
Oxford
OX1 4EH
ENGLAND
Alternatively, you can find me on the Amiga E mailing list, or E-mail me
directly at one of the following addresses:
@{b }jason@fsel.com@{ub }
@{b }m88jrh@ecs.oxford.ac.uk@{ub }
If you have any changes or additions you'd like to see then I'd be very
happy to consider them. Criticism of the text is also welcome, especially
if you can suggest a better way of explaining things. I am also keen to
hear from people who can highlight areas that are particularly confusing
or badly worded!
@ENDNODE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,254 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Constants"
@Next "Types.guide/main"
@Prev "Procedures.guide/main"
@Toc "Contents.guide/main"
Constants
*********
A @{fg shine }constant@{fg text } is a value that does not change. A (literal) number like
121 is a good example of a constant--its value is always 121. We've
already met another kind of constant: string constants (see @{"Strings" Link "Introduction.guide/Strings" }). As
you can doubtless tell, constants are pretty important things.
@{" Numeric Constants " Link "Numeric Constants" }
@{" String Constants Special Character Sequences " Link "String Constants Special Character Sequences" }
@{" Named Constants " Link "Named Constants" }
@{" Enumerations " Link "Enumerations" }
@{" Sets " Link "Sets" }
@ENDNODE
@NODE "Numeric Constants" "Numeric Constants"
@Next "String Constants Special Character Sequences"
@Toc "main"
Numeric Constants
=================
We've met a lot of numbers in the previous examples. Technically
speaking, these were numeric constants (constant because they don't change
value like a variable might). They were all decimal numbers, but you can
use hexadecimal and binary numbers as well. There's also a way of
specifying a number using characters. To specify a hexadecimal number you
use a @{b }$@{ub } before the digits (and after the optional minus sign @{b }-@{ub } to
represent a negative value). To specify a binary number you use a @{b }%@{ub }
instead.
Specifying numbers using characters is more complicated, because the
base of this system is 256 (the base of decimal is ten, that of
hexadecimal is 16 and that of binary is two). The digits are enclosed in
double-quotes (the " character), and there can be at most four digits.
Each digit is a character representing its ASCII value. Therefore, the
character @{b }A@{ub } represents 65 and the character @{b }0@{ub } (zero) represents 48. This
upshot of this is that character @{b }A@{ub } has ASCII value @{b }"A"@{ub } in E, and @{b }"0z"@{ub }
represents ("0" * 256) + "z" = (48 * 256) + 122 = 12,410. However, you
probably don't need to worry about anything other than the single
character case, which gives you the ASCII value of the character.
The following table shows the decimal value of several numeric
constants. Notice that you can use upper- or lower-case letters for the
hexadecimal constants. Obviously the case of characters is significant
for character numbers.
@{i }Number@{ui } @{i }Decimal value@{ui }
----------------------
21 21
-143 -143
$1a 26
-$B1 -177
%1110 14
-%1010 -10
"z" 122
"Je" 19,045
-"A" -65
@ENDNODE
@NODE "String Constants Special Character Sequences" "String Constants Special Character Sequences"
@Next "Named Constants"
@Prev "Numeric Constants"
@Toc "main"
String Constants: Special Character Sequences
=============================================
We have seen that in a string the character sequence @{b }\\n@{ub } means a
linefeed (see @{"Strings" Link "Introduction.guide/Strings" }). There are several other similar such special
character sequences which represent useful characters that can't be typed
in a string. The following table shows all these sequences. Note that
there are some other similar sequences which are used to control
formatting with built-in procedures like @{b }WriteF@{ub }. These are listed where
@{b }WriteF@{ub } and similar procedures are described (see
@{"Input and output functions" Link "BuiltIns.guide/Input and output functions" }).
@{i }Sequence@{ui } @{i }Meaning@{ui }
--------------------------------------
\\0 A null (ASCII zero)
\\a An apostrophe '
\\b A carriage return (ASCII 13)
\\e An escape (ASCII 27)
\\n A linefeed (ASCII 10)
\\q A double quote (ASCII 34)
\\t A tab (ASCII 9)
\\\\ A backslash \\
An apostrophe can also be produced by typing two apostrophes in a row in a
string. It's best to use this only in the middle of a string, where it's
nice and obvious:
WriteF('Here\\as an apostrophe.\\n') /* Using \\a */
WriteF('Here''s another apostrophe.\\n') /* Using '' */
@ENDNODE
@NODE "Named Constants" "Named Constants"
@Next "Enumerations"
@Prev "String Constants Special Character Sequences"
@Toc "main"
Named Constants
===============
It is often nice to be able to give names to certain constants. For
instance, as we saw earlier, the truth value @{b }TRUE@{ub } actually represents the
value -1, and @{b }FALSE@{ub } represents zero (see @{"Logic and comparison" Link "Introduction.guide/Logic and comparison" }). These are
our first examples of named constants. To define your own you use the
@{b }CONST@{ub } keyword as follows:
CONST ONE=1, LINEFEED=10, BIG_NUM=999999
This has defined the constant @{b }ONE@{ub } to represent one, @{b }LINEFEED@{ub } ten and
@{b }BIG_NUM@{ub } 999,999. Named constants must begin with two uppercase letters,
as mentioned before (see @{"Identifiers" Link "Format.guide/Identifiers" }).
You can use previously defined constants to give the value of a new
constant, but in this case the definitions must occur on different @{b }CONST@{ub }
lines.
CONST ZERO=0
CONST ONE=ZERO+1
CONST TWO=ONE+1
The expression used to define the value of a constant can use only simple
operators (no function calls) and constants.
@ENDNODE
@NODE "Enumerations" "Enumerations"
@Next "Sets"
@Prev "Named Constants"
@Toc "main"
Enumerations
============
Often you want to define a whole lot of constants and you just want
them all to have a different value so you can tell them apart easily. For
instance, if you wanted to define some constants to represent some famous
cities and you only needed to know how to distinguish one from another
then you could use an @{fg shine }enumeration@{fg text } like this:
ENUM LONDON, MOSCOW, NEW_YORK, PARIS, ROME, TOKYO
The @{b }ENUM@{ub } keyword begins the definitions (like the @{b }CONST@{ub } keyword does for
an ordinary constant definition). The actual values of the constants
start at zero and stretch up to five. In fact, this is exactly the same
as writing:
CONST LONDON=0, MOSCOW=1, NEW_YORK=2, PARIS=3, ROME=4, TOKYO=5
The enumeration does not have to start at zero, though. You can change
the starting value at any point by specifying a value for an enumerated
constant. For example, the following constant definitions are equivalent:
ENUM APPLE, ORANGE, CAT=55, DOG, GOLDFISH, FRED=-2,
BARNEY, WILMA, BETTY
CONST APPLE=0, ORANGE=1, CAT=55, DOG=56, GOLDFISH=57,
FRED=-2, BARNEY=-1, WILMA=0, BETTY=1
@ENDNODE
@NODE "Sets" "Sets"
@Prev "Enumerations"
@Toc "main"
Sets
====
Yet another kind of constant definition is the @{fg shine }set@{fg text } definition. This
useful for defining flag sets, i.e., a number of options each of which can
be on or off. The definition is like a simple enumeration, but using the
@{b }SET@{ub } keyword and this time the values start at one and increase as powers
of two (so the next value is two, the next is four, the next eight, and so
on). Therefore, the following definitions are equivalent:
SET ENGLISH, FRENCH, GERMAN, JAPANESE, RUSSIAN
CONST ENGLISH=1, FRENCH=2, GERMAN=4, JAPANESE=8, RUSSIAN=16
However, the significance of the values it is best shown by using binary
constants:
CONST ENGLISH=%00001, FRENCH=%00010, GERMAN=%00100,
JAPANESE=%01000, RUSSIAN=%10000
If a person speaks just English then we can use the constant @{b }ENGLISH@{ub }. If
they also spoke Japanese then to represent this with a single value we'd
normally need a new constant (something like @{b }ENG_JAP@{ub }). In fact, we'd
probably need a constant for each combination of languages a person might
know. However, with the set definition we can @{b }OR@{ub } the @{b }ENGLISH@{ub } and @{b }JAPANESE@{ub }
values together to get a new value, @{b }%01001@{ub }, and this represents a set
containing both @{b }ENGLISH@{ub } and @{b }JAPANESE@{ub }. On the other hand, to find out if
someone speaks French we would @{b }AND@{ub } the value for the languages they know
with @{b }%00010@{ub } (or the constant @{b }FRENCH@{ub }). (As you might have guessed, @{b }AND@{ub } and
@{b }OR@{ub } are really bit-wise operators, not simply logical operators. See
@{"Bitwise AND and OR" Link "MoreExpressions.guide/Bitwise AND and OR" }.)
Consider this program fragment:
speak:=GERMAN OR ENGLISH OR RUSSIAN /* Speak any of these */
IF speak AND JAPANESE
WriteF('Can speak in Japanese\\n')
ELSE
WriteF('Unable to speak in Japanese\\n')
ENDIF
IF speak AND (GERMAN OR FRENCH)
WriteF('Can speak in German or French\\n')
ELSE
WriteF('Unable to speak in German or French\\n')
ENDIF
The assignment sets @{b }speak@{ub } to show that the person can speak in German,
English or Russian. The first @{b }IF@{ub } block tests whether the person can speak
in Japanese, and the second tests whether they can speak in German or
French.
When using sets be careful you don't get tempted to add values instead
of @{b }OR@{ub }-ing them. Adding two different constants from the same set is the
same as @{b }OR@{ub }-ing them, but adding the same set constant to itself isn't.
This is not the only time addition doesn't give the same answer, but it's
the most obvious. If you to stick to using @{b }OR@{ub } you won't have a problem.
@ENDNODE

View File

@ -0,0 +1,69 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "beginner.guide"
Copyright (c) 1994-1997, Jason R. Hulance.
A Beginner's Guide to Amiga E
*****************************
This Guide gives an introduction to the Amiga E programming language
and, to some extent, programming in general.
Part One: Getting Started
@{" Introduction to Amiga E " Link "Introduction.guide/main" }
@{" Understanding a Simple Program " Link "Introduction.guide/Understanding a Simple Program" }
@{" Variables and Expressions " Link "Introduction.guide/Variables and Expressions" }
@{" Program Flow Control " Link "Introduction.guide/Program Flow Control" }
@{" Summary " Link "Introduction.guide/Summary" }
Part Two: The E Language
@{" Format and Layout " Link "Format.guide/main" }
@{" Procedures and Functions " Link "Procedures.guide/main" }
@{" Constants " Link "Constants.guide/main" }
@{" Types " Link "Types.guide/main" }
@{" More About Statements and Expressions " Link "MoreExpressions.guide/main" }
@{" E Built-In Constants Variables and Functions " Link "BuiltIns.guide/main" }
@{" Modules " Link "Modules.guide/main" }
@{" Exception Handling " Link "Exceptions.guide/main" }
@{" Memory Allocation " Link "Memory.guide/main" }
@{" Floating-Point Numbers " Link "FloatingPoint.guide/main" }
@{" Recursion " Link "Recursion.guide/main" }
@{" Object Oriented E " Link "OOE.guide/main" }
Part Three: Worked Examples
@{" Introduction to the Examples " Link "Examples.guide/main" }
@{" String Handling and I-O " Link "Examples.guide/String Handling and I-O" }
@{" Timing Expressions " Link "Examples.guide/Timing Expressions" }
@{" Argument Parsing " Link "Examples.guide/Argument Parsing" }
@{" Gadgets IDCMP and Graphics " Link "Examples.guide/Gadgets IDCMP and Graphics" }
@{" Recursion Example " Link "Examples.guide/Recursion Example" }
Part Four: Appendices
@{" Common Problems " Link "Appendices.guide/main" }
@{" Other Information " Link "Appendices.guide/Other Information" }
Indices
@{" E Language Index " Link "EIndex.guide/main" }
@{" Main Index " Link "Index.guide/main" }
@ENDNODE

View File

@ -0,0 +1,294 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "E Language Index"
@Next "Index.guide/main"
@Prev "Appendices.guide/Other Information"
@Toc "Contents.guide/main"
E Language Index
****************
This index should be used to find detailed information about the
keywords, functions, variables and constants which are part of the Amiga E
language. There is a separate index which deals with concepts etc. (see
@{"Main Index" Link "Index.guide/main" }).
@{" Symbol, close curly brace " Link "Types.guide/Finding addresses (making pointers)" 0 } Finding addresses (making pointers)
@{" Symbol, double-quote " Link "Constants.guide/Numeric Constants" 11 } Numeric Constants
@{" Symbol, open curly brace " Link "Types.guide/Finding addresses (making pointers)" 0 } Finding addresses (making pointers)
@{" Symbol, ! " Link "FloatingPoint.guide/Floating-Point Calculations" 0 } Floating-Point Calculations
@{" Symbol, $ " Link "Constants.guide/Numeric Constants" 0 } Numeric Constants
@{" Symbol, % " Link "Constants.guide/Numeric Constants" 0 } Numeric Constants
@{" Symbol, ' .. ' (string) " Link "Types.guide/Normal strings and E-strings" 0 } Normal strings and E-strings
@{" Symbol, * " Link "Introduction.guide/Mathematics" 0 } Mathematics
@{" Symbol, + " Link "Introduction.guide/Mathematics" 0 } Mathematics
@{" Symbol, + (strings) " Link "Format.guide/Statements" 58 } Statements
@{" Symbol, ++ " Link "Types.guide/Point to other elements" 0 } Point to other elements
@{" Symbol, - " Link "Introduction.guide/Mathematics" 0 } Mathematics
@{" Symbol, -- " Link "Types.guide/Point to other elements" 0 } Point to other elements
@{" Symbol, -> " Link "Format.guide/Comments" 0 } Comments
@{" Symbol, / " Link "Introduction.guide/Mathematics" 0 } Mathematics
@{" Symbol, /* .. */ " Link "Format.guide/Comments" 0 } Comments
@{" Symbol, : " Link "MoreExpressions.guide/Labelling and the JUMP statement" 0 } Labelling and the JUMP statement
@{" Symbol, := " Link "Introduction.guide/Assignment" 0 } Assignment
@{" Symbol, ; " Link "Format.guide/Statements" 18 } Statements
@{" Symbol, < " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" Symbol, <= " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" Symbol, <=> " Link "MoreExpressions.guide/Unification" 0 } Unification
@{" Symbol, <> " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" Symbol, = " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" Symbol, > " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" Symbol, >= " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" Symbol, [ .. , .. ] (list) " Link "Types.guide/Lists and E-lists" 12 } Lists and E-lists
@{" Symbol, [ .. , .. ]:type (typed list) " Link "Types.guide/Typed lists" 0 } Typed lists
@{" Symbol, [ .. ] (array) " Link "Types.guide/Tables of data" 0 } Tables of data
@{" Symbol, [] (array) " Link "Types.guide/Accessing array data" 55 } Accessing array data
@{" Symbol, \0 " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, \a " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, \b " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, \c " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
@{" Symbol, \d " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
@{" Symbol, \d " Link "Introduction.guide/Changing the example" 0 } Changing the example
@{" Symbol, \e " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, \h " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
@{" Symbol, \l " Link "BuiltIns.guide/Input and output functions" 54 } Input and output functions
@{" Symbol, \n " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, \n " Link "Introduction.guide/Strings" 0 } Strings
@{" Symbol, \q " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, \r " Link "BuiltIns.guide/Input and output functions" 54 } Input and output functions
@{" Symbol, \s " Link "BuiltIns.guide/Input and output functions" 16 } Input and output functions
@{" Symbol, \t " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, \z " Link "BuiltIns.guide/Input and output functions" 54 } Input and output functions
@{" Symbol, \\ " Link "Constants.guide/String Constants Special Character Sequences" 0 } String Constants Special Character Sequences
@{" Symbol, ^ " Link "Types.guide/Extracting data (dereferencing pointers)" 0 } Extracting data (dereferencing pointers)
@{" Symbol, ` (backquote) " Link "MoreExpressions.guide/Quoted Expressions" 0 } Quoted Expressions
@{" Abs " Link "BuiltIns.guide/Maths and logic functions" 30 } Maths and logic functions
@{" ALL " Link "BuiltIns.guide/Built-In Constants" 12 } Built-In Constants
@{" And " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
@{" AND " Link "MoreExpressions.guide/Bitwise AND and OR" 0 } Bitwise AND and OR
@{" arg " Link "BuiltIns.guide/Built-In Variables" 7 } Built-In Variables
@{" ARRAY " Link "Types.guide/Tables of data" 0 } Tables of data
@{" ARRAY OF type " Link "Types.guide/Tables of data" 0 } Tables of data
@{" Bounds " Link "BuiltIns.guide/Maths and logic functions" 55 } Maths and logic functions
@{" Box " Link "BuiltIns.guide/Graphics functions" 30 } Graphics functions
@{" BUT " Link "MoreExpressions.guide/BUT expression" 0 } BUT expression
@{" CASE " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
@{" CASE " Link "Introduction.guide/SELECT block" 0 } SELECT block
@{" CASE ..TO.. " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
@{" CHAR " Link "Types.guide/Indirect types" 0 } Indirect types
@{" Char " Link "BuiltIns.guide/Maths and logic functions" 116 } Maths and logic functions
@{" CHAR " Link "MoreExpressions.guide/Static memory" 0 } Static memory
@{" CleanUp " Link "BuiltIns.guide/System support functions" 65 } System support functions
@{" CloseS " Link "BuiltIns.guide/Intuition support functions" 178 } Intuition support functions
@{" CloseW " Link "BuiltIns.guide/Intuition support functions" 122 } Intuition support functions
@{" Colour " Link "BuiltIns.guide/Graphics functions" 35 } Graphics functions
@{" conout " Link "BuiltIns.guide/Built-In Variables" 34 } Built-In Variables
@{" CONST " Link "Constants.guide/Named Constants" 0 } Named Constants
@{" CtrlC " Link "BuiltIns.guide/System support functions" 74 } System support functions
@{" DEC " Link "MoreExpressions.guide/INC and DEC statements" 0 } INC and DEC statements
@{" DEF " Link "Introduction.guide/Variable declaration" 0 } Variable declaration
@{" DEFAULT " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
@{" DEFAULT " Link "Introduction.guide/SELECT block" 0 } SELECT block
@{" Dispose " Link "BuiltIns.guide/System support functions" 39 } System support functions
@{" DisposeLink " Link "BuiltIns.guide/System support functions" 44 } System support functions
@{" Div " Link "BuiltIns.guide/Maths and logic functions" 0 } Maths and logic functions
@{" DO, (FOR loop) " Link "Introduction.guide/FOR loop" 55 } FOR loop
@{" DO, (WHILE loop) " Link "Introduction.guide/WHILE loop" 57 } WHILE loop
@{" dosbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
@{" ELSE " Link "Introduction.guide/IF block" 0 } IF block
@{" ELSEIF " Link "Introduction.guide/IF block" 0 } IF block
@{" EMPTY " Link "OOE.guide/Inheritance in E" 275 } Inheritance in E
@{" end " Link "OOE.guide/Methods in E" 118 } Methods in E
@{" END " Link "Memory.guide/NEW and END Operators" 0 } NEW and END Operators
@{" ENDFOR " Link "Introduction.guide/FOR loop" 0 } FOR loop
@{" ENDIF " Link "Introduction.guide/IF block" 0 } IF block
@{" ENDLOOP " Link "MoreExpressions.guide/LOOP block" 0 } LOOP block
@{" ENDOBJECT " Link "Types.guide/Example object" 23 } Example object
@{" ENDPROC " Link "Introduction.guide/Procedure Definition" 0 } Procedure Definition
@{" ENDPROC value " Link "Procedures.guide/Functions" 23 } Functions
@{" ENDSELECT " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
@{" ENDSELECT " Link "Introduction.guide/SELECT block" 0 } SELECT block
@{" ENDWHILE " Link "Introduction.guide/WHILE loop" 0 } WHILE loop
@{" ENUM " Link "Constants.guide/Enumerations" 0 } Enumerations
@{" Eor " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
@{" EstrLen " Link "Types.guide/String functions" 125 } String functions
@{" Eval " Link "MoreExpressions.guide/Evaluation" 0 } Evaluation
@{" Even " Link "BuiltIns.guide/Maths and logic functions" 41 } Maths and logic functions
@{" EXCEPT " Link "Exceptions.guide/Procedures with Exception Handlers" 0 } Procedures with Exception Handlers
@{" EXCEPT DO " Link "Exceptions.guide/Raising an Exception" 73 } Raising an Exception
@{" exception " Link "Exceptions.guide/Raising an Exception" 36 } Raising an Exception
@{" exceptioninfo " Link "Exceptions.guide/Raising an Exception" 50 } Raising an Exception
@{" execbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
@{" Exists " Link "MoreExpressions.guide/Lists and quoted expressions" 46 } Lists and quoted expressions
@{" EXIT " Link "MoreExpressions.guide/EXIT statement" 0 } EXIT statement
@{" Fabs " Link "FloatingPoint.guide/Floating-Point Functions" 55 } Floating-Point Functions
@{" FALSE " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" FALSE " Link "BuiltIns.guide/Built-In Constants" 5 } Built-In Constants
@{" FastDispose " Link "BuiltIns.guide/System support functions" 59 } System support functions
@{" FastDisposeList " Link "Memory.guide/List and typed list allocation" 66 } List and typed list allocation
@{" FastNew " Link "BuiltIns.guide/System support functions" 50 } System support functions
@{" Fceil " Link "FloatingPoint.guide/Floating-Point Functions" 59 } Floating-Point Functions
@{" Fcos " Link "FloatingPoint.guide/Floating-Point Functions" 51 } Floating-Point Functions
@{" Fexp " Link "FloatingPoint.guide/Floating-Point Functions" 66 } Floating-Point Functions
@{" Ffloor " Link "FloatingPoint.guide/Floating-Point Functions" 59 } Floating-Point Functions
@{" FileLength " Link "BuiltIns.guide/Input and output functions" 154 } Input and output functions
@{" Flog " Link "FloatingPoint.guide/Floating-Point Functions" 73 } Floating-Point Functions
@{" Flog10 " Link "FloatingPoint.guide/Floating-Point Functions" 73 } Floating-Point Functions
@{" FOR " Link "Introduction.guide/FOR loop" 0 } FOR loop
@{" ForAll " Link "MoreExpressions.guide/Lists and quoted expressions" 29 } Lists and quoted expressions
@{" Forward " Link "Types.guide/Linked Lists" 56 } Linked Lists
@{" Fpow " Link "FloatingPoint.guide/Floating-Point Functions" 66 } Floating-Point Functions
@{" FreeStack " Link "BuiltIns.guide/System support functions" 79 } System support functions
@{" Fsin " Link "FloatingPoint.guide/Floating-Point Functions" 51 } Floating-Point Functions
@{" Fsqrt " Link "FloatingPoint.guide/Floating-Point Functions" 63 } Floating-Point Functions
@{" Ftan " Link "FloatingPoint.guide/Floating-Point Functions" 51 } Floating-Point Functions
@{" Gadget " Link "BuiltIns.guide/Intuition support functions" 187 } Intuition support functions
@{" GADGETSIZE " Link "BuiltIns.guide/Built-In Constants" 16 } Built-In Constants
@{" gfxbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
@{" HANDLE " Link "Exceptions.guide/Procedures with Exception Handlers" 0 } Procedures with Exception Handlers
@{" IF " Link "Introduction.guide/IF block" 0 } IF block
@{" IF, (expression) " Link "Introduction.guide/IF expression" 0 } IF expression
@{" INC " Link "MoreExpressions.guide/INC and DEC statements" 0 } INC and DEC statements
@{" INCBIN " Link "MoreExpressions.guide/Static memory" 0 } Static memory
@{" Inp " Link "BuiltIns.guide/Input and output functions" 109 } Input and output functions
@{" InStr " Link "Types.guide/String functions" 156 } String functions
@{" Int " Link "BuiltIns.guide/Maths and logic functions" 116 } Maths and logic functions
@{" INT " Link "MoreExpressions.guide/Static memory" 0 } Static memory
@{" INT " Link "Types.guide/Indirect types" 0 } Indirect types
@{" intuitionbase " Link "BuiltIns.guide/Built-In Variables" 51 } Built-In Variables
@{" IS " Link "Procedures.guide/One-Line Functions" 0 } One-Line Functions
@{" JUMP " Link "MoreExpressions.guide/Labelling and the JUMP statement" 0 } Labelling and the JUMP statement
@{" KickVersion " Link "BuiltIns.guide/System support functions" 84 } System support functions
@{" LeftMouse " Link "BuiltIns.guide/Intuition support functions" 273 } Intuition support functions
@{" Line " Link "BuiltIns.guide/Graphics functions" 27 } Graphics functions
@{" Link " Link "Types.guide/Linked Lists" 23 } Linked Lists
@{" LIST " Link "Types.guide/Lists and E-lists" 35 } Lists and E-lists
@{" List " Link "Types.guide/List functions" 12 } List functions
@{" ListAdd " Link "Types.guide/List functions" 47 } List functions
@{" ListCmp " Link "Types.guide/List functions" 28 } List functions
@{" ListCopy " Link "Types.guide/List functions" 37 } List functions
@{" ListItem " Link "Types.guide/List functions" 65 } List functions
@{" ListLen " Link "Types.guide/List functions" 55 } List functions
@{" ListMax " Link "Types.guide/List functions" 59 } List functions
@{" Long " Link "BuiltIns.guide/Maths and logic functions" 116 } Maths and logic functions
@{" LONG " Link "Types.guide/LONG Type" 0 } LONG Type
@{" LONG " Link "MoreExpressions.guide/Static memory" 0 } Static memory
@{" LONG, preliminary " Link "Introduction.guide/Variable types" 0 } Variable types
@{" LOOP " Link "MoreExpressions.guide/LOOP block" 0 } LOOP block
@{" LowerStr " Link "Types.guide/String functions" 169 } String functions
@{" main " Link "Introduction.guide/Procedures" 0 } Procedures
@{" MapList " Link "MoreExpressions.guide/Lists and quoted expressions" 8 } Lists and quoted expressions
@{" Max " Link "BuiltIns.guide/Maths and logic functions" 49 } Maths and logic functions
@{" MidStr " Link "Types.guide/String functions" 144 } String functions
@{" Min " Link "BuiltIns.guide/Maths and logic functions" 52 } Maths and logic functions
@{" Mod " Link "BuiltIns.guide/Maths and logic functions" 70 } Maths and logic functions
@{" MODULE " Link "Modules.guide/Using Modules" 0 } Using Modules
@{" Mouse " Link "BuiltIns.guide/Intuition support functions" 235 } Intuition support functions
@{" MouseX " Link "BuiltIns.guide/Intuition support functions" 259 } Intuition support functions
@{" MouseY " Link "BuiltIns.guide/Intuition support functions" 266 } Intuition support functions
@{" MsgCode " Link "BuiltIns.guide/Intuition support functions" 313 } Intuition support functions
@{" MsgIaddr " Link "BuiltIns.guide/Intuition support functions" 316 } Intuition support functions
@{" MsgQualifier " Link "BuiltIns.guide/Intuition support functions" 321 } Intuition support functions
@{" Mul " Link "BuiltIns.guide/Maths and logic functions" 0 } Maths and logic functions
@{" NEW " Link "Memory.guide/NEW and END Operators" 0 } NEW and END Operators
@{" New " Link "BuiltIns.guide/System support functions" 0 } System support functions
@{" NEWFILE " Link "BuiltIns.guide/Built-In Constants" 20 } Built-In Constants
@{" NewM " Link "BuiltIns.guide/System support functions" 19 } System support functions
@{" NewR " Link "BuiltIns.guide/System support functions" 14 } System support functions
@{" Next " Link "Types.guide/Linked Lists" 38 } Linked Lists
@{" NIL " Link "BuiltIns.guide/Built-In Constants" 8 } Built-In Constants
@{" Not " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
@{" OBJECT " Link "Types.guide/Example object" 23 } Example object
@{" OBJECT..OF " Link "OOE.guide/Inheritance in E" 0 } Inheritance in E
@{" Odd " Link "BuiltIns.guide/Maths and logic functions" 45 } Maths and logic functions
@{" OLDFILE " Link "BuiltIns.guide/Built-In Constants" 20 } Built-In Constants
@{" OpenS " Link "BuiltIns.guide/Intuition support functions" 129 } Intuition support functions
@{" OpenW " Link "BuiltIns.guide/Intuition support functions" 16 } Intuition support functions
@{" OR " Link "MoreExpressions.guide/Bitwise AND and OR" 0 } Bitwise AND and OR
@{" Or " Link "BuiltIns.guide/Maths and logic functions" 14 } Maths and logic functions
@{" Out " Link "BuiltIns.guide/Input and output functions" 99 } Input and output functions
@{" Plot " Link "BuiltIns.guide/Graphics functions" 16 } Graphics functions
@{" PrintF " Link "BuiltIns.guide/Input and output functions" 85 } Input and output functions
@{" PRIVATE " Link "OOE.guide/Data-Hiding in E" 0 } Data-Hiding in E
@{" PROC " Link "Introduction.guide/Procedure Definition" 0 } Procedure Definition
@{" PROC..OF " Link "OOE.guide/Methods in E" 0 } Methods in E
@{" PTR TO type " Link "Types.guide/PTR Type" 0 } PTR Type
@{" PUBLIC " Link "OOE.guide/Data-Hiding in E" 0 } Data-Hiding in E
@{" PutChar " Link "BuiltIns.guide/Maths and logic functions" 123 } Maths and logic functions
@{" PutInt " Link "BuiltIns.guide/Maths and logic functions" 123 } Maths and logic functions
@{" PutLong " Link "BuiltIns.guide/Maths and logic functions" 123 } Maths and logic functions
@{" Raise " Link "Exceptions.guide/Raising an Exception" 0 } Raising an Exception
@{" RAISE " Link "Exceptions.guide/Automatic Exceptions" 25 } Automatic Exceptions
@{" ReadStr " Link "BuiltIns.guide/Input and output functions" 114 } Input and output functions
@{" RealF " Link "FloatingPoint.guide/Floating-Point Functions" 19 } Floating-Point Functions
@{" RealVal " Link "FloatingPoint.guide/Floating-Point Functions" 10 } Floating-Point Functions
@{" REPEAT " Link "Introduction.guide/REPEAT..UNTIL loop" 0 } REPEAT..UNTIL loop
@{" ReThrow " Link "Exceptions.guide/Raise within an Exception Handler" 68 } Raise within an Exception Handler
@{" RETURN " Link "Procedures.guide/Functions" 32 } Functions
@{" RightStr " Link "Types.guide/String functions" 136 } String functions
@{" Rnd " Link "BuiltIns.guide/Maths and logic functions" 84 } Maths and logic functions
@{" RndQ " Link "BuiltIns.guide/Maths and logic functions" 93 } Maths and logic functions
@{" SELECT " Link "Introduction.guide/SELECT block" 0 } SELECT block
@{" SELECT " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
@{" SELECT..OF " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
@{" SelectList " Link "MoreExpressions.guide/Lists and quoted expressions" 62 } Lists and quoted expressions
@{" self " Link "OOE.guide/Methods in E" 67 } Methods in E
@{" SET " Link "Constants.guide/Sets" 0 } Sets
@{" SetColour " Link "BuiltIns.guide/Graphics functions" 49 } Graphics functions
@{" SetList " Link "Types.guide/List functions" 62 } List functions
@{" SetStdIn " Link "BuiltIns.guide/Input and output functions" 161 } Input and output functions
@{" SetStdOut " Link "BuiltIns.guide/Input and output functions" 170 } Input and output functions
@{" SetStdRast " Link "BuiltIns.guide/Graphics functions" 58 } Graphics functions
@{" SetStr " Link "Types.guide/String functions" 178 } String functions
@{" SetTopaz " Link "BuiltIns.guide/Graphics functions" 67 } Graphics functions
@{" Shl " Link "BuiltIns.guide/Maths and logic functions" 100 } Maths and logic functions
@{" Shr " Link "BuiltIns.guide/Maths and logic functions" 108 } Maths and logic functions
@{" Sign " Link "BuiltIns.guide/Maths and logic functions" 36 } Maths and logic functions
@{" SIZEOF " Link "MoreExpressions.guide/SIZEOF expression" 0 } SIZEOF expression
@{" stdin " Link "BuiltIns.guide/Built-In Variables" 34 } Built-In Variables
@{" stdout " Link "BuiltIns.guide/Built-In Variables" 34 } Built-In Variables
@{" stdrast " Link "BuiltIns.guide/Built-In Variables" 46 } Built-In Variables
@{" STEP " Link "Introduction.guide/FOR loop" 37 } FOR loop
@{" StrAdd " Link "Types.guide/String functions" 99 } String functions
@{" StrCmp " Link "Types.guide/String functions" 40 } String functions
@{" StrCopy " Link "Types.guide/String functions" 58 } String functions
@{" STRING " Link "Types.guide/Normal strings and E-strings" 49 } Normal strings and E-strings
@{" String " Link "Types.guide/String functions" 14 } String functions
@{" StringF " Link "BuiltIns.guide/Input and output functions" 90 } Input and output functions
@{" StrLen " Link "Types.guide/String functions" 108 } String functions
@{" STRLEN " Link "BuiltIns.guide/Built-In Constants" 24 } Built-In Constants
@{" StrMax " Link "Types.guide/String functions" 131 } String functions
@{" SUPER " Link "OOE.guide/Inheritance in E" 110 } Inheritance in E
@{" TextF " Link "BuiltIns.guide/Graphics functions" 42 } Graphics functions
@{" THEN " Link "Introduction.guide/IF block" 29 } IF block
@{" Throw " Link "Exceptions.guide/Raising an Exception" 0 } Raising an Exception
@{" TO " Link "Introduction.guide/FOR loop" 0 } FOR loop
@{" TO, (CASE range) " Link "Introduction.guide/SELECT..OF block" 0 } SELECT..OF block
@{" TO, (FOR loop) " Link "Introduction.guide/FOR loop" 0 } FOR loop
@{" TrimStr " Link "Types.guide/String functions" 161 } String functions
@{" TRUE " Link "Introduction.guide/Logic and comparison" 11 } Logic and comparison
@{" TRUE " Link "BuiltIns.guide/Built-In Constants" 5 } Built-In Constants
@{" UNTIL " Link "Introduction.guide/REPEAT..UNTIL loop" 0 } REPEAT..UNTIL loop
@{" UpperStr " Link "Types.guide/String functions" 174 } String functions
@{" Val " Link "Types.guide/String functions" 199 } String functions
@{" VOID " Link "MoreExpressions.guide/Turning an Expression into a Statement" 0 } Turning an Expression into a Statement
@{" WaitIMessage " Link "BuiltIns.guide/Intuition support functions" 282 } Intuition support functions
@{" WaitLeftMouse " Link "BuiltIns.guide/Intuition support functions" 324 } Intuition support functions
@{" wbmessage " Link "BuiltIns.guide/Built-In Variables" 23 } Built-In Variables
@{" WHILE " Link "Introduction.guide/WHILE loop" 0 } WHILE loop
@{" WriteF " Link "BuiltIns.guide/Input and output functions" 0 } Input and output functions
@ENDNODE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,405 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Exception Handling"
@Next "Memory.guide/main"
@Prev "Modules.guide/main"
@Toc "Contents.guide/main"
Exception Handling
******************
Often your program has to check the results of functions and do
different things if errors have occurred. For instance, if you try to
open a window (using @{b }OpenW@{ub }), you may get a @{b }NIL@{ub } pointer returned which
shows that the window could not be opened for some reason. In this case
you normally can't continue with the program, so you must tidy up and
terminate. Tidying up can sometimes involve closing windows, screens and
libraries, so sometimes your error cases can make your program cluttered
and messy. This is where exceptions come in--an @{fg shine }exception@{fg text } is simply an
error case, and @{fg shine }exception handling@{fg text } is dealing with error cases. The
exception handling in E neatly separates error specific code from the real
code of your program.
@{" Procedures with Exception Handlers " Link "Procedures with Exception Handlers" }
@{" Raising an Exception " Link "Raising an Exception" }
@{" Automatic Exceptions " Link "Automatic Exceptions" }
@{" Raise within an Exception Handler " Link "Raise within an Exception Handler" }
@ENDNODE
@NODE "Procedures with Exception Handlers" "Procedures with Exception Handlers"
@Next "Raising an Exception"
@Toc "main"
Procedures with Exception Handlers
==================================
A procedure with an exception handler looks like this:
PROC fred(params...) HANDLE
/* Main, real code */
EXCEPT
/* Error handling code */
ENDPROC
This is very similar to a normal procedure, apart from the @{b }HANDLE@{ub } and
@{b }EXCEPT@{ub } keywords. The @{b }HANDLE@{ub } keyword means the procedure is going to have
an exception handler, and the @{b }EXCEPT@{ub } keyword marks the end of the normal
code and the start of the exception handling code. The procedure works
just as normal, executing the code in the part before the @{b }EXCEPT@{ub }, but when
an error happens you can pass control to the exception handler (i.e., the
code after the @{b }EXCEPT@{ub } is executed).
@ENDNODE
@NODE "Raising an Exception" "Raising an Exception"
@Next "Automatic Exceptions"
@Prev "Procedures with Exception Handlers"
@Toc "main"
Raising an Exception
====================
When an error occurs (and you want to handle it), you @{fg shine }raise@{fg text } an
exception using either the @{b }Raise@{ub } or @{b }Throw@{ub } function. You call @{b }Raise@{ub } with a
number which identifies the kind of error that occurred. The code in the
exception handler is responsible for decoding the number and then doing
the appropriate thing. @{b }Throw@{ub } is very similar to @{b }Raise@{ub }, and the following
description of @{b }Raise@{ub } also applies to @{b }Throw@{ub }. The difference is that @{b }Throw@{ub }
takes a second argument which can be used to pass extra information to a
handler (usually a string). The terms `raising' and `throwing' an
exception can be used interchangeably.
When @{b }Raise@{ub } is called it immediately stops the execution of the current
procedure code and passes control to the exception handler of most recent
procedure which has a handler (which may be the current procedure). This
is a bit complicated, but you can stick to raising exceptions and handling
them in the same procedure, as in the next example:
CONST BIG_AMOUNT = 100000
ENUM ERR_MEM=1
PROC main() HANDLE
DEF block
block:=New(BIG_AMOUNT)
IF block=NIL THEN Raise(ERR_MEM)
WriteF('Got enough memory\\n')
EXCEPT
IF exception=ERR_MEM
WriteF('Not enough memory\\n')
ELSE
WriteF('Unknown exception\\n')
ENDIF
ENDPROC
This uses an exception handler to print a message saying there wasn't
enough memory if the call to @{b }New@{ub } returns @{b }NIL@{ub }. The parameter to @{b }Raise@{ub } is
stored in the special variable @{b }exception@{ub } in the exception handler part of
the code, so if @{b }Raise@{ub } is called with a number other than @{b }ERR_MEM@{ub } a message
saying "Unknown exception" will be printed.
Try running this program with a really large @{b }BIG_AMOUNT@{ub } constant, so
that the @{b }New@{ub } can't allocate the memory. Notice that the "Got enough
memory" is not printed if @{b }Raise@{ub } is called. That's because the execution
of the normal procedure code stops when @{b }Raise@{ub } is called, and control
passes to the appropriate exception handler. When the end of the
exception handler is reached the procedure is finished, and in this case
the program terminates because the procedure was the @{b }main@{ub } procedure.
If @{b }Throw@{ub } is used instead of @{b }Raise@{ub } then, in the handler, the special
variable @{b }exceptioninfo@{ub } will contain the value of the second parameter.
This can be used in conjunction with @{b }exception@{ub } to provide the handler with
more information about the error. Here's the above example re-written to
use @{b }Throw@{ub }:
CONST BIG_AMOUNT = 100000
ENUM ERR_MEM=1
PROC main() HANDLE
DEF block
block:=New(BIG_AMOUNT)
IF block=NIL THEN Throw(ERR_MEM, 'Not enough memory\\n')
WriteF('Got enough memory\\n')
EXCEPT
IF exception=ERR_MEM
WriteF(exceptioninfo)
ELSE
WriteF('Unknown exception\\n')
ENDIF
ENDPROC
An enumeration (using @{b }ENUM@{ub }) is a good way of getting different
constants for various exceptions. It's always a good idea to use
constants for the parameter to @{b }Raise@{ub } and in the exception handler, because
it makes everything a lot more readable: @{b }Raise(ERR_MEM)@{ub } is much clearer
than @{b }Raise(1)@{ub }. The enumeration starts at one because zero is a special
exception: it usually means that no error occurred. This is useful when
the handler does the same cleaning up that would normally be done when the
program terminates successfully. For this reason there is a special form
of @{b }EXCEPT@{ub } which automatically raises a zero exception when the code in the
procedure successfully terminates. This is @{b }EXCEPT DO@{ub }, with the @{b }DO@{ub }
suggesting to the reader that the exception handler is called even if no
error occurs. Also, the argument to the @{b }Raise@{ub } function defaults to zero
if it is omitted (see @{"Default Arguments" Link "Procedures.guide/Default Arguments" }).
So, what happens if you call @{b }Raise@{ub } in a procedure without an exception
handler? Well, this is where the real power of the handling mechanism
comes to light. In this case, control passes to the exception handler of
the most @{fg shine }recent@{fg text } procedure with a handler. If none are found then the
program terminates. `Recent' means one of the procedures involved in
calling your procedure. So, if the procedure @{b }fred@{ub } calls @{b }barney@{ub }, then when
@{b }barney@{ub } is being executed @{b }fred@{ub } is a recent procedure. Because the @{b }main@{ub }
procedure is where the program starts it is a recent procedure for every
other procedure in the program. This means, in practice:
@{b }*@{ub } If you define @{b }fred@{ub } to be a procedure with an exception handler then
any procedures called by @{b }fred@{ub } will have their exceptions handled by
the handler in @{b }fred@{ub } if they don't have their own handler.
@{b }*@{ub } If you define @{b }main@{ub } to be a procedure with an exception handler then
any exceptions that are raised will always be dealt with by some
exception handling code (i.e., the handler of @{b }main@{ub } or some other
procedure).
Here's a more complicated example:
ENUM FRED=1, BARNEY
PROC main()
WriteF('Hello from main\\n')
fred()
barney()
WriteF('Goodbye from main\\n')
ENDPROC
PROC fred() HANDLE
WriteF(' Hello from fred\\n')
Raise(FRED)
WriteF(' Goodbye from fred\\n')
EXCEPT
WriteF(' Handler fred: \\d\\n', exception)
ENDPROC
PROC barney()
WriteF(' Hello from barney\\n')
Raise(BARNEY)
WriteF(' Goodbye from barney\\n')
ENDPROC
When you run this program you get the following output:
Hello from main
Hello from fred
Handler fred: 1
Hello from barney
This is because the @{b }fred@{ub } procedure is terminated by the @{b }Raise(FRED)@{ub } call,
and the whole program is terminated by the @{b }Raise(BARNEY)@{ub } call (since
@{b }barney@{ub } and @{b }main@{ub } do not have handlers).
Now try this:
ENUM FRED=1, BARNEY
PROC main()
WriteF('Hello from main\\n')
fred()
WriteF('Goodbye from main\\n')
ENDPROC
PROC fred() HANDLE
WriteF(' Hello from fred\\n')
barney()
Raise(FRED)
WriteF(' Goodbye from fred\\n')
EXCEPT
WriteF(' Handler fred: \\d\\n', exception)
ENDPROC
PROC barney()
WriteF(' Hello from barney\\n')
Raise(BARNEY)
WriteF(' Goodbye from barney\\n')
ENDPROC
When you run this you get the following output:
Hello from main
Hello from fred
Hello from barney
Handler fred: 2
Goodbye from main
Now the @{b }fred@{ub } procedure calls @{b }barney@{ub }, so @{b }main@{ub } and @{b }fred@{ub } are recent
procedures when @{b }Raise(BARNEY)@{ub } is executed, and therefore the @{b }fred@{ub }
exception handler is called. When this handler finishes the call to @{b }fred@{ub }
in @{b }main@{ub } is finished, so the @{b }main@{ub } procedure is completed and we see the
`Goodbye' message. In the previous program the @{b }Raise(BARNEY)@{ub } call did not
get handled and the whole program terminated at that point.
@ENDNODE
@NODE "Automatic Exceptions" "Automatic Exceptions"
@Next "Raise within an Exception Handler"
@Prev "Raising an Exception"
@Toc "main"
Automatic Exceptions
====================
In the previous section we saw an example of raising an exception when
a call to @{b }New@{ub } returned @{b }NIL@{ub }. We can re-write this example to use
@{fg shine }automatic@{fg text } exception raising:
CONST BIG_AMOUNT = 100000
ENUM ERR_MEM=1
RAISE ERR_MEM IF New()=NIL
PROC main() HANDLE
DEF block
block:=New(BIG_AMOUNT)
WriteF('Got enough memory\\n')
EXCEPT
IF exception=ERR_MEM
WriteF('Not enough memory\\n')
ELSE
WriteF('Unknown exception\\n')
ENDIF
ENDPROC
The only difference is the removal of the @{b }IF@{ub } which checked the value of
@{b }block@{ub }, and the addition of a @{b }RAISE@{ub } part. This @{b }RAISE@{ub } part means that
whenever the @{b }New@{ub } function is called in the program, the exception @{b }ERR_MEM@{ub }
will be raised if it returns @{b }NIL@{ub } (i.e., the exception @{b }ERR_MEM@{ub } is
automatically raised). This unclutters the program by removing a lot of
error checking @{b }IF@{ub } statements.
The precise form of the @{b }RAISE@{ub } part is:
RAISE @{fg shine }exception@{fg text } IF @{fg shine }function@{fg text }() @{fg shine }compare@{fg text } @{fg shine }value@{fg text } ,
@{fg shine }exception2@{fg text } IF @{fg shine }function2@{fg text }() @{fg shine }compare2@{fg text } @{fg shine }value2@{fg text } ,
...
@{fg shine }exceptionN@{fg text } IF @{fg shine }functionN@{fg text }() @{fg shine }compareN@{fg text } @{fg shine }valueN@{fg text }
The @{fg shine }exception@{fg text } is a constant (or number) which represents the exception
to be raised, @{fg shine }function@{fg text } is the E built-in or system function to be
automatically checked, @{fg shine }value@{fg text } is the return value to be checked against,
and @{fg shine }compare@{fg text } is the method of checking (i.e., @{b }=@{ub }, @{b }<>@{ub }, @{b }<@{ub }, @{b }<=@{ub }, @{b }>@{ub } or @{b }>=@{ub }).
This mechanism only exists for built-in or library functions because they
would otherwise have no way of raising exceptions. The procedures you
define yourself can, of course, use @{b }Raise@{ub } to raise exceptions in a much
more flexible way.
@ENDNODE
@NODE "Raise within an Exception Handler" "Raise within an Exception Handler"
@Prev "Automatic Exceptions"
@Toc "main"
@{b }Raise@{ub } within an Exception Handler
=================================
If you call @{b }Raise@{ub } within an exception handler then control passes to
the next most recent handler. In this way you can write procedures which
have handlers that perform local tidying up. By using @{b }Raise@{ub } at the end of
the handler code you can invoke the next layer of tidying up.
As an example we'll use the Amiga system functions @{b }AllocMem@{ub } and @{b }FreeMem@{ub }
which are like the built-in function @{b }New@{ub } and @{b }Dispose@{ub }, but the memory
allocated by @{b }AllocMem@{ub } @{i }must@{ui } be deallocated (using @{b }FreeMem@{ub }) when it's
finished with, before the end of the program.
CONST SMALL=100, BIG=123456789
ENUM ERR_MEM=1
RAISE ERR_MEM IF AllocMem()=NIL
PROC main()
allocate()
ENDPROC
PROC allocate() HANDLE
DEF mem=NIL
mem:=AllocMem(SMALL, 0)
morealloc()
FreeMem(mem, SMALL)
EXCEPT
IF mem THEN FreeMem(mem, SMALL)
WriteF('Handler: deallocating "allocate" local memory\\n')
ENDPROC
PROC morealloc() HANDLE
DEF more=NIL, andmore=NIL
more:=AllocMem(SMALL, 0)
andmore:=AllocMem(BIG, 0)
WriteF('Allocated all the memory!\\n')
FreeMem(andmore, BIG)
FreeMem(more, SMALL)
EXCEPT
IF andmore THEN FreeMem(andmore, BIG)
IF more THEN FreeMem(more, SMALL)
WriteF('Handler: deallocating "morealloc" local memory\\n')
Raise(ERR_MEM)
ENDPROC
The calls to @{b }AllocMem@{ub } are automatically checked, and if @{b }NIL@{ub } is returned
the exception @{b }ERR_MEM@{ub } is raised. The handler in the @{b }allocate@{ub } procedure
checks to see if it needs to free the memory pointed to by @{b }mem@{ub }, and the
handler in the @{b }morealloc@{ub } checks @{b }andmore@{ub } and @{b }more@{ub }. At the end of the
@{b }morealloc@{ub } handler is the call @{b }Raise(ERR_MEM)@{ub }. This passes control to the
exception handler of the @{b }allocate@{ub } procedure, since @{b }allocate@{ub } called
@{b }morealloc@{ub }.
There's a couple of subtle points to notice about this example.
Firstly, the memory variables are all initialised to @{b }NIL@{ub }. This is because
the automatic exception raising on @{b }AllocMem@{ub } will result in the variables
not being assigned if the call returns @{b }NIL@{ub } (i.e., the exception is raised
before the assignment takes place), and the handler needs them to be @{b }NIL@{ub }
if @{b }AllocMem@{ub } fails. Of course, if @{b }AllocMem@{ub } does not return @{b }NIL@{ub } the
assignments work as normal.
Secondly, the @{b }IF@{ub } statements in the handlers check the memory pointer
variables do not contain @{b }NIL@{ub } by using their values as truth values. Since
@{b }NIL@{ub } is actually zero, a non-@{b }NIL@{ub } pointer will be non-zero, i.e., true in
the @{b }IF@{ub } check. This shorthand is often used, and so you should be aware of
it.
It is quite common that an exception handler will want to raise the
same exception after it has done its processing. The function @{b }ReThrow@{ub }
(which has no arguments) can be used for this purpose. It will re-raise
the exception, but only if the exception is not zero (since this special
value means that no error occurred). If the exception is zero then this
function has no effect. In fact, the following code fragments (within a
handler) are equivalent:
ReThrow()
IF exception THEN Throw(exception, exceptioninfo)
There are two examples, in Part Three, of how to use an exception
handler to make a program more readable: one deals with using data files
(see @{"String Handling and I-O" Link "Examples.guide/String Handling and I-O" }) and the other deals with opening screens and
windows (see @{"Screens" Link "Examples.guide/Screens" }).
@ENDNODE

View File

@ -0,0 +1,358 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Floating-Point Numbers"
@Next "Recursion.guide/main"
@Prev "Memory.guide/main"
@Toc "Contents.guide/main"
Floating-Point Numbers
**********************
@{fg shine }Floating-point@{fg text } or @{fg shine }real@{fg text } numbers can be used to represent both very
small fractions and very large numbers. However, unlike a @{b }LONG@{ub } which can
hold every integer in a certain range (see @{"Variable types" Link "Introduction.guide/Variable types" }), floating-point
numbers have limited @{fg shine }accuracy@{fg text }. Be warned, though: using floating-point
arithmetic in E is quite complicated and most problems can be solved
without using floating-point numbers, so you may wish to skip this chapter
until you really need to use them.
@{" Floating-Point Values " Link "Floating-Point Values" }
@{" Floating-Point Calculations " Link "Floating-Point Calculations" }
@{" Floating-Point Functions " Link "Floating-Point Functions" }
@{" Accuracy and Range " Link "Accuracy and Range" }
@ENDNODE
@NODE "Floating-Point Values" "Floating-Point Values"
@Next "Floating-Point Calculations"
@Toc "main"
Floating-Point Values
=====================
Floating-point values in E are written just like you might expect and
are stored in @{b }LONG@{ub } variables:
DEF x
x:=3.75
x:=-0.0000367
x:=275.0
You must remember to use a decimal point (without any spaces around it) in
the number if you want it to be considered a floating-point number, and
this is why a trailing @{b }.0@{ub } was used on the number in the last assignment.
At present you can't express every floating-point value in this way; the
compiler may complain that the value does not fit in 32-bits if you try to
use more than about nine digits in a single number. You can, however, use
the various floating-point maths functions to calculate any value you want
(see @{"Floating-Point Functions" Link "Floating-Point Functions" }).
@ENDNODE
@NODE "Floating-Point Calculations" "Floating-Point Calculations"
@Next "Floating-Point Functions"
@Prev "Floating-Point Values"
@Toc "main"
Floating-Point Calculations
===========================
Since a floating-point number is stored in a @{b }LONG@{ub } variable it would
normally be interpreted as an integer, and this interpretation will
generally not give a number anything like the intended floating-point
number. To use floating-point numbers in expressions you must use the
(rather complicated) floating-point conversion operator, which is the @{b }!@{ub }
character. This converts expressions and the normal maths and comparison
operators to and from floating-point.
All expressions are, by default, integer expressions. That is, they
represent @{b }LONG@{ub } integer values, rather than floating-point values. The
first time a @{b }!@{ub } occurs in an expression the value of the expression so far
is converted to floating-point and all the operators and variables after
this point are considered floating-point. The next time it occurs the
(floating-point) value of the expression so far is converted to an
integer, and the following operators and variables are considered integer
again. You can use @{b }!@{ub } as often as necessary within an expression. Parts
of an expression in parentheses are treated as separate expressions, so
are, by default, integer expressions (this, includes function call
arguments).
The integer/floating-point conversions performed by @{b }!@{ub } are not simple.
They involve rounding and also bounding. Conversion, for example, from
integer to floating-point and back again will generally not result in the
original integer value.
Here's a few commented examples, where @{b }f@{ub } always holds a floating-point
number, and @{b }i@{ub } and @{b }j@{ub } always hold integers:
DEF f, i, j
i:=1
f:=1.0
f:=i! -> i converted to floating-point (1.0)
f:=6.2
i:=!f! -> the expression f is floating-point,
-> then converted to integer (6)
In the first assignment, the integer value one is assigned to @{b }i@{ub }. In the
second, the floating-point value 1.0 is assigned to @{b }f@{ub }. The expression on
the right-hand side of third assignment is considered to be an integer
until the @{b }!@{ub } is met, at which point it is converted to the nearest
floating-point value. So, @{b }f@{ub } is assigned the floating-point value of one
(i.e., 1.0), just like it is by the second assignment. The expression in
the final assignment needs to start off as floating-point in order to
interpret the value stored in @{b }f@{ub } as floating-point. The expression
finishes by converting back to integer. The overall result is to turn the
floating-point value of @{b }f@{ub } into the nearest integer (in this case, six).
The assignments below are more complicated, but should be
straight-forward to follow. Again, @{b }f@{ub } always holds a floating-point
number, and @{b }i@{ub } and @{b }j@{ub } always hold integers.
f:=!f*f -> the whole expression is floating-point,
-> and f is squared (6.2*6.2)
f:=!f*(i!) -> the whole expression is floating-point,
-> i is converted to floating-point and
-> multiplied by f
j:=!f/(i!)! -> the whole division is floating-point,
-> with the result converted to integer
j:=!f!/i -> floating-point f is converted to integer
-> and is (integer) divided by i
IF !f<230.0 THEN RETURN 0 -> floating-point comparison <
IF !f>(i!) THEN RETURN 0 -> i converted to floating-point,
-> then compared to f
If the @{b }!@{ub } were omitted from the first assignment, then not only would the
value in @{b }f@{ub } be interpreted (incorrectly) as integer, but the multiplication
performed would be integer multiplication, rather than floating-point. In
the second assignment, the parentheses around the expression involving @{b }i@{ub }
are crucial. Without the parentheses the value stored in @{b }i@{ub } would be
interpreted as floating-point. This would be wrong because @{b }i@{ub } actually
stores an integer value, so parentheses are used to start a new expression
(which defaults to being integer). The value of @{b }i@{ub } is then interpreted
correctly, and finally converted to floating-point (by the @{b }!@{ub } just before
the closing parenthesis). The (floating-point) multiplication then takes
place with two floating-point values, and the result is stored in @{b }f@{ub }. In
the last two assignments (using division), @{b }j@{ub } is assigned roughly the same
value. However, the expression in the first assignment allows for greater
accuracy, since it uses floating-point division. This means the result
will be rounded, whereas it is truncated when integer division is used.
One important thing to know about floating-point numbers in E is that
the following assignments store the same value in @{b }g@{ub } (again, @{b }f@{ub } stores a
floating-point number). This is because no computation is performed and
no conversion happens: the value in @{b }f@{ub } is simply copied to @{b }g@{ub }. This is
especially important for function calls, as we shall see in the next
section. Strictly speaking, however, the second version is better, since
it shows (to the reader of the code) that the value in @{b }f@{ub } is meant to be
floating-point.
g:=f
g:=!f
@ENDNODE
@NODE "Floating-Point Functions" "Floating-Point Functions"
@Next "Accuracy and Range"
@Prev "Floating-Point Calculations"
@Toc "main"
Floating-Point Functions
========================
There are functions for formatting floating-point numbers to E-strings
(so that they can be printed) and for decoding floating-point numbers from
strings. There are also a number of built-in, floating-point functions
which compute some of the less common mathematical functions, such as the
various trigonometric functions.
@{b }RealVal(@{ub }@{fg shine }string@{fg text }@{b })@{ub }
This works in a similar way to @{b }Val@{ub } for extracting integers from a
string. The decoded floating-point value is returned as the regular
return value, and the number of characters of @{fg shine }string@{fg text } that were read
to make the number is returned as the first optional return value.
If a floating-point value could not be decoded from the string then
zero is returned as the optional return value and the regular return
value will be zero (i.e., 0.0).
@{b }RealF(@{ub }@{fg shine }e-string@{fg text }@{b },@{ub }@{fg shine }float@{fg text }@{b },@{ub }@{fg shine }digits@{fg text }@{b })@{ub }
Converts the floating-point value @{b }float@{ub } into a string which is stored
in @{fg shine }e-string@{fg text }. The number of digits to use after the decimal point
is specified by @{fg shine }digits@{fg text }, which can be zero to eight. The
floating-point value is rounded to the specified number of digits. A
value of zero for @{fg shine }digits@{fg text } gives a result with no fractional part and
no decimal point. The @{fg shine }e-string@{fg text } is returned by this function, and
this makes it easy to use with @{b }WriteF@{ub }.
PROC main()
DEF s[20]:STRING, f, i
f:=21.60539
FOR i:=0 TO 8
WriteF('f is \\s (using digits=\\d)\\n', RealF(s, f, i), i)
ENDFOR
ENDPROC
Notice that the floating-point argument, @{b }f@{ub }, to @{b }RealF@{ub } does not need a
leading @{b }!@{ub } because we are simply passing its value and not performing
a computation with it. The program should generate the following
output:
f is 22 (using digits=0)
f is 21.6 (using digits=1)
f is 21.61 (using digits=2)
f is 21.605 (using digits=3)
f is 21.6054 (using digits=4)
f is 21.60539 (using digits=5)
f is 21.605390 (using digits=6)
f is 21.6053900 (using digits=7)
f is 21.60539000 (using digits=8)
@{b }Fsin(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Fcos(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Ftan(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
These compute the sine, cosine and tangent (respectively) of the
supplied @{fg shine }float@{fg text } angle, which is specified in radians.
@{b }Fabs(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
Returns the absolute value of @{fg shine }float@{fg text }, much like @{b }Abs@{ub } does for
integers.
@{b }Ffloor(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Fceil(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
The @{b }Ffloor@{ub } function rounds a floating-point value down to the
nearest, whole floating-point value. The @{b }Fceil@{ub } function rounds it up.
@{b }Fsqrt(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
Returns the square root of @{fg shine }float@{fg text }.
@{b }Fpow(@{ub }@{fg shine }x@{fg text }@{b },@{ub }@{fg shine }y@{fg text }@{b })@{ub }, @{b }Fexp(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
The @{b }Fpow@{ub } function returns the value of @{fg shine }x@{fg text } raised to the power of @{fg shine }y@{fg text }
(which are both floating-point values). The @{b }Fexp@{ub } function returns
the value of e raised to the power of @{fg shine }float@{fg text }, where e is the
mathematically special value (roughly 2.718282). `Raising to a
power' is known as @{fg shine }exponentiation@{fg text }.
@{b }Flog10(@{ub }@{fg shine }float@{fg text }@{b })@{ub }, @{b }Flog(@{ub }@{fg shine }float@{fg text }@{b })@{ub }
The @{b }Flog10@{ub } function returns the log to base ten of @{fg shine }float@{fg text } (the
@{fg shine }common logarithm@{fg text }). The @{b }Flog@{ub } function returns the log to base e of
@{fg shine }float@{fg text } (the @{fg shine }natural logarithm@{fg text }). @{b }Flog10@{ub } and @{b }Fpow@{ub } are linked in the
following way (ignoring floating-point inaccuracies):
x = Fpow(10.0, Flog10(x))
@{b }Flog@{ub } and @{b }Fexp@{ub } are similarly related (@{b }Fexp@{ub } could be used again, using
2.718282 as the first argument in place of 10.0).
x = Fexp(Flog(x))
Here's a small program which uses a few of the above functions, and
shows how to define functions which use and/or return floating-point
values.
DEF f, i, s[20]:STRING
PROC print_float()
WriteF('\\tf is \\s\\n', RealF(s, !f, 8))
ENDPROC
PROC print_both()
WriteF('\\ti is \\d, ', i)
print_float()
ENDPROC
/* Square a float */
PROC square_float(f) IS !f*f
/* Square an integer */
PROC square_integer(i) IS i*i
/* Converts a float to an integer */
PROC convert_to_integer(f) IS Val(RealF(s, !f, 0))
/* Converts an integer to a float */
PROC convert_to_float(i) IS RealVal(StringF(s, '\\d', i))
/* This should be the same as Ftan */
PROC my_tan(f) IS !Fsin(!f)/Fcos(!f)
/* This should show float inaccuracies */
PROC inaccurate(f) IS Fexp(Flog(!f))
PROC main()
WriteF('Next 2 lines should be the same\\n')
f:=2.7; i:=!f!
print_both()
f:=2.7; i:=convert_to_integer(!f)
print_both()
WriteF('Next 2 lines should be the same\\n')
i:=10; f:=i!
print_both()
i:=10; f:=convert_to_float(i)
print_both()
WriteF('f and i should be the same\\n')
i:=square_integer(i)
f:=square_float(f)
print_both()
WriteF('Next 2 lines should be the same\\n')
f:=Ftan(.8)
print_float()
f:=my_tan(.8)
print_float()
WriteF('Next 2 lines should be the same\\n')
f:=.35
print_float()
f:=inaccurate(f)
print_float()
ENDPROC
The @{b }convert_to_integer@{ub } and @{b }convert_to_float@{ub } functions perform similar
conversions to those done by @{b }!@{ub } when it occurs in an expression. To make
things more explicit, there are a lot of unnecessary uses of @{b }!@{ub }, and these
are when @{b }f@{ub } is passed directly as a parameter to a function (in these
cases, the @{b }!@{ub } could safely be omitted). All of the examples have the
potential to give different results where they ought to give the same, and
this is due to the inaccuracy of floating-point numbers. The last example
has been carefully chosen to show this.
@ENDNODE
@NODE "Accuracy and Range" "Accuracy and Range"
@Prev "Floating-Point Functions"
@Toc "main"
Accuracy and Range
==================
A floating-point number is just another 32-bit value, so can be stored
in @{b }LONG@{ub } variables. It's just the interpretation of the 32-bits which
makes them different. A floating-point number can range from numbers as
small as 1.3E-38 to numbers as large as 3.4E+38 (that's very small and
very large if you don't understand the scientific notation!). However,
not every number in this range can @{fg shine }accurately@{fg text } be represented, since the
number of significant digits is roughly eight.
Accuracy is an important consideration when trying to compare two
floating-point numbers and when combining floating-point values after
dividing them. It is usually best to check that a floating-point value is
in a small range of values, rather than just a particular value. And when
combining values, allow for a small amount of error due to rounding etc.
See the `Reference Manual' for more details about the implementation of
floating-point numbers.
@ENDNODE

View File

@ -0,0 +1,208 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Format and Layout"
@Next "Procedures.guide/main"
@Prev "Introduction.guide/Summary"
@Toc "Contents.guide/main"
Format and Layout
*****************
In this chapter we'll look at the rules which govern the format and
layout of E code. In the previous Part we saw examples of E code that
were quite nicely indented and the structure of the program was easily
visible. This was just a convention and the E language does not constrain
you to write code in this way. However, there are certain rules that must
be followed. (This chapter refers to some concepts and parts of the E
language which were not covered in Part One. Don't let this put you
off--those things will be dealt with in later chapters, and it's maybe a
good idea to read this chapter again when they have been.)
@{" Identifiers " Link "Identifiers" }
@{" Statements " Link "Statements" }
@{" Spacing and Separators " Link "Spacing and Separators" }
@{" Comments " Link "Comments" }
@ENDNODE
@NODE "Identifiers" "Identifiers"
@Next "Statements"
@Toc "main"
Identifiers
===========
An @{fg shine }identifier@{fg text } is a word which the compiler must interpret rather than
treating literally. For instance, a variable is an identifier, as is a
keyword (e.g., @{b }IF@{ub }), but anything in a string is not (e.g., @{b }fred@{ub } in @{b }'fred
and wilma'@{ub } is not an identifier). Identifiers can be made up of upper- or
lower-case letters, numbers and underscores (the @{b }_@{ub } character). There are
only two constraints:
1. The first character cannot be a number (this would cause confusion
with numeric constants).
2. The case of the first few characters of identifiers is significant.
For keywords (e.g., @{b }ENDPROC@{ub }), constants (e.g., @{b }TRUE@{ub }) and assembly
mnemonics (e.g., @{b }MOVE.L@{ub }) the first two characters must both be uppercase.
For E built-in or Amiga system procedures/functions the first character
must be uppercase and the second must be lowercase. For all other
identifiers (i.e., local, global and procedure parameter variables, object
names and element names, procedure names and code labels) the first
character must be lowercase.
Apart from these constraints you are free to write identifiers how you
like, although it's arguably more tasteful to use all lowercase for
variables and all uppercase for keywords and constants.
@ENDNODE
@NODE "Statements" "Statements"
@Next "Spacing and Separators"
@Prev "Identifiers"
@Toc "main"
Statements
==========
A @{fg shine }statement@{fg text } is normally a single instruction to the computer, and
each statement normally occupies a single line. If you think of a
procedure as a paragraph then a statement is a sentence. Using the same
analogy, variables, expressions and keywords are the words which make up
the sentence.
So far in our examples we have met only two kinds of statement: the
single line statement and the multi-line statement. The assignments we
have seen were single line statements, and the vertical form of the @{b }IF@{ub }
block is a multi-line statement. The horizontal form of the @{b }IF@{ub } block was
actually the single line statement form of the @{b }IF@{ub } block. Notice that
statements can be built up from other statements, as is the case for @{b }IF@{ub }
blocks. The code parts between the @{b }IF@{ub }, @{b }ELSEIF@{ub }, @{b }ELSE@{ub } and @{b }ENDIF@{ub } lines are
sequences of statements.
Single line statements can often be very short, and you may be able to
fit several of them onto an single line without the line getting too long.
To do this in E you use a semi-colon (the @{b };@{ub } character) to separate each
statement on the line. For example, the following code fragments are
equivalent:
fred(y,z)
y:=x
x:=z+1
fred(y,z); y:=x; x:=z+1
On the other hand you may want to split a long statement over several
lines. This is a bit more tricky because the compiler needs to see that
you haven't finished the statement when it gets to the end of a line.
Therefore you can only break a statement at certain places. The most
common place is after a comma that is part of the statement (like in a
procedure call with more than one parameter), but you can also split a
line after binary operators and anywhere between opening and closing
brackets. The following examples are rather silly but show some allowable
line breaking places.
fred(a, b, c,
d, e, f) /* After a comma */
x:=x+
y+
z /* After a binary operator */
x:=(1+2
+3) /* Between open...close brackets */
list:= [ 1,2,
[3,4]
] /* Between open...close brackets */
The simple rule is this: if a complete line can be interpreted as a
statement then it will be, otherwise it will be interpreted as part of a
statement which continues on the following lines.
Strings may also get a bit long. You can split them over several lines
by breaking them into several separate strings and using @{b }+@{ub } between them.
If a line ends with a @{b }+@{ub } and the previous thing on the line was a string
then the E compiler takes the next string to be a continuation. The
following calls to @{b }WriteF@{ub } print the same thing:
WriteF('This long string can be broken over several lines.\\n')
WriteF('This long string ' +
'can be broken over several lines.\\n')
WriteF('This long' +
' string can be ' +
'broken over several ' +
'lines.\\n')
@ENDNODE
@NODE "Spacing and Separators" "Spacing and Separators"
@Next "Comments"
@Prev "Statements"
@Toc "main"
Spacing and Separators
======================
The examples we've seen so far used a rigid indentation convention
which was intended to illuminate the structure of the program. This was
just a convention, and the E language places no constraints on the amount
of @{fg shine }whitespace@{fg text } (spaces, tabs and linefeeds) you place between statements.
However, within statements you must supply enough spacing to make the
statement readable. This generally means that you must put whitespace
between adjacent identifiers which start or end with a letter, number or
underscore (so that the compiler does not think it's one big identifier!).
So, in practice, you should put a space after a keyword if it might run
into a variable or procedure name. Most other times (like in expressions)
identifiers are separated by non-identifier characters (a comma,
parenthesis or other symbol).
@ENDNODE
@NODE "Comments" "Comments"
@Prev "Spacing and Separators"
@Toc "main"
Comments
========
A @{fg shine }comment@{fg text } is something that the E compiler ignores and is only there
to help the reader. Remember that one day in the future you may be the
reader, and it may be quite hard to decipher your own code without a few
decent comments! Comments are therefore pretty important.
You can write comments anywhere you can write whitespace that isn't
part of a string. There are two kinds of comment: one uses @{b }/*@{ub } to mark the
start of the comment text and @{b }*/@{ub } to mark the end, and the other uses @{b }->@{ub } to
mark the start, with the comment text continuing to the end of the line.
You must be careful not to write @{b }/*@{ub }, @{b }*/@{ub } or @{b }->@{ub } as part of the comment text,
unless part of a nested comment. In practice a comment is best put on a
line by itself or after the end of the code on a line.
/* This line is a comment */
x:=1 /* This line contains an assignment then a comment */
/* y:=2 /* This whole line is a comment with a nested comment */*/
x:=1 -> Assignment then a comment
-> y:=2 /* A nested comment comment */
@ENDNODE

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,557 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Memory Allocation"
@Next "FloatingPoint.guide/main"
@Prev "Exceptions.guide/main"
@Toc "Contents.guide/main"
Memory Allocation
*****************
When a program is running memory is being used in various different
ways. In order to use any memory it must first be @{fg shine }allocated@{fg text }, which is
simply a way of marking memory as being `in use'. This is to prevent the
same piece of memory being used for different data storage (e.g., by
different programs), and so helps prevent corruption of the data stored
there. There are two general ways in which memory can be allocated:
dynamically and statically.
@{" Static Allocation " Link "Static Allocation" }
@{" Deallocation of Memory " Link "Deallocation of Memory" }
@{" Dynamic Allocation " Link "Dynamic Allocation" }
@{" NEW and END Operators " Link "NEW and END Operators" }
@ENDNODE
@NODE "Static Allocation" "Static Allocation"
@Next "Deallocation of Memory"
@Toc "main"
Static Allocation
=================
@{fg shine }Statically@{fg text } allocated memory is memory allocated by the program for
variables and static data like string constants, lists and typed lists
(see @{"Static data" Link "Types.guide/Static data" }). Every variable in a program requires some memory in
which to store its value. Variables declared to be of type @{b }ARRAY@{ub }, @{b }LIST@{ub },
@{b }STRING@{ub } or any object require two lots of memory: one to hold the value of
the pointer and one to hold the large amount of data (e.g., the elements
in an @{b }ARRAY@{ub }). In fact, such declarations are merely @{b }PTR TO @{ub }@{fg shine }type@{fg text }@{b }@{ub }
declarations together with an initialisation of the pointer to the address
of some (statically) allocated memory to hold the data. The following
example shows very similar declarations, with the difference being that in
the second case (using @{b }PTR@{ub }) only memory to hold the pointer values is
allocated. The first case also allocates memory to hold the appropriate
size of array, object and E-string.
DEF a[20]:ARRAY, m:myobj, s[10]:STRING
DEF a:PTR TO CHAR, m:PTR TO myobj, s:PTR TO CHAR
The pointers in the second case are not initialised by the declaration
and, therefore, they are not valid pointers. This means that they should
not be dereferenced in any way, until they have been initialised to the
address of some allocated memory. This usually involves dynamic
allocation of memory (see @{"Dynamic Allocation" Link "Dynamic Allocation" }).
@ENDNODE
@NODE "Deallocation of Memory" "Deallocation of Memory"
@Next "Dynamic Allocation"
@Prev "Static Allocation"
@Toc "main"
Deallocation of Memory
======================
When memory is allocated it is, conceptually, marked as being `in use'.
This means that this piece of memory cannot be allocated again, so a
different piece will be allocated (if any is available) when the program
wants to allocate some more. In this way, variables are allocated
different pieces of memory, and so their values can be distinct. But
there is only a certain amount of memory available, and if it could not be
marked as `not in use' again it would soon run out (and the program would
come to a nasty end). This is what @{fg shine }deallocation@{fg text } does: it marks
previously allocated memory as being `not in use' and so makes it
available for allocation again. However, memory should be deallocated
only when it is actually no longer in use, and this is where things get a
bit complicated.
Memory is such a vital resource in every computer that it is important
to use as little of it as necessary and to deallocate it whenever possible.
This is why a programming language like E handles most of the memory
allocation for variables. The memory allocated for variables can be
automatically deallocated when it is no longer possible for the program to
use that variable. However, this automatic deallocation is not useful for
global variables, since they can be used from any procedure and so can be
deallocated only when the program terminates. A procedure's local
variables, on the other hand, are allocated when the procedure is called
but cannot be used after the procedure returns. They can, therefore, be
deallocated when the procedure returns.
Pointers, as always, can cause big problems. The following example
shows why you need to be careful when using pointers as the return value
of a procedure.
/* This is an example of what *NOT* to do */
PROC fullname(first, last)
DEF full[40]:STRING
StrCopy(full, first)
StrAdd(full, ' ')
StrAdd(full, last)
ENDPROC full
PROC main()
WriteF('Name is \\s\\n', fullname('Fred', 'Flintstone'))
ENDPROC
On first sight this seems fine, and, in fact, it may even work correctly
if you run it once or twice (but be careful: it could crash your machine).
The problem is that the procedure @{b }fullname@{ub } returns the value of the local
variable @{b }full@{ub }, which is a pointer to some statically allocated memory for
the E-string and this memory will be deallocated when the procedure
returns. This means that the return value of any call to @{b }fullname@{ub } is the
address of recently deallocated memory, so it is invalid to dereference it.
But the call to @{b }WriteF@{ub } does just that: it dereferences the result of
@{b }fullname@{ub } in order to print the E-string it points to. This is a very
common problem, because it is such an easy thing to do. The fact that it
may, on many occasions, appear to work makes it much harder to find, too.
The solution, in this case, is to use dynamic allocation (see
@{"Dynamic Allocation" Link "Dynamic Allocation" }).
If you're still a bit sceptical that this really is a problem, try the
above @{b }fullname@{ub } procedure definition with either of these replacement @{b }main@{ub }
procedures, but be aware, again, that each one has the potential to crash
your machine.
/* This might not print the correct string */
PROC main()
DEF f
f:=fullname('Fred', 'Flintstone')
WriteF('Name is \\s\\n', f)
ENDPROC
/* This will definitely print g instead of f */
PROC main()
DEF f, g
f:=fullname('Fred', 'Flintstone')
g:=fullname('Barney', 'Rubble')
WriteF('Name is \\s\\n', f)
ENDPROC
(The reason why things go wrong is outlined above, but the reasons why
each prints what it does is beyond the scope of this Guide.)
@ENDNODE
@NODE "Dynamic Allocation" "Dynamic Allocation"
@Next "NEW and END Operators"
@Prev "Deallocation of Memory"
@Toc "main"
Dynamic Allocation
==================
@{fg shine }Dynamically@{fg text } allocated memory is any memory that is not statically
allocated. To allocate memory dynamically you can use the @{b }List@{ub } and @{b }String@{ub }
functions, all flavours of @{b }New@{ub }, and the versatile @{b }NEW@{ub } operator. But
because the memory is dynamically allocated it must be explicitly
deallocated when no longer needed. In all the above cases, though, any
memory that is still allocated when the program terminates will be
deallocated automatically.
Another way to allocate memory dynamically is to use the Amiga system
functions based on @{b }AllocMem@{ub }. However, these functions require that the
memory allocated using them be deallocated (using functions like @{b }FreeMem@{ub })
before the program terminates, or else it will never be deallocated (not
until your machine is rebooted, anyway). It is safer, therefore, to try
to use the E functions for dynamic allocation whenever possible.
There are many reasons why you might want to use dynamic allocation,
and most of them involve initialisation of pointers. For example, the
declarations in the section about static allocation can be extended to
give initialisations for the pointers declared in the second @{b }DEF@{ub } line (see
@{"Static Allocation" Link "Static Allocation" }).
DEF a[20]:ARRAY, m:myobj, s[10]:STRING
DEF a:PTR TO CHAR, m:PTR TO myobj, s:PTR TO CHAR
a:=New(20)
m:=New(SIZEOF myobj)
s:=String(20)
These are initialisations to dynamically allocated memory, whereas the
first line of declarations initialise similar pointers to statically
allocated memory. If these sections of code were part of a procedure
then, since they would now be local variables, there would be one other,
significant difference: the dynamically allocated memory would not
automatically be deallocated when the procedure returns, whereas the
statically allocated memory would. This means that we can solve the
deallocation problem (see @{"Deallocation of Memory" Link "Deallocation of Memory" }).
/* This is the correct way of doing it */
PROC fullname(first, last)
DEF full
full:=String(40)
StrCopy(full, first)
StrAdd(full, ' ')
StrAdd(full, last)
ENDPROC full
PROC main()
DEF f, g
WriteF('Name is \\s\\n', fullname('Fred', 'Flintstone'))
f:=fullname('Fred', 'Flintstone')
g:=fullname('Barney', 'Rubble')
WriteF('Name is \\s\\n', f)
ENDPROC
The memory for the E-string pointed to by @{b }full@{ub } is now allocated
dynamically, using @{b }String@{ub }, and is not deallocated until the end of the
program. This means that it is quite valid to pass the value of @{b }full@{ub } as
the result of the procedure @{b }fullname@{ub }, and it is quite valid to dereference
the result by printing it using @{b }WriteF@{ub }. However, this has caused one last
problem: the memory is not deallocated until the end of the program, so is
potentially wasted since it could be used, for example, to hold the
results of subsequent calls. Of course, the memory can be deallocated
only when the data it stores is no longer required. The following
replacement @{b }main@{ub } procedure shows when you might want to deallocate the
E-string (using @{b }DisposeLink@{ub }).
PROC main()
DEF f, g
f:=fullname('Fred', 'Flintstone')
WriteF('Name is \\s, f points to $\\h\\n', f, f)
/* Try this with and without the next DisposeLink line */
DisposeLink(f)
g:=fullname('Barney', 'Rubble')
WriteF('Name is \\s, g points to $\\h\\n', g, g)
DisposeLink(g)
ENDPROC
If you run this with the @{b }DisposeLink(f)@{ub } line you'll probably find that
@{b }g@{ub } will be a pointer to the same memory as @{b }f@{ub }. This is because the call
to @{b }DisposeLink@{ub } has deallocated the memory pointed to by @{b }f@{ub }, so it can be
reused to store the E-string pointed to by @{b }g@{ub }. If you comment out (or
delete) the @{b }DisposeLink@{ub } line, then you will find that @{b }f@{ub } and @{b }g@{ub } always point
to different memory.
In some ways it is best to never do any deallocation, because of the
problems you can get into if you deallocate memory too early (i.e., before
you've finished with the data it contains). Of course, it is safe (but
temporarily wasteful) to do this with the E dynamic allocation functions,
but it is very wasteful (and wrong) to do this with the Amiga system
functions like @{b }AllocMem@{ub }.
Another benefit of using dynamic allocation is that the size of the
arrays, E-lists and E-strings that can be created can be the result of any
expression, so is not restricted to constant values. (Remember that the
size given on @{b }ARRAY@{ub }, @{b }LIST@{ub } and @{b }STRING@{ub } declarations must be a constant.)
This means that the @{b }fullname@{ub } procedure can be made more efficient and
allocate only the amount of memory it needs for the E-string it creates.
PROC fullname(first, last)
DEF full
/* The extra +1 is for the added space */
full:=String(StrLen(first)+StrLen(last)+1)
StrCopy(full, first)
StrAdd(full, ' ')
StrAdd(full, last)
ENDPROC full
However, it may be very complicated or inefficient to calculate the
correct size. In these cases, a quick, constant estimate might be better,
overall.
The various functions for allocating memory dynamically have
corresponding functions for deallocating that memory. The following table
shows some of the more common pairings.
Allocation Deallocation
------------------------------
New Dispose
NewR Dispose
List DisposeLink
String DisposeLink
NEW END
FastNew FastDispose
AllocMem FreeMem
AllocVec FreeVec
AllocDosObject FreeDosObject
@{b }NEW@{ub } and @{b }END@{ub } are versatile and powerful operators, discussed in the
following section. The functions beginning with @{b }Alloc-@{ub } are Amiga system
functions and are paired with similarly suffixed functions with a @{b }Free-@{ub }
prefix. See the `Rom Kernel Reference Manual' for more details.
@ENDNODE
@NODE "NEW and END Operators" "NEW and END Operators"
@Prev "Dynamic Allocation"
@Toc "main"
@{b }NEW@{ub } and @{b }END@{ub } Operators
=====================
To help deal with dynamic allocation and deallocation of memory there
are two, powerful operators, @{b }NEW@{ub } and @{b }END@{ub }. The @{b }NEW@{ub } operator is very
versatile, and similar in operation to the @{b }New@{ub } family of built-in
functions (see @{"System support functions" Link "BuiltIns.guide/System support functions" }). The @{b }END@{ub } operator is the
deallocating complement of @{b }NEW@{ub } (so it is similar to the @{b }Dispose@{ub } family of
built-in functions). The major difference between @{b }NEW@{ub } and the various
flavours of @{b }New@{ub } is that @{b }NEW@{ub } allocates memory based on the types of its
arguments.
@{" Object and simple typed allocation " Link "Object and simple typed allocation" }
@{" Array allocation " Link "Array allocation" }
@{" List and typed list allocation " Link "List and typed list allocation" }
@{" OOP object allocation " Link "OOP object allocation" }
@ENDNODE
@NODE "Object and simple typed allocation" "Object and simple typed allocation"
@Next "Array allocation"
@Toc "NEW and END Operators"
Object and simple typed allocation
----------------------------------
The following sections of code are roughly equivalent and serve to show
the function of @{b }NEW@{ub }, and how it is closely related to @{b }NewR@{ub }. (The @{fg shine }type@{fg text }
can be any object or simple type.)
DEF p:PTR TO @{fg shine }type@{fg text }
NEW p
DEF p:PTR TO @{fg shine }type@{fg text }
p:=NewR(SIZEOF @{fg shine }type@{fg text })
Notice that the use of @{b }NEW@{ub } is not like a function call, as there are no
parentheses around the parameter @{b }p@{ub }. This is because @{b }NEW@{ub } is an operator
rather than a function. It works differently from a function, since it
also needs to know the types of its arguments. This means that the
declaration of @{b }p@{ub } is very important, since it governs how much memory is
allocated by @{b }NEW@{ub }. The version using @{b }NewR@{ub } explicitly gives the amount of
memory to be allocated (using the @{b }SIZEOF@{ub } operator), so in this case the
declared type of @{b }p@{ub } is not so important for correct allocation.
The next example shows how @{b }NEW@{ub } can be used to initialise several
pointers at once. The second section of code is roughly equivalent, but
uses @{b }NewR@{ub }. (Remember that the default type of a variable is @{b }LONG@{ub }, which
is actually @{b }PTR TO CHAR@{ub }.)
DEF p:PTR TO LONG, q:PTR TO myobj, r
NEW p, q, r
DEF p:PTR TO LONG, q:PTR TO myobj, r
p:=NewR(SIZEOF LONG)
q:=NewR(SIZEOF myobj)
r:=NewR(SIZEOF CHAR)
These first two examples have shown the statement form of @{b }NEW@{ub }. There
is also an expression form, which has one parameter and returns the
address of the newly allocated memory as well as initialising the argument
pointer to this address.
DEF p:PTR TO myobj, q:PTR TO myobj
q:=NEW p
DEF p:PTR TO myobj, q:PTR TO myobj
q:=(p:=NewR(SIZEOF @{fg shine }type@{fg text }))
This may not seem desperately useful, but it's also the way that @{b }NEW@{ub } is
used to allocate copies of lists and typed lists (see
@{"List and typed list allocation" Link "List and typed list allocation" }).
To deallocate memory allocated using @{b }NEW@{ub } you use the @{b }END@{ub } statement with
the pointers that you want to deallocate. To work properly, @{b }END@{ub } requires
that the type of each pointer matches the type used when it was allocated
with @{b }NEW@{ub }. Failure to do this will result in an incorrect amount of memory
being deallocated, and this can cause many subtle problems in a program.
You must also be careful not to deallocate the same memory twice, and to
this end the pointers given to @{b }END@{ub } are re-initialised to @{b }NIL@{ub } after the
memory they point to is deallocated (it is quite safe to use @{b }END@{ub } with a
pointer which is @{b }NIL@{ub }). This does not catch all problems, however, since
more than one pointer can point to the same piece of memory, as shown in
the example below.
DEF p:PTR TO LONG, q:PTR TO LONG
q:=NEW p
p[]:=-24
q[]:=613
END p
/* p is now NIL, but q is now invalid but not NIL */
The first assignment initialises @{b }q@{ub } to be the same as @{b }p@{ub } (which is
initialised by @{b }NEW@{ub }). @{i }Both@{ui } the next two assignments change the value
pointed to by @{i }both@{ui } @{b }p@{ub } and @{b }q@{ub }. The memory allocated to store this value is
then deallocated, using @{b }END@{ub }, and this also sets @{b }p@{ub } to @{b }NIL@{ub }. However, the
address stored in @{b }q@{ub } is not altered, and still points to the memory that
has just been deallocated. This means that @{b }q@{ub } now has a plausible, but
invalid, pointer value. The only thing that can safely be done with @{b }q@{ub } is
re-initialise it. One of the @{i }worst@{ui } things that could be done is to use it
with @{b }END@{ub }, which would deallocate the same memory again, and potentially
crash your machine. So, in summary, don't deallocate the same pointer
value more than once, and keep track of which variables point to the same
memory as others.
Just as a use of @{b }NEW@{ub } has a simple (but rough) equivalent using @{b }NewR@{ub },
@{b }END@{ub } has an equivalent using @{b }Dispose@{ub }, as shown by the following
sections of code.
END p
IF p
Dispose(p)
p:=NIL
ENDIF
In fact, it's a tiny bit more complicated than that, since OOP objects are
allocated and deallocated using @{b }NEW@{ub } and @{b }END@{ub } (see @{"Object Oriented E" Link "OOE.guide/main" }).
@ENDNODE
@NODE "Array allocation" "Array allocation"
@Next "List and typed list allocation"
@Prev "Object and simple typed allocation"
@Toc "NEW and END Operators"
Array allocation
----------------
Arrays can also be allocated using @{b }NEW@{ub }, and this works in a very
similar way to that outlined in the previous section. The difference is
that the size of the array must also be supplied, in both the use of @{b }NEW@{ub }
and @{b }END@{ub }. Of course, the size supplied to @{b }END@{ub } must be the same as the size
supplied to the appropriate use of @{b }NEW@{ub }. All this extra effort also gains
you the ability to create an array of a size which is not a constant
(unlike variables of type @{b }ARRAY@{ub }). This means that the size supplied to
@{b }NEW@{ub } and @{b }END@{ub } can be the result of an arbitrary expression.
DEF a:PTR TO LONG, b:PTR TO myobj, s
NEW a[10] /* A dynamic array of LONG */
s:=my_random(20)
NEW b[s] /* A dynamic array of myobj */
/* ...some other code... */
END a[10], b[s]
The @{b }my_random@{ub } function stands for some arbitrary calculation, to show that
@{b }s@{ub } does not have to be a constant. This form of @{b }NEW@{ub } can also be used as an
expression, as before.
@ENDNODE
@NODE "List and typed list allocation" "List and typed list allocation"
@Next "OOP object allocation"
@Prev "Array allocation"
@Toc "NEW and END Operators"
List and typed list allocation
------------------------------
Lists and typed lists are usually static data, but @{b }NEW@{ub } can be used to
create dynamically allocated versions. This form of @{b }NEW@{ub } can be used only
as an expression, and it takes the list (or typed list) as its argument
and returns the address of the dynamically allocated copy of the list.
Deallocation of the memory allocated in this way is a bit more complicated
than before, but you can, of course, let it be deallocated automatically
at the end of the program.
The following example shows how simple it is to use @{b }NEW@{ub } to cure the
static data problem described previously (see @{"Static data" Link "Types.guide/Static data" }). The
difference from the original, incorrect program is very subtle.
PROC main()
DEF i, a[10]:ARRAY OF LONG, p:PTR TO LONG
FOR i:=0 TO 9
a[i]:=NEW [1, i, i*i]
/* a[i] is now dynamically allocated */
ENDFOR
FOR i:=0 TO 9
p:=a[i]
WriteF('a[\\d] is an array at address \\d\\n', i, p)
WriteF(' and the second element is \\d\\n', p[1])
ENDFOR
ENDPROC
The minor alteration is to prefix the list with @{b }NEW@{ub }, thereby making the
list dynamic. This means that each @{b }a[i]@{ub } is now a different list, rather
than the same, static list of the original version of the program.
Typed lists are allocated in a similar way, and the following example
also shows how to deallocate this memory. Basically, you need to know how
long the new array is (i.e., how many elements there are), since a typed
list is really just an initialised array. You can then deallocate it like
a normal array, remembering to use an appropriately typed pointer.
Object-typed lists are restricted (when used with @{b }NEW@{ub }) to an array of at
most one object, so is useful only for allocating an initialised object
(not really an array). Notice how, in the following code, the pointer @{b }q@{ub }
can be treated both as an object and as an array of one object (see
@{"Element selection and element types" Link "Types.guide/Element selection and element types" }).
OBJECT myobj
x:INT, y:LONG, z:INT
ENDOBJECT
PROC main()
DEF p:PTR TO INT, q:PTR TO myobj
p:=NEW [1, 9, 3, 7, 6]:INT
q:=NEW [1, 2]:myobj
WriteF('Last element in array p is \\d\\n', p[4])
WriteF('Object q is x=\\d, y=\\d, z=\\d\\n',
q.x, q.y, q.z)
WriteF('Array q is q[0].x=\\d, q[0].y=\\d, q[0].z=\\d\\n',
q[].x, q[].y, q[].z)
END p[5], q
ENDPROC
The dynamically allocated version of an object-typed list differs from the
static version in another way: it always has memory allocated for a whole
number of objects, so a partially initialised object is padded with zero
elements. The static version does not allocate this extra padding, so you
must be careful not to access any element beyond those mentioned in the
list.
The deallocation of @{b }NEW@{ub } copies of normal lists can, as ever, be left to
be done automatically at the end of the program. If you want to
deallocate them before this time you must use the function
@{b }FastDisposeList@{ub }, passing the address of the list as the only argument.
You @{i }must@{ui } not use @{b }END@{ub } or any other method of deallocation. @{b }FastDisposeList@{ub }
is the only safe way of deallocating lists allocated using @{b }NEW@{ub }.
@ENDNODE
@NODE "OOP object allocation" "OOP object allocation"
@Prev "List and typed list allocation"
@Toc "NEW and END Operators"
OOP object allocation
---------------------
Currently, the only way to create OOP objects in E is to use @{b }NEW@{ub } and
the only safe way to destroy them is to use @{b }END@{ub }. This is probably the
most common use of @{b }NEW@{ub } and @{b }END@{ub } and is described in detail later (see
@{"Objects in E" Link "OOE.guide/Objects in E" }).
@ENDNODE

View File

@ -0,0 +1,260 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Modules"
@Next "Exceptions.guide/main"
@Prev "BuiltIns.guide/main"
@Toc "Contents.guide/main"
Modules
*******
A @{fg shine }module@{fg text } is the E equivalent of a C header file and an Assembly
include file. It can contain various object and constant definitions, and
also library function offsets and library base variables. This information is
necessary for the correct use of a library.
A @{fg shine }code module@{fg text } is an extension of the module idea. As well as object
and constant definitions, a code module can contain procedures and global
variables. Code modules are like C and Assembly object files.
@{" Using Modules " Link "Using Modules" }
@{" Amiga System Modules " Link "Amiga System Modules" }
@{" Non-Standard Modules " Link "Non-Standard Modules" }
@{" Example Module Use " Link "Example Module Use" }
@{" Code Modules " Link "Code Modules" }
@ENDNODE
@NODE "Using Modules" "Using Modules"
@Next "Amiga System Modules"
@Toc "main"
Using Modules
=============
To use the definitions in a particular module you use the @{b }MODULE@{ub }
statement at the beginning of your program (before the first procedure
definition). You follow the @{b }MODULE@{ub } keyword by a comma-separated list of
strings, each of which is the filename (with path if necessary) of a
module (without the @{b }.m@{ub } extension--since every module file name must end
with @{b }.m@{ub }). The filenames (and paths) are all relative to the logical
volume @{b }Emodules:@{ub } (which can be set-up using an @{b }assign@{ub } as described in the
`Reference Manual'), unless the first character of the string is @{b }*@{ub }. In
this case the files are relative to the directory of the current source
file. For instance, the statement:
MODULE 'fred', 'dir/barney', '*mymod'
will try to load the files @{b }Emodules:fred.m@{ub }, @{b }Emodules:dir/barney.m@{ub } and
@{b }mymod.m@{ub }. If it can't find these files or they aren't proper modules the E
compiler will complain.
All the definitions in the modules included in this way are available
to every procedure in the program. To see what a module contains you can
use the @{b }showmodule@{ub } program that comes with the Amiga E distribution.
@ENDNODE
@NODE "Amiga System Modules" "Amiga System Modules"
@Next "Non-Standard Modules"
@Prev "Using Modules"
@Toc "main"
Amiga System Modules
====================
Amiga E comes with the standard Amiga system include files as E modules.
The AmigaDOS 2.04 modules are supplied with E version 2.1, and the
AmigaDOS 3.0 modules are supplied with E version 3.0. However, modules
are much more useful in E version 3.0 (see @{"Code Modules" Link "Code Modules" }). If you want to
use any of the standard Amiga libraries properly you will need to
investigate the modules for that library. The top-level @{b }.m@{ub } files in
@{b }Emodules:@{ub } contain the library function offsets, and those in directories
in @{b }Emodules:@{ub } contain constant and object definitions for the appropriate
library. For instance, the module @{b }asl@{ub } (i.e., the file @{b }Emodules:asl.m@{ub })
contains the ASL library function offsets and @{b }libraries/asl@{ub } contains the
ASL library constants and objects.
If you are going to use, say, the ASL library then you need to open the
library using the @{b }OpenLibrary@{ub } function (an Amiga system function) before
you can use any of the library functions. You also need to define the
library function offsets by using the @{b }MODULE@{ub } statement. However, the DOS,
Exec, Graphics and Intuition libraries don't need to be opened and their
function offsets are built in to E. That's why you won't find, for
example, a @{b }dos.m@{ub } file in @{b }Emodules:@{ub }. The constants and objects for these
libraries still need to be included via modules (they are not built in to
E).
@ENDNODE
@NODE "Non-Standard Modules" "Non-Standard Modules"
@Next "Example Module Use"
@Prev "Amiga System Modules"
@Toc "main"
Non-Standard Modules
====================
Several non-standard library modules are also supplied with Amiga E. To
make your own modules you need the @{b }pragma2module@{ub } and @{b }iconvert@{ub } programs.
These convert standard format C header files and Assembly include files to
modules. The C header file should contain pragmas for function offsets,
and the Assembly include file should contain constant and structure
definitions (the Assembly structures will be converted to objects).
However, unless you're trying to do really advanced things you probably
don't need to worry about any of this!
@ENDNODE
@NODE "Example Module Use" "Example Module Use"
@Next "Code Modules"
@Prev "Non-Standard Modules"
@Toc "main"
Example Module Use
==================
The gadget example program in Part Three shows how to use constants
from the module @{b }intuition/intuition@{ub } (see @{"Gadgets" Link "Examples.guide/Gadgets" }), and the IDCMP example
program shows the object @{b }gadget@{ub } from that module being used (see
@{"IDCMP Messages" Link "Examples.guide/IDCMP Messages" }). The following program uses the modules for the Reqtools
library, which is not a standard Amiga system library but a commonly used
one, and the appropriate modules are supplied with Amiga E. To run this
program, you will, of course, need the @{b }reqtools.library@{ub } in @{b }Libs:@{ub }.
MODULE 'reqtools'
PROC main()
DEF col
IF (reqtoolsbase:=OpenLibrary('reqtools.library',37))<>NIL
IF (col:=RtPaletteRequestA('Select a colour', 0,0))<>-1
RtEZRequestA('You picked colour \\d',
'I did|I can\\at remember',0,[col],0)
ENDIF
CloseLibrary(reqtoolsbase)
ELSE
WriteF('Could not open reqtools.library, version 37+\\n')
ENDIF
ENDPROC
The @{b }reqtoolsbase@{ub } variable is the library base variable for the Reqtools
library. This is defined in the module @{b }reqtools@{ub } and you @{i }must@{ui } store the
result of the @{b }OpenLibrary@{ub } call in this variable if you are going to use
any of the functions from the Reqtools library. (You can find out which
variable to use for other libraries by running the @{b }showmodule@{ub } program on
the library module.) The two functions the program uses are
@{b }RtPaletteRequestA@{ub } and @{b }RtEZRequestA@{ub }. Without the inclusion of the @{b }reqtools@{ub }
module and the setting up of the @{b }reqtoolsbase@{ub } variable you would not be
able to use these functions. In fact, if you didn't have the @{b }MODULE@{ub } line
you wouldn't even be able to compile the program because the compiler
wouldn't know where the functions came from and would complain bitterly.
Notice that the Reqtools library is closed before the program
terminates (if it had been successfully opened). This is always
necessary: if you succeed in opening a library you @{i }must@{ui } close it when
you're finished with it.
@ENDNODE
@NODE "Code Modules" "Code Modules"
@Prev "Example Module Use"
@Toc "main"
Code Modules
============
You can also make modules containing procedure definitions and some
global variables. These are called @{fg shine }code modules@{fg text } and can be extremely
useful. This section briefly outlines their construction and use. For
in-depth details see the `Reference Manual'.
Code modules can be made by using the E compiler as you would to make
an executable, except you put the statement @{b }OPT MODULE@{ub } at the start of the
code. Also, any definitions that are to be accessed from outside the
module need to be marked with the @{b }EXPORT@{ub } keyword. Alternatively, all
definitions can be exported using @{b }OPT EXPORT@{ub } at the start of the code.
You include the definitions from this module in your program using @{b }MODULE@{ub }
in the normal way.
The following code is an example of a small module:
OPT MODULE
EXPORT CONST MAX_LEN=20
EXPORT OBJECT fullname
firstname, surname
ENDOBJECT
EXPORT PROC printname(p:PTR TO fullname)
IF short(p.surname)
WriteF('Hello, \\s \\s\\n', p.firstname, p.surname)
ELSE
WriteF('Gosh, you have a long name\\n')
ENDIF
ENDPROC
PROC short(s)
RETURN StrLen(s)<MAX_LEN
ENDPROC
Everything is exported except the @{b }short@{ub } procedure. Therefore, this can be
accessed only in the module. In fact, the @{b }printname@{ub } procedure uses it
(rather artificially) to check the length of the @{b }surname@{ub }. It's not of
much use or interest apart from in the module, so that's why it isn't
exported. In effect, we've hidden the fact that @{b }printname@{ub } uses @{b }short@{ub } from
the user of the module.
Assuming the above code was compiled to module @{b }mymods/name@{ub }, here's how
it could be used:
MODULE 'mymods/name'
PROC main()
DEF fred:PTR TO fullname, bigname
fred.firstname:='Fred'
fred.surname:='Flintstone'
printname(fred)
bigname:=['Peter', 'Extremelybiglongprehistoricname']:fullname
printname(bigname)
ENDPROC
Global variables in a module are a bit more problematic than the other
kinds of definitions. You cannot initialise them in the declaration or
make them reserve chunks memory. So you can't have @{b }ARRAY@{ub }, @{b }OBJECT@{ub }, @{b }STRING@{ub }
or @{b }LIST@{ub } declarations. However, you can have pointers so this isn't a big
problem. The reason for this limitation is that exported global variables
with the same name in a module and the main program are taken to be the
same variable, and the values are shared. So you can have an array
declaration in the main program:
DEF a[80]:ARRAY OF INT
and the appropriate pointer declaration in the module:
EXPORT DEF a:PTR TO INT
The array from the main program can then be accessed in the module! For
this reason you also need to be pretty careful about the names of your
exported variables so you don't get unwanted sharing. Global variables
which are not exported are private to the module, so will not clash with
variables in the main program or other modules.
@ENDNODE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,929 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Object Oriented E"
@Next "Examples.guide/main"
@Prev "Recursion.guide/main"
@Toc "Contents.guide/main"
Object Oriented E
*****************
The Object Oriented Programming (OOP) aspects of E are covered in this
chapter. Don't worry if you don't know the OOP buzz words like `object',
`method' and `inheritance': these terms are explained in the OOP
introduction, below. (For some reason, computer science uses strange
words to cloak simple concepts in secrecy.)
@{" OOP Introduction " Link "OOP Introduction" }
@{" Objects in E " Link "Objects in E" }
@{" Methods in E " Link "Methods in E" }
@{" Inheritance in E " Link "Inheritance in E" }
@{" Data-Hiding in E " Link "Data-Hiding in E" }
@ENDNODE
@NODE "OOP Introduction" "OOP Introduction"
@Next "Objects in E"
@Toc "main"
OOP Introduction
================
`Object Oriented Programming' is the name given to a collection of
programming techniques that are meant to speed up development and ease
maintenance of large programs. These techniques have been around for a
long time, but it is only recently that languages that explicitly support
them have become popular. You do not @{i }need@{ui } to use a language that supports
OOP to program in an Object Oriented way; it's just a bit simpler if you
do!
@{" Classes and methods " Link "Classes and methods" }
@{" Example class " Link "Example class" }
@{" Inheritance " Link "Inheritance" }
@ENDNODE
@NODE "Classes and methods" "Classes and methods"
@Next "Example class"
@Toc "OOP Introduction"
Classes and methods
-------------------
The heart of OOP is the `Black Box' approach to programming. The kind
of black box in question is one where the contents are unknown but there
is a number of wires on the outside which give you some way of interacting
with the stuff on the inside. The black boxes of OOP are actually
collections of data (just like the idea of variables that we've already
met) and they are called @{fg shine }objects@{fg text } (this is the general term, which is,
coincidentally, connected with the @{b }OBJECT@{ub } type in E). Objects can be
grouped together in @{fg shine }classes@{fg text }, like the types for variables, except that a
class also defines what different kinds of wires protrude from the black
box. This extra bit (the wires) is known as the @{fg shine }interface@{fg text } to the
object, and is made up of a number of @{fg shine }methods@{fg text } (so a method is analogous
to a wire). Each method is actually just like a procedure. With a real
black box, the wires are the only way of interacting with the box, so the
methods of an object ought to be the @{i }only@{ui } way of creating and using the
object. Of course, the methods themselves normally need to know the
internal workings of the object, just like the way the wires are normally
connected to something inside the black box.
There are two special kinds of methods: @{fg shine }constructors@{fg text } and
@{fg shine }destructors@{fg text }. A @{fg shine }constructor@{fg text } is a method which is used to
initialise the data in an object, and a class may have several different
constructors (allowing for different kinds of initialisation) or it may
have none if no special initialisation is necessary. Constructors are
normally used to allocate the resources (such as memory) that an object
needs. The deallocation of such resources is done by the @{fg shine }destructor@{fg text }, of
which there is at most one for each class.
Protecting the contents of an object in the `black box' way is known as
@{fg shine }data-hiding@{fg text } (the data in the object is visible only to its methods), and
only allowing the contents of an object to be manipulated via its
interface is known as @{fg shine }data abstraction@{fg text }. By using this approach, only
the methods know the structure of the data in an object and so this
structure can be changed without affecting the whole of a program: only
the methods would potentially need recoding. As you might be able to
tell, this simplifies maintenance quite considerably.
@ENDNODE
@NODE "Example class" "Example class"
@Next "Inheritance"
@Prev "Classes and methods"
@Toc "OOP Introduction"
Example class
-------------
A good example of a class is the mathematical notion of a set (of
integers). A particular object from this class would represent a
particular set of integers. The interface for the class would probably
include the following methods:
1. @{b }Add@{ub } -- adds an integer to a set object.
2. @{b }Member@{ub } -- tests for membership of an integer in a set object.
3. @{b }Empty@{ub } -- tests for emptiness of a set object.
4. @{b }Union@{ub } -- unions a set object with a set object.
A more complete class would also contain methods for removing elements,
intersecting sets etc. The important thing to notice is that to use this
class you need to know only how to use the methods. The black box
approach means that we don't (and shouldn't) know how the set class is
actually implemented, i.e., how data is structured within a set object.
Only the methods themselves need to know how to manipulate the data that
represents a set object.
The benefit of OOP comes when you actually use the classes, so suppose
you implement this set class and then use it in your code for some
database program. If you found that the set implementation was a bit
inefficient (in terms of memory or speed), then, since you programmed in
this OOP way, you wouldn't have to recode the whole database program, just
the set class! You can change the way the set data is structured in an
object as much and as often as you like, so long as each implementation
has the same interface (and gives the same results!).
@ENDNODE
@NODE "Inheritance" "Inheritance"
@Prev "Example class"
@Toc "OOP Introduction"
Inheritance
-----------
The remaining OOP concept of interest is @{fg shine }inheritance@{fg text }. This is a
grand name for a way of building on classes that enables the @{fg shine }derived@{fg text }
(i.e., bigger) class to be used as if its objects were really members of
the inherited, or @{fg shine }base@{fg text }, class. For example, suppose class @{b }D@{ub } were
derived from class @{b }B@{ub }, so @{b }D@{ub } is the derived class and @{b }B@{ub } is the base class.
In this case, class @{b }D@{ub } inherits the data structure of class @{b }B@{ub }, and may add
extra data to it. It also inherits all the methods of class @{b }B@{ub }, and
objects of class @{b }D@{ub } may be treated as if they were really objects of class
@{b }B@{ub }.
Of course, an inherited method cannot affect the extra data in class @{b }D@{ub },
only the inherited data. To affect the extra data, class @{b }D@{ub } can have extra
methods defined, or it can make new definitions for the inherited methods. The
latter approach is only really useful if the new definition of an
inherited method is pretty similar to the inherited method, differing only
in how it affects the extra data in class @{b }D@{ub }. This overriding of methods
does not affect the methods in class @{b }B@{ub } (nor those of other classes derived
from @{b }B@{ub }), but only those in class @{b }D@{ub } and the classes derived from @{b }D@{ub }.
@ENDNODE
@NODE "Objects in E" "Objects in E"
@Next "Methods in E"
@Prev "OOP Introduction"
@Toc "main"
Objects in E
============
Classes are defined using @{b }OBJECT@{ub } in the same way that we've seen before
(see @{"OBJECT Type" Link "Types.guide/OBJECT Type" }). So, in E, the terms `object declaration' and `class'
may be used interchangeably. However, referring to an @{b }OBJECT@{ub } type as a
`class' signals the presence of methods in an object.
The following example @{b }OBJECT@{ub } is the basis of a set class, as described
above (see @{"Example class" Link "Example class" }). This set implementation is going to be quite
simple and it will be limited to a maximum of 100 elements.
OBJECT set
elts[100]:ARRAY OF LONG
size
ENDOBJECT
Currently, the only way to allocate an OOP object is to use @{b }NEW@{ub } with an
appropriately typed pointer. The following sections of code all allocate
memory for the data of @{b }set@{ub }, but only the last one allocates an OOP @{b }set@{ub }
object. Each one may use and access the @{b }set@{ub } data, but only the last one
may call the methods of @{b }set@{ub }.
DEF s:set
DEF s:PTR TO set
s:=NewR(SIZEOF set)
DEF s:PTR TO set
s:=NEW s
OOP objects can, of course, be deallocated using @{b }END@{ub }, in which case the
destructor for the corresponding class is also called. Leaving an OOP
object to be deallocated automatically at the end of the program is not
quite as safe as normal, since in this case the destructor will not be
called. Also, when using @{b }END@{ub } to deallocate an object you do not need to
use a pointer of exactly the same type as the object (like you would for
normal @{b }NEW@{ub } allocations). Instead you can use a pointer of any of the base
classes' types. Constructors and destructors are described in more detail
below.
@ENDNODE
@NODE "Methods in E" "Methods in E"
@Next "Inheritance in E"
@Prev "Objects in E"
@Toc "main"
Methods in E
============
The @{fg shine }methods@{fg text } of E are very similar to normal procedures, but there is
one, big difference: a method is part of a class, so must somehow be
identified with the other parts of the class. In E this identification is
done by relating all methods to the corresponding @{b }OBJECT@{ub } type for the
class, using the @{b }OF@{ub } keyword after the description of the method's
parameters. So, the methods of the simple set class would be defined as
outlined below (of course, these examples have omitted the code of
methods).
PROC add(x) OF set
/* code for add method */
ENDPROC
PROC member(x) OF set
/* code for member method */
ENDPROC
PROC empty() OF set
/* code for empty method */
ENDPROC
PROC union(s:PTR TO set) OF set
/* code for union method */
ENDPROC
At first sight it might seem that the particular @{b }set@{ub } object which would
be manipulated by these methods is missing from the parameters. For
instance, it appears that the @{b }empty@{ub } method should need an extra @{b }PTR TO set@{ub }
parameter, and that would be the @{b }set@{ub } object it tested for emptiness.
However, methods are called in a slightly different way to normal
procedures. A method is a part of a class, and is called in a similar way
to accessing the data elements of the class. That is, the method is
selected using @{b }.@{ub } and acts (implicitly) on the object from which it was
selected. The following example shows the allocation of a set object and
the use of some of the above methods.
DEF s:PTR TO set
NEW s -> Allocate an OOP object
s.add(17)
s.add(-34)
IF s.empty()
WriteF('Error: the set s should not be empty!\\n')
ELSE
WriteF('OK: not empty\\n')
ENDIF
IF s.member(0)
WriteF('Error: how did 0 get in there?\\n')
ELSE
WriteF('OK: 0 is not a member\\n')
ENDIF
IF s.member(-34)
WriteF('OK: -34 is a member\\n')
ELSE
WriteF('Error: where has -34 gone?\\n')
ENDIF
END s -> Finished with s now
This is why the methods do not take that extra @{b }PTR TO set@{ub } argument. If a
method is called then it has been selected from an appropriate object, and
so this must be the object which it affects. The slightly complicated
method is @{b }union@{ub } which adds another @{b }set@{ub } object by unioning it. In this
case, the argument to the method is a @{b }PTR TO set@{ub }, but this is the set to
be added, not the set which is being expanded.
So, how do you refer to the object which is being affected? In other
words, how do you affect it? Well, this is the remaining difference from
normal procedures: every method has a special local variable, @{b }self@{ub }, which
is of type @{b }PTR TO @{ub }@{fg shine }class@{fg text }@{b }@{ub } and is initialised to point to the object from
which the method was selected. Using this variable, the data and methods
of object can be accessed and used as normal. For instance, the @{b }empty@{ub }
method has a @{b }self@{ub } local variable of type @{b }PTR TO set@{ub }, and can be defined as
below:
PROC empty() OF set IS self.size=0
@{fg shine }Constructors@{fg text } are simply methods which initialise the data of an
object. For this reason they should normally be called only when the
object is allocated. The @{b }NEW@{ub } operator allows OOP objects to call a
constructor at the point at which they are allocated, to make this easier
and more explicit. The constructor will be called after @{b }NEW@{ub } has allocated
the memory for the object. It is wise to give constructors suggestive
names like @{b }create@{ub } and @{b }copy@{ub }, or the same name as the class. The following
constructors might be defined for the set class:
/* Create empty set */
PROC create() OF set
self.size=0
ENDPROC
/* Copy existing set */
PROC copy(oldset:PTR TO set) OF set
DEF i
FOR i:=0 TO oldset.size-1
self.elements[i]:=oldset.elements[i]
ENDFOR
self.size:=oldset.size
ENDPROC
They would be used as in the code below. Notice that the @{b }create@{ub }
constructor is, in this case, redundant since @{b }NEW@{ub } will initialise the data
elements to zero. If @{b }NEW@{ub } does sufficient initialisation then you do not
have to define any constructors, and even if you do have constructors you
don't @{i }have@{ui } to use them when allocating objects.
DEF s:PTR TO set, t:PTR TO set, u:PTR TO set
NEW s.create()
IF s.empty THEN WriteF('s is empty\\n')
END s
NEW t /* This happens to be the same as using create */
IF t.empty THEN WriteF('t is empty\\n')
t.add(10)
NEW u.copy(t)
IF u.member(10) THEN WriteF('10 is in u\\n')
END t, u
For each class there is at most one @{fg shine }destructor@{fg text }, and this is
responsible for clearing up and deallocating resources. If one is needed
then it must be called @{b }end@{ub }, and (as this might suggest) it is called
automatically when an OOP object is deallocated using @{b }END@{ub }. So, for OOP
objects with a destructor, the (roughly) equivalent code to @{b }END@{ub } using
@{b }Dispose@{ub } is a bit different. Take care to note that the destructor is @{i }not@{ui }
called if @{b }END@{ub } is not used to deallocate an OOP object (i.e., if
deallocation is left to be done automatically at the end of the program).
END p
IF p
p.end() -> Call destructor
Dispose(p)
p:=NIL
ENDIF
The simple implementation of the set class needs no destructor. If,
however, the @{b }elements@{ub } data were a pointer (to @{b }LONG@{ub }), and the array were
allocated based on some size parameter to a constructor, then a destructor
would be useful. In this case the set class would also need a @{b }maxsize@{ub }
data element, which records the maximum, allocated size of the @{b }elements@{ub }
array.
OBJECT set
elements:PTR TO LONG
size
maxsize
ENDOBJECT
PROC create(sz=100) OF set -> Default to 100
DEF p:PTR TO LONG
self.maxsize:=IF (sz>0) AND (sz<100000) THEN sz ELSE 100
self.elements:=NEW p[self.maxsize]
ENDPROC
PROC end() OF set
DEF p:PTR TO LONG
IF self.maxsize=0
WriteF('Error: did not create() the set\\n')
ELSE
p:=self.elements
END p[self.maxsize]
ENDIF
ENDPROC
Without the destructor @{b }end@{ub }, the memory allocated for @{b }elements@{ub } would not be
deallocated when @{b }END@{ub } is used, although it would get deallocated at the end
of the program (in this case). However, if @{b }AllocMem@{ub } were used instead of
@{b }NEW@{ub } to allocate the array, then the memory would have to be deallocated
using @{b }FreeMem@{ub }, and this would best be done in the destructor, as above.
(The memory would not be deallocated automatically at the end of the
program if @{b }AllocMem@{ub } is used.) Another solution to this kind of problem
would be to have a special method which called @{b }FreeMem@{ub }, and to remember to
call this method just before deallocating one of these objects, so you can
see that the interaction of @{b }END@{ub } with destructors is quite useful.
Already, the above re-definition of @{b }set@{ub } begins to show the power of OOP.
The actual implementation of the set class is very different, but the
interface can remain the same. The code for the methods would need to
change to take into account the new @{b }maxsize@{ub } element (where before the
fixed size of 100 was used), and also to deal with the possibility the
@{b }create@{ub } constructor had not been used (in which case @{b }elements@{ub } would be @{b }NIL@{ub }
and @{b }maxsize@{ub } zero). But the code which used the set class would not need
to change, except maybe to allocate more sensibly sized sets!
Yet another, different implementation of a set was outlined above (see
@{"Binary Trees" Link "Recursion.guide/Binary Trees" }). In fact, remarkably few changes would be needed to convert
the code from that section into another implementation of the set class.
The @{b }new_set@{ub } procedure is like a set constructor which initialises the set
to be a singleton (i.e., to contain one element), and the @{b }add@{ub } procedure is
just like the @{b }add@{ub } method of the set class. The only slight problem is
that empty sets are not modelled by the binary tree implementation, so it
wouldn't, as it stands, be a complete implementation. It would be
straight-forward (but unduly complicated at this point) to add support for
empty sets to this particular implementation.
@ENDNODE
@NODE "Inheritance in E" "Inheritance in E"
@Next "Data-Hiding in E"
@Prev "Methods in E"
@Toc "main"
Inheritance in E
================
One class is @{fg shine }derived@{fg text } from another using the @{b }OF@{ub } keyword in the
definition of the derived class @{b }OBJECT@{ub }, in a similar way that @{b }OF@{ub } is used
with methods. For instance, the following code shows how to define the
class @{b }d@{ub } to be derived from class @{b }b@{ub }. The class @{b }b@{ub } is then said to be
@{fg shine }inherited@{fg text } by the class @{b }d@{ub }.
OBJECT b
b_data
ENDOBJECT
OBJECT d OF b
extra_d_data
ENDOBJECT
The names @{b }b@{ub } and @{b }d@{ub } have been chosen to be somewhat suggestive, since the
class which is inherited (i.e., @{b }b@{ub }) is known as the @{fg shine }base@{fg text } class, whilst
the inheriting class (i.e., @{b }d@{ub }) is known as the @{fg shine }derived@{fg text } class.
The definition of @{b }d@{ub } is the same as the following definition of @{b }duff@{ub },
except for one major difference: with the above derivation the methods of
@{b }b@{ub } are also inherited by @{b }d@{ub } and they become methods of class @{b }d@{ub }. The
definition of @{b }duff@{ub } relates it in no way to @{b }b@{ub }, except at best accidentally
(since any changes to @{b }b@{ub } do not affect @{b }duff@{ub }, whereas they would affect @{b }d@{ub }).
OBJECT duff
b_data
extra_d_data
ENDOBJECT
One property of this derivation applies to the data records built by
@{b }OBJECT@{ub } as well as the OOP classes. The data records of type @{b }d@{ub } or @{b }duff@{ub } may
be used wherever a data record of type @{b }b@{ub } were required (e.g., the argument
to some procedure), and they are, in fact, indistinguishable from records
of type @{b }b@{ub }. Although, if the definition of @{b }b@{ub } were changed (e.g., by
changing the name of the @{b }b_data@{ub } element) then data records of type @{b }duff@{ub }
would not be usable in this way, but those of type @{b }d@{ub } still would.
Therefore, it is wise to use inheritance to show the relationships between
classes or data of @{b }OBJECT@{ub } types. The following example shows how
procedure @{b }print_b_data@{ub } can validly be called in several ways, given the
definitions of @{b }b@{ub }, @{b }d@{ub } and @{b }duff@{ub } above.
PROC print_b_data(p:PTR TO b)
WriteF('b_data = \\d\\n', p.b_data)
ENDPROC
PROC main()
DEF p_b:PTR TO b, p_d:PTR TO d, p_duff:PTR TO duff
NEW p_b, p_d, p_duff
p_b.b_data:=11
p_d.b_data:=-3
p_duff.b_data:=27
WriteF('Printing p_b: ')
print_b_data(p_b)
WriteF('Printing p_d: ')
print_b_data(p_d)
WriteF('Printing p_duff: ')
print_b_data(p_duff)
ENDPROC
So far, no methods have been defined for @{b }b@{ub }, which means that it is just
an @{b }OBJECT@{ub } type. The procedure @{b }print_b_data@{ub } suggests a useful method of @{b }b@{ub },
which will be called @{b }print@{ub }.
PROC print() OF b
WriteF('b_data = \\d\\n', self.b_data)
ENDPROC
This definition would also define a @{b }print@{ub } method for @{b }d@{ub }, since @{b }d@{ub } is derived
from @{b }b@{ub } and it inherits all the methods of @{b }b@{ub }. However, @{b }duff@{ub } would, of
course, still be just an @{b }OBJECT@{ub } type, although it could have a similar
@{b }print@{ub } method explicitly defined for it. If @{b }b@{ub } has any methods defined for
it (i.e., if it is a class) then data records of type @{b }duff@{ub } cannot be used
as if they were @{i }objects@{ui } of the class @{b }b@{ub }, and it is not safe to try! In
this case, only objects of derived class @{b }d@{ub } can be used in this manner.
(If @{b }b@{ub } is a class then @{b }d@{ub } is a class, due to inheritance.)
PROC main()
DEF p_b:PTR TO b, p_d:PTR TO d, p_duff:PTR TO duff
NEW p_b, p_d, p_duff
p_b.b_data:=11
p_d.b_data:=-3; p_d.extra_d_data:=3
p_duff.b_data:=7; p_duff.extra_d_data:=-7
WriteF('Printing p_b: ')
/* b explicitly has print method */
p_b.print()
WriteF('Printing p_d: ')
/* d inherits print method from b */
p_d.print()
WriteF('No print method for p_duff\\n')
/* Do not try to print p_duff in this way */
/* p_duff.print() */
ENDPROC
Unfortunately, the @{b }print@{ub } method inherited by @{b }d@{ub } only prints the @{b }b_data@{ub }
element (since it is really a method of @{b }b@{ub }, so cannot access the extra data
added in @{b }d@{ub }). However, any inherited method can be overridden by defining
it again, this time for the derived class.
PROC print() OF d
WriteF('extra_d_data = \\d, ', self.extra_d_data)
WriteF('b_data = \\d\\n', self.b_data)
ENDPROC
With this extra definition, the same @{b }main@{ub } procedure above would now print
all the data of @{b }d@{ub }, but only the @{b }b_data@{ub } element of @{b }b@{ub }. This is because the
new definition of @{b }print@{ub } affects only class @{b }d@{ub } (and classes derived from @{b }d@{ub }).
Inherited methods are often overridden just to add extra functionality,
as in the case above where we wanted the extra data to be printed as well
as the data derived from @{b }b@{ub }. For this purpose, the @{b }SUPER@{ub } operator can be
used on a method call to force the base class method to be used, where
normally the derived class method would be used. So, the definition of
the @{b }print@{ub } method for class @{b }d@{ub } could call the @{b }print@{ub } method of class @{b }b@{ub }.
PROC print() OF d
WriteF('extra_d_data = \\d, ', self.extra_d_data)
SUPER self.print()
ENDPROC
Be careful, though, because without the @{b }SUPER@{ub } operator this would involve
a recursive call to the @{b }print@{ub } method of class @{b }d@{ub }, rather than a call to the
base class method.
Just as data records of type @{b }d@{ub } can be used wherever data records of
type @{b }b@{ub } were required, objects of class @{b }d@{ub } can used in place of objects of
class @{b }b@{ub }. The following procedure prints a message and the object data,
using the @{b }print@{ub } method of @{b }b@{ub }. (Of course, only the methods named by class
@{b }b@{ub } can be used in such a procedure, since the pointer @{b }p@{ub } is of type @{b }PTR
TO b@{ub }.)
PROC msg_print(msg, p:PTR TO b)
WriteF('Printing \\s: ', msg)
p.print()
ENDPROC
PROC main()
DEF p_b:PTR TO b, p_d:PTR TO d
NEW p_b, p_d
p_b.b_data:=11
p_d.b_data:=-3; p_d.extra_d_data:=3
msg_print('p_b', p_b)
msg_print('p_d', p_d)
ENDPROC
You can't use @{b }duff@{ub } now, since it is not a class and @{b }b@{ub } is, and @{b }msg_print@{ub }
expects a pointer to class @{b }b@{ub }. The only other objects that can be passed
to @{b }msg_print@{ub } are objects from classes derived from @{b }b@{ub }, and this is why @{b }p_d@{ub }
can be printed using @{b }msg_print@{ub }. If you collect together the code and run
the example you will see that the call to @{b }print@{ub } in @{b }msg_print@{ub } uses the
overridden @{b }print@{ub } method when @{b }msg_print@{ub } is called with @{b }p_d@{ub } as a parameter.
That is, the correct method is called even though the pointer @{b }p@{ub } is not of
type @{b }PTR TO d@{ub }. This is called @{fg shine }polymorphism@{fg text }: different implementations
of @{b }print@{ub } may be called depending on the real, @{fg shine }dynamic@{fg text } type of @{b }p@{ub }. Here's
what should be printed:
Printing p_b: b_data = 11
Printing p_d: extra_d_data = 3, b_data = -3
Inheritance is not limited to a single layer: you can derive other
classes from @{b }b@{ub }, you can derive classes from @{b }d@{ub }, and so on. For instance,
if class @{b }e@{ub } is derived from class @{b }d@{ub } then it would inherit all the data of @{b }d@{ub }
and all the methods of @{b }d@{ub }. This means that @{b }e@{ub } would inherit the richer
version of @{b }print@{ub }, and may even override it yet again. In this case, class
@{b }e@{ub } would have two base classes, @{b }b@{ub } and @{b }d@{ub }, but would be derived directly from
@{b }d@{ub } (and indirectly from @{b }b@{ub }, via @{b }d@{ub }). Class @{b }d@{ub } would therefore be known as the
@{fg shine }super@{fg text } class of @{b }e@{ub }, since @{b }e@{ub } is derived directly from @{b }d@{ub }. (The super class
of @{b }d@{ub } is its only base class, @{b }b@{ub }.) So, the @{b }SUPER@{ub } operator is actually used
to call the methods in the super class. In this example, the @{b }SUPER@{ub }
operator can be used in the methods of @{b }e@{ub } to call methods of @{b }d@{ub }.
The binary tree implementation above (see @{"Binary Trees" Link "Recursion.guide/Binary Trees" }) suggests a good
example for a @{fg shine }class hierarchy@{fg text } (a collection of classes related by
inheritance). A basic tree structure can be encapsulated in a base class
definition, and then specific kinds of tree (with different data at the
nodes) can be derived from this. In fact, the base class @{b }tree@{ub } defined
below is only useful for inheriting, since a tree is pretty useless
without some data attached to the nodes. Since it is very likely that
objects of class @{b }tree@{ub } will never be useful (but objects of classes derived
from @{b }tree@{ub } would be), the @{b }tree@{ub } class is called an @{fg shine }abstract@{fg text } class.
OBJECT tree
left:PTR TO tree, right:PTR TO tree
ENDOBJECT
PROC nodes() OF tree
DEF tot=1
IF self.left THEN tot:=tot+self.left.nodes()
IF self.right THEN tot:=tot+self.right.nodes()
ENDPROC tot
PROC leaves(show=FALSE) OF tree
DEF tot=0
IF self.left
tot:=tot+self.left.leaves(show)
ENDIF
IF self.right
tot:=tot+self.right.leaves(show)
ELSEIF self.left=NIL
IF show THEN self.print_node()
tot++
ENDIF
ENDPROC tot
PROC print_node() OF tree
WriteF('<NULL> ')
ENDPROC
PROC print() OF tree
IF self.left THEN self.left.print()
self.print_node()
IF self.right THEN self.right.print()
ENDPROC
The @{b }nodes@{ub } and @{b }leaves@{ub } methods return the number of nodes and leaves of the
tree, respectively, with the @{b }leaves@{ub } method taking a flag to specify
whether the leaves should also be printed. These methods should never
need overriding in a class derived from @{b }tree@{ub }, and neither should @{b }print@{ub },
which traverses the tree, printing the nodes from left to right. However,
the @{b }print_node@{ub } method probably should be overridden, as is the case in the
integer tree defined below.
OBJECT integer_tree OF tree
int
ENDOBJECT
PROC create(i) OF integer_tree
self.int:=i
ENDPROC
PROC add(i) OF integer_tree
DEF p:PTR TO integer_tree
IF i < self.int
IF self.left
p:=self.left
p.add(i)
ELSE
self.left:=NEW p.create(i)
ENDIF
ELSEIF i > self.int
IF self.right
p:=self.right
p.add(i)
ELSE
self.right:=NEW p.create(i)
ENDIF
ENDIF
ENDPROC
PROC print_node() OF integer_tree
WriteF('\\d ', self.int)
ENDPROC
This is a nice example of polymorphism at work: we can implement a @{b }tree@{ub }
which works with integers simply by defining the appropriate methods. The
@{b }leaves@{ub } method (of the @{b }tree@{ub } class) will then automatically call the
@{b }integer_tree@{ub } version of @{b }print_node@{ub } whenever we pass it an @{b }integer_tree@{ub }
object. The definitions of @{b }tree@{ub } and @{b }integer_tree@{ub } can even be in different
modules (see @{"Data-Hiding in E" Link "Data-Hiding in E" }), and, using these OOP techniques, the
module containing @{b }tree@{ub } would not need to be recompiled even if a class
like @{b }integer_tree@{ub } is added or changed. This shows why OOP is good for
code-reuse and extensibility: with traditional programming techniques we
would have to adapt the binary tree functions to account for integers, and
again for each new datatype.
Notice that the recursive use of the new method @{b }add@{ub } must be called via
an auxiliary pointer, @{b }p@{ub }, of the derived class. This is because the @{b }left@{ub }
and @{b }right@{ub } elements of @{b }tree@{ub } are pointers to @{b }tree@{ub } objects and @{b }add@{ub } is not a
method of @{b }tree@{ub } (the compiler would reject the code as a syntax error if
you tried to directly access @{b }add@{ub } under these circumstances). Of course,
if the @{b }tree@{ub } class had an @{b }add@{ub } method there would not be this problem, but
what would the code be for such a method?
An @{b }add@{ub } method does not really make sense for @{b }tree@{ub }, but if almost all
classes derived from @{b }tree@{ub } are going to need such a method it might be nice
to include it in the @{b }tree@{ub } base class. This is the purpose of @{fg shine }abstract@{fg text }
methods. An @{fg shine }abstract@{fg text } method is one which exists in a base class solely
so that it can be overridden in some derived class. Normally, such
methods have no sensible definition in the base class, so there is a
special keyword, @{b }EMPTY@{ub }, which can be used to define them. For example,
the @{b }add@{ub } method in @{b }tree@{ub } would be defined as below.
PROC add(x) OF tree IS EMPTY
With this definition, the code for the @{b }add@{ub } method for the @{b }integer_tree@{ub }
class could be simplified. (The auxiliary pointer, @{b }p@{ub }, is still needed for
use with @{b }NEW@{ub }, since an expression like @{b }self.left@{ub } is not a pointer
variable.)
PROC add(i) OF integer_tree
DEF p:PTR TO integer_tree
IF i < self.int
IF self.left
self.left.add(i)
ELSE
self.left:=NEW p.create(i)
ENDIF
ELSEIF i > self.int
IF self.right
self.right.add(i)
ELSE
self.right:=NEW p.create(i)
ENDIF
ENDIF
ENDPROC
This, however, is not the best example of an abstract method, since the
@{b }add@{ub } method in every class derived from @{b }tree@{ub } must now take a single @{b }LONG@{ub }
value as an parameter, in order to be compatible. In general, though, a
class representing a tree with node data of type @{fg shine }t@{fg text } would really want an
@{b }add@{ub } method to take a single parameter of type @{fg shine }t@{fg text }. The fact that a @{b }LONG@{ub }
value can represent a pointer to any type is helpful, here. This means
that the definition of @{b }add@{ub } may not be so limiting, after all.
The @{b }print_node@{ub } method is much more obviously suited to being an
abstract method. The above definition prints something silly, because at
that point we didn't know about abstract methods and we needed the method
to be defined in the base class. A much better definition would make
@{b }print_node@{ub } abstract.
PROC print_node() OF tree IS EMPTY
It is quite safe to call these abstract methods, even for @{b }tree@{ub } class
objects. If a method is still abstract in any class (i.e., it has not
been overridden), then calling it on objects of that class has the same
effect as calling a function which just returns zero (i.e., it does very
little!).
The @{b }integer_tree@{ub } class could be used like this:
PROC main()
DEF t:PTR TO integer_tree
NEW t.create(10)
t.add(-10)
t.add(3)
t.add(5)
t.add(-1)
t.add(1)
WriteF('t has \\d nodes, with \\d leaves: ',
t.nodes(), t.leaves())
t.leaves(TRUE)
WriteF('\\n')
WriteF('Contents of t: ')
t.print()
WriteF('\\n')
END t
ENDPROC
@ENDNODE
@NODE "Data-Hiding in E" "Data-Hiding in E"
@Prev "Inheritance in E"
@Toc "main"
Data-Hiding in E
================
@{fg shine }Data-hiding@{fg text } is accomplished in E at the module level. This means,
effectively, that it is wise to define classes in separate modules (or at
least only closely related classes together in a module), taking care to
@{b }EXPORT@{ub } only the definitions that you need to. You can also use the
@{b }PRIVATE@{ub } keyword in the definition of any @{b }OBJECT@{ub } to hide all the
elements following it from code which uses the module (although this does
not affect the code within the module). The @{b }PUBLIC@{ub } keyword can be used in
a similar way to make the elements which follow visible (i.e., accessible)
again, as they are by default. For instance, the following @{b }OBJECT@{ub }
definition makes @{b }x@{ub }, @{b }y@{ub }, @{b }a@{ub } and @{b }b@{ub } private (so only visible to the code within
the same module), and @{b }p@{ub }, @{b }q@{ub } and @{b }r@{ub } public (so visible to code external to
the module, too).
OBJECT rec
p:INT
PRIVATE
x:INT
y
PUBLIC
q
r:PTR TO LONG
PRIVATE
a:PTR TO LONG, b
ENDOBJECT
For the set class you would probably want to make all the data private
and all the methods public. In this way you force programs which use this
module to use the supplied interface, rather than fiddling with the set
data structures themselves. The following example is the complete code
for a simple, inefficient set class, and can be compiled to a module.
OPT MODULE -> Define class 'set' in a module
OPT EXPORT -> Export everything
/* The data for the class */
OBJECT set PRIVATE -> Make all the data private
elements:PTR TO LONG
maxsize, size
ENDOBJECT
/* Creation constructor */
/* Minimum size of 1, maximum 100000, default 100 */
PROC create(sz=100) OF set
DEF p:PTR TO LONG
self.maxsize:=IF (sz>0) AND (sz<100000) THEN sz ELSE 100 -> Check size
self.elements:=NEW p[self.maxsize]
ENDPROC
/* Copy constructor */
PROC copy(oldset:PTR TO set) OF set
DEF i
self.create(oldset.maxsize) -> Call create method!
FOR i:=0 TO oldset.size-1 -> Copy elements
self.elements[i]:=oldset.elements[i]
ENDFOR
self.size:=oldset.size
ENDPROC
/* Destructor */
PROC end() OF set
DEF p:PTR TO LONG
IF self.maxsize<>0 -> Check that it was allocated
p:=self.elements
END p[self.maxsize]
ENDIF
ENDPROC
/* Add an element */
PROC add(x) OF set
IF self.member(x)=FALSE -> Is it new? (Call member method!)
IF self.size=self.maxsize
Raise("full") -> The set is already full
ELSE
self.elements[self.size]:=x
self.size:=self.size+1
ENDIF
ENDIF
ENDPROC
/* Test for membership */
PROC member(x) OF set
DEF i
FOR i:=0 TO self.size-1
IF self.elements[i]=x THEN RETURN TRUE
ENDFOR
ENDPROC FALSE
/* Test for emptiness */
PROC empty() OF set IS self.size=0
/* Union (add) another set */
PROC union(other:PTR TO set) OF set
DEF i
FOR i:=0 TO other.size-1
self.add(other.elements[i]) -> Call add method!
ENDFOR
ENDPROC
/* Print out the contents */
PROC print() OF set
DEF i
WriteF('{ ')
FOR i:=0 TO self.size-1
WriteF('\\d ', self.elements[i])
ENDFOR
WriteF('}')
ENDPROC
This class can be used in another module or program, as below:
MODULE '*set'
PROC main() HANDLE
DEF s=NIL:PTR TO set
NEW s.create(20)
s.add(1)
s.add(-13)
s.add(91)
s.add(42)
s.add(-76)
IF s.member(1) THEN WriteF('1 is a member\\n')
IF s.member(11) THEN WriteF('11 is a member\\n')
WriteF('s = ')
s.print()
WriteF('\\n')
EXCEPT DO
END s
SELECT exception
CASE "NEW"
WriteF('Out of memory\\n')
CASE "full"
WriteF('Set is full\\n')
ENDSELECT
ENDPROC
@ENDNODE

View File

@ -0,0 +1,306 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Procedures and Functions"
@Next "Constants.guide/main"
@Prev "Format.guide/main"
@Toc "Contents.guide/main"
Procedures and Functions
************************
A @{fg shine }function@{fg text } is a procedure which returns a value. This value can be
formed from any expression so it may depend on the parameters with which
the function was called. For instance, the addition operator @{b }+@{ub } can be
thought of as a function which returns the sum of its two parameters.
@{" Functions " Link "Functions" }
@{" One-Line Functions " Link "One-Line Functions" }
@{" Default Arguments " Link "Default Arguments" }
@{" Multiple Return Values " Link "Multiple Return Values" }
@ENDNODE
@NODE "Functions" "Functions"
@Next "One-Line Functions"
@Toc "main"
Functions
=========
We can define our own addition function, @{b }add@{ub }, in a very similar way to
the definition of a procedure.
PROC main()
DEF sum
sum:=12+79
WriteF('Using +, sum is \\d\\n', sum)
sum:=add(12,79)
WriteF('Using add, sum is \\d\\n', sum)
ENDPROC
PROC add(x, y)
DEF s
s:=x+y
ENDPROC s
This should generate the following output:
Using +, sum is 91
Using add, sum is 91
In the procedure @{b }add@{ub } the value @{b }s@{ub } is returned using the @{b }ENDPROC@{ub } label. The
value returned from @{b }add@{ub } can be used in expressions, just like any other
value. You do this by writing the procedure call where you want the value
to be. In the above example we wanted the value to be assigned to @{b }sum@{ub } so
we wrote the call to @{b }add@{ub } on the right-hand side of the assignment. Notice
the similarities between the uses of @{b }+@{ub } and @{b }add@{ub }. In general, @{b }add(a,b)@{ub } can
be used in exactly the same places that @{b }a+b@{ub } can (more precisely, it can be
used anywhere @{b }(a+b)@{ub } can be used).
The @{b }RETURN@{ub } keyword can also be used to return values from a procedure.
If the @{b }ENDPROC@{ub } method is used then the value is returned when the
procedure reaches the end of its code. However, if the @{b }RETURN@{ub } method is
used the value is returned immediately at that point and no more of the
procedure's code is executed. Here's the same example using @{b }RETURN@{ub }:
PROC add(x, y)
DEF s
s:=x+y
RETURN s
ENDPROC
The only difference is that you can write @{b }RETURN@{ub } anywhere in the code part
of a procedure and it finishes the execution of the procedure at that
point (rather than execution finishing when it reaches the end of the
code). In fact, you can use @{b }RETURN@{ub } in the @{b }main@{ub } procedure to prematurely
finish the execution of a program.
Here's a slightly more complicated use of @{b }RETURN@{ub }:
PROC limitedadd(x,y)
IF x>10000
RETURN 10000
ELSEIF x<-10000
RETURN -10000
ELSE
RETURN x+y
ENDIF
/* The following code is redundant */
x:=1
IF x=1 THEN RETURN 9999 ELSE RETURN -9999
ENDPROC
This function checks to see if @{b }x@{ub } is greater than 10,000 or less than
-10,000, and if it is a limited value is returned (which is generally not
the correct sum!). If @{b }x@{ub } is between -10,000 and 10,000 the correct answer
is returned. The lines after the first @{b }IF@{ub } block will never get executed
because execution will have finished at one of the @{b }RETURN@{ub } lines. Those
lines are therefore just a waste of compiler time and can safely be
omitted (as the comment suggests).
If no value is given with the @{b }ENDPROC@{ub } or @{b }RETURN@{ub } keyword then zero is
returned. Therefore, all procedures are actually functions (and the terms
@{fg shine }procedure@{fg text } and @{fg shine }function@{fg text } will tend to be used interchangeably). So, what
happens to the value when you write a procedure call on a line by itself,
not in an expression? Well, as we will see, the value is simply discarded
(see @{"Turning an Expression into a Statement" Link "MoreExpressions.guide/Turning an Expression into a Statement" }). This is what happened in
the previous examples when we called the procedures @{b }fred@{ub } and @{b }WriteF@{ub }.
@ENDNODE
@NODE "One-Line Functions" "One-Line Functions"
@Next "Default Arguments"
@Prev "Functions"
@Toc "main"
One-Line Functions
==================
Just as the @{b }IF@{ub } block and @{b }FOR@{ub } loop have horizontal, single line forms,
so does a procedure definition. The general form is:
PROC @{fg shine }name@{fg text } (@{fg shine }arg1@{fg text }, @{fg shine }arg2@{fg text }, ...) IS @{fg shine }expression@{fg text }
Alternatively, the @{b }RETURN@{ub } keyword can be used:
PROC @{fg shine }name@{fg text } (@{fg shine }arg1@{fg text }, @{fg shine }arg2@{fg text }, ...) RETURN @{fg shine }expression@{fg text }
At first sight this might seem pretty unusable, but it is useful for very
simple functions and our @{b }add@{ub } function in the previous section is a good
example. If you look closely at the original definition you'll see that
the local variable @{b }s@{ub } wasn't really needed. Here's the one-line definition
of @{b }add@{ub }:
PROC add(x,y) IS x+y
@ENDNODE
@NODE "Default Arguments" "Default Arguments"
@Next "Multiple Return Values"
@Prev "One-Line Functions"
@Toc "main"
Default Arguments
=================
Sometimes a procedure (or function) will quite often be called with a
particular (constant) value for one of its parameters, and it might be
nice if you didn't have to fill this value in all the time. Luckily, E
allows you to define @{fg shine }default@{fg text } values for a procedure's parameters when
you define the procedure. You can then just leave out that parameter when
you call the procedure and it will default to the value you defined for it.
Here's a simple example:
PROC play(track=1)
WriteF('Starting to play track \\d\\n', track)
/* Rest of the code... */
ENDPROC
PROC main()
play(1) -> Start playing from track 1
play(6) -> Start playing from track 6
play() -> Start playing from track 1
ENDPROC
This is an outline of a program to control something like a CD player.
The @{b }play@{ub } procedure has one parameter, @{b }track@{ub }, which represents the first
track that should be played. Often, though, you just tell the CD player
to play, and don't specify a particular track. In this case, play starts
from the first track. This is exactly what happens in the example above:
the @{b }track@{ub } parameter has a default value of 1 defined for it (the @{b }=1@{ub } in the
definition of the @{b }play@{ub } procedure), and the third call to @{b }play@{ub } in @{b }main@{ub } does
not specify a value for @{b }track@{ub }, so the default value is used.
There are two constraints on the use of default arguments:
1. Any number of the parameters of a procedure may have default values
defined for them, although they may only be the right-most parameters.
This means that for a three parameter procedure, the second parameter
can have a default value only if the last parameter does as well, and
the first can have one only if both the others do. This should not
be a big problem because you can always reorder the parameters in the
procedure definition (and in all the places it has been called!).
The following examples show legal definitions of procedures with
default arguments:
PROC fred(x, y, z) IS x+y+z -> No defaults
PROC fred(x, y, z=1) IS x+y+z -> z defaults to 1
PROC fred(x, y=23, z=1) IS x+y+z -> y and z have defaults
PROC fred(x=9, y=23, z=1) IS x+y+z -> All have defaults
On the other hand, these definitions are all illegal:
PROC fred(x, y=23, z) IS x+y+z -> Illegal: no z default
PROC fred(x=9, y, z=1) IS x+y+z -> Illegal: no y default
2. When you call a procedure which has default arguments you can only
leave out the right-most parameters. This means that for a three
parameter procedure with all three parameters having default values,
you can leave out the second parameter in a call to this procedure
only if you also leave out the third parameter. The first parameter
may be left out only if both the others are, too.
The following example shows which parameters are considered defaults:
PROC fred(x, y=23, z=1)
WriteF('x is \\d, y is \\d, z is \\d\\n', x, y, z)
ENDPROC
PROC main()
fred(2, 3, 4) -> No defaults used
fred(2, 3) -> z defaults to 1
fred(2) -> y and z default
fred() -> Illegal: x has no default
ENDPROC
In this example, you cannot leave out the @{b }y@{ub } parameter in a call to
@{b }fred@{ub } without leaving out the @{b }z@{ub } parameter as well. To make @{b }y@{ub } have its
default value and @{b }z@{ub } some value other than its default you need to
supply the @{b }y@{ub } value explicitly in the call:
fred(2, 23, 9) -> Need to supply 23 for y
These constraints are necessary in order to make procedure calls
unambiguous. Consider a three-parameter procedure with default values for
two of the parameters. If it is called with only two parameters then,
without these constraints, it would not be clear which two parameters had
been supplied and which had not. If, however, the procedure were defined
and called according to these constraints, then it must be the third
parameter that needs to be defaulted (and the two parameters with default
values must be the last two).
@ENDNODE
@NODE "Multiple Return Values" "Multiple Return Values"
@Prev "Default Arguments"
@Toc "main"
Multiple Return Values
======================
So far we've only seen functions which return only one value, since
this is something common to most programming languages. However, E allows
you to return up to three values from a function. To do this you list the
values separated by commas after the @{b }ENDPROC@{ub }, @{b }RETURN@{ub } or @{b }IS@{ub } keyword, where
you would normally have specified only one value. A good example is a
function which manipulates a screen coordinate, which is a pair of values:
the x- and y-coordinates.
PROC movediag(x, y) IS x+8, y+4
All this function does is add 8 to the x-coordinate and 4 to the
y-coordinate. To get to the return values other than the first one you
must use a multiple-assignment statement:
PROC main()
DEF a, b
a, b:=movediag(10, 3)
/* Now a should be 10+8, and b should be 3+4 */
WriteF('a is \\d, b is \\d\\n', a, b)
ENDPROC
@{b }a@{ub } is assigned the first return value and @{b }b@{ub } is assigned the second. You
don't need to use all the return values from a function, so the assignment
in the example above could have assigned only to @{b }a@{ub } (in which case it would
not be a multiple-assignment anymore). A multiple-assignment makes sense
only if the right-hand side is a function call, so don't expect things
like the following example to set @{b }b@{ub } properly:
a,b:=6+movediag(10,3) -> No obvious value for b
If you use a function with more than one return value in any other
expression (i.e., something which is not the right-hand side of an
assignment), then only the first return value is used. For this reason
the return values of a function have special names: the first return value
is called the @{fg shine }regular@{fg text } value of the function, and the other values are
the @{fg shine }optional@{fg text } values.
PROC main()
DEF a, b
/* The next two lines ignore the second return value */
a:=movediag(10, 3)
WriteF('x-coord of movediag(21, 4) is \\d\\n', movediag(21,4))
ENDPROC
@ENDNODE

View File

@ -0,0 +1,423 @@
@database beginner.guide
@Master beginner
@Width 75
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
the input file beginner.
@NODE "main" "Recursion"
@Next "OOE.guide/main"
@Prev "FloatingPoint.guide/main"
@Toc "Contents.guide/main"
Recursion
*********
A @{fg shine }recursive@{fg text } function is very much like a function which uses a loop.
Basically, a @{fg shine }recursive@{fg text } function calls itself (usually after some
manipulation of data) rather than iterating a bit of code using a loop.
There are also recursive types, which are objects with elements which have
the object type (in E these would be pointers to objects). We've already
seen a recursive type: linked lists, where each element in the list
contains a pointer to the next element (see @{"Linked Lists" Link "Types.guide/Linked Lists" }).
Recursive definitions are normally much more understandable than an
equivalent iterative definition, and it's usually easier to use recursive
functions to manipulate this data from a recursive type. However,
recursion is by no means a simple topic. Read on at your own peril!
@{" Factorial Example " Link "Factorial Example" }
@{" Mutual Recursion " Link "Mutual Recursion" }
@{" Binary Trees " Link "Binary Trees" }
@{" Stack (and Crashing) " Link "Stack (and Crashing)" }
@{" Stack and Exceptions " Link "Stack and Exceptions" }
@ENDNODE
@NODE "Factorial Example" "Factorial Example"
@Next "Mutual Recursion"
@Toc "main"
Factorial Example
=================
The normal example for a recursive definition is the factorial
function, so let's not be different. In school mathematics the symbol @{b }!@{ub }
is used after a number to denote the factorial of that number (and only
positive integers have factorials). n! is n-factorial, which is defined
as follows:
n! = n * (n-1) * (n-2) * ... * 1 (for n >= 1)
So, 4! is 4*3*2*1, which is 24. And, 5! is 5*4*3*2*1, which is 120.
Here's the iterative definition of a factorial function (we'll @{b }Raise@{ub } an
exception is the number is not positive, but you can safely leave this
check out if you are sure the function will be called only with positive
numbers):
PROC fact_iter(n)
DEF i, result=1
IF n<=0 THEN Raise("FACT")
FOR i:=1 TO n
result:=result*i
ENDFOR
ENDPROC result
We've used a @{b }FOR@{ub } loop to generate the numbers one to @{b }n@{ub } (the parameter to
the @{b }fact_iter@{ub }), and @{b }result@{ub } holds the intermediate and final results. The
final result is returned, so check that @{b }fact_iter(4)@{ub } returns 24 and
@{b }fact_iter(5)@{ub } returns 120 using a @{b }main@{ub } procedure something like this:
PROC main()
WriteF('4! is \\d\\n5! is\\d\\n', fact_iter(4), fact_iter(5))
ENDPROC
If you're really observant you might have noticed that 5! is 5*4!, and,
in general, n! is n*(n-1)!. This is our first glimpse of a recursive
definition--we can define the factorial function in terms of itself. The
real definition of factorial is (the reason why this is the real
definition is because the `...' in the previous definition is not
sufficiently precise for a mathematical definition):
1! = 1
n! = n * (n-1)! (for n > 1)
Notice that there are now two cases to consider. The first case is called
the @{fg shine }base@{fg text } case and gives an easily calculated value (i.e., no recursion
is used). The second case is the @{fg shine }recursive@{fg text } case and gives a definition
in terms of a number nearer the base case (i.e., (n-1) is nearer 1 than n,
for n>1). The normal problem people get into when using recursion is they
forget the base case. Without the base case the definition is meaningless.
Without a base case in a recursive program the machine is likely to crash!
(See @{"Stack (and Crashing)" Link "Stack (and Crashing)" }.)
We can now define the recursive version of the @{b }fact_iter@{ub } function
(again, we'll use a @{b }Raise@{ub } if the number parameter is not positive):
PROC fact_rec(n)
IF n=1
RETURN 1
ELSEIF n>=2
RETURN n*fact_rec(n-1)
ELSE
Raise("FACT")
ENDIF
ENDPROC
Notice how this looks just like the mathematical definition, and is nice
and compact. We can even make a one-line function definition (if we omit
the check on the parameter being positive):
PROC fact_rec2(n) RETURN IF n=1 THEN 1 ELSE n*fact_rec2(n-1)
You might be tempted to omit the base case and write something like this:
/* Don't do this! */
PROC fact_bad(n) RETURN n*fact_bad(n-1)
The problem is the recursion will never end. The function @{b }fact_bad@{ub } will
be called with every number from @{b }n@{ub } to zero and then all the negative
integers. A value will never be returned, and the machine will crash
after a while. The precise reason why it will crash is given later (see
@{"Stack (and Crashing)" Link "Stack (and Crashing)" }).
@ENDNODE
@NODE "Mutual Recursion" "Mutual Recursion"
@Next "Binary Trees"
@Prev "Factorial Example"
@Toc "main"
Mutual Recursion
================
In the previous section we saw the function @{b }fact_rec@{ub } which called
itself. If you have two functions, @{b }fun1@{ub } and @{b }fun2@{ub }, and @{b }fun1@{ub } calls @{b }fun2@{ub },
and @{b }fun2@{ub } calls @{b }fun1@{ub }, then this pair of functions are @{fg shine }mutually@{fg text } recursive.
This extends to any amount of functions linked in this way.
This is a rather contrived example of a pair of mutually recursive
functions.
PROC f(n)
IF n=1
RETURN 1
ELSEIF n>=2
RETURN n*g(n-1)
ELSE
Raise("F")
ENDIF
ENDPROC
PROC g(n)
IF n=1
RETURN 2*1
ELSEIF n>=2
RETURN 2*n*f(n-1)
ELSE
Raise("G")
ENDIF
ENDPROC
Both functions are very similar to the @{b }fact_rec@{ub } function, but @{b }g@{ub } returns
double the normal values. The overall effect is that every other value in
long version of the multiplication is doubled. So, @{b }f(n)@{ub } computes
n*(2*(n-1))*(n-2)*(2*(n-3))*...*2 which probably isn't all that
interesting.
@ENDNODE
@NODE "Binary Trees" "Binary Trees"
@Next "Stack (and Crashing)"
@Prev "Mutual Recursion"
@Toc "main"
Binary Trees
============
This is an example of a recursive type and the effect it has on
functions which manipulate this type of data. A @{fg shine }binary tree@{fg text } is like a
linked list, but instead of each element containing only one link to
another element there are two links in each element of a binary tree
(which point to smaller trees called @{fg shine }branches@{fg text }). The first link points
to the @{i }left@{ui } branch and the second points to the @{i }right@{ui } branch. Each
element of the tree is called a @{fg shine }node@{fg text } and there are two kinds of special
node: the start point, called the @{fg shine }root@{fg text } of the tree (like the head of a
list), and the nodes which do not have left or right branches (i.e., @{b }NIL@{ub }
pointers for both links), called @{fg shine }leaves@{fg text }. Every node of the tree
contains some kind of data (just as the linked lists contained an E-string
or E-list in each element). The following diagram illustrates a small
tree.
+------+
| Root |
+--*---+
/ \\
Left / \\ Right
/ \\
+------* *------+
| Node | | Node |
+--*---+ +--*---+
/ / \\
Left / Left / \\ Right
/ / \\
+--*---+ +----*-+ +-*----+
| Leaf | | Leaf | | Leaf |
+------+ +------+ +------+
Notice that a node might have only one branch (it doesn't have to have
both the left and the right). Also, the leaves on the example were all at
the same level, but this doesn't have to be the case. Any of the leaves
could easily have been a node which had a lot of nodes branching off it.
So, how can a tree structure like this be written as an E object?
Well, the general outline is this:
OBJECT tree
data
left:PTR TO tree, right:PTR TO tree
ENDOBJECT
The @{b }left@{ub } and @{b }right@{ub } elements are pointers to the left and right branches
(which will be @{b }tree@{ub } objects, too). The @{b }data@{ub } element is some data for each
node. This could equally well be a pointer, an @{b }ARRAY@{ub } or a number of
different data elements.
So, what use can be made of such a tree? Well, a common use is for
holding a sorted collection of data that needs to be able to have elements
added quickly. As an example, the data at each node could be an integer,
so a tree of this kind could hold a sorted set of integers. To make the
tree sorted, constraints must be placed on the left and right branches of
a node. The left branch should contain only nodes with data that is @{i }less@{ui }
than the parent node's data, and, similarly, the right branch should
contain only nodes with data that is @{i }greater@{ui }. Nodes with the same data
could be included in one of the branches, but for our example we'll
disallow them. We are now ready to write some functions to manipulate our
tree.
The first function is one which starts off a new set of integers (i.e.,
begins a new tree). This should take an integer as a parameter and return
a pointer to the root node of new tree (with the integer as that node's
data).
PROC new_set(int)
DEF root:PTR TO tree
NEW root
root.data:=int
ENDPROC root
The memory for the new tree element must be allocated dynamically, so this
is a good example of a use of @{b }NEW@{ub }. Since @{b }NEW@{ub } clears the memory it
allocates all elements of the new object will be zero. In particular, the
@{b }left@{ub } and @{b }right@{ub } pointers will be @{b }NIL@{ub }, so the root node will also be a leaf.
If the @{b }NEW@{ub } fails a @{b }"MEM"@{ub } exception is raised; otherwise the data is set to
the supplied value and a pointer to the root node is returned.
To add a new integer to such a set we need to find the appropriate
position to insert it and set the left and right branches correctly. This
is because if the integer is new to the set it will be added as a new
leaf, and so one of the existing nodes will change its left or right
branch.
PROC add(i, set:PTR TO tree)
IF set=NIL
RETURN new_set(i)
ELSE
IF i<set.data
set.left:=add(i, set.left)
ELSEIF i>set.data
set.right:=add(i, set.right)
ENDIF
RETURN set
ENDIF
ENDPROC
This function returns a pointer to the set to which it added the integer.
If this set was initially empty a new set is created; otherwise the
original pointer is returned. The appropriate branches are corrected as
the search progresses. Only the last assignment to the left or right
branch is significant (all others do not change the value of the pointer),
since it is this assignment that adds the new leaf. Here's an iterative
version of this function:
PROC add_iter(i, set:PTR TO tree)
DEF node:PTR TO tree
IF set=NIL
RETURN new_set(i)
ELSE
node:=set
LOOP
IF i<node.data
IF node.left=NIL
node.left:=new_set(i)
RETURN set
ELSE
node:=node.left
ENDIF
ELSEIF i>node.data
IF node.right=NIL
node.right:=new_set(i)
RETURN set
ELSE
node:=node.right
ENDIF
ELSE
RETURN set
ENDIF
ENDLOOP
ENDIF
ENDPROC
As you can see, it's quite a bit messier. Recursive functions work well
with manipulation of recursive types.
Another really neat example is printing the contents of the set. It's
deceptively simple:
PROC show(set:PTR TO tree)
IF set<>NIL
show(set.left)
WriteF('\\d ', set.data)
show(set.right)
ENDIF
ENDPROC
The integers in the nodes will get printed in order (providing they were
added using the @{b }add@{ub } function). The left-hand nodes contain the smallest
elements so the data they contain is printed first, followed by the data
at the current node, and then that in the right-hand nodes. Try writing
an iterative version of this function if you fancy a really tough problem.
Putting everything together, here's a @{b }main@{ub } procedure which can be used
to test the above functions:
PROC main() HANDLE
DEF s, i, j
Rnd(-999999) /* Initialise seed */
s:=new_set(10) /* Initialise set s to contain the number 10 */
WriteF('Input:\\n')
FOR i:=1 TO 50 /* Generate 50 random numbers and add them to set s */
j:=Rnd(100)
add(j, s)
WriteF('\\d ',j)
ENDFOR
WriteF('\\nOutput:\\n')
show(s) /* Show the contents of the (sorted) set s */
WriteF('\\n')
EXCEPT
IF exception="NEW" THEN WriteF('Ran out of memory\\n')
ENDPROC
@ENDNODE
@NODE "Stack (and Crashing)" "Stack (and Crashing)"
@Next "Stack and Exceptions"
@Prev "Binary Trees"
@Toc "main"
Stack (and Crashing)
====================
When you call a procedure you use up a bit of the program's @{fg shine }stack@{fg text }.
The stack is used to keep track of procedures in a program which haven't
finished, and real problems can arise when the stack space runs out.
Normally, the amount of stack available to each program is sufficient,
since the E compiler handles all the fiddly bits quite well. However,
programs which use a lot of recursion can quite easily run out of stack.
For example, the @{b }fact_rec(10)@{ub } will need enough stack for ten calls of
@{b }fact_rec@{ub }, nine of which are recursively called. This is because each call
does not finish until the return value has been computed, so all recursive
calls up to @{b }fact_rec(1)@{ub } need to be kept on the stack until @{b }fact_rec(1)@{ub }
returns one. Then each procedure will be taken off the stack as they
finish. If you try to compute @{b }fact_rec(40000)@{ub }, not only will this take a
long time, but it will probably run out of stack space. When it does run
out of stack, the machine will probably crash or do other weird things.
The iterative version, @{b }fact_iter@{ub } does not have these problems, since it
only takes one procedure call to calculate a factorial using this function.
If there is the possibility of running out of stack space you can use
the @{b }FreeStack@{ub } (built-in) function call (see @{"System support functions" Link "BuiltIns.guide/System support functions" }).
This returns the amount of free stack space. If it drops below about 1KB
then you might like to stop the recursion or whatever else is using up the
stack. Also, you can specify amount of stack your program gets (and
override what the compiler might decide is appropriate) using the @{b }OPT
STACK@{ub } option. See the `Reference Manual' for more details on E's
stack organisation.
@ENDNODE
@NODE "Stack and Exceptions" "Stack and Exceptions"
@Prev "Stack (and Crashing)"
@Toc "main"
Stack and Exceptions
====================
The concept `recent' used earlier is connected with the stack (see
@{"Raising an Exception" Link "Exceptions.guide/Raising an Exception" }). A recent procedure is one which is on the stack,
the most recent being the current procedure. So, when @{b }Raise@{ub } is called it
looks through the stack until it finds a procedure with an exception
handler. That handler will then be used, and all procedures before the
selected one on the stack are taken off the stack.
Therefore, a recursive function with an exception handler can use @{b }Raise@{ub }
in the handler to call the handler in the previous (recursive) call of the
function. So anything that has been recursively allocated can be
`recursively' deallocated by exception handlers. This is a very powerful
and important feature of exception handlers.
@ENDNODE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
This directory contains 'E.guide', the original documentation written by me,
and 'beginner.guide', an excellent introduction to E by Jason Hulance. Both
are supplied in amigaguide format, if you wish to read them in plain ascii
instead, use Jason's own Ag2Txt utility that I included (in Bin/ and
Src/Utils/Ag2Txt/), for example:
1> bin/ag2txt >docs/e.txt docs/e.guide

Binary file not shown.

2
amigae33a/E_v3.3a/README Normal file
View File

@ -0,0 +1,2 @@
This is the Amiga E v3.3a distribution.
Read docs/e.guide for details.

BIN
amigae33a/E_v3.3a/Src.lha Normal file

Binary file not shown.

BIN
ec33a/ec33a/EC Normal file

Binary file not shown.

19
ec33a/ec33a/ec33a.readme Normal file
View File

@ -0,0 +1,19 @@
Short: The Amiga E v3.3a compiler (ec)
Type: dev/e
Author: wvo96r@ecs.soton.ac.uk
This is the EC compiler, formerly shareware, now freeware. I've decided to
give away the E compiler for free. To read about the reasons why and other
details: http://www.ecs.soton.ac.uk/~wvo96r/e/efuture.txt
This archive contains only the compiler executable, and to do anything with
it, you need the distribution archive, dev/e/amigae33a.lha from any aminet
mirror. The source to the compiler is available as dev/e/ec33a_src.lha
EC is now freeware. You may distribute it in any way, i.e. via the internet
or on CDROMs. The only (obvious) exceptions are that you aren't allowed to
make money out of selling EC, and you're not allowed to change anything about
this archive. Thanks.
Wouter van Oortmerssen, march 1999

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,127 @@
Short: The Amiga E v3.3a compiler source
Type: dev/e
Author: wvo96r@ecs.soton.ac.uk
This archive contains the source code (in assembly) to the E compiler EC.
WARNING: This archive is for highly skilled assembly programmers with an
intimate knowledge of E and compilers, who are curious about EC's internals.
If this doesn't apply to you there's no point in even downloading this
archive. EC's source code redefines the term "hairy" and will make even
the best hackers faint.
This source code release is an addon to the freeware release of the E
compiler, EC v3.3a, which you can get from where you got this or any aminet
mirror as dev/e/ec33a.lha. You also need the distribution which was already
available: dev/e/amigae33a.lha. To read all about why I released this all
for free, read http://www.ecs.soton.ac.uk/~wvo96r/e/efuture.txt
As far as distributing the _unmodified_ archive goes, it is freeware, you can
distribute it in any way you see fit, i.e. via the internet or on CDROMs. The
only (obvious) exceptions are that you aren't allowed to make money out of
selling EC, and you're not allowed to change anything about this archive. Thanks.
I discourage people to distribute modified versions of EC, but if you feel you
really have to, a different set of rules applies:
As long as the modified compiler & source don't leave your system, you
can obviously do anything you please with it.
If you wish to re-distribute a modified version of EC you made, you have to
comply with the following rules:
* besided the new executable, you also distribute the new modified source
that generated it. EC's source code adheres to a similar scheme as "Open
Source" or the "GNU public license" with the only difference that you
don't need to include their licenses.
* You make very clear in your executable name, archive name, program welcome
message and anywhere else that this is YOUR version of EC, not the original,
i.e. someone who downloads your EC shouldn't be confused as to who made this.
Make sure you give the whole project a different name (e.g. BobsEC), and a
version number higher than 3.3a.
* You clearly mention me as the original author, refer to the 3 original
archives mentioned in this text, and in general do your best to educate the
users of your version to understand how this software came to be.
Obviously if they want to modify/redistribute your version, they should
also have read the texts in the original archives.
* EC is unsupported, so don't come back to me to ask wether you can have
your changes incorporated in the next official release, because there
won't be any. Instead, look around on the internet (the E mailing list
is a good place) for other people interested in doing an extended EC, and
cooperate with them.
Now to the hard bit, modifying EC's source code. As mentioned above, think
long and hard wether this is a really a sensible thing to do, and when you
have decided yes, think long and hard again. In the later years of
development, modifying EC required a very concentrated effort to modify and
debug, especially doing it correctly to not introduce new problems. EC's
source code is terribly complicated, very badly programmed, and contains more
long distance dependancies than any bit of code I've ever seen. In short,
unless the modification you intend to make is absolutely trivial, writing
the compiler from scratch would be a lot quicker.
Given that you still want to modify it:
* the code was written using AsmOne, most recently with version v1.25. Don't
ask me to send it to you, because I don't think it's public domain.
* If you insist on converting it to another assembler, be prepared for a lot
of work. Realise that a lot of the code in the source plays a role in the
code generation process, and as such is more critical to change than any
other assembly program would be: for example occurrences of MOVE.L #1,D0
in the source might be there for me to grab its opcode, if your assembler
changes that to MOVEQ #1,D0, the code generator will generate illegal code
and all compiled E programs will crash, and it'll be very hard to find out
why it went wrong. So make sure you disable any flags/options of your
assembler that try to optimise or otherwise change the code.
* register usage is completely non transparant, i.e. if a certain register
isn't used for an entire subroutine, you can't assume it's free to use.
there are all sorts of weird conventions, and many registers span entire
sections of the compiler in their lifetime. Basically if you modification
is in need of registers (and it will be) your safest bet is to JSR
from the point where you want to insert the new code, safe all registers
that you will be using and in general make sure that you restore the
machine state to exactly how you found it. Failing to do this WILL
introduce new hairy bugs.
* the whole compiler (>80k) is written in small model, i.e. uses 16bit
signed branch offsets, so insert new code at the end or start, otherwise
you'll break a lot of branches.
* the source code contains hardly any comments :P
There are tons more pitfals, but these are the most obvious I could think of.
besides the actual source code, the following files have been supplied in
the extras/ dir with the sole purpose of helping people who intend to
modify/enhance EC. The files are supplied AS IS, please don't come and ask
me to explain to you what their contents means.
extras/TODO.TXT:
the most recent todo list I had for future releases. May give you an idea
as to what bugs havent been fixed should you get to the point of improving
EC a lot.
extras/EC.TXT
this is the text I used internally to keep track of data structures and
other data used in EC, as the source itself is rather unselfdocumenting.
This text could help you a bit in trying to understand EC's code, but
most of the data structures in this text contain no documentation, so
you're on your own as to what it all means. Still its better than nothing.
extras/utils/
E source code to utilities supplied in the distribution, i.e. showmodule
and others. One utility called ecdebug can dump some of the internal
data structures of EC while its running (run it as EC HOLD in another shell),
and can be easily extended.
Wouter van Oortmerssen, march 1999

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,772 @@
+---------------------------------------------------------------+
| TODO for next release |
+---------------------------------------------------------------+
- jasons sc.e crashes with textview.e -> needed recompiled sctext.e,
put in distr.
- ignore EXPORT if were not in a MODULE
- problems:
> OBJECT foo
> bar
> ENDOBJECT
>
> PROC main()
> DEF x:foo
> WriteF('\d\n', x.bar.bar)
> ENDPROC
- Facos() / Fsinh() crashes?
+---------------------------------------------------------------+
| PUBLIC TODO LIST |
+---------------------------------------------------------------+
Bugs I haven't been able to reproduce:
* compilation problems on 060
* broken Lock in combination with MCX
Bugs still not fixed:
* dereferencing an untyped OBJECT member is not properly interpreted
as "PTR TO CHAR". Easy workaround: Give the member a type.
* debug hunks don't contain directory information for projects
split up over sub directories.
* error reports can sometimes be off by several lines.
* error reports for inline assembly often doesn't work.
* unreferenced check too conservative in some cases.
* doesn't detect two library functions with the same names.
* constant folding optimizer often misbehaves, disallowing
you to do things like `1+SIZEOF o' in constant expressions
or 1+a as list-elements.
* internal linker problem doesn't allow for private methods.
* strings in lists cannot be joined by "+" (optimizer problem).
* StrCopy/MidStr don't fill dest with empty-string if len=0
* methods and procs in one module cannot have the same name
(other than by inheritance).
* Exists() evaluates all regardless.
* SUPER doesn't check wether the superclass actually has such
a method.
* recompiling a module that is being used at the same time
by another invocation of EC can give problems.
Future Todo's
* fix above bugs :)
* extend CONST expressions with floats etc.
* find a way to split up executables in more hunks
* add symbols for methods
* lots of extensions to the optimizer
* proper 020+ support
* improve error reporting
* class variables
* short-circuit AND / OR
Wild Ideas (aka Big Todo's)
* plug a general back-end onto EC (to allow for code
generation to other cpus (e.g. PPC) or even languages
(e.g. C) or better optimizers etc.)
* extend the language with lambda's / higher order
functions / lazyness etc.
* concurrency system based on Linda
* better debugging / analysis / type inference tools
* full visual language on top of E
+---------------------------------------------------------------+
| TODO for maybe some future release ? |
+---------------------------------------------------------------+
[unconfirmed bugs / could not reproduce / does it make sense?]
- doesn't compile large sources on 060
- lock still doesn't work in some circumstances (v2.1b does work ok, does work
with snoopdos, doesn't with mcx: register trashing?) [tried with mcx no prob]
- bug: object x; x; x; ENDOBJECT no all detected
-> tested all combinations with modules and inheritance, no prob
- LIBB_DELEXP is meant to be used on lib_flags, not sb_flags, though
sample.library does it this way and it seems to function ok?
- the GTMENUITEM_USERDATA(x) macro doesn't work [jolyon]? not true.
- flushcache flushes all
- unsigned comparison????: no practical example sofar
- [2,4,826,0,$1C8E],[2,4,27,0,$2A7E],[9,4,207,0,$802E] on a 030 no hits: which EC?
- DEF a[; missing "]" found only in next proc!
- myfunc(a,b,[blah1,BLAH,[foo,[bar,NIL,a]) <error here>
- in v3.1a: EC INTERNAL ERROR [2,0,-1,1,$0] (please report!) (Started this while an other EC was compiling...)
- not in exec.m: private functions. (no prob?)
- supposedly tools/pt.m would not produce a sound on 030, but does on 020 same
config [tried on my 030 which works]
[bug-fixes]
- a membor 'bla' and a method 'bla()' on the same OBJECT collide.
- bug: doing obj.f[1] where f is untyped interpreted f as "PTR TO
SIZEOF obj" instead of "PTR TO CHAR", because type for next deref
isn't properly set. Fixing it however causess
imsg.iaddress::gadget.gadgetid to break
- sourcecode name in debug hunks misses path if compiled in a
different directory, e.g. '*tools/bla'
- large mui window def, comma fails in the middle, error at first line.
- MULU.W A0,D0 (error) gives ERRBYTE of 0
- library bases are shown as unreferenced if only calls (opened in other module)
- bug: proc (or libsfuns) with same names from libs aren't detected!
- bug: RealVal('\0...') = 0
- since CONST idents aren't resolved until the end of the line in lex,
RAISE IF gives 'immediate value expected' instead of 'unknown keyword/const'.
- INCBIN doesn't work in ASM mode !
- OBJECT testobject;ENDPROC;PROC main();ENDPROC -> gives label expected at "main"
- StrCopy returns NIL on empty string. check retvals other stringfuns as well.
- ERRBYTE = 0 als MODULE 'x' \n 'y' (no comma)
- module by o2m with empty section causes [3,0,-1,1,$0]
- EOR.W #xx,Dx not automatically translated to EORI
- (in main:) if in an object in a module there is a "PTR TO x", and the
module defining x is not included, attempting to dereference to a
member in x gives "illegal use of/reference to object", whereas
"x undeclared" would be more helpful. (while loading EC converts
"PTR TO x" to "LONG" if "x" can't be found).
-> have a typed object member in a module remember type even if obj is not available.
- a local in a proc in a mod is not shown as unreferenced
- unknown function only shown after (...) ?
- obj::class.method() ignores ::class part!
- 1*SIZEOF LONG werkt niet want ASM_GRABVALUE kent geen SIZEOF LONG
- macro prep kan #define DEBUG -> etc niet, want comment stopt bij 0?
- 2x zelfde label naam -> geen error! (x: na x: of x())
- een module met alleen labels en inline asm geeft scope errors
- private classes in een module worden NIET geexporteerd,
dus krijgen ook geen geinit delegate obeject in main!!
temp solution: compiler forces export
- I can declare instance var "x" even if already in super
(and access both via different pointers!)
- strings in list cannot have "+"
- MOVEQ doesn't accept -128
- StrCopy/MidStr don't fill dest with empty-string if len=0
- tools/vector.m saved geen regs
- can't have methods with the same name without inheritance.
methods and procs in one module cannot have same name.
- flushing: what if .m's get flushed currently used by EC?
- SUPER doesn't check if super has a method there
-> could pass a maxoffset to expcallmethod
- Exists() evaluates all regardless
[todo]
- "-D" like switch for defines
- floats in CONSTs etc?
- see etext/truelambda.txt!
- one hunk
- lowercase sourcename
- () in constexp (and maybe other things)
- %bin for binary nums
- a read_e_module(): jason/gregor/vincenzo and others
- "LINE" debug of modules -> "L" thus unusable with enforcer!
- some space in library node for magic values?
GoldED (V3) syntax-parser 'magicid' ("SCAN") at offset $24 in libbase(a6) ($22=exec base size, +word pad)
-> could easily preserve $22 to $32 or so for own data... how to give A6 to
main() so that magic can be poked there?
- symbols for methods?
- \num for strings?
- "::" on anything? -> on typedecls as short for ":PTR TO "
- for libs: stack alloc [] or somesuch. warning for [] in opt library
maybe a NEW equivalent for existing mem (and stackmem?)
- linenum by var used as function warning?
- put some forbids in modulecache access.
- faq: more interesting questions (how to start on own compilers etc.)
[code-optis]
- OPTI: lea 4..8(A7),A7 --> ADDQ
- OPTI: CMP.L #1,D0 --> MOVEQ #1,D1; CMP.L D1,D0
- OPTI: recognise a:=a+<exp>
- OPTI: ELSEIF doesn't optimize EXTs away
- OPYI: LISTs should make use of EXPEA
- OPTI: get rid of LINK iff no args + only REG locals
- OPTI: optimize NEW [1,2,3] code with EXPEA
[020/030/040/881]
- 680x0 code gen.
- jsr --> bsr.l (no relochunks needed!)
- array/record access etc. with ([base,a5],d2*4,1) etc. (FAAST!!)
- EXTL etc. + see how other 020 modi can be applied
- 020/881 asm
[unstablenesses]
- [..] allows for private CHAR fields to be cleared,
NEW [..] allows all private fields.
prob = CHAR is equal to align byte
- arrange for double declarations in idents from module etc.
- check more number of args etc. where main->b->c, c is
changed and recompiled, but b is just linked.
- OPTIBUG: OPTIIF makes too many assumptions about code
- we might need a token-look-back-stack or similar for lex
[module-system]
- o2m: maybe do something with C in .library-link mode.
- o2m: "Raise" en "dosbase" als XREF
- checksums on modules
[errors]
- selected multiple errormessages for most occuring errors
(lex, for starters) with per-case specific recovery code (+ switch)
- catch errors in writemod: otherwise readlock
Locks on modules when error from moduleload
- calc Levenstein distance for idents? unknown=Writef, suggest: WriteF
- problems with error-reconstruction:
- assembly not properly supported
- stat;stat wrong spot error
- problems if "->ENDPROC" last line
- bla,bla <no_comma> ->comment <lf> <err_spot> bla,bla
- "....:REG",10,"<here>whatever"
- meer foutmeldingen, syntax error opsplitsen
- foutmeldingen in andere talen (frans/duits/italiaans)
(locale.library??)
[EC]
- resident check?
- SHOWBUF option also lines/minute etc.
[compiler-organisation-OPTI]
- raise-tab linked maken
- enlarge hash tables from 211 to near 1024
- change all sorts IF-THEN situations into jump-tables
- OPTI: hash bij first search members tijdens lex?
- OPTI: E-system functions ge-hashed
- OPTI: intui/exec/dos/gfx functions ge-hashed
[code-generation]
- chip/data hunk stuff? INCBIN x TO CHIP/DATA -> how to put in var?
more code hunks?
- Jan Schulz zn SELECT idee
- better implementation of [1-x] etc.
- lists have to be able to get longer then a few hundred entries!
prob: runtime stack (just a little).
better code gen altogether?
- LANGUAGE TEST SUITE for code-gen testing
- write disassembler for E exe's to some interim language.
[inline-asm]
- asm instr + LONG use of reloc possibilties
- MOVE.L .member:obj(A0),D0
[run-time-system]
- if >37 then conout AUTO/WAIT/CLOSE
[oo-language-features]
- method hiding
- x <=> self.x ??
- constructor without need for pointer
allow NEW [..]:x.constr(), since '..' inits only public fields
- SHARED (class variables) as "Side Effect Functions"
- private methods
- PROBLEM: auto-destructors <=> exceptions
for now: ONLY DYNAMIC objects from classes.
sol: linked list on the stack, upon raise etc. call destructors
below return stackpoint. data=(next,objptr),depth=stackptr,
destructor at fixed offset.
[END (do it yourself),normal ENDPROC,Raise(),CleanUp()]
[language features]
- E component project doen? (weinig werk voor mij)
- tagged args doenbaar? (helpt niet erg voor easygui) tagged objects+defaults dan mischien?
- FastString() etc., -> table in docs
- more constexps (enum/select_of)
- richer constexps (and/shl/mod)
- let "::" work on anything, so you can do gfxbase::gfxbase.defaultfont
:: on any exp (only lhs) func()::y.x
- macros should allow for macro()
-> module format has nargs=0 as macro, not macro()
- case insensitive OstrCmp() (how localisation?)
-> better: convert all stuff to one case anyway, or use utility.library
- EXIT in LOOP and REPEAT
- +:= for self.x:=self.x+1 cases
- ASSERT / DEBUG..ENDDEBUG
- _vector_ calc on lists?: MAP x,y,z IN x:=fun(y)+z+a*10
- Linda tuples + persistance (combined with Linda / as a class to
inherit from / special language feature ??)
- RENAME statement? could work with almost all types of idents.
- LOOP as short form
- IF a AND THEN b ..., IF a OR ELSE b ...
BUT AND/OR <=> major probs OPTIIF
x BUT OR y = IF x THEN x ELSE y = r:=x; IF r=0 THEN r:=y
x BUT AND y = IF x THEN y ELSE x = r:=x; IF r THEN r:=y
- UNIFICATION:
- strings: _pattern_matching_ REGEXPs, and string binding
- ranges and comparisons: a <=> [1 TO 10,<5] (watch <5 with CONS)
- UNIFY more than one exp with list of patterns (a la SELECT)
- ENDSELECT <var> (beter als ListItem hack)
- NOT unary operator (mischien ook "-")
- more useful quoted expression functions
- [<constexp>]:ARRAY
- const exps pre-calc uitbreiden met ">" "AND" etc.
[docs]
- extend the FAQ! be even clearer about pointers? -> hints & pitfalls for beginners
- an INDEX!
- [chapter on optimizing, more on (data)types
- design_of_e, good_programming_style, debugging etc.
[dist]
- UTIL: fd2module
- Lex equivalent for E
- large set of utils in E, a la diff/grep
- p2m en Iconvert update with options:
- examples: keep_prefixes egs_window etc.
[modules]
- serial.device!!
- also very simple things, like classes for intuition etc. -> as many HQ mods as possible!
- SEE: container classes
- task object ofzo
functions for tasks. (see E source) +other amiga.lib
geta4() routine etc.
- common Lex() routine.
lex+pars
- play samples, allocating channels, playsample() audiodevice support
- garbage collecting string-lib
- String matching (regexps) Parse() en Match() routines
- bignums.m: lijst [] of LONGs makes up an integer of any size
use ADDX. how opti for short ints?
- bobs, sprites
- fast mem-copy + string shifting [b], fastinsert/delete
- all sorts of Rexx string parsing stuff
- OohNo! module
- 24bit module
- harwarebang module :-)
- structure:
emodules:tools/ -> general tools supplied with E
emodules:class/ -> same, but now as class
emodules:other/ -> tools not standard with E, 3rd party stuff
^^^nobody listens to that...
+---------------------------------------------------------------+
| DONE !!!! |
+---------------------------------------------------------------+
[see other texts for pre-v3.0a stuff]
[sofar EC627 (=3.0a)]
+ odd-size created with NEWM macro -> internal errors on 68000
+ ODEL field not properly set in all cases of NEWMETHODS
caused delegates not to be constructed -> method probs!
++ keep track of PTR TO object idents from modules, and find
objects after lex. (not found = no action)
+ modules: 4 bytes of code [mathexit generated RTS]
+ defargs of method not installed correctly after moduleload
+ double-decl test on method tests for bit that wasn't set yet
thus: not possible to redefine method for subclass!
[EC631;v3.0b, released as such (patch)]
[EC632;v3.0c]
+++ debugger! DEBUG/S option also switches on LINEDEBUG/S
+ adds NOPs and EVAR hunks.
++ SUPER method calls possible! notes: [calls from there
not super, super-calc is static]
[EC639;c3.0c]
[EC640;v3.0d]
+ enforcer hit in linker fixed (strcmp)
++ BIGBUG: BSR GLOBJUMP
in initcode didn't work with modules-total >32k!!!!!!!!!
+ code-buf estimate bigger ivm modules in small main
now starts at 100k ivp 50k, and ReadArgs allows ADDBUFs
1..1000 (each *100k add, i.e. max 100meg exes)
+ method_arg_stacklea bug: code for SUPER trashed reg
+ bug: String() would trash D3
+ bug: error report within OBJECT always on 1st line
+ modules with only constdefs etc. would still save debughunk
+ compiler would accept things like PROC pippo(a,b,)
+ method def of object in other module!!! now illegal.
+ BUG: privateness would continue in next object decl
+ AND.L Dx,regvar (and similar constructions, such as with ADD)
assembles as AND.L Dn,ea instead of AND.L ea,Dn -> ILLEGAL!!!
+ CMP.B A3,D0 (and other An <=> .B) -> ILLEGAL!
+ temp export force on accessed classes.
[EC655;v3.0d]
[v3.0e released as patch]
[v3.0f]
++++ newer EDBG!
+++ Macro preprocessor and conditional compilation!
+++ enhanced EasyGUI
++ a FAQ!!!!
+ doc
* .guide niet duidelijk genoeg
* error doc: too size exp also strings
* Organise, optimise... It's organize and optimize!
* ERROR: illegal use of/reference to object LINE 66: NEW raum[MAX_RAEUME]
* prob: bij module main->a->b, add method in b without recomp a: crash.
* In the E.doc it still says "use <another_function>".
* syntax errors in <|> syntax!!
* no mention of *2 *4 /2 /4 getting optimized
* in 4K: automatically at the end of the program, or by hand with ___Dispose(p)___
* Fexp(y) gives e^y, not y^e, Fpow(y,z) gives z^y, instead of y^z
* explain SUPER
* docs: better con-destructor explanation
* remarks in letter Martin F. Combs
* how to report bugs
* no language suggestions please
* reg. sites
* refs naar ML,EPD etc. -> in FAQ
* anti-serge hamm. message
- dist: fonts, nieuwe aprof
- fix errors in stack
- BUG: method with equivalent name inobject wouldn't work (left,name...)
- BUG: nested method calls disturb self at call site
- closing brackets would continue until decl of next proc with stange error,
now "missing )" at endproc
- demoversion limit to 8k
- finished macroprep + save macros. tested with BGUI/MUI
- lets fix it on "FastDisposeList()" + LONG align opti for NEW [1,2,3]
- unification bug in LISP-lists <>
- RealF(s,-3.14159,0) produces '-', RealVal() returns bagger as readlen?? (68000?)
RealF(s,12345.67,4) produces '1234.6699'??
more than one complaint about RealF on 68000
FIXED: all non-regsave 68881 vs. emulation
- flushcache flushes everything
- E-build executes actions in reverse order! :-) -> jasons version in dist
- docs: linedebug buiten procs
- docs: EXPORT gebruik met methods? currently not poss., EXPORT voor nonsense
- smod uses proper commandline arg (for completion)
- dist: nieuwe modules, sources etc.
- devices/serial.m termarray member were 0,1
- In inputevent fehlen ie_x, ie_y und ie_addr. Warum? -> in C it's a union
- BUG: SUPER doesn't work as exp
- list consts will accept no negative floating point
- SMOD doet foute arg-idents met nummers + defargs
- PROC ...IS obj.unknown; PROC <HERE> ...
Without following pro: internal [3,3,-1,0,$750E]
.unknown had a continue_on_next_line because of .method()
- docs: explain all new features
- docs: nieuwe errors
- finish easygui -> in DIST!
- finish EDBG
* weird bugs: doesn't find vars in allx.e
-> in DIST!
[EC666, v3.1a, released as such]
[EC667, v3.1b]
- BUG: .L in some instructions needs to be rejected, f.e. MULU.L #3,D1 = bagger
(done for mulu/muls/divu/divs)
- BUG: labels not in symhunk
- BUG: ASR.B #1,(A0) word toegestaan (alleen ASR.W geldig hier)
- BUG: Bounds(-1,0,255) -> 255
- BUG: macros of other module are saved AGAIN in current module
- BUG: 1/0 causes internal error
- stringf/textf made use of task-stack, now e-allocated-stack (like writef does)
- various emodules with problems, but fixed in v40 (datatypes/datatypesclass.m)
- v40 std modules in EC
- BUG: voegde linedebug toe voor elk statement, maar alloc alleen voor elke
sourceregel (nu alleen linedebug voor stats zonder ";" ervoor)
- forth.e closes console twice (still!)
- HUGEBUG: exp*globvar in module not compiled ok: not offset-2
now also operand_size in modules >= v10 (makes incompat!)
- bug: .B etc. at end of stat didn't work
- globals in modules used in inline asm were not exported/offsets adjusted
- incbin does any size no prob!?
- a-- where a:PTR TO o where SIZEOF o>8 translated into ADDI ipv SUBI
- ignorecache werkt niet: trashes own cookie. by load module.
- macros don't allow for ";"
-> disabled altogether ivm problems in eol-processing otherwise
- show expanded text within macros -> only for lex-phase
[EC676, v3.1b, to_jason]
[EC677, v3.1c]
- fixed bug in easygui: used gl instead of glist
- another bug in easygui: listv put userdata in all parts
++ NILCHECK/S, build in NIL checker for deref! raises "NIL" for "." and "[]"
- bigbug: (found in niklas' sources): when adding methods to an object that
inherits from a method-less object, delegates would start at 0 (not 4),
thereby trashing the object_dynamic_length and causing END trouble.
- self.s <=> <x|y> gives syntax error
- LEA -99(A2,D0.W),A2 becomes LEA -$0063(A2,A7.L),A2 ??? -xx(Ax,Dx.w)
- NEW en END moeten ook op subobjs werken.
- edbg: show current line one too high in t/test2.e -> file
[EC685, v3.1c, to_jason?,pnolan]
[v3.1d]
- EasyGUI: fixed newlook menus
- EasyGUI: activates first stringgad
- EDBG: doesn't work with SYM hunks
- EDBG: cursor keys: down = step over, right = step in
- EDBG: output to debugger window?
- EDBG: source menu option improved
- EDBG: menus in strange colour
- EDBG: scroller prob
- EDBG: doesn't jump to correct spot if outta sight.
- EDBG: doesn't display tabs: tempfix: convert to space
- EDBG: better graphics for toolbar & about
- EDBG: breakpoints on sourcelines
- EDBG: breakpoints on memory/vars
- EDBG: run upto breakpoint
- EDBG: E expression parser for all value/addr inputs
- EDBG: Raise()
- EDBG: shows vars own proc + globals only (scoped!)
- EDBG: watched variables window
[v3.1d, to various persons]
[v3.1e,EC688]
- .library output
- integrated jasons newest modules, etc.
- new easygui integrated
- new EE
- easygui leaves 160 bytes if exception from callback: missed a ReplyMsg()
- forbid when changing task-a4 table!
- #else mentioned in some error messy
- sc.m etc.
- BUG: /2 added 1 also for >0
- unknown ident of functionname still at the end.
- doc:
+ clear about where globals need to be defined
+ too many use flushcache
+ SELECT_OF takes n-1 as max, not n (DEFAULT)
+ labels ook exportable->doc
+ (wrong!): ' ARRAYs are declared by specifying their lenght (in bytes) '
+ explain that error message can point as much as 2 tokens beyond real cause of error.
+ FastDipose#? accepts NIL
+ macros don't do ;
+ libdoc: eg, and what happens to stdout etc.
+ FAQ uitbreiden
[v3.2a, EC707, released as such]
[v3.2b,EC708]
- hung or gave wrong error on procident.member [jaco]
- prepbug: rkrmsrc/intuition/windows/lines.e. internal [2,3,232,0,$698] on 68000
- It has problems using control characters or extended character set characters in #defines
- extra safeties for preprocessor (i.e. tries to expand interim buf after each
macro expansion).
- 'W\0Save Statistics...' exists in the compiled binary as 'W' (under 3.1)
[v3.2b,EC712,released to almathera]
- eg: fixed an enforcer hit
v3.2a all.e with EC DEBUG byte reads (-1,0) on amosaic & font, from rom(-1) too. 569A(0) (= StrLen)
EDBG v3.2a byte-reads from addr. 0 hunk offset E6FE (often when an easygui opens/closes?) (= StrLen)
- eg: cleangui() -> $DEADBEEF mem already freed: used window after closing!
- eg: added settext() and setnum()
- eg: fixed NUM gadget
- eg: advice not to give to high values for proportional values because of high fonts (fheight*val)
- eg: topaz function
- eg: easygui renderen soms in bottom border: check wborbot! check with sysihack
- eg: PLUGINs!!!
- eg: there might still be some string-fields causing hits at -1 in an OS call?
- edbg: about & toolbar do not respect bigger borders
- edbg: console (+tool+about?) don't work on PUBSCREEN
- edbg: sourcenaam in title
- edbg: check doubles in var view
- edbg: save & loads prefs + window sizes + watched vars
- edbg: arexx-port & script launching
- bug: problem with Lock() in EC: 2 instead of -2 as mode
- bug: $FFFF as default arg becomes $FFFF0000
- bug: macro errors printed incorrectly: fixed with a kludge
- bug: /* */ in macros didn't work (e:t/macc.e)
- bug: OBJECT x OF x not detected and causes major guru in exe
- bug: doesn't accept a:REG in 0(A3,a) or 0(A3,a.L) or lab(PC,a) etc.
- bug: AND.L D0, tst where tst:REG screws up (changes dest-src, also for OR/EOR etc.)
[v3.2e, EC716, released as small update]
[EC719]
- faq: these features will not be implemented (so don't ask for them):
* assembly source output by the compiler
* .o
* inits for global vars in modules
* unaligned objectmembers
* bitfields
* not opening intuition.library etc, in startup-code
* automatically include _all_ modules used by a module
* `member' ipv `self.member'
* DEFs halfway PROCs
* disassembly mixed mode for EDBG
* sourcecode include
* unsigned INT/LONG or signed CHAR
* ";" in macros
* multiple error messages
* x.y in assignment expression, {}
* inline code for Shl() etc.
- doc changes:
* mark all obsolete features in the docs as such (CleanUp() etc.)
* :: works only on object types, <>cast
* New and NEW behaviour in libraries
* you can't have zero argument macros
* update registration sites (sweden + germany)
* Link() doesn't work on NEW [...] etc.
* if you want to use vars from other modules, you have to EXPORT DEF them too
* make EXTREMELY clear the error-indication is always after the error!
* be more specific about module-loading behaviour (type set to LONG if
object is not defined _before_ this object).
* warning for POLAND
* E_bonk.e: make clearer that p.i++ works on p, not i
* chequepoint stuff
* tell crackers about double protection
* .end() is not called automatically
* In de docs staat het eerste deel van 12A aan het einde van 11C.
* backslashes dubbel doen in guide
* ^ is NOT equivalent to []
* OO alleen met NEW duidelijK?
* make clear librarybases are provided by the modules
* no lectures on asm, opti in compiler is hard
* modules circularity?
* OO objs with NEW only
* edbg: raise exceptions for current handler only 1 after DEF
- tell regs for libraries! myfun(A0), but slower than movem.
- in methods with a handler and register allocation on, "self" could
be allocated to a register where it shouldn't, and cause trashed
variables elsewhere
- "EXPORT PROC array( w, h, m=$10000) OF array" with OBJECT array
-> dirty backwards parsing, now made more specific
- compiles no errors, crashes on run: PROC main();DEF x;WriteF('\d\n',x.4);ENDPROC
-> two bugs: floatval would sometimes not lex as float (reg-prob), and
not check for preceding operator in expression.
[EC722]
[EC723]
- p2m maakt bagger van tweede char als libname bv `E_..' is.
- pp: #define X Unknown; X results in LINE 22: X ;WITH: Unknown;MACRO: nknown
- twice the same label: doesn't give error
- FOR x:=0 TO 3 DO FOR y:=0 TO 3; ...; ENDFOR
- in the compile part, methods could be called before the corresponding
OBJECT was processed (by calling it from an instance variable in a method
of an object defined higherup), which caused an intern102, now it is
an error71.
- double declaration objecthead geen probleem
- double declaration objectmember geen probleem
also solves nokielski's bug (cd2:src/betas/todebug/pack.lha)
- doc: both /* */ and -> are included in macros, -> gives problems
[EC723]
[EC724]
- bigbug: cache problem clear: what if compiled module (that needs to
be flushed) was accessed from a less deep dir...
paths in cache need to be ABSOLUTE
NameFromFH/NameFromLock/MatchFirst: all 2.04, all need file access
-> solved: flushcache() compares without path.
- IEEEbug
* patch is for 040/fpu only?
* internal errors on 060?!??
* make workaround for 3.1 floatproblem!!! [2,11,36,0,$0]
* 030/no881/3.1/mmu
PROC main()
DEF x
x:=2.1
ENDPROC
* compiling with Blizzard 060 -> "internal Errors": Apollo 1240:
[2,11,38,0,$0] DEF x=0.0, exp=0.0, pos, pos2,
* doc: FAQ: probs with floats in 3.1:
PatchMathSB10.lha util/boot 4K 7+Patch MathIEEESingBas/SPDiv,SPMul
-> EC warns + messys in docs.
- in Iconvert for PhxAss:
1) in makeasmfile(), just before the WriteF(' INCLUDE...), I added this line:
WriteF(' TTL ""\n')
(this line ensure that the chunk won't get any name, like A68K does).
2) The new command line for invokea68k() is:
IF Execute('SmallPhxAss I INCLUDES: FROM ram:iconvert.s noexe opt 0',NIL,stdout)=FALSE THEN error(ER_INVOKE)
-> added option "-p" for phxass
[v3.2g, EC724, to jason]
[v3.2h, EC725]
- device with library feature: not possible without different startup-code, or
atleast various registers made available to main() and close() -> doc
- doc:
* E source should contain atleast one function
* e reg is personal
* #define CharIsX(x) (x="x") substitutes the "x" too.
handy, i.e.: #define debug(x) (WriteF('now evaluating: x\n') BUT x)
* main() in lib works under Forbid() of OpenLibrary()
* homepage!
* FAQ: more on resources (available translations)
- eg: extra arg to plugin methods for font
- sw: PGA_TOTAL wasn't updated (refresh_scrollbars() (to allow max to change))
-> needed in EDBG for varwin
- edbg: var view scroller altijd 100%
- edbg: a search (for large sources)
- edbg: zwarte balk in onderste regel, dan window met een regel kleiner maken: rendering in border
- doc
* shr is ASR
* writef/printf string limit.
* address
- edbg: fixed enforcer hit in deletetoolbar
- fixed obj/member double decl test enfhit
- new tools, e.g. Iconvert, p2m, doc2guide? etc.
- explorer?
- include jasons rkrm2
[v3.2i, EC725, for Amy Resource]
- [easygui: over to jason]
[unreproduced bugs]
* eg: PALETTE: If the relx Argument is smaller than 3 it has got problems (Recoverble alert, hang-up)
* eg: double menu-selections do not trigger actions multiple times
[features]
* eg: maybe supply a host of handy plugins as standard (for images / image gadgets / boopsi gadgets etc.)
* eg: support GTLV_SHOWSELECTED (what were the problems?): really wanted
* eg: allow cut of window: smaller than minimum and solves small screens too?! panes? pages?
* eg: Other IDCMPs and messages (i.e. rawkey!)
* eg: the MX gadget does not use the "abovetext" field
* eg: APPWINDOW?
* eg: maybe builtin topaz fallback??
* eg: block input on other easygui's
prob: - intuition/blockinput.e doesn't stop resize and has ugly waitptr
- IDCMP_SIZEVERIFY not appropriate for long periods
better: if >1 window, process them all!
* eg: size num voor slider berekenen.
* eg: text in screenmode can't compute max, same for sqrt in raymond
* eg: bug: midsize align is mixed when one gadget has text on the left and other
on the right
- [edbg->to->jason]
* allow input hunkoffset->source in EDBG (cooperation with enforcer)
* allow obj editing with explorer
* double clicking on scroller gives breakpoint
* (see uncommented stuff (=new) in begl2) es wird nur kein SELECT <zahl> OF <variable> weiter verfolgt
* with many modules, source window tiling gets too small
* allow to break program while running? (non trivial)
* no builtin global variables
* show changed registers (and vars?)
* really describe all features in doc
[edbg, less urgent]
* skip statement/jump?
* object browser!
* repeat step in/over "exp" times (so in a for-loop of 3 lines, step over a-1*3)
* record step as rexx script
* do something about debug-interactivity or rexx-scripts
* more gadgets in toolbar
* enable more windows to hang around instead of requester-like
* add all builtin vars. what about other librarybases? -> redefine
* make memory and other stuff modifiable
* optimize refresh of some windows
[EC726, v3.2k]
- bug: doing obj.f[1] where f is untyped interpreted f as "PTR TO
SIZEOF obj" instead of "PTR TO CHAR", because type for next deref
wasn't properly set.
- bug: module-flush flushed only first module that matches. now guaranteed if
you compile x.m from anywhere, all modules called x.m (any path) will be flushed.
(also when you create a .m with LIBRARY)
- enum now works with negative numbers (ENUM AA=-1,BB,CC)
- 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]
- OPT RTD flawed in the context of hooks, call backs etc -> disabled
- include other float ops ArcSin, ArcCos and ArcTan etc.
- "self" now has own (code 5) entry in debughunk for EDBG -> jason?
- bug fix of obj.f[1] undone :( now part of known bugs
- dist: include ieee patch? -> setpatch v43.6
- dist: texturemapper (v7)
- dist: nieuwe sw
- docs:
* validness of other registration sites
* fabio's new ML
* better email warning
* add to version history
* new float functions
* new features etc.
* make a known bugs list and a todo list (without any promises!)
- dist:
* fabio's objects
* gregors OOstuff, new build
* edbg: do more tests: doesn't find vars correctly. test self
* jason's EasyGUI, EDBG etc.
[-> out as beta to jason/gregor/fabio]
- incorporated some final tiny changes
- ebug.e: battclockbase not real base so needs EXPORT DEF
- EDBG sources
- demo ec + patches for 30 31 32
[EC733, v3.3a final release version]

View File

@ -0,0 +1,43 @@
-RS
-L7
+NL
+AA
+RL
+XR
-PD
-LF
+PG
+HP
-AE
+DB
-IL
-1B
+.S
-CW
-LN
+AI
+L:
+DA
+SS
-OA
-UL
+;C
-PI
+PS
+RR
+AU
+SM
-FW
+FP
+OD
+DC
+UD
+EP
+LD
+KX
!.s
*ec:
|797|000|EEE|575|
[CPU2
[MMU0
\\700\r\

View File

@ -0,0 +1,280 @@
/* Iconvert.e
This little utility converts a '.i' assembly include file
into an E binary module.
it understands '=' and 'EQU' for constant definitions, and builds
OBJECTs out of STRUCTURE definitions (from "exec/types.i").
it needs a macroassembler like A68k to run.
USAGE: iconvert <.ifile> */
OBJECT def
type:INT,name
ENDOBJECT
ENUM T_ARRAY,T_CHAR,T_INT,T_LONG=4,T_CONST=$10,T_STRUCT,T_END
ENUM ER_NONE,ER_IN,ER_OUT,ER_MEM,ER_WORK,ER_BREAK,ER_FORM,ER_TEMPW,
ER_INVOKE,ER_TEMPR,ER_OFORM,ER_I
CONST MAX_DEF=2500,IDMAX=10
DEF defs[MAX_DEF]:ARRAY OF def,
infile[100]:STRING,outfile[100]:STRING,
handle=NIL,flen,buf,bbuf,defn=0,line=0,mode=0,a,phx=FALSE
PROC main()
WriteF('Iconvert v0.2 1992 $#%!\n\n')
readinfile()
collectidents()
makeasmfile()
invokea68k()
readbinary()
makemodule()
error(ER_NONE)
ENDPROC
PROC readinfile()
DEF len
IF (arg[0]="-") AND (arg[1]="p") AND (arg[2]=" ")
arg:=arg+3
phx:=TRUE
ENDIF
StrCopy(infile,arg,ALL)
len:=EstrLen(infile)-2
IF infile[len]<>"." THEN error(ER_I)
StrCopy(outfile,arg,ALL); outfile[len+1]:="m"
IF (flen:=FileLength(infile))<1 THEN error(ER_IN)
IF (buf:=New(flen+1))=NIL THEN error(ER_MEM)
buf[flen]:=10
IF (handle:=Open(infile,OLDFILE))=NIL THEN error(ER_IN)
IF Read(handle,buf,flen)<>flen THEN error(ER_IN)
Close(handle); handle:=NIL
ENDPROC
PROC collectidents()
DEF p,a,id[IDMAX]:ARRAY OF LONG,num,f,c,end,zero=NIL,macro=FALSE
DEF struct=FALSE,d:PTR TO def,size,work[10]:STRING
WriteF('Now trying to convert "\s".\n',infile)
INC line; p:=buf; end:=p+flen; d:=defs
WHILE p<end
FOR a:=0 TO IDMAX-1 DO id[a]:=0
num:=0; f:=TRUE
WHILE f
c:=p[]++
IF zero; zero[]:=0; zero:=NIL; ENDIF
IF (c=10) OR (c=0)
f:=FALSE
ELSEIF (c<=" ") OR (c=",")
ELSEIF (c=";") OR (c="*")
f:=FALSE
WHILE p[]++<>10 DO NOP
ELSE
id[num]:=p-1
WHILE ((c:=p[]++)>" ") AND (c<>",") AND (c<>";") DO NOP
p--; zero:=p
IF num++=IDMAX THEN error(ER_WORK)
ENDIF
ENDWHILE
IF num
size:=-1
IF StrCmp(id[1],'MACRO',ALL)
macro:=TRUE
ELSEIF StrCmp(id[1],'EQU',ALL) OR StrCmp(id[1],'=',ALL) OR StrCmp(id[1],'equ',ALL)
IF macro=FALSE
d.type:=T_CONST
d.name++:=id[0]
defn++
ENDIF
ELSEIF StrCmp(id[0],'STRUCTURE',ALL)
struct:=TRUE
d.type:=T_STRUCT
d.name++:=id[1]
defn++
IF (InStr(id[2],'SIZEOF',0)<>-1) OR (InStr(id[2],'SIZE',0)<>-1)
IF defn>=MAX_DEF THEN error(ER_WORK)
d.type:=T_ARRAY
c:=InStr(id[2],'_',0)
IF c<>-1 THEN PutChar(id[2]+c,0)
d.name++:=id[2]
defn++
ELSEIF (Val(id[2],{c})<>0) OR (c=0)
WriteF('WARNING: object "\s" does not start at offset 0\n',id[1])
ENDIF
ELSEIF StrCmp(id[0],'LABEL',ALL)
IF struct=FALSE THEN error(ER_FORM)
IF (InStr(id[1],'SIZEOF',0)<>-1) OR (InStr(id[1],'SIZE',0)<>-1)
struct:=FALSE
d.type:=T_END
d.name++:=id[1]
defn++
ENDIF
ELSEIF StrCmp(id[0],'BYTE',ALL); size:=1
ELSEIF StrCmp(id[0],'UBYTE',ALL); size:=1
ELSEIF StrCmp(id[0],'WORD',ALL); size:=2
ELSEIF StrCmp(id[0],'UWORD',ALL); size:=2
ELSEIF StrCmp(id[0],'SHORT',ALL); size:=2
ELSEIF StrCmp(id[0],'USHORT',ALL); size:=2
ELSEIF StrCmp(id[0],'LONG',ALL); size:=4
ELSEIF StrCmp(id[0],'ULONG',ALL); size:=4
ELSEIF StrCmp(id[0],'APTR',ALL); size:=4
ELSEIF StrCmp(id[0],'BPTR',ALL); size:=4
ELSEIF StrCmp(id[0],'CPTR',ALL); size:=4
ELSEIF StrCmp(id[0],'BSTR',ALL); size:=4
ELSEIF StrCmp(id[0],'BOOL',ALL); size:=2
ELSEIF StrCmp(id[0],'FPTR',ALL); size:=4
ELSEIF StrCmp(id[0],'STRUCT',ALL); size:=0
ELSEIF StrCmp(id[1],'MACRO',ALL)
macro:=TRUE
ELSEIF StrCmp(id[0],'ENDM',ALL)
macro:=FALSE
ELSEIF StrCmp(id[0],'BITDEF',ALL) /* depends on correctness */
c:=id[1]-1
FOR a:=1 TO id[2]-id[1]-1 DO c[]++:=c[1]
c[]++:="F"; c[]++:="_"
d.type:=T_CONST
d.name++:=id[1]-1
defn++
ELSEIF StrCmp(id[0],'EITEM',ALL)
d.type:=T_CONST
d.name++:=id[1]
defn++
ELSEIF StrCmp(id[0],'DEVCMD',ALL)
d.type:=T_CONST
d.name++:=id[1]
defn++
ELSEIF StrCmp(id[1],'SET',ALL) OR StrCmp(id[0],'IFND',ALL) OR StrCmp(id[0],'ENDC',ALL) OR StrCmp(id[0],'INCLUDE',ALL) OR StrCmp(id[0],'ENUM',ALL) OR StrCmp(id[0],'LIBENT',ALL) OR StrCmp(id[0],'LIBINIT',ALL) OR StrCmp(id[0],'LIBDEF',ALL) OR StrCmp(id[0],'DEVINIT',ALL) OR StrCmp(id[0],'FUNCDEF',ALL) OR StrCmp(id[0],'include',ALL) OR StrCmp(id[0],'IFD',ALL)
ELSE
IF macro=FALSE THEN error(ER_FORM)
ENDIF
IF size<>-1
IF (InStr(id[1],'Kludge',0)<>-1) /* ask for deletion */
WriteF('Skip member "\s" (y/n)? >',id[1])
Read(stdout,work,2)
c:=work[0]
ELSE
c:=0
ENDIF
IF (c<>"y") AND (macro=FALSE)
IF struct=FALSE THEN error(ER_FORM)
IF size=1 THEN d.type:=T_CHAR ELSE IF size=2 THEN d.type:=T_INT ELSE IF size=4 THEN d.type:=T_LONG ELSE d.type:=T_ARRAY
d.name++:=id[1]
defn++
ENDIF
ENDIF
IF defn>=MAX_DEF THEN error(ER_WORK)
ENDIF
IF CtrlC() THEN error(ER_BREAK)
IF (line AND $F)=0 THEN WriteF('line=\d\b',line)
INC line
ENDWHILE
line:=0
ENDPROC
PROC makeasmfile()
DEF a,oldout,d:PTR TO def
IF (handle:=Open('ram:iconvert.s',NEWFILE))=NIL THEN error(ER_TEMPW)
IF defn=0 THEN error(ER_FORM)
d:=defs
oldout:=SetStdOut(handle)
IF phx THEN WriteF(' TTL ""\n')
WriteF(' INCLUDE "\s"\n\n',infile)
FOR a:=1 TO defn
WriteF(' DC.L \s\n',d.name++)
ENDFOR
WriteF('\n END\n')
SetStdOut(oldout)
Close(handle); handle:=NIL
ENDPROC
PROC invokea68k()
IF phx
IF Execute('SmallPhxAss I INCLUDES: FROM ram:iconvert.s noexe opt 0',NIL,stdout)=FALSE THEN error(ER_INVOKE)
ELSE
IF Execute('A68k -iINCLUDES: ram:iconvert.s',NIL,stdout)=FALSE THEN error(ER_INVOKE)
ENDIF
ENDPROC
PROC readbinary()
DEF bsize
bsize:=defn*4+32
IF FileLength('ram:iconvert.o')<>bsize THEN error(ER_TEMPR)
bbuf:=New(bsize)
IF bbuf=NIL THEN error(ER_MEM)
IF (handle:=Open('ram:iconvert.o',OLDFILE))=NIL THEN error(ER_TEMPR)
IF Read(handle,bbuf,bsize)<>bsize THEN error(ER_TEMPR)
Close(handle); handle:=NIL
ENDPROC
PROC makemodule()
DEF v:PTR TO LONG,work[3]:ARRAY OF INT,d:PTR TO def,l,x,obj=NIL
v:=bbuf+28; d:=defs
IF (handle:=Open(outfile,NEWFILE))=NIL THEN error(ER_OUT)
Write(handle,'EMOD',4)
FOR a:=1 TO defn
IF d.type=T_CONST
IF mode=2 THEN endobj(obj)
IF mode=0 THEN Write(handle,[1]:INT,2)
l:=StrLen(d.name)+1
work[0]:=IF Odd(l) THEN l+1 ELSE l
PutLong(work+2,v[]++)
Write(handle,work,6)
UpperStr(d.name)
Write(handle,d.name++,l)
IF Odd(l) THEN Write(handle,'',1)
mode:=1
ELSEIF d.type=T_END
IF mode<>2 THEN error(ER_OFORM)
mode:=0
Write(handle,[0,v[]++]:INT,4)
d++
ELSE
IF (mode=2) AND (d.type=T_STRUCT) THEN endobj(obj)
IF mode=1 THEN Write(handle,[0]:INT,2)
IF mode<>2
Write(handle,[2]:INT,2)
IF d.type<>T_STRUCT THEN error(ER_OFORM)
obj:=d.name
ENDIF
work[1]:=IF d.type=T_STRUCT THEN -1 ELSE d.type
work[2]:=v[]++
x:=InStr(d.name,'_',0)
IF x<>-1 THEN d.name:=d.name+x+1
LowerStr(d.name)
l:=StrLen(d.name)+1
work[0]:=IF Odd(l) THEN l+1 ELSE l
Write(handle,work,6)
Write(handle,d.name++,l)
IF Odd(l) THEN Write(handle,'',1)
mode:=2
ENDIF
ENDFOR
IF mode=2 THEN endobj(obj)
IF mode=1 THEN Write(handle,[0]:INT,2)
Close(handle); handle:=NIL
ENDPROC
PROC endobj(obj)
Write(handle,[0,-1]:INT,4)
WriteF('WARNING: object "\s" has no SIZE\n',obj)
mode:=0
ENDPROC
PROC error(er)
IF handle THEN Close(handle)
SELECT er
CASE ER_NONE; WriteF('Done.\n')
CASE ER_IN; WriteF('Could not read "\s"\n',infile)
CASE ER_OUT; WriteF('Could not write "\s"\n',outfile)
CASE ER_MEM; WriteF('Could not allocate memory\n')
CASE ER_WORK; WriteF('Work buffer overflow\n')
CASE ER_BREAK; WriteF('User terminated convertion\n')
CASE ER_FORM; WriteF('.i file format error\n')
CASE ER_TEMPW; WriteF('Trouble creating temporarily file\n')
CASE ER_TEMPR; WriteF('Trouble reading temporarily file\n')
CASE ER_INVOKE; WriteF('Could not invoke A68k\n')
CASE ER_OFORM; WriteF('Error in object order line \d in "ram:iconvert.s"\n',a+1)
CASE ER_I; WriteF('Not a ".i" file: "\s"\n',infile)
ENDSELECT
IF line THEN WriteF('At line \d in file "\s"\n',line,infile)
IF er>0 THEN DeleteFile(outfile)
CleanUp(0)
ENDPROC

View File

@ -0,0 +1,106 @@
/* Pragma2Module
convert a SAS/C library pragma file to an E module.
Usage: p2m <file>
converts <file.h> to <file.m> */
ENUM INPUT_ERROR=10,OUTPUT_ERROR,FORMAT_ERROR
DEF cfh,efh,eof,
gotbase=FALSE,
offset=30,
cfile[200]:STRING,
efile[200]:STRING,
cstring[200]:STRING
PROC main()
StrCopy(cfile,arg,ALL)
StrAdd(cfile,'.h',ALL)
StrCopy(efile,arg,ALL)
StrAdd(efile,'.m',ALL)
WriteF('Amiga E Pragma2Module by $#%! in 1992\nconverting: "\s"\n',cfile)
IF (cfh:=Open(cfile,OLDFILE))=0 THEN closeall(INPUT_ERROR)
IF (efh:=Open(efile,NEWFILE))=0 THEN closeall(OUTPUT_ERROR)
REPEAT
eof:=ReadStr(cfh,cstring)
convert(cstring)
UNTIL eof
Write(efh,[$FF,0,0,0]:CHAR,4)
WriteF('last offset: -\d\n',offset)
WriteF('Done.\n')
closeall(0)
ENDPROC
PROC closeall(er)
IF cfh<>0 THEN Close(cfh)
IF efh<>0 THEN Close(efh)
SELECT er
CASE INPUT_ERROR; WriteF('Could not open input file!\n')
CASE OUTPUT_ERROR; WriteF('Could not open output file!\n')
CASE FORMAT_ERROR; WriteF('Pragma file format error!\n')
ENDSELECT
CleanUp(er)
ENDPROC
/* format of line to convert:
#pragma libcall <lib>Base <funcname> <hexoffset> <regsused> */
PROC convert(str)
DEF pos,pos2,off2,len,narg,a,empty,dstr[50]:STRING,basestr[50]:STRING
DEF funcstr[50]:STRING,regstr[20]:STRING
IF StrCmp(str,'#pragma libcall ',STRLEN) /* just those lines */
pos:=STRLEN
pos2:=InStr(str,' ',pos) /* find base */
IF pos2=-1 THEN closeall(FORMAT_ERROR)
IF gotbase=FALSE /* get base */
gotbase:=TRUE
MidStr(basestr,str,pos,pos2-pos)
LowerStr(basestr)
WriteF('Base will be: \s\n',basestr)
Write(efh,["EM","OD",6]:INT,6)
Out(efh,0)
Write(efh,basestr,EstrLen(basestr)+1)
ENDIF
pos:=pos2+1
pos2:=InStr(str,' ',pos) /* find func */
IF pos2=-1 THEN closeall(FORMAT_ERROR)
MidStr(funcstr,str,pos,pos2-pos)
IF (funcstr[0]>="a") AND (funcstr[0]<="z") THEN funcstr[0]:=funcstr[0]-32 /* Func, not fUnc */
IF (funcstr[1]>="A") AND (funcstr[1]<="Z") THEN funcstr[1]:=funcstr[1]+32
str[pos2]:="$"
UpperStr(str+pos2)
off2:=Val(str+pos2,NIL) /* get offset */
IF off2=0 THEN closeall(FORMAT_ERROR)
empty:=0
WHILE off2<>offset
Out(efh,16) /* "empty function slots" */
offset:=offset+6
INC empty
IF offset>off2 THEN closeall(FORMAT_ERROR)
ENDWHILE
IF empty>0 THEN WriteF('found \d empty or private functions before -\d.\n',empty,offset)
pos2:=InStr(str,' ',pos2+1) /* find offset/regs */
IF pos2=-1 THEN closeall(FORMAT_ERROR)
pos:=pos2+1
MidStr(dstr,str,pos,ALL)
Write(efh,funcstr,EstrLen(funcstr))
len:=EstrLen(dstr)
narg:=Val(dstr+len-1,NIL)
IF len>2
IF len-2<narg THEN StrCopy(regstr,'0',ALL)
StrAdd(regstr,dstr,len-2)
ELSE
StrCopy(regstr,'',ALL)
ENDIF
len:=EstrLen(regstr)
StrCopy(dstr,'$ ',ALL)
IF len>0
FOR a:=1 TO len
dstr[1]:=regstr[len-a]
Out(efh,Val(dstr,NIL))
ENDFOR
ELSE
IF narg=0 THEN Out(efh,16) ELSE Out(efh,0)
ENDIF
offset:=offset+6
ENDIF
ENDPROC

View File

@ -0,0 +1,405 @@
-> EC debugger
OBJECT debuginfo
objs,procs,identhash,dbugval,dbugadr,modinfolist,codelist,memlist,heap,macrohash
ENDOBJECT
PROC main()
DEF s[10]:STRING,c=1,port,db:PTR TO debuginfo
WriteF('EC structure debugger\n')
port:=FindPort('EmoduleCache')
IF port=NIL
WriteF('no Cache!\n')
RETURN 10
ENDIF
WHILE c
WriteF('show which datastructure? (<lf> to quit)\n')
WriteF('["O"bjects,"P"procs,"I"dents,"V"alue,"A"dr,"M"odInfo,"C"odeRem,\n"K"ookiecheck,"H"eap,mac"R"os]: ')
ReadStr(stdin,s)
LowerStr(s)
lf()
c:=s[]
db:=Long(port+42)
do(c,db)
lf()
ENDWHILE
ENDPROC
PROC do(c,db:PTR TO debuginfo) HANDLE
SELECT c
CASE "o"; object(Long(db.objs))
CASE "p"; proc(Long(db.procs))
CASE "m"; modinfo(Long(db.modinfolist))
CASE "c"; coderem(Long(db.codelist))
CASE "k"; cookie(Long(db.memlist))
CASE "h"; heaps(db.heap)
CASE "i"; identhash(db.identhash)
CASE "v"; val(db.dbugval)
CASE "a"; mem(db.dbugadr)
CASE "r"; macros(db.macrohash)
CASE "\0"; WriteF('\ndone.\n')
DEFAULT; WriteF('Unsupported datastructure\n')
ENDSELECT
EXCEPT
WriteF(IF exception="INV" THEN 'terminated because of invalid address\n' ELSE 'exception!')
ENDPROC
PROC valid(a)
IF (a<$20000) OR (a>$A00000) -> based on 2meg chip and 8meg fast
IF a
WriteF('[INVALID:$\z\h[8]]',a)
RETURN FALSE
ENDIF
ENDIF
ENDPROC TRUE
PROC string(s) IS IF valid(s) THEN WriteF(IF s THEN 'str="\s"; ' ELSE 'str=NIL; ',s) ELSE 0
PROC str(name,s) IS IF valid(s) THEN IF s<>NIL THEN WriteF('\s="\s"; ',name,s) ELSE 0 ELSE 0
PROC flags(b) IS WriteF('flags=$\h; ',b)
PROC int(s,i) IS WriteF('\s=\d; ',s,i)
PROC lf() IS WriteF('\n')
PROC t() IS WriteF(' ')
PROC negnext(o) IS Long(o-4)
OBJECT objectheader
->onext:LONG -> -4
odel:INT -> 0, delegate size
otype:CHAR -> 2, flags: bit 0=export
dummy:CHAR -> 3, _empty_spot_
osize:INT -> 4, sizeof object
oid:INT -> 6, object ID
oascii:LONG -> 8
omemb:LONG -> 12, memberlist
omethod:LONG -> 16, methodlist
osuper:LONG -> 20, superclass
odcode -> 24
oacc -> 28
odeloff:INT -> 32
odestr:INT -> 34
ENDOBJECT
PROC object(a:PTR TO objectheader)
DEF s:PTR TO objectheader
IF valid(a)
IF a
IF a.otype AND 2 = 0
string(a.oascii)
flags(a.otype)
int('sizeof',a.osize)
int('id',a.oid)
int('delsize',a.odel)
int('deloff',a.odeloff)
int('destr',a.odestr)
s:=a.osuper
IF s THEN s:=s.oascii
WriteF('superclass:')
string(s)
WriteF('\nmemberlist:\n')
member(a.omemb)
WriteF('methodlist:\n')
method(a.omethod)
lf()
object(negnext(a))
ENDIF
ENDIF
ENDIF
ENDPROC
OBJECT member
-> onext:PTR TO member -> -4
ooff:INT -> 0, offset
oflags:CHAR -> 2, SET PRIVATE,HASPTRTYPE
dummy:CHAR -> 3, _empty_spot_
osize:INT -> 4, fieldsize [1,2,4,0=array]
oid:INT -> 6, object ID
oascii:LONG -> 8, NIL if PRIVATE
optrtype:LONG -> 12, only if HASPTRTYPE
ENDOBJECT
PROC member(m:PTR TO member)
IF m
IF valid(m)
t(); string(m.oascii)
int('offset',m.ooff)
int('size',m.osize)
int('id',m.oid)
flags(m.oflags)
IF m.oflags AND 2
IF m.optrtype AND $FFFFFFF0 = 0
int('ptrtype',m.optrtype)
ELSE
WriteF('ptrtype=<object>')
ENDIF
ENDIF
lf()
member(negnext(m))
ENDIF
ENDIF
ENDPROC
OBJECT proc
-> next:PTR TO proc -> -4
nrargs:INT -> 0
flags:CHAR -> 2, SET COMPILE_RTD,METHOD
numregvars:CHAR -> 3, 0..3
nrloc:INT -> 4, -(nrloc*4)
defargs:LONG -> 6
of_object:LONG -> 10
ident:LONG -> 14
self -> 18
method -> 22
regtab:PTR TO LONG -> 26
ENDOBJECT
OBJECT method
m_next:LONG -> 0
m_proc:LONG -> 4
m_type:CHAR -> 8 [0=MT_METHOD, 1=MT_FROZEN, 2=MT_SHARED]
m_flags:CHAR -> 9 SET INHERITED
m_off:INT -> 10 delegate offset
m_name:LONG -> 12
ENDOBJECT
PROC method(m:PTR TO method)
DEF p:PTR TO proc
IF m
IF valid(m)
t(); string(m.m_name)
int('type',m.m_type)
flags(m.m_flags)
int('deloff',m.m_off)
WriteF(' -> proc: ')
p:=m.m_proc
int('nargs',p.nrargs)
lf()
method(m.m_next)
ENDIF
ENDIF
ENDPROC
PROC val(x)
WriteF('value = \d, $\h\n',x,x)
ENDPROC
PROC mem(a:PTR TO LONG)
DEF b,c
c:=a
WriteF('dump ($\h) = ',a)
IF valid(a)
FOR b:=1 TO 16 DO WriteF('\z$\h[8] ',a[]++)
ENDIF
WriteF('\n"\s"\n',c)
lf()
ENDPROC
PROC identhash(p:PTR TO LONG)
DEF x
IF valid(p)
IF p
FOR x:=1 TO 256 DO IF p[] THEN ident(p[]++) ELSE p++
ENDIF
ENDIF
ENDPROC
OBJECT ident
-> flags = SET USED,SYS_VAR,EXPORT(in main var is uit module),REG,REAL|METHOD
-> etype = PTR TO [1,2,4] | PTR TO <object>
-> offset = +arg, -localvar
->next:LONG -> -4
etype:LONG -> 0, [type,type,ascii]
type:CHAR -> 4, [local,global,lab] = [1,2,3]
flags:CHAR -> 5
pr:LONG -> 6, [proc,PTR TO globinfo|NIL,proc|NIL if lab]
info:INT -> 10, [offset/regnum,offset,ID=label]
heavy:LONG -> VARHEAVY, only for local+reg_alloc
ENDOBJECT
PROC ident(p:PTR TO ident)
DEF t,n=NIL,pr=NIL,tn,f,fs[100]:STRING,type=NIL
IF valid(p)
IF (p.flags AND 2)=0 AND p
t:=p.type
f:=p.flags
IF f AND 4 THEN StrAdd(fs,'EXPORT')
IF f AND 8 THEN StrAdd(fs,'REG')
IF f AND 16 THEN StrAdd(fs,'METHOD/REAL')
IF EstrLen(fs)=0 THEN fs:=NIL
SELECT t
CASE 1; tn:='LOCAL'; pr:=Long(Long(p.pr+14))
CASE 2; tn:='GLOB'
CASE 3; tn:='PROC'; IF p.pr=NIL THEN tn:='LAB'; n:=p.etype
DEFAULT; tn:='UNDEF'
ENDSELECT
IF (t=1) OR (t=2)
type:=p.etype
SELECT type
CASE 1; type:=NIL
CASE 2; type:='INT'
CASE 4; type:='LONG'
DEFAULT; type:=Long(p.etype+8)
ENDSELECT
ENDIF
WriteF(' ')
str('proc',pr)
str('name',n)
str('sort',tn)
str('flags',fs)
str('ptr_to',type)
WriteF('info=\d; heavy=$\h\n',p.info,p.heavy)
ident(negnext(p))
ENDIF
ENDIF
ENDPROC
PROC proc(p:PTR TO proc)
DEF i:PTR TO ident, s:PTR TO objectheader, rt:PTR TO LONG
IF valid(p)
IF p
WriteF('at:$\h; ',p)
int('args',p.nrargs)
int('locals',p.nrloc)
int('numregvars',p.numregvars)
flags(p.flags)
WriteF('ident:')
i:=p.ident
IF i AND valid(i) THEN string(i.etype)
IF i:=p.self
IF valid(i)
WriteF('\n self:')
IF valid(s:=i.etype) THEN string(s.oascii)
ENDIF
ENDIF
lf()
IF rt:=p.regtab
WriteF('\tregalloc:')
WHILE rt[]
WriteF(' \d',Shr(Long(rt[]+12),8)-1)
rt++
ENDWHILE
lf()
ENDIF
proc(negnext(p))
ENDIF
ENDIF
ENDPROC
OBJECT modinfo
next,flags:INT,namelen,mod,list,name
ENDOBJECT
OBJECT procclass
next,type:INT,info,acc
ENDOBJECT
OBJECT acc
next,code
ENDOBJECT
PROC modinfo(p:PTR TO modinfo)
IF valid(p)
IF p
str('mod',p.name)
int('flags',p.flags)
lf()
procclass(p.list)
modinfo(p.next)
ENDIF
ENDIF
ENDPROC
PROC procclass(p:PTR TO procclass)
DEF x=0,l:PTR TO acc
IF valid(p)
IF p
int('type',p.type)
l:=p.acc
WHILE l
x++
l:=l.next
ENDWHILE
IF p.type=2 THEN str('lab',Long(p.info))
int('numacc',x)
lf()
procclass(p.next)
ENDIF
ENDIF
ENDPROC
OBJECT coderem
next,type:INT,info1,info2
ENDOBJECT
PROC coderem(p:PTR TO coderem)
IF valid(p)
IF p
int('type',p.type)
IF (p.type=5) OR (p.type=6) THEN str('ref_name',p.info1)
lf()
coderem(p.next)
ENDIF
ENDIF
ENDPROC
PROC template(p:PTR TO proc)
IF valid(p)
IF p
-> ...
template(negnext(p))
ENDIF
ENDIF
ENDPROC
CONST COOKIE=$BE
OBJECT memlist
next,size
ENDOBJECT
PROC cookie(meml:PTR TO memlist)
DEF a
WHILE meml
IF valid(meml)
a:=meml+meml.size-1
IF a[]<>COOKIE
DisplayBeep(NIL)
DisplayBeep(NIL)
DisplayBeep(NIL)
WriteF('cookie check failed!!!!!!!!!\n')
RETURN
ENDIF
meml:=meml.next
ELSE
RETURN
ENDIF
ENDWHILE
ENDPROC
PROC heaps(h:PTR TO LONG)
WHILE CtrlC()=FALSE
IF (h[2]-100>h[]) OR (h[2]+13000<h[])
WriteF('!!!!\n')
DisplayBeep(NIL)
Delay(10)
ENDIF
ENDWHILE
ENDPROC
OBJECT macro
next:PTR TO macro
name,body
nargs:INT
flags:CHAR
ENDOBJECT
PROC macros(m:PTR TO LONG)
DEF a,l:PTR TO macro
FOR a:=0 TO 255
l:=m[a]
WHILE l
WriteF('\s/\d = "\s"\n',l.name,l.nargs,l.body)
l:=l.next
ENDWHILE
ENDFOR
ENDPROC

View File

@ -0,0 +1,42 @@
-> FlushCache.e
MODULE 'exec/ports'
OBJECT cache
port:mp, cookie, modlist, debuginfo, lock:INT, reserved:INT, name[50]:ARRAY
ENDOBJECT
OBJECT mod
next,memlen,stradr,strlen,moduleadr,modulelen,checksum,reserved
ENDOBJECT
PROC main()
DEF c:PTR TO cache, m:PTR TO mod, d, find, last:PTR TO LONG
WriteF('Emodule Cache Flush (c) 1993 $#%!\n\n')
find:=IF arg[] THEN arg ELSE '.m'
IF c:=FindPort('EmoduleCache')
IF c.lock=0
IF c.cookie=$DEADBEEF
m:=c.modlist
last:=c+38 -> dirty!
WHILE m
d:=m.next
IF InStr(m.stradr,find)<>-1
FreeMem(m,m.memlen)
^last:=d
ELSE
last:=m
ENDIF
m:=d
ENDWHILE
WriteF('done.\n')
ELSE
WriteF('Dead cookie!!!\n')
ENDIF
ELSE
WriteF('Cache currently in use!\n')
ENDIF
ELSE
WriteF('No module cache available.\n')
ENDIF
ENDPROC

View File

@ -0,0 +1,139 @@
-> o2m.e, convert assembly (and a subset of C) .o files to E modules.
MODULE 'dos/doshunks', 'tools/file'
DEF type,codesize,codeadr,relocadr=NIL,relocsize -> filled by process()
OBJECT syminfo
next,sym,len,val
ENDOBJECT
DEF slist=NIL
PROC main() HANDLE
DEF infile[100]:STRING, outfile[100]:STRING, m, l
WriteF('o2m v0.1 by $#%! 1993\n')
StringF(infile,'\s.o',arg)
StringF(outfile,'\s.m',arg)
WriteF('Converting "\s" to "\s"\n',infile,outfile)
m,l:=readfile(infile)
process(m,l)
writeinfos()
writemodule(outfile)
EXCEPT
SELECT exception
CASE "MEM"; WriteF('No Mem!\n')
CASE "OPEN"; WriteF('Could not open file\n')
CASE "IN"; WriteF('Problems while reading\n')
CASE "FORM"; WriteF('Hunk format error!\n')
ENDSELECT
ENDPROC
PROC process(o:PTR TO LONG,len) HANDLE
DEF n,hunk,s:PTR TO syminfo,c
IF o[]++<>HUNK_UNIT THEN Raise("FORM")
o:=o[]++*4+o
IF o[]=HUNK_NAME
o++
o:=o[]++*4+o
ENDIF
type:=o[]++
IF (type<>HUNK_CODE) AND (type<>HUNK_DATA) THEN Raise("CODE")
codesize:=o[]++
codeadr:=o
o:=codesize*4+o
WHILE (hunk:=o[]++)<>HUNK_END
IF hunk=HUNK_RELOC32
IF relocadr THEN Raise("1REL")
n:=o[]++
IF o[]++ THEN Raise("1REL")
relocadr:=o
o:=n*4+o
IF o[]++ THEN Raise("1REL")
relocsize:=n
ELSEIF hunk=HUNK_EXT
WHILE Char(o)=EXT_DEF
n:=o[]++ AND $FFFFFF
s:=NewR(SIZEOF syminfo)
s.sym:=o
c:=Char(o)
IF (c>="A") AND (c<="Z") THEN PutChar(o,c+32)
s.len:=n*4
s.next:=slist
slist:=s
o:=n*4+o
s.val:=o[]++
ENDWHILE
IF o[]++ THEN Raise("DEF")
ELSE
Raise("HUNK")
ENDIF
ENDWHILE
EXCEPT
SELECT exception
CASE "HUNK"; WriteF('Only hunk_code, hunk_ext and hunk_reloc32 allowed\n')
CASE "CODE"; WriteF('hunk_code or hunk_data expected\n')
CASE "1REL"; WriteF('At most one relochunk expected\n')
CASE "DEF"; WriteF('Only XDEFs allowed, no XREFs\n')
ENDSELECT
Raise("FORM")
ENDPROC
PROC writeinfos()
DEF list:PTR TO syminfo, s[50]:STRING
list:=slist
WriteF('codesize=\d, numreloc=\d\n',codesize*4,relocsize)
WHILE list
StrCopy(s,list.sym,list.len)
WriteF('\s=$\h ',s,list.val)
list:=list.next
ENDWHILE
WriteF('\n')
ENDPROC
PROC writemodule(modname)
DEF handle,list:PTR TO syminfo, s[50]:STRING, res[50]:STRING, num, l, ll
IF (handle:=Open(modname,NEWFILE))=NIL THEN Raise("OPEN")
Write(handle,["EM","OD",5,0,1000,0,0,0,0,0,0,2,0,0,3,0,codesize]:INT,34)
Write(handle,codeadr,codesize*4)
IF relocadr
IF relocsize
Write(handle,[7,0,relocsize]:INT,6)
Write(handle,relocadr,relocsize*4)
ENDIF
ENDIF
Write(handle,[4]:INT,2)
list:=slist
WHILE list
StrCopy(s,list.sym,list.len)
num:=findargs(s,res)
l:=EstrLen(res)
ll:=l+IF Even(l) THEN 2 ELSE 1
Write(handle,[ll]:INT,2)
Write(handle,res,ll)
Write(handle,[list.val]:LONG,4)
IF type=HUNK_CODE
Write(handle,[1,num,0,0,0]:INT,10)
ELSE
Write(handle,[2]:INT,2)
ENDIF
list:=list.next
WriteF('\s/\d ',res,num)
ENDWHILE
Write(handle,[-1,0,0]:INT,6)
Close(handle)
WriteF('\n')
ENDPROC
PROC findargs(sym,res)
DEF fpos,lpos,ipos,a=0,c,s
lpos:=IF (ipos:=InStr(sym,'_i'))>1 THEN ipos ELSE EstrLen(sym)
fpos:=0
WHILE sym[fpos]="_" DO fpos++
WHILE sym[lpos-1]="_" DO lpos--
IF lpos>fpos THEN StrCopy(res,sym+fpos,lpos-fpos) ELSE StrCopy(res,sym)
IF ipos>1
s:=ipos+sym
WHILE c:=s[]++ DO IF c="i" THEN INC a
ENDIF
ENDPROC a

View File

@ -0,0 +1,41 @@
-> ShowCache.e
MODULE 'exec/ports'
OBJECT cache
port:mp, cookie, modlist, debuginfo, lock:INT, reserved:INT, name[50]:ARRAY
ENDOBJECT
OBJECT mod
next,memlen,stradr,strlen,moduleadr,modulelen,checksum,reserved
ENDOBJECT
PROC main()
DEF c:PTR TO cache, m:PTR TO mod, total=0, x
WriteF('Emodule Cache Show (c) 1993 $#%!\n')
IF c:=FindPort('EmoduleCache')
IF c.lock=0
IF c.cookie=$DEADBEEF
m:=c.modlist
IF m
WriteF('\nsize name\n------- -----------------------------------\n')
WHILE m
WriteF('\d[7] \s\n',m.modulelen,m.stradr)
total:=total+m.modulelen+SIZEOF mod+StrLen(m.stradr)+8
m:=m.next
IF CtrlC() THEN m:=NIL
ENDWHILE
WriteF('\nTotal memory occupied by cached E-modules: \d bytes.\n',total)
ELSE
WriteF('\nEmpty cache.\n')
ENDIF
ELSE
WriteF('\nDead cookie.\n')
ENDIF
ELSE
WriteF('Cache currently in use!\n')
ENDIF
ELSE
WriteF('\nNo module cache available.\n')
ENDIF
ENDPROC

View File

@ -0,0 +1,313 @@
/* ShowModule.e; dumps all the infos in a '.m' binary file */
ENUM JOB_DONE,JOB_CONST,JOB_OBJ,JOB_CODE,JOB_PROCS,
JOB_SYS,JOB_LIB,JOB_RELOC,JOB_GLOBS,JOB_MODINFO,JOB_DEBUG,JOB_MACROS
ENUM ER_NONE,ER_FILE,ER_MEM,ER_USAGE,ER_JOBID,
ER_BREAK,ER_FILETYPE,ER_TOONEW
CONST MODVERS=10, -> upto which version we understand
SKIPMARK=$FFFF8000
DEF flen,o:PTR TO INT,mem,handle=NIL,file[250]:STRING,thisvers=0,cmode=FALSE
PROC main() HANDLE
DEF a,b,ae
PrintF('ShowModule v1.\d (c) 1992 $#%!\n',MODVERS)
IF StrCmp(arg,'',1) OR StrCmp(arg,'?',2)
Raise(ER_USAGE)
ELSE
IF (arg[]="-") AND (arg[1]="c") AND (arg[2]=" ")
arg:=arg+3
WHILE arg[]=" " DO arg++
cmode:=TRUE
ENDIF
ae:=arg+StrLen(arg)
WHILE (ae[-1]=" ") AND (ae>arg) DO ae[]--:=0
StrCopy(file,arg)
LowerStr(file)
IF (file[EstrLen(file)-2]<>".") OR (file[EstrLen(file)-1]<>"m") THEN StrAdd(file,'.m')
PrintF('now showing: "\s"\n',file)
PrintF('NOTE: don\at use this output in your code, use the module instead.\n\n')
flen:=FileLength(file)
handle:=Open(file,OLDFILE)
IF (flen<8) OR (handle=NIL)
Raise(ER_FILE)
ELSE
mem:=New(flen+10)
IF mem=NIL
Raise(ER_MEM)
ELSE
IF Read(handle,mem,flen)<>flen THEN Raise(ER_FILE)
Close(handle)
a:=mem+flen
FOR b:=1 TO 6 DO a[]++:=0
handle:=NIL
process()
ENDIF
ENDIF
ENDIF
EXCEPT
IF handle THEN Close(handle)
PrintF('\n')
SELECT exception
CASE ER_FILE; PrintF('Could not read file "\s" !\n',file)
CASE ER_MEM; PrintF('No memory for loading module!\n')
CASE ER_USAGE; PrintF('USAGE: ShowModule [-c] <module>\n')
CASE ER_JOBID; PrintF('Illegal job id!\n')
CASE ER_BREAK; PrintF('User interupted ShowModule\n')
CASE ER_FILETYPE; PrintF('Not an E module file.\n')
CASE ER_TOONEW; PrintF('You need a newer version of ShowModule to view this module\n')
ENDSELECT
ENDPROC
PROC process()
DEF end,job,len,val,f,off,types:PTR TO LONG,c,r,c2,l,narg,priv,darg:PTR TO LONG
o:=mem
end:=o+flen
types:=['substructure','CHAR','INT','','LONG']
IF ^o++<>"EMOD" THEN Raise(ER_FILETYPE)
WHILE o<end
IF CtrlC() THEN Raise(ER_BREAK)
job:=o[]++
SELECT job
CASE JOB_CONST
IF thisvers>=6 THEN o:=o+4
len:=o[]++; f:=TRUE
WHILE len
val:=^o++
PrintF(IF cmode THEN '#define ' ELSE IF f THEN 'CONST ' ELSE ' ')
PrintF(IF cmode THEN '\s ' ELSE '\s=',o)
IF (val>=-$20) AND (val<$20) THEN PrintF('\d',val) ELSE PrintF(IF cmode THEN '0x\h' ELSE '$\h',val)
o:=o+len; len:=o[]++; f:=FALSE
PrintF(IF len THEN (IF cmode THEN '\n' ELSE ',\n') ELSE '\n\n')
IF CtrlC() THEN Raise(ER_BREAK)
ENDWHILE
CASE JOB_OBJ
IF thisvers>=6 THEN o:=o+4
priv:=0
l:=o[]++;
PrintF('(----) \s \s\s\n',IF cmode THEN 'struct' ELSE 'OBJECT',o+4,IF cmode THEN ' {' ELSE '')
o:=o+4+l
WHILE l:=o[]++
val:=o[]++
off:=o[]++
IF l>0
PrintF('(\d[4]) \s',off,o)
o:=o+l
priv:=0
ELSE
IF priv++=0 THEN PrintF('(----) /* private member(s) here */\n')
ENDIF
IF thisvers>=6
IF (c:=o[]++)>=0
IF c=0
PrintF(':\s\n',types[val])
ELSE
PrintF(IF val THEN '\s:PTR TO \s\n' ELSE '[\d]:ARRAY OF \s\n',IF val THEN '' ELSE Int(o+IF o[] THEN 4 ELSE 2)-off/c,ListItem(['','CHAR','INT','','LONG'],c))
ENDIF
ELSE
l:=o[]++
PrintF(IF val THEN ':PTR TO \s\n' ELSE ':\s (or ARRAY OF \s)\n',o,o)
o:=o+l
ENDIF
ELSE
PrintF(':\s\n',types[val])
ENDIF
IF CtrlC() THEN Raise(ER_BREAK)
ENDWHILE
val:=o[]++
IF thisvers>=7
IF o[]++
o:=o+4
l:=o[]++
o:=o+l+4
WHILE (c:=o[]++)<>-1
o++; l:=o[]++
PrintF(' \s(',o)
o:=o+l
IF l:=o[]++ THEN FOR off:=1 TO l DO PrintF(IF off=l THEN '\c' ELSE '\c,',off+96)
PutStr(')\n')
l:=o[]++; o:=l*4+o
ENDWHILE
WHILE o[]++<>-1 DO o:=o+4
ENDIF
ENDIF
PrintF('(----) \s /* SIZEOF=',IF cmode THEN '}' ELSE 'ENDOBJECT')
PrintF(IF val<>-1 THEN '\d */\n\n' ELSE 'NONE !!! */\n\n',val)
CASE JOB_CODE
l:=^o++*4
PrintF('/* this module contains \d bytes of code! */\n\n',l)
o:=l+o
CASE JOB_PROCS
WHILE (l:=o[]++)>0
c:=o
o:=o+l+4
IF o[]++=1
PrintF('PROC \s(',c)
narg:=o[]++
o++
c2:=o[]++
darg:=o
o:=c2*4+o
c:=o[]++
IF c
IF c2
l:=o
FOR r:=1 TO narg
WHILE l[]>"0" DO FputC(stdout,l[]++)
IF narg-r<c2 THEN PrintF('=\d',darg[]++)
PutStr(IF r<>narg THEN ',' ELSE ')\n')
l++
ENDFOR
ELSE
PrintF('\s)\n',o)
ENDIF
ELSE
IF narg THEN FOR c2:=1 TO narg DO PrintF(IF c2=narg THEN '\c' ELSE '\c,',c2+96)
PrintF(')\n')
ENDIF
o:=o+c
ELSE
PrintF('\s:\n',c)
ENDIF
ENDWHILE
PrintF('\n')
CASE JOB_SYS
o:=o+4
f:=FALSE
IF c:=o[]++
f:=TRUE
PrintF('/* osvers: \d+ ',c)
ENDIF
o:=o+4
IF c:=o[]++
IF f=FALSE THEN PrintF('/* ')
f:=TRUE
PrintF('cpu: \s+ ',ListItem(['68020/030','68040/060'],c-1))
ENDIF
IF c:=o[]++
IF f=FALSE THEN PrintF('/* ')
f:=TRUE
PrintF('fpu: \s+ ',ListItem(['68881/2','68040/060'],c-1))
ENDIF
o:=o+2
IF (thisvers:=o[]++)>MODVERS THEN Raise(ER_TOONEW)
o:=o+4
IF f THEN PrintF('*/\n\n')
CASE JOB_LIB
c:=o
WHILE c[]++ DO NOP
PrintF(IF cmode THEN '##base _\s\n##bias 30\n##public\n' ELSE 'LIBRARY \s /* informal notation */\n',c)
WHILE c[]++ DO NOP
off:=-30
WHILE (c[]<>$FF) AND (c<end)
IF c[]=16
INC c
ELSE
c2:=c
WHILE c[]++>" " DO NOP; c--
r:=c[]; c[]++:=0
PrintF(IF cmode THEN '\s' ELSE ' \s',c2)
IF cmode THEN dargs(r,c)
PrintF('(')
IF r<>16
WHILE r<16
IF r<8 THEN PrintF('D\d',r) ELSE PrintF('A\d',r-8)
r:=c[]++
IF r<16 THEN PrintF(',')
ENDWHILE
c--
ENDIF
PrintF(IF cmode THEN ')\n' ELSE ') /* \d (\h) */\n',off,Abs(off))
ENDIF
off:=off-6
ENDWHILE
PrintF(IF cmode THEN '##end\n\n' ELSE 'ENDLIBRARY\n\n')
o:=end
CASE JOB_RELOC
c:=^o++
o:=c*4+o
PrintF('/* ... and \d reloc entries */\n\n',c)
CASE JOB_DONE
o:=end
CASE JOB_GLOBS
c:=0; f:=TRUE
IF o[]=SKIPMARK THEN o:=o+6
WHILE (len:=o[]++)>=0
IF len
IF f
PrintF('DEF ')
f:=FALSE
ELSE
PrintF(',')
ENDIF
PrintF('\s',o)
o:=o+len
ELSE
c++
ENDIF
WHILE ^o++ DO IF thisvers>=10 THEN o++
ENDWHILE
IF f=FALSE THEN PrintF('\n')
IF c THEN PrintF('/* \d private global variable(s) in this module */\n',c)
PrintF('\n')
CASE JOB_MODINFO
o:=o+4
PutStr('/*\n')
WHILE len:=o[]++
PrintF(' code from module "\s" used:\n',o)
o:=o+len
WHILE c:=o[]++
len:=o[]++
c2:=o
o:=o+len
IF c=2
f:=o[]++
PrintF(IF f<>-1 THEN ' \s\c)/\d' ELSE ' \s:',c2,"(",f)
PrintF(' (\dx)\n',c:=o[]++)
o:=c*4+o
ELSE
c:=o[]++
PrintF(' OBJECT \s [\d acc]\n',c2,c)
o:=c*6+o
ENDIF
ENDWHILE
ENDWHILE
PutStr('*/\n')
CASE JOB_DEBUG
WHILE ^o++=$3F1
len:=^o++
o:=o+4
c:=^o++
o:=len*4+o-8
PrintF('/* This module contains \d bytes \s DEBUG infos! */\n\n',
len*4,IF c="EVAR" THEN 'EVAR' ELSE 'LINE')
ENDWHILE
CASE JOB_MACROS
WHILE len:=o[]++
PrintF('#define \s',o)
o:=o+len
PrintF('/\d\n',o[]++)
o++
o:=o[]+++o
ENDWHILE
PutStr('\n')
DEFAULT
Raise(ER_JOBID)
ENDSELECT
ENDWHILE
ENDPROC
PROC dargs(r,c)
DEF ch="a"
PrintF('(')
IF r<>16
WHILE r<16
PrintF('\c',ch++)
r:=c[]++
IF r<16 THEN PrintF(',')
ENDWHILE
ENDIF
PrintF(')')
ENDPROC