diff --git a/changelog.md b/changelog.md index f39f68856ab46..e7ab1a2982b2c 100644 --- a/changelog.md +++ b/changelog.md @@ -37,6 +37,17 @@ and this can now throw in edge cases where `getCurrentDir` throws. `relativePath` also now works for js with `-d:nodejs`. +- JavaScript and NimScript standard library changes: `streams.StringStream` is + now supported in JavaScript, with the limitation that any buffer `pointer`s + used must be castable to `ptr string`, any incompatible pointer type will not + work. The `lexbase` and `streams` modules used to fail to compile on + NimScript due to a bug, but this has been fixed. + + The following modules now compile on both JS and NimScript: `parsecsv`, + `parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally + supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for + NimScript: `json`, `parsejson`, `strtabs` and `unidecode`. + - Added `streams.readStr` and `streams.peekStr` overloads to accept an existing string to modify, which avoids memory allocations, similar to `streams.readLine` (#13857). diff --git a/doc/backends.rst b/doc/backends.rst index 1a6729ee6f4f8..52c16b9f72204 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -76,13 +76,15 @@ available. This includes: * manual memory management (``alloc``, etc.) * casting and other unsafe operations (``cast`` operator, ``zeroMem``, etc.) * file management -* most modules of the standard library +* OS-specific operations +* threading, coroutines +* some modules of the standard library * proper 64 bit integer arithmetic -* unsigned integer arithmetic -However, the modules `strutils `_, `math `_, and -`times `_ are available! To access the DOM, use the `dom -`_ module that is only available for the JavaScript platform. +To compensate, the standard library has modules `catered to the JS backend +`_ +and more support will come in the future (for instance, Node.js bindings +to get OS info). To compile a Nim module into a ``.js`` file use the ``js`` command; the default is a ``.js`` file that is supposed to be referenced in an ``.html`` diff --git a/doc/nims.rst b/doc/nims.rst index 8e3638d62ad09..c1d434476d2ac 100644 --- a/doc/nims.rst +++ b/doc/nims.rst @@ -52,8 +52,6 @@ NimScript is subject to some limitations caused by the implementation of the VM * ``random.randomize()`` requires an ``int64`` explicitly passed as argument, you *must* pass a Seed integer. -* ``unicode`` can be imported, but not ``unidecode``. - Standard library modules ======================== diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim index fe9ceb68b6f7f..a2a8fbc2ff933 100644 --- a/lib/pure/cstrutils.nim +++ b/lib/pure/cstrutils.nim @@ -19,29 +19,43 @@ proc toLowerAscii(c: char): char {.inline.} = else: result = c -proc startsWith*(s, prefix: cstring): bool {.noSideEffect, - rtl, extern: "csuStartsWith".} = - ## Returns true iff ``s`` starts with ``prefix``. - ## - ## If ``prefix == ""`` true is returned. - var i = 0 - while true: - if prefix[i] == '\0': return true - if s[i] != prefix[i]: return false - inc(i) +when defined(js): + proc startsWith*(s, prefix: cstring): bool {.noSideEffect, + importjs: "#.startsWith(#)".} -proc endsWith*(s, suffix: cstring): bool {.noSideEffect, - rtl, extern: "csuEndsWith".} = - ## Returns true iff ``s`` ends with ``suffix``. - ## - ## If ``suffix == ""`` true is returned. - let slen = s.len - var i = 0 - var j = slen - len(suffix) - while i+j <% slen: - if s[i+j] != suffix[i]: return false - inc(i) - if suffix[i] == '\0': return true + proc endsWith*(s, suffix: cstring): bool {.noSideEffect, + importjs: "#.endsWith(#)".} + + # JS string has more operations that might warrant its own module: + # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String +else: + proc startsWith*(s, prefix: cstring): bool {.noSideEffect, + rtl, extern: "csuStartsWith".} = + ## Returns true iff ``s`` starts with ``prefix``. + ## + ## If ``prefix == ""`` true is returned. + ## + ## JS backend uses native ``String.prototype.startsWith``. + var i = 0 + while true: + if prefix[i] == '\0': return true + if s[i] != prefix[i]: return false + inc(i) + + proc endsWith*(s, suffix: cstring): bool {.noSideEffect, + rtl, extern: "csuEndsWith".} = + ## Returns true iff ``s`` ends with ``suffix``. + ## + ## If ``suffix == ""`` true is returned. + ## + ## JS backend uses native ``String.prototype.endsWith``. + let slen = s.len + var i = 0 + var j = slen - len(suffix) + while i+j <% slen: + if s[i+j] != suffix[i]: return false + inc(i) + if suffix[i] == '\0': return true proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect, rtl, extern: "csuCmpIgnoreStyle".} = @@ -53,6 +67,9 @@ proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect, ## | 0 iff a == b ## | < 0 iff a < b ## | > 0 iff a > b + ## + ## Not supported for JS backend, use `strutils.cmpIgnoreStyle + ## `_ instead. var i = 0 var j = 0 while true: @@ -72,6 +89,9 @@ proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect, ## | 0 iff a == b ## | < 0 iff a < b ## | > 0 iff a > b + ## + ## Not supported for JS backend, use `strutils.cmpIgnoreCase + ## `_ instead. var i = 0 while true: var aa = toLowerAscii(a[i]) diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index 8bc96c82cc3aa..27225ab8dbd88 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -52,7 +52,8 @@ proc fillBuffer(L: var BaseLexer) = toCopy = L.buf.len - (L.sentinel + 1) assert(toCopy >= 0) if toCopy > 0: - when defined(js): + when defined(js) or defined(nimscript): + # nimscript has to be here to avoid compiling other branch (moveMem) for i in 0 ..< toCopy: L.buf[i] = L.buf[L.sentinel + 1 + i] else: diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index daf313b5c1fba..b6ad2e20f1356 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -50,8 +50,14 @@ ## * `streams module `_ ## * `json module `_ -when defined(nimV2): - {.error: """marshal module is not supported in new runtime. +const unsupportedPlatform = + when defined(nimV2): "new runtime" + elif defined(js): "javascript" + elif defined(nimscript): "nimscript" + else: "" + +when unsupportedPlatform != "": + {.error: "marshal module is not supported in " & unsupportedPlatform & """. Please use alternative packages for serialization. It is possible to reimplement this module using generics and type traits. Please contribute new implementation.""".} diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index b84c1a744abec..68e75e5fa34ab 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -557,7 +557,14 @@ type tok: Token proc newNode*(k: SqlNodeKind): SqlNode = - result = SqlNode(kind: k) + when defined(js): # bug #14117 + case k + of LiteralNodes: + result = SqlNode(kind: k, strVal: "") + else: + result = SqlNode(kind: k, sons: @[]) + else: + result = SqlNode(kind: k) proc newNode*(k: SqlNodeKind, s: string): SqlNode = result = SqlNode(kind: k) @@ -1469,34 +1476,33 @@ proc treeRepr*(s: SqlNode): string = result = newStringOfCap(128) treeReprAux(s, 0, result) -when not defined(js): - import streams +import streams - proc open(L: var SqlLexer, input: Stream, filename: string) = - lexbase.open(L, input) - L.filename = filename +proc open(L: var SqlLexer, input: Stream, filename: string) = + lexbase.open(L, input) + L.filename = filename - proc open(p: var SqlParser, input: Stream, filename: string) = - ## opens the parser `p` and assigns the input stream `input` to it. - ## `filename` is only used for error messages. - open(SqlLexer(p), input, filename) - p.tok.kind = tkInvalid - p.tok.literal = "" - getTok(p) +proc open(p: var SqlParser, input: Stream, filename: string) = + ## opens the parser `p` and assigns the input stream `input` to it. + ## `filename` is only used for error messages. + open(SqlLexer(p), input, filename) + p.tok.kind = tkInvalid + p.tok.literal = "" + getTok(p) - proc parseSQL*(input: Stream, filename: string): SqlNode = - ## parses the SQL from `input` into an AST and returns the AST. - ## `filename` is only used for error messages. - ## Syntax errors raise an `SqlParseError` exception. - var p: SqlParser - open(p, input, filename) - try: - result = parse(p) - finally: - close(p) - - proc parseSQL*(input: string, filename = ""): SqlNode = - ## parses the SQL from `input` into an AST and returns the AST. - ## `filename` is only used for error messages. - ## Syntax errors raise an `SqlParseError` exception. - parseSQL(newStringStream(input), "") +proc parseSQL*(input: Stream, filename: string): SqlNode = + ## parses the SQL from `input` into an AST and returns the AST. + ## `filename` is only used for error messages. + ## Syntax errors raise an `SqlParseError` exception. + var p: SqlParser + open(p, input, filename) + try: + result = parse(p) + finally: + close(p) + +proc parseSQL*(input: string, filename = ""): SqlNode = + ## parses the SQL from `input` into an AST and returns the AST. + ## `filename` is only used for error messages. + ## Syntax errors raise an `SqlParseError` exception. + parseSQL(newStringStream(input), "") diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index f5023cb6cf9f8..41d6211b41484 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -287,46 +287,47 @@ proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. ## shortcut for ``add(c, frmt % args)``. add(c, frmt % args) -const - bufSize = 1024 # 1 KB is reasonable - -proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = - ## returns true if the contents of the file `f` equal `r`. - var - buf: array[bufSize, char] - bpos = buf.len - blen = buf.len - - for s in leaves(r): - var spos = 0 - let slen = s.len - while spos < slen: - if bpos == blen: - # Read more data - bpos = 0 - blen = readBuffer(f, addr(buf[0]), buf.len) - if blen == 0: # no more data in file +when not defined(js) and not defined(nimscript): + const + bufSize = 1024 # 1 KB is reasonable + + proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = + ## returns true if the contents of the file `f` equal `r`. + var + buf: array[bufSize, char] + bpos = buf.len + blen = buf.len + + for s in leaves(r): + var spos = 0 + let slen = s.len + while spos < slen: + if bpos == blen: + # Read more data + bpos = 0 + blen = readBuffer(f, addr(buf[0]), buf.len) + if blen == 0: # no more data in file + result = false + return + let n = min(blen - bpos, slen - spos) + # TODO There's gotta be a better way of comparing here... + if not equalMem(addr(buf[bpos]), + cast[pointer](cast[int](cstring(s))+spos), n): result = false return - let n = min(blen - bpos, slen - spos) - # TODO There's gotta be a better way of comparing here... - if not equalMem(addr(buf[bpos]), - cast[pointer](cast[int](cstring(s))+spos), n): - result = false - return - spos += n - bpos += n - - result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all - -proc equalsFile*(r: Rope, filename: string): bool {.rtl, extern: "nro$1Str".} = - ## returns true if the contents of the file `f` equal `r`. If `f` does not - ## exist, false is returned. - var f: File - result = open(f, filename) - if result: - result = equalsFile(r, f) - close(f) + spos += n + bpos += n + + result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all + + proc equalsFile*(r: Rope, filename: string): bool {.rtl, extern: "nro$1Str".} = + ## returns true if the contents of the file `f` equal `r`. If `f` does not + ## exist, false is returned. + var f: File + result = open(f, filename) + if result: + result = equalsFile(r, f) + close(f) new(N) # init dummy node for splay algorithm diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 4af4ae2ecd462..8efe5b227bfa5 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -216,6 +216,9 @@ proc getPosition*(s: Stream): int = proc readData*(s: Stream, buffer: pointer, bufLen: int): int = ## Low level proc that reads data into an untyped `buffer` of `bufLen` size. + ## + ## **JS note:** `buffer` is treated as a ``ptr string`` and written to between + ## ``0..= (1, 3) or not defined(js): proc readAll*(s: Stream): string = ## Reads all available data. - ## - ## **Note:** Not available for JS backend. runnableExamples: var strm = newStringStream("The first line\nthe second line\nthe third line") doAssert strm.readAll() == "The first line\nthe second line\nthe third line" @@ -253,7 +266,7 @@ when not defined(js): strm.close() const bufferSize = 1024 - when nimvm: + jsOrVmBlock: var buffer2: string buffer2.setLen(bufferSize) while true: @@ -265,7 +278,7 @@ when not defined(js): result[prevLen..`_ for now. + ## ## .. code-block:: Nim ## ## s.writeData(s, unsafeAddr(x), sizeof(x)) @@ -336,7 +358,12 @@ proc write*(s: Stream, x: string) = when nimvm: writeData(s, cstring(x), x.len) else: - if x.len > 0: writeData(s, cstring(x), x.len) + if x.len > 0: + when defined(js): + var x = x + writeData(s, addr(x), x.len) + else: + writeData(s, cstring(x), x.len) proc write*(s: Stream, args: varargs[string, `$`]) = ## Writes one or more strings to the the stream. No length fields or @@ -366,6 +393,8 @@ proc writeLine*(s: Stream, args: varargs[string, `$`]) = proc read*[T](s: Stream, result: var T) = ## Generic read procedure. Reads `result` from the stream `s`. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream("012") ## readInt @@ -383,6 +412,8 @@ proc read*[T](s: Stream, result: var T) = proc peek*[T](s: Stream, result: var T) = ## Generic peek procedure. Peeks `result` from the stream `s`. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream("012") ## peekInt @@ -412,11 +443,11 @@ proc readChar*(s: Stream): char = doAssert strm.readChar() == '\x00' strm.close() - when nimvm: + jsOrVmBlock: var str = " " - if readDataStr(s, str, 0..`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -461,6 +499,8 @@ proc peekBool*(s: Stream): bool = ## A bool is one byte long and it is `true` for every non-zero ## (`0000_0000`) value. ## Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -482,6 +522,8 @@ proc peekBool*(s: Stream): bool = proc readInt8*(s: Stream): int8 = ## Reads an int8 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -499,6 +541,8 @@ proc readInt8*(s: Stream): int8 = proc peekInt8*(s: Stream): int8 = ## Peeks an int8 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -518,6 +562,8 @@ proc peekInt8*(s: Stream): int8 = proc readInt16*(s: Stream): int16 = ## Reads an int16 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -535,6 +581,8 @@ proc readInt16*(s: Stream): int16 = proc peekInt16*(s: Stream): int16 = ## Peeks an int16 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -554,6 +602,8 @@ proc peekInt16*(s: Stream): int16 = proc readInt32*(s: Stream): int32 = ## Reads an int32 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -571,6 +621,8 @@ proc readInt32*(s: Stream): int32 = proc peekInt32*(s: Stream): int32 = ## Peeks an int32 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -590,6 +642,8 @@ proc peekInt32*(s: Stream): int32 = proc readInt64*(s: Stream): int64 = ## Reads an int64 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -607,6 +661,8 @@ proc readInt64*(s: Stream): int64 = proc peekInt64*(s: Stream): int64 = ## Peeks an int64 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -626,6 +682,8 @@ proc peekInt64*(s: Stream): int64 = proc readUint8*(s: Stream): uint8 = ## Reads an uint8 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -643,6 +701,8 @@ proc readUint8*(s: Stream): uint8 = proc peekUint8*(s: Stream): uint8 = ## Peeks an uint8 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -662,6 +722,8 @@ proc peekUint8*(s: Stream): uint8 = proc readUint16*(s: Stream): uint16 = ## Reads an uint16 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -679,6 +741,8 @@ proc readUint16*(s: Stream): uint16 = proc peekUint16*(s: Stream): uint16 = ## Peeks an uint16 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -698,6 +762,8 @@ proc peekUint16*(s: Stream): uint16 = proc readUint32*(s: Stream): uint32 = ## Reads an uint32 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -716,6 +782,8 @@ proc readUint32*(s: Stream): uint32 = proc peekUint32*(s: Stream): uint32 = ## Peeks an uint32 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -735,6 +803,8 @@ proc peekUint32*(s: Stream): uint32 = proc readUint64*(s: Stream): uint64 = ## Reads an uint64 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -752,6 +822,8 @@ proc readUint64*(s: Stream): uint64 = proc peekUint64*(s: Stream): uint64 = ## Peeks an uint64 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -771,6 +843,8 @@ proc peekUint64*(s: Stream): uint64 = proc readFloat32*(s: Stream): float32 = ## Reads a float32 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -788,6 +862,8 @@ proc readFloat32*(s: Stream): float32 = proc peekFloat32*(s: Stream): float32 = ## Peeks a float32 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -807,6 +883,8 @@ proc peekFloat32*(s: Stream): float32 = proc readFloat64*(s: Stream): float64 = ## Reads a float64 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `readStr <#readStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -824,6 +902,8 @@ proc readFloat64*(s: Stream): float64 = proc peekFloat64*(s: Stream): float64 = ## Peeks a float64 from the stream `s`. Raises `IOError` if an error occurred. + ## + ## **Note:** Not available for JS backend. Use `peekStr <#peekStr,Stream,int>`_ for now. runnableExamples: var strm = newStringStream() ## setup for reading data @@ -841,10 +921,19 @@ proc peekFloat64*(s: Stream): float64 = peek(s, result) +template untaint(s: var TaintedString): var string = + when taintMode: # for VM, bug #12282 + s.string + else: + s + proc readStrPrivate(s: Stream, length: int, str: var TaintedString) = - if length > len(str): setLen(str.string, length) - var L = readData(s, cstring(str), length) - if L != len(str): setLen(str.string, L) + if length > len(str): setLen(str.untaint, length) + when defined(js): + let L = readData(s, addr(str), length) + else: + let L = readData(s, cstring(str.string), length) + if L != len(str): setLen(str.untaint, L) proc readStr*(s: Stream, length: int, str: var TaintedString) {.since: (1, 3).} = ## Reads a string of length `length` from the stream `s`. Raises `IOError` if @@ -865,9 +954,12 @@ proc readStr*(s: Stream, length: int): TaintedString = readStrPrivate(s, length, result) proc peekStrPrivate(s: Stream, length: int, str: var TaintedString) = - if length > len(str): setLen(str.string, length) - var L = peekData(s, cstring(str), length) - if L != len(str): setLen(str.string, L) + if length > len(str): setLen(str.untaint, length) + when defined(js): + let L = peekData(s, addr(str), length) + else: + let L = peekData(s, cstring(str.string), length) + if L != len(str): setLen(str.untaint, L) proc peekStr*(s: Stream, length: int, str: var TaintedString) {.since: (1, 3).} = ## Peeks a string of length `length` from the stream `s`. Raises `IOError` if @@ -918,13 +1010,7 @@ proc readLine*(s: Stream, line: var TaintedString): bool = result = s.readLineImpl(s, line) else: # fallback - when nimvm: #Bug #12282 - when taintMode: - line.string.setLen(0) - else: - line.setLen(0) - else: - line.string.setLen(0) + line.untaint.setLen(0) while true: var c = readChar(s) if c == '\c': @@ -934,13 +1020,7 @@ proc readLine*(s: Stream, line: var TaintedString): bool = elif c == '\0': if line.len > 0: break else: return false - when nimvm: #Bug #12282 - when taintMode: - line.string.add(c) - else: - line.add(c) - else: - line.string.add(c) + line.untaint.add(c) result = true proc peekLine*(s: Stream, line: var TaintedString): bool = @@ -1002,7 +1082,7 @@ proc readLine*(s: Stream): TaintedString = if c == '\L' or c == '\0': break else: - result.string.add(c) + result.untaint.add(c) proc peekLine*(s: Stream): TaintedString = ## Peeks a line from a stream `s`. Raises `IOError` if an error occurred. @@ -1048,17 +1128,13 @@ iterator lines*(s: Stream): TaintedString = type StringStream* = ref StringStreamObj ## A stream that encapsulates a string. - ## - ## **Note:** Not available for JS backend. StringStreamObj* = object of StreamObj ## A string stream object. - ## - ## **Note:** Not available for JS backend. data*: string ## A string data. ## This is updated when called `writeLine` etc. pos: int -when defined(js): #This section exists so that string streams work at compile time for the js backend +when (NimMajor, NimMinor) < (1, 3) and defined(js): proc ssAtEnd(s: Stream): bool {.compileTime.} = var s = StringStream(s) return s.pos >= s.data.len @@ -1111,7 +1187,7 @@ when defined(js): #This section exists so that string streams work at compile ti if readBytes < bufferSize: break -else: +else: # after 1.3 or JS not defined proc ssAtEnd(s: Stream): bool = var s = StringStream(s) return s.pos >= s.data.len @@ -1128,9 +1204,9 @@ else: var s = StringStream(s) result = min(slice.b + 1 - slice.a, s.data.len - s.pos) if result > 0: - when nimvm: + jsOrVmBlock: buffer[slice.a.. 0: - copyMem(buffer, addr(s.data[s.pos]), result) + when defined(js): + try: + cast[ptr string](buffer)[][0.. 0: - copyMem(buffer, addr(s.data[s.pos]), result) + when defined(js): + try: + cast[ptr string](buffer)[][0.. s.data.len: setLen(s.data, s.pos + bufLen) - copyMem(addr(s.data[s.pos]), buffer, bufLen) + when defined(js): + try: + s.data[s.pos..`_ creates a file stream from ## opened File. @@ -1195,163 +1290,166 @@ else: result.atEndImpl = ssAtEnd result.setPositionImpl = ssSetPosition result.getPositionImpl = ssGetPosition - result.readDataImpl = ssReadData - result.peekDataImpl = ssPeekData - result.writeDataImpl = ssWriteData result.readDataStrImpl = ssReadDataStr + when nimvm: + discard + else: + result.readDataImpl = ssReadData + result.peekDataImpl = ssPeekData + result.writeDataImpl = ssWriteData - type - FileStream* = ref FileStreamObj - ## A stream that encapsulates a `File`. - ## - ## **Note:** Not available for JS backend. - FileStreamObj* = object of Stream - ## A file stream object. - ## - ## **Note:** Not available for JS backend. - f: File - - proc fsClose(s: Stream) = - if FileStream(s).f != nil: - close(FileStream(s).f) - FileStream(s).f = nil - proc fsFlush(s: Stream) = flushFile(FileStream(s).f) - proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f) - proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos) - proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f)) - - proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = - result = readBuffer(FileStream(s).f, buffer, bufLen) - - proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int = - result = readBuffer(FileStream(s).f, addr buffer[slice.a], slice.b + 1 - slice.a) - - proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int = - let pos = fsGetPosition(s) - defer: fsSetPosition(s, pos) - result = readBuffer(FileStream(s).f, buffer, bufLen) - - proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) = - if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen: - raise newEIO("cannot write to stream") - - proc fsReadLine(s: Stream, line: var TaintedString): bool = - result = readLine(FileStream(s).f, line) - - proc newFileStream*(f: File): owned FileStream = - ## Creates a new stream from the file `f`. +type + FileStream* = ref FileStreamObj + ## A stream that encapsulates a `File`. ## ## **Note:** Not available for JS backend. + FileStreamObj* = object of Stream + ## A file stream object. ## - ## See also: - ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream - ## from string. - ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same - ## as using `open proc `_ - ## on Examples. - ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a - ## file stream from the file name and the mode. - runnableExamples: - ## Input (somefile.txt): + ## **Note:** Not available for JS backend. + f: File + +proc fsClose(s: Stream) = + if FileStream(s).f != nil: + close(FileStream(s).f) + FileStream(s).f = nil +proc fsFlush(s: Stream) = flushFile(FileStream(s).f) +proc fsAtEnd(s: Stream): bool = return endOfFile(FileStream(s).f) +proc fsSetPosition(s: Stream, pos: int) = setFilePos(FileStream(s).f, pos) +proc fsGetPosition(s: Stream): int = return int(getFilePos(FileStream(s).f)) + +proc fsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = readBuffer(FileStream(s).f, buffer, bufLen) + +proc fsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int = + result = readBuffer(FileStream(s).f, addr buffer[slice.a], slice.b + 1 - slice.a) + +proc fsPeekData(s: Stream, buffer: pointer, bufLen: int): int = + let pos = fsGetPosition(s) + defer: fsSetPosition(s, pos) + result = readBuffer(FileStream(s).f, buffer, bufLen) + +proc fsWriteData(s: Stream, buffer: pointer, bufLen: int) = + if writeBuffer(FileStream(s).f, buffer, bufLen) != bufLen: + raise newEIO("cannot write to stream") + +proc fsReadLine(s: Stream, line: var TaintedString): bool = + result = readLine(FileStream(s).f, line) + +proc newFileStream*(f: File): owned FileStream = + ## Creates a new stream from the file `f`. + ## + ## **Note:** Not available for JS backend. + ## + ## See also: + ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream + ## from string. + ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same + ## as using `open proc `_ + ## on Examples. + ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a + ## file stream from the file name and the mode. + runnableExamples: + ## Input (somefile.txt): + ## The first line + ## the second line + ## the third line + var f: File + if open(f, "somefile.txt", fmRead, -1): + var strm = newFileStream(f) + var line = "" + while strm.readLine(line): + echo line + ## Output: ## The first line ## the second line ## the third line - var f: File - if open(f, "somefile.txt", fmRead, -1): - var strm = newFileStream(f) - var line = "" - while strm.readLine(line): - echo line - ## Output: - ## The first line - ## the second line - ## the third line - strm.close() + strm.close() - new(result) - result.f = f - result.closeImpl = fsClose - result.atEndImpl = fsAtEnd - result.setPositionImpl = fsSetPosition - result.getPositionImpl = fsGetPosition - result.readDataStrImpl = fsReadDataStr - result.readDataImpl = fsReadData - result.readLineImpl = fsReadLine - result.peekDataImpl = fsPeekData - result.writeDataImpl = fsWriteData - result.flushImpl = fsFlush - - proc newFileStream*(filename: string, mode: FileMode = fmRead, - bufSize: int = -1): owned FileStream = - ## Creates a new stream from the file named `filename` with the mode `mode`. - ## - ## If the file cannot be opened, `nil` is returned. See the `io module - ## `_ for a list of available `FileMode enums `_. - ## - ## **Note:** - ## * **This function returns nil in case of failure.** - ## To prevent unexpected behavior and ensure proper error handling, - ## use `openFileStream proc <#openFileStream,string,FileMode,int>`_ - ## instead. - ## * Not available for JS backend. - ## - ## See also: - ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream - ## from string. - ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from - ## opened File. - ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a - ## file stream from the file name and the mode. - runnableExamples: - from os import removeFile - var strm = newFileStream("somefile.txt", fmWrite) - if not isNil(strm): - strm.writeLine("The first line") - strm.writeLine("the second line") - strm.writeLine("the third line") - strm.close() - ## Output (somefile.txt) - ## The first line - ## the second line - ## the third line - removeFile("somefile.txt") + new(result) + result.f = f + result.closeImpl = fsClose + result.atEndImpl = fsAtEnd + result.setPositionImpl = fsSetPosition + result.getPositionImpl = fsGetPosition + result.readDataStrImpl = fsReadDataStr + result.readDataImpl = fsReadData + result.readLineImpl = fsReadLine + result.peekDataImpl = fsPeekData + result.writeDataImpl = fsWriteData + result.flushImpl = fsFlush + +proc newFileStream*(filename: string, mode: FileMode = fmRead, + bufSize: int = -1): owned FileStream = + ## Creates a new stream from the file named `filename` with the mode `mode`. + ## + ## If the file cannot be opened, `nil` is returned. See the `io module + ## `_ for a list of available `FileMode enums `_. + ## + ## **Note:** + ## * **This function returns nil in case of failure.** + ## To prevent unexpected behavior and ensure proper error handling, + ## use `openFileStream proc <#openFileStream,string,FileMode,int>`_ + ## instead. + ## * Not available for JS backend. + ## + ## See also: + ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream + ## from string. + ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from + ## opened File. + ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a + ## file stream from the file name and the mode. + runnableExamples: + from os import removeFile + var strm = newFileStream("somefile.txt", fmWrite) + if not isNil(strm): + strm.writeLine("The first line") + strm.writeLine("the second line") + strm.writeLine("the third line") + strm.close() + ## Output (somefile.txt) + ## The first line + ## the second line + ## the third line + removeFile("somefile.txt") - var f: File - if open(f, filename, mode, bufSize): result = newFileStream(f) + var f: File + if open(f, filename, mode, bufSize): result = newFileStream(f) - proc openFileStream*(filename: string, mode: FileMode = fmRead, - bufSize: int = -1): owned FileStream = - ## Creates a new stream from the file named `filename` with the mode `mode`. - ## If the file cannot be opened, an IO exception is raised. - ## - ## **Note:** Not available for JS backend. - ## - ## See also: - ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream - ## from string. - ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from - ## opened File. - ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ creates a - ## file stream from the file name and the mode. - runnableExamples: - try: - ## Input (somefile.txt): - ## The first line - ## the second line - ## the third line - var strm = openFileStream("somefile.txt") - echo strm.readLine() - ## Output: - ## The first line - strm.close() - except: - stderr.write getCurrentExceptionMsg() +proc openFileStream*(filename: string, mode: FileMode = fmRead, + bufSize: int = -1): owned FileStream = + ## Creates a new stream from the file named `filename` with the mode `mode`. + ## If the file cannot be opened, an IO exception is raised. + ## + ## **Note:** Not available for JS backend. + ## + ## See also: + ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream + ## from string. + ## * `newFileStream proc <#newFileStream,File>`_ creates a file stream from + ## opened File. + ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ creates a + ## file stream from the file name and the mode. + runnableExamples: + try: + ## Input (somefile.txt): + ## The first line + ## the second line + ## the third line + var strm = openFileStream("somefile.txt") + echo strm.readLine() + ## Output: + ## The first line + strm.close() + except: + stderr.write getCurrentExceptionMsg() - var f: File - if open(f, filename, mode, bufSize): - return newFileStream(f) - else: - raise newEIO("cannot open file stream: " & filename) + var f: File + if open(f, filename, mode, bufSize): + return newFileStream(f) + else: + raise newEIO("cannot open file stream: " & filename) when false: type diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index c6b44a3caebff..11aa851a11d80 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -52,7 +52,7 @@ runnableExamples: import hashes, strutils -when defined(js): +when defined(js) or defined(nimscript): {.pragma: rtlFunc.} else: {.pragma: rtlFunc, rtl.} @@ -298,7 +298,7 @@ proc raiseFormatException(s: string) = proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = if hasKey(t, key): return t.getOrDefault(key) # hm difficult: assume safety in taint mode here. XXX This is dangerous! - when defined(js): + when defined(js) or defined(nimscript): result = "" else: if useEnvironment in flags: result = os.getEnv(key).string diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index ee2c5fe22892e..eb478f60fa53b 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -251,7 +251,7 @@ method testEnded*(formatter: ConsoleOutputFormatter, testResult: TestResult) = template rawPrint() = echo(prefix, "[", $testResult.status, "] ", testResult.testName) when not defined(ECMAScript): - if formatter.colorOutput and not defined(ECMAScript): + if formatter.colorOutput: var color = case testResult.status of TestStatus.OK: fgGreen of TestStatus.FAILED: fgRed diff --git a/lib/pure/volatile.nim b/lib/pure/volatile.nim index b3705a19994d2..208f0fcaaac51 100644 --- a/lib/pure/volatile.nim +++ b/lib/pure/volatile.nim @@ -13,18 +13,24 @@ template volatileLoad*[T](src: ptr T): T = ## Generates a volatile load of the value stored in the container `src`. ## Note that this only effects code generation on `C` like backends - when defined(js): + when nimvm: src[] else: - var res: T - {.emit: [res, " = (*(", type(src[]), " volatile*)", src, ");"].} - res + when defined(js): + src[] + else: + var res: T + {.emit: [res, " = (*(", type(src[]), " volatile*)", src, ");"].} + res template volatileStore*[T](dest: ptr T, val: T) = ## Generates a volatile store into the container `dest` of the value ## `val`. Note that this only effects code generation on `C` like ## backends - when defined(js): + when nimvm: dest[] = val else: - {.emit: ["*((", type(dest[]), " volatile*)(", dest, ")) = ", val, ";"].} + when defined(js): + dest[] = val + else: + {.emit: ["*((", type(dest[]), " volatile*)(", dest, ")) = ", val, ";"].} diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index ec55733e1e160..b16f2cbbced97 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -import system/indexerrors +include system/indexerrors proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.} diff --git a/tests/js/tstdlib_imports.nim b/tests/js/tstdlib_imports.nim new file mode 100644 index 0000000000000..7611b7dcb9993 --- /dev/null +++ b/tests/js/tstdlib_imports.nim @@ -0,0 +1,71 @@ +discard """ + action: compile +""" + +{.warning[UnusedImport]: off.} + +import std/[ + # Core: + bitops, typetraits, lenientops, macros, volatile, typeinfo, + # fails: endians, rlocks + # works but shouldn't: cpuinfo, locks + + # Algorithms: + algorithm, sequtils, + + # Collections: + critbits, deques, heapqueue, intsets, lists, options, sets, + sharedlist, tables, + # fails: sharedtables + + # Strings: + cstrutils, editdistance, wordwrap, parseutils, ropes, + pegs, punycode, strformat, strmisc, strscans, strtabs, + strutils, unicode, unidecode, + # fails: encodings + + # Time handling: + monotimes, times, + + # Generic operator system services: + os, streams, + # fails: distros, dynlib, marshal, memfiles, osproc, terminal + + # Math libraries: + complex, math, mersenne, random, rationals, stats, sums, + # works but shouldn't: fenv + + # Internet protocols: + cookies, httpcore, mimetypes, uri, + # fails: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, + # asyncnet, cgi, httpclient, nativesockets, net, selectors, smtp + # works but shouldn't test: asyncstreams, asyncfutures + + # Threading: + # fails: threadpool + + # Parsers: + htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, + # fails: parseopt + + # XML processing: + xmltree, xmlparser, + + # Generators: + htmlgen, + + # Hashing: + base64, hashes, + # fails: md5, oids, sha1 + + # Miscellaneous: + colors, logging, sugar, unittest, varints, + # fails: browsers, coro + # works but shouldn't: segfaults + + # Modules for JS backend: + asyncjs, dom, jsconsole, jscore, jsffi, + + # Unlisted in lib.html: + decls, compilesettings, with, wrapnils +] diff --git a/tests/js/tstdlib_various.nim b/tests/js/tstdlib_various.nim new file mode 100644 index 0000000000000..d19f40c39cbeb --- /dev/null +++ b/tests/js/tstdlib_various.nim @@ -0,0 +1,194 @@ +discard """ +output: ''' +abc +def +definition +prefix +xyz +def +definition +Hi Andreas! How do you feel, Rumpf? + +@[0, 2, 1] +@[1, 0, 2] +@[1, 2, 0] +@[2, 0, 1] +@[2, 1, 0] +@[2, 0, 1] +@[1, 2, 0] +@[1, 0, 2] +@[0, 2, 1] +@[0, 1, 2] +[5] +[4, 5] +[3, 4, 5] +[2, 3, 4, 5] +[2, 3, 4, 5, 6] +[1, 2, 3, 4, 5, 6] +''' +""" + +import + critbits, cstrutils, sets, strutils, tables, random, algorithm, ropes, + lists, htmlgen, xmltree, strtabs + + +block tcritbits: + var r: CritBitTree[void] + r.incl "abc" + r.incl "xyz" + r.incl "def" + r.incl "definition" + r.incl "prefix" + doAssert r.contains"def" + #r.del "def" + + for w in r.items: + echo w + for w in r.itemsWithPrefix("de"): + echo w + + + +block testequivalence: + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "equivalent or subset") + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3]), "equivalent or subset") + doAssert((not(toHashSet(@[1,2,3]) <= toHashSet(@[1,2]))), "equivalent or subset") + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2,3]))), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2]))), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3,4]))), "==") + doAssert(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3]), "==") + doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2]))), "==") + + + +block tformat: + echo("Hi $1! How do you feel, $2?\n" % ["Andreas", "Rumpf"]) + + + +block tnilecho: + var x = @["1", "", "3"] + doAssert $x == """@["1", "", "3"]""" + + + +block torderedtable: + var t = initOrderedTable[int,string]() + + # this tests issue #5917 + var data = newSeq[int]() + for i in 0..<1000: + var x = rand(1000) + if x notin t: data.add(x) + t[x] = "meh" + + # this checks that keys are re-inserted + # in order when table is enlarged. + var i = 0 + for k, v in t: + doAssert(k == data[i]) + doAssert(v == "meh") + inc(i) + + + +block tpermutations: + var v = @[0, 1, 2] + while v.nextPermutation(): + echo v + while v.prevPermutation(): + echo v + + +block tropes: + var + r1 = rope("") + r2 = rope("123") + doAssert r1.len == 0 + doAssert r2.len == 3 + doAssert $r1 == "" + doAssert $r2 == "123" + + r1.add("123") + r2.add("456") + doAssert r1.len == 3 + doAssert r2.len == 6 + doAssert $r1 == "123" + doAssert $r2 == "123456" + doAssert $r1[1] == "2" + doAssert $r2[2] == "3" + + +block tsinglylinkedring: + var r = initSinglyLinkedRing[int]() + r.prepend(5) + echo r + r.prepend(4) + echo r + r.prepend(3) + echo r + r.prepend(2) + echo r + r.append(6) + echo r + r.prepend(1) + echo r + +block tsplit: + var s = "" + for w in split("|abc|xy|z", {'|'}): + s.add("#") + s.add(w) + + doAssert s == "##abc#xy#z" + +block tsplit2: + var s = "" + for w in split("|abc|xy|z", {'|'}): + s.add("#") + s.add(w) + + var errored = false + try: + discard "hello".split("") + except AssertionError: + errored = true + doAssert errored + +block txmlgen: + var nim = "Nim" + doAssert h1(a(href="http://force7.de/nim", nim)) == + "

