# # # Nim's Runtime Library # (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## A simple XML tree. More efficient and simpler than the DOM. import macros, strtabs type XmlNode* = ref XmlNodeObj ## an XML tree consists of ``PXmlNode``'s. XmlNodeKind* = enum ## different kinds of ``PXmlNode``'s xnText, ## a text element xnElement, ## an element with 0 or more children xnCData, ## a CDATA node xnEntity, ## an entity (like ``&thing;``) xnComment ## an XML comment XmlAttributes* = StringTableRef ## an alias for a string to string mapping XmlNodeObj {.acyclic.} = object case k: XmlNodeKind # private, use the kind() proc to read this field. of xnText, xnComment, xnCData, xnEntity: fText: string of xnElement: fTag: string s: seq[XmlNode] fAttr: XmlAttributes fClientData: int ## for other clients {.deprecated: [PXmlNode: XmlNode, TXmlNodeKind: XmlNodeKind, PXmlAttributes: XmlAttributes, TXmlNode: XmlNodeObj].} proc newXmlNode(kind: XmlNodeKind): XmlNode = ## creates a new ``XmlNode``. new(result) result.k = kind proc newElement*(tag: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnText`` with the given `tag`. result = newXmlNode(xnElement) result.fTag = tag result.s = @[] # init attributes lazily to safe memory proc newText*(text: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnText`` with the text `text`. result = newXmlNode(xnText) result.fText = text proc newComment*(comment: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `comment`. result = newXmlNode(xnComment) result.fText = comment proc newCData*(cdata: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnComment`` with the text `cdata`. result = newXmlNode(xnCData) result.fText = cdata proc newEntity*(entity: string): XmlNode = ## creates a new ``PXmlNode`` of kind ``xnEntity`` with the text `entity`. result = newXmlNode(xnCData) result.fText = entity proc text*(n: XmlNode): string {.inline.} = ## gets the associated text with the node `n`. `n` can be a CDATA, Text, ## comment, or entity node. assert n.k in {xnText, xnComment, xnCData, xnEntity} result = n.fText proc rawText*(n: XmlNode): string {.inline.} = ## returns the underlying 'text' string by reference. ## This is only used for speed hacks. shallowCopy(result, n.fText) proc rawTag*(n: XmlNode): string {.inline.} = ## returns the underlying 'tag' string by reference. ## This is only used for speed hacks. shallowCopy(result, n.fTag) proc innerText*(n: XmlNode): string = ## gets the inner text of `n`. `n` has to be an ``xnElement`` node. Only ## ``xnText`` and ``xnEntity`` nodes are considered part of `n`'s inner text, ## other child nodes are silently ignored. result = "" assert n.k == xnElement for i in 0 .. n.s.len-1: if n.s[i].k in {xnText, xnEntity}: result.add(n.s[i].fText) proc tag*(n: XmlNode): string {.inline.} = ## gets the tag name of `n`. `n` has to be an ``xnElement`` node. assert n.k == xnElement result = n.fTag proc add*(father, son: XmlNode) {.inline.} = ## adds the child `son` to `father`. add(father.s, son) proc len*(n: XmlNode): int {.inline.} = ## returns the number `n`'s children. if n.k == xnElement: result = len(n.s) proc kind*(n: XmlNode): XmlNodeKind {.inline.} = ## returns `n`'s kind. result = n.k proc `[]`* (n: XmlNode, i: int): XmlNode {.inline.} = ## returns the `i`'th child of `n`. assert n.k == xnElement result = n.s[i] proc mget* (n: var XmlNode, i: int): var XmlNode {.inline.} = ##
-d:ssl
of `name`. ## Returns `nil` on failure. assert n.kind == xnElement for i in items(n): if i.kind == xnElement: if i.tag == name: return i proc attr*(n: XmlNode, name: string): string = ## Finds the first attribute of `n` with a name of `name`. ## Returns "" on failure. assert n.kind == xnElement if n.attrs == nil: return "" return n.attrs[name] proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode]) = ## Iterates over all the children of `n` returning those matching `tag`. ## ## Found nodes satisfying the condition will be appended to the `result` ## sequence, which can't be nil or the proc will crash. Usage example: ## ## .. code-block:: ## var ## html: XmlNode ## tags: seq[XmlNode] = @[] ## ## html = buildHtml() ## findAll(html, "img", tags) ## for imgTag in tags: ## process(imgTag) assert isNil(result) == false assert n.k == xnElement for child in n.items(): if child.k != xnElement: continue if child.tag == tag: result.add(child) child.findAll(tag, result) proc findAll*(n: XmlNode, tag: string): seq[XmlNode] = ## Shortcut version to assign in let blocks. Example: ## ## .. code-block:: ## var html: XmlNode ## ## html = buildHtml(html) ## for imgTag in html.findAll("img"): ## process(imgTag) newSeq(result, 0) findAll(n, tag, result) when isMainModule: let link = "http://nim-lang.org" assert """Nim rules.""" == $(<>a(href="http://nim-lang.org", newText("Nim rules.")))