Index: main.d
==================================================================
--- main.d
+++ main.d
@@ -12,10 +12,11 @@
 import polemy.failure;
 import polemy.layer;
 import polemy.parse;
 import polemy.ast;
 import polemy.eval;
+import polemy.runtime;
 
 enum VersionNoMajor = 0;
 enum VersionNoMinor = 1;
 enum VersionNoRev   = 0;
 
@@ -26,10 +27,11 @@
 Evaluator ev;
 	/// Load the prelude environment
 	this()
 	{
 		ev = new Evaluator;
+		enrollRuntimeLibrary(ev);
 	}
 
 	/// Print the version number etc.
 	void greet()
 	{

Index: polemy/ast.d
==================================================================
--- polemy/ast.d
+++ polemy/ast.d
@@ -11,18 +11,19 @@
 
 ///
 abstract class AST
 {
 	LexPosition pos;
+
 	mixin SimpleConstructor;
-	mixin SimplePatternMatch;
 }
 
 ///
 class Int : AST
 {
 	BigInt data;
+
 	mixin SimpleClass;
 	this(LexPosition pos, int n) {super(pos); data = n;}
 	this(LexPosition pos, long n) {super(pos); data = n;}
 	this(LexPosition pos, BigInt n) {super(pos); data = n;}
 	this(LexPosition pos, string n) {super(pos); data = BigInt(n);}
@@ -30,25 +31,28 @@
 
 ///
 class Str : AST
 {
 	string data;
+
 	mixin SimpleClass;
 }
 
 ///
 class Var : AST
 {
 	string name;
+
 	mixin SimpleClass;
 }
 
 ///
 class Lay : AST
 {
 	Layer layer;
 	AST   expr;
+
 	mixin SimpleClass;
 }
 
 ///
 class Let : AST
@@ -55,36 +59,39 @@
 {
 	string name;
 	Layer  layer;
 	AST    init;
 	AST    expr;
+
 	mixin SimpleClass;
 }
 
 ///
 class App : AST
 {
 	AST   fun;
-	AST[] args;
-	this(LexPosition pos, AST fun, AST[] args...)
-		{ super(pos); this.fun=fun; this.args=args.dup; }
+	AST[] args;
+
 	mixin SimpleClass;
+	this(LexPosition pos, AST fun, AST[] args...) { super(pos); this.fun=fun; this.args=args.dup; }
 }
 
 ///
 class Parameter
 {
 	string  name;
 	Layer[] layers;
+
 	mixin SimpleClass;
 }
 
 ///
 class Fun : AST
 {
 	Parameter[] params;
 	AST         funbody;
+
 	mixin SimpleClass;
 }
 
 /// Handy Generator for AST nodes. To use this, mixin EasyAst;
 

Index: polemy/eval.d
==================================================================
--- polemy/eval.d
+++ polemy/eval.d
@@ -10,41 +10,45 @@
 import polemy.ast;
 import polemy.parse;
 import polemy.value;
 import polemy.layer;
 
+/// Objects for maitaining global environment and evaluation of expression on it
 class Evaluator
 {
 public:
+	/// Initialize evaluator with empty context
 	this() { theContext = new Table; }
 
+	/// Evaluate the AST
 	Value evalAST(AST e)
 	{
 		return eval(e, ValueLayer, theContext, OverwriteCtx);
 	}
 
+	/// Evaluate the string
 	Value evalString(S,T...)(S str, T fn_ln_cn)
 	{
 		return evalAST(parseString(str,fn_ln_cn));
 	}
 
+	/// Evaluate the file
 	Value evalFile(S,T...)(S filename, T ln_cn)
 	{
 		return evalAST(parseFile(filename,ln_cn));
 	}
 
+	/// Get the global context
 	Table globalContext()
 	{
 		return theContext;
 	}
 
 private:
 	Table theContext;
 
-private:
 	enum : bool { CascadeCtx=false, OverwriteCtx=true };
-	
 	Value eval( AST e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
 		// dynamic-overload-resolution-pattern: modify here
 		enum funName = "eval";
 		alias TypeTuple!(e,lay,ctx,overwriteCtx) params;
@@ -59,148 +63,78 @@
 
 		// dynamic-overload-resolution-pattern: default behavior
 		assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined"));
 	}
 
-private:
 	Value eval( Str e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
-		Value v = new StrValue(e.data);
-		if( lay==RawMacroLayer || lay==MacroLayer )
-		{
-			auto ast = new Table;
-			ast.set("pos",  ValueLayer, fromPos(e.pos));
-			ast.set("is",   ValueLayer, new StrValue("str"));
-			ast.set("data", ValueLayer, v);
-			return ast;
-		}
+		if( isMacroishLayer(lay) )
+			return ast2table(e, (AST e){return eval(e,lay,ctx);});
 		if( lay==ValueLayer )
-			return v;
-		return lift(v, lay, ctx, e.pos);
+			return new StrValue(e.data);
+		return lift(new StrValue(e.data), lay, ctx, e.pos);
 	}
 
 	Value eval( Int e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
-		Value v = new IntValue(e.data);
-		if( lay==RawMacroLayer || lay==MacroLayer )
-		{
-			auto ast = new Table;
-			ast.set("pos",  ValueLayer, fromPos(e.pos));
-			ast.set("is",   ValueLayer, new StrValue("int"));
-			ast.set("data", ValueLayer, v);
-			return ast;
-		}
+		if( isMacroishLayer(lay) )
+			return ast2table(e, (AST e){return eval(e,lay,ctx);});
 		if( lay==ValueLayer )
-			return v;
-		return lift(v, lay, ctx, e.pos);
+			return new IntValue(e.data);
+		return lift(new IntValue(e.data), lay, ctx, e.pos);
 	}
 
 	Value eval( Var e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
-		if( lay==RawMacroLayer || lay==MacroLayer )
-		{
+		if( isMacroishLayer(lay) )
 			if( ctx.has(e.name,MacroLayer) )
 				return ctx.get(e.name, MacroLayer, e.pos);
-			auto ast = new Table;
-			ast.set("pos",  ValueLayer, fromPos(e.pos));
-			ast.set("is",   ValueLayer, new StrValue("var"));
-			ast.set("name", ValueLayer, new StrValue(e.name));
-			return ast;
-		}
+			else
+				return ast2table(e, (AST e){return eval(e,lay,ctx);});
 		if( lay==ValueLayer || ctx.has(e.name, lay) )
 			return ctx.get(e.name, lay, e.pos);
 		return lift(ctx.get(e.name, ValueLayer, e.pos), lay, ctx, e.pos);
 	}
 
 	Value eval( App e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
 		Value f = eval( e.fun, lay, ctx );
-		if( lay==RawMacroLayer || lay==MacroLayer )
-		{
+		if( isMacroishLayer(lay) )
 			if( auto ff = cast(FunValue)f )
 				return invokeFunction(ff, e.args, MacroLayer, ctx, e.pos);
-			Table ast = new Table;
-			ast.set("pos",  ValueLayer, fromPos(e.pos));
-			ast.set("is",   ValueLayer, new StrValue("app"));
-			ast.set("fun",  ValueLayer, f);
-			Table args = new Table;
-			foreach_reverse(a; e.args)
-				args = makeCons(eval(a, lay, ctx), args);
-			ast.set("args", ValueLayer, args);
-			return ast;
-		}
-		else
-		{
-			return invokeFunction(f, e.args, lay, ctx, e.pos);
-		}
+			else
+				return ast2table(e, (AST e){return eval(e,lay,ctx);});
+		return invokeFunction(f, e.args, lay, ctx, e.pos);
 	}
 	
 	Value eval( Fun e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
-		if( lay==RawMacroLayer || lay==MacroLayer )
-		{
-			Table t = new Table;
-			t.set("pos",     ValueLayer, fromPos(e.pos));
-			t.set("is",      ValueLayer, new StrValue("fun"));
-			t.set("funbody", ValueLayer, eval(e.funbody,lay,ctx));
-			Table params = new Table;
-			foreach_reverse(p; e.params)
-			{
-				Table lays = new Table;
-				foreach_reverse(l; p.layers)
-					lays = makeCons(new StrValue(l), lays);
-				Table kv = new Table;
-				kv.set("name", ValueLayer, new StrValue(p.name));
-				kv.set("layers", ValueLayer, lays);
-				Table cons = new Table;
-				params = makeCons(kv, params);
-			}
-			t.set("params", ValueLayer, params);
-			return t;
-		}
+		if( isMacroishLayer(lay) )
+			return ast2table(e, (AST e){return eval(e,lay,ctx);});
 		else
-		{
 			return createNewFunction(e, ctx);
-		}
 	}
 	
 	Value eval( Lay e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
-		if( lay == RawMacroLayer )
-		{
-			Value r = eval(e.expr, lay, ctx);
-			auto ast = new Table; // todo: pos
-			ast.set("pos",   ValueLayer, fromPos(e.pos));
-			ast.set("is",    ValueLayer, new StrValue("lay"));
-			ast.set("layer", ValueLayer, new StrValue(e.layer));
-			ast.set("expr",  ValueLayer, r);
-			return ast;
-		}
+		if( isNoLayerChangeLayer(lay) )
+			return ast2table(e, (AST e){return eval(e,lay,ctx);});
 		else
 			return eval(e.expr, e.layer, ctx);
 	}
 	
 	Value eval( Let e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx )
 	{
 		// todo @macro let
-		if( lay==RawMacroLayer || lay==MacroLayer )
-		{
-			auto ast = new Table; // todo: pos
-			ast.set("pos",   ValueLayer, fromPos(e.pos));
-			ast.set("is",    ValueLayer, new StrValue("let"));
-			ast.set("name",  ValueLayer, new StrValue(e.name));
-			ast.set("layer", ValueLayer, new StrValue(e.layer));
-			ast.set("init",  ValueLayer, eval(e.init, lay, ctx));
-			ast.set("expr",  ValueLayer, eval(e.expr, lay, ctx));
-			return ast;
-		}
+		if( isMacroishLayer(lay) )
+			return ast2table(e, (AST e){return eval(e,lay,ctx);});
 		else
 		{
 			if( !overwriteCtx )
 				ctx = new Table(ctx, Table.Kind.NotPropagateSet);
 			Value ri = eval(e.init, lay, ctx);
-			string theLayer = e.layer.empty ? (lay==RawMacroLayer ? MacroLayer : lay) : e.layer;
+			string theLayer = e.layer.empty ? lay : e.layer; // neutral layer
 			ctx.set(e.name, theLayer, ri);
 			return eval(e.expr, lay, ctx, OverwriteCtx);
 		}
 	}
 
@@ -214,17 +148,19 @@
 				if( p.layers.empty )
 					newCtx.set(p.name, (lay==RawMacroLayer ? MacroLayer : lay), eval(args[i], lay, ctx));
 				else
 					foreach(argLay; p.layers)
 						newCtx.set(p.name, argLay, eval(args[i], argLay, ctx));
-			return f.invoke(pos, lay, newCtx);
+			return f.invoke(lay==RawMacroLayer ? MacroLayer : lay, newCtx, pos);
 		}
 		throw genex!RuntimeException(pos, text("tried to call non-function: ",_f));
 	}
 
 	Value lift(Value v, Layer lay, Table ctx, LexPosition pos=null)
 	{
+		assert( !isMacroishLayer(lay), "lift to the @macro layer should not happen" );
+
 		// functions are automatically lifterd
 		if( cast(FunValue) v )
 			return v;
 
 		// similar to invoke Function, but with only one argument bound to ValueLayer
@@ -235,11 +171,11 @@
 			if( ps.length != 1 )
 				throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
 			if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer )
 			{
 				newCtx.set(ps[0].name, ValueLayer, v);
-				return f.invoke(pos, ValueLayer, newCtx);
+				return f.invoke(ValueLayer, newCtx, pos);
 			}
 			else
 				throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer");
 		}
 		throw genex!RuntimeException(pos, "tried to call non-function");
