minid.ex

This module contains the "extension" API, which is a bunch of useful functionality built on top of the "raw" API.

License:
Copyright (c) 2008 Jarrett Billingsley


This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.

2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.

3. This notice may not be removed or altered from any source distribution.

void makeModule (MDThread* t, char[] name, uint function(MDThread*, uint) loader);
Simple function that attempts to create a custom loader (by making an entry in modules.customLoaders) for a module. Throws an exception if a loader for the given module name already exists.

Params:
char[] name The name of the module. If it's a nested module, include all name components (like "foo.bar.baz").
uint function(MDThread*, uint) loader The module's loader function. Serves as the top-level function when the module is imported, and any globals defined in it become the module's public symbols.

struct CreateClass ;
A little helper object for making native classes. Just removes some of the boilerplate involved.

You use it like so:

// Make a class named ClassName with no base class
CreateClass
(t,
"ClassName"
, (
CreateClass
* c) {
// Some normal methods
c.method(
"blah"
, &blah); c.method(
"forble"
, &forble);
// A method with one upval.  Push it, then call c.method
pushInt(t, 0); c.method(
"funcWithUpval"
, &funcWithUpval, 1); });
// At this point, the class is sitting on top of the stack, so we have to store it
newGlobal(t,
"ClassName"
);
// Make a class that derives from ClassName
CreateClass
(t,
"DerivedClass"
,
"ClassName"
, (
CreateClass
* c) {}); newGlobal(t,
"DerivedClass"
);
If you pop the class inside the callback delegate accidentally, it'll check for that and throw an error.

You can, of course, modify the class object after creating it, like if you need to add a finalizer or allocator.

static void opCall (MDThread* t, char[] name, void delegate(CreateClass*) dg);


static void opCall (MDThread* t, char[] name, char[] base, void delegate(CreateClass*) dg);


void method (char[] name, uint function(MDThread*, uint) f, uint numUpvals = cast(uint)0);
Register a method .

Params:
char[] name Method name. The actual name that the native function closure will be created with is the class's name concatenated with a period and then the method name, so that in the example code above, the "blah" method would be named "ClassName.blah".
uint function(MDThread*, uint) f The native function.
uint numUpvals How many upvalues this function needs. There should be this many values sitting on the stack.

uword BasicClassAllocator (uword numFields, Members)(MDThread* t, uword numParams);
A standard class allocator for when you need to allocate some extra fields/bytes in a class instance. Allocates the new instance with the given number of extra fields and bytes, then calls the ctor.

Params:
numFields The number of extra fields to allocate in the instance.
Members Any type. Members.sizeof extra bytes will be allocated in the instance, and those bytes will be initialized to Members.init.

Example:
newClass(t, 
"Foob"
);
// ...
// We need 1 extra field, and SomeStruct will be used as the extra bytes
newFunction(t, &
BasicClassAllocator
!(1, SomeStruct),
"Foob.allocator"
); setAllocator(t, -2); newGlobal(t,
"Foob"
);


uword BasicClassAllocator (uword numFields, uword numBytes)(MDThread* t, uword numParams);
Similar to above, but instead of a type for the extra bytes, just takes a number of bytes. In this case the extra bytes will be uninitialized.

T* getMembers (T)(MDThread* t, word index);
For the instance at the given index, gets the extra bytes and returns them cast to a pointer to the given type. Checks that the number of extra bytes is at least the size of the given type, but this should not be used as a foolproof way of identifying the type of instances.

struct StrBuffer ;
A utility structure for building up strings out of several pieces more efficiently than by just pushing all the bits and concatenating. This struct keeps an internal buffer so that strings are built up in large chunks.

This struct uses the stack of a thread to hold its intermediate results, so if you perform any stack manipulation to calls to this struct's functions, make sure your stack operations are balanced, or you will mess up the string building. Also, negative stack indices may change where they reference during string building since the stack may grow, so be sure to use absolute (positive) indices where necessary.

A typical use looks something like this:

auto
buf =
StrBuffer
(t); buf.addString(someString); buf.addChar(someChar);
// ...
auto
strIdx = buf.finish();
// The stack is how it was before we created the buffer, except with the result string is on top.


static StrBuffer opCall (MDThread* t);
Create an instance of this struct. The struct is bound to a single thread.

