Index: main.d
==================================================================
--- main.d
+++ main.d
@@ -49,11 +49,19 @@
 		string line = readln();
 		if( line.startsWith("exit") || line.startsWith("quit") )
 			return false;
 		try {
 			if( tryRun(line) )
+			{
+				// for debugging.
+				//try {
+				//	writeln(tableToAST("@v", cast(Table)lastVal));
+				//} catch(Throwable e) {
+				//	writeln(e);
+				//}
 				writeln(lastVal);
+			}
 		} catch(Throwable e) {
 			writeln(e);
 		}
 		return true;
 	}

Index: polemy/eval.d
==================================================================
--- polemy/eval.d
+++ polemy/eval.d
@@ -186,10 +186,120 @@
 		{
 			throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
 		}
 	);
 }
+
+// [TODO] Optimization
+Value macroEval(AST e, Table ctx, bool AlwaysMacro)
+{
+	Layer theLayer = "@v";
+
+	Table pos = new Table;
+	pos.set("filename", theLayer, new StrValue(e.pos.filename));
+	pos.set("lineno",   theLayer, new IntValue(BigInt(e.pos.lineno)));
+	pos.set("column",   theLayer, new IntValue(BigInt(e.pos.column)));
+	return e.match(
+		(StrLiteral e)
+		{
+			Table t = new Table;
+			t.set("pos",  theLayer, pos);
+			t.set("is",   theLayer, new StrValue("str"));
+			t.set("data", theLayer, new StrValue(e.data));
+			return t;
+		},
+		(IntLiteral e)
+		{
+			Table t = new Table;
+			t.set("pos",  theLayer, pos);
+			t.set("is",   theLayer, new StrValue("int"));
+			t.set("data", theLayer, new IntValue(e.data));
+			return t;
+		},
+		(VarExpression e)
+		{
+			Table t = new Table;
+			t.set("pos",  theLayer, pos);
+			t.set("is",   theLayer, new StrValue("var"));
+			t.set("name", theLayer, new StrValue(e.var));
+			return t;
+		},
+		(LayeredExpression e)
+		{
+			if( AlwaysMacro )
+			{
+				Table t = new Table;
+				t.set("pos",  theLayer, pos);
+				t.set("is",   theLayer, new StrValue("lay"));
+				t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
+				return cast(Value)t;
+			}
+			else
+			{
+				return eval(e.expr, ctx, true, e.lay);
+			}
+		},
+		(LetExpression e)
+		{
+			Table t = new Table;
+			t.set("pos",  theLayer, pos);
+			t.set("is",   theLayer, new StrValue("let"));
+			t.set("name", theLayer, new StrValue(e.var));
+			t.set("init", theLayer, macroEval(e.init,ctx,AlwaysMacro));
+			t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro));
+			return t;
+		},
+		(FuncallExpression e)
+		{
+			// [TODO] @macro invokation!!!!
+			//    if e.fun is varname and its ctx[@macro] is set, switch to usual eval
+			Table t = new Table;
+			t.set("pos",  theLayer, pos);
+			t.set("is",   theLayer, new StrValue("app"));
+			t.set("fun", theLayer, macroEval(e.fun,ctx,AlwaysMacro));
+			Table args = new Table;
+			foreach_reverse(a; e.args) {
+				Table cons = new Table;
+				cons.set("car",theLayer,macroEval(a,ctx,AlwaysMacro));
+				cons.set("cdr",theLayer,args);
+				args = cons;
+			}
+			t.set("arg", theLayer, args);
+			return t;
+		},
+		(FunLiteral e)
+		{
+			Table t = new Table;
+			t.set("pos",   theLayer, pos);
+			t.set("is",    theLayer, new StrValue("fun"));
+			t.set("body",  theLayer, macroEval(e.funbody,ctx,AlwaysMacro));
+			Table param = new Table;
+			foreach_reverse(p; e.params)
+			{
+				Table cons = new Table;
+				Table kv = new Table;
+				kv.set("name", theLayer, new StrValue(p.name));
+				foreach_reverse(lay; p.layers)
+				{
+					Table cons2 = new Table;
+					cons2.set("car", theLayer, new StrValue(lay));
+					cons2.set("cdr", theLayer, kv);
+					kv = cons2;
+				}
+				cons.set("car", theLayer, kv);
+				cons.set("cdr", theLayer, param);
+				param = cons;
+			}
+			t.set("param", theLayer, param);
+			return t;
+		},
+		delegate Value (AST e)
+		{
+			throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e)));
+		}
+	);
+}
 
 unittest
 {
 	auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) );
 	assert_eq( r.val, new IntValue(BigInt(21+21*21)) );

Index: polemy/lex.d
==================================================================
--- polemy/lex.d
+++ polemy/lex.d
@@ -12,11 +12,17 @@
 /*mixin*/
 template ExceptionWithPosition()
 {
 	const LexPosition pos;
 	this( const LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null )
-		{ super(sprintf!"[%s] %s"(pos, msg), file, line, next); this.pos = pos; }
+	{
+		if(pos is null)
+			super(sprintf!"[??] %s"(msg), file, line, next);
+		else
+			super(sprintf!"[%s] %s"(pos, msg), file, line, next);
+		this.pos = pos;
+	}
 }
 
 /// Thrown when encountered an EOF in the middle of a lexical token
 
 class UnexpectedEOF : Exception

