/*
 * Bernoulli Compiler
 * Copyright (c) Cornell University
 * Department of Computer Science
 * 
 * Kamen Yotov (kamen@yotov.org)
 * 
 * $Source: C:/CVS/kyotov/kyotov/Research/BC/C/CLexer.cs,v $
 * $Revision: 1.6 $
 * $Date: 2003/01/06 07:03:29 $
 */

using System;

using BC.Core.Lexers;

namespace BC.C
{
	[Serializable]
	sealed public class CLexer: Lexer
	{
		private string name (string t)
		{
			while (t.Length > 0 && t[0] == '_')
				t = t.Substring(1);

			return t.ToLower();
		}

		protected override void Configure ()
		{
			this[C.T_comment] = @"\/\*([^\*]|\*(?!\/))*\*\/|\/\/[^\n\r]*(\n|\r)";

			string digit = "[0-9]";
			string nonzero_digit = "[1-9]";
			string octal_digit = "[0-7]";
			string hexadecimal_digit = "[0-9a-fA-F]";
			string nondigit = "[_a-zA-Z]";

			string universal_character_name = string.Format(@"\\(u{0}{{4}}|U{0}{{8}})", hexadecimal_digit);

			string identifier_nondigit = string.Format("({0}|{1})", nondigit, universal_character_name);

			this[C.T_identifier] = string.Format("(?<!{0}|{1}){0}({0}|{1})*", identifier_nondigit, digit);

			foreach (string s in C.keywords)
				this[C.T_keyword(name(s))] = string.Format("{0}(?!{1}|{2})", s, identifier_nondigit, digit);

			string integer_suffix = "|";

			string[] unsigned_suffix = {string.Empty, "u", "U"};
			string[] long_suffix = {string.Empty, "l", "L", "ll", "LL"};

			string suffix_check = "(?![lLuU])";

			foreach (string us in unsigned_suffix)
				foreach (string ls in long_suffix)
					if (us + ls != string.Empty)
						if (integer_suffix.IndexOf("|" + us + ls + suffix_check + "|") < 0)
							integer_suffix += us + ls + suffix_check + "|";
						else if (integer_suffix.IndexOf("|" + ls + us + suffix_check + "|") < 0)
							integer_suffix += ls + us + suffix_check + "|";

			integer_suffix = string.Format("({0})?", integer_suffix.Substring(1, integer_suffix.Length - 2));

			string hexadecimal_prefix = "0[xX]";

			this[C.T_integer_decimal] = string.Format("{0}{1}*{2}", nonzero_digit, digit, integer_suffix);
			this[C.T_integer_octal] = string.Format("0(?![xX]){0}*{1}", octal_digit, integer_suffix);
			this[C.T_integer_hexadecimal] = string.Format("{0}{1}*{2}", hexadecimal_prefix, hexadecimal_digit, integer_suffix);

			string digit_sequence = string.Format("({0}+)", digit);
			string fractional_constant = string.Format(@"({0}?\.{0}|{0}\.)", digit_sequence);
			string exponent_part = string.Format("([eE][-+]?{0})", digit_sequence);

			this[C.T_floating_decimal] = string.Format("({1}{2}?|{0}{2})[flFL]?", digit_sequence, fractional_constant, exponent_part);

			string hexadecimal_digit_sequence = string.Format("({0}+)", hexadecimal_digit);
			string hexadecimal_fractional_constant = string.Format(@"({0}?\.{0}|{0}\.)", hexadecimal_digit_sequence);
			string binary_exponent_part = string.Format("([pP][-+]?{0})", digit_sequence);

			this[C.T_floating_hexadecimal] = string.Format("{0}({2}{3}|{1}{3})[flFL]?", hexadecimal_prefix, digit_sequence, fractional_constant, exponent_part);

			string escape_characters = @"'?abfnrtv";

			string escape_sequence = string.Format(@"|\\{0}{{1,3}}|\\x{1}+|\\\\|{2}", octal_digit, hexadecimal_digit, universal_character_name);

			foreach (char c in escape_characters)
				escape_sequence += string.Format(@"|\\{0}", c);

			string c_char = string.Format(@"([^\'\\\n]{0})", escape_sequence);

			this[C.T_character] = string.Format("L?'{0}+'", c_char);

			string s_char = string.Format(@"([^\{0}\\\n]{1})", "\"", escape_sequence);;

			this[C.T_string] = string.Format("L?\"{0}*\"", s_char);

			this[C.T_leftbracket] = @"\[|\<\:";
			this[C.T_rightbracket] = @"\]|\:\>";
			this[C.T_leftparenthesis] = @"\(";
			this[C.T_rightparenthesis] = @"\)";
			this[C.T_leftbrace] = @"\{|\<\%";
			this[C.T_rightbrace] = @"\}|\%\>";

			this[C.T_assign_add] = @"\+\=";
			this[C.T_assign_subtract] = @"\-\=";
			this[C.T_assign_multiply] = @"\*\=";
			this[C.T_assign_divide] = @"\/\=";
			this[C.T_assign_modulo] = @"\%\=";
			this[C.T_assign_shiftleft] = @"\<\<\=";
			this[C.T_assign_shiftright] = @"\>\>\=";
			this[C.T_assign_and] = @"\&\=";
			this[C.T_assign_or] = @"\|\=";
			this[C.T_assign_xor] = @"\^\=";
			this[C.T_assign_plain] = @"\=(?![\=])";

			this[C.T_dot] = @"\.(?!\.)";
			this[C.T_reference] = @"\-\>";
			this[C.T_increment] = @"\+\+";
			this[C.T_decrement] = @"\-\-";

			this[C.T_ampersand] = @"\&(?![&=])";
			this[C.T_asterisk] = @"\*(?![=])";

			this[C.T_add] = @"\+(?![\+\=])";
			this[C.T_subtract] = @"\-(?![\-\=\>])";
			//	this[C.T_multiply]
			this[C.T_divide] = @"\/(?![\/\=\*])";
			this[C.T_modulo] = @"\%(?![\%\=\>])";

			this[C.T_tilda] = @"\~";

			this[C.T_less] = @"\<(?![\=\<\%\:])";
			this[C.T_greater] = @"\>(?![\=\>])";
			this[C.T_lessorequal] = @"\<\=";
			this[C.T_greaterorequal] = @"\>\=";
			this[C.T_equal] = @"\=\=";
			this[C.T_notequal] = @"\!\=";

			this[C.T_logical_not] = @"\!(?![\=])";
			this[C.T_logical_and] = @"\&\&";
			this[C.T_logical_or] = @"\|\|";

			//	this[C.T_bitwise_not]
			//	this[C.T_bitwise_and]
			this[C.T_bitwise_or] = @"\|(?![\|\=])";
			this[C.T_xor] = @"\^(?![\=])";
			this[C.T_shiftleft] = @"\<\<";
			this[C.T_shiftright] = @"\>\>";

			this[C.T_question] = @"\?";
			this[C.T_colon] = @"\:(?![\>])";
			this[C.T_semicolon] = @"\;";
			this[C.T_ellipsis] = @"\.\.\.";
			this[C.T_coma] = @"\,";

			// # (%:), ## (%:%:)

			this[WhiteSpace] = @"[ \t\v\n\r\f]";
		}
	}
}