void addChar (dchar c);
Add a character to the internal buffer.

void addString (char[] s);
Add a string to the internal buffer.

void addTop ();
Add the value on top of the stack to the buffer. This is the only function that breaks the rule of leaving the stack balanced. For this function to work, you must have exactly one value on top of the stack, and it must be a string or a char.

uint sink (char[] s);
A convenience function for hooking up to the Tango IO and formatting facilities. You can pass "&buf.sink" to many Tango functions that expect a sink function for string data.

int finish ();
Indicate that the string building is complete. This function will leave just the finished string on top of the stack. The StrBuffer will also be in a state to build a new string if you so desire.

int lookup (MDThread* t, char[] name);
Look up some value using a name that looks like a chain of dot-separated identifiers (like a MiniD expression). The name must follow the regular expression "\w[\w\d]*(\.\w[\w\d]*)*". No spaces are allowed. The looked-up value is left at the top of the stack.

This functions behaves just as though you were evaluating this expression in MiniD. Global lookup and opField metamethods are respected.

auto
slot =
lookup
(
t
,
"time.Timer"
); pushNull(
t
); rawCall(
t
, slot, 1);
// We now have an instance of time.Timer on top of the stack.
If you want to set a long name, such as "foo.bar.baz.quux", you just lookup everything but the last name and use 'fielda' to set it:

auto
slot =
lookup
(
t
,
"foo.bar.baz"
); pushInt(
t
, 5); fielda(
t
, slot,
"quux"
);


Params:
char[] name The dot-separated name of the value to look up.

Returns:
The stack index of the looked-up value.

word lookupCT (char[] name)(MDThread* t);
Very similar to lookup, this function trades a bit of code bloat for the benefits of checking that the name is valid at compile time and of being faster. The name is validated and translated directly into a series of API calls, meaning this function will be likely to be inlined. The usage is exactly the same as lookup, except the name is now a template parameter instead of a normal parameter.

Returns:
The stack index of the looked-up value.

int getRegistryVar (MDThread* t, char[] name);
Pushes the variable that is stored in the registry with the given name onto the stack. An error will be thrown if the variable does not exist in the registry.

Returns:
The stack index of the newly-pushed value.

void setRegistryVar (MDThread* t, char[] name);
Pops the value off the top of the stack and sets it into the given registry variable.

int loadString (MDThread* t, char[] code, bool customEnv = false, char[] name = "<loaded by loadString >");
Similar to the loadString function in the MiniD base library, this compiles some statements into a function that takes variadic arguments and pushes that function onto the stack.

Params:
char[] code The source code of the function. This should be one or more statements.
bool customEnv If true, expects the value on top of the stack to be a namespace which will be set as the environment of the new function. The namespace will be replaced. Defaults to false, in which case the current function's environment will be used.
char[] name The name to give the function. Defaults to "".

Returns:
The stack index of the newly-compiled function.

void runString (MDThread* t, char[] code, bool customEnv = false, char[] name = "<loaded by runString >");
This is a quick way to run some MiniD code. Basically this just calls loadString and then runs the resulting function with no parameters. This function's parameters are the same as loadString's.

uint eval (MDThread* t, char[] code, int numReturns = 1, bool customEnv = false);
Similar to the eval function in the MiniD base library, this compiles an expression, evaluates it, and leaves the result(s) on the stack.

Params:
char[] code The source code of the expression.
int numReturns How many return values you want from the expression. Defaults to 1. Works just like the numReturns parameter of the call functions; -1 gets all return values.
bool customEnv If true, expects the value on top of the stack to be a namespace which will be used as the environment of the expression. The namespace will be replaced. Defaults to false, in which case the current function's environment will be used.

Returns:
If numReturns >= 0, returns numReturns. If numReturns == -1, returns how many values the expression returned.

void runFile (MDThread* t, char[] filename, uint numParams = cast(uint)0);
Imports a module or file and runs any main() function in it.

Params:
char[] filename The name of the file or module to load. If it's a path to a file, it must end in .md or .mdm. It will be compiled or deserialized as necessary. If it's a module name, it must be in dotted form and not end in md or mdm. It will be imported as normal.
uint numParams How many arguments you have to pass to the main() function. If you want to pass params, they must be on top of the stack when you call this function.

