import dlib;

import buffer;

enum TokenStyle : u8
{
	None = 0,
	Keyword,
	Import,
	ImportTarget,
	Type,
	Identifier,
	Op,
	Bracket,
	Function,
	Macro,
	Comment,
	String,
	Number,
	Char,
	Exclamation,
	Max,
}

alias TS = TokenStyle;

enum TokenType : u8
{
	None = 0,
	Keyword,
	Type,
	Identifier,
	String,
	Char,
	Function,
	LiteralStr,
	Macro,
	Exclamation,
	At,
	Dollar,
	Percent,
	Caret,
	Ampersand,
	Asterisk,
	LeftParen,
	RightParen,
	VertBar,
	LeftBrace,
	RightBrace,
	LeftSquareBrace,
	RightSquareBrace,
	Tilde,
	BackTick,
	Semicolon,
	Hash,
	Colon,
	Dot,
	Minus,
	Plus,
	Equals,
	Underscore,
	BackSlash,
	Slash,
	LessThan,
	GreaterThan,
	Question,
	Comma,
	Comment,
	Number,
	SingleQuote,
	DoubleQuote,
	Whitespace,
	NewLine,
	Import,
	ImportTarget,
	TypeKeyword,
	EnumKeyword,
	Max,
}

alias TT = TokenType;

const Vec4 GREY      = Vec4(0.45, 0.45, 0.45, 1.0);
const Vec4 PURPLE    = Vec4(0.67, 0.63, 0.81, 1.0);
const Vec4 YELLOW    = Vec4(1.0, 1.0, 0.4, 1.0);
const Vec4 BLUE      = Vec4(0.24, 0.51, 0.72, 1.0);
const Vec4 RED       = Vec4(1.0, 0.0, 0.0, 1.0);
const Vec4 GREEN     = Vec4(0.0, 1.0, 0.0, 1.0);
const Vec4 TURQUOISE = Vec4(0.15, 0.59, 0.76, 1.0);
const Vec4 PINK      = Vec4(0.85, 0.48, 0.60, 1.0);

const Vec4[TS.max] DEFAULT_COLORS = Vec4(1.0);
const Vec4[TS.max] SYNTAX_COLORS  = SyntaxCols();

static Vec4[TS.max]
SyntaxCols()
{
	Vec4[TS.max] colors = [
		TS.Op:           YELLOW,
		TS.Bracket:      GREY,
		TS.Function:     PURPLE,
		TS.Macro:        PURPLE,
		TS.Keyword:      BLUE,
		TS.String:       GREEN,
		TS.Char:         GREEN,
		TS.Import:       RED,
		TS.ImportTarget: RED,
		TS.Type:         TURQUOISE,
		TS.Number:       PINK,
		TS.Comment:      GREY,
	];

	Vec4[TS.max] result = Vec4(0.8, 0.8, 0.8, 1.0);

	import std.traits;
	
	static foreach(i; TS.min .. TS.max)
	{
		if(colors[i].v[3] != 0.0)
		{
			result[i] = colors[i];
		}
	}

	return result;
}

const TokenStyle[TT.max] TOKEN_STYLES = [
	TT.Dollar:      TS.Op,
	TT.Hash:        TS.Op,
	TT.Tilde:       TS.Op,
	TT.Percent:     TS.Op,
	TT.Caret:       TS.Op,
	TT.Ampersand:   TS.Op,
	TT.Asterisk:    TS.Op,
	TT.Minus:       TS.Op,
	TT.Equals:      TS.Op,
	TT.Plus:        TS.Op,
	TT.LessThan:    TS.Op,
	TT.GreaterThan: TS.Op,
	TT.Exclamation: TS.Op,
	TT.Slash:       TS.Op,

	TT.LeftParen:        TS.Bracket,
	TT.RightParen:       TS.Bracket,
	TT.LeftSquareBrace:  TS.Bracket,
	TT.RightSquareBrace: TS.Bracket,
	TT.LeftBrace:        TS.Bracket,
	TT.RightBrace:       TS.Bracket,
	TT.VertBar:          TS.Bracket,
	TT.Semicolon:        TS.Bracket,
	TT.Colon:            TS.Bracket,
	TT.Question:         TS.Bracket,
	TT.Comma:            TS.Bracket,
	
	TT.SingleQuote: TS.Char, 
	TT.DoubleQuote: TS.String,
	TT.BackTick:    TS.String,

	TT.Keyword:      TS.Keyword,
	TT.TypeKeyword:  TS.Keyword,
	TT.EnumKeyword:  TS.Keyword,
	TT.Function:     TS.Function,
	TT.Macro:        TS.Macro,
	TT.Type:         TS.Type,
	TT.Number:       TS.Number,
	TT.Import:       TS.Import,
	TT.ImportTarget: TS.Import,
	TT.Comment:      TS.Comment,
];

