2016-12-27 08:10:47 -08:00

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;
}
}
}