@@ -275,54 +211,69 @@
 					return this.defCtx.opCmp(rhs.defCtx);
 				}
 				assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
 			}
 
-			override Value invoke(LexPosition pos, Layer lay, Table ctx)
+			override Value invoke(Layer lay, Table ctx, LexPosition pos)
 			{
 				if( lay == MacroLayer )
 					return eval(ast.funbody, lay, ctx);
-				auto macroed = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx));
-				return eval(macroed, lay, ctx);
+				if( afterMacroAST is null )
+					afterMacroAST = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx));
+				return eval(afterMacroAST, lay, ctx);
 			}
+
+			AST afterMacroAST;
 		}
 		return new UserDefinedFunValue(e,ctx);
 	}
 
 public:
-	/// TODO: move up
-	/// TDOO: to other layers?
-	void addPrimitive(R,T...)(string name, Layer lay, R delegate (T) dg)
+	/// Add primitive function to the global context
+	void addPrimitive(R,T...)(string name, Layer defLay, R delegate (T) dg)
 	{
 		class NativeFunValue : FunValue
 		{
-			Parameter[] params_data;
-			override string toString() { return sprintf!"(native:%x)"(dg.funcptr); }
 			override const(Parameter[]) params() { return params_data; }
-			override Table definitionContext() { return new Table; } // todo: cache
-			this(){
+			override Table definitionContext()   { return theContext; }
+
+			override string toString() { return sprintf!"(native:%x)"(dg.funcptr); }
+			override int opCmp(Object rhs) {
+				if(auto r = cast(NativeFunValue)rhs) return typeid(typeof(dg)).compare(&dg,&r.dg);
+				if(auto r = cast(Value)rhs)          return typeid(this).opCmp(typeid(r));
+				throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other");
+			}
+			mixin SimpleToHash;
+
+			R delegate(T) dg;
+			Parameter[] params_data;
+
+			this(R delegate(T) dg)
+			{
+				this.dg = dg;
 				foreach(i, Ti; T)
 					params_data ~= new Parameter(text(i), []);
 			}
-			override Value invoke(LexPosition pos, Layer lay, Table ctx)
+
+			override Value invoke(Layer lay, Table ctx, LexPosition pos)
 			{
-				if( lay != ValueLayer )
-					throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function");
+				if( lay != defLay )
+					throw genex!RuntimeException(pos, text("only ", defLay, " layer can call native function: ", name));
 				T typed_args;
 				foreach(i, Ti; T) {
-					typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer);
+					typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer, pos);
 					if( typed_args[i] is null )
-						throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1));
+						throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d of native function: %s"(i+1,name));
 				}
 				try {
 					return dg(typed_args);
 				} catch( RuntimeException e ) {
 					throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e;
 				}
 			}
 		}
