All the examples so far have presumed you are executing code interactively.
This is great for learning and experimentation, but for serious coding you need to be able to edit up archival code in a text editor, save it in a host file, and compile it into the db.
The purpose of this section is to take you quickly through the detailed mechanics of one way of establishing a serious Muq MUF project of your own: Where to put the source files, how to compile them and so forth. It presumes you are running your own Muq developmental server, with full Unix level access to the development account.
The purpose of this section is not to cover all the possibilities, just to present one simple, workable approach.
We will do the development using a development version of the Muq executable in `muq/c/muq', and a test version of the database, in `muq/c/muq-*'. This frees us up to do whatever debugging hacks we want on either, since the production server normally uses the executable `muq/bin/muq' and keeps the database in `muq/db/*'.
Start by installing the full source distribution, if you have not already, and doing
cynbe@chee muq/c> make cynbe@chee muq/c> make check
to verify that you can compile the server from scratch. I usually do
cynbe@chee muq/c> rm -rf muq-* cynbe@chee muq/c> rm -rf #check.tmp#
next to clean out the debris from make check
.
Now do
% muq-db-c [...]
to create a test db in `muq/c/muq-*'. Test that it works as expected interactively:
cynbe@chee muq/c> ./muq [...] Hints from me$s.loginHints$h[1,2...]: For configuration menu do: config To exit server from console do: <CTRL>-C or rootShutdown root: 2 2 + root: 4 rootShutdown [...] cynbe@chee muq/c>
(For what it is worth, I usually do all this sort of stuff
from inside an emacs shell buffer: M-x shell
in
emacs. This makes editing and re-trying expressions trivial,
and also allows pasting in full function definitions direct
from files in other emacs buffers.)
Now, let's create our own MUF source file establishing our own package, and compile it into the db.
MUF source code lives in MUQ/PKG/. When MUQ/BIN/MUQ-DB-C builds a database from scratch, it compiles the files in ascending order of numerical prefix, which ensures that critical system files are loaded before other files that depend on them.
Prefix numbers 000-499
are reserved for the standard Muq
libraries, while prefix numbers 500-999
are reserved for local
source libraries. By obeying this convention, you ensure that your
files won't be clobbered by files in future source distributions, and
that your files will be loaded after the standard Muq facilities they
depend on are in place.
Use your favorite text editor to create a file muq/pkg/500-mystuff.t
containing
@example @c "mystuff" inPackage 2001 -->constant myconst 2002 --> _myvar : myfn "Hello, world!\n" , ; myfn @end example
Explanation:
@c
!)
"mystuff" inPackage
creates a "package" (namespace) named
"mystuff", into which the following code compiles. If the named
package already exists, it is merely selected as the current
package. inPackage
is a bit like Unix cd
.
2001 -->constant myconst
is an example of creating a constant.
2002 --> _myvar
is an example of creating a global variable.
: myfn "Hello, world!\n" , ;
is an example of defining a
function.
myfn
is an example of invoking the above function during
compilation -- I've included this as a reminder that you can
execute arbitrary code during these "compiles", since the input
code is in fact being evaluated line by line just as it is during
interactive sessions. You'll see the result print out during the
nest step.
(I usually test code like this interactively before saving it in a file, but we'll skip that step here for brevity. One reason I do this is because the interactive error diagnostics are not as bad as the batchmode compile diagnostics, which currently often consist of the server hanging...)
Now load that file into the db:
cynbe@chee muq/c> muq-c-lib 500-mystuff [...] cynbe@chee muq/c>
For a simple example like this, of course, it would be easier -- and equivalent -- to simply skip the source file editing and enter the code interactively, but for a realistic project consisting of a dozen or two source files each containing hundreds or thousands of lines of MUF source, batchmode compilation is a virtual necessity.
Now we check that the db has been updated as we expect:
cynbe@chee muq/c> ./muq [...] Hints from me$s.loginHints$h[1,2...]: For configuration menu do: config To exit server from console do: <CTRL>-C or rootShutdown root: "mystuff" inPackage mystuff: myconst mystuff: 2001 pop mystuff: _myvar mystuff: 2002 pop mystuff: myfn Hello, world! mystuff: rootShutdown [...] cynbe@chee muq/c>
Now let's create the world's simplest shell. Edit muq/pkg/500-mystuff.t
to contain
@example @c "mystuff" inPackage : ]shell { [] -> @ } do{ t @.standardInput readStreamPacket[ ]pop "Huh?\n" , } ; @end example
Explanation: This declares ]shell
to be a function accepting one stackblock
and never returning. The do{...}
construct is an infinite loop, and
inside it we eternally read one line from standard input, ignore the result, and
print "Huh?" to standard output.
Again, this example is so simple that we could as easily have defined the function interactively, but we're warming up for cases where the shell might have hundreds or thousands of lines of code.
We could load the file in just as before, using muq-c-lib
, but let us suppose
instead that we've spent a few days thrashing around and prefer now to build a
pristine new database from scratch:
cynbe@chee muq/c> rm -rf muq-* cynbe@chee muq/c> muq-db-c [...] cynbe@chee muq/c>
As you do this, you'll notice that your muq/pkg/500-mystuff.t
file now gets
loaded in last automatically.
Go to the first, previous, next, last section, table of contents.