/*description:
{
	Walks through the AST of a DTD file parsed previously, and generates a \CodeWorker\ script
	for parsing and validating any XML file that conforms to the DTD.

	If you encounter some bugs, please send a mail to \email{\WebSite\ }.
}
*/

function normalizeClauseName(sClauseName) {
	return replaceString("-", "_", sClauseName);
}

@
function normalizeClauseName(sClauseName) {
	return replaceString("-", "_", sClauseName);
}

function generateCompositeElementContent(myComposite : node, sIndentation : value) {
	if myComposite#front.operator == "|" || myComposite.multiplicity {
		@@sIndentation@[
@
	}
	foreach i in myComposite {
		if i.constant == "#PCDATA" {
			@@sIndentation@		PCDATA_LITERAL@
			if !getProperty("SCANNER_ONLY") {@:myCurrentNode@}
			@
@
		} else if i.element {
			if i.element.multiplicity {
				@@sIndentation@		[@
				if !getProperty("SCANNER_ONLY") {@#pushItem(myCurrentNode.@normalizeClauseName(i.element)@) @}
				@@normalizeClauseName(i.element)@(myCurrentNode@
				if !getProperty("SCANNER_ONLY") {@.@normalizeClauseName(i.element)@#back@}
				@)]@i.element.multiplicity@
@
			} else {
				@@sIndentation@		@normalizeClauseName(i.element)@(myCurrentNode@
				if !getProperty("SCANNER_ONLY") {@.@normalizeClauseName(i.element)@@}
				@)
@
			}
		} else if existVariable(i.composite) {
			generateCompositeElementContent(i.composite, sIndentation + "\t\t");
		}
		if i.operator == "," {
			// nothing to do: sequence to the next line
		} else if i.operator == "|" {
			@@sIndentation@	|
@
		}
	}
	if myComposite#front.operator == "|" || myComposite.multiplicity {
		@@sIndentation@]@myComposite.multiplicity@
@
	}
}

function generateAttributes(myElement : node) {
	if existVariable(myElement.listOfAttributes) {
		@		#continue
		[
@
		foreach i in myElement.listOfAttributes {
			if !first(i) {
				@			|
@
			}
			@				IDENTIFIER:"@i@" #continue '=' STRING_LITERAL@
			if i.value == "#FIXED" {
				@:"@composeCLikeString(i.constant)@"@
			} else if !i.listOfEnums.empty() {
				@:{@
				foreach j in i.listOfEnums {
					if !j.first() {
						@, @
					}
					@"@j.composeCLikeString()@"@
				}
				@}@
			}
			if !getProperty("SCANNER_ONLY") {@:myCurrentNode.@normalizeClauseName(i)@@}
			@
@
		}
		if getArraySize(myElement.listOfAttributes) == "1" {
			@		]?
@
		} else {
			@		]*
@
		}
		foreach i in myElement.listOfAttributes {
			if i.constant && (i.value != "#FIXED") && !getProperty("SCANNER_ONLY") {
				@		=> if !existVariable(myCurrentNode.@normalizeClauseName(i)@) insert myCurrentNode.@normalizeClauseName(i)@ = "@composeCLikeString(i.constant)@";
@
			}
		}
	}
}

function generateElement(myElement : node) {
	if myElement.comment {
		@/**@myElement.comment@**/
@
	}
	@@normalizeClauseName(myElement)@(myCurrentNode : node)	::=
		'<' IDENTIFIER:"@myElement@"
@
	if (myElement.category == "EMPTY") || (myElement.category == "ANY") {
		if myElement.category == "ANY" {
			@		"/>"
	|
		"<@myElement@" #continue ANY_ATTRIBUTES(myCurrentNode)
		["/>" | '>' #continue ELEMENT_VALUE@
		if !getProperty("SCANNER_ONLY") {@:myCurrentNode@}
		@ '/' "@myElement@" '>']
@
		} else { // EMPTY
			generateAttributes(myElement);
			@		"/>"
@
		}
	} else {
		if existVariable(myElement.listOfAttributes) {
			generateAttributes(myElement);
			if existVariable(myElement.composite) {
				@		'>'
@
				generateCompositeElementContent(myElement.composite, "");
				@		"</@myElement@>"
@
			} else {
				@		"/>"
@
			}
		} else {
			if existVariable(myElement.composite) {
				@		#continue
		[
				"/>"
			|
				#continue '>'
@
				generateCompositeElementContent(myElement.composite, "\t\t");
				@				"</@myElement@>"
		]
@
			} else {
				@		#continue ["/>" | ELEMENT_VALUE@
				if !getProperty("SCANNER_ONLY") {@:myCurrentNode@}
				@ '/' "@myElement@" '>']
@
			}
		}
	}
	@		;

@
}

if this.comment {
	@/**@this.comment@**/

@
}

@XML_DOCUMENT	::=
		#ignore(XML)
		[
			"<?xml" #continue
			"version" '=' STRING_LITERAL
			["encoding" #continue '=' STRING_LITERAL]?
			"?>"
		]?
		[
			"<!DOCTYPE" #continue
			IDENTIFIER EXTERNAL_DOCTYPE
			[STRING_LITERAL]*
			[
				'['
				["<!" ->'>']+
				']'
			]?
			'>'
		]?
		@
if !isEmpty(this.listOfElements) {
	@#continue @normalizeClauseName(this.listOfElements#front)@(this) #empty@
}
@;

@

foreach i in this.listOfElements {
	generateElement(i);
}

@

ANY_ATTRIBUTES(myCurrentNode : node)	::=
		[
			IDENTIFIER:sAttribute
			#continue '='
			STRING_LITERAL:sValue
@
			if !getProperty("SCANNER_ONLY") {
				@			=> insert #evaluateVariable("myCurrentNode." + normalizeClauseName(sAttribute)) = sValue;
@
			}
@		]*;
ANY_ELEMENTS(myCurrentNode : node)	::=
		[
			'<'
			IDENTIFIER:sOpenElement
			#continue
@
			if !getProperty("SCANNER_ONLY") {
@			=> local sVariable = "myCurrentNode." + normalizeClauseName(sOpenElement);
			=> pushItem #evaluateVariable(sVariable);
			=> localref myElement = #evaluateVariable(sVariable + "#back");
@
			} else {
				@			=> local myElement;
@
			}
@			ANY_ATTRIBUTES(myElement)
			[
					"/>"
				|
					'>' ANY_ELEMENTS(myElement) "</" IDENTIFIER:sCloseElement
					=> if sOpenElement != sCloseElement error("'</" + sCloseElement + ">' found to close '<" + sOpenElement + ">'");
					'>'
			]
		]*;

PCDATA_LITERAL	::=	#!ignore #continue [~'<']*:PCDATA_LITERAL;
EXTERNAL_DOCTYPE	::= "PUBLIC" #continue STRING_LITERAL STRING_LITERAL | "SYSTEM" #continue STRING_LITERAL;
IDENTIFIER : value	::=
		#!ignore #readIdentifier:IDENTIFIER
		['-' #readIdentifier]*:sIdentifier
		=> set IDENTIFIER += sIdentifier;
		;
STRING_LITERAL:value ::= '"' #!ignore #continue ->(:STRING_LITERAL)'"' | "'" #!ignore #continue ->(:STRING_LITERAL)"'";
ELEMENT_VALUE:value ::= ->(:ELEMENT_VALUE)"<";
