parseDocument function

DocumentNode parseDocument(
  1. String src
)

Parse a Dingo document from a string.

Implementation

DocumentNode parseDocument(String src) {
	// create scanner and tokenize
	var scanner = Scanner(src);
	var tokens = Token.tokenize(scanner);
	// create root node to parse into
	var document = DocumentNode();

	// this function recursively builds document trees! i adore recursion
	void parseChildren(Node parent) {
		bool keepParsing = true;
		while (tokens.isNotEmpty && keepParsing) {
			// get first token, pop from mound
			var token = tokens.removeAt(0);
			// check for each type
			switch (token.type) {
				// comments
				case TokenType.comment:
					// do nothing
					continue;
				// declarations
				case TokenType.declaration:
					// make declaration (done separately to ensure that even empty declarations are created)
					document.makeDeclaration(token.name);
					// copy declarations from token
					var attributes = token.attributes!;
					for (var attribute in attributes.keys) {
						document.setDeclaration(token.name, attribute, attributes[attribute]!);
					}
					break;
				// tag openers
				case TokenType.tagOpen:
					// create node
					var newNode = TagNode(token.name);
					// copy attributes from token
					var attributes = token.attributes!;
					for (var attribute in attributes.keys) {
						newNode[attribute] = attributes[attribute]!;
					}
					// parse children if not self-closing
					if (!token.selfClosing) {
						parseChildren(newNode);
					}
					// append
					parent.appendChild(newNode);
					break;
				// tag closers
				case TokenType.tagClose:
					// end node if this closer matches this opener
					if (parent.runtimeType == TagNode && (parent as TagNode).name == token.name) {
						keepParsing = false;
						break;
					}
					// otherwise, throw an exception
					throw "unmatched closer for tag '${token.name}' at ${scanner.getLineCol(token.position)}";
				// text
				case TokenType.text:
					// just create a text node
					parent.appendChild(TextNode(token.text));
					break;
				// cdata
				case TokenType.cdata:
					// create a cdata node
					parent.appendChild(CdataNode(token.text));
					break;
			}
		}
	}

	// parse and return document
	parseChildren(document);
	return document;
}