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.