/**
 * Authors: k.inaba
 * License: NYSL 0.9982 http://www.kmonos.net/nysl/
 *
 * Error Information for Polemy Programming Language
 */
module polemy.failure;
import polemy._common;

/// Represents a position in source codes

alias immutable(LexPosition_t) LexPosition;

/// Represents a position in source codes

class LexPosition_t
{
	immutable string filename; /// name of the source file
	immutable int    lineno;   /// 1-origin
	immutable int    column;   /// 1-origin

	mixin SimpleClass;
	override string toString() const
		{ return sprintf!("%s:%d:%d")(filename, lineno, column); }
	static LexPosition dummy;
	static this(){ dummy = new LexPosition("<nowhere>",0,0); }
}

unittest
{
	auto p = new LexPosition("hello.cpp", 123, 45);

	assert_eq( p.filename, "hello.cpp" );
	assert_eq( p.lineno, 123 );
	assert_eq( p.column, 45 );
	assert_eq( text(p), "hello.cpp:123:45" );

	assert( !__traits(compiles, new LexPosition) );
	assert( !__traits(compiles, p.filename="foo") );
	assert( !__traits(compiles, p.lineno  =789) );
	assert( !__traits(compiles, p.column  =222) );

	auto q = new LexPosition("hello.cpp", 123, 46);
	assert_lt( p, q );
	assert_ne( p, q );
}

/*mixin*/
template ExceptionWithPosition()
{
	LexPosition pos;
	this( LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null )
	{
		string fullmsg = pos is null ? sprintf!("\n[??] %s")(msg)
		                             : sprintf!("\n[%s] %s")(pos, msg);
		for(int i=0; i<callstack_pos.length || i<callstack_msg.length; ++i)
		{
			LexPosition p = (i<callstack_pos.length ? callstack_pos[i] : null);
			string      m = (i<callstack_msg.length ? callstack_msg[i] : null);
			fullmsg ~= p is null ? sprintf!("\n[??] %s")(m)
		                         : sprintf!("\n[%s] %s")(p, m);
		}
		super(fullmsg, file, line, next);
		this.pos = pos;
	}
	this( string msg, string file=null, size_t line=0, Throwable next=null )
	{
		this(null, msg, file, line, next);
	}
}

class UnexpectedEOF    : Exception { mixin ExceptionWithPosition; } /// EOF during lexing/parsing
class LexException     : Exception { mixin ExceptionWithPosition; } /// Lexer errors
class ParseException   : Exception { mixin ExceptionWithPosition; } /// Parser errors
class RuntimeException : Exception { mixin ExceptionWithPosition; } /// Evaluator errors

/// Per-thread call stack management.
/// This scoped class's ctor&dtor maintain the callstack.
/// TODO: make it "per-evaluator" !!!!!!!!!!!

scope class PushCallStack
{
	this(LexPosition pos, string msg) { callstackEnterFunction(pos,msg); }
	~this() { callstackLeaveFunction(); }
}

LexPosition[] callstack_pos;
string[]      callstack_msg;

private void callstackEnterFunction(LexPosition pos, string msg)
{
	callstack_pos ~= pos;
	callstack_msg ~= msg;
}

private void callstackLeaveFunction()
{
	callstack_pos.length -= 1;
	callstack_msg.length -= 1;
}