// Bad idea vvv

const TT[128] D_STD_TOKEN = [
	'$': TT.Dollar,
	'(': TT.LeftParen,
	')': TT.RightParen,
	'[': TT.LeftSquareBrace,
	']': TT.RightSquareBrace,
	'{': TT.LeftBrace,
	'}': TT.RightBrace,
	'|': TT.VertBar,
	';': TT.Semicolon,
	':': TT.Colon,
	'?': TT.Question,
	',': TT.Comma,
];

const TT[128] D_OP_TOKEN = [
	'~': TT.Tilde,
	'%': TT.Percent,
	'^': TT.Caret,
	'&': TT.Ampersand,
	'*': TT.Asterisk,
	'-': TT.Minus,
	'=': TT.Equals,
	'+': TT.Plus,
	'<': TT.LessThan,
	'>': TT.GreaterThan,
	'!': TT.Exclamation,
];

const TT[128] D_STR_TOKEN = [
	'`' : TT.BackTick,
	'\'': TT.SingleQuote, 
	'"' : TT.DoubleQuote,
];

u8[][][128] D_TYPES = [
	'a': [
		cast(u8[])r"auto",
	],
	'b': [
		cast(u8[])r"bool",
		cast(u8[])r"byte",
	],
	'c': [
		cast(u8[])r"char",
	],
	'd': [
		cast(u8[])r"delegate",
		cast(u8[])r"dchar",
		cast(u8[])r"double",
	],
	'f': [
		cast(u8[])r"float",
		cast(u8[])r"function",
	],
	'i': [
		cast(u8[])r"int",
	],
	'l': [
		cast(u8[])r"long",
	],
	'r': [
		cast(u8[])r"real",
	],
	's': [
		cast(u8[])r"short",
	],
	'u': [
		cast(u8[])r"ubyte",
		cast(u8[])r"uint",
		cast(u8[])r"ulong",
		cast(u8[])r"ushort",
	],
	'v': [
		cast(u8[])r"void",
	],
	'w': [
		cast(u8[])r"wchar",
	],
];

u8[][][128] D_KEYWORDS = [
	'_': [
		cast(u8[])r"__FILE__",
		cast(u8[])r"__FILE_FULL_PATH__",
		cast(u8[])r"__FUNCTION__",
		cast(u8[])r"__LINE__",
		cast(u8[])r"__MODULE__",
		cast(u8[])r"__PRETTY_FUNCTION__",
		cast(u8[])r"__gshared",
		cast(u8[])r"__parameters",
		cast(u8[])r"__rvalue",
		cast(u8[])r"__traits",
		cast(u8[])r"__vector",
	],
	'a': [
		cast(u8[])r"abstract",
		cast(u8[])r"alias",
		cast(u8[])r"align",
		cast(u8[])r"asm",
		cast(u8[])r"assert",
	],
	'b': [
		cast(u8[])r"break",
	],
	'c': [
		cast(u8[])r"case",
		cast(u8[])r"cast",
		cast(u8[])r"catch",
		cast(u8[])r"class",
		cast(u8[])r"const",
		cast(u8[])r"continue",
	],
	'd': [
		cast(u8[])r"debug",
		cast(u8[])r"default",
		cast(u8[])r"deprecated",
		cast(u8[])r"do",
	],
	'e': [
		cast(u8[])r"else",
		cast(u8[])r"enum",
		cast(u8[])r"export",
		cast(u8[])r"extern",
	],
	'f': [
		cast(u8[])r"false",
		cast(u8[])r"final",
		cast(u8[])r"finally",
		cast(u8[])r"for",
		cast(u8[])r"foreach",
		cast(u8[])r"foreach_reverse",
	],
	'g': [
		cast(u8[])r"goto",
	],
	'i': [
		cast(u8[])r"if",
		cast(u8[])r"immutable",
		cast(u8[])r"import",
		cast(u8[])r"in",
		cast(u8[])r"inout",
		cast(u8[])r"interface",
		cast(u8[])r"invariant",
		cast(u8[])r"is",
	],
	'l': [
		cast(u8[])r"lazy",
	],
	'm': [
		cast(u8[])r"mixin",
		cast(u8[])r"module",
	],
	'n': [
		cast(u8[])r"new",
		cast(u8[])r"nothrow",
		cast(u8[])r"null",
	],
	'o': [
		cast(u8[])r"out",
		cast(u8[])r"override",
	],
	'p': [
		cast(u8[])r"package",
		cast(u8[])r"pragma",
		cast(u8[])r"private",
		cast(u8[])r"protected",
		cast(u8[])r"public",
		cast(u8[])r"pure",
	],
	'r': [
		cast(u8[])r"ref",
		cast(u8[])r"return",
	],
	's': [
		cast(u8[])r"scope",
		cast(u8[])r"shared",
		cast(u8[])r"static",
		cast(u8[])r"struct",
		cast(u8[])r"super",
		cast(u8[])r"switch",
		cast(u8[])r"synchronized",
	],
	't': [
		cast(u8[])r"template",
		cast(u8[])r"this",
		cast(u8[])r"throw",
		cast(u8[])r"true",
		cast(u8[])r"try",
		cast(u8[])r"typeid",
		cast(u8[])r"typeof",
	],
	'u': [
		cast(u8[])r"union",
		cast(u8[])r"unittest",
	],
	'v': [
		cast(u8[])r"version",
	],
	'w': [
		cast(u8[])r"while",
		cast(u8[])r"with",
	],
];

