home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
editor
/
me_cd.arc
/
MUTT.DOC
< prev
next >
Wrap
Lisp/Scheme
|
1988-10-24
|
25KB
|
640 lines
========================================================================
== The Mutt Programming Language Craig Durland 9/88 ==
========================================================================
symbol action
( evaluate expression
) end expression
{ start pgm
} end pgm
SYNTAX
pgm : {exp [exp ...]}, {}
exp : (fcn [args]), (var [args]), var, const, ()
const : number (123, 0xAbC), string ("abc", 'abc'), TRUE, FALSE
arg : block
block : pgm, exp or const
fcn : a Mutt function (such as + or while) or defined function.
======================================================================
Return Values
======================================================================
All blocks return a value. Pgms return the value of the last exp
executed.
The class of a return value can be one of:
BOOLEAN TRUE or FALSE
BLOB a pointer to a block of data
NUMBER 32 bit signed integer
STRING a bunch of characters
VOID a type of nothing
======================================================================
Variables (aka vars)
======================================================================
CREATION
Vars must be created (and allocated) before they can be used.
Global vars are initialized to:
bool: FALSE
INT, int, byte: 0
string: ""
Local vars are NOT initialized (ie they contain garbage when
created).
EVALUATION & ASSIGNMENT
Vars behave like fcns, ie (var) evaluates the var (returns its value)
and (var val) passes val to the var for assignment. (var val)
returns val. If you just want to evaluate a var, you can treat it
like a const - ie var is equivalent to (var). Note, however, doing
this may cause logic bugs if var also happens to be the name of a
const (since the const is locked out). See GOTCHAS. In some cases,
(var) and var don't mean the same thing - see pointer.
Cascading assignment: you can assign multiple vars the same value.
eg (int x y z)(x (y (z 123))) assigns 123 to x, y and z.
eg (int x)(string s 20)(x (atoi (s "123"))) sets s to "123" and x
to 123.
SCOPE
The scope of a variable is the same as the pgm it is allocated in.
A variable allocated outside of a fcn is global - ie anybody can get
at it. Global variables are local to the file (and any included
file(s)) they are allocated in.
For example:
If foo.mut contains:
(int foo) ; declare and allocate global var "foo"
(defun "hoho" {(int bar)(bar foo)})
(foo 123) ; assign 123 to foo
(load "foo.mut") will create a global var foo. (hoho)
creates bar and sets it to 123. hoho is done and bar is deallocated.
foo remains allocated and equal to 123.
======================================================================
Functions and Keywords
======================================================================
Everything in this list is a function except those that have a return
class of zip.
KEYWORD [arg class(s) : return class]
; a comment that extends to the end of the line
(== value value ...) [STRINGs, NUMBERs or BOOLEANs : BOOLEAN]
Test 2 or more items for equality.
You can only compare like types - eg (== "123" 123) is illegal.
eg (== foo 1), (== "one" "two"), (== (+ foo 3) bar 5).
(!= value value) Same as == but nonequality. Only 2 items.
(< value value) [NUMBER NUMBER : BOOLEAN]
(<= value value) [NUMBER NUMBER : BOOLEAN]
(> value value) [NUMBER NUMBER : BOOLEAN]
(>= value value) [NUMBER NUMBER : BOOLEAN]
(+ values) [NUMBERs : NUMBER]
add a bunch of numbers eg (+ 1 2 3 4) => 1+2+3+4
(- values) [NUMBERs : NUMBER]
subtract a bunch of numbers eg (- 1 2 3 4) => 1-2-3-4 = -8
(* values) multiply a bunch of numbers [NUMBERs : NUMBER]
(/ values) divide a bunch of numbers [NUMBERs : NUMBER]
(+= var values) [TOKEN, NUMBERs : NUMBER]
Add value(s) to var and assign it back to the var. Returns the result.
Short hand for (var (+ var values)).
eg (+= foo 1) (+= foo fud 3)
(-= var values) Subtract value from var. [TOKEN, NUMBERs : NUMBER]
(*= var values) Multiply var by value. [TOKEN, NUMBERs : NUMBER]
(/= var values) Divide var by value. [TOKEN, NUMBERs : NUMBER]
"string"
String constant.
Special characters:
\ quote the next character. "\\" => '\' "\^" => '^'
^ convert the next character to a control character (make sure that
the letter is UPPERCASE!). eg "^A"
Note: escape == ^[.
'string'
String constant.
No special characters except '' reduces to ' ie if you need a string
like "don't" use 'don''t'.
This form is handy for regular expressions.
(and values) [BOOLEANs : BOOLEAN]
Logically and a bunch of things
The first FALSE value will terminate (ie the rest of the and will
not be evaluated). eg (and (previous-line) (foo))
(array type name dimensions) [TOKEN TOKEN NUMBERs [TOKEN NUMBERs] : zip]
Create an array. Types allowed: bool, int, INT, string.
(array int x 5 z 7) creates two arrays: x with 5 ints and z with 7.
(array INT y 2 3) creates an array with 2 rows of 3 INTs each.
(array string s 5 80) creates 5 strings that can each hold up to 80
characters.
(x 3 123) sets the third element of x to 123.
(x 3) returns 123.
(arg n) [NUMBER : any class]
Get the nth argument from the parameter list.
The arguments are numbered 0,1...,(nargs)-1
eg (foo 1 "two" (three)) has 3 arguments: numeric 1, string "two" and
whatever fcn three returns. (arg 0) returns 1, (arg 1) returns
"two", (arg 2) returns result of (three). (arg 3), (arg -1), etc
error.
Notes:
You cannot set a arg unless it is a pointer.
See also: ask, defun, nargs.
(ask prompt) [STRING : STRING]
Get the next argument from the argument list; if the argument list is
empty, query the user.
eg if (foo (ask "foo = ")) is in fud then (fud "hoho") will pass "hoho"
to foo. (fud) will cause the message "foo = " to appear and will wait
for a response to assign to foo.
You can force the pgm to query the user with (ask-user). If you do
this, you will need one per ask. The next unmodified ask will behave
as above.
See also: arg, ask-user, atoi.
(ask-user) [zip : VOID]
The next ask will query the keyboard and not the program args.
Turned off after every ask.
See also: ask.
(atoi string) [STRING : NUMBER]
Convert a string to a number. If the string is not numeric,
atoi returns 0.
eg (foo (atoi (ask "A number please: ")))
Note: To convert a number into a string, use concat.
eg (string s 20)(s (concat 123)) will convert numeric 123 into a
string and assign it to s.
See also: ask.
(bool var [var ...]) [TOKENS : zip]
Evaluation & assignment: var, (var value) [TOKEN [BOOLEAN] : BOOLEAN]
Allocate 1 or more boolean variables.
Example: (bool foo)(foo TRUE) ; set foo TRUE
See also: int, string.
(break) [zip : zip]
Get out of the smallest enclosing for or while loop. The loop then
retuns the value of the last block executed.
See also: continue, for, while.
(byte var [var ...]) [TOKENs : zip]
Evaluation & assignment: var, (var value) [TOKEN [NUMBER] : NUMBER]
Allocate 8 bit unsigned integers.
The range of a int is [0 : 255].
Note that a NUMBER may not fit in a byte.
See also: int, INT.
(case test body ...)
[[BOOLEAN, block, [BOOLEAN, block ...]] : last block executed]
Same as nested if's. If the test is TRUE then the next block is
executed and the case is ended. Otherwise the next block is skipped
and the cycle repeats.
eg
(string str 50)(str (ask "str = "))
(case
(== str "one")(msg "number 1")
(== str "foo")(msg "bar")
TRUE (msg str " is not something I know about.")
)
See also: switch
(concat parameters) [anything : STRING]
Concatenate parameters.
eg (concat "foo = " foo) return "foo = 123" if foo is 123 or "123".
(const name value [name value ...]) [TOKEN BOOLEAN|NUMBER|STRING : zip]
Create one or more constants. Used like other constants (such as
numbers, strings, etc).
Precedence: args, local vars, global vars, pgms, consts.
eg
(const foo 123 bar "hoho")
(msg foo bar) ; this prints "123hoho")
Note: Do not use ()'s around constants - they are for functions only.
See GOTCHAS.
(continue) [zip : zip]
Go to the bottom of the smallest enclosing for or while loop.
If in a for loop, this means the inc part.
See also: break, for, while.
(defun name [arglist] [modifier(s)] pgm [name ...])
[TOKEN [([type] TOKEN)...] [TOKENs] pgm : zip]
Define (ie compile) a function. Multiple functions can be defined.
Comments and whitespace have no performance or size penalty, so use
them.
You can optionally name the arg list.
([type] name [dimension(s)] [name [dimension(s)]] ...) ...
INT, int and byte all mean NUMBER (since all numerics are promoted
to NUMBER when passed). Do not include the length when naming
strings. Array subscripts are used to determine the dimension the
passed in array - the first dimension is a placeholder (as in C).
Arg type is optional - if not used, no assumations are made about
the the arg (ie it will be type checked at runtime if needbe). If
an arg is typed, what ever is passed in is assumed to be of the
correct type - it is not checked.
Modifiers are used to put conditions on a function.
modifier : what it does
HIDDEN Only functions defined after this one can reference it.
MAIN This function will be executed when it is loaded.
MAIN is a special function name. It defines a function that is
executed at load time (the same as a function marked MAIN) and can
not be called by anyone. Use MAINs to do things that only need to
be done once before anything else is done (for example, initializing
some variables). Use the modifier MAIN if you also want to be able
to repeat things at other times. There is no limit on the number of
MAIN functions or functions marked MAIN. MAINs are called in the
order that they are defined. Functions marked MAIN are called
alphabetically.
For example:
(defun
foo { (msg "this is foo") } ; (foo) prints "this is foo"
bar (int x y)(string s) ; bar has 3 args: 2 numbers and a string
{
(msg s) ; same as (msg (arg 2))
(msg (+ x y)) ; same as (msg (+ (arg 0)(arg 1)))
}
fred (z) { (msg z (arg 1)) } ; print the first and second args
)
Notes:
You can not assign to an arg unless it is a pointer.
See also: arg, pointer, the example pgms.
(done) [zip : zip]
Exit smallest enclosing pgm.
See also: halt.
FALSE [zip : BOOLEAN]
The boolean constant FALSE.
(floc function-name [args])
[TOKEN|STRING : BLOB] or [TOKEN|STRING any : last block executed]
Return the address of a function or call a function.
Note that the keywords and functions in this list are not "real"
functions - ie they have no address and can't be called from floc.
If given args, a function call is generated. If you want to call a
function that has no args, use () as the args list
(eg "(floc "foobar" ())").
Example:
(defun
add (pointer defun plus) (x y)
{ (msg x " + " y " = " (plus x y)) }
add-two-numbers (int a b) { (+ a b) }
silly-add (a b) { "three" }
)
(add (floc add-two-numbers) 1 2) ; 1 + 2 = 3
(add (floc silly-add) "one" "two") ; one + two = three
Notes:
(floc foobar) returns the address of a already defined function. If
foobar has not been defined yet, an error is generated.
(floc "foobar"): The location of foobar is resolved at runtime. This
is slower and generates more code but always works (if foobar really
is a function and not a typo).
See also: loc, defun, pointer, qsort (in the EXAMPLE PGMS below)
(for init test inc body)
[block, BOOLEAN, block, block : FALSE (unless break used)]
A for next loop: perform init once. Perform body and inc while
test is TRUE. This is equivalent to:
init (while test { body inc })
break will terminate the loop and continue will jump to inc.
For example:
(int j)
(for (j 1) (< j 6) (+= j 1)
{
(if (== j 3)(continue)) ; skip 3
(msg j)
})
prints out: 1 2 4 5
See also: break, continue, while.
(halt) [zip : zip]
Halt the mutt machine.
See also: done.
(goto label) [TOKEN : zip]
Even if you dare, use this with care.
Don't cross defun boundaries.
See also: label.
(if test true [false]) [BOOLEAN, block, block : last block executed]
test and true must exist even if blank (ie "{}" or "()").
eg (if (== foo bar)(msg "foo == bar")(msg "foo != bar"))
(== foo bar)(if () (msg "foo == bar"))
The C conditional expression "condition ? true : false" can be mimiced
with (if condition true false).
(include filename) [TOKEN|STRING : zip]
Compile another file. After filename is compiled, compiling resumes
at the line after the one that contained the include.
ie "(include foo.mut)" is the same as "(include foo ; doo daa"
(INT var [var ...]) [TOKENs : zip]
Evaluation & assignment: var, (var value) [TOKEN [NUMBER] : NUMBER]
Allocate 32 bit integers.
The range of a INT is [-2,147,483,648 : 2,147,483,647].
See also: bool, byte, int, string.
(int var [var ...]) [TOKENs : zip]
Evaluation & assignment: var, (var value) [TOKEN [NUMBER] : NUMBER]
Allocate 16 bit integers. The only advantage of using int instead of
INT is that ints use less storage.
The range of a int is [-32,768 : 32,767].
Note that a NUMBER may not fit in a int.
See also: bool, byte, INT, string.
(label label-name) [TOKEN : zip]
The target of a goto.
eg (label foo)(goto foo) creates an infinite loop.
See also: goto.
(loc var) [TOKEN : BLOB]
Return the address of a variable. In this way you can modify the
variables you pass to functions.
Example:
(int a b c d) ; 4 ints in row. Same order as (array int a 4)
(defun
foo (array int x 2) ; first element of x maps to a
{
(msg "(x 0) = " (x 0)) ; display the value of a
(x 0 456) ; set a to 456
(x 1 111) ; set b to 111
}
bar (pointer int x)
{ (msg "x = " (x)) } ; display the value of a
)
(a 123)(foo (loc a))(msg "a = " a " b = " b)
(bar (loc a))
When this is run it produces:
(x 0) = 123
a = 456 b = 111
x = 456
See also: arg, defun, floc, pointer.
(msg stuff) [any class : STRING]
Prints a message. The list is concatenation of the arg list.
Returns the entire message.
eg (msg "foo = " foo)
(nargs) [zip : NUMBER]
Return the number of parameters passed to the function.
eg (foo 1 "two" (three)) has 3 arguments and (nargs) returns 3.
See also: arg.
(not value) [BOOLEAN : BOOLEAN]
Negate value. eg (not (== foo 1)).
(novalue) [zip : VOID]
(or value ...) [BOOLEANs : BOOLEAN]
Logically or a bunch of things.
The first TRUE value will terminate (ie the rest of the or will
not be evaluated). eg (or (beginning-of-line) foo)
(pointer type var-names) [TOKEN TOKENs : zip]
Declare pointer vars. You can create pointers to byte, int, INT,
bool, string, and defun (functions).
Assignment: (pv location) where location is a pointer
arg or something returned by loc or floc.
Pointers to data:
To get what a pointer points to: (pv)
Pointers to functions:
(pf) calls function pointed to by pf with no args.
(pf arg1 ... argn) calls pf with n args.
See also: defun, floc, loc.
(push-args n) [NUMBER : last arg]
Push all the args from n back on the stack. The first arg is 0.
For example: If max is a function that takes the maximum of all its args,
and foobar is a function that takes a string and a list of numbers and
you want to find the maximum of those numbers, you could:
(defun foo (string s) { (int m) (m (max (push-args 1))) })
Notes:
push-args can only be used with defined functions plus concat and msg.
For example (+ (push-args 3)) is an error, (msg (push-args 3)) is OK.
If n is greater than the number of args, no args are pushed and no
error is generated.
You can treat push-args like a run-of-the-mill arg - eg (foo 123
(push-args 2) "hoho") and (foo (push-args 4) (push-args 2)) are OK.
(string var length [var length ...]) [TOKEN NUMBER ... : zip]
Evaluation & assignment: var, (var value) [TOKEN [STRING] : STRING]
Allocate some strings of length characters.
eg (string foo 10) foo will be able to hold up to 10 characters.
Note: There is no length checking when assigning to strings.
See also: array, bool, int.
(strlen string) [STRING : NUMBER]
Returns the length of the string.
(substr string index length) [STRING, NUMBER, NUMBER : STRING]
Return a string of length characters starting at index (0 = first
character).
If length or index are <0 then the length of the string is added to them.
If index > length of string, it is set after the last character of the
string.
eg (substr "foobar" 0 3) => "foo", (substr "foobar" -1 1) => "r"
(substr "foobar" 0 -1) => "fooba"
(substr "foobar" 3 100) => "bar"
(substr "foobar" 50 100) => ""
(switch value value block [value block ...] [default block])
[NUMBER|STRING|BOOLEAN, same, block, same, block ... : last block executed]
Get a value. Scan down the list until a matching value is found and then
execute the next block and exit the switch.
If no match is found, the default block is executed (if it exists).
All values must have the same class.
eg
(switch (substr (ask "Is the moon made of green cheese? ") 0 1)
"y" (msg "Not according to current scientific thought.")
"n" (msg "Probably the case.")
default (msg "Burp.")
)
See also: case
TRUE [zip : BOOLEAN]
The boolean constant TRUE.
Use this for the default case in case.
(while test body) [BOOLEAN, block : FALSE (unless break used)]
Perform body while test is TRUE.
For example: (int j) (j 0) (while (< j 5) { (msg j) (+= j 1) } )
will print out: 0 1 2 3 4.
A do..while loop can be constructed like so:
(while {body test} ())
You can use break to get out of the loop or continue to move back to
test.
See also: break, continue, for.
====================================================================
How args are passed
====================================================================
All args are evaluated before being passed. bytes, ints and INTS are
converted to NUMBER.
Args are passed by value and reference. Value means a copy of the arg
is pushed on the stack and changes to it will not be reflected in the
original. When something is passed by reference, a pointer to the
original is pushed on the stack and changes will be to the original.
- bools, numbers, voids: value.
- arrays, blobs, strings: reference.
====================================================================
Example PGMS
====================================================================
; Factorial the recursive way
(defun
fact ; the recursive part. input: x output: x!
{
(if (== (arg 0) 0) 1 ; 0! = 1
(* (arg 0) (fact (- (arg 0) 1))) ; x! = x * (x-1)!
)
}
! ; the main routine
{
(int x)
(x (atoi (ask "Take factorial of: ")))
(msg x "! = " (fact x))
}
)
(!) ; (! 5) produces "5! = 120"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(defun square ; print a table of squares
{
(int foo max)
(max (atoi(ask "Max = "))) (foo 1)
(while (<= foo max) {(msg foo "\^2 = " (* foo foo))(+= foo 1)})
})
; (square 3) produces
; 1^2 = 1
; 2^2 = 4
; 3^2 = 9
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Good ol towers of hanoi
; Usage: (hanoi <n>) where n is the number of discs
(defun
print-move (from to) { (msg "Move Disk From " from " To " to) }
transfer (from to via)(int n)
{
(if (== n 1)(print-move from to)
{
(transfer from via to (- n 1))
(print-move from to)
(transfer via to from (- n 1))
})
}
hanoi {(transfer "A" "B" "C" (atoi (ask "n = ")))(msg "done.")}
)
(hanoi)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; tobase(n,base): convert n (base 10) to base. eg (tobase 10 16) => "A"
; C Durland
(defun
mod (int n base) ; n mod base = n - (n/base)*base
{(- n (* (/ n base) base))}
tobase (int n base)
{
(if (< n base) (substr "0123456789ABCDEF" n 1)
(concat
(tobase (/ n base) base) ; tobase n/base base
(tobase (mod n base) base) ; tobase (n mod base) base
)
)
}
)
(msg (tobase (atoi (ask "n = "))(atoi (ask "base = "))))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(defun sort (array int list 1)(int n) ; shell sort an array of n ints
{
(int gap i j t k)
(gap (/ n 2))
(while (> gap 0)
{
(i gap)
(while (< i n)
{
(j (- i gap))
(while (and (>= j 0) (> (list j)(list (+ j gap))))
{
(k (+ j gap))(t (list j))(list j (list k))(list k t)
(-= j gap)
})
(+= i 1)
})
(/= gap 2)
})
})
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Quick sort. From Horowitz & Sahni pg 347
; Sort list[m..n] into assending order
; swap is a routine that interchanges list[a] with list[b]
; cmp compares list[a] with list[b] and returns:
; negative if list[a] < list[b]
; positive if list[a] > list[b]
; zero if list[a] = list[b]
(defun
Qsort (list)(int m n)(pointer defun swap cmp)
{
(int i j)
(if (>= m n)(done))
(i m)(j (+ n 1))
(while TRUE
{
(while { (+= i 1)(and (<= i n) (< (cmp list i m) 0)) } ())
(while { (-= j 1)(> (cmp list j m) 0) } ())
(if (< i j) (swap list i j) (break))
})
(swap list m j)
(Qsort list m (- j 1) swap cmp)
(Qsort list (+ j 1) n swap cmp)
}
)
; swap and cmp routines for arrays of ints
(defun
swap (array int list 1)(int a b) ; swap 2 elements
{
(int tmp)
(tmp (list a))(list a (list b))(list b tmp)
}
cmp (array int list 1)(int a b) ; compare 2 elements
{ (- (list a)(list b)) }
)
; (array int list 100)
; (Qsort list 0 n (floc swap) (floc cmp))
======================================================================
Notes and Tricks
======================================================================
* Lets say you have a number and wish to turn it into a string with a
leading zero if it is less than 10.
(int n)(string s 20)
(n (atoi (ask "n = ")))
(s (concat (if (< n 10) ("0")("")) n))
This trick also works in msgs, etc.
* In general, only the last thing in a block matters.
So, (foo { (msg "set foo to 123") 123 }) is the same as
(msg "set foo to 123") (foo 123).
======================================================================
======================================================================
HOW TO RUN THE COMPILER
Type "mc filename". filename is assumed to have a .MUT extension (ie
you don't have to type it). The compiler produces an output file in
the current directory with the name name.MCO where name is the same
base name as filename.
options:
-I dir: An alternate directory for include files.
For example, if foo.mut is in directory fred and contains
(include sam.mut) and sam.mut is in directory /bar then you need
mc -I/bar foo.
-l : produce a list file showing the compiler output (name.LST).
-t tname : use external token file tname.TOK. This a code crunching
option.
-v : Display the version of the compiler
You run your compiled pgm with MM (the Mutt Machine). Type "mm fname"
where fname is the name of the file you compiled.
For example, to compile and run the factorial pgm listed above:
mc fact ; compile the fact pgm
Mutt compiler 11/20/87 CD
Errors: 0 Warnings: 0. Code size = 110 bytes.
mm fact ; run the pgm
Take factorial of: 5 ; user is prompted for value and types 5
5! = 120 ; the answer is printed out
======================================================================
Gotchas
======================================================================
* Be careful of name overlap. Note that the following progam is legal
but may not do what you think.
(const foo 123)
(defun foo
{
(int foo)
(foo foo) ; what does this line really do???
})
======================================================================
Bugs
======================================================================
======================================================================
======================================================================
Copyright 1987, 1988 Craig Durland