StringStream & more stdlib modules support for JS/NimScript (#14095)

* StringStream & more stdlib modules support for JS/NimScript

* change back pegs test in line with #14134
This commit is contained in:
hlaaftana
2020-04-28 20:44:52 +03:00
committed by GitHub
parent d5ed4fba3e
commit cd9af6b804
22 changed files with 852 additions and 356 deletions

View File

@@ -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).

View File

@@ -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 <strutils.html>`_, `math <math.html>`_, and
`times <times.html>`_ are available! To access the DOM, use the `dom
<dom.html>`_ module that is only available for the JavaScript platform.
To compensate, the standard library has modules `catered to the JS backend
<https://nim-lang.org/docs/lib.html#pure-libraries-modules-for-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``

View File

@@ -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
========================

View File

@@ -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
## <https://nim-lang.org/docs/strutils.html#cmpIgnoreStyle%2Cstring%2Cstring>`_ 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
## <https://nim-lang.org/docs/strutils.html#cmpIgnoreCase%2Cstring%2Cstring>`_ instead.
var i = 0
while true:
var aa = toLowerAscii(a[i])

View File

@@ -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:

View File

@@ -50,8 +50,14 @@
## * `streams module <streams.html>`_
## * `json module <json.html>`_
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.""".}

View File

@@ -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: 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: 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), "")

View File

@@ -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
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
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
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
spos += n
bpos += n
result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all
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)
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

View File

@@ -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..<bufLen``.
runnableExamples:
var strm = newStringStream("abcde")
var buffer: array[6, char]
@@ -241,11 +244,21 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int =
# fallback
result = s.readData(addr buffer[0], buffer.len)
when not defined(js):
template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped =
when nimvm:
block:
caseJsOrVm
else:
block:
when defined(js) or defined(nimscript):
# nimscript has to be here to avoid semantic checking of caseElse
caseJsOrVm
else:
caseElse
when (NimMajor, NimMinor) >= (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..<prevLen+readBytes] = buffer2[0..<readBytes]
if readBytes < bufferSize:
break
else:
do: # not JS or VM
var buffer {.noinit.}: array[bufferSize, char]
while true:
let readBytes = readData(s, addr(buffer[0]), bufferSize)
@@ -280,6 +293,9 @@ when not defined(js):
proc peekData*(s: Stream, buffer: pointer, bufLen: int): int =
## Low level proc that reads data into an untyped `buffer` of `bufLen` size
## without moving stream position.
##
## **JS note:** `buffer` is treated as a ``ptr string`` and written to between
## ``0..<bufLen``.
runnableExamples:
var strm = newStringStream("abcde")
var buffer: array[6, char]
@@ -293,6 +309,9 @@ proc peekData*(s: Stream, buffer: pointer, bufLen: int): int =
proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
## Low level proc that writes an untyped `buffer` of `bufLen` size
## to the stream `s`.
##
## **JS note:** `buffer` is treated as a ``ptr string`` and read between
## ``0..<bufLen``.
runnableExamples:
## writeData
var strm = newStringStream("")
@@ -311,6 +330,9 @@ proc writeData*(s: Stream, buffer: pointer, bufLen: int) =
proc write*[T](s: Stream, x: T) =
## Generic write procedure. Writes `x` to the stream `s`. Implementation:
##
## **Note:** Not available for JS backend. Use `write(Stream, string)
## <#write,Stream,string>`_ 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..<sizeof(result)) != 1: result = '\0'
if readDataStr(s, str, 0..0) != 1: result = '\0'
else: result = str[0]
else:
do:
if readData(s, addr(result), sizeof(result)) != 1: result = '\0'
proc peekChar*(s: Stream): char =
@@ -430,7 +461,12 @@ proc peekChar*(s: Stream): char =
doAssert strm.peekChar() == '\x00'
strm.close()
if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
when defined(js):
var str = " "
if peekData(s, addr(str), sizeof(result)) != 1: result = '\0'
else: result = str[0]
else:
if peekData(s, addr(result), sizeof(result)) != 1: result = '\0'
proc readBool*(s: Stream): bool =
## Reads a bool from the stream `s`.
@@ -438,6 +474,8 @@ proc readBool*(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 `readStr <#readStr,Stream,int>`_ 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..<slice.a+result] = s.data[s.pos..<s.pos+result]
else:
do:
copyMem(unsafeAddr buffer[slice.a], addr s.data[s.pos], result)
inc(s.pos, result)
else:
@@ -1140,7 +1216,14 @@ else:
var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos)
if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result)
when defined(js):
try:
cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
except:
raise newException(Defect, "could not read string stream, " &
"did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript):
copyMem(buffer, addr(s.data[s.pos]), result)
inc(s.pos, result)
else:
result = 0
@@ -1149,7 +1232,14 @@ else:
var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos)
if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result)
when defined(js):
try:
cast[ptr string](buffer)[][0..<result] = s.data[s.pos..<s.pos+result]
except:
raise newException(Defect, "could not peek string stream, " &
"did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript):
copyMem(buffer, addr(s.data[s.pos]), result)
else:
result = 0
@@ -1159,7 +1249,14 @@ else:
return
if s.pos + bufLen > 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..<s.pos+bufLen] = cast[ptr string](buffer)[][0..<bufLen]
except:
raise newException(Defect, "could not write to string stream, " &
"did you use a non-string buffer pointer?", getCurrentException())
elif not defined(nimscript):
copyMem(addr(s.data[s.pos]), buffer, bufLen)
inc(s.pos, bufLen)
proc ssClose(s: Stream) =
@@ -1172,8 +1269,6 @@ else:
proc newStringStream*(s: string = ""): owned StringStream =
## Creates a new stream from the string `s`.
##
## **Note:** Not available for JS backend.
##
## See also:
## * `newFileStream proc <#newFileStream,File>`_ 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 <io.html#open,File,string,FileMode,int>`_
## on Examples.
## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a
## file stream from the file name and the mode.
runnableExamples:
## **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 <io.html#open,File,string,FileMode,int>`_
## 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
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
## <io.html>`_ for a list of available `FileMode enums <io.html#FileMode>`_.
##
## **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)
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 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()
var strm = openFileStream("somefile.txt")
echo strm.readLine()
## Output:
## The first line
strm.close()
except:
stderr.write getCurrentExceptionMsg()
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
## <io.html>`_ for a list of available `FileMode enums <io.html#FileMode>`_.
##
## **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)
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

View File

@@ -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

View File

@@ -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

View File

@@ -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, ";"].}

View File

@@ -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.}

View File

@@ -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
]

View File

@@ -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)) ==
"<h1><a href=\"http://force7.de/nim\">Nim</a></h1>"
block txmltree:
var x = <>a(href="nim.de", newText("www.nim-test.de"))
doAssert($x == "<a href=\"nim.de\">www.nim-test.de</a>")
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")

22
tests/js/tstreams.nim Normal file
View File

@@ -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

View File

@@ -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: </thead> expected,...
doAssert errors.len == 0 # Errors: </thead> expected,...
var len = tree.findAll("tr").len # len = 6
var rows: seq[XmlNode] = @[]

View File

@@ -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
"""

View File

@@ -1,3 +1,6 @@
discard """
targets: "c js"
"""
import parsesql
doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;"

View File

@@ -1,4 +1,5 @@
discard """
targets: "c js"
output: '''
PEG AST traversal output
------------------------

View File

@@ -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

View File

@@ -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