Mark McGranaghan 8d31ec147c move to vendor
2012-11-17 08:21:42 -08:00

11 KiB

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

}