Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

API Introduction

Note: this part of the specification is not implementation binding -- that is, this only describes how the official implementation's API works. Other implementations are free to design their APIs in any way.

MiniD is not really designed to function entirely as a standalone language. In fact, its main purpose is, like Lua, to be an "extensible extension" language. It's "extensible" in that it's possible to add new functions and classes to the language to give it more capabilities than the standard libraries provide. It's an "extension" language in that it makes it possible to add programmability to a native host program, so that new code and behaviors can be programmed into an application which has already been written and compiled. Both of these behaviors imply a close relationship between the host application and the MiniD runtime.

The native API provides this relationship. When you want to add scriptability to your native application, you use the native API to load and run scripts and to provide application-specific types and functions to MiniD.

This page documents some of the fundamental ideas of and overall organization of the native MiniD API.

MDValue

MDValue is a variant structure type which can hold any MiniD value. This is the D equivalent of a MiniD variable, and is in fact what the runtime uses to represent variables, array elements, table keys and values etc. Here is an example of some MiniD code which assigns values into a variable:

local x = 5;
x = [1, 2, 3];
x = "hi";

And the equivalent D code:

MDValue x = 5;
x = [1, 2, 3];
x = "hi";

As you can see, the use is identical.

MDValue is composed of two pieces of data: the type tag and the value. What the value represents depends upon the type tag.

MDValue is not a reference type, it is a value type. Some languages, like Python, make all values -- even things like integers and booleans -- reference types. MDValue can hold references to reference types, but it itself is a value type.

MDValue has a templated overload of opAssign. This allows behavior such as that shown in the above D code snippet. You can assign any convertible D type into an MDValue, and it will be automatically converted ("autoboxed") to the corresponding MiniD type.

MDValue also has corresponding templated methods for converting a MiniD to a D type. You can check to see if an MDValue can be converted to a D type, and you can do the conversion.

When comparing MDValues, there are two kinds of comparisons that can happen: identity and comparison/equality. MiniD does not differentiate between equality (== , !=) and comparison (<, <=, >, >=) like D does. The identity ('is') operator cannot be overridden, so MDValue overloads opEquals to perform the function if identity. This may seem confusing, but in reality this is the exact behavior necessary to get MDValues to work as associative array keys (important for the 'table' type!), and secondly because proper comparison of two MDValues requires an MDState to perform the comparison (see the section on language semantics being handled by MDState a bit further down). So what is "a is b" in MiniD code becomes "a == b" in D code, and "a op b" where op is any of ==, !=, <, <=, >, and >= becomes "s.cmp(a, b) op 0" in D code, where s is an MDState instance.

Type Conversion

These concepts apply mainly to MDValue, but the same concepts are used in many places throughout the API.

When converting D types to MiniD types, some form of 'quantization' has to occur. What this means is that D has a lot more types than MiniD has, so many D types have to be mapped to a single MiniD type. In most cases, little information is lost, as the MiniD types can represent a fair range of D types. The following is a table of conversions from D types to MiniD types.

D Typeconverted to MiniD TypeComments
void*nullMust be null; this is asserted in debug builds
boolbool
byte, ubyte, short, ushort, intint
uint, long, ulongintLoss of precision as MiniD integers are only 32-bit
float, doublefloat
realfloatLoss of precision as MiniD floats are only double-precision
char, wchar, dcharchar
char[], wchar[], dchar[]stringA duplicate of the data is made, as MiniD strings are immutable
associative array V[K]tableThe key type K and the value type V must both be convertible
array T[]arrayThe element type T must be convertible

Assigning the literal null to an MDValue sets it to null. This is really just a convenience, as there is no D equivalent of the MiniD null type. Of course, assigning instances of any of the MiniD class types (MDString, MDArray etc.) into an MDValue will work just as you expect, as no conversion has to be done.

The other direction is from MiniD types to D types. In this case, the opposite mapping -- from few to many -- must occur, and as such it's much simpler. This is done using either the .to() or .as() methods of MDValue.

MiniD Typecan be converted to D TypeComments
nulln/aCannot be converted
boolbool
intAny integral or floating-point typeTruncation happens as necessary
floatAny floating-point typeTruncation happens as necessary
charAny character typeYou really should use dchar, though, as single chars and wchars can't hold all the possible values
stringAny string typeWill be automatically converted to the right encoding, always gives a duplicate of the data
tableAny associative array type with convertible key and value typesAll the keys and values must be convertible to the correct types
arrayAny array type with a convertible element typeAll the values must be convertible to the correct type

Notice that null cannot be converted. This is because null has no real corresponding D type, and knowing that a value is null is enough; you don't need to convert it to something else.

Also notice that integers can be implicitly converted to floats; this is a common idiom throughout MiniD, not only in the API but in the language and the standard libraries as well.

Language semantics are handled by MDState

This is an important aspect of the API design. Most of the D types which are the native equivalent of the MiniD types (like MDArray, MDTable etc.) provide some convenience operations and operator overloads, so that you can i.e. loop through all the elements of an array or table, append arrays, look up keys in tables and namespaces etc. However, these operations do not follow the semantics of the language for an important reason: they have no access to an instance of MDState, and as such, no way to call or even look up any metamethods which may be defined for those objects.

Because of this, operations which could involve a metamethod or which require for there to be a state (like making a function call) are all handled by instances of the MDState class. Using the methods of the MDState class, you can write native code which functions identically to the corresponding MiniD code.

Take the following MiniD code as an example:

local t = 
{
	x = 5,
	y = 10,

	// Stupid, but for showing a point
	function opLength()
	{
		return 0;
	}

	function toString()
	{
		return format("Table x = {} y = {}", x, y);
	}
};

writefln(#t);
writefln(t);

Let's (naively) try to convert this code into native code. For simplicity, we'll only be translating the last two lines, as they're really the only place where the conversion can go wrong:

MDTable t = ...;
Stdout.formatln("{}", t.length);
Stdout.formatln("{}", t);

What will happen here? Well the MiniD code will output "0", and then "Table x = 5 y = 10". But the native code will output "4", and then "table 0x0012abc0" (or something like that). This is because metamethods are not being called. The table doesn't and can't know about them.

Instead, you should use the methods of an MDState instead. Look at the following example ('s' is a previously-defined MDState):

MDValue t = ...;
Stdout.formatln("{}", s.len(t).as!(size_t));
Stdout.formatln("{}", s.valueToString(t));

This code now prints "0" and then "Table x = 5 y = 10". This is because the MDState methods len and valueToString will call any metamethods defined for the object on which they're called. Notice also that t is now an MDValue instead of an MDTable. All of the methods of MDState which operate on values use MDValues.

Basically, for any operation which could use a metamethod, there is an MDState method to perform that operation. This includes all unary and binary operators in MiniD, indexing and slicing, comparison, and conversion to a string. Some operations, like function and method calls, intrinsically require an MDState to perform, and there are MDState methods for those as well.