747 lines
11 KiB
Plaintext
747 lines
11 KiB
Plaintext
module simple;
|
|
|
|
// Importing stuff.
|
|
{
|
|
function loadMod(name, ns)
|
|
{
|
|
assert(name == "mod");
|
|
|
|
ns.x = "I'm x";
|
|
|
|
ns.foo = function foo()
|
|
{
|
|
writefln("foo");
|
|
};
|
|
|
|
ns.bar = function bar(x)
|
|
{
|
|
return x[0];
|
|
};
|
|
|
|
ns.baz = function baz()
|
|
{
|
|
writefln(x);
|
|
};
|
|
|
|
foreach(k, v; ns)
|
|
if(isFunction(v))
|
|
v.environment(ns);
|
|
}
|
|
|
|
setModuleLoader("mod", loadMod);
|
|
|
|
import mod : foo, bar;
|
|
foo();
|
|
writefln(bar([5]));
|
|
mod.baz();
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Super calls.
|
|
{
|
|
class Base
|
|
{
|
|
function fork()
|
|
{
|
|
writefln("Base fork.");
|
|
}
|
|
}
|
|
|
|
class Derived : Base
|
|
{
|
|
function fork()
|
|
{
|
|
writefln("Derived fork!");
|
|
super.fork();
|
|
}
|
|
}
|
|
|
|
local d = Derived();
|
|
d.fork();
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Coroutines and coroutine iteration.
|
|
{
|
|
local countDown = coroutine function countDown(x)
|
|
{
|
|
yield();
|
|
|
|
while(x > 0)
|
|
{
|
|
yield(x);
|
|
x--;
|
|
}
|
|
};
|
|
|
|
foreach(v; countDown, 5)
|
|
writefln(v);
|
|
|
|
writefln();
|
|
|
|
local forEach = coroutine function forEach(t)
|
|
{
|
|
yield();
|
|
|
|
foreach(k, v; t)
|
|
yield(k, v);
|
|
};
|
|
|
|
foreach(_, k, v; forEach, {hi = 1, bye = 2})
|
|
writefln("key: ", k, ", value: ", v);
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Testing tailcalls.
|
|
{
|
|
function recurse(x)
|
|
{
|
|
writefln("recurse: ", x);
|
|
|
|
if(x == 0)
|
|
return toString(x);
|
|
else
|
|
return recurse(x - 1);
|
|
}
|
|
|
|
writefln(recurse(5));
|
|
writefln();
|
|
|
|
class A
|
|
{
|
|
function f(x)
|
|
{
|
|
writefln("A.f: ", x);
|
|
|
|
if(x == 0)
|
|
return toString(x);
|
|
else
|
|
return this.f(x - 1); // call it as this.f to force a 'method' instruction to be generated
|
|
}
|
|
}
|
|
|
|
local a = A();
|
|
writefln(a.f(5));
|
|
writefln();
|
|
}
|
|
|
|
{
|
|
// A function which lets us define properties for a class.
|
|
// The varargs should be a bunch of tables, each with a 'name' field, and 'getter' and/or 'setter' fields.
|
|
function mixinProperties(classType, vararg)
|
|
{
|
|
classType.mProps = { };
|
|
|
|
classType.opIndex = function opIndex(key)
|
|
{
|
|
local prop = mProps[key];
|
|
|
|
if(prop is null)
|
|
throw format(classType, ".opIndex() - Property '%s' does not exist", key);
|
|
|
|
local getter = prop.getter;
|
|
|
|
if(getter is null)
|
|
throw format(classType, ".opIndex() - Property '%s' has no getter", key);
|
|
|
|
return getter(with this);
|
|
};
|
|
|
|
classType.opIndexAssign = function opIndexAssign(key, value)
|
|
{
|
|
local prop = mProps[key];
|
|
|
|
if(prop is null)
|
|
throw format(classType, ".opIndexAssign() - Property '%s' does not exist", key);
|
|
|
|
local setter = prop.setter;
|
|
|
|
if(setter is null)
|
|
throw format(classType, ".opIndexAssign() - Property '%s' has no setter", key);
|
|
|
|
setter(with this, value);
|
|
};
|
|
|
|
foreach(i, prop; [vararg])
|
|
{
|
|
if(!isTable(prop))
|
|
throw format("mixinProperties() - property ", i, " is not a table");
|
|
|
|
if(prop.name is null)
|
|
throw format("mixinProperties() - property ", i, " has no name");
|
|
|
|
if(prop.setter is null && prop.getter is null)
|
|
throw format("mixinProperties() - property '%s' has no getter or setter", prop.name);
|
|
|
|
classType.mProps[prop.name] = prop;
|
|
}
|
|
}
|
|
|
|
// Create a class to test out.
|
|
class PropTest
|
|
{
|
|
mX = 0;
|
|
mY = 0;
|
|
mName = "";
|
|
|
|
function constructor(name)
|
|
{
|
|
mName = name;
|
|
}
|
|
|
|
function toString()
|
|
{
|
|
return format("name = '", mName, "' x = ", mX, " y = ", mY);
|
|
}
|
|
}
|
|
|
|
// Mix in the properties.
|
|
mixinProperties
|
|
(
|
|
PropTest,
|
|
|
|
{
|
|
name = "x",
|
|
|
|
function setter(value)
|
|
{
|
|
mX = value;
|
|
}
|
|
|
|
function getter()
|
|
{
|
|
return mX;
|
|
}
|
|
},
|
|
|
|
{
|
|
name = "y",
|
|
|
|
function setter(value)
|
|
{
|
|
mY = value;
|
|
}
|
|
|
|
function getter()
|
|
{
|
|
return mY;
|
|
}
|
|
},
|
|
|
|
{
|
|
name = "name",
|
|
|
|
function getter()
|
|
{
|
|
return mName;
|
|
}
|
|
}
|
|
);
|
|
|
|
// Create an instance and try it out.
|
|
local p = PropTest("hello");
|
|
|
|
writefln(p);
|
|
p.x = 46;
|
|
p.y = 123;
|
|
p.x = p.x + p.y;
|
|
writefln(p);
|
|
|
|
// Try to access a nonexistent property.
|
|
try
|
|
p.name = "crap";
|
|
catch(e)
|
|
{
|
|
writefln("caught: ", e);
|
|
writefln(getTraceback());
|
|
}
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Some container classes.
|
|
{
|
|
class PQ
|
|
{
|
|
mData;
|
|
mLength = 0;
|
|
|
|
function constructor()
|
|
{
|
|
mData = array.new(15);
|
|
}
|
|
|
|
function insert(data)
|
|
{
|
|
resizeArray();
|
|
mData[mLength] = data;
|
|
|
|
local index = mLength;
|
|
local parentIndex = (index - 1) / 2;
|
|
|
|
while(index > 0 && mData[parentIndex] > mData[index])
|
|
{
|
|
local temp = mData[parentIndex];
|
|
mData[parentIndex] = mData[index];
|
|
mData[index] = temp;
|
|
|
|
index = parentIndex;
|
|
parentIndex = (index - 1) / 2;
|
|
}
|
|
|
|
mLength += 1;
|
|
}
|
|
|
|
function remove()
|
|
{
|
|
if(mLength == 0)
|
|
throw "PQ.remove() - No items to remove";
|
|
|
|
local data = mData[0];
|
|
mLength -= 1;
|
|
mData[0] = mData[mLength];
|
|
|
|
local index = 0;
|
|
local left = 1;
|
|
local right = 2;
|
|
|
|
while(index < mLength)
|
|
{
|
|
local smaller;
|
|
|
|
if(left >= mLength)
|
|
{
|
|
if(right >= mLength)
|
|
break;
|
|
else
|
|
smaller = right;
|
|
}
|
|
else
|
|
{
|
|
if(right >= mLength)
|
|
smaller = left;
|
|
else
|
|
{
|
|
if(mData[left] < mData[right])
|
|
smaller = left;
|
|
else
|
|
smaller = right;
|
|
}
|
|
}
|
|
|
|
if(mData[index] > mData[smaller])
|
|
{
|
|
local temp = mData[index];
|
|
mData[index] = mData[smaller];
|
|
mData[smaller] = temp;
|
|
|
|
index = smaller;
|
|
left = (index * 2) + 1;
|
|
right = left + 1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
function resizeArray()
|
|
{
|
|
if(mLength >= #mData)
|
|
mData.length((#mData + 1) * 2 - 1);
|
|
}
|
|
|
|
function hasData()
|
|
{
|
|
return mLength != 0;
|
|
}
|
|
}
|
|
|
|
class Stack
|
|
{
|
|
mHead = null;
|
|
|
|
function push(data)
|
|
{
|
|
local t = { data = data, next = mHead };
|
|
mHead = t;
|
|
}
|
|
|
|
function pop()
|
|
{
|
|
if(mHead is null)
|
|
throw "Stack.pop() - No items to pop";
|
|
|
|
local item = mHead;
|
|
mHead = mHead.next;
|
|
|
|
return item.data;
|
|
}
|
|
|
|
function hasData()
|
|
{
|
|
return mHead !is null;
|
|
}
|
|
}
|
|
|
|
class Queue
|
|
{
|
|
mHead = null;
|
|
mTail = null;
|
|
|
|
function push(data)
|
|
{
|
|
local t = { data = data, next = null };
|
|
|
|
if(mTail is null)
|
|
{
|
|
mHead = t;
|
|
mTail = t;
|
|
}
|
|
else
|
|
{
|
|
mTail.next = t;
|
|
mTail = t;
|
|
}
|
|
}
|
|
|
|
function pop()
|
|
{
|
|
if(mTail is null)
|
|
throw "Queue.pop() - No items to pop";
|
|
|
|
local item = mHead;
|
|
mHead = mHead.next;
|
|
|
|
if(mHead is null)
|
|
mTail = null;
|
|
|
|
return item.data;
|
|
}
|
|
|
|
function hasData()
|
|
{
|
|
return mHead !is null;
|
|
}
|
|
}
|
|
|
|
writefln("Priority queue (heap)");
|
|
|
|
local prioQ = PQ();
|
|
|
|
for(i : 0 .. 10)
|
|
prioQ.insert(math.rand(0, 20));
|
|
|
|
while(prioQ.hasData())
|
|
writefln(prioQ.remove());
|
|
|
|
writefln();
|
|
writefln("Stack");
|
|
|
|
local stack = Stack();
|
|
|
|
for(i : 0 .. 5)
|
|
stack.push(i + 1);
|
|
|
|
while(stack.hasData())
|
|
writefln(stack.pop());
|
|
|
|
writefln();
|
|
writefln("Queue");
|
|
|
|
local queue = Queue();
|
|
|
|
for(i : 0 .. 5)
|
|
queue.push(i + 1);
|
|
|
|
while(queue.hasData())
|
|
writefln(queue.pop());
|
|
|
|
writefln();
|
|
}
|
|
|
|
// opApply tests.
|
|
{
|
|
class Test
|
|
{
|
|
mData = [4, 5, 6];
|
|
|
|
function opApply(extra)
|
|
{
|
|
if(isString(extra) && extra == "reverse")
|
|
{
|
|
local function iterator_reverse(index)
|
|
{
|
|
index--;
|
|
|
|
if(index < 0)
|
|
return;
|
|
|
|
return index, mData[index];
|
|
}
|
|
|
|
return iterator_reverse, this, #mData;
|
|
}
|
|
else
|
|
{
|
|
local function iterator(index)
|
|
{
|
|
index++;
|
|
|
|
if(index >= #mData)
|
|
return;
|
|
|
|
return index, mData[index];
|
|
}
|
|
|
|
return iterator, this, -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
local test = Test();
|
|
|
|
foreach(k, v; test)
|
|
writefln("test[", k, "] = ", v);
|
|
|
|
writefln();
|
|
|
|
foreach(k, v; test, "reverse")
|
|
writefln("test[", k, "] = ", v);
|
|
|
|
writefln();
|
|
|
|
test =
|
|
{
|
|
fork = 5,
|
|
knife = 10,
|
|
spoon = "hi"
|
|
};
|
|
|
|
foreach(k, v; test)
|
|
writefln("test[", k, "] = ", v);
|
|
|
|
test = [5, 10, "hi"];
|
|
|
|
writefln();
|
|
|
|
foreach(k, v; test)
|
|
writefln("test[", k, "] = ", v);
|
|
|
|
writefln();
|
|
|
|
foreach(k, v; test, "reverse")
|
|
writefln("test[", k, "] = ", v);
|
|
|
|
writefln();
|
|
|
|
foreach(k, v; "hello")
|
|
writefln("str[", k, "] = ", v);
|
|
|
|
writefln();
|
|
|
|
foreach(k, v; "hello", "reverse")
|
|
writefln("str[", k, "] = ", v);
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Testing upvalues in for loops.
|
|
{
|
|
local arr = array.new(10);
|
|
|
|
for(i : 0 .. 10)
|
|
arr[i] = function() { return i; };
|
|
|
|
writefln("This should be the values 0 through 9:");
|
|
|
|
foreach(func; arr)
|
|
writefln(func());
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Testing nested functions.
|
|
{
|
|
function outer()
|
|
{
|
|
local x = 3;
|
|
|
|
function inner()
|
|
{
|
|
x++;
|
|
writefln("inner x: ", x);
|
|
}
|
|
|
|
writefln("outer x: ", x);
|
|
inner();
|
|
writefln("outer x: ", x);
|
|
|
|
return inner;
|
|
}
|
|
|
|
local func = outer();
|
|
func();
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Testing Exceptions.
|
|
{
|
|
function thrower(x)
|
|
{
|
|
if(x >= 3)
|
|
throw "Sorry, x is too big for me!";
|
|
}
|
|
|
|
function tryCatch(iterations)
|
|
{
|
|
try
|
|
{
|
|
for(i : 0 .. iterations)
|
|
{
|
|
writefln("tryCatch: ", i);
|
|
thrower(i);
|
|
}
|
|
}
|
|
catch(e)
|
|
{
|
|
writefln("tryCatch caught: ", e);
|
|
throw e;
|
|
}
|
|
finally
|
|
writefln("tryCatch finally");
|
|
}
|
|
|
|
try
|
|
{
|
|
tryCatch(2);
|
|
tryCatch(5);
|
|
}
|
|
catch(e)
|
|
writefln("caught: ", e);
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Testing arrays.
|
|
{
|
|
local array = [7, 9, 2, 3, 6];
|
|
|
|
array.sort();
|
|
|
|
foreach(i, v; array)
|
|
writefln("arr[", i, "] = ", v);
|
|
|
|
array ~= ["foo", "far"];
|
|
|
|
writefln();
|
|
|
|
foreach(i, v; array)
|
|
writefln("arr[", i, "] = ", v);
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Testing vararg functions.
|
|
{
|
|
function vargs(vararg)
|
|
{
|
|
local args = [vararg];
|
|
|
|
writefln("num varargs: ", #args);
|
|
|
|
foreach(i, v; args)
|
|
writefln("args[", i, "] = ", v);
|
|
}
|
|
|
|
vargs();
|
|
|
|
writefln();
|
|
|
|
vargs(2, 3, 5, "foo", "bar");
|
|
|
|
writefln();
|
|
}
|
|
|
|
// Testing switches.
|
|
{
|
|
foreach(v; ["hi", "bye", "foo"])
|
|
{
|
|
switch(v)
|
|
{
|
|
case "hi":
|
|
writefln("switched to hi");
|
|
break;
|
|
|
|
case "bye":
|
|
writefln("switched to bye");
|
|
break;
|
|
|
|
default:
|
|
writefln("switched to something else");
|
|
break;
|
|
}
|
|
}
|
|
|
|
writefln();
|
|
|
|
foreach(v; [null, false, 1, 2.3, 'x', "hi"])
|
|
{
|
|
switch(v)
|
|
{
|
|
case null: writefln("null"); break;
|
|
case false: writefln("false"); break;
|
|
case 1: writefln("1"); break;
|
|
case 2.3: writefln("2.3"); break;
|
|
case 'x': writefln("x"); break;
|
|
case "hi": writefln("hi"); break;
|
|
}
|
|
}
|
|
|
|
writefln();
|
|
|
|
class A
|
|
{
|
|
mValue;
|
|
|
|
this(value)
|
|
{
|
|
mValue = value;
|
|
}
|
|
|
|
function opCmp(other)
|
|
{
|
|
assert(other as A);
|
|
return mValue <=> other.mValue;
|
|
}
|
|
}
|
|
|
|
local a1 = A(1);
|
|
local a2 = A(2);
|
|
local a3 = A(3);
|
|
|
|
for(s : 1 .. 4)
|
|
{
|
|
local ss = A(s);
|
|
|
|
switch(ss)
|
|
{
|
|
case a1:
|
|
writefln(1);
|
|
break;
|
|
|
|
case a2:
|
|
writefln(2);
|
|
break;
|
|
|
|
case a3:
|
|
writefln(3);
|
|
break;
|
|
}
|
|
}
|
|
} |