diff options
-rw-r--r-- | contributing.rst | 137 | ||||
-rw-r--r-- | lib/pure/algorithm.nim | 21 | ||||
-rw-r--r-- | lib/pure/htmlparser.nim | 99 | ||||
-rw-r--r-- | lib/pure/math.nim | 18 | ||||
-rw-r--r-- | tests/stdlib/thtmlparser2813.nim | 45 |
5 files changed, 271 insertions, 49 deletions
diff --git a/contributing.rst b/contributing.rst new file mode 100644 index 000000000..8e669c614 --- /dev/null +++ b/contributing.rst @@ -0,0 +1,137 @@ +The git stuff +============= + +`Guide by github, scroll down a bit <https://guides.github.com/activities/contributing-to-open-source/>`_ + +Deprecation +=========== + +Backward compatibility is important, so if you are renaming a proc or +a type, you can use + + +.. code-block:: nim + + {.deprecated [oldName: new_name].} + +Or you can simply use + +.. code-block:: nim + + proc oldProc() {.deprecated.} + +to mark a symbol as deprecated. Works for procs/types/vars/consts, +etc. + +`Deprecated pragma in the manual. <http://nim-lang.org/docs/manual.html#pragmas-deprecated-pragma>`_ + +Writing tests +============= + +Not all the tests follow this scheme, feel free to change the ones +that don't. Always leave the code cleaner than you found it. + +Stdlib +------ + +If you change the stdlib (anything under ``lib/``), put a test in the +file you changed. Add the tests under an ``when isMainModule:`` +condition so they only get executed when the tester is building the +file. Each test should be in a separate ``block:`` statement, such that +each has its own scope. Use boolean conditions and ``doAssert`` for the +testing by itself, don't rely on echo statements or similar. + +Sample test: + +.. code-block:: nim + + when isMainModule: + block: # newSeqWith tests + var seq2D = newSeqWith(4, newSeq[bool](2)) + seq2D[0][0] = true + seq2D[1][0] = true + seq2D[0][1] = true + doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] + +Compiler +-------- + +The tests for the compiler work differently, they are all located in +``tests/``. Each test has its own file, which is different from the +stdlib tests. At the beginning of every test is the expected side of +the test. Possible keys are: + +- output: The expected output, most likely via ``echo`` +- exitcode: Exit code of the test (via ``exit(number)``) +- errormsg: The expected error message +- file: The file the errormsg +- line: The line the errormsg was produced at + +An example for a test: + +.. code-block:: nim + + discard """ + errormsg: "type mismatch: got (PTest)" + """ + + type + PTest = ref object + + proc test(x: PTest, y: int) = nil + + var buf: PTest + buf.test() + +Running tests +============= + +You can run the tests with + +.. code-block:: bash + + ./koch tests + +which will run a good subset of tests. Some tests may fail. If you +only want to run failing tests, go for + +.. code-block:: bash + + ./koch tests --failing all + +You can also run only a single category of tests. For a list of +categories, see ``tests/testament/categories.nim``, at the bottom. + +.. code-block:: bash + + ./koch tests c lib + +Comparing tests +=============== + +Because some tests fail in the current ``devel`` branch, not every fail +after your change is necessarily caused by your changes. + +The tester can compare two test runs. First, you need to create the +reference test. You'll also need to the commit id, because that's what +the tester needs to know in order to compare the two. + +.. code-block:: bash + + git checkout devel + DEVEL_COMMIT=$(git rev-parse HEAD) + ./koch tests + +Then switch over to your changes and run the tester again. + +.. code-block:: bash + + git checkout your-changes + ./koch tests + +Then you can ask the tester to create a ``testresults.html`` which will +tell you if any new tests passed/failed. + +.. code-block:: bash + + ./koch --print html $DEVEL_COMMIT diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index c9f779018..ac18ae420 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -237,6 +237,17 @@ template sortedByIt*(seq1, op: expr): expr = result = cmp(a, b)) result +proc isSorted*[T](a: openarray[T], + cmp: proc(x, y: T): int {.closure.}, + order = SortOrder.Ascending): bool = + ## Checks to see whether `a` is already sorted in `order` + ## using `cmp` for the comparison. Parameters identical + ## to `sort` + result = true + for i in 0..<len(a)-1: + if cmp(a[i],a[i+1]) * order > 0: + return false + proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = ## produces the Cartesian product of the array. Warning: complexity ## may explode. @@ -340,4 +351,14 @@ when isMainModule: assert arr.lowerBound(4) == 1 assert arr.lowerBound(5) == 1 assert arr.lowerBound(6) == 2 + # Tests for isSorted + var srt1 = [1,2,3,4,4,4,4,5] + var srt2 = ["iello","hello"] + var srt3 = [1.0,1.0,1.0] + var srt4 = [] + assert srt1.isSorted(cmp) == true + assert srt2.isSorted(cmp) == false + assert srt3.isSorted(cmp) == true + var srtseq = newSeq[int]() + assert srtseq.isSorted(cmp) == true diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index a0e2ecd9b..ff1cd7d8d 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -9,7 +9,7 @@ ## This module parses an HTML document and creates its XML tree representation. ## It is supposed to handle the *wild* HTML the real world uses. -## +## ## It can be used to parse a wild HTML document and output it as valid XHTML ## document (well, if you are lucky): ## @@ -19,7 +19,7 @@ ## ## Every tag in the resulting tree is in lower case. ## -## **Note:** The resulting ``PXmlNode`` already uses the ``clientData`` field, +## **Note:** The resulting ``PXmlNode`` already uses the ``clientData`` field, ## so it cannot be used by clients of this library. ## ## Example: Transforming hyperlinks @@ -182,24 +182,24 @@ type const tagToStr* = [ - "a", "abbr", "acronym", "address", "applet", "area", "article", + "a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", - "b", "base", "basefont", "bdi", "bdo", "big", "blockquote", "body", - "br", "button", "canvas", "caption", "center", "cite", "code", + "b", "base", "basefont", "bdi", "bdo", "big", "blockquote", "body", + "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "command", - "datalist", "dd", "del", "details", "dfn", "dialog", "div", - "dir", "dl", "dt", "em", "embed", "fieldset", + "datalist", "dd", "del", "details", "dfn", "dialog", "div", + "dir", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "font", "footer", - "form", "frame", "frameset", "h1", "h2", "h3", - "h4", "h5", "h6", "head", "header", "hgroup", "html", "hr", - "i", "iframe", "img", "input", "ins", "isindex", + "form", "frame", "frameset", "h1", "h2", "h3", + "h4", "h5", "h6", "head", "header", "hgroup", "html", "hr", + "i", "iframe", "img", "input", "ins", "isindex", "kbd", "keygen", "label", "legend", "li", "link", "map", "mark", - "menu", "meta", "meter", "nav", "nobr", "noframes", "noscript", - "object", "ol", - "optgroup", "option", "output", "p", "param", "pre", "progress", "q", - "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", - "source", "span", "strike", "strong", "style", - "sub", "summary", "sup", "table", + "menu", "meta", "meter", "nav", "nobr", "noframes", "noscript", + "object", "ol", + "optgroup", "option", "output", "p", "param", "pre", "progress", "q", + "rp", "rt", "ruby", "s", "samp", "script", "section", "select", "small", + "source", "span", "strike", "strong", "style", + "sub", "summary", "sup", "table", "tbody", "td", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"] InlineTags* = {tagA, tagAbbr, tagAcronym, tagApplet, tagB, tagBasefont, @@ -207,17 +207,17 @@ const tagEm, tagFont, tagI, tagImg, tagIns, tagInput, tagIframe, tagKbd, tagLabel, tagMap, tagObject, tagQ, tagSamp, tagScript, tagSelect, tagSmall, tagSpan, tagStrong, tagSub, tagSup, tagTextarea, tagTt, - tagVar, tagApplet, tagBasefont, tagFont, tagIframe, tagU, tagS, + tagVar, tagApplet, tagBasefont, tagFont, tagIframe, tagU, tagS, tagStrike, tagWbr} - BlockTags* = {tagAddress, tagBlockquote, tagCenter, tagDel, tagDir, tagDiv, - tagDl, tagFieldset, tagForm, tagH1, tagH2, tagH3, tagH4, - tagH5, tagH6, tagHr, tagIns, tagIsindex, tagMenu, tagNoframes, tagNoscript, - tagOl, tagP, tagPre, tagTable, tagUl, tagCenter, tagDir, tagIsindex, + BlockTags* = {tagAddress, tagBlockquote, tagCenter, tagDel, tagDir, tagDiv, + tagDl, tagFieldset, tagForm, tagH1, tagH2, tagH3, tagH4, + tagH5, tagH6, tagHr, tagIns, tagIsindex, tagMenu, tagNoframes, tagNoscript, + tagOl, tagP, tagPre, tagTable, tagUl, tagCenter, tagDir, tagIsindex, tagMenu, tagNoframes} - SingleTags* = {tagArea, tagBase, tagBasefont, + SingleTags* = {tagArea, tagBase, tagBasefont, tagBr, tagCol, tagFrame, tagHr, tagImg, tagIsindex, tagLink, tagMeta, tagParam, tagWbr} - + Entities = [ ("nbsp", 0x00A0), ("iexcl", 0x00A1), ("cent", 0x00A2), ("pound", 0x00A3), ("curren", 0x00A4), ("yen", 0x00A5), ("brvbar", 0x00A6), ("sect", 0x00A7), @@ -226,13 +226,13 @@ const ("deg", 0x00B0), ("plusmn", 0x00B1), ("sup2", 0x00B2), ("sup3", 0x00B3), ("acute", 0x00B4), ("micro", 0x00B5), ("para", 0x00B6), ("middot", 0x00B7), ("cedil", 0x00B8), ("sup1", 0x00B9), ("ordm", 0x00BA), ("raquo", 0x00BB), - ("frac14", 0x00BC), ("frac12", 0x00BD), ("frac34", 0x00BE), + ("frac14", 0x00BC), ("frac12", 0x00BD), ("frac34", 0x00BE), ("iquest", 0x00BF), ("Agrave", 0x00C0), ("Aacute", 0x00C1), ("Acirc", 0x00C2), ("Atilde", 0x00C3), ("Auml", 0x00C4), ("Aring", 0x00C5), ("AElig", 0x00C6), ("Ccedil", 0x00C7), ("Egrave", 0x00C8), ("Eacute", 0x00C9), ("Ecirc", 0x00CA), ("Euml", 0x00CB), ("Igrave", 0x00CC), ("Iacute", 0x00CD), ("Icirc", 0x00CE), ("Iuml", 0x00CF), ("ETH", 0x00D0), - ("Ntilde", 0x00D1), ("Ograve", 0x00D2), ("Oacute", 0x00D3), + ("Ntilde", 0x00D1), ("Ograve", 0x00D2), ("Oacute", 0x00D3), ("Ocirc", 0x00D4), ("Otilde", 0x00D5), ("Ouml", 0x00D6), ("times", 0x00D7), ("Oslash", 0x00D8), ("Ugrave", 0x00D9), ("Uacute", 0x00DA), ("Ucirc", 0x00DB), ("Uuml", 0x00DC), ("Yacute", 0x00DD), ("THORN", 0x00DE), @@ -264,7 +264,7 @@ const ("zwnj", 0x200C), ("zwj", 0x200D), ("lrm", 0x200E), ("rlm", 0x200F), ("ndash", 0x2013), ("mdash", 0x2014), ("lsquo", 0x2018), ("rsquo", 0x2019), ("sbquo", 0x201A), ("ldquo", 0x201C), ("rdquo", 0x201D), ("bdquo", 0x201E), - ("dagger", 0x2020), ("Dagger", 0x2021), ("bull", 0x2022), + ("dagger", 0x2020), ("Dagger", 0x2021), ("bull", 0x2022), ("hellip", 0x2026), ("permil", 0x2030), ("prime", 0x2032), ("Prime", 0x2033), ("lsaquo", 0x2039), ("rsaquo", 0x203A), ("oline", 0x203E), ("frasl", 0x2044), ("euro", 0x20AC), @@ -423,7 +423,8 @@ proc toHtmlTag(s: string): HtmlTag = of "wbr": tagWbr else: tagUnknown -proc htmlTag*(n: XmlNode): HtmlTag = + +proc htmlTag*(n: XmlNode): HtmlTag = ## gets `n`'s tag as a ``HtmlTag``. if n.clientData == 0: n.clientData = toHtmlTag(n.tag).ord @@ -435,7 +436,7 @@ proc htmlTag*(s: string): HtmlTag = let s = if allLower(s): s else: s.toLower result = toHtmlTag(s) -proc entityToUtf8*(entity: string): string = +proc entityToUtf8*(entity: string): string = ## converts an HTML entity name like ``Ü`` to its UTF-8 equivalent. ## "" is returned if the entity name is unknown. The HTML parser ## already converts entities to UTF-8. @@ -443,7 +444,7 @@ proc entityToUtf8*(entity: string): string = if name == entity: return toUTF8(Rune(val)) result = "" -proc addNode(father, son: XmlNode) = +proc addNode(father, son: XmlNode) = if son != nil: add(father, son) proc parse(x: var XmlParser, errors: var seq[string]): XmlNode @@ -453,9 +454,9 @@ proc expected(x: var XmlParser, n: XmlNode): string = template elemName(x: expr): expr = rawData(x) -proc untilElementEnd(x: var XmlParser, result: XmlNode, +proc untilElementEnd(x: var XmlParser, result: XmlNode, errors: var seq[string]) = - # we parsed e.g. ``<br>`` and don't really expect a ``</br>``: + # we parsed e.g. ``<br>`` and don't really expect a ``</br>``: if result.htmlTag in SingleTags: if x.kind != xmlElementEnd or cmpIgnoreCase(x.elemName, result.tag) != 0: return @@ -469,7 +470,7 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode, tagOption}: errors.add(expected(x, result)) break - of tagTd, tagTh, tagTfoot, tagThead: + of tagTd, tagTh: if htmlTag(x.elemName) in {tagTr, tagTd, tagTh, tagTfoot, tagThead}: errors.add(expected(x, result)) break @@ -483,11 +484,11 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode, break else: discard result.addNode(parse(x, errors)) - of xmlElementEnd: - if cmpIgnoreCase(x.elemName, result.tag) == 0: + of xmlElementEnd: + if cmpIgnoreCase(x.elemName, result.tag) == 0: next(x) else: - #echo "5; expected: ", result.htmltag, " ", x.elemName + #echo "5; expected: ", result.htmltag, " ", x.elemName errors.add(expected(x, result)) # do not skip it here! break @@ -499,7 +500,7 @@ proc untilElementEnd(x: var XmlParser, result: XmlNode, proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = case x.kind - of xmlComment: + of xmlComment: result = newComment(x.rawData) next(x) of xmlCharData, xmlWhitespace: @@ -517,11 +518,11 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = untilElementEnd(x, result, errors) of xmlElementEnd: errors.add(errorMsg(x, "unexpected ending tag: " & x.elemName)) - of xmlElementOpen: + of xmlElementOpen: result = newElement(x.elemName.toLower) next(x) result.attrs = newStringTable() - while true: + while true: case x.kind of xmlAttribute: result.attrs[x.rawData] = x.rawData2 @@ -541,7 +542,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = of xmlAttribute, xmlElementClose: errors.add(errorMsg(x, "<some_tag> expected")) next(x) - of xmlCData: + of xmlCData: result = newCData(x.rawData) next(x) of xmlEntity: @@ -550,8 +551,8 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = next(x) of xmlEof: discard -proc parseHtml*(s: Stream, filename: string, - errors: var seq[string]): XmlNode = +proc parseHtml*(s: Stream, filename: string, + errors: var seq[string]): XmlNode = ## parses the XML from stream `s` and returns a ``PXmlNode``. Every ## occurred parsing error is added to the `errors` sequence. var x: XmlParser @@ -559,7 +560,7 @@ proc parseHtml*(s: Stream, filename: string, next(x) # skip the DOCTYPE: if x.kind == xmlSpecial: next(x) - + result = newElement("document") result.addNode(parse(x, errors)) #if x.kind != xmlEof: @@ -574,22 +575,22 @@ proc parseHtml*(s: Stream, filename: string, if result.len == 1: result = result[0] -proc parseHtml*(s: Stream): XmlNode = +proc parseHtml*(s: Stream): XmlNode = ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing ## errors are ignored. var errors: seq[string] = @[] result = parseHtml(s, "unknown_html_doc", errors) -proc loadHtml*(path: string, errors: var seq[string]): XmlNode = - ## Loads and parses HTML from file specified by ``path``, and returns +proc loadHtml*(path: string, errors: var seq[string]): XmlNode = + ## Loads and parses HTML from file specified by ``path``, and returns ## a ``PXmlNode``. Every occurred parsing error is added to ## the `errors` sequence. var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file: " & path) result = parseHtml(s, path, errors) -proc loadHtml*(path: string): XmlNode = - ## Loads and parses HTML from file specified by ``path``, and returns +proc loadHtml*(path: string): XmlNode = + ## Loads and parses HTML from file specified by ``path``, and returns ## a ``PXmlNode``. All parsing errors are ignored. var errors: seq[string] = @[] result = loadHtml(path, errors) @@ -597,10 +598,10 @@ proc loadHtml*(path: string): XmlNode = when not defined(testing) and isMainModule: import os - var errors: seq[string] = @[] + var errors: seq[string] = @[] var x = loadHtml(paramStr(1), errors) for e in items(errors): echo e - + var f: File if open(f, "test.txt", fmWrite): f.write($x) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index b91c7f0d8..494dfc4c8 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -161,6 +161,8 @@ proc randomize*(seed: int) {.benign.} when not defined(JS): proc sqrt*(x: float): float {.importc: "sqrt", header: "<math.h>".} ## computes the square root of `x`. + proc cbrt*(x: float): float {.importc: "cbrt", header: "<math.h>".} + ## computes the cubic root of `x` proc ln*(x: float): float {.importc: "log", header: "<math.h>".} ## computes ln(x). @@ -200,6 +202,16 @@ when not defined(JS): proc tanh*(x: float): float {.importc: "tanh", header: "<math.h>".} proc pow*(x, y: float): float {.importc: "pow", header: "<math.h>".} ## computes x to power raised of y. + + proc erf*(x: float): float {.importc: "erf", header: "<math.h>".} + ## The error function + proc erfc*(x: float): float {.importc: "erfc", header: "<math.h>".} + ## The complementary error function + + proc lgamma*(x: float): float {.importc: "lgamma", header: "<math.h>".} + ## Natural log of the gamma function + proc tgamma*(x: float): float {.importc: "tgamma", header: "<math.h>".} + ## The gamma function # C procs: when defined(vcc): @@ -411,3 +423,9 @@ when isMainModule and not defined(JS): # Check for no side effect annotation proc mySqrt(num: float): float {.noSideEffect.} = return sqrt(num) + + # check gamma function + assert(tgamma(5.0) == 24.0) # 4! + assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 + assert(erf(6.0) > erf(5.0)) + assert(erfc(6.0) < erfc(5.0)) diff --git a/tests/stdlib/thtmlparser2813.nim b/tests/stdlib/thtmlparser2813.nim new file mode 100644 index 000000000..4b04bc3f0 --- /dev/null +++ b/tests/stdlib/thtmlparser2813.nim @@ -0,0 +1,45 @@ +discard """ + output: "@[]" +""" +import htmlparser +import xmltree +from streams import newStringStream + +const + html = """ + <html> + <head> + <title>Test</title> + </head> + <body> + <table> + <thead> + <tr><td>A</td></tr> + <tr><td>B</td></tr> + </thead> + <tbody> + <tr><td></td>A<td></td></tr> + <tr><td></td>B<td></td></tr> + <tr><td></td>C<td></td></tr> + </tbody> + <tfoot> + <tr><td>A</td></tr> + </tfoot> + </table> + </body> + </html> + """ +var errors: seq[string] = @[] + +let tree = parseHtml(newStringStream(html), "test.html", errors) + +echo errors # Errors: </thead> expected,... + +var len = tree.findAll("tr").len # len = 6 + +var rows: seq[XmlNode] = @[] +for n in tree.findAll("table"): + n.findAll("tr", rows) # len = 2 + break + +assert tree.findAll("tr").len == rows.len |