const Token g_nil_tk;
Token* g_NIL_TOKEN;

struct Token
{
	Token*    prev;
	Token*    next;
	TokenType type;
	u64       start;
	u64       end;
}

struct Tokenizer
{
	Arena          arena;
	TokenStyle[]   tokens;
	u64            tk_count;
	u64            pos;
	Token*         first;
	Token*         last;
}

bool
Nil(Token* tk)
{
	return tk == g_NIL_TOKEN || tk == null;
}

void
SetBuffers(Tokenizer* tk, TokenStyle[] tokens)
{
	tk.tokens = tokens;
}

Tokenizer
CreateTokenizer(FlatBuffer* fb)
{
	Tokenizer tk = {
		arena: CreateArena(MB(4)),
	};

	SetBuffers(&tk, Alloc!(TS)(fb.data.length));

	g_NIL_TOKEN = cast(Token*)&g_nil_tk;
	tk.first = tk.last = g_NIL_TOKEN;

	for(u64 i = 0; i < D_KEYWORDS.length; i += 1)
	{
		if(D_KEYWORDS[i] == null)
		{
			D_KEYWORDS[i] = [];
		}
	}

	for(u64 i = 0; i < D_TYPES.length; i += 1)
	{
		if(D_TYPES[i] == null)
		{
			D_TYPES[i] = [];
		}
	}

	return tk;
}

void
ResetTokenizer(Tokenizer* tk, u64 len)
{
	Reset(&tk.arena);
	Free(tk.tokens);

	SetBuffers(tk, Alloc!(TS)(len));
	tk.pos     = 0;
	tk.first   = tk.last = g_NIL_TOKEN;
}

Token*
MakeToken(Tokenizer* tk, TokenType type, u64 start, u64 end)
{
	Token* t = Alloc!(Token)(&tk.arena);

	t.type  = type;
	t.start = start;
	t.end   = end;

	DLLPush(tk, t, g_NIL_TOKEN);

	return t;
}

u8
Peek(FlatBuffer* fb)
{
	return fb.tk.pos+1 < fb.length ? fb.data[fb.tk.pos+1] : 0;
}

