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:
CreateClass (t, "ClassName" , (CreateClass * c)
{
c.method("blah" , &blah);
c.method("forble" , &forble);
pushInt(t, 0);
c.method("funcWithUpval" , &funcWithUpval, 1);
});
newGlobal(t, "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" );
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();
- 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);
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:
pushString(t , "a" );
pushString(t , "b" );
runFile (t , "foo.bar.baz" , 2);
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 ,
{
},
(MDException e, word mdEx)
{
},
{
});
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:
stackCheck (t , 2,
{
pushNull(t );
pushInt(t , 5);
});
stackCheck (t , -1,
{
pushString(t , "foobar" );
});
- 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);
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);
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);
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.
|