= Program Development under InfernoRoger Peppé <rog@vitanuova.com> == Introduction Inferno provides a set of programs that, used in combination, providea powerful development environment in which to write Limbo programs.`limbo`(1) is the compiler for the Limbo language; there are versionsthat run inside and outside the Inferno environment. `acme`(1) isan integrated window system and editor, and the preferred source-codeediting tool within Inferno. The Limbo debugger, `wm-debug`(1), allowsinteractive inspection of running Limbo programs. `stack`(1) allows aquick inspection of the execution stack of a currently running process. == Getting started This document assumes that you have already managed to install Infernoand have managed to obtain an Inferno window, running the Inferno windowmanager, `wm`(1). The document _Installing Inferno_ in this volume hasdetails on this. If running within emu, it is worth giving Inferno aslarge a window as possible, as it cannot be resized later. This paperassumes that you are using a three-button mouse, as it is not feasibleto use Acme without a three-button mouse. (if you have a two buttonmouse with a 'mouse wheel', the wheel can be used as the middle button).The first thing to do is to get Acme going. By clicking on the VitaNuova logo at the bottom left of the window, you can display a menunaming some preconfigured commands. If this has an ``Acme'' entry, thenjust clicking on that entry will start acme. If not, then click on the``Shell'' entry, and type acme to start it up. The Acme window should then appear, filling most of thescreen (the window manager toolbar should still be visible). == Acme basics For a general overview and the rationale behind Acme, see ``Acme: A UserInterface for Programmers'', elsewhere in this volume, and for detaileddocumentation, see `acme`(1). The basics are as follows: Acme windows are text-only and organised into columns. A distinctivefeature of Acme is that there are no graphical title bars to windows;instead, each window (and additionally each column, and the whole Acmewindow itself) has a textual _tag_ , which can be edited at will, andis initially primed to contain a few appropriate commands. An Acme command is just represented by text; any textual command wordmay be executed simply by clicking with the middle mouse button on theword. (See ``Acme mouse commands'', below). If Acme recognizes theword that has been clicked on as one of its internal commands (e.g. Put,Undo), then it will take the appropriate action; otherwise it will runthe text as a shell command. (See `sh`(1)). == Acme mouse commands Mouse usage within Acme is somewhat more versatile than in most otherwindow systems. Each of the three mouse buttons has its own action, andthere are also actions bound to _chords_ of mouse buttons (i.e. mousebuttons depressed simultaneously). Mouse buttons are numbered fromleft (1) to right (3). Button 1 follows similar conventions to otherwindow systems - it selects text; a double click will select a line ifat the beginning or end of a line, or match brackets if on a bracketcharacter, or select a word otherwise. Button 2, as mentioned above,executes an Acme command; a single click with button 2 will execute thesingle word under the click, otherwise the swept text will be executed.Button 3 is a general ``look'' operator; if the text under the clickrepresents a filename, then Acme will open a new window for the fileand read it in, otherwise it will search within the current window forthe next occurrence of the text. Clicking button 2 or button 3 on sometext already selected by button 1 causes the click to refer exactly tothe text selected, rather than gathering likely-looking characters fromaround the click as is the default. There are two mouse chord sequences which are commonly used in Acme (andyou will find that some other programs in the system also recognise thesesequences, e.g. `wm-sh`(1)). They are both available once some texthas been selected by dragging the mouse with button 1, but before thebutton has been released. At this point, touching button 2 will deletethe selected text and save it in Acme's _snarf_ buffer; clicking button 3replaces the selected text with the contents of the snarf buffer. Beforebutton 1 has been released, these two buttons reverse each other'sactions, so, for example, selecting some text with button 1, keepingbutton 1 held down, then clicking button 2 and button 3 in succession,will save the selected text in the snarf buffer while leaving the originalintact. The following table summarises the mouse commands in Acme: .Acme mouse command summary B1 Select text. B2 Execute text. B3 Open file or search for text. B1-B2 Cut text. B1-B3 Paste text. B2-B3 Cancel the pending B2 action. B3-B2 Cancel the pending B3 action. == Scrolling and resizing Acme windows The scroll bars in Acme are somewhat different from conventionalscroll bars (including the scroll bars found in other parts ofInferno). Clicking, or dragging, with button-2 on the scrollbar actsthe most like the conventional behaviour, namely that the further downthe scroll bar you click, the further down the file you are shown. True to form, however, Acme doesn't omit to make the other buttonsuseful: button-1 and button-3 move backwards and forwards through thefile respectively. The nearer the top of the scrollbar the mouse, theslower the movement. Holding one of these buttons down on the scrollbarwill cause the scrolling motion to auto-repeat, so it is easy to scrollgently through the entire file, for instance. The small square at the top left of each Acme window is the handle forresizing the window. Dragging this square from one place to another(within Acme) will move the window to the new place. A single buttonclick in this square will grow the window: button 1 grows it a little bit;button 2 grows it as much as possible without obscuring the other windowtitles in the column; button 3 grows it so it covers the whole column(all other windows in the column are obscured). == Creating a new file All Limbo programs are composed of _modules_ and each module is storedin its own file. To write a Limbo program, you need to write at leastone module, the Limbo _source file_ , which will then be compiled intoDis code which can then be run by the Inferno Virtual Machine (VM).The first step is to decide where to store the file. When Acme startsup, it creates a new window containing a list of all the files in thedirectory in which it was started (usually your home directory). As aconsequence of the mouse rules above, a click of button-3 on any of thosefilenames in that window will open a new window showing that file or,if it is a directory, a list of the files and directories it contains. An important aspect in Acme's mouse commands, is that the commandis interpreted _relative to the window's current directory_, wherethe current directory is determined from the filename in the window'stag. For instance, Acme commands executed in the tag or body of a windowon the file `/usr/joebloggs/myfile.txt` would run in the directory`/usr/joebloggs`. So, to create a new file in Acme, first open the directory in which tocreate the file. (If this is your home directory, then it's probablyalready on the screen; otherwise, you can just type (anywhere) the nameof the directory, and button-3 click on it. If the directory does notexist, then no window will be created. Then, within the directory'swindow or its tag, choose a name, _filename_ , for your file (I'll use`myprog` from here on, for explanatory convenience) , type the text: New _filename_.b select this text (the Escape key can also be used to highlight text thatyou have just typed), and button-2 click on it. This should create anew empty window in which you can edit your Limbo source file. It willalso create a window giving a warning that the file does not currentlyexist - you can get rid of this by clicking with button-2 on the text`Del` in the tag of that window. == Editing the source file You can now edit text in the new window. Type in the following program: implement Myprog; include "sys.m"; sys: Sys; include "draw.m"; Myprog: module { init: fn(nil: ref Draw->Context, argv: list of string); }; init(nil: ref Draw->Context, argv: list of string) { sys = load Sys Sys->PATH; sys->print("Hello, world\en"); } When typing it in, note that two new commands have appeared in the tag ofthe new window: `Put` and `Undo`. `Put` saves the file; `Undo` undoesthe last change to the file, and successive executions of `Undo` willmove further back in time. In case you move too far back accidentally,there is also `Redo`, which redoes a change that you have just undone.Changes in the body of any window in Acme can be undone this way. Click with button-2 on the `Put` command, and the file is now savedand ready to be compiled. If you have problems at this point (say Acmecomplains about not being able to write the file), you have probablychosen an inappropriate directory, one in which you do not have writepermission, in which to put the file. In this case you can change thename of the file simply by editing its name in the window's tag, andclicking on `Put` again. == Compiling the source file Now, you are in a position to compile the Limbo program. Although youcan execute the Limbo compiler directly from the tag of the new file'swindow, it is usually more convenient to do it from a shell window. Tostart a shell window, type ''win'' at the right of the tag of thenew file's window, select it, and click with button-2 on it. A newwindow should appear showing a shell prompt (usually "`; `" or "`% `".At this, you can type any of the commands mentioned in Section 1 of theProgrammer's Manual. Note that, following Acme's usual rule, the shellhas started up in the same directory as the new file; typing lc at the prompt will show all the files in the directory, includinghopefully the newly written Limbo file. Type the following command to the shell: limbo -g myprog.b If you typed in the example program correctly, then you'll get a shortpause, and then another shell prompt. This indicates a successfulcompilation (no news is good news), in which case you will now havetwo new files in the current directory, `myprog.sbl` and `myprog.dis`.The `-g` option to the `limbo` command directed it to produce the`myprog.sbl` file, which contains symbolic information relatingthe source code to the Dis executable file. The `myprog.dis` filecontains the actual executable file. At this point, if you type `lc`,to get a listing of the files in the current directory, and then clickwith button-2 on the `myprog.dis` file, and you should see the output``Hello, world''. You could also just type `myprog` at the shell prompt. If you are normal, however, the above compilation probably failed becauseof some mistyped characters in the source code; and for larger newlycreated programs, in my experience, this is almost invariably the case.If you got no errors in the above compilation, try changing `sys->print`to `print`, saving the file again, and continue with the next section. == Finding compilation errors When the Limbo compiler finds errors, it prints the errors, one per line,each one looking something like the following: myprog.b:13: print is not declared This shows the filename where the error has occurred, its line numberin the file, and a description of the error. Acme's button-3 mouseclicking makes it extremely easy to see where in the source code theerror has occurred. Click with button-3 anywhere in the filename onthe line of the compilation error, and Acme will automatically take thecursor to the file of that name and highlight the correct line. If there had been no currently appropriate open Acme window representingthe file, then a new one would be created, and the appropriate lineselected. Edit `myprog.b` until you have a program that compiles successfully andproduces the ``Hello, world'' output. For a program as simple as this,that's all there is to it - you now know the essential stages involvedin writing a Limbo program; there's just the small matter of absorbingthe Limbo language and familiarising yourself with the libraries (``TheLimbo Programming Language'' elsewhere in this volume, and `intro`(2)are the two essential starting points here). == Finding run-time errors For larger programs, there is the problem of programs that dieunexpectedly with a run-time error. This will happen when, for instance,a Limbo program uses a reference that has not been initialised, or refersto an out-of-bounds array element. When a Limbo program dies with a run-time exception, it does not go awaycompletely, but remains hanging around, dormant, in a _broken_ state;the state that it was in when it died may now be examined at leisure. Toexperiment with this, edit the Myprog module above to delete the line thatloads the `Sys` module (`sys = load Sys` ...), and recompile the program. This time when you come to run `myprog`, it will die, printing amessage like: sh: 319 "Myprog":module not loaded The number `319` is the _process id_ (or just _pid_) of the brokenprocess. The command `ps` , which shows all currently running processes,can be used at this point - you will see a line like this: 319 245 rog broken 64K Myprog The first number is the pid of the process; the second is the _processgroup_ id of the process; the third field gives the owner of the process;the fourth gives its state (broken, in this case); the fifth shows thecurrent size of the process, and the last gives the name of the modulethat the process is currently running. The `stack` command can be used to quickly find the line at which theprocess has broken; type: stack _pid_ where _pid_ is the number mentioned in the ``module not loaded'' message(319 in this case). It produces something like the following output: init() myprog.b:12.1, 29 unknown fn() Module /dis/sh.dis PC 1706 As usual, a quick button-3 click on the `myprog.b` part of the firstline takes you to the appropriate part of the source file. The reasonthat the program has died here is that, in Limbo, all external modulesmust be explicitly loaded before they can be used; to try to call anuninitialised module is an error and causes an exception. == More sophisticated debugging `stack` is fine for getting a quick summary of the state in which aprogram has died, but there are times when such a simple post-mortemanalysis is inadequate. The `wm/deb` (see `wm-deb`(1)) commandprovides an interactive windowing debugger for such occasions. It runsoutside Acme, in the default window system. A convenient way to startdebugging an existing process is to raise `wm/task` (``Task Manager''on the main menu), select with the mouse the process to debug, and click``Debug''. This will start `wm/deb` on that process. Before it can start,the debugger will ask for the names of any source files that it hasnot been able to find (usually this includes the source for the shell,as the module being debugged is often started by the shell, and so thetop-level function will be in the shell's module). `Wm/deb` can be used to debug multiple threads, to inspect the datastructures in a thread, and to interactively step through the runningof a thread (single stepping). See `wm-deb`(1) for details.
= Program Development under Inferno
Roger Peppé <rog@vitanuova.com>
== Introduction
Inferno provides a set of programs that, used in combination, provide
a powerful development environment in which to write Limbo programs.
`limbo`(1) is the compiler for the Limbo language; there are versions
that run inside and outside the Inferno environment. `acme`(1) is
an integrated window system and editor, and the preferred source-code
editing tool within Inferno. The Limbo debugger, `wm-debug`(1), allows
interactive inspection of running Limbo programs. `stack`(1) allows a
quick inspection of the execution stack of a currently running process.
== Getting started
This document assumes that you have already managed to install Inferno
and have managed to obtain an Inferno window, running the Inferno window
manager, `wm`(1). The document _Installing Inferno_ in this volume has
details on this. If running within emu, it is worth giving Inferno as
large a window as possible, as it cannot be resized later. This paper
assumes that you are using a three-button mouse, as it is not feasible
to use Acme without a three-button mouse. (if you have a two button
mouse with a 'mouse wheel', the wheel can be used as the middle button).
The first thing to do is to get Acme going. By clicking on the Vita
Nuova logo at the bottom left of the window, you can display a menu
naming some preconfigured commands. If this has an ``Acme'' entry, then
just clicking on that entry will start acme. If not, then click on the
``Shell'' entry, and type
acme
to start it up. The Acme window should then appear, filling most of the
screen (the window manager toolbar should still be visible).
== Acme basics
For a general overview and the rationale behind Acme, see ``Acme: A User
Interface for Programmers'', elsewhere in this volume, and for detailed
documentation, see `acme`(1). The basics are as follows:
Acme windows are text-only and organised into columns. A distinctive
feature of Acme is that there are no graphical title bars to windows;
instead, each window (and additionally each column, and the whole Acme
window itself) has a textual _tag_ , which can be edited at will, and
is initially primed to contain a few appropriate commands.
An Acme command is just represented by text; any textual command word
may be executed simply by clicking with the middle mouse button on the
word. (See ``Acme mouse commands'', below). If Acme recognizes the
word that has been clicked on as one of its internal commands (e.g. Put,
Undo), then it will take the appropriate action; otherwise it will run
the text as a shell command. (See `sh`(1)).
== Acme mouse commands
Mouse usage within Acme is somewhat more versatile than in most other
window systems. Each of the three mouse buttons has its own action, and
there are also actions bound to _chords_ of mouse buttons (i.e. mouse
buttons depressed simultaneously). Mouse buttons are numbered from
left (1) to right (3). Button 1 follows similar conventions to other
window systems - it selects text; a double click will select a line if
at the beginning or end of a line, or match brackets if on a bracket
character, or select a word otherwise. Button 2, as mentioned above,
executes an Acme command; a single click with button 2 will execute the
single word under the click, otherwise the swept text will be executed.
Button 3 is a general ``look'' operator; if the text under the click
represents a filename, then Acme will open a new window for the file
and read it in, otherwise it will search within the current window for
the next occurrence of the text. Clicking button 2 or button 3 on some
text already selected by button 1 causes the click to refer exactly to
the text selected, rather than gathering likely-looking characters from
around the click as is the default.
There are two mouse chord sequences which are commonly used in Acme (and
you will find that some other programs in the system also recognise these
sequences, e.g. `wm-sh`(1)). They are both available once some text
has been selected by dragging the mouse with button 1, but before the
button has been released. At this point, touching button 2 will delete
the selected text and save it in Acme's _snarf_ buffer; clicking button 3
replaces the selected text with the contents of the snarf buffer. Before
button 1 has been released, these two buttons reverse each other's
actions, so, for example, selecting some text with button 1, keeping
button 1 held down, then clicking button 2 and button 3 in succession,
will save the selected text in the snarf buffer while leaving the original
intact. The following table summarises the mouse commands in Acme:
.Acme mouse command summary
B1 Select text.
B2 Execute text.
B3 Open file or search for text.
B1-B2 Cut text.
B1-B3 Paste text.
B2-B3 Cancel the pending B2 action.
B3-B2 Cancel the pending B3 action.
== Scrolling and resizing Acme windows
The scroll bars in Acme are somewhat different from conventional
scroll bars (including the scroll bars found in other parts of
Inferno). Clicking, or dragging, with button-2 on the scrollbar acts
the most like the conventional behaviour, namely that the further down
the scroll bar you click, the further down the file you are shown.
True to form, however, Acme doesn't omit to make the other buttons
useful: button-1 and button-3 move backwards and forwards through the
file respectively. The nearer the top of the scrollbar the mouse, the
slower the movement. Holding one of these buttons down on the scrollbar
will cause the scrolling motion to auto-repeat, so it is easy to scroll
gently through the entire file, for instance.
The small square at the top left of each Acme window is the handle for
resizing the window. Dragging this square from one place to another
(within Acme) will move the window to the new place. A single button
click in this square will grow the window: button 1 grows it a little bit;
button 2 grows it as much as possible without obscuring the other window
titles in the column; button 3 grows it so it covers the whole column
(all other windows in the column are obscured).
== Creating a new file
All Limbo programs are composed of _modules_ and each module is stored
in its own file. To write a Limbo program, you need to write at least
one module, the Limbo _source file_ , which will then be compiled into
Dis code which can then be run by the Inferno Virtual Machine (VM).
The first step is to decide where to store the file. When Acme starts
up, it creates a new window containing a list of all the files in the
directory in which it was started (usually your home directory). As a
consequence of the mouse rules above, a click of button-3 on any of those
filenames in that window will open a new window showing that file or,
if it is a directory, a list of the files and directories it contains.
An important aspect in Acme's mouse commands, is that the command
is interpreted _relative to the window's current directory_, where
the current directory is determined from the filename in the window's
tag. For instance, Acme commands executed in the tag or body of a window
on the file `/usr/joebloggs/myfile.txt` would run in the directory
`/usr/joebloggs`.
So, to create a new file in Acme, first open the directory in which to
create the file. (If this is your home directory, then it's probably
already on the screen; otherwise, you can just type (anywhere) the name
of the directory, and button-3 click on it. If the directory does not
exist, then no window will be created. Then, within the directory's
window or its tag, choose a name, _filename_ , for your file (I'll use
`myprog` from here on, for explanatory convenience) , type the text:
New _filename_.b
select this text (the Escape key can also be used to highlight text that
you have just typed), and button-2 click on it. This should create a
new empty window in which you can edit your Limbo source file. It will
also create a window giving a warning that the file does not currently
exist - you can get rid of this by clicking with button-2 on the text
`Del` in the tag of that window.
== Editing the source file
You can now edit text in the new window. Type in the following program:
implement Myprog;
include "sys.m";
sys: Sys;
include "draw.m";
Myprog: module {
init: fn(nil: ref Draw->Context, argv: list of string);
};
init(nil: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
sys->print("Hello, world\en");
}
When typing it in, note that two new commands have appeared in the tag of
the new window: `Put` and `Undo`. `Put` saves the file; `Undo` undoes
the last change to the file, and successive executions of `Undo` will
move further back in time. In case you move too far back accidentally,
there is also `Redo`, which redoes a change that you have just undone.
Changes in the body of any window in Acme can be undone this way.
Click with button-2 on the `Put` command, and the file is now saved
and ready to be compiled. If you have problems at this point (say Acme
complains about not being able to write the file), you have probably
chosen an inappropriate directory, one in which you do not have write
permission, in which to put the file. In this case you can change the
name of the file simply by editing its name in the window's tag, and
clicking on `Put` again.
== Compiling the source file
Now, you are in a position to compile the Limbo program. Although you
can execute the Limbo compiler directly from the tag of the new file's
window, it is usually more convenient to do it from a shell window. To
start a shell window, type ''win'' at the right of the tag of the
new file's window, select it, and click with button-2 on it. A new
window should appear showing a shell prompt (usually "`; `" or "`% `".
At this, you can type any of the commands mentioned in Section 1 of the
Programmer's Manual. Note that, following Acme's usual rule, the shell
has started up in the same directory as the new file; typing
lc
at the prompt will show all the files in the directory, including
hopefully the newly written Limbo file.
Type the following command to the shell:
limbo -g myprog.b
If you typed in the example program correctly, then you'll get a short
pause, and then another shell prompt. This indicates a successful
compilation (no news is good news), in which case you will now have
two new files in the current directory, `myprog.sbl` and `myprog.dis`.
The `-g` option to the `limbo` command directed it to produce the
`myprog.sbl` file, which contains symbolic information relating
the source code to the Dis executable file. The `myprog.dis` file
contains the actual executable file. At this point, if you type `lc`,
to get a listing of the files in the current directory, and then click
with button-2 on the `myprog.dis` file, and you should see the output
``Hello, world''. You could also just type `myprog` at the shell prompt.
If you are normal, however, the above compilation probably failed because
of some mistyped characters in the source code; and for larger newly
created programs, in my experience, this is almost invariably the case.
If you got no errors in the above compilation, try changing `sys->print`
to `print`, saving the file again, and continue with the next section.
== Finding compilation errors
When the Limbo compiler finds errors, it prints the errors, one per line,
each one looking something like the following:
myprog.b:13: print is not declared
This shows the filename where the error has occurred, its line number
in the file, and a description of the error. Acme's button-3 mouse
clicking makes it extremely easy to see where in the source code the
error has occurred. Click with button-3 anywhere in the filename on
the line of the compilation error, and Acme will automatically take the
cursor to the file of that name and highlight the correct line.
If there had been no currently appropriate open Acme window representing
the file, then a new one would be created, and the appropriate line
selected.
Edit `myprog.b` until you have a program that compiles successfully and
produces the ``Hello, world'' output. For a program as simple as this,
that's all there is to it - you now know the essential stages involved
in writing a Limbo program; there's just the small matter of absorbing
the Limbo language and familiarising yourself with the libraries (``The
Limbo Programming Language'' elsewhere in this volume, and `intro`(2)
are the two essential starting points here).
== Finding run-time errors
For larger programs, there is the problem of programs that die
unexpectedly with a run-time error. This will happen when, for instance,
a Limbo program uses a reference that has not been initialised, or refers
to an out-of-bounds array element.
When a Limbo program dies with a run-time exception, it does not go away
completely, but remains hanging around, dormant, in a _broken_ state;
the state that it was in when it died may now be examined at leisure. To
experiment with this, edit the Myprog module above to delete the line that
loads the `Sys` module (`sys = load Sys` ...), and recompile the program.
This time when you come to run `myprog`, it will die, printing a
message like:
sh: 319 "Myprog":module not loaded
The number `319` is the _process id_ (or just _pid_) of the broken
process. The command `ps` , which shows all currently running processes,
can be used at this point - you will see a line like this:
319 245 rog broken 64K Myprog
The first number is the pid of the process; the second is the _process
group_ id of the process; the third field gives the owner of the process;
the fourth gives its state (broken, in this case); the fifth shows the
current size of the process, and the last gives the name of the module
that the process is currently running.
The `stack` command can be used to quickly find the line at which the
process has broken; type:
stack _pid_
where _pid_ is the number mentioned in the ``module not loaded'' message
(319 in this case). It produces something like the following output:
init() myprog.b:12.1, 29
unknown fn() Module /dis/sh.dis PC 1706
As usual, a quick button-3 click on the `myprog.b` part of the first
line takes you to the appropriate part of the source file. The reason
that the program has died here is that, in Limbo, all external modules
must be explicitly loaded before they can be used; to try to call an
uninitialised module is an error and causes an exception.
== More sophisticated debugging
`stack` is fine for getting a quick summary of the state in which a
program has died, but there are times when such a simple post-mortem
analysis is inadequate. The `wm/deb` (see `wm-deb`(1)) command
provides an interactive windowing debugger for such occasions. It runs
outside Acme, in the default window system. A convenient way to start
debugging an existing process is to raise `wm/task` (``Task Manager''
on the main menu), select with the mouse the process to debug, and click
``Debug''. This will start `wm/deb` on that process. Before it can start,
the debugger will ask for the names of any source files that it has
not been able to find (usually this includes the source for the shell,
as the module being debugged is often started by the shell, and so the
top-level function will be in the shell's module).
`Wm/deb` can be used to debug multiple threads, to inspect the data
structures in a thread, and to interactively step through the running
of a thread (single stepping). See `wm-deb`(1) for details.