void
TokenizeD(FlatBuffer* fb)
{
	Tokenizer* tk = &fb.tk;

	Reset(&tk.arena);
	tk.pos = 0;
	tk.first = tk.last = g_NIL_TOKEN;

	for(; tk.pos < fb.length;)
	{
		u8 ch = fb.data[tk.pos];
		bool alpha = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');

		if(ch == ' ' || ch == '\t')
		{
			Token* t = MakeToken(tk, TT.Whitespace, tk.pos, tk.pos+1);

			for(tk.pos += 1; tk.pos < fb.length; tk.pos += 1)
			{
				if(fb.data[tk.pos] != ' ' && fb.data[tk.pos] != '\t')
				{
					break;
				}
			}

			t.end = tk.pos;
		}
		else if(ch == '\n')
		{
			Token* t = MakeToken(tk, TT.NewLine, tk.pos, tk.pos+1);
			tk.pos += 1;
		}
		else if(ch == 'r')
		{
			u8 next = Peek(fb);
			if(next == '"')
			{
				tk.pos += 1;
				ParseStr!('"')(fb);
			}
			else
			{
				ParseId(fb);
			}
		}

		else if(ch >= '0' && ch <= '9')
		{
			ParseNum(fb);
		}

		else if(ch == 'P' || ch == 'E' || ch == 'e' || ch == 'p')
		{
			u8 next = Peek(fb);
			if(next == '-' || next == '+' || (next >= '0' && next <= '9'))
			{
				ParseNum(fb);
			}
			else
			{
				ParseId(fb);
			}
		}
		else if(alpha || ch == '_')
		{
			ParseId(fb);
		}
		else if(ch < 128 && D_STD_TOKEN[ch] != TT.None)
		{
			Token* t = MakeToken(tk, D_STD_TOKEN[ch], tk.pos, tk.pos+1);
			tk.pos += 1;
		}
		else if(ch < 128 && D_OP_TOKEN[ch] != TT.None)
		{
			Token* t = MakeToken(tk, D_OP_TOKEN[ch], tk.pos, tk.pos+1);
			tk.pos += 1;
			FixOpAssign(fb, t);
		}
		else if(ch < 128 && D_STR_TOKEN[ch] != TT.None)
		{
			if(ch == '"')
			{
				ParseStr!('"')(fb);
			}
			else if(ch == '`')
			{
				ParseStr!('`')(fb);
			}
			else
			{
				ParseStr!('\'')(fb);
			}
		}
		else
		{
			switch (ch)
			{
				case '@': 
					{
						Token* t = MakeToken(tk, TT.At, tk.pos, tk.pos+1);
						tk.pos += 1;

						for(; tk.pos < fb.length; tk.pos += 1)
						{
							u8 c = fb.data[tk.pos];

							if(CheckWhiteSpace(c)) break;
							if(!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z')) break;
						}

						t.end = tk.pos;
					} break;
				case '.': 
					{
						Token* t = MakeToken(tk, TT.Dot, tk.pos, tk.pos+1);

						for(; tk.pos < fb.length; tk.pos += 1)
						{
							if(fb.data[tk.pos] != '.') break;
						}

						t.end = tk.pos;
					} break;
				case '#':
					{
						Token* t = MakeToken(tk, TT.Import, tk.pos, tk.pos+1);
	
						for(; tk.pos < fb.length; tk.pos += 1)
						{
							if(CheckWhiteSpace(fb.data[tk.pos])) break;
						}

						t.end = tk.pos;
					} break;
				case '/': 
					{
						Token* t = MakeToken(tk, TT.Slash, tk.pos, tk.pos+1);

						u8 next = Peek(fb);
						if(next == '/')
						{
							tk.pos += 1;
							t.type = TT.Comment;

							for(; tk.pos < fb.length; tk.pos += 1)
							{
								if(CheckEOL(fb.data[tk.pos]))
								{
									break;
								}
							}

							t.end = tk.pos+1;
						}
						else if(next == '*')
						{
							tk.pos += 1;
							t.type = TT.Comment;

							for(; tk.pos < fb.length; tk.pos += 1)
							{
								if(fb.data[tk.pos] == '/' && fb.data[tk.pos-1] == '*')
								{
									break;
								}
							}

							tk.pos += 1;
							t.end = tk.pos;
						}
						else
						{
							tk.pos += 1;
							FixOpAssign(fb, t);
						}
					} break;
				default: 
					{
						Token* t = MakeToken(tk, TT.None, tk.pos, tk.pos+1);

						for(; tk.pos < fb.length; tk.pos += 1)
						{
							if(CheckWhiteSpace(fb.data[tk.pos]))
							{
								break;
							}
						}

						t.end   = tk.pos;
						tk.pos += 1;
					} break;
			}
		}
	}

	for(auto n = tk.first; !Nil(n); n = Next(n))
	{
		Token* prev = Prev(n);
		Token* next = Next(n);

		if(CheckFuncOrTemplateSig(fb, n))
		{
			continue;
		}

		if(!Nil(prev))
		{
			if(prev.type == TT.Import)
			{
				n.type = TT.ImportTarget;
				continue;
			}
			else if(prev.type == TT.TypeKeyword && n.type == TT.Identifier)
			{
				n.type = TT.Type;
				CheckTypeDeclaration(fb, n);
				continue;
			}
			else if(prev.type == TT.EnumKeyword && n.type == TT.Identifier)
			{
				n.type = TT.Type;
				continue;
			}
		}
		
		if(!Nil(next))
		{
			if(n.type == TT.Identifier && next.type == TT.LeftParen)
			{
				n.type = TT.Function;
				continue;
			}
			else if(n.type == TT.Identifier && next.type == TT.Exclamation)
			{
				n.type = TT.Macro;
				continue;
			}
		}

		CheckVarDeclaration(n);
	}

	for(auto token = tk.first; !Nil(token); token = token.next)
	{
		tk.tokens[token.start .. token.end] = TOKEN_STYLES[token.type];
	}
}

bool
CheckVarDeclaration(Token* token)
{
	bool found, ignore;
	if(token.type == TT.Identifier && Next(token).type != TT.Equals)
	{
		for(Token* ahead = Next(token); !Nil(ahead); ahead = Next(ahead))
		{
			if(ahead.type == TT.Semicolon || ahead.next.type == TT.NewLine)
			{
				break;
			}

			if(ahead.type == TT.Asterisk)
			{
				continue;
			}
			else if(ahead.type == TT.LeftSquareBrace)
			{
				ignore = true;
			}
			else if(ahead.type == TT.RightSquareBrace)
			{
				ignore = false;
			}
			else if(!ignore && ahead.type == TT.Identifier)
			{
				Token* eq = Next(ahead);
				if(eq.type == TT.Equals || eq.type == TT.Semicolon)
				{
					token.type = TT.Type;
					found = true;
					break;
				}
			}
			else if(!ignore)
			{
				break;
			}
		}
	}

	return found;
}

void
CheckTypeDeclaration(FlatBuffer* fb, Token* token)
{
	for(Token* tk = Next(token); !Nil(tk) && tk.type != TT.RightBrace; tk = Next(tk))
	{
		Token* prev = Prev(tk);
		if(prev.type == TT.LeftBrace || prev.type == TT.Semicolon)
		{
			tk.type = TT.Type;
		}
	}
}

bool
CheckFuncOrTemplateSig(FlatBuffer* fb, Token* token)
{
	bool found = false;

	if(token.type == TT.Identifier || token.type == TT.Type)
	{
		Token* n = Next(token);
		if(n.type == TT.Asterisk)
		{
			n = Next(n);
		}

		if(n.type == TT.Identifier && Next(n).type == TT.LeftParen)
		{
			token.type = TT.Type;
			n.type = TT.Function;
			found = true;
		}
	}

	if(found)
	{
		bool ignore     = false;
		bool macro_decl = false;
		for(Token* next = Next(token); !Nil(next); next = Next(next))
		{
			if(next.type == TT.RightParen && Next(next).type != TT.LeftParen && !macro_decl)
			{
				macro_decl = true;
			}
			else if(next.type == TT.RightParen && next.next.type == TT.NewLine)
			{
				break;
			}
			else if((next.type == TT.LeftParen || next.type == TT.Comma) && Next(next).type == TT.Identifier && Next(next).type != TT.RightParen)
			{
				Next(next).type = TT.Type;
			}
		}
	}

	return found;
}

u8[]
TokenStr(FlatBuffer* fb, Token* t)
{
	return fb.data[t.start .. t.end];
}

Token*
Prev(types...)(Token* t)
{
	Token* res = g_NIL_TOKEN;

PrevMain:
	for(auto p = t.prev; !Nil(p); p = p.prev)
	{
		if(p.type == TT.NewLine || p.type == TT.Whitespace || p.type == TT.Comment)
		{
			continue;
		}

		static if(types.length == 0)
		{
			res = p;
			break PrevMain;
		}

		static foreach(type; types)
		{
			if(p.type == type)
			{
				res = p;
				break PrevMain;
			}
		}
	}

	return res;
}

alias PrevFnToken = Prev!(TT.Identifier, TT.LeftParen, TT.RightParen, TT.Comma, TT.Keyword, TT.Dot, TT.Exclamation, TT.Type);

Token*
Next(types...)(Token* t)
{
	Token* res = g_NIL_TOKEN;

NextMain:
	for(auto n = t.next; !Nil(n); n = n.next)
	{
		if(n.type == TT.NewLine || n.type == TT.Whitespace || n.type == TT.Comment)
		{
			continue;
		}

		static if(types.length == 0)
		{
			res = n;
			break NextMain;
		}

		static foreach(type; types)
		{
			if(n.type == type)
			{
				res = n;
				break NextMain;
			}
		}
	}

	return res;
}

alias NextFnToken = Next!(TT.Identifier, TT.LeftParen, TT.RightParen, TT.Comma, TT.Keyword, TT.Dot, TT.Exclamation, TT.Type);

void
ParseNum(FlatBuffer* fb)
{
	Tokenizer* tk = &fb.tk;

	Token* t = Alloc!(Token)(&tk.arena);
	t.type   = TT.Number;
	t.start  = tk.pos;

	bool first = true;
	while (tk.pos < fb.length)
	{
		tk.pos += 1;

		u8 ch = fb.data[tk.pos];

		if(ch != '_' && ch != '.' && !(ch >= '0' && ch <= '9') && ch != 'x' && ch != 'X' && !((ch == '-' || ch == '+') && first))
		{
			break;
		}

		first = false;
	}

	while (tk.pos < fb.length)
	{
		u8 ch = fb.data[tk.pos];
		if(ch != 'l' && ch != 'u' && ch != 'U' && ch != 'L' && ch != 'f' && ch != 'F')
		{
			break;
		}

		tk.pos += 1;
	}

	t.end = tk.pos;

	DLLPush(tk, t, g_NIL_TOKEN);
}

void 
ParseStr(u8 str_ch)(FlatBuffer* fb)
{
	static assert(str_ch == '`' || str_ch == '\'' || str_ch == '"');

	Tokenizer* tk = &fb.tk;

	Token* t = Alloc!(Token)(&tk.arena);

	t.type  = D_STR_TOKEN[str_ch];
	t.start = tk.pos;

	static if(str_ch == '`') tk.pos += 1;

	bool ignore = str_ch != '`';
	for(; (fb.data[tk.pos] != str_ch || ignore) && tk.pos < fb.length; tk.pos += 1)
	{
		static if(str_ch != '`') ignore = fb.data[tk.pos] == '\\';
	}

	tk.pos += 1;
	t.end   = tk.pos;

	DLLPush(tk, t, g_NIL_TOKEN);
}

void
ParseId(FlatBuffer* fb)
{
	Tokenizer* tk = &fb.tk;
	Token* t      = MakeToken(tk, TT.Identifier, tk.pos, tk.pos+1);

	for(; tk.pos < fb.length; tk.pos += 1)
	{
		u8 ch = fb.data[tk.pos];

		if(!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && ch != '.' && ch != '_' && !(ch >= '0' && ch <= '9'))
		{
			t.end = tk.pos;
			break;
		}
	}

	u8 ch = fb.data[t.start];
	if(ch < D_KEYWORDS.length && D_KEYWORDS[ch] != null && D_KEYWORDS[ch].length > 0)
	{
		bool found = false;
		u8[] id = fb.data[t.start .. t.end];
		
		foreach(ref k; D_TYPES[ch])
		{
			if(id == k)
			{
				t.type = TT.Type;
				found = true;
				break;
			}
		}

		foreach(ref k; D_KEYWORDS[ch])
		{
			if(found) break;

			if(id == k)
			{
				if(k == r"import")
				{
					t.type = TT.Import;
				}
				else if(k == r"class" || k == r"struct")
				{
					t.type = TT.TypeKeyword;
				}
				else if(k == r"enum")
				{
					t.type = TT.EnumKeyword;
				}
				else
				{
					t.type = TT.Keyword; 
				}

				break;
			}
		}
	}

	assert(t.start < t.end, "ParseId failure: start is lower or equal to end");
}

void
FixOpAssign(FlatBuffer* fb, Token* t)
{
	if(t.end+1 < fb.length)
	{
		u8[] str = fb.data[t.start .. t.end+1];
		if(
			str == "/=" ||
			str == "+=" ||
			str == "*=" ||
			str == "%=" ||
			str == "&=" ||
			str == "==" ||
			str == "!="
		)
		{
			fb.tk.pos += 1;
			t.end += 1;
		}
	}
}

pragma(inline) bool
CheckEOL(u8 ch)
{
	return ch == 0x0D||
				 ch == 0x0A||
				 ch == 0x0B||
				 ch == 0x0C;
}

pragma(inline) bool
CheckWhiteSpace(u8 ch)
{
	return ch == ' ' ||
				 ch == '\t'||
				 ch == '\n'||
				 ch == 0x0D||
				 ch == 0x0A||
				 ch == 0x0B||
				 ch == 0x0C;
}

void
SkipWhiteSpace(FlatBuffer* fb)
{
	while (fb.tk.pos < fb.length)
	{
		u8 ch = fb.data[fb.tk.pos];

		bool white_space = CheckWhiteSpace(ch);

		if(!white_space)
		{
			break;
		}

		fb.tk.pos += 1;
	}
}

