/// <summary>
		///		Interpreta un nodo
		/// </summary>
		private MLNode ParseNode(string strName, ParserTokenizer objTokenizer, bool blnSearchBracket)
		{ MLNode objMLNode = new MLNode(strName);

				// Captura el siguiente nodo
					if (!objTokenizer.IsEof())
						{ if (blnSearchBracket)
								{	// Obtiene el siguiente token
										GetNextToken(objTokenizer);
									// Debería ser una llave de apertura
										if (intIDActualType == TokenType.BraceOpen)
											objMLNode.Nodes.Add(ParseNodesArray(objTokenizer));
										else if (intIDActualType != TokenType.BracketOpen)
											throw new ParserException("Se esperaba una llave de apertura");
										else
											ParseNodeAttributes(objTokenizer, objMLNode);
								}
							else
								ParseNodeAttributes(objTokenizer, objMLNode);
						}
				// Devuelve el nodo interpretado
					return objMLNode;
		}
		/// <summary>
		///		Interpreta los atributos de un nodo "id":"value","id":"value", ... ó "id":{object} ó "id":[array]
		/// </summary>
		private void ParseNodeAttributes(ParserTokenizer objTokenizer, MLNode objMLNodeParent)
		{ bool blnEnd = false;

				// Obtiene los nodos
					while (!objTokenizer.IsEof() && !blnEnd)		
						{ // Lee el siguiente Token, debería ser un identificador
								GetNextToken(objTokenizer);
							// Comprueba que sea correcto
								if (intIDActualType == TokenType.BracketClose) // ... es un objeto vacío
									blnEnd = true;
								else if (intIDActualType != TokenType.String) // ... no se ha encontrado el identificador
									throw new ParserException("Se esperaba el identificador del elemento");
								else
									{ MLAttribute objMLAttribute = new MLAttribute();

											// Asigna el código del atributo
												objMLAttribute.Name = objActualToken.Lexema;
											// Lee el siguiente token. Deberían ser dos puntos
												GetNextToken(objTokenizer);
											// Comprueba que sea correcto
												if (intIDActualType != TokenType.Colon)
													throw new ParserException("Se esperaban dos puntos (separador de identificador / valor)");
												else
													{ // Lee el siguiente token...
															GetNextToken(objTokenizer);
														// Interpreta el valor
															switch (intIDActualType)
																{ case TokenType.String:
																	case TokenType.True:
																	case TokenType.False:
																	case TokenType.Numeric:
																	case TokenType.Null:
																			// Asigna el valor al atributo
																				switch (intIDActualType)
																					{ case TokenType.Null:
																								objMLAttribute.Value = "";
																							break;
																						case TokenType.String:
																								objMLAttribute.Value = ParseUnicode(objActualToken.Lexema);
																							break;
																						default:
																								objMLAttribute.Value = objActualToken.Lexema;
																							break;
																					}
																			// Añade el atributo al nodo
																				objMLNodeParent.Attributes.Add(objMLAttribute);
																		break;
																	case TokenType.BracketOpen: // ... definición de objeto
																			MLNode objMLNode = ParseNode(objMLAttribute.Name, objTokenizer, false);

																				// Añade el nodo como objeto
																					objMLNodeParent.Nodes.Add(objMLNode);
																		break;
																	case TokenType.BraceOpen: // ... definición de array
																			objMLNodeParent.Nodes.Add(ParseNodesArray(objMLAttribute.Name, objTokenizer));
																		break;
																	default:
																		throw new ParserException("Cadena desconocida. " + objActualToken.Lexema);
																}
													}
											// Lee el siguiente token
												GetNextToken(objTokenizer);
											// Si es una coma, seguir con el siguiente atributo del nodo, si es una llave de cierre, terminar
												switch (intIDActualType)
													{ case TokenType.Comma:
																// ... no hace nada, simplemente pasa a la creación del siguiente nodo
															break;
														case TokenType.BracketClose:
																blnEnd = true;
															break;
														default:
															throw new ParserException("Cadena desconocida. " + objActualToken.Lexema);
													}
									}
						}
		}
		/// <summary>
		///		Inicializa el objeto de creación de tokens
		/// </summary>
		private ParserTokenizer InitTokenizer()
		{ ParserTokenizer objTokenizer = new ParserTokenizer();

				// Asigna los tokens
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.BracketOpen, "{"));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.BracketClose, "}"));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.String, "\"", "\""));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.Colon, ":"));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.Comma, ","));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.BraceOpen, "["));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.BraceClose, "]"));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.True, "true"));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.False, "false"));
					objTokenizer.TokensDefinitions.Add(GetTokenDefinition(TokenType.Null, "null"));
					objTokenizer.TokensDefinitions.Add((int) TokenType.Numeric, "Numeric");
				// Devuelve el objeto de creación de tokens
					return objTokenizer;
		}
		/// <summary>
		///		Obtiene los datos del siguiente token
		/// </summary>
		private void GetNextToken(ParserTokenizer objTokenizer)
		{ objActualToken = objTokenizer.GetToken();
			intIDActualType = GetIDType(objActualToken);
		}
		/// <summary>
		///		Interpreta los nodos de un array
		/// </summary>
		private MLNode ParseNodesArray(string strNodeParent, ParserTokenizer objTokenizer)
		{ MLNode objMLNode = new MLNode(strNodeParent);
			bool blnEnd = false;
			int intIndex = 0;

				// Obtiene el siguiente token (puede que se trate de un array vacío)
					while (!objTokenizer.IsEof() && !blnEnd)
						{ // Obtiene el siguiente token
								GetNextToken(objTokenizer);
							// Interpreta el nodo
								switch (intIDActualType)
									{ case TokenType.BracketOpen:
												objMLNode.Nodes.Add(ParseNode("Struct", objTokenizer, false));
											break;
										case TokenType.BraceOpen:
												objMLNode.Nodes.Add(ParseNodesArray(objTokenizer));
											break;
										case TokenType.String:
										case TokenType.Numeric:
										case TokenType.True:
										case TokenType.False:
										case TokenType.Null:
												objMLNode.Nodes.Add("Item", objActualToken.Lexema);
											break;
										case TokenType.Comma: // ... no hace nada, simplemente pasa al siguiente incrementando el índice
												intIndex++;
											break;
										case TokenType.BraceClose: // ... corchete de cierre, indica que ha terminado
												blnEnd = true;
											break;
										default:
											throw new NotImplementedException("No se ha encontrado un token válido ('" + objActualToken.Lexema + "')");
									}
						}
				// Si no se ha encontrado un corchete, lanza una excepción
					if (!blnEnd)
						throw new ParserException("No se ha encontrado el carácter de fin del array ']'");
				// Devuelve la colección de nodos
					return objMLNode;
		}
		/// <summary>
		///		Interpreta los nodos de un array
		/// </summary>
		private MLNode ParseNodesArray(ParserTokenizer objTokenizer)
		{ return ParseNodesArray("Array", objTokenizer);
		}