Go to the first, previous, next, last section, table of contents.


Basics

You're assumed to know C backwards and forwards, so we won't waste time on orientation, just cut to the chase:

There is (as yet?) no C-style preprocessor for MUC: In general #define and kith are not supported. As a special exception,

#if (expression)
...
#else	/* optional */
...
#endif

is emulated by the compiler. Since Muq compiles files incrementally and the #if is emulated by the compiler, expression is not limited to constant expressions: Arbitrary expressions may be used, including function calls. If expression is a bare variable, the parentheses may be dropped. For internal technical reasons, this construct will only work at the top level, not nested inside functions or other expressions.

Two styles of comments are supported:

/* Traditional C style comments. Must currently be contained on one line. */
// C++ style comments running to end of line.

There is no address arithmetic whatever in MUC: You cannot modify a pointer, and there is no way to point to part of an object, only to the entire object. Use code like for (i=len; i --> 0;)a[i]=0; instead of C-style code like for (i=len,p=&a[0]; i --> 0;)*p++=0;. There is no C-style unary '&' operator, so C expressions like int*p=&a[5]; simply do no translate. Similarly, there is no C-style unary * operator. This is also no C-style -> operator: MUC uses a.b where C would use a->b.

There is in general no direct access to host files, directories or other resources from muc: Muq is primarily a sandbox environment for building virtual worlds and hosting guests without risking damage to the host filesystem or computer. (The Muq virtual machine does have commands for inbound and outbound TCP and UDP communication and for spawning subprocesses, but for now they are best accessed from MUF.)

Declarations and statements can be freely mixed in MUC, unlike C: {int a=10;printf("a=%d\n",a);int b=1<<a;} is perfectly good MUC syntax.

Since MUC is a high-level scripting language, not a systems implementation langauge, expressions are evaluated one by one in the order presented. This means it is perfectly legal, for example, to put printf("Got this far!\n"); in the middle of a MUC source file, outside of any function definition.

The Muq virtual machine is a tagged archicture: the type of any value can be determined by the interpreter by inspection, without needing to know anything about the type of the variable holding it.

This means that in general any MUC variable can hold any type of value without causing problems or misinterpretations, which in turn means that MUC can and does ignore most variable type declarations: MUC currently treats all the following as exactly equivalent:

int i=0;
byte i=0;
char i=0;
short i=0;
long i=0;
obj i=0;

I suggest that when in doubt you use obj i; -- all Muq values including ints, chars and floats are objects (unlike, say, C++ or Java, where they are special exceptions to the object orientation), so everything is of type obj.

The Muq virtual machine provides garbage collection: In essence, this means you cannot create a memory leak, so you can sling dynamically created strings and objects around freely. There is no malloc() -- one typically uses various make*() functions to allocate new store. There is also no free().

Most C arithmetic types and expressions should work in MUC just as you expect. There are three new binary operators: You may use a ! b to compute the dot-product of two length-three vectors, you may use a >< b to compute the cross-product of two such vectors, and you may use x ~ y to compare floats or such vectors for approximate equality (to about one part in 100,000) -- it is usually a bad idea to compare floats for exact equality due to accumulation of rounding errors. You may also add, subtract, multiply, divide, and negate floats and such vectors, and also do floating point modular reduction.

The distance(P,Q) function will compute the distance between points (three-vectors) P and Q, and magnitude(V) will compute the length of vector V using the usual Euclidean square root of sum of squares formula.

If you're feeling concise you can abbreviate magnitude(V) to just =V -- the unary = is a synonym. Similarly, |f (unary vertical bar) is a synonym for abs(f) -- this is an approximation to the traditional |f| notation for absolute value.

Floats are a bit less than double in precision. (59 bits.)

Ints are indefinite precision: Muq internally uses 62-bit arithmetic until it gets an overflow, then switches automatically and transparently to indefinite precision bignum arithmetic. This means 1<<1000 will produce a correct result in MUC, unlike in C or Java.

The += and ++ type operators currently work only on simple variables, not expressions.

There is an extra "**" operator for raising numbers to an exponent.

There is also an integer exptmod(base,exponent,modulus) function which raises base to exponent mod modulus efficiently enough for basic public-key style digital signature computations, or of course just playing with number theory.

You also have available acos() asin() atan() atan2() cos() sin() tan() cosh() sinh() tanh() exp() log() log10() sqrt() abs() ffloor() fceiling() floor() ceiling() truncate() round() all more or less direct from the C math library.

Character constants are distinct from int constants in the Muq virtual machine: Use 'a' as in C to specify a character constant. Use int i=charInt('a'); and char c=intChar(60); to convert back and forth.