Index: polemy/value.d
==================================================================
--- polemy/value.d
+++ polemy/value.d
@@ -5,10 +5,11 @@
  * Runtime data structures for Polemy programming language.
  */
 module polemy.value;
 import polemy._common;
 import polemy.lex;
+import polemy.ast;
 
 /// Raised when something went wrong in runtime
 
 class RuntimeException : Exception
 {
@@ -131,10 +132,25 @@
 		}
 		if( prototype is null )
 			throw new RuntimeException(pos, sprintf!"variable %s not found"(i));
 		return prototype.get(i, lay, pos);
 	}
+
+	T access(T,S...)( Layer lay, string path, S rest )
+	{
+		static if( rest.length == 0 )
+		{
+			if( this.has(path, lay) )
+				return cast(T) this.get(path, lay);
+		}
+		else
+		{
+			if(auto next = this.access!Table(lay,path))
+				return next.access!T(lay,rest);
+		}
+		return null;
+	}
 
 private:
 	Table                prototype;
 	Kind                 kind;
 	Value[Layer][string] data;
@@ -188,5 +204,126 @@
 	assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) );
 	assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) );
 
 	// [TODO] define the semantics and test @layers
 }
+
+immutable(LexPosition) extractPos( Table t )
+{
+	Layer theLayer = "@v";
+	if(auto tt = t.access!Table(theLayer, "pos"))
+	{
+		auto fn = tt.access!StrValue(theLayer, "filename");
+		auto ln = tt.access!IntValue(theLayer, "lineno");
+		auto cl = tt.access!IntValue(theLayer, "column");
+		if(fn !is null && ln !is null && cl !is null)
+			return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt);
+	}
+	return null;
+}
+
+Value[] tableAsConsList( Layer theLayer, Table t )
+{
+	Value[] result;
+	while(t)
+		if(auto v  = t.access!Value(theLayer, "car"))
+		{
+			result ~= v;
+			t = t.access!Table(theLayer, "cdr");
+		}
+		else
+			break;
+	return result;
+}
+
+AST[] tableToASTList( Layer theLayer, Table t )
+{
+	AST[] result;
+	foreach(v; tableAsConsList(theLayer, t))
+		if(auto t = cast(Table)v)
+			result ~= tableToAST(theLayer,t);
+		else
+			throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)");
+	return result;
+}
+
+AST tableToAST( Layer theLayer, Table t )
+{
+	auto nodeType = t.access!StrValue(theLayer, "is");
+	if( nodeType is null )
+		throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}");
+	auto pos = extractPos(t);
+	switch(nodeType.data)
+	{
+	case "int":
+		if(auto v = t.access!IntValue(theLayer, "data"))
+			return new IntLiteral(pos, v.data);
+		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`);
+	case "str":
+		if(auto v = t.access!StrValue(theLayer, "data"))
+			return new StrLiteral(pos, v.data);
+		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`);
+	case "var":
+		if(auto v = t.access!StrValue(theLayer, "name"))
+			return new VarExpression(pos, v.data);
+		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`);
+	case "lay":
+		if(auto v = t.access!StrValue(theLayer, "layer"))
+			if(auto e = t.access!Table(theLayer, "expr"))
+				return new LayeredExpression(pos, v.data, tableToAST(theLayer,e));
+			else
+				throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`);
+		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`);
+	case "let":
+		if(auto n = t.access!StrValue(theLayer, "name"))
+		if(auto e = t.access!Table(theLayer, "init"))
+		if(auto b = t.access!Table(theLayer, "expr"))
+		{
+			string nn = n.data;
+			auto ee = tableToAST(theLayer, e);
+			auto bb = tableToAST(theLayer, b);
+			Layer lay="";
+			if(auto l = t.access!StrValue(theLayer, "layer"))
+				lay = l.data;
+			return new LetExpression(pos, nn, lay, ee, bb);
+		}
+		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`);
+	case "app":
+		if(auto f = t.access!Table(theLayer, "fun"))
+		if(auto a = t.access!Table(theLayer, "arg"))
+			return new FuncallExpression(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a));
+		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, arg:???}`);
+	case "fun":
+		if(auto p = t.access!Table(theLayer, "param"))
+		if(auto b = t.access!Table(theLayer, "body"))
+		{
+			Parameter[] ps;
+			foreach(v; tableAsConsList(theLayer, p))
+			{
+				if(auto tt = cast(Table)v)
+				if(auto ss = tt.access!StrValue(theLayer, "name"))
+				if(auto ll = tt.access!Table(theLayer, "layer"))
+				{
+					Layer[] ls;
+					foreach(lll; tableAsConsList(theLayer, ll))
+						if(auto l = cast(StrValue)lll)
+							ls ~= l.data;
+						else
+							throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {bad fun params}`);
+					ps ~= new Parameter(ss.data, ls);
+					continue;
+				}
+				else
+				{
+					Layer[] emp;
+					ps ~= new Parameter(ss.data, emp);
+				}
+				throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {bad fun params}`);
+			}
+			auto bb = tableToAST(theLayer, b);
+			return new FunLiteral(pos,ps,bb);
+		}
+		throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`);
+	default:
+		throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data));
+	}
+}