-		theContext.set(name, lay, new NativeFunValue);
+		theContext.set(name, defLay, new NativeFunValue(dg));
 	}
 }
 
 version(unittest) import polemy.runtime;
 unittest

Index: polemy/layer.d
==================================================================
--- polemy/layer.d
+++ polemy/layer.d
@@ -14,8 +14,18 @@
 
 enum : Layer
 {
 	SystemLayer   = "(system)",   /// Predefined layer for internal data
 	ValueLayer    = "@value",     /// Predefined layer for normal run
-	MacroLayer    = "@macro",     /// Predefined layer for macro run
-	RawMacroLayer = "(rawmacro)", /// Predefined layer for raw-macro run
+	MacroLayer    = "@macro",     /// Predefined layer for macro run (@lay() changes layer)
+	RawMacroLayer = "(rawmacro)", /// Predefined layer for macro run (@lay() becomes AST)
+}
+
+bool isMacroishLayer( Layer lay )
+{
+	return lay==MacroLayer || lay==RawMacroLayer;
+}
+
+bool isNoLayerChangeLayer( Layer lay )
+{
+	return lay==RawMacroLayer;
 }

Index: polemy/value.d
==================================================================
--- polemy/value.d
+++ polemy/value.d
@@ -7,49 +7,71 @@
 module polemy.value;
 import polemy._common;
 import polemy.failure;
 import polemy.ast;
 import polemy.layer;
