home *** CD-ROM | disk | FTP | other *** search
-
- +-----------------------------------------------+
- | |
- | Amiga E v2.1b |
- | Compiler for The E Language |
- | By Wouter van Oortmerssen |
- | |
- | Tutorial |
- | |
- +-----------------------------------------------+
-
-
- +-----------------------------------------------+
- | LANGUAGE TUTORIAL |
- +-----------------------------------------------+
-
- This chapter will take you on a step-by-step guided tour to learn
- some E. As soon as you've covered the basics in this section, you may
- enhance your knowledge of E using Reference.doc and the examples on
- disk. It's best to try the examples with the compiler as you go along.
- It is assumed that the reader possesses some kind of knowledge of any
- higher programming language (like C/Pascal) to be able to omit the more
- obvious explanations. If you feel that you need more explanation about
- some feature, see reference.doc.
-
- make sure you have installed Amiga E in your system as described
- in 'Compiler.doc' and compiled 'Helloworld.e' as a first test.
-
- Ok, we'll start off easy. Doesn't it strike you as odd that there's
- no such command as CLS in the shell? yes? then we'll write our own:
-
- PROC main()
- WriteF('\c',12)
- ENDPROC
-
- save it as 'cls.e' and compile it with 'ec cls'. then see the result.
- nothing shocking so far, but it gets us to two important points.
- first of all, you'll notice that even the smallest programs have to
- be put in a function, with the name "main". For now it may look
- superfluous, but when you start writing bigger programs, you'll notice
- why this is. Secondly, we learn something new about WriteF(), and
- what the "F" stands for. In compiling HelloWorld.e, you saw that
- WriteF() just prints strings to the console. Here we see that such
- a string may contain formatting codes introduced by a "\" and a
- character that says what type of formatting will take place, like
- "c" is for characters, and "d" for decimals. Any arguments to such
- a code are simply added as arguments to WriteF(). Here we print
- a character with ascii-code 12, which wipes the screen.
-
- And now for something completely different.
- Every programmer who starts to learn a new language on the amiga wants
- to open his own window. Ok, so lets do that in E.
-
- First we use the E system function OpenW() to open a window:
-
- w:=OpenW(20,11,400,100,$200,$F,'My first window in E!',NIL,1,NIL)
-
- The first few arguments are, of course, the coordinates. The
- string must be the title (you can check the exact parameters in
- reference.doc chapter 9D). Those two hexadecimal values are flags:
- the values for those are defined in the module file 'intuition/intuition'.
- There are two ways to use flags like that, by using the module:
-
- MODULE 'intuition/intuition'
-
- idcmpflags:=IDCMP_CLOSEWINDOW
-
- or the lazy way, by just writing down the values:
-
- idcmpflags:=$200
-
- These methods are exact equivalents, and it's up to you to choose which
- one you use. Of course, an empty window is boring, so we put a line in it:
-
- Line(20,20,50,50,2)
-
- Line() knows from the last call to OpenW() where to draw it.
- We specified $200 (=IDCMP_CLOSEWINDOW) while opening our
- window, because we want the user to press the closegadget when he's
- ready watching our window. We accomplish this waiting by:
-
- WaitIMessage(w)
-
- This function waits for exactly one message to arrive at our window's
- port. And because we told intuition we only want to know about the
- close-gadget, this must be IDCMP_CLOSEWINDOW.
- Now we can close our window:
-
- CloseW(w)
-
- Putting all this together for our first working window-program:
-
-
- /* Opening a window in E */
-
- DEF w
-
- PROC main()
- IF w:=OpenW(20,11,400,100,$200,$F,'My first window in E!',NIL,1,NIL)
- Line(20,20,50,50,2)
- WaitIMessage(w)
- CloseW(w)
- ENDIF
- ENDPROC
-
-
- Save this as 'mywindow.e' and compile it with 'ec mywindow'
- You'll notice that there's a small addition to our program: the
- IF statement. We do this to check if our window could be opened.
- You must _always_ check if you're allocating resources like screens,
- windows, libraries and memory! If the assignment in the IF-statement
- looks strange to you, remember that OpenW() will return NIL (=0) if
- it fails, and then w:=OpenW() will also evaluate to 0, which is FALSE.
-
- Note that if you want to display text in your window, you cannot use
- WriteF(), because WriteF() operates on consoles (or files). however,
- there's a "F" function that works on rastports (an underlying structure
- for windows and screens and the like): TextF().
- For example, add after the Line() function:
-
- TextF(20,70,'Well well, i'm writing to my window')
-
- You can even use the Colour() function to change the front- and
- background colours of the text.
-
- Now we'll get a bit more complex: we want to have gadgets in our
- window to perform actions. Anyone who has used gadgets in other
- languages knows that this means filling in tons of structures.
- Not so in E: We use the function Gadget() to create gadgets for us.
- This function needs buffers to store those structures, so we compute
- the required buffersize in a constant:
-
- CONST BUFSIZE=GADGETSIZE*3
-
- where "3" is of course the number of gadgets we wish to create.
- "GADGETSIZE" is a system constant that tells us how many bytes
- we need to store one gadget.
- Then we create the space as a global array:
-
- DEF buf[BUFSIZE]:ARRAY, next
-
- we need the variable next as a sort of dummy with the Gadget() function.
- now we can start creating our gadgets:
-
- next:=Gadget(buf,NIL,1,0,20,20,100,'Gadget 1')
- next:=Gadget(next,buf,2,0,20,35,100,'Gadget 2')
- next:=Gadget(next,buf,3,0,20,50,100,'Gadget 3')
-
- and so our complete program will look like:
-
-
-
- /* Opening a window with gadgets in E */
-
- MODULE 'intuition/intuition'
-
- CONST BUFSIZE=GADGETSIZE*3, IFLAGS=IDCMP_CLOSEWINDOW+IDCMP_GADGETUP
-
- DEF buf[BUFSIZE]:ARRAY,next,w,gad:PTR TO gadget
-
- PROC main()
- next:=Gadget(buf,NIL,1,0,20,20,100,'Gadget 1')
- next:=Gadget(next,buf,2,0,20,35,100,'Gadget 2')
- next:=Gadget(next,buf,3,0,20,50,100,'Gadget 3')
- IF w:=OpenW(20,11,300,100,IFLAGS,$F,'My first window in E!',NIL,1,buf)
- WHILE WaitIMessage(w)<>IDCMP_CLOSEWINDOW
- gad:=MsgIaddr()
- TextF(20,80,'You pressed gadget #\d',gad.userdata)
- ENDWHILE
- CloseW(w)
- ENDIF
- ENDPROC
-
-
-
- As you will notice, we now go the straight way of using our intuition
- constants by including the module. We define the constant IFLAGS for
- all the flags we need: this is now one more, as we also want to hear
- about the user pressing gadgets. The OpenW() call has also changed a
- little, as the last argument for gadgets, which was NIL in our previous
- example, is now filled. Because we now have to respond to two types
- of events, our WaitIMessage() construction has changed: the function
- returns the class of the event which we didn't use before. Now we'll
- only do the WHILE loop for pressed gadgets. We can get a pointer to
- the object that caused the event (in our case always a gadget), and
- we can look in the .userdata field of the gadget to see which one
- was pressed. The value that we find here is exactly the same as what
- we wrote as third argument to Gadget(), thus 1,2 or 3. then we
- write that value to our window with the TextF() function.
-
- Note that to be able to read fields of a structure relative to a
- pointer like "gad", we need to tell the compiler what kind of object
- it will be pointing to. We do this with the declaration:
-
- DEF gad:PTR TO gadget
-
- "gad" will still be a variable like the ones we declare like "DEF gad",
- but the compiler will now enable us to use it as an object just like
- one we create ourselves.
-
- Of course there are lots of other ways to process events. one way is
- to make a loop, and have each item receive one message:
-
- class:=WaitIMessage(w)
-
- and then use a "SELECT class" statement to check out all the
- different types of classes.
-
- Now we have a pretty good picture of how to write Intuition front-ends
- to our programs. Another way of creating even more professional
- interfaces that works remarkably well with E is using the 2.0
- gadtools library. Using immediate lists and typed lists, you can
- create all sorts of gadgets (not just boolean gadgets) with little
- more machinery than we used before.
- See GadToolsDemo.e for how to create interfaces like that.
-
- To give just a tiny example of how we can write a program that uses
- those lists in a powerful fashion we'll write a program that puts up
- a requester with a message from the command-line arguments, and returns
- 0 or 5 (WARN) to dos, according to the gadget selected:
-
-
- PROC main() RETURN EasyRequestArgs(0,[20,0,0,arg,'ok|cancel'],0,NIL)*5
-
-
- Yes, indeed, that's the whole program. Note that it only runs on 2.0
- or higher. save as 'select.e', and compile 'ec select'. now run:
-
- 1> select Select "ok" if you wish to perform this action
-
- This would present the requester and return a value suitable for
- tests in scripts ("IF WARN ...").
-
- The compactness of this example lies in the fact that normally functions
- like EasyRequestArgs get pointers to structures as arguments,
- which have to be built up bit by bit and then given as argument to the
- function (see the C-example for this function in the RKRM's to see
- what I mean). In E there's the list-feature which is unique for
- procedural programming languages which enables you to build complex
- data structures 'on-the-fly' without separately defining them and
- making identifiers for them.
-
- In this case we have created a structure of type "easystruct" (which is
- an object defined in 'intuition/intuition.m'), that is the second argument
- to the function, like:
-
- [20,0,0,arg,'ok|cancel']:easystruct
-
- The :<type> specifies the type of the data structure; default is :LONG,
- and as all fields of easystruct are again of type LONG we left the ":"
- spec. out. Always note that the value of such expressions is a pointer
- to the data structure in memory.
-
- As a second example we'll use taglists: we'll open a screen in 2.0
- fashion with OpenScreenTaglist():
-
- s:=OpenScreenTagList(0,[SA_DEPTH,8,SA_DISPLAYID,$8020,0])
-
- As you see, we need not build up a taglist structure separately,
- nor need we link any varargs hacks.
-
-
- Next we'll be watching how to use library calls other than those
- built in (like EasyRequestArgs() we just saw from intuition):
- We'll be using the "asl.library" to pop up a filerequester
- (again, this is 2.0 and up only). This example is a short version
- of 'AslDemo.e', and is also featured in reference.doc in the
- modules chapter. Here we'll explain how it works.
-
-
- MODULE 'Asl', 'libraries/Asl'
-
- PROC main()
- DEF req:PTR TO filerequestr
- IF aslbase:=OpenLibrary('asl.library',37)
- IF req:=AllocFileRequest()
- IF RequestFile(req) THEN WriteF('File: "\s" in "\s"\n',req.file,req.dir)
- FreeFileRequest(req)
- ENDIF
- CloseLibrary(aslbase)
- ENDIF
- ENDPROC
-
-
- Until recently, we only used modules to load definitions of constants
- and objects; now we'll use modules to define new function calls.
-
- as you see from the module statement, the modules that define
- library calls are in the root of emodules: while all other definitions
- are in the specific directories (read all about that in reference.doc).
-
- Such a module provides us with a variable for the base address of
- the library: usually this is <libname>+"base". in this example,
- we have to fill the variable aslbase with a correct value before we
- can use its functions, and at the end we have to close it again.
- As you can see, the actual usage of the library is very simple.
- To hear about the result, we need to check our requester structure
- that we got returned from AllocFileRequest() for the members
- req.file and req.dir . RequestFile() returns TRUE or FALSE depending
- on whether a file was selected in the first place.
-
- There are lots of sources in the examples/ dir that show how to
- interface with other libraries in a similar fashion, for example
- with the great "ReqTools.library".
-
-
- In our next example we'll be developing a small utility from
- 'sources/utilities' step by step: DirQuick.e.
- It is a nice and short no-nonsense three column directory-lister.
-
-
- MODULE 'dos/dos'
-
- PROC main()
- DEF info:fileinfoblock,lock,ok,c=0
- IF lock:=Lock(arg,-2)
- IF Examine(lock,info)
- IF info.direntrytype>0
- WriteF('Directory of: \s\n',info.filename)
- WHILE ExNext(lock,info)
- IF info.direntrytype>0
- WriteF('\e[1;32m\l\s[25]\e[0;31m',info.filename)
- ELSE
- WriteF('\l\s[17] \r\d[7]',info.filename,info.size)
- ENDIF
- WriteF(IF c++=2 THEN (c:=0)+'\n' ELSE ' ')
- ENDWHILE
- IF c THEN WriteF('\n')
- ELSE
- WriteF('No Dir!\n')
- ENDIF
- ENDIF
- UnLock(lock)
- ELSE
- WriteF('What ?!?\n')
- ENDIF
- ENDPROC
-
- We need only one module, 'dos/dos.m' to tell us about the object
- "fileinfoblock". Then we encounter something new: our variable
- "info" is not a pointer, but has as type an object. Such a declaration
- gives us space on the stack for a complete fileinfoblock structure.
- To get to the directory, we have to try to lock its name, and then
- examine it. That's where our "info" block comes in: the function
- Examine() fills our structure with data it reads from disk. Before
- reading all directory entries in this fashion with ExNext(), we'll
- have to check if the block is a directory (if direntrytype>0).
- With each entry we display colour #2 for directories, and
- normal display (with filesize) for the files. Compare those format-
- strings with the output, and with the string-formatting descriptions
- in 'reference.doc'. With the variable "c" we check how many columns we
- did so far: after every 3 we write a <lf> ('\n').
-
-
- That's it for the tutorial: to learn more, read all the documentation
- thoroughly, and study the examples. But most of all: try your own
- programs!
-