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
commit cd9af6b804
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 852 additions and 356 deletions

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