+import std.string;
 
 /// Runtime values of Polemy
 
 abstract class Value
 {
+	override bool opEquals(Object rhs) { return 0==opCmp(rhs); }
 }
 
 ///
 class IntValue : Value
 {
 	BigInt data;
 
-	mixin SimpleClass;
-	override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); }
+	this(int n) { this.data = n; }
+	this(long n) { this.data = n; }
+	this(BigInt n) { this.data = n; }
+	this(string n) { this.data = BigInt(n); }
+	override string toString() const { return toDecimalString(cast(BigInt)data); }
+	override int opCmp(Object rhs) {
+		if(auto r = cast(IntValue)rhs) return data.opCmp(r.data);
+		if(auto r = cast(Value)rhs)    return typeid(this).opCmp(typeid(r));
+		throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other");
+	}
+	mixin SimpleToHash;
 }
 
 ///
 class StrValue : Value
 {
 	string data;
 
-	mixin SimpleClass;
+	mixin SimpleConstructor;
 	override string toString() const { return data; }
+	override int opCmp(Object rhs) {
+		if(auto r = cast(StrValue)rhs) return typeid(string).compare(&data, &r.data);
+		if(auto r = cast(Value)rhs)    return typeid(this).opCmp(typeid(r));
+		throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other");
+	}
+	mixin SimpleToHash;
 }
 
 ///