Example:
// We want to load "foo.bar.baz" and pass it "a" and "b" as params.
// Push the params first.
pushString(
t
,
"a"
); pushString(
t
,
"b"
);
// Run the file and tell it we have two params.
runFile
(
t
,
"foo.bar.baz"
, 2);
// Just showing how you'd execute a module by filename instead of module name.
runFile
(
t
,
"foo/bar/baz.md"
);


void mdtry (MDThread* t, void delegate() try_, void delegate(MDException, int) catch_, void delegate() finally_ = null);
This function abstracts away some of the boilerplate code that is usually associated with try-catch blocks that handle MiniD exceptions in D code.

This function will store the stack size of the given thread when it is called, before the try code is executed. If an exception occurs, the stack will be restored to that size, the MiniD exception will be caught (with catchException), and the catch code will be called with the D exception object and the MiniD exception object's stack index as parameters. The catch block is expected to leave the stack balanced, that is, it should be the same size upon exit as it was upon entry (an error will be thrown if this is not the case). Lastly, the given finally code, if any, will be executed as a finally block usually is.

This function is best used with anonymous delegates, like so:

mdtry
(
t
,
// try block
{
// foo bar baz
},
// catch block
(MDException e, word mdEx) {
// deal with exception here
},
// finally block
{
// cleanup, whatever
});
It can be easy to forget that those blocks are actually delegates, and returning from them just returns from the delegate instead of from the enclosing function. Hey, don't look at me; it's D's fault for not having AST macros ;)

If you just need a try-finally block, you don't need this function, and please don't call it with a null catch_ parameter. Just use a normal try-finally block in that case (or better yet, a scope(exit) block).

Params:
void delegate() try_ The try code.
void delegate(MDException, int) catch_ The catch code. It takes two parameters - the D exception object and the stack index of the caught MiniD exception object.
void delegate() finally_ The optional finally code.

void stackCheck (MDThread* t, int diff, void delegate() dg);
A useful wrapper for code where you want to ensure that the stack is balanced, that is, it is the same size after some set of operations as before it. Having a balanced stack is more than just good practice - it prevents stack overflows and underflows.

You can also use this function when your code requires that the stack be a certain number of slots larger or smaller after some stack operations - for instance, a function which always returns two values, regardless of multiple execution paths.

If the stack size is not correct after running the code, an exception will be thrown in the passed-in thread.

Params:
int diff How many more (or fewer) items there should be on the stack after running the code. If 0, it means that the stack size after running the code should be exactly as it was before (there is an overload for this common case below). Positive numbers mean the stack should be bigger, and negative numbers mean it should be smaller.
void delegate() dg The code to run.

Examples:
// check that the stack is two bigger after the code than before
stackCheck
(
t
, 2, { pushNull(
t
); pushInt(
t
, 5); });
// it is indeed 2 slots bigger, so it succeeds.
// check that the stack shrinks by 1 slot
stackCheck
(
t
, -1, { pushString(
t
,
"foobar"
); });
// oh noes, it's 1 slot bigger instead - throws an exception.


void stackCheck (MDThread* t, void delegate() dg);
An overload of the above which simply calls it with a difference of 0 (i.e. the stack is completely balanced). This is the most common case.

T[] allocArray (T)(MDThread* t, uword length);
Wraps the allocMem API. Allocates an array of the given type with the given length. You'll have to explicitly specify the type of the array.

auto
arr =
allocArray
!(
int
)(t, 10);
// arr is an int[] of length 10
The array returned by this function should not have its length set or be appended to (~=).

Params:
length The length, in items, of the array to allocate.

Returns:
The new array.

void resizeArray (T)(MDThread* t, ref T[] arr, uword length);
Wraps the resizeMem API. Resizes an array to the new length. Use this instead of using .length on the array. Only call this on arrays which have been allocated by the MiniD allocator.

Calling this function on a 0-length array is legal and will allocate a new array. Resizing an existing array to 0 is legal and will deallocate the array.

The array returned by this function through the arr parameter should not have its length set or be appended to (~=).

resizeArray
(t, arr, 4);
// arr.length is now 4


Params:
arr A reference to the array you want to resize. This is a reference so that the original array reference that you pass in is updated. This can be a 0-length array.
length The length, in items, of the new size of the array.

