ScriptDocComment *ScriptDocComment::Create( const VString &text )
{
	ScriptDocLexer *lexer = new ScriptDocLexer( const_cast< VString * >( &text ) );

	// If the first token we lex isn't a start token, then we've got a problem
	if (ScriptDocTokenValues::START != lexer->GetNextTokenForParser())
	{	
		delete lexer;
		return NULL;
	}

	ScriptDocComment *ret = new ScriptDocComment( text );
	while (true) {
		int tk = lexer->GetNextTokenForParser();

		// If we reach the end of input before finding an END token, this isn't a valid
		// ScriptDoc comment
		if (-1 == tk) {
			delete lexer;
			delete ret;
			return NULL;
		}

		// If we find the ending token, then we're done parsing
		if (ScriptDocTokenValues::END == tk)	break;

		if (ScriptDocTokenValues::ELEMENT == tk) {
			ret->fElements.push_back( lexer->GetElement() );
		} else {
			delete lexer;
			delete ret;
			return NULL;
		}
	}

	// If there are more tokens past the end, then something has gone wrong
	if (-1 != lexer->GetNextTokenForParser()) {
		delete lexer;
		delete ret;
		return NULL;
	}

	delete lexer;

	return ret;
}
void ScriptDocLexer::Test()
{
	ScriptDocLexer *lexer = new ScriptDocLexer();
	Element *element = NULL;

	VString sourceString = "/**\n"
							"* This is a test script doc comment\n"
							"*/";
	lexer->SetLexerInput( &sourceString );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::START );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element );
	xbox_assert( element->Type() == IScriptDocCommentField::kComment );
	xbox_assert( static_cast< CommentElement * >( element )->fCommentText == "This is a test script doc comment" );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::END );

	sourceString = "/**\n"
					"*/";
	lexer->SetLexerInput( &sourceString );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::START );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::END );

	sourceString = "/**\n"
					"* This is a multiline comment\n"
					"* that should still work.\n"
					"*/";
	lexer->SetLexerInput( &sourceString );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::START );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element );
	xbox_assert( element->Type() == IScriptDocCommentField::kComment );
	xbox_assert( static_cast< CommentElement * >( element )->fCommentText == "This is a multiline comment\nthat should still work." );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::END );

	sourceString = "/**\n"
					"* This is a multiline comment\n"
					"* that should still work.\n"
					"*\n"
					"* @author Aaron Ballman [email protected]\n"
					"*/";
	lexer->SetLexerInput( &sourceString );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::START );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element );
	xbox_assert( element->Type() == IScriptDocCommentField::kComment );
	xbox_assert( static_cast< CommentElement * >( element )->fCommentText == "This is a multiline comment\nthat should still work.\n" );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kAuthor );
	xbox_assert( static_cast< AuthorElement * >( element )->fAuthorName == "Aaron Ballman [email protected]" );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::END );

	sourceString = "/**\n"
					"* This is a test for exception tags.\n"
					"*\n"
					"* @exception {MemoryException}		Throws a memory exception if we've run out of memory.\n"
					"* @constructor\n"
					"*/";
	lexer->SetLexerInput( &sourceString );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::START );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element );
	xbox_assert( element->Type() == IScriptDocCommentField::kComment );
	xbox_assert( static_cast< CommentElement * >( element )->fCommentText == "This is a test for exception tags.\n" );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kException );
	xbox_assert( static_cast< ExceptionElement * >( element )->fName == "MemoryException" );
	xbox_assert( static_cast< ExceptionElement * >( element )->fDesc == "Throws a memory exception if we've run out of memory." );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kConstructor );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::END );

	// We want to test multiple parameter elements
	sourceString = "/**\n"
					"* @param {String}			Name	A simple string parameter.\n"
					"* @param {Integer, Object}	Type	This can be a number or an object.\n"
					"* @param {Date}			[When]	An optional date parameter.\n"	
					"*/";
	lexer->SetLexerInput( &sourceString );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::START );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kParam );
	xbox_assert( static_cast< ParamElement * >( element )->fName == "Name" );
	xbox_assert( static_cast< ParamElement * >( element )->fTypes == "String" );
	xbox_assert( static_cast< ParamElement * >( element )->fDesc == "A simple string parameter." );
	xbox_assert( static_cast< ParamElement * >( element )->fOptional == false );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kParam );
	xbox_assert( static_cast< ParamElement * >( element )->fName == "Type" );
	xbox_assert( static_cast< ParamElement * >( element )->fTypes == "Integer, Object" );
	xbox_assert( static_cast< ParamElement * >( element )->fDesc == "This can be a number or an object." );
	xbox_assert( static_cast< ParamElement * >( element )->fOptional == false );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kParam );
	xbox_assert( static_cast< ParamElement * >( element )->fName == "When" );
	xbox_assert( static_cast< ParamElement * >( element )->fTypes == "Date" );
	xbox_assert( static_cast< ParamElement * >( element )->fDesc == "An optional date parameter." );
	xbox_assert( static_cast< ParamElement * >( element )->fOptional == true );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::END );

	// We are going to do the same test as above, only with the non-ScriptDoc version of the
	// param tags, where the type comes after the name
	sourceString = "/**\n"
					"* @param Name		{String}			A simple string parameter.\n"
					"* @param Type		{Integer, Object}	This can be a number or an object.\n"
					"* @param [When]	{Date}				An optional date parameter.\n"	
					"*/";
	lexer->SetLexerInput( &sourceString );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::START );
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kParam );
	xbox_assert( static_cast< ParamElement * >( element )->fName == "Name" );
	xbox_assert( static_cast< ParamElement * >( element )->fTypes == "String" );
	xbox_assert( static_cast< ParamElement * >( element )->fDesc == "A simple string parameter." );
	xbox_assert( static_cast< ParamElement * >( element )->fOptional == false );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kParam );
	xbox_assert( static_cast< ParamElement * >( element )->fName == "Type" );
	xbox_assert( static_cast< ParamElement * >( element )->fTypes == "Integer, Object" );
	xbox_assert( static_cast< ParamElement * >( element )->fDesc == "This can be a number or an object." );
	xbox_assert( static_cast< ParamElement * >( element )->fOptional == false );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::ELEMENT );
	element = lexer->GetElement();
	xbox_assert( element->Type() == IScriptDocCommentField::kParam );
	xbox_assert( static_cast< ParamElement * >( element )->fName == "When" );
	xbox_assert( static_cast< ParamElement * >( element )->fTypes == "Date" );
	xbox_assert( static_cast< ParamElement * >( element )->fDesc == "An optional date parameter." );
	xbox_assert( static_cast< ParamElement * >( element )->fOptional == true );
	delete element;
	xbox_assert( lexer->GetNextTokenForParser() == ScriptDocTokenValues::END );

	sourceString = "/**\n"
					"* Testing to ensure that packaged ScriptDocComments work\n"
					"* just as well as the lexed ones do.\n"
					"*\n"
					"* @method\n"
					"* @see someClass#someOtherMethod\n"
					"* @memberOf AwesomeClass\n"
					"* @deprecated\n"
					"* @author Aaron Ballman\n"
					"* @version 1.5\n"
					"*/";

	ScriptDocComment *comment = ScriptDocComment::Create( sourceString );
	xbox_assert( comment->ElementCount() == 7 );
	xbox_assert( comment->GetElement( 0 )->Type() == IScriptDocCommentField::kComment );
	xbox_assert( comment->GetElement( 1 )->Type() == IScriptDocCommentField::kMethod );
	xbox_assert( comment->GetElement( 2 )->Type() == IScriptDocCommentField::kSee );
	xbox_assert( static_cast< SeeElement * >( comment->GetElement( 2 ) )->fClassName == "someClass" );
	xbox_assert( static_cast< SeeElement * >( comment->GetElement( 2 ) )->fMethodName == "someOtherMethod" );
	xbox_assert( comment->GetElement( 3 )->Type() == IScriptDocCommentField::kMemberOf );
	xbox_assert( static_cast< MemberOfElement * >( comment->GetElement( 3 ) )->fClass == "AwesomeClass" );
	xbox_assert( comment->GetElement( 4 )->Type() == IScriptDocCommentField::kDeprecated );
	xbox_assert( comment->GetElement( 5 )->Type() == IScriptDocCommentField::kAuthor );
	xbox_assert( static_cast< AuthorElement * >( comment->GetElement( 5 ) )->fAuthorName == "Aaron Ballman" );
	xbox_assert( comment->GetElement( 6 )->Type() == IScriptDocCommentField::kVersion );
	xbox_assert( static_cast< VersionElement * >( comment->GetElement( 6 ) )->fVersion == "1.5" );
	delete comment;

	// We are going to do a few more tests, but use the public interface for ScriptDoc parsing
	std::vector< IScriptDocCommentField * > fields;
	xbox_assert( gLanguageSyntax->ParseScriptDocComment( sourceString, fields ) );
	xbox_assert( fields.size() == 7 );
	xbox_assert( fields[ 0 ]->GetKind() == IScriptDocCommentField::kComment );
	xbox_assert( fields[ 1 ]->GetKind() == IScriptDocCommentField::kMethod );
	xbox_assert( fields[ 2 ]->GetKind() == IScriptDocCommentField::kSee );
	xbox_assert( fields[ 3 ]->GetKind() == IScriptDocCommentField::kMemberOf );
	xbox_assert( fields[ 4 ]->GetKind() == IScriptDocCommentField::kDeprecated );
	xbox_assert( fields[ 5 ]->GetKind() == IScriptDocCommentField::kAuthor );
	xbox_assert( fields[ 6 ]->GetKind() == IScriptDocCommentField::kVersion );
	for (std::vector< IScriptDocCommentField * >::iterator iter = fields.begin(); iter != fields.end(); ++iter) {
		(*iter)->Release();
	}
	fields.clear();

	sourceString = "/**\n"
					"* @param {String}			Name	A simple string parameter.\n"
					"* @param {Integer, Object}	Type	This can be a number or an object.\n"
					"* @param {Date}			[When]	An optional date parameter.\n"	
					"*/";
	xbox_assert( gLanguageSyntax->ParseScriptDocComment( sourceString, fields ) );
	xbox_assert( fields.size() == 3 );
	VValueBag *values = fields[ 0 ]->GetContents();
	xbox_assert( values );
	VString valueText;
	xbox_assert( values->GetString( ScriptDocKeys::Name, valueText ) );
	xbox_assert( valueText == "Name" );
	xbox_assert( values->GetString( ScriptDocKeys::Types, valueText ) );
	xbox_assert( valueText == "String" );
	xbox_assert( values->GetString( ScriptDocKeys::Comment, valueText ) );
	xbox_assert( valueText == "A simple string parameter." );
	values->Release();
	values = fields[ 2 ]->GetContents();
	xbox_assert( values );
	xbox_assert( values->GetString( ScriptDocKeys::Name, valueText ) );
	xbox_assert( valueText == "When" );
	xbox_assert( values->GetString( ScriptDocKeys::Types, valueText ) );
	xbox_assert( valueText == "Date" );
	xbox_assert( values->GetString( ScriptDocKeys::Comment, valueText ) );
	xbox_assert( valueText == "An optional date parameter." );
	bool valueBool = false;
	xbox_assert( values->GetBool( ScriptDocKeys::IsOptional, valueBool ) );
	xbox_assert( valueBool );
	values->Release();
	for (std::vector< IScriptDocCommentField * >::iterator iter = fields.begin(); iter != fields.end(); ++iter) {
		(*iter)->Release();
	}
	fields.clear();

	sourceString = "/**\n"
					"* @unknown\n"
					"* @anotherUnkownTag	This is an unknown tag, but we should still display it fine\n"
					"*/";

	comment = ScriptDocComment::Create( sourceString );
	xbox_assert( comment );
	xbox_assert( comment->ElementCount() == 2 );
	xbox_assert( comment->GetElement( 0 )->Type() == IScriptDocCommentField::kUnknown );
	xbox_assert( comment->GetElement( 1 )->Type() == IScriptDocCommentField::kUnknown );
	xbox_assert( static_cast< UnknownElement * >( comment->GetElement( 0 ) )->fTag == "unknown" );
	xbox_assert( static_cast< UnknownElement * >( comment->GetElement( 1 ) )->fTag == "anotherUnkownTag" );
	xbox_assert( static_cast< UnknownElement * >( comment->GetElement( 1 ) )->fData == "This is an unknown tag, but we should still display it fine" );
	delete comment;

	sourceString = "/**\n"
					"* add two number\n"
					"* @param {Number} n1\n"
					"* @param {Number} n2\n"
					"* @type Number\n"
					"*/";
	comment = ScriptDocComment::Create( sourceString );
	xbox_assert( comment );
	xbox_assert( comment->ElementCount() == 4 );
	xbox_assert( comment->GetElement( 0 )->Type() == IScriptDocCommentField::kComment );
	xbox_assert( comment->GetElement( 1 )->Type() == IScriptDocCommentField::kParam );
	xbox_assert( comment->GetElement( 2 )->Type() == IScriptDocCommentField::kParam );
	xbox_assert( comment->GetElement( 3 )->Type() == IScriptDocCommentField::kType );
	xbox_assert( static_cast< CommentElement * >( comment->GetElement( 0 ) )->fCommentText == "add two number" );
	xbox_assert( comment->GetElement( 1 )->FormatTextForDisplay() == "Parameter Name: n1, Type: Number" );
	xbox_assert( comment->GetElement( 2 )->FormatTextForDisplay() == "Parameter Name: n2, Type: Number" );
	xbox_assert( static_cast< TypeElement * >( comment->GetElement( 3 ) )->fTypes == "Number" );
	delete comment;

	sourceString =	"/**"
					" * <p>Return a String containing this Number value represented in decimal\n"
					" * fixed-point notation with <var>fractionDigits</var> digits after the decimal\n"
					" * point. If <var>fractionDigits</var> is undefined, 0 is assumed.</p>\n"
					" *\n"
					" * @ecma 3, 5\n"
					" *\n"
					" * @method toFixed\n"
					" * @throws {RangeError} If <var>fractionDigits</var> greater than 20 or lesser\n"
					" * than 0\n"
					" * @param {Number} [fractionDigits] Default to 0\n"
					" * @return {String}\n"
					" */";
	comment = ScriptDocComment::Create( sourceString );
	xbox_assert( comment );
	xbox_assert( comment->ElementCount() == 6 );
	xbox_assert( comment->GetElement( 0 )->Type() == IScriptDocCommentField::kComment );
	xbox_assert( comment->GetElement( 1 )->Type() == IScriptDocCommentField::kUnknown );
	xbox_assert( comment->GetElement( 2 )->Type() == IScriptDocCommentField::kMethod );
	xbox_assert( comment->GetElement( 3 )->Type() == IScriptDocCommentField::kUnknown );
	xbox_assert( static_cast< UnknownElement * >( comment->GetElement( 3 ) )->FormatTextForDisplay() == "throws: {RangeError} If <var>fractionDigits</var> greater than 20 or lesser\nthan 0" );
	xbox_assert( comment->GetElement( 4 )->Type() == IScriptDocCommentField::kParam );
	xbox_assert( comment->GetElement( 5 )->Type() == IScriptDocCommentField::kReturn );
	delete comment;
}