#region COPYRIGHT
//
//     THIS IS GENERATED BY TEMPLATE
//     
//     AUTHOR  :     ROYE
//     DATE       :     2010
//
//     COPYRIGHT (C) 2010, TIANXIAHOTEL TECHNOLOGIES CO., LTD. ALL RIGHTS RESERVED.
//
#endregion

using System;
using System.Collections.Generic;
using System.Globalization;

namespace System.Text.Template
{
    public class CSharpParser : ExpressionParser
    {
        private static readonly NumberFormatInfo _NumberFormat;
        public MissingFieldHandler MissingMember;
        public MissingFieldHandler InterceptMember;

        static CSharpParser()
        {
            _NumberFormat = new NumberFormatInfo();
            _NumberFormat.NumberDecimalSeparator = ".";
            _NumberFormat.NumberGroupSeparator = ",";
            _NumberFormat.NumberGroupSizes = new int[] { 3 };
        }

        public CSharpParser()
        {
            AddTerm(@"""([^""\\]|\\['""\\0abfnrtv]|\\x[a-fA-F0-9][a-fA-F0-9]{0,3})*""", EvalStringLiteral);
            AddTerm(@"'([^'\\]|\\['""\\0abfnrtv]|\\x[a-fA-F0-9][a-fA-F0-9]{0,3})'", EvalCharLiteral);

            AddUnaryOperator(@"\([a-zA-Z_@][a-zA-Z0-9_]*\)(?=\s*[a-zA-Z0-9_])", 19, EvalTypeCast);

            AddLeftParen(@"\(");
            AddRightParen(@"\)");
            AddLeftParen(@"\[");
            AddRightParen(@"\]");
            AddArgumentSeparator(",");

            AddBinaryOperator(@"&&", 10, EvalShortcutOperator);
            AddBinaryOperator(@"\|\|", 9, EvalShortcutOperator);
            AddBinaryOperator(@"\?\?", 8, EvalCoalesce);

            AddTernaryOperator(@"\?", @"\:", 7, OperatorAssociativity.Right, EvalTernary);

            AddBinaryOperator(@"==|!=", 14, EvalOperator);
            AddBinaryOperator(@"\.", 20, EvalDotOperator);
            AddUnaryOperator(@"!", 19, EvalUnary);
            AddUnaryOperator(@"\-", 19, EvalUnary);
            AddUnaryOperator(@"\~", 19, EvalUnary);
            AddBinaryOperator(@"\*|/|%", 18, EvalOperator);
            AddBinaryOperator(@"\+", 17, EvalOperator);
            AddBinaryOperator(@"\-", 17, EvalOperator);
            AddBinaryOperator(@"\<\<|\>\>", 16, EvalOperator);
            AddBinaryOperator(@"\<=|\>=", 15, EvalOperator);
            AddBinaryOperator(@"\<|\>", 15, EvalOperator);
            AddBinaryOperator(@"\bas|is\b", 15, EvalIsAsOperator);
            AddBinaryOperator(@"&", 13, EvalOperator);
            AddBinaryOperator(@"\^", 12, EvalOperator);
            AddBinaryOperator(@"\|", 11, EvalOperator);
            AddBinaryOperator(@"=", 6, OperatorAssociativity.Right, EvalAssignment);

            AddTerm("typeof", EvalTypeOf);
            AddTerm(@"new\s+[a-zA-Z_][a-zA-Z0-9_]*", EvalConstructor);

            AddTerm(@"[a-zA-Z_@][a-zA-Z0-9_@]*", EvalVarName);
            AddTerm(@"\d+(?:\.\d+)?([uU][lL]|[lL][uU]|[FfDdMmUuLl])?", EvalNumber);

            FunctionEvaluator = EvalFunction;
            FunctionCallPrecedence = 20;
        }

        protected virtual Expression EvalTypeCast(string token, Expression[] terms)
        {
            return new TypeCastExpression(new VariableExpression(token.Substring(1, token.Length - 2).Trim()), terms[0]);
        }

        protected virtual Expression EvalIsAsOperator(string token, Expression[] terms)
        {
            if (token == "as")
                return new AsExpression(terms[0], terms[1]);

            if (token == "is")
                return new IsExpression(terms[0], terms[1]);

            return null;
        }

        protected virtual Expression EvalTernary(string token, Expression[] terms)
        {
            return new ConditionalExpression(terms[0], terms[1], terms[2]);
        }

        private static char UnEscape(string s)
        {
            if (s.Length == 1)
                return s[0];

            if (s.Length == 2)
            {
                switch (s[1])
                {
                    case '\\':
                    case '\"':
                    case '\'':
                        return s[1];
                    case '0':
                        return (char)0;
                    case 'a':
                        return '\a';
                    case 'b':
                        return '\b';
                    case 'f':
                        return '\f';
                    case 'n':
                        return '\n';
                    case 'r':
                        return '\r';
                    case 't':
                        return '\t';
                    case 'v':
                        return '\v';
                    default:
                        throw new ArgumentException();
                }
            }
            else
            {
                return (char)Convert.ToUInt16(s.Substring(2), 16);
            }
        }