-class UndValue : Value
+class UndefinedValue : Value
 {
-	mixin SimpleClass;
+	mixin SimpleConstructor;
 	override string toString() const { return "<undefined>"; }
+	override int opCmp(Object rhs) {
+		if(auto r = cast(StrValue)rhs) return 0;
+		if(auto r = cast(Value)rhs)    return typeid(this).opCmp(typeid(r));
+		throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other");
+	}
+	mixin SimpleToHash;
 }
-
 
 ///
 abstract class FunValue : Value
 {
 	const(Parameter[]) params();
 	Table definitionContext();
-	Value invoke(LexPosition pos, Layer lay, Table ctx);
+	Value invoke(Layer lay, Table ctx, LexPosition pos);
 }
 
 /// Context (variable environment)
 /// Simlar to prototype chain of ECMAScript etc.
 /// But extended with the notion of "Layer"
@@ -336,14 +358,65 @@
 Table fromPos(LexPosition pos)
 {
 	Table t = new Table;
 	if( pos !is null ) {
 		t.set("filename", ValueLayer, new StrValue(pos.filename));
-		t.set("lineno",   ValueLayer, new IntValue(BigInt(pos.lineno)));
-		t.set("column",   ValueLayer, new IntValue(BigInt(pos.column)));
+		t.set("lineno",   ValueLayer, new IntValue(pos.lineno));
+		t.set("column",   ValueLayer, new IntValue(pos.column));
 	} else {
 		t.set("filename", ValueLayer, new StrValue("nullpos"));
-		t.set("lineno",   ValueLayer, new IntValue(BigInt(0)));
-		t.set("column",   ValueLayer, new IntValue(BigInt(0)));
+		t.set("lineno",   ValueLayer, new IntValue(0));
+		t.set("column",   ValueLayer, new IntValue(0));
 	}
 	return t;
 }