Nim

" + +block txmltree: + var x = <>a(href="nim.de", newText("www.nim-test.de")) + + doAssert($x == "www.nim-test.de") + doAssert(newText("foo").innerText == "foo") + doAssert(newEntity("bar").innerText == "bar") + doAssert(newComment("baz").innerText == "") + + let y = newXmlTree("x", [ + newText("foo"), + newXmlTree("y", [ + newText("bar") + ]) + ]) + doAssert(y.innerText == "foobar") + + + +block tcstrutils: + let s = cstring "abcdef" + doAssert s.startsWith("a") + doAssert not s.startsWith("b") + doAssert s.endsWith("f") + doAssert not s.endsWith("a") + + let a = cstring "abracadabra" + doAssert a.startsWith("abra") + doAssert not a.startsWith("bra") + doAssert a.endsWith("abra") + doAssert not a.endsWith("dab") diff --git a/tests/js/tstreams.nim b/tests/js/tstreams.nim new file mode 100644 index 0000000000000..43c26e01a1399 --- /dev/null +++ b/tests/js/tstreams.nim @@ -0,0 +1,22 @@ +discard """ + output: ''' +I +AM +GROOT +''' +""" + +import streams + +var s = newStringStream("I\nAM\nGROOT") +doAssert s.peekStr(1) == "I" +doAssert s.peekChar() == 'I' +for line in s.lines: + echo line +s.close + +var s2 = newStringStream("abc") +doAssert s2.readAll == "abc" +s2.write("def") +doAssert s2.data == "abcdef" +s2.close diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim index ccf2f62026657..e7dcd50d5b10f 100644 --- a/tests/stdlib/thtmlparser.nim +++ b/tests/stdlib/thtmlparser.nim @@ -1,6 +1,6 @@ discard """ + targets: "c js" output: ''' -@[] true https://example.com/test?format=jpg&name=orig## https://example.com/test?format=jpg&name=orig##text @@ -40,7 +40,7 @@ block t2813: """ var errors: seq[string] = @[] let tree = parseHtml(newStringStream(html), "test.html", errors) - echo errors # Errors: expected,... + doAssert errors.len == 0 # Errors: expected,... var len = tree.findAll("tr").len # len = 6 var rows: seq[XmlNode] = @[] diff --git a/tests/stdlib/tparscfg.nim b/tests/stdlib/tparscfg.nim index fc735f3eb2e69..ddb9b02b71212 100644 --- a/tests/stdlib/tparscfg.nim +++ b/tests/stdlib/tparscfg.nim @@ -1,29 +1,5 @@ discard """ -output: ''' -utf-8 -on -hello -lihf8515 -10214028 -lihaifeng@wxm.com -=== -charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lhf -qq=10214028 -email="lihaifeng@wxm.com" -=== -charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lihf8515 -qq=10214028 -''' + targets: "c js" """ import parsecfg, streams @@ -46,24 +22,35 @@ var pname = dict2.getSectionValue("Package","name") var name = dict2.getSectionValue("Author","name") var qq = dict2.getSectionValue("Author","qq") var email = dict2.getSectionValue("Author","email") -echo charset -echo threads -echo pname -echo name -echo qq -echo email - -echo "===" +doAssert charset == "utf-8" +doAssert threads == "on" +doAssert pname == "hello" +doAssert name == "lihf8515" +doAssert qq == "10214028" +doAssert email == "lihaifeng@wxm.com" ## Modifying a configuration file. var dict3 = loadConfig(newStringStream(ss.data)) dict3.setSectionKey("Author","name","lhf") -write(stdout, $dict3) - -echo "===" +doAssert $dict3 == """charset=utf-8 +[Package] +name=hello +--threads:on +[Author] +name=lhf +qq=10214028 +email="lihaifeng@wxm.com" +""" ## Deleting a section key in a configuration file. var dict4 = loadConfig(newStringStream(ss.data)) dict4.delSectionKey("Author","email") -write(stdout, $dict4) +doAssert $dict4 == """charset=utf-8 +[Package] +name=hello +--threads:on +[Author] +name=lihf8515 +qq=10214028 +""" diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim index 8cf8fa848e0db..ba9e601a1c239 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -1,3 +1,6 @@ +discard """ + targets: "c js" +""" import parsesql doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;" diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index b07442dcb46a2..2990a44a5c7ea 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -1,4 +1,5 @@ discard """ + targets: "c js" output: ''' PEG AST traversal output ------------------------ diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim index d1723df786547..6856fcd792a86 100644 --- a/tests/stdlib/tstdlib_various.nim +++ b/tests/stdlib/tstdlib_various.nim @@ -38,8 +38,8 @@ true """ import - critbits, sets, strutils, tables, random, algorithm, re, ropes, segfaults, - lists, parsesql, streams, os, htmlgen, xmltree, strtabs + critbits, cstrutils, sets, strutils, tables, random, algorithm, re, ropes, + segfaults, lists, parsesql, streams, os, htmlgen, xmltree, strtabs block tcritbits: @@ -245,3 +245,24 @@ block txmltree: ]) ]) doAssert(y.innerText == "foobar") + + +block tcstrutils: + let s = cstring "abcdef" + doAssert s.startsWith("a") + doAssert not s.startsWith("b") + doAssert s.endsWith("f") + doAssert not s.endsWith("a") + + let a = cstring "abracadabra" + doAssert a.startsWith("abra") + doAssert not a.startsWith("bra") + doAssert a.endsWith("abra") + doAssert not a.endsWith("dab") + + doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0 + doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0 + doAssert cmpIgnoreCase(cstring "Foo5", "foo4") > 0 + + doAssert cmpIgnoreStyle(cstring "foo_bar", "FooBar") == 0 + doAssert cmpIgnoreStyle(cstring "foo_bar_5", "FooBar4") > 0 diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 91b23efbfb2f7..0b454ad1cc279 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -1,26 +1,74 @@ # This nimscript is used to test if the following modules can be imported # http://nim-lang.org/docs/nims.html -import algorithm -import base64 -import colors -import hashes -import lists -import math -# import marshal -import options -import os -# import parsecfg -# import parseopt -import parseutils -# import pegs -import deques -import sequtils -import strutils -import tables -import unicode -import uri -import macros +{.warning[UnusedImport]: off.} + +import std/[ + # Core: + bitops, typetraits, lenientops, macros, volatile, + # fails: typeinfo, endians + # works but shouldn't: cpuinfo, rlocks, locks + + # Algorithms: + algorithm, sequtils, + + # Collections: + critbits, deques, heapqueue, intsets, lists, options, sets, + sharedlist, tables, + # fails: sharedtables + + # Strings: + editdistance, wordwrap, parseutils, ropes, + pegs, punycode, strformat, strmisc, strscans, strtabs, + strutils, unicode, unidecode, + # works but shouldn't: cstrutils, encodings + + # Time handling: + # fails: monotimes, times + # but times.getTime() implemented for VM + + # Generic operator system services: + os, streams, + # fails: distros, dynlib, marshal, memfiles, osproc, terminal + + # Math libraries: + complex, math, mersenne, random, rationals, stats, sums, + # works but shouldn't: fenv + + # Internet protocols: + httpcore, mimetypes, uri, + # fails: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, + # asyncnet, cgi, cookies, httpclient, nativesockets, net, selectors, smtp + # works but shouldn't test: asyncstreams, asyncfutures + + # Threading: + # fails: threadpool + + # Parsers: + htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, + # fails: parseopt + + # XML processing: + xmltree, xmlparser, + + # Generators: + htmlgen, + + # Hashing: + base64, hashes, + # fails: md5, oids, sha1 + + # Miscellaneous: + colors, sugar, varints, + # fails: browsers, coro, logging (times), segfaults, unittest (uses methods) + + # Modules for JS backend: + # fails: asyncjs, dom, jsconsole, jscore, jsffi, + + # Unlisted in lib.html: + decls, compilesettings, with, wrapnils +] + block: doAssert "./foo//./bar/".normalizedPath == "foo/bar".unixToNativePath