// Goldie: GOLD Engine for D
// Written in the D programming language.

module goldie.base;

import std.conv;
import std.string;

import semitwist.util.all;

public enum SymbolType
{
	NonTerminal  = 0,
	Terminal     = 1,
	Whitespace   = 2,
	EOF          = 3,
	CommentStart = 4,
	CommentEnd   = 5,
	CommentLine  = 6,
	Error        = 7,
}

public string symbolTypeToString(SymbolType type)
{
	switch(type)
	{
	case SymbolType.NonTerminal:
		return "NonTerminal";
	case SymbolType.Terminal:
		return "Terminal";
	case SymbolType.Whitespace:
		return "Whitespace";
	case SymbolType.EOF:
		return "EOF";
	case SymbolType.CommentStart:
		return "CommentStart";
	case SymbolType.CommentEnd:
		return "CommentEnd";
	case SymbolType.CommentLine:
		return "CommentLine";
	case SymbolType.Error:
		return "Error";
	default:
		throw new Exception("Unexpected SymbolType #"~to!string(cast(int)type));
	}
}

unittest
{
	// 'symbolTypeToString' and 'typeToString' expect this to work at compile-time:
	enum test_ctfe_int_to_string = to!string(42);
}

public string fullSymbolTypeToString(SymbolType type)
{
	return typeof(type).stringof ~ "." ~ symbolTypeToString(type);
}

public string[] symbolTypesToStrings(SymbolType[] types)
{
	return map!(string)(types,
		(SymbolType type) { return symbolTypeToString(type); }
	);
}

public string symbolTypesToString(SymbolType[] types)
{
	return types.symbolTypesToStrings().join(", ");
}

struct CharSet
{
	dstring chars;

	const bool opEquals(ref const CharSet s)
	{
		return chars == s.chars;
	}

	string toString()
	{
		return "%s(%sd)".format(typeof(this).stringof, chars.escapeDDQS());
	}
}

struct Symbol
{
	string name;
	SymbolType type;
	int id;
	
	string toString()
	{
		return
			"%s(%s,%s,%s)".format(
				typeof(this).stringof,
				name.escapeDDQS(),
				fullSymbolTypeToString(type),
				id
			);
	}
}

struct Rule
{
	int symbolIndex;
	int[] subSymbolIndicies;

	string toString()
	{
		return "%s( %s, %s )".format(typeof(this).stringof, symbolIndex, subSymbolIndicies);
	}
}

struct DFAState
{
	bool accept;
	int acceptSymbolIndex;
	DFAStateEdge[] edges;

	string toString()
	{
		string edgeStr =
			reduceTo(edges,
				(string a, DFAStateEdge b)
				{
					return a ~ (a==""?"":",") ~ to!string(b);
				}
			);
		return "%s( %s, %s, %s )".format(typeof(this).stringof, accept, acceptSymbolIndex, edges);
	}
}

struct DFAStateEdge
{
	int charSetIndex;
	int targetDFAStateIndex;

	string toString()
	{
		return "%s(%s,%s)".format(typeof(this).stringof, charSetIndex, targetDFAStateIndex);
	}
}

struct LALRState
{
	LALRAction[] actions;

	string toString()
	{
		return "%s( %s )".format(typeof(this).stringof, actions);
	}
}

struct LALRAction
{
	enum Type
	{
		Shift  = 1, // target: LALR State Index
		Reduce = 2, // target: Rule Index
		Goto   = 3, // target: LALR State Index
		Accept = 4  // target: unused
	}

	int symbolId;
	Type type;
	int target;

	string toString()
	{
		return
			"%s(%s,%s,%s)".format(
				typeof(this).stringof, symbolId, fullTypeToString(type), target
			);
	}

	public static string typeToString(Type type)
	{
		switch(type)
		{
		case LALRAction.Type.Shift:
			return "Shift";
		case LALRAction.Type.Reduce:
			return "Reduce";
		case LALRAction.Type.Goto:
			return "Goto";
		case LALRAction.Type.Accept:
			return "Accept";
		default:
			throw new Exception("Unexpected LALRAction.Type #"~to!string(cast(int)type));
		}
	}

	public static string fullTypeToString(Type type)
	{
		return typeof(this).stringof ~ "." ~ typeof(type).stringof ~ "." ~ typeToString(type);
	}
}

alias unittestSection!"Goldie_unittest" unittestGoldie;