        protected virtual Expression EvalTypeOf(string token, Expression[] terms)
        {
            return new TypeOfExpression();
        }

        protected virtual Expression EvalCharLiteral(string token, Expression[] terms)
        {
            return Expression.Value(UnEscape(token.Substring(1, token.Length - 2)));
        }

        protected virtual Expression EvalNumber(string token, Expression[] terms)
        {
            string s = token;

            Type type = null;

            if (!char.IsDigit(s[s.Length - 1]))
            {
                string suffix = "" + char.ToUpper(s[s.Length - 1]);
                s = s.Remove(s.Length - 1);
                if (!char.IsDigit(s[s.Length - 1]))
                {
                    suffix = char.ToUpper(s[s.Length - 1]) + suffix;
                    s = s.Remove(s.Length - 1);
                }

                switch (suffix)
                {
                    case "M":
                        type = typeof(decimal);
                        break;
                    case "D":
                        type = typeof(double);
                        break;
                    case "F":
                        type = typeof(float);
                        break;
                    case "L":
                        type = typeof(long);
                        break;
                    case "U":
                        type = typeof(uint);
                        break;
                    case "LU":
                    case "UL":
                        type = typeof(ulong);
                        break;
                }
            }

            if (type != null)
                return new ValueExpression(Convert.ChangeType(s, type, _NumberFormat), type);

            if (s.LastIndexOf('.') >= 0)
            {
                return Expression.Value(Convert.ToDouble(s, _NumberFormat));
            }
            else
            {
                long n = Convert.ToInt64(s);
                if (n > Int32.MaxValue || n < Int32.MinValue)
                    return Expression.Value(n);
                else
                    return Expression.Value((int)n);
            }
        }

        protected virtual Expression EvalVarName(string token, Expression[] terms)
        {
            return new VariableExpression(token);
        }

        protected virtual Expression EvalFunction(string token, Expression[] terms)
        {
            Expression[] parameters = new Expression[terms.Length - 1];

            Array.Copy(terms, 1, parameters, 0, parameters.Length);

            if (token == "[")
            {
                return new IndexExpression(terms[0], parameters);
            }
            else
            {
                return new CallExpression(terms[0], parameters);
            }
        }

        protected virtual Expression EvalCoalesce(string token, Expression[] terms)
        {
            return new CoalesceExpression(terms[0], terms[1]);
        }

        protected virtual Expression EvalShortcutOperator(string token, Expression[] terms)
        {
            if (token == "&&")
                return new AndAlsoExpression(terms[0], terms[1]);

            if (token == "||")
                return new OrElseExpression(terms[0], terms[1]);

            return null;
        }

        protected virtual Expression EvalUnary(string token, Expression[] terms)
        {
            switch (token)
            {
                case "!":
                    return new NegationExpression(terms[0]);
                case "-":
                    return new UnaryMinusExpression(terms[0]);
                case "~":
                    return new BitwiseComplementExpression(terms[0]);
                default:
                    return null;
            }
        }

        protected virtual Expression EvalOperator(string token, Expression[] terms)
        {
            return Expression.Op(token, terms[0], terms[1]);
        }

        protected virtual Expression EvalAssignment(string token, Expression[] terms)
        {
            return new AssignmentExpression(terms[0], terms[1]);
        }

        protected virtual Expression EvalStringLiteral(string token, Expression[] terms)
        {
            string s = token.Substring(1, token.Length - 2);

            if (s.IndexOf('\\') < 0)
                return Expression.Value(s);

            string output = "";

            bool inEscape = false;
            string hexString = null;

            for (int i = 0; i < s.Length; i++)
            {
                char c = s[i];

                if (inEscape)
                {
                    if (c == 'x')
                    {
                        hexString = "";
                        continue;
                    }

                    if (hexString == null && (c != 'x' || c != 'X'))
                    {
                        output += UnEscape("\\" + c);
                        inEscape = false;
                        continue;
                    }

                    if (hexString == null)
                    {
                        inEscape = false;
                    }
                    else
                    {
                        if (((char.ToLower(c) < 'a' || char.ToLower(c) > 'f') && (c < '0' || c > '9')) || hexString.Length == 4)
                        {
                            output += UnEscape("\\x" + hexString);
                            inEscape = false;
                            hexString = null;
                        }
                        else
                        {
                            hexString += c;
                            continue;
                        }
                    }
                }

                if (c != '\\')
                {
                    output += c;
                    continue;
                }

                inEscape = true;
            }

            return Expression.Value(output);
        }

        protected virtual Expression EvalDotOperator(string token, Expression[] terms)
        {
            VariableExpression varExpression = terms[1] as VariableExpression;

            if (varExpression == null)
                throw new ArgumentException();

            return new FieldExpression(terms[0], varExpression.Variable, MissingMember, InterceptMember);
        }

        protected virtual Expression EvalConstructor(string token, Expression[] terms)
        {
            string className = token.Substring(3).Trim();
            return new ConstructorExpression(new VariableExpression(className), terms);
        }
    }
}
