parseDocument function
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;
}