diff options
Diffstat (limited to 'lib/pure/xmlparser.nim')
-rw-r--r--[-rwxr-xr-x] | lib/pure/xmlparser.nim | 138 |
1 files changed, 78 insertions, 60 deletions
diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 635497fa8..2c1e4e37c 100755..100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -1,6 +1,6 @@ # # -# Nimrod's Runtime Library +# Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this @@ -9,30 +9,34 @@ ## This module parses an XML document and creates its XML tree representation. -import streams, parsexml, strtabs, xmltree +import std/[streams, parsexml, strtabs, xmltree] + +when defined(nimPreviewSlimSystem): + import std/syncio type - EInvalidXml* = object of E_Base ## exception that is raised for invalid XML - errors*: seq[string] ## all detected parsing errors + XmlError* = object of ValueError ## Exception that is raised + ## for invalid XML. + errors*: seq[string] ## All detected parsing errors. -proc raiseInvalidXml(errors: seq[string]) = - var e: ref EInvalidXml +proc raiseInvalidXml(errors: seq[string]) = + var e: ref XmlError new(e) e.msg = errors[0] e.errors = errors raise e -proc addNode(father, son: PXmlNode) = +proc addNode(father, son: XmlNode) = if son != nil: add(father, son) -proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode +proc parse(x: var XmlParser, errors: var seq[string]): XmlNode {.gcsafe.} -proc untilElementEnd(x: var TXmlParser, result: PXmlNode, +proc untilElementEnd(x: var XmlParser, result: XmlNode, errors: var seq[string]) = while true: case x.kind - of xmlElementEnd: - if x.elementName == result.tag: + of xmlElementEnd: + if x.elementName == result.tag: next(x) else: errors.add(errorMsg(x, "</" & result.tag & "> expected")) @@ -44,9 +48,9 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode, else: result.addNode(parse(x, errors)) -proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = +proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = case x.kind - of xmlComment: + of xmlComment: result = newComment(x.charData) next(x) of xmlCharData, xmlWhitespace: @@ -58,20 +62,20 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = of xmlError: errors.add(errorMsg(x)) next(x) - of xmlElementStart: ## ``<elem>`` + of xmlElementStart: ## ``<elem>`` result = newElement(x.elementName) next(x) untilElementEnd(x, result, errors) of xmlElementEnd: errors.add(errorMsg(x, "unexpected ending tag: " & x.elementName)) - of xmlElementOpen: + of xmlElementOpen: result = newElement(x.elementName) next(x) - result.attr = newStringTable() - while true: + result.attrs = newStringTable() + while true: case x.kind of xmlAttribute: - result.attr[x.attrKey] = x.attrValue + result.attrs[x.attrKey] = x.attrValue next(x) of xmlElementClose: next(x) @@ -88,71 +92,85 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode = of xmlAttribute, xmlElementClose: errors.add(errorMsg(x, "<some_tag> expected")) next(x) - of xmlCData: + of xmlCData: result = newCData(x.charData) next(x) of xmlEntity: ## &entity; - errors.add(errorMsg(x, "unknown entity: " & x.entityName)) + result = newEntity(x.entityName) next(x) - of xmlEof: nil - -proc parseXml*(s: PStream, filename: string, - errors: var seq[string]): PXmlNode = - ## parses the XML from stream `s` and returns a ``PXmlNode``. Every - ## occured parsing error is added to the `errors` sequence. - var x: TXmlParser - open(x, s, filename, {reportComments}) + of xmlEof: discard + +proc parseXml*(s: Stream, filename: string, + errors: var seq[string], options: set[XmlParseOption] = {reportComments}): XmlNode = + ## Parses the XML from stream ``s`` and returns a ``XmlNode``. Every + ## occurred parsing error is added to the ``errors`` sequence. + var x: XmlParser + open(x, s, filename, options) while true: x.next() case x.kind - of xmlElementOpen, xmlElementStart: + of xmlElementOpen, xmlElementStart: result = parse(x, errors) break - of xmlComment, xmlWhitespace: nil # just skip it + of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it of xmlError: errors.add(errorMsg(x)) - of xmlSpecial: - errors.add(errorMsg(x, "<some_tag> expected")) else: errors.add(errorMsg(x, "<some_tag> expected")) break close(x) -proc parseXml*(s: PStream): PXmlNode = - ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing - ## errors are turned into an ``EInvalidXML`` exception. +proc parseXml*(s: Stream, options: set[XmlParseOption] = {reportComments}): XmlNode = + ## Parses the XML from stream ``s`` and returns a ``XmlNode``. All parsing + ## errors are turned into an ``XmlError`` exception. var errors: seq[string] = @[] - result = parseXml(s, "unknown_html_doc", errors) - if errors.len > 0: raiseInvalidXMl(errors) + result = parseXml(s, "unknown_xml_doc", errors, options) + if errors.len > 0: raiseInvalidXml(errors) -proc loadXml*(path: string, errors: var seq[string]): PXmlNode = - ## Loads and parses XML from file specified by ``path``, and returns - ## a ``PXmlNode``. Every occured parsing error is added to the `errors` +proc parseXml*(str: string, options: set[XmlParseOption] = {reportComments}): XmlNode = + ## Parses the XML from string ``str`` and returns a ``XmlNode``. All parsing + ## errors are turned into an ``XmlError`` exception. + parseXml(newStringStream(str), options) + +proc loadXml*(path: string, errors: var seq[string], options: set[XmlParseOption] = {reportComments}): XmlNode = + ## Loads and parses XML from file specified by ``path``, and returns + ## a ``XmlNode``. Every occurred parsing error is added to the ``errors`` ## sequence. var s = newFileStream(path, fmRead) - if s == nil: raise newException(EIO, "Unable to read file: " & path) - result = parseXml(s, path, errors) + if s == nil: raise newException(IOError, "Unable to read file: " & path) + result = parseXml(s, path, errors, options) -proc loadXml*(path: string): PXmlNode = - ## Loads and parses XML from file specified by ``path``, and returns - ## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML`` - ## exception. +proc loadXml*(path: string, options: set[XmlParseOption] = {reportComments}): XmlNode = + ## Loads and parses XML from file specified by ``path``, and returns + ## a ``XmlNode``. All parsing errors are turned into an ``XmlError`` + ## exception. var errors: seq[string] = @[] - result = loadXml(path, errors) - if errors.len > 0: raiseInvalidXMl(errors) + result = loadXml(path, errors, options) + if errors.len > 0: raiseInvalidXml(errors) when isMainModule: - import os - - var errors: seq[string] = @[] - var x = loadXml(paramStr(1), errors) - for e in items(errors): echo e - - var f: TFile - if open(f, "xmltest.txt", fmWrite): - f.write($x) - f.close() + when not defined(testing): + import std/os + + var errors: seq[string] = @[] + var x = loadXml(paramStr(1), errors) + for e in items(errors): echo e + + var f: File + if open(f, "xmltest.txt", fmWrite): + f.write($x) + f.close() + else: + quit("cannot write test.txt") else: - quit("cannot write test.txt") - + block: # correctly parse ../../tests/testdata/doc1.xml + let filePath = "tests/testdata/doc1.xml" + var errors: seq[string] = @[] + var xml = loadXml(filePath, errors) + assert(errors.len == 0, "The file tests/testdata/doc1.xml should be parsed without errors.") + + block bug1518: + var err: seq[string] = @[] + assert $parsexml(newStringStream"<tag>One & two</tag>", "temp.xml", + err) == "<tag>One & two</tag>" |