Strings are not null-terminated. You can concatenate them using '+' if you wish; Many other string operations are provided. (For now, you'll have to check the MUF reference manual. Most can be called directly from MUF.) As with vectors (strings are in fact a special kind of vector) use length(string) to get the length of a string.

String constants are specified using double-quotes as usual -- modifying the contents of strings created this way is currently possible but discouraged and likely to be forbidden in future Muq releases.

Regular expressions using a close approximation to Perl4 syntax and semantics are available using the vars ~= /regex/ (string); idiom: Think of the regex as naming a function. The first return value is a flag indicating whether the match was successful, subsequent return values correspond to parenthesized matches:

muc>
int i;char* s;
muc>
muc>
i,s ~= /^a(b*)c$/ ("abbc");
muc>
i;s;
muc>  t
muc>  "bb"
i,s ~= /^a(b*)c$/ "abbc";
muc>
i;s;
muc>  t
muc>  "bb"
i,s ~= /^a(b*)c$/ sprintf("a%sc","bb");
muc>
i;s;
muc>  t
muc>  "bb"
i,s ~= /^a(b*)c$/ sprintf("a%sc","cc");
muc>
i;s;
muc>  nil
muc>  ""

Currently the regular expression delimiter must be '/', must follow a '~=' and the regular expression must all be on one line. (Future releases will allow other delimiters and also support var ~= s/regex/replacement/ (string); and var ~= tr/A-Z/a-z/ (string); syntax.)

Most C control structures should work just as you expect in MUC.

There is however no C-style statement-level comma operator as in C for (a=0, b=len; a<b; ++a, --b); -- comma is instead used for multiple assignments such as a,b = b,a; and (more importantly) a,b,c = threevalued();.

muc> 
int a=1;
muc> 
int b=2;
muc> 
a,b=b,a;
muc> 
a;
muc>  2
b;
muc>  1

This means you must rewrite the above for loop as for (a,b = 0,len; a<b; a,b = a+1,b+1);. Life is tough.

Also, 'switch' is not yet implemented, nor is 'goto'.

Cascaded assignments (a=b=0;) are not yet supported.

printf("...\n",a,b...); and sprintf(...); should work just as you expect. Also gets(); to read a line from standard input.

Also available is sscanf, but due to the lack of C-style unary '&' in MUC is uses multiple return values in place of pointer arguments:

muc> 
sscanf("12 128.45 hike!", "%d %f %s");
muc>  12 128.45 "hike!"
float f;char*s;int i;
muc> 
muc> 
muc> 
i,f,s = sscanf("12 128.45 hike!", "%d %f %s");
muc> 
i; f; s;
muc>  12
muc>  128.45
muc>  "hike!"

Some might find this syntax actually prettier than what C does.

There is a small potential gotcha with boolean values: C treats zero and NULL as being 'false' and everything else as being 'true': MUC treats only nil as being 'false'. (There are however NULL and FALSE synonyms for nil, to make C programmers feel a bit more at home.) This means you cannot do for (i=10; i; i--); and expect it to terminate. Use explicit tests instead and you'll be fine: for (i=10; i>0; i--);

Another small potential gotcha is that MUC allows characters in identifiers which C does not: In particular '?' is allowed because other Muq languages have so many predicates such as vector? -- so be careful to write int a = b ? c : d; not int a=b?c:d; in which both the '?' and the ':' are likely to cause problems.

You may use < > <= >= = != to compare any two Muq values whatever: comparing an integer to a float is kosher (and will do the right thing) as is comparing an integer to an object. The latter sort of comparison will return consistent results but is not otherwise specified: The intention is to only allow rational sorting and lookup of arbitrary sets of values. Strings compare using case-sensitive ASCII collating order. (Use lt gt le ge eq ne to do case-insensitive string compares.)

The cheapest small chunks of storage you can allocate are vectors, which behave like one-dimensional arrays which can hold any type of value in any slot.

As in C, slots are numbered starting at zero, not at one.

Use obj myvec=makeVector(0,10); to generate a ten-slot vector initialized to zeroes.

Use myvec[i]=myvec[i+1]; style syntax to read and write slots.

You may use int len=length(myvec); to get the length of a vector in slots. If you are in a concise mood, you may use int len=#myvec; instead. Similarly, you may if you wish use +/vector; as a shorthand for sum(vector); to compute the sum of the elements in a vector, and */vector; as a shorthand for product(vector);.

You may use negative indices to access slots starting from the far end: myvec[-1] gives the value of the last slot.

There is a special syntax {a,1,i+3} which may be used to create (in this case) a three-slot vector containing the given values: This syntax may be used anywhere any constant can appear in an expression. In particular, it may be used to construct arguments to functions compactly. By nesting this construct you may conveniently create small tree structures on the fly: This can be useful for example in functions which recursively rewrite a parsetree.

There are specialized types of vectors which hold only bytes, shorts, 32-bit ints, 32-bit floats, or 64-bit floats. These vectors are intended primarily for doing OpenGL graphics from MUC, since the OpenGL API calls expect simple numeric vectors, not Muq's native internal datastructures.

There are separate calls for creating these types of vectors -- the following examples create length-ten specialized vectors initialized to zero:

byte*   p=makeVectorI08(0,10);	/* 'byte' and 'char' are synonyms. */
short*  p=makeVectorI16(0,10);
int*    p=makeVectorI32(0,10,0);
float*  p=makeVectorF32(0.0,10);
double* p=makeVectorF64(0.0,10);

(Again, remember that the above type declarations are currently completely ignored: Each variable could have been as well declared obj p=...;.)

As with plain Muq vectors, there is special MUC syntax for creating short vectors on the fly in the middle of an expression by explicit enumeration of contents:

(char*) {0,a+3,f(x)}
(short*) {0,a+3,f(x)}
(int*) {0,a+3,f(x)}
(float*) {0.0,a+3.0,f(x)}
(double*) {0.0,a+3.0,f(x)}

In the above example, the types do matter, they determine the type of vector created. Other than this special construct, there is no cast operator in MUC.

There are no multidimensional arrays yet, although you can construct vectors of vectors.

Muq has Index objects which are used similarly to hashes in Perl or Dictionary objects in some other systems: they map an arbitary set of keys to matching values. They may be created by obj myindex=makeIndex(); values may be added by myindex["key"]=someval; and retrieved by obj val=myindex["key"]; You will shortly be able to remove keys by doing delete myindex["key"]; but this isn't supported quite yet.

Muq Index objects use sorted B-trees internally, so they can potentially scale to very large numbers of key-val pairs. Muq also has Hash objects which use hashed instead of sorted B-trees, which may sometimes buy some lookup speed at the cost of the additional space used internally to store the hashcodes: You may use obj myhash=makeHash(); to create one, after which usage is the same as with Index objects.

You may use unary ^ as a synonym for return: This can look better in very compact expressions.

Use inPackage("mypackage"); to select a new package: This is somewhat similar to doing cd directory in Unix, except that packages may not be nested. If the named package does not exist, it will be created.

Use ls(obj); to list the fields, values or properties associated with object, vector or value objdx. For example, ls(.u); will list known users, ls(.db); will list mounted databases, ls(.cfg); will list the server compiletime configuration parameters, ls(.muq); will list the server runtime configuration parameters, and ls(.lib["muf"];) will list all exported (public) symbols in the muf package. (Be warned that the latter listing is fairly long -- currently over 2500 symbols.)

Muq objects actually have four sets of properties: public, hidden, system and admins. (The latter two are in general only of interest to system administrators.) Use lsh(obj); to list the hidden properties on obj. Use lsa(obj); and lss(obj); to list the admins and system properties.

Unix uses a leading separator / to indicate an absolute path (/usr/home/cynbe/tmp vs cynbe/tmp). Muq uses a similar convention, but with a leading dot instead of slash, since dot is the matching C separator. Thus for example .lib is the absolute pathname for the global list of standard packages, .u is the absolute pathname for the global list of known users, and .db is the absolute pathname for the global list of mounted databases. (A "database" in Muq is comparable to a filesystem in Unix.)

Use root(); to get the root object itself.

In similar fashion, job(); will give you the currently running job: Do ls(job()); to list all the properties on it. To compactly access individual properties of the job you may use the .lib syntax. For example, .pid is the PID for the current job, and .package returns the currently selected package. (If you need a mnemonic, think of the '' as a little whirling turbine slaving away doing your job's computation.)

There is as yet no support for defining structs or classes in MUC.

There is currently no sizeof in MUC.

All C reserved words are also reserved words in MUC and in addition MUC has the following reserved words:

after
bit
byte
cilk
class
delete
endif
eq
ge
generic
gt
inlet
le
lt
macro
method
ne
noreturn
obj
public
spawn
sync
try
with

(Many of these are currently unused.)

The Muq virtual machine provides literally hundreds of functions, notably including the OpenGL API. For now, you'll have to peek at the Muf Reference Manual for a listing of them. Almost all of them should be usable directly from MUC.

In addition to these hardcoded functions, the Muq softcode libraries have various other useful functions, in particular muq/pkg/100-C-utils.t and muq/pkg/100-C-list.t.

At the time of writing, MUC is about two weeks old, and undoubtedly contains many deficiences not mentioned here. Caveat programmer.


Go to the first, previous, next, last section, table of contents.