T[] dupArray (T)(MDThread* t, T[] arr);
Wraps the dupMem API. Duplicates an array. This is safe to call on arrays that were not allocated by the MiniD allocator. The new array will be the same length and contain the same data as the old array.

The array returned by this function should not have its length set or be appended to (~=).

auto
newArr =
dupArray
(t, arr);
// newArr has the same data as arr


Params:
arr The array to duplicate. This is not required to have been allocated by the MiniD allocator.

void freeArray (T)(MDThread* t, ref T[] arr);
Wraps the freeMem API. Frees an array. Only call this on arrays which have been allocated by the MiniD allocator. Freeing a 0-length array is legal.

freeArray
(t, arr);
freeArray
(t, newArr);


Params:
arr A reference to the array you want to free. This is a reference so that the original array reference that you pass in is updated. This can be a 0-length array.

class RefManager ;
A class that makes it possible to automatically remove references to MiniD objects. You should create a SCOPE instance of this class, which you can then use to create references to MiniD objects. This class is not itself marked scope so that you can pass references to it around, but you should still ALWAYS create instances of it as scope.

By using the reference objects that this manager creates, you can be sure that any MiniD objects you reference using it will be dereferenced by the time the instance of RefManager goes out of scope.

class Ref ;
An actual reference object. This is basically an object-oriented wrapper around a MiniD reference identifier. You don't create instances of this directly; see .

void remove ();
Removes the reference using . You can call this manually, or it will be called automatically when this object is collected or when its owning manager leaves scope.

int push ();
Push the reference using . It is pushed onto the current thread of the VM in which it was created.

Returns:
The stack index of the object that was pushed.

Ref create (MDThread* t, int idx);
Create a reference object to refer to the object at slot idx in thread t using . The given thread's VM is associated with the reference object.

Returns:
A new reference object.

void checkAnyParam (MDThread* t, int index);
Check that there is any parameter at the given index. You can use this to ensure that a minimum number of parameters were passed to your function.

bool checkBoolParam (MDThread* t, int index);
long checkIntParam (MDThread* t, int index);
double checkFloatParam (MDThread* t, int index);
dchar checkCharParam (MDThread* t, int index);
char[] checkStringParam (MDThread* t, int index);
These all check that a parameter of the given type was passed at the given index, and return the value of that parameter. Very simple.

double checkNumParam (MDThread* t, int index);
Checks that the parameter at the given index is an int or a float, and returns the value as a float, casting ints to floats as necessary.

void checkInstParam ()(MDThread* t, word index);
Checks that the parameter at the given index is an instance.

void checkInstParam ()(MDThread* t, word index, char[] name);
Checks that the parameter at the given index is an instance of the class given by name. name must be a dotted-identifier name suitable for passing into lookup().

Params:
index The stack index of the parameter to check.
name The name of the class from which the given parameter must be derived.

T* checkInstParam (T)(MDThread* t, word index, char[] name);
Same as above, but also takes a template type parameter that should be a struct the same size as the given instance's extra bytes. Returns the extra bytes cast to a pointer to that struct type.

void checkParam (MDThread* t, int index, Type type);
Checks that the parameter at the given index is of the given type.

void paramTypeError (MDThread* t, int index, char[] expected);
Throws an informative exception about the parameter at the given index, telling the parameter index ('this' for parameter 0), the expected type, and the actual type.

bool optBoolParam (MDThread* t, int index, bool def);
long optIntParam (MDThread* t, int index, long def);
double optFloatParam (MDThread* t, int index, double def);
dchar optCharParam (MDThread* t, int index, dchar def);
char[] optStringParam (MDThread* t, int index, char[] def);
These all get an optional parameter of the given type at the given index. If no parameter was passed to that index or if 'null' was passed, 'def' is returned; otherwise, the passed parameter must match the given type and its value is returned. This is the same behavior as in MiniD.

double optNumParam (MDThread* t, int index, double def);
Just like the above, allowing ints or floats, and returns the value cast to a float, casting ints to floats as necessary.

bool optParam (MDThread* t, int index, Type type);
Similar to above, but works for any type. Returns false to mean that no parameter was passed, and true to mean that one was.

Page was generated with on Thu Jul 16 22:47:49 2009