+
+/// Convert AST to Table so that it can be used in Polemy
+/// TODO: generalize to DValue2PolemyValue
+
+Value ast2table(T)(T e, Value delegate(AST) rec)
+{
+	assert( typeid(e) == typeid(T) );
+
+	static if(is(T==BigInt) || is(T==long) || is(T==int))
+		return new IntValue(e);
+	else
+	static if(is(T==string))
+		return new StrValue(e);
+	else
+	static if(is(T S : S[]))
+	{
+		Table lst = new Table;
+		foreach_reverse(a; e)
+			static if(is(S : AST))
+				lst = makeCons(rec(a), lst);
+			else
+				lst = makeCons(ast2table(a,rec), lst);
+		return lst;
+	}
+	else
+	static if(is(T : AST))
+	{
+		auto t = new Table;
+		t.set("pos", ValueLayer, fromPos(e.pos));
+		t.set("is" , ValueLayer, new StrValue(typeid(e).name.split(".")[$-1].tolower()));
+		foreach(i,m; e.tupleof)
+			static if(is(typeof(m) : AST))
+				t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m));
+			else
+				t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec));
+		return t;
+	}
+	else
+	static if(is(T == class))
+	{
+		auto t = new Table;
+		foreach(i,m; e.tupleof)
+			static if(is(typeof(m) : AST))
+				t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m));
+			else
+				t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec));
+		return t;
+	}
+	else
+		static assert(false, "unknown type <"~T.stringof~"> during AST encoding");
+}

Index: tricks/tricks.d
==================================================================
--- tricks/tricks.d
+++ tricks/tricks.d
@@ -98,10 +98,23 @@
 	assert( !__traits(compiles, {
 		class Tamp : Tomp { mixin SimpleConstructor; }
 	}) );
 }
 
+/// Mixing-in the MOST-DERIVED-member-wise comparator for a class
+
+template SimpleToHash()
+{
+	override hash_t toHash() const /// member-by-member hash
+	{
+		hash_t h = 0;
+		foreach(mem; this.tupleof)
+			h += typeid(mem).getHash(&mem);
+		return h;
+	}
+}
+
 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
 
 /*mixin*/
 template SimpleCompare()
 {
@@ -114,18 +127,12 @@
 					return false;
 			return true;
 		}
 		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
 	}
