private static void Shift(Stack<int> shiftReduceStack, JsonLexicalAnalyzer lex, JsonLexicalType currentLexeme, ShiftReduce shiftReduce, Stack<IJsonValue> inputStack)
        {
            shiftReduceStack.Push((int)currentLexeme);
            shiftReduceStack.Push(shiftReduce.Action);

            //if (Log.IsDebugEnabled)
            //    Log.Debug("Shift {0} s{1}", currentLexeme, shiftReduce.Action);

            switch (currentLexeme)
            {
                case JsonLexicalType.Number:
                    if (lex.CurrentDouble.HasValue)
                        inputStack.Push(new JsonNumber(lex.CurrentDouble.Value));
                    else if (lex.CurrentInt64.HasValue)
                        inputStack.Push(new JsonNumber(lex.CurrentInt64.Value));
                    break;

                case JsonLexicalType.String:
                    inputStack.Push(new JsonString(lex.CurrentString));
                    break;

                case JsonLexicalType.True:
                    inputStack.Push(new JsonBoolean(true));
                    break;

                case JsonLexicalType.False:
                    inputStack.Push(new JsonBoolean(false));
                    break;

                case JsonLexicalType.Null:
                    inputStack.Push(Singleton<JsonNull>.Instance);
                    break;

                default:
                    break;
            }
        }
        private static IJsonValue ShiftReduceParse(string json, JsonLexicalAnalyzer lex)
        {
            var inputStack = new Stack<IJsonValue>();
            var parseStack = new Stack<ParseItem>();
            var shiftReduceStack = new Stack<int>();
            shiftReduceStack.Push(0);

            var currentLexeme = lex.Scan();
            var done = false;

            while (!done)
            {
                if (currentLexeme == JsonLexicalType.Error)
                    LogAndThrowJsonError(lex, json, false);

                var topOfStack = shiftReduceStack.Peek();
                var shiftReduceAction = ActionGotoTable[topOfStack, (int)currentLexeme];

                switch (shiftReduceAction.Type)
                {
                    case ShiftReduceType.Shift:
                        Shift(shiftReduceStack, lex, currentLexeme, shiftReduceAction, inputStack);
                        currentLexeme = lex.Scan();
                        break;

                    case ShiftReduceType.Reduce:
                        Reduce(shiftReduceStack, shiftReduceAction, inputStack, parseStack);
                        break;

                    case ShiftReduceType.Accept:
                        if (Log.IsDebugEnabled)
                            Log.Debug("JSON Parser Accept State");
                        done = true;
                        break;

                    default:
                        LogAndThrowJsonError(lex, json, true);
                        break;
                }
            }

            // ParseRoot should always be left on the parseStack at this point (otherwise an error would have already occured)
            return parseStack.Pop().ToJsonValue();
        }
        private static void LogAndThrowJsonError(JsonLexicalAnalyzer lex, string json, bool isParseError)
        {
            var parseAnalysisError = isParseError ? "parsing" : "analyzing";
            var errorMessage = string.Format("Error {3} JSON at line number {1} character {2}: {0}", json, lex.LineNumber, lex.Index, parseAnalysisError);

            if (Log.IsErrorEnabled)
                Log.Error(errorMessage);

            throw new JsonException(errorMessage);
        }
        /// <summary>
        /// Parses a JSON string to create an IJsonValue representation of the JSON
        /// </summary>
        /// <param name="json">JSON string to parse</param>
        /// <returns>IJsonValue representation of the JSON</returns>
        public static IJsonValue Parse(string json)
        {
            if (string.IsNullOrEmpty(json))
            {
                if (Log.IsErrorEnabled)
                    Log.Error("Attempt to parse null or empty JSON string");

                throw new JsonException("Attempt to parse null or empty JSON string");
            }

            using (var lex = new JsonLexicalAnalyzer(json))
            {
                return ShiftReduceParse(json, lex);
            }
        }