-
-	override hash_t toHash() const /// member-by-member hash
-	{
-		hash_t h = 0;
-		foreach(mem; this.tupleof)
-			h += typeid(mem).getHash(&mem);
-		return h;
-	}
+
+	mixin SimpleToHash;
 
 	override int opCmp(Object rhs_) const /// member-by-member compare
 	{
 		if( auto rhs = cast(typeof(this))rhs_ )
 		{
@@ -217,185 +224,11 @@
 	mixin SimpleConstructor;
 	mixin SimpleCompare;
 	mixin SimpleToString;
 }
 
-/// Simple PatternMatcher
-
-/*mixin*/
-template SimplePatternMatch()
-{
-	SPM_Return!(PP) match(string fn=__FILE__, size_t ln=__LINE__, PP...)(PP pts)
-	{
-		foreach(i,_; pts)
-		{
-			alias pts[i] pt; // bug? pts[i]-->pt do not work
-			static if(__traits(compiles, SPM_isMatchTag(pt)))
-			{
-				if( auto v = cast(pt.dynamicType)this )
-					return pt(v.tupleof);
-			}
-			else
-			static if(__traits(compiles, SPM_isMatchAny(pt)))
-			{
-				return pt();
-			}
-			else
-			{
-				if( auto v = cast(SPM_PTT!(pt)[0])this )
-					return pt(v);
-			}
-		}
-		SPM_throwAssertError(fn, ln, "pattern matching failure");
-		assert(false);
-	}
-}
-
-/// Pattern case clause
-
-SPM_MatchTag!(T, fn) when(T, alias fn)()
-{
-	SPM_MatchTag!(T, fn) m;
-	return m;
-}
-
-/// Pattern case clause
-
-SPM_MatchAny!(fn) otherwise(alias fn)()
-{
-	SPM_MatchAny!(fn) m;
-	return m;
-}
-
-// implementation detail of SimplePatternMatch
-
-void SPM_throwAssertError(T...)(T t) { core.exception.onAssertErrorMsg(t); }
-
-struct SPM_MatchTag(T, alias fn)
-{
-	alias T dynamicType;
-	auto opCall(typeof(T.tupleof) s) { return fn(s); }
-}
-
-struct SPM_MatchAny(alias fn)
-{
-	auto opCall() { return fn(); }
-}
-
-template SPM_PTT(alias p)
-{
-	alias ParameterTypeTuple!(p) SPM_PTT;
-}
-
-template SPM_Each(P)
-{	
-	static if(__traits(compiles, SPM_isMatchTag(P.init)))
-		alias typeof(P(P.dynamicType.tupleof)) SPM_Each;
-	else
-	static if(__traits(compiles, SPM_isMatchAny(P.init)))
-		alias typeof(P()) SPM_Each;
-	else
-		alias ReturnType!(P) SPM_Each;
-}
-
-template SPM_aVoid(T:void, TS...) { alias SPM_aVoid!(TS) SPM_aVoid; }
-template SPM_aVoid(T, TS...) { alias TypeTuple!(T,SPM_aVoid!(TS)) SPM_aVoid; }
-template SPM_aVoid() { alias TypeTuple!() SPM_aVoid; }
-
-template SPM_Return(PP...)
-{
-	alias CommonType!(SPM_aVoid!(staticMap!(SPM_Each, PP))) SPM_Return;
-}
-
-void SPM_isMatchTag(T,alias fn)(SPM_MatchTag!(T,fn)){}
-void SPM_isMatchAny(alias fn)(SPM_MatchAny!(fn)){}
-
-unittest
-{
-	static abstract class Base {
-		mixin SimplePatternMatch;
-	}
-	class D1 : Base {
-		int x;
-		real y;
-		mixin SimpleConstructor;
-	}
-	class D2 : Base {
-		string s;
-		mixin SimpleConstructor;
-	}
-	class D3 : Base {
-		int[int] m;
-		mixin SimpleConstructor;
-	}
-
-	Base d1 = new D1(1, 2.3);
-	Base d2 = new D2("foobar");
-	Base d3 = new D3(null); (cast(D3)d3).m[1]=10;
-
-	// normal dispatch
-	assert_eq( d1.match(
-		(D1 x){return 1;},
-		(D2 x){return 2;}
-	), 1);
-	assert_eq( d2.match(
-		(D1 x){return 1;},
-		(D2 x){return 2;}
-	), 2);
-	assert_throw!AssertError( d3.match(
-		(D1 x){return 1;},
-		(D2 x){return 2;}
-	));
-	assert_eq( d3.match(
-		(D1 x){return 1;},
-		(D2 x){return 2;},
-		(Base x){return 3;}
-	), 3);
-	assert_eq( d2.match(
-		(D1 x){return 1;},
-		(D2 x){return 2;},
-		(Base x){return 3;}
-	), 2);
-	assert_eq( d2.match(
-		(D1 x){return 1;},
-		(Base x){return 3;},
-		(D2 x){return 2;}
-	), 3);
-
-	// member decomposing match
-	assert_eq( d1.match(
-		when!(D1, (x, y){return x + cast(int)y;}),
-		when!(D2, (x){return x.length;}),
-		when!(D3, (x){return x[1];})
-	), 3);
-	assert_eq( d2.match(
-		when!(D1, (x, y){return x + cast(int)y;}),
-		when!(D2, (x){return x.length;}),
-		when!(D3, (x){return x[1];})
-	), 6);
-	assert_eq( d3.match(
-		when!(D1, (x, y){return x + cast(int)y;}),
-		when!(D2, (x){return x.length;}),
-		when!(D3, (x){return x[1];})
-	), 10);
-	assert_throw!AssertError( d3.match(
-		when!(D1, (x, y){return x + cast(int)y;}),
-		when!(D2, (x){return x.length;})
-	));
-	assert_eq( d2.match(
-		when!(D1, (x, y){return x + cast(int)y;}),
-		when!(D2, (x){return x.length;}),
-		otherwise!({return 999;})
-	), 6);
-	assert_eq( d2.match(
-		when!(D1, (x, y){return x + cast(int)y;}),
-		otherwise!({return 999;}),
-		when!(D2, (x){return x.length;})
-	), 999);
-}
-
 /// Will be used for dynamic overload resolution pattern
 
 template firstParam(T)
 {
 	alias ParameterTypeTuple!(T)[0] firstParam;
 }