inlining of the write barrier for dlls
This commit is contained in:
parent
c9e011e36c
commit
8098e2a421
25 changed files with 856 additions and 850 deletions
|
|
@ -169,16 +169,6 @@ in mind:
|
||||||
without the ``-w`` option helps!
|
without the ``-w`` option helps!
|
||||||
|
|
||||||
|
|
||||||
Generation of dynamic link libraries
|
|
||||||
====================================
|
|
||||||
|
|
||||||
Generation of dynamic link libraries or shared libraries is not difficult; the
|
|
||||||
underlying C compiler already does all the hard work for us. The problem is the
|
|
||||||
common runtime library, especially the memory manager. Note that Borland's
|
|
||||||
Delphi had exactly the same problem. The workaround is to not link the GC with
|
|
||||||
the Dll and provide an extra runtime dll that needs to be initialized.
|
|
||||||
|
|
||||||
|
|
||||||
The Garbage Collector
|
The Garbage Collector
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,16 +70,21 @@ However, the generated C code is not platform independent. C code generated for
|
||||||
Linux does not compile on Windows, for instance. The comment on top of the
|
Linux does not compile on Windows, for instance. The comment on top of the
|
||||||
C file lists the OS, CPU and CC the file has been compiled for.
|
C file lists the OS, CPU and CC the file has been compiled for.
|
||||||
|
|
||||||
..
|
|
||||||
DLL generation
|
|
||||||
==============
|
|
||||||
|
|
||||||
Nimrod supports the generation of DLLs. However, there must be only one
|
DLL generation
|
||||||
instance of the GC per process/address space. This instance is contained in
|
==============
|
||||||
``nimrtl.dll``. This means that every generated Nimrod DLL depends
|
|
||||||
on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command::
|
Nimrod supports the generation of DLLs. However, there must be only one
|
||||||
|
instance of the GC per process/address space. This instance is contained in
|
||||||
|
``nimrtl.dll``. This means that every generated Nimrod DLL depends
|
||||||
|
on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command::
|
||||||
|
|
||||||
|
nimrod c -d:release lib/nimrtl.nim
|
||||||
|
|
||||||
|
To link to ``nimrtl.dll`` use the command::
|
||||||
|
|
||||||
|
nimrod c -d:useNimRtl myprog.nim
|
||||||
|
|
||||||
nimrod c -d:release lib/nimrtl.nim
|
|
||||||
|
|
||||||
|
|
||||||
Additional Features
|
Additional Features
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ macro meaning
|
||||||
``[^ \9-\13]``
|
``[^ \9-\13]``
|
||||||
``\w`` any "word" character: ``[a-zA-Z0-9_]``
|
``\w`` any "word" character: ``[a-zA-Z0-9_]``
|
||||||
``\W`` any "non-word" character: ``[^a-zA-Z0-9_]``
|
``\W`` any "non-word" character: ``[^a-zA-Z0-9_]``
|
||||||
|
``\a`` same as ``[a-zA-Z]``
|
||||||
|
``\A`` same as ``[^a-zA-Z]``
|
||||||
``\n`` any newline combination: ``\10 / \13\10 / \13``
|
``\n`` any newline combination: ``\10 / \13\10 / \13``
|
||||||
``\i`` ignore case for matching; use this at the start of the PEG
|
``\i`` ignore case for matching; use this at the start of the PEG
|
||||||
``\y`` ignore style for matching; use this at the start of the PEG
|
``\y`` ignore style for matching; use this at the start of the PEG
|
||||||
|
|
|
||||||
|
|
@ -11,23 +11,18 @@
|
||||||
## The default Nimrtl does not only contain the ``system`` module, but these
|
## The default Nimrtl does not only contain the ``system`` module, but these
|
||||||
## too:
|
## too:
|
||||||
##
|
##
|
||||||
## * strutils
|
|
||||||
## * parseutils
|
## * parseutils
|
||||||
|
## * strutils
|
||||||
## * parseopt
|
## * parseopt
|
||||||
## * parsecfg
|
## * parsecfg
|
||||||
## * strtabs
|
## * strtabs
|
||||||
## * times
|
## * times
|
||||||
## * os
|
## * os
|
||||||
## * osproc
|
## * osproc
|
||||||
## * pegs
|
|
||||||
## * unicode
|
## * unicode
|
||||||
|
## * pegs
|
||||||
## * ropes
|
## * ropes
|
||||||
## * re
|
|
||||||
##
|
##
|
||||||
## So the resulting dynamic library is quite big. However, it is very easy to
|
|
||||||
## strip modules out. Just modify the ``import`` statement in
|
|
||||||
## ``lib/nimrtl.nim`` and recompile. Note that simply *adding* a module
|
|
||||||
## here is not sufficient, though.
|
|
||||||
|
|
||||||
when system.appType != "lib":
|
when system.appType != "lib":
|
||||||
{.error: "This file has to be compiled as a library!".}
|
{.error: "This file has to be compiled as a library!".}
|
||||||
|
|
@ -35,5 +30,7 @@ when system.appType != "lib":
|
||||||
when not defined(createNimRtl):
|
when not defined(createNimRtl):
|
||||||
{.error: "This file has to be compiled with '-d:createNimRtl'".}
|
{.error: "This file has to be compiled with '-d:createNimRtl'".}
|
||||||
|
|
||||||
|
import
|
||||||
|
parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes,
|
||||||
|
os, osproc, times
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,8 @@ proc FillBuffer(L: var TBaseLexer) =
|
||||||
toCopy = L.BufLen - L.sentinel - 1
|
toCopy = L.BufLen - L.sentinel - 1
|
||||||
assert(toCopy >= 0)
|
assert(toCopy >= 0)
|
||||||
if toCopy > 0:
|
if toCopy > 0:
|
||||||
MoveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) # "moveMem" handles overlapping regions
|
MoveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||||
|
# "moveMem" handles overlapping regions
|
||||||
charsRead = L.input.readData(L.input, addr(L.buf[toCopy]),
|
charsRead = L.input.readData(L.input, addr(L.buf[toCopy]),
|
||||||
(L.sentinel + 1) * chrSize) div chrSize
|
(L.sentinel + 1) * chrSize) div chrSize
|
||||||
s = toCopy + charsRead
|
s = toCopy + charsRead
|
||||||
|
|
|
||||||
136
lib/pure/os.nim
136
lib/pure/os.nim
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
{.push debugger: off.}
|
{.push debugger: off.}
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, times
|
strutils, times
|
||||||
|
|
||||||
|
|
@ -145,19 +147,7 @@ const
|
||||||
## The character which separates the base filename from the extension;
|
## The character which separates the base filename from the extension;
|
||||||
## for example, the '.' in ``os.nim``.
|
## for example, the '.' in ``os.nim``.
|
||||||
|
|
||||||
# procs dealing with command line arguments:
|
proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
|
||||||
proc paramCount*(): int
|
|
||||||
## Returns the number of command line arguments given to the
|
|
||||||
## application.
|
|
||||||
|
|
||||||
proc paramStr*(i: int): string
|
|
||||||
## Returns the `i`-th command line arguments given to the
|
|
||||||
## application.
|
|
||||||
##
|
|
||||||
## `i` should be in the range `1..paramCount()`, else
|
|
||||||
## the `EOutOfIndex` exception is raised.
|
|
||||||
|
|
||||||
proc OSError*(msg: string = "") {.noinline.} =
|
|
||||||
## raises an EOS exception with the given message ``msg``.
|
## raises an EOS exception with the given message ``msg``.
|
||||||
## If ``msg == ""``, the operating system's error flag
|
## If ``msg == ""``, the operating system's error flag
|
||||||
## (``errno``) is converted to a readable error message. On Windows
|
## (``errno``) is converted to a readable error message. On Windows
|
||||||
|
|
@ -182,7 +172,8 @@ proc OSError*(msg: string = "") {.noinline.} =
|
||||||
else:
|
else:
|
||||||
raise newException(EOS, msg)
|
raise newException(EOS, msg)
|
||||||
|
|
||||||
proc UnixToNativePath*(path: string): string {.noSideEffect.} =
|
proc UnixToNativePath*(path: string): string {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Converts an UNIX-like path to a native one.
|
## Converts an UNIX-like path to a native one.
|
||||||
##
|
##
|
||||||
## On an UNIX system this does nothing. Else it converts
|
## On an UNIX system this does nothing. Else it converts
|
||||||
|
|
@ -227,7 +218,7 @@ proc UnixToNativePath*(path: string): string {.noSideEffect.} =
|
||||||
add result, path[i]
|
add result, path[i]
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc existsFile*(filename: string): bool =
|
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1".} =
|
||||||
## Returns true if the file exists, false otherwise.
|
## Returns true if the file exists, false otherwise.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var a = GetFileAttributesA(filename)
|
var a = GetFileAttributesA(filename)
|
||||||
|
|
@ -237,7 +228,7 @@ proc existsFile*(filename: string): bool =
|
||||||
var res: TStat
|
var res: TStat
|
||||||
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
|
||||||
|
|
||||||
proc existsDir*(dir: string): bool =
|
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1".} =
|
||||||
## Returns true iff the directory `dir` exists. If `dir` is a file, false
|
## Returns true iff the directory `dir` exists. If `dir` is a file, false
|
||||||
## is returned.
|
## is returned.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -248,7 +239,7 @@ proc existsDir*(dir: string): bool =
|
||||||
var res: TStat
|
var res: TStat
|
||||||
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
||||||
|
|
||||||
proc getLastModificationTime*(file: string): TTime =
|
proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||||
## Returns the `file`'s last modification time.
|
## Returns the `file`'s last modification time.
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
var res: TStat
|
var res: TStat
|
||||||
|
|
@ -261,7 +252,7 @@ proc getLastModificationTime*(file: string): TTime =
|
||||||
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
|
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
|
||||||
findclose(h)
|
findclose(h)
|
||||||
|
|
||||||
proc getLastAccessTime*(file: string): TTime =
|
proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||||
## Returns the `file`'s last read or write access time.
|
## Returns the `file`'s last read or write access time.
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
var res: TStat
|
var res: TStat
|
||||||
|
|
@ -274,7 +265,7 @@ proc getLastAccessTime*(file: string): TTime =
|
||||||
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
|
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
|
||||||
findclose(h)
|
findclose(h)
|
||||||
|
|
||||||
proc getCreationTime*(file: string): TTime =
|
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||||
## Returns the `file`'s creation time.
|
## Returns the `file`'s creation time.
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
var res: TStat
|
var res: TStat
|
||||||
|
|
@ -287,12 +278,12 @@ proc getCreationTime*(file: string): TTime =
|
||||||
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
|
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
|
||||||
findclose(h)
|
findclose(h)
|
||||||
|
|
||||||
proc fileNewer*(a, b: string): bool =
|
proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
|
||||||
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
|
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
|
||||||
## modification time is later than `b`'s.
|
## modification time is later than `b`'s.
|
||||||
result = getLastModificationTime(a) - getLastModificationTime(b) > 0
|
result = getLastModificationTime(a) - getLastModificationTime(b) > 0
|
||||||
|
|
||||||
proc getCurrentDir*(): string =
|
proc getCurrentDir*(): string {.rtl, extern: "nos$1".} =
|
||||||
## Returns the current working directory.
|
## Returns the current working directory.
|
||||||
const bufsize = 512 # should be enough
|
const bufsize = 512 # should be enough
|
||||||
result = newString(bufsize)
|
result = newString(bufsize)
|
||||||
|
|
@ -314,7 +305,8 @@ proc setCurrentDir*(newDir: string) {.inline.} =
|
||||||
else:
|
else:
|
||||||
if chdir(newDir) != 0'i32: OSError()
|
if chdir(newDir) != 0'i32: OSError()
|
||||||
|
|
||||||
proc JoinPath*(head, tail: string): string {.noSideEffect.} =
|
proc JoinPath*(head, tail: string): string {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Joins two directory names to one.
|
## Joins two directory names to one.
|
||||||
##
|
##
|
||||||
## For example on Unix:
|
## For example on Unix:
|
||||||
|
|
@ -342,7 +334,8 @@ proc JoinPath*(head, tail: string): string {.noSideEffect.} =
|
||||||
else:
|
else:
|
||||||
result = head & DirSep & tail
|
result = head & DirSep & tail
|
||||||
|
|
||||||
proc JoinPath*(parts: openarray[string]): string {.noSideEffect.} =
|
proc JoinPath*(parts: openarray[string]): string {.noSideEffect,
|
||||||
|
rtl, extern: "nos$1OpenArray".} =
|
||||||
## The same as `JoinPath(head, tail)`, but works with any number
|
## The same as `JoinPath(head, tail)`, but works with any number
|
||||||
## of directory parts.
|
## of directory parts.
|
||||||
result = parts[0]
|
result = parts[0]
|
||||||
|
|
@ -370,7 +363,8 @@ proc SplitPath*(path: string, head, tail: var string) {.noSideEffect,
|
||||||
head = ""
|
head = ""
|
||||||
tail = path # make a string copy here
|
tail = path # make a string copy here
|
||||||
|
|
||||||
proc SplitPath*(path: string): tuple[head, tail: string] {.noSideEffect.} =
|
proc SplitPath*(path: string): tuple[head, tail: string] {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Splits a directory into (head, tail), so that
|
## Splits a directory into (head, tail), so that
|
||||||
## ``JoinPath(head, tail) == path``.
|
## ``JoinPath(head, tail) == path``.
|
||||||
##
|
##
|
||||||
|
|
@ -395,7 +389,8 @@ proc SplitPath*(path: string): tuple[head, tail: string] {.noSideEffect.} =
|
||||||
result.head = ""
|
result.head = ""
|
||||||
result.tail = path
|
result.tail = path
|
||||||
|
|
||||||
proc parentDir*(path: string): string {.noSideEffect.} =
|
proc parentDir*(path: string): string {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Returns the parent directory of `path`.
|
## Returns the parent directory of `path`.
|
||||||
##
|
##
|
||||||
## This is often the same as the ``head`` result of ``splitPath``.
|
## This is often the same as the ``head`` result of ``splitPath``.
|
||||||
|
|
@ -434,7 +429,8 @@ proc searchExtPos(s: string): int =
|
||||||
elif s[i] in {dirsep, altsep}:
|
elif s[i] in {dirsep, altsep}:
|
||||||
break # do not skip over path
|
break # do not skip over path
|
||||||
|
|
||||||
proc splitFile*(path: string): tuple[dir, name, ext: string] {.noSideEffect.} =
|
proc splitFile*(path: string): tuple[dir, name, ext: string] {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Splits a filename into (dir, filename, extension).
|
## Splits a filename into (dir, filename, extension).
|
||||||
## `dir` does not end in `DirSep`.
|
## `dir` does not end in `DirSep`.
|
||||||
## `extension` includes the leading dot.
|
## `extension` includes the leading dot.
|
||||||
|
|
@ -472,7 +468,8 @@ proc extractDir*(path: string): string {.noSideEffect, deprecated.} =
|
||||||
## **Deprecated since version 0.8.2**: Use ``splitFile(path).dir`` instead.
|
## **Deprecated since version 0.8.2**: Use ``splitFile(path).dir`` instead.
|
||||||
result = splitFile(path).dir
|
result = splitFile(path).dir
|
||||||
|
|
||||||
proc extractFilename*(path: string): string {.noSideEffect.} =
|
proc extractFilename*(path: string): string {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Extracts the filename of a given `path`. This is the same as
|
## Extracts the filename of a given `path`. This is the same as
|
||||||
## ``name & ext`` from ``splitFile(path)``.
|
## ``name & ext`` from ``splitFile(path)``.
|
||||||
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
|
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
|
||||||
|
|
@ -480,7 +477,7 @@ proc extractFilename*(path: string): string {.noSideEffect.} =
|
||||||
else:
|
else:
|
||||||
result = splitPath(path).tail
|
result = splitPath(path).tail
|
||||||
|
|
||||||
proc expandFilename*(filename: string): string =
|
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1".} =
|
||||||
## Returns the full path of `filename`, raises EOS in case of an error.
|
## Returns the full path of `filename`, raises EOS in case of an error.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var unused: cstring
|
var unused: cstring
|
||||||
|
|
@ -524,7 +521,8 @@ proc extractFileTrunk*(filename: string): string {.noSideEffect, deprecated.} =
|
||||||
## **Deprecated since version 0.8.2**: Use ``splitFile(path).name`` instead.
|
## **Deprecated since version 0.8.2**: Use ``splitFile(path).name`` instead.
|
||||||
result = splitFile(filename).name
|
result = splitFile(filename).name
|
||||||
|
|
||||||
proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
|
proc ChangeFileExt*(filename, ext: string): string {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Changes the file extension to `ext`.
|
## Changes the file extension to `ext`.
|
||||||
##
|
##
|
||||||
## If the `filename` has no extension, `ext` will be added.
|
## If the `filename` has no extension, `ext` will be added.
|
||||||
|
|
@ -536,7 +534,8 @@ proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
|
||||||
if extPos < 0: result = filename & normExt(ext)
|
if extPos < 0: result = filename & normExt(ext)
|
||||||
else: result = copy(filename, 0, extPos-1) & normExt(ext)
|
else: result = copy(filename, 0, extPos-1) & normExt(ext)
|
||||||
|
|
||||||
proc addFileExt*(filename, ext: string): string {.noSideEffect.} =
|
proc addFileExt*(filename, ext: string): string {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Adds the file extension `ext` to `filename`, unless
|
## Adds the file extension `ext` to `filename`, unless
|
||||||
## `filename` already has an extension.
|
## `filename` already has an extension.
|
||||||
##
|
##
|
||||||
|
|
@ -552,7 +551,8 @@ proc AppendFileExt*(filename, ext: string): string {.
|
||||||
## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
|
## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
|
||||||
result = addFileExt(filename, ext)
|
result = addFileExt(filename, ext)
|
||||||
|
|
||||||
proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} =
|
proc cmpPaths*(pathA, pathB: string): int {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Compares two paths.
|
## Compares two paths.
|
||||||
##
|
##
|
||||||
## On a case-sensitive filesystem this is done
|
## On a case-sensitive filesystem this is done
|
||||||
|
|
@ -566,7 +566,7 @@ proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} =
|
||||||
else:
|
else:
|
||||||
result = cmpIgnoreCase(pathA, pathB)
|
result = cmpIgnoreCase(pathA, pathB)
|
||||||
|
|
||||||
proc sameFile*(path1, path2: string): bool =
|
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
|
||||||
## Returns True if both pathname arguments refer to the same file or
|
## Returns True if both pathname arguments refer to the same file or
|
||||||
## directory (as indicated by device number and i-node number).
|
## directory (as indicated by device number and i-node number).
|
||||||
## Raises an exception if an stat() call on either pathname fails.
|
## Raises an exception if an stat() call on either pathname fails.
|
||||||
|
|
@ -590,7 +590,7 @@ proc sameFile*(path1, path2: string): bool =
|
||||||
else:
|
else:
|
||||||
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
|
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
|
||||||
|
|
||||||
proc sameFileContent*(path1, path2: string): bool =
|
proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
|
||||||
## Returns True if both pathname arguments refer to files with identical
|
## Returns True if both pathname arguments refer to files with identical
|
||||||
## binary content.
|
## binary content.
|
||||||
const
|
const
|
||||||
|
|
@ -620,7 +620,7 @@ proc sameFileContent*(path1, path2: string): bool =
|
||||||
close(a)
|
close(a)
|
||||||
close(b)
|
close(b)
|
||||||
|
|
||||||
proc copyFile*(dest, source: string) {.deprecated.} =
|
proc copyFile*(dest, source: string) {.deprecated, rtl, extern: "nos$1".} =
|
||||||
## Copies a file from `source` to `dest`. If this fails,
|
## Copies a file from `source` to `dest`. If this fails,
|
||||||
## `EOS` is raised.
|
## `EOS` is raised.
|
||||||
## **Deprecated since version 0.8.8**: Use this proc with named arguments
|
## **Deprecated since version 0.8.8**: Use this proc with named arguments
|
||||||
|
|
@ -650,13 +650,13 @@ proc copyFile*(dest, source: string) {.deprecated.} =
|
||||||
close(s)
|
close(s)
|
||||||
close(d)
|
close(d)
|
||||||
|
|
||||||
proc moveFile*(dest, source: string) {.deprecated.} =
|
proc moveFile*(dest, source: string) {.deprecated, rtl, extern: "nos$1".} =
|
||||||
## Moves a file from `source` to `dest`. If this fails, `EOS` is raised.
|
## Moves a file from `source` to `dest`. If this fails, `EOS` is raised.
|
||||||
## **Deprecated since version 0.8.8**: Use this proc with named arguments
|
## **Deprecated since version 0.8.8**: Use this proc with named arguments
|
||||||
## only, because the order will change!
|
## only, because the order will change!
|
||||||
if crename(source, dest) != 0'i32: OSError()
|
if crename(source, dest) != 0'i32: OSError()
|
||||||
|
|
||||||
proc removeFile*(file: string) =
|
proc removeFile*(file: string) {.rtl, extern: "nos$1".} =
|
||||||
## Removes the `file`. If this fails, `EOS` is raised.
|
## Removes the `file`. If this fails, `EOS` is raised.
|
||||||
if cremove(file) != 0'i32: OSError()
|
if cremove(file) != 0'i32: OSError()
|
||||||
|
|
||||||
|
|
@ -664,7 +664,7 @@ proc executeShellCommand*(command: string): int {.deprecated.} =
|
||||||
## **Deprecated since version 0.8.2**: Use `execShellCmd` instead.
|
## **Deprecated since version 0.8.2**: Use `execShellCmd` instead.
|
||||||
result = csystem(command)
|
result = csystem(command)
|
||||||
|
|
||||||
proc execShellCmd*(command: string): int =
|
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} =
|
||||||
## Executes a shell command.
|
## Executes a shell command.
|
||||||
##
|
##
|
||||||
## Command has the form 'program args' where args are the command
|
## Command has the form 'program args' where args are the command
|
||||||
|
|
@ -675,6 +675,9 @@ proc execShellCmd*(command: string): int =
|
||||||
## module.
|
## module.
|
||||||
result = csystem(command)
|
result = csystem(command)
|
||||||
|
|
||||||
|
# Environment handling cannot be put into RTL, because the ``envPairs``
|
||||||
|
# iterator depends on ``environment``.
|
||||||
|
|
||||||
var
|
var
|
||||||
envComputed: bool = false
|
envComputed: bool = false
|
||||||
environment: seq[string] = @[]
|
environment: seq[string] = @[]
|
||||||
|
|
@ -700,8 +703,8 @@ when defined(windows):
|
||||||
discard FreeEnvironmentStringsA(env)
|
discard FreeEnvironmentStringsA(env)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
var
|
var gEnv {.importc: "environ".}: cstringArray
|
||||||
gEnv {.importc: "gEnv".}: ptr array [0..10_000, CString]
|
# var gEnv {.importc: "gEnv".}: cstringArray
|
||||||
|
|
||||||
proc getEnvVarsC() =
|
proc getEnvVarsC() =
|
||||||
# retrieves the variables of char** env of C's main proc
|
# retrieves the variables of char** env of C's main proc
|
||||||
|
|
@ -897,7 +900,7 @@ proc rawRemoveDir(dir: string) =
|
||||||
else:
|
else:
|
||||||
if rmdir(dir) != 0'i32: OSError()
|
if rmdir(dir) != 0'i32: OSError()
|
||||||
|
|
||||||
proc removeDir*(dir: string) =
|
proc removeDir*(dir: string) {.rtl, extern: "nos$1".} =
|
||||||
## Removes the directory `dir` including all subdirectories and files
|
## Removes the directory `dir` including all subdirectories and files
|
||||||
## in `dir` (recursively). If this fails, `EOS` is raised.
|
## in `dir` (recursively). If this fails, `EOS` is raised.
|
||||||
for kind, path in walkDir(dir):
|
for kind, path in walkDir(dir):
|
||||||
|
|
@ -914,7 +917,7 @@ proc rawCreateDir(dir: string) =
|
||||||
if CreateDirectoryA(dir, nil) == 0'i32 and GetLastError() != 183'i32:
|
if CreateDirectoryA(dir, nil) == 0'i32 and GetLastError() != 183'i32:
|
||||||
OSError()
|
OSError()
|
||||||
|
|
||||||
proc createDir*(dir: string) =
|
proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
|
||||||
## Creates the directory `dir`.
|
## Creates the directory `dir`.
|
||||||
##
|
##
|
||||||
## The directory may contain several subdirectories that do not exist yet.
|
## The directory may contain several subdirectories that do not exist yet.
|
||||||
|
|
@ -925,7 +928,8 @@ proc createDir*(dir: string) =
|
||||||
if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
|
if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
|
||||||
rawCreateDir(dir)
|
rawCreateDir(dir)
|
||||||
|
|
||||||
proc parseCmdLine*(c: string): seq[string] =
|
proc parseCmdLine*(c: string): seq[string] {.
|
||||||
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Splits a command line into several components;
|
## Splits a command line into several components;
|
||||||
## This proc is only occassionally useful, better use the `parseopt` module.
|
## This proc is only occassionally useful, better use the `parseopt` module.
|
||||||
##
|
##
|
||||||
|
|
@ -1025,7 +1029,8 @@ type
|
||||||
fpOthersWrite, ## write access for others
|
fpOthersWrite, ## write access for others
|
||||||
fpOthersRead ## read access for others
|
fpOthersRead ## read access for others
|
||||||
|
|
||||||
proc getFilePermissions*(filename: string): set[TFilePermission] =
|
proc getFilePermissions*(filename: string): set[TFilePermission] {.
|
||||||
|
rtl, extern: "nos$1".} =
|
||||||
## retrieves file permissions for `filename`. `OSError` is raised in case of
|
## retrieves file permissions for `filename`. `OSError` is raised in case of
|
||||||
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
||||||
## permission is available in any case.
|
## permission is available in any case.
|
||||||
|
|
@ -1053,7 +1058,8 @@ proc getFilePermissions*(filename: string): set[TFilePermission] =
|
||||||
else:
|
else:
|
||||||
result = {fpUserExec..fpOthersRead}
|
result = {fpUserExec..fpOthersRead}
|
||||||
|
|
||||||
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) =
|
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
|
||||||
|
rtl, extern: "nos$1".} =
|
||||||
## sets the file permissions for `filename`. `OSError` is raised in case of
|
## sets the file permissions for `filename`. `OSError` is raised in case of
|
||||||
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
||||||
## ``fpUserWrite``.
|
## ``fpUserWrite``.
|
||||||
|
|
@ -1083,7 +1089,8 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) =
|
||||||
OSError()
|
OSError()
|
||||||
|
|
||||||
proc inclFilePermissions*(filename: string,
|
proc inclFilePermissions*(filename: string,
|
||||||
permissions: set[TFilePermission]) =
|
permissions: set[TFilePermission]) {.
|
||||||
|
rtl, extern: "nos$1".} =
|
||||||
## a convenience procedure for:
|
## a convenience procedure for:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
|
|
@ -1091,19 +1098,20 @@ proc inclFilePermissions*(filename: string,
|
||||||
setFilePermissions(filename, getFilePermissions(filename)+permissions)
|
setFilePermissions(filename, getFilePermissions(filename)+permissions)
|
||||||
|
|
||||||
proc exclFilePermissions*(filename: string,
|
proc exclFilePermissions*(filename: string,
|
||||||
permissions: set[TFilePermission]) =
|
permissions: set[TFilePermission]) {.
|
||||||
|
rtl, extern: "nos$1".} =
|
||||||
## a convenience procedure for:
|
## a convenience procedure for:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
||||||
setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
setFilePermissions(filename, getFilePermissions(filename)-permissions)
|
||||||
|
|
||||||
proc getHomeDir*(): string =
|
proc getHomeDir*(): string {.rtl, extern: "nos$1".} =
|
||||||
## Returns the home directory of the current user.
|
## Returns the home directory of the current user.
|
||||||
when defined(windows): return getEnv("USERPROFILE") & "\\"
|
when defined(windows): return getEnv("USERPROFILE") & "\\"
|
||||||
else: return getEnv("HOME") & "/"
|
else: return getEnv("HOME") & "/"
|
||||||
|
|
||||||
proc getConfigDir*(): string =
|
proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
|
||||||
## Returns the config directory of the current user for applications.
|
## Returns the config directory of the current user for applications.
|
||||||
when defined(windows): return getEnv("APPDATA") & "\\"
|
when defined(windows): return getEnv("APPDATA") & "\\"
|
||||||
else: return getEnv("HOME") & "/.config/"
|
else: return getEnv("HOME") & "/.config/"
|
||||||
|
|
@ -1117,24 +1125,32 @@ when defined(windows):
|
||||||
var
|
var
|
||||||
ownArgv: seq[string]
|
ownArgv: seq[string]
|
||||||
|
|
||||||
proc paramStr(i: int): string =
|
proc paramCount*(): int {.rtl, extern: "nos$1".} =
|
||||||
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
## Returns the number of command line arguments given to the
|
||||||
return ownArgv[i]
|
## application.
|
||||||
|
|
||||||
proc paramCount(): int =
|
|
||||||
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
||||||
result = ownArgv.len-1
|
result = ownArgv.len-1
|
||||||
|
|
||||||
else:
|
proc paramStr*(i: int): string {.rtl, extern: "nos$1".} =
|
||||||
|
## Returns the `i`-th command line argument given to the
|
||||||
|
## application.
|
||||||
|
##
|
||||||
|
## `i` should be in the range `1..paramCount()`, else
|
||||||
|
## the `EOutOfIndex` exception is raised.
|
||||||
|
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
||||||
|
return ownArgv[i]
|
||||||
|
|
||||||
|
elif not defined(createNimRtl):
|
||||||
|
# On Posix, there is no portable way to get the command line from a DLL.
|
||||||
var
|
var
|
||||||
cmdCount {.importc: "cmdCount".}: cint
|
cmdCount {.importc: "cmdCount".}: cint
|
||||||
cmdLine {.importc: "cmdLine".}: cstringArray
|
cmdLine {.importc: "cmdLine".}: cstringArray
|
||||||
|
|
||||||
proc paramStr(i: int): string =
|
proc paramStr*(i: int): string =
|
||||||
if i < cmdCount and i >= 0: return $cmdLine[i]
|
if i < cmdCount and i >= 0: return $cmdLine[i]
|
||||||
raise newException(EInvalidIndex, "invalid index")
|
raise newException(EInvalidIndex, "invalid index")
|
||||||
|
|
||||||
proc paramCount(): int = return cmdCount-1
|
proc paramCount*(): int = return cmdCount-1
|
||||||
|
|
||||||
when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
|
when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
|
||||||
proc getApplAux(procPath: string): string =
|
proc getApplAux(procPath: string): string =
|
||||||
|
|
@ -1153,7 +1169,7 @@ when defined(macosx):
|
||||||
proc getExecPath2(c: cstring, size: var int32): bool {.
|
proc getExecPath2(c: cstring, size: var int32): bool {.
|
||||||
importc: "_NSGetExecutablePath", header: "<mach-o/dyld.h>".}
|
importc: "_NSGetExecutablePath", header: "<mach-o/dyld.h>".}
|
||||||
|
|
||||||
proc getApplicationFilename*(): string =
|
proc getApplicationFilename*(): string {.rtl, extern: "nos$1".} =
|
||||||
## Returns the filename of the application's executable.
|
## Returns the filename of the application's executable.
|
||||||
|
|
||||||
# Linux: /proc/<pid>/exe
|
# Linux: /proc/<pid>/exe
|
||||||
|
|
@ -1190,11 +1206,11 @@ proc getApplicationFilename*(): string =
|
||||||
var x = joinPath(p, result)
|
var x = joinPath(p, result)
|
||||||
if ExistsFile(x): return x
|
if ExistsFile(x): return x
|
||||||
|
|
||||||
proc getApplicationDir*(): string =
|
proc getApplicationDir*(): string {.rtl, extern: "nos$1".} =
|
||||||
## Returns the directory of the application's executable.
|
## Returns the directory of the application's executable.
|
||||||
result = splitFile(getApplicationFilename()).dir
|
result = splitFile(getApplicationFilename()).dir
|
||||||
|
|
||||||
proc sleep*(milsecs: int) =
|
proc sleep*(milsecs: int) {.rtl, extern: "nos$1".} =
|
||||||
## sleeps `milsecs` milliseconds.
|
## sleeps `milsecs` milliseconds.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
winlean.sleep(int32(milsecs))
|
winlean.sleep(int32(milsecs))
|
||||||
|
|
@ -1204,7 +1220,7 @@ proc sleep*(milsecs: int) =
|
||||||
a.tv_nsec = (milsecs mod 1000) * 1000
|
a.tv_nsec = (milsecs mod 1000) * 1000
|
||||||
discard posix.nanosleep(a, b)
|
discard posix.nanosleep(a, b)
|
||||||
|
|
||||||
proc getFileSize*(file: string): biggestInt =
|
proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1".} =
|
||||||
## returns the file size of `file`. Can raise ``EOS``.
|
## returns the file size of `file`. Can raise ``EOS``.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
var a: TWin32FindData
|
var a: TWin32FindData
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
## This module implements an advanced facility for executing OS processes
|
## This module implements an advanced facility for executing OS processes
|
||||||
## and process communication.
|
## and process communication.
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, os, strtabs, streams
|
strutils, os, strtabs, streams
|
||||||
|
|
||||||
|
|
@ -39,7 +41,8 @@ type
|
||||||
|
|
||||||
proc execProcess*(command: string,
|
proc execProcess*(command: string,
|
||||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||||
poUseShell}): string
|
poUseShell}): string {.
|
||||||
|
rtl, extern: "nosp$1".}
|
||||||
## A convience procedure that executes ``command`` with ``startProcess``
|
## A convience procedure that executes ``command`` with ``startProcess``
|
||||||
## and returns its output as a string.
|
## and returns its output as a string.
|
||||||
|
|
||||||
|
|
@ -50,7 +53,7 @@ proc executeProcess*(command: string,
|
||||||
## **Deprecated since version 0.8.2**: Use `execProcess` instead.
|
## **Deprecated since version 0.8.2**: Use `execProcess` instead.
|
||||||
result = execProcess(command, options)
|
result = execProcess(command, options)
|
||||||
|
|
||||||
proc execCmd*(command: string): int
|
proc execCmd*(command: string): int {.rtl, extern: "nosp$1".}
|
||||||
## Executes ``command`` and returns its error code. Standard input, output,
|
## Executes ``command`` and returns its error code. Standard input, output,
|
||||||
## error streams are inherited from the calling process.
|
## error streams are inherited from the calling process.
|
||||||
|
|
||||||
|
|
@ -63,7 +66,8 @@ proc startProcess*(command: string,
|
||||||
workingDir: string = "",
|
workingDir: string = "",
|
||||||
args: openarray[string] = [],
|
args: openarray[string] = [],
|
||||||
env: PStringTable = nil,
|
env: PStringTable = nil,
|
||||||
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess
|
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||||
|
PProcess {.rtl, extern: "nosp$1".}
|
||||||
## Starts a process. `Command` is the executable file, `workingDir` is the
|
## Starts a process. `Command` is the executable file, `workingDir` is the
|
||||||
## process's working directory. If ``workingDir == ""`` the current directory
|
## process's working directory. If ``workingDir == ""`` the current directory
|
||||||
## is used. `args` are the command line arguments that are passed to the
|
## is used. `args` are the command line arguments that are passed to the
|
||||||
|
|
@ -78,32 +82,32 @@ proc startProcess*(command: string,
|
||||||
## Return value: The newly created process object. Nil is never returned,
|
## Return value: The newly created process object. Nil is never returned,
|
||||||
## but ``EOS`` is raised in case of an error.
|
## but ``EOS`` is raised in case of an error.
|
||||||
|
|
||||||
proc suspend*(p: PProcess)
|
proc suspend*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||||
## Suspends the process `p`.
|
## Suspends the process `p`.
|
||||||
|
|
||||||
proc resume*(p: PProcess)
|
proc resume*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||||
## Resumes the process `p`.
|
## Resumes the process `p`.
|
||||||
|
|
||||||
proc terminate*(p: PProcess)
|
proc terminate*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||||
## Terminates the process `p`.
|
## Terminates the process `p`.
|
||||||
|
|
||||||
proc running*(p: PProcess): bool
|
proc running*(p: PProcess): bool {.rtl, extern: "nosp$1".}
|
||||||
## Returns true iff the process `p` is still running. Returns immediately.
|
## Returns true iff the process `p` is still running. Returns immediately.
|
||||||
|
|
||||||
proc processID*(p: PProcess): int =
|
proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
|
||||||
## returns `p`'s process ID.
|
## returns `p`'s process ID.
|
||||||
return p.id
|
return p.id
|
||||||
|
|
||||||
proc waitForExit*(p: PProcess): int
|
proc waitForExit*(p: PProcess): int {.rtl, extern: "nosp$1".}
|
||||||
## waits for the process to finish and returns `p`'s error code.
|
## waits for the process to finish and returns `p`'s error code.
|
||||||
|
|
||||||
proc inputStream*(p: PProcess): PStream
|
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||||
## returns ``p``'s input stream for writing to
|
## returns ``p``'s input stream for writing to
|
||||||
|
|
||||||
proc outputStream*(p: PProcess): PStream
|
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||||
## returns ``p``'s output stream for reading from
|
## returns ``p``'s output stream for reading from
|
||||||
|
|
||||||
proc errorStream*(p: PProcess): PStream
|
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||||
## returns ``p``'s output stream for reading from
|
## returns ``p``'s output stream for reading from
|
||||||
|
|
||||||
when defined(macosx) or defined(bsd):
|
when defined(macosx) or defined(bsd):
|
||||||
|
|
@ -115,7 +119,7 @@ when defined(macosx) or defined(bsd):
|
||||||
a: var int, b: pointer, c: int): cint {.
|
a: var int, b: pointer, c: int): cint {.
|
||||||
importc: "sysctl", header: "<sys/sysctl.h>".}
|
importc: "sysctl", header: "<sys/sysctl.h>".}
|
||||||
|
|
||||||
proc countProcessors*(): int =
|
proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
|
||||||
## returns the numer of the processors/cores the machine has.
|
## returns the numer of the processors/cores the machine has.
|
||||||
## Returns 0 if it cannot be detected.
|
## Returns 0 if it cannot be detected.
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
|
|
@ -150,7 +154,7 @@ proc startProcessAux(cmd: string, options: set[TProcessOption]): PProcess =
|
||||||
|
|
||||||
proc execProcesses*(cmds: openArray[string],
|
proc execProcesses*(cmds: openArray[string],
|
||||||
options = {poStdErrToStdOut, poParentStreams},
|
options = {poStdErrToStdOut, poParentStreams},
|
||||||
n = countProcessors()): int =
|
n = countProcessors()): int {.rtl, extern: "nosp$1".} =
|
||||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||||
## that execute in parallel. The highest return value of all processes
|
## that execute in parallel. The highest return value of all processes
|
||||||
## is returned.
|
## is returned.
|
||||||
|
|
@ -192,27 +196,16 @@ proc execProcesses*(cmds: openArray[string],
|
||||||
var p = startProcessAux(cmds[i], options=options)
|
var p = startProcessAux(cmds[i], options=options)
|
||||||
result = max(waitForExit(p), result)
|
result = max(waitForExit(p), result)
|
||||||
|
|
||||||
when true:
|
when not defined(useNimRtl):
|
||||||
nil
|
proc execProcess(command: string,
|
||||||
else:
|
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||||
proc startGUIProcess*(command: string,
|
poUseShell}): string =
|
||||||
workingDir: string = "",
|
var p = startProcessAux(command, options=options)
|
||||||
args: openarray[string] = [],
|
var outp = outputStream(p)
|
||||||
env: PStringTable = nil,
|
result = ""
|
||||||
x = -1,
|
while running(p) or not outp.atEnd(outp):
|
||||||
y = -1,
|
result.add(outp.readLine())
|
||||||
width = -1,
|
result.add("\n")
|
||||||
height = -1): PProcess
|
|
||||||
|
|
||||||
proc execProcess(command: string,
|
|
||||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
|
||||||
poUseShell}): string =
|
|
||||||
var p = startProcessAux(command, options=options)
|
|
||||||
var outp = outputStream(p)
|
|
||||||
result = ""
|
|
||||||
while running(p) or not outp.atEnd(outp):
|
|
||||||
result.add(outp.readLine())
|
|
||||||
result.add("\n")
|
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc deallocCStringArray(a: cstringArray) =
|
proc deallocCStringArray(a: cstringArray) =
|
||||||
|
|
@ -222,7 +215,7 @@ when false:
|
||||||
inc(i)
|
inc(i)
|
||||||
dealloc(a)
|
dealloc(a)
|
||||||
|
|
||||||
when defined(Windows):
|
when defined(Windows) and not defined(useNimRtl):
|
||||||
# We need to implement a handle stream for Windows:
|
# We need to implement a handle stream for Windows:
|
||||||
type
|
type
|
||||||
PFileHandleStream = ref TFileHandleStream
|
PFileHandleStream = ref TFileHandleStream
|
||||||
|
|
@ -405,7 +398,7 @@ when defined(Windows):
|
||||||
result = -1
|
result = -1
|
||||||
discard CloseHandle(Process)
|
discard CloseHandle(Process)
|
||||||
|
|
||||||
else:
|
elif not defined(useNimRtl):
|
||||||
const
|
const
|
||||||
readIdx = 0
|
readIdx = 0
|
||||||
writeIdx = 1
|
writeIdx = 1
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
import
|
import
|
||||||
hashes, strutils, lexbase, streams
|
hashes, strutils, lexbase, streams
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TCfgEventKind* = enum ## enumation of all events that may occur when parsing
|
TCfgEventKind* = enum ## enumation of all events that may occur when parsing
|
||||||
cfgEof, ## end of file reached
|
cfgEof, ## end of file reached
|
||||||
|
|
@ -65,37 +67,17 @@ type
|
||||||
state: TParserState
|
state: TParserState
|
||||||
filename: string
|
filename: string
|
||||||
|
|
||||||
proc open*(c: var TCfgParser, input: PStream, filename: string)
|
|
||||||
## initializes the parser with an input stream. `Filename` is only used
|
|
||||||
## for nice error messages.
|
|
||||||
|
|
||||||
proc close*(c: var TCfgParser)
|
|
||||||
## closes the parser `c` and its associated input stream.
|
|
||||||
|
|
||||||
proc next*(c: var TCfgParser): TCfgEvent
|
|
||||||
## retrieves the first/next event. This controls the parser.
|
|
||||||
|
|
||||||
proc getColumn*(c: TCfgParser): int
|
|
||||||
## get the current column the parser has arrived at.
|
|
||||||
|
|
||||||
proc getLine*(c: TCfgParser): int
|
|
||||||
## get the current line the parser has arrived at.
|
|
||||||
|
|
||||||
proc getFilename*(c: TCfgParser): string
|
|
||||||
## get the filename of the file that the parser processes.
|
|
||||||
|
|
||||||
proc errorStr*(c: TCfgParser, msg: string): string
|
|
||||||
## returns a properly formated error message containing current line and
|
|
||||||
## column information.
|
|
||||||
|
|
||||||
|
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||||
|
|
||||||
proc rawGetTok(c: var TCfgParser, tok: var TToken)
|
proc rawGetTok(c: var TCfgParser, tok: var TToken)
|
||||||
proc open(c: var TCfgParser, input: PStream, filename: string) =
|
|
||||||
|
proc open*(c: var TCfgParser, input: PStream, filename: string) {.
|
||||||
|
rtl, extern: "npc$1".} =
|
||||||
|
## initializes the parser with an input stream. `Filename` is only used
|
||||||
|
## for nice error messages.
|
||||||
lexbase.open(c, input)
|
lexbase.open(c, input)
|
||||||
c.filename = filename
|
c.filename = filename
|
||||||
c.state = startState
|
c.state = startState
|
||||||
|
|
@ -103,16 +85,20 @@ proc open(c: var TCfgParser, input: PStream, filename: string) =
|
||||||
c.tok.literal = ""
|
c.tok.literal = ""
|
||||||
rawGetTok(c, c.tok)
|
rawGetTok(c, c.tok)
|
||||||
|
|
||||||
proc close(c: var TCfgParser) =
|
proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} =
|
||||||
|
## closes the parser `c` and its associated input stream.
|
||||||
lexbase.close(c)
|
lexbase.close(c)
|
||||||
|
|
||||||
proc getColumn(c: TCfgParser): int =
|
proc getColumn*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
||||||
|
## get the current column the parser has arrived at.
|
||||||
result = getColNumber(c, c.bufPos)
|
result = getColNumber(c, c.bufPos)
|
||||||
|
|
||||||
proc getLine(c: TCfgParser): int =
|
proc getLine*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
|
||||||
|
## get the current line the parser has arrived at.
|
||||||
result = c.linenumber
|
result = c.linenumber
|
||||||
|
|
||||||
proc getFilename(c: TCfgParser): string =
|
proc getFilename*(c: TCfgParser): string {.rtl, extern: "npc$1".} =
|
||||||
|
## get the filename of the file that the parser processes.
|
||||||
result = c.filename
|
result = c.filename
|
||||||
|
|
||||||
proc handleHexChar(c: var TCfgParser, xi: var int) =
|
proc handleHexChar(c: var TCfgParser, xi: var int) =
|
||||||
|
|
@ -300,7 +286,9 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
||||||
tok.literal = "[EOF]"
|
tok.literal = "[EOF]"
|
||||||
else: getSymbol(c, tok)
|
else: getSymbol(c, tok)
|
||||||
|
|
||||||
proc errorStr(c: TCfgParser, msg: string): string =
|
proc errorStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||||
|
## returns a properly formated error message containing current line and
|
||||||
|
## column information.
|
||||||
result = `%`("$1($2, $3) Error: $4",
|
result = `%`("$1($2, $3) Error: $4",
|
||||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||||
|
|
||||||
|
|
@ -323,7 +311,8 @@ proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
|
||||||
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
|
||||||
rawGetTok(c, c.tok)
|
rawGetTok(c, c.tok)
|
||||||
|
|
||||||
proc next(c: var TCfgParser): TCfgEvent =
|
proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} =
|
||||||
|
## retrieves the first/next event. This controls the parser.
|
||||||
case c.tok.kind
|
case c.tok.kind
|
||||||
of tkEof:
|
of tkEof:
|
||||||
result.kind = cfgEof
|
result.kind = cfgEof
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2009 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
{.push debugger: off.}
|
{.push debugger: off.}
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
import
|
import
|
||||||
os, strutils
|
os, strutils
|
||||||
|
|
||||||
|
|
@ -32,24 +34,28 @@ type
|
||||||
## or the argument, ``value`` is not "" if
|
## or the argument, ``value`` is not "" if
|
||||||
## the option was given a value
|
## the option was given a value
|
||||||
|
|
||||||
proc initOptParser*(cmdline = ""): TOptParser =
|
when defined(os.ParamCount):
|
||||||
## inits the option parser. If ``cmdline == ""``, the real command line
|
# we cannot provide this for NimRtl creation on Posix, because we can't
|
||||||
## (as provided by the ``OS`` module) is taken.
|
# access the command line arguments then!
|
||||||
result.pos = 0
|
|
||||||
result.inShortState = false
|
|
||||||
if cmdline != "":
|
|
||||||
result.cmd = cmdline
|
|
||||||
else:
|
|
||||||
result.cmd = ""
|
|
||||||
for i in countup(1, ParamCount()):
|
|
||||||
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i)) & ' '
|
|
||||||
result.kind = cmdEnd
|
|
||||||
result.key = ""
|
|
||||||
result.val = ""
|
|
||||||
|
|
||||||
proc init*(cmdline: string = ""): TOptParser {.deprecated.} =
|
proc initOptParser*(cmdline = ""): TOptParser =
|
||||||
## **Deprecated since version 0.8.2**: Use `initOptParser` instead.
|
## inits the option parser. If ``cmdline == ""``, the real command line
|
||||||
result = initOptParser(cmdline)
|
## (as provided by the ``OS`` module) is taken.
|
||||||
|
result.pos = 0
|
||||||
|
result.inShortState = false
|
||||||
|
if cmdline != "":
|
||||||
|
result.cmd = cmdline
|
||||||
|
else:
|
||||||
|
result.cmd = ""
|
||||||
|
for i in countup(1, ParamCount()):
|
||||||
|
result.cmd = result.cmd & quoteIfContainsWhite(paramStr(i)) & ' '
|
||||||
|
result.kind = cmdEnd
|
||||||
|
result.key = ""
|
||||||
|
result.val = ""
|
||||||
|
|
||||||
|
proc init*(cmdline: string = ""): TOptParser {.deprecated.} =
|
||||||
|
## **Deprecated since version 0.8.2**: Use `initOptParser` instead.
|
||||||
|
result = initOptParser(cmdline)
|
||||||
|
|
||||||
proc parseWord(s: string, i: int, w: var string,
|
proc parseWord(s: string, i: int, w: var string,
|
||||||
delim: TCharSet = {'\x09', ' ', '\0'}): int =
|
delim: TCharSet = {'\x09', ' ', '\0'}): int =
|
||||||
|
|
@ -82,7 +88,8 @@ proc handleShortOption(p: var TOptParser) =
|
||||||
if p.cmd[i] == '\0': p.inShortState = false
|
if p.cmd[i] == '\0': p.inShortState = false
|
||||||
p.pos = i
|
p.pos = i
|
||||||
|
|
||||||
proc next*(p: var TOptParser) =
|
proc next*(p: var TOptParser) {.
|
||||||
|
rtl, extern: "npo$1".} =
|
||||||
## parses the first or next option; ``p.kind`` describes what token has been
|
## parses the first or next option; ``p.kind`` describes what token has been
|
||||||
## parsed. ``p.key`` and ``p.val`` are set accordingly.
|
## parsed. ``p.key`` and ``p.val`` are set accordingly.
|
||||||
var i = p.pos
|
var i = p.pos
|
||||||
|
|
@ -116,7 +123,8 @@ proc next*(p: var TOptParser) =
|
||||||
p.kind = cmdArgument
|
p.kind = cmdArgument
|
||||||
p.pos = parseWord(p.cmd, i, p.key)
|
p.pos = parseWord(p.cmd, i, p.key)
|
||||||
|
|
||||||
proc cmdLineRest*(p: TOptParser): string =
|
proc cmdLineRest*(p: TOptParser): string {.
|
||||||
|
rtl, extern: "npo$1".} =
|
||||||
## retrieves the rest of the command line that has not been parsed yet.
|
## retrieves the rest of the command line that has not been parsed yet.
|
||||||
result = strip(copy(p.cmd, p.pos, len(p.cmd) - 1))
|
result = strip(copy(p.cmd, p.pos, len(p.cmd) - 1))
|
||||||
|
|
||||||
|
|
@ -124,29 +132,31 @@ proc getRestOfCommandLine*(p: TOptParser): string {.deprecated.} =
|
||||||
## **Deprecated since version 0.8.2**: Use `cmdLineRest` instead.
|
## **Deprecated since version 0.8.2**: Use `cmdLineRest` instead.
|
||||||
result = cmdLineRest(p)
|
result = cmdLineRest(p)
|
||||||
|
|
||||||
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
|
when defined(initOptParser):
|
||||||
## This is an convenience iterator for iterating over the command line.
|
|
||||||
## This uses the TOptParser object. Example:
|
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
|
||||||
##
|
## This is an convenience iterator for iterating over the command line.
|
||||||
## .. code-block:: nimrod
|
## This uses the TOptParser object. Example:
|
||||||
## var
|
##
|
||||||
## filename = ""
|
## .. code-block:: nimrod
|
||||||
## for kind, key, val in getopt():
|
## var
|
||||||
## case kind
|
## filename = ""
|
||||||
## of cmdArgument:
|
## for kind, key, val in getopt():
|
||||||
## filename = key
|
## case kind
|
||||||
## of cmdLongOption, cmdShortOption:
|
## of cmdArgument:
|
||||||
## case key
|
## filename = key
|
||||||
## of "help", "h": writeHelp()
|
## of cmdLongOption, cmdShortOption:
|
||||||
## of "version", "v": writeVersion()
|
## case key
|
||||||
## of cmdEnd: assert(false) # cannot happen
|
## of "help", "h": writeHelp()
|
||||||
## if filename == "":
|
## of "version", "v": writeVersion()
|
||||||
## # no filename has been given, so we show the help:
|
## of cmdEnd: assert(false) # cannot happen
|
||||||
## writeHelp()
|
## if filename == "":
|
||||||
var p = initOptParser()
|
## # no filename has been given, so we show the help:
|
||||||
while true:
|
## writeHelp()
|
||||||
next(p)
|
var p = initOptParser()
|
||||||
if p.kind == cmdEnd: break
|
while true:
|
||||||
yield (p.kind, p.key, p.val)
|
next(p)
|
||||||
|
if p.kind == cmdEnd: break
|
||||||
|
yield (p.kind, p.key, p.val)
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
{.push debugger:off .} # the user does not want to trace a part
|
{.push debugger:off .} # the user does not want to trace a part
|
||||||
# of the standard library!
|
# of the standard library!
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
const
|
const
|
||||||
Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||||
IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
||||||
|
|
@ -23,7 +25,8 @@ const
|
||||||
proc toLower(c: char): char {.inline.} =
|
proc toLower(c: char): char {.inline.} =
|
||||||
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
|
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
|
||||||
|
|
||||||
proc parseHex*(s: string, number: var int, start = 0): int =
|
proc parseHex*(s: string, number: var int, start = 0): int {.
|
||||||
|
rtl, extern: "npuParseHex", noSideEffect.} =
|
||||||
## parses a hexadecimal number and stores its value in ``number``. Returns
|
## parses a hexadecimal number and stores its value in ``number``. Returns
|
||||||
## the number of the parsed characters or 0 in case of an error.
|
## the number of the parsed characters or 0 in case of an error.
|
||||||
var i = start
|
var i = start
|
||||||
|
|
@ -46,7 +49,8 @@ proc parseHex*(s: string, number: var int, start = 0): int =
|
||||||
inc(i)
|
inc(i)
|
||||||
if foundDigit: result = i-start
|
if foundDigit: result = i-start
|
||||||
|
|
||||||
proc parseOct*(s: string, number: var int, start = 0): int =
|
proc parseOct*(s: string, number: var int, start = 0): int {.
|
||||||
|
rtl, extern: "npuParseOct", noSideEffect.} =
|
||||||
## parses an octal number and stores its value in ``number``. Returns
|
## parses an octal number and stores its value in ``number``. Returns
|
||||||
## the number of the parsed characters or 0 in case of an error.
|
## the number of the parsed characters or 0 in case of an error.
|
||||||
var i = start
|
var i = start
|
||||||
|
|
@ -116,13 +120,15 @@ proc rawParseInt(s: string, b: var biggestInt, start = 0): int =
|
||||||
result = i - start
|
result = i - start
|
||||||
{.pop.} # overflowChecks
|
{.pop.} # overflowChecks
|
||||||
|
|
||||||
proc parseBiggestInt*(s: string, number: var biggestInt, start = 0): int =
|
proc parseBiggestInt*(s: string, number: var biggestInt, start = 0): int {.
|
||||||
|
rtl, extern: "npuParseBiggestInt", noSideEffect.} =
|
||||||
## parses an integer starting at `start` and stores the value into `number`.
|
## parses an integer starting at `start` and stores the value into `number`.
|
||||||
## Result is the number of processed chars or 0 if there is no integer.
|
## Result is the number of processed chars or 0 if there is no integer.
|
||||||
## `EOverflow` is raised if an overflow occurs.
|
## `EOverflow` is raised if an overflow occurs.
|
||||||
result = rawParseInt(s, number, start)
|
result = rawParseInt(s, number, start)
|
||||||
|
|
||||||
proc parseInt*(s: string, number: var int, start = 0): int =
|
proc parseInt*(s: string, number: var int, start = 0): int {.
|
||||||
|
rtl, extern: "npuParseInt", noSideEffect.} =
|
||||||
## parses an integer starting at `start` and stores the value into `number`.
|
## parses an integer starting at `start` and stores the value into `number`.
|
||||||
## Result is the number of processed chars or 0 if there is no integer.
|
## Result is the number of processed chars or 0 if there is no integer.
|
||||||
## `EOverflow` is raised if an overflow occurs.
|
## `EOverflow` is raised if an overflow occurs.
|
||||||
|
|
@ -134,7 +140,8 @@ proc parseInt*(s: string, number: var int, start = 0): int =
|
||||||
else:
|
else:
|
||||||
number = int(res)
|
number = int(res)
|
||||||
|
|
||||||
proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int =
|
proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int {.
|
||||||
|
rtl, extern: "npuParseBiggestFloat", noSideEffect.} =
|
||||||
## parses a float starting at `start` and stores the value into `number`.
|
## parses a float starting at `start` and stores the value into `number`.
|
||||||
## Result is the number of processed chars or 0 if there occured a parsing
|
## Result is the number of processed chars or 0 if there occured a parsing
|
||||||
## error.
|
## error.
|
||||||
|
|
@ -206,7 +213,8 @@ proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int =
|
||||||
number = number * sign
|
number = number * sign
|
||||||
result = i - start
|
result = i - start
|
||||||
|
|
||||||
proc parseFloat*(s: string, number: var float, start = 0): int =
|
proc parseFloat*(s: string, number: var float, start = 0): int {.
|
||||||
|
rtl, extern: "npuParseFloat", noSideEffect.} =
|
||||||
## parses a float starting at `start` and stores the value into `number`.
|
## parses a float starting at `start` and stores the value into `number`.
|
||||||
## Result is the number of processed chars or 0 if there occured a parsing
|
## Result is the number of processed chars or 0 if there occured a parsing
|
||||||
## error.
|
## error.
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
## .. include:: ../doc/pegdocs.txt
|
## .. include:: ../doc/pegdocs.txt
|
||||||
##
|
##
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
const
|
const
|
||||||
useUnicode = true ## change this to deactivate proper UTF-8 support
|
useUnicode = true ## change this to deactivate proper UTF-8 support
|
||||||
|
|
||||||
|
|
@ -79,7 +81,7 @@ type
|
||||||
|
|
||||||
TPeg* = TNode ## type that represents a PEG
|
TPeg* = TNode ## type that represents a PEG
|
||||||
|
|
||||||
proc term*(t: string): TPeg =
|
proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
|
||||||
## constructs a PEG from a terminal string
|
## constructs a PEG from a terminal string
|
||||||
if t.len != 1:
|
if t.len != 1:
|
||||||
result.kind = pkTerminal
|
result.kind = pkTerminal
|
||||||
|
|
@ -88,23 +90,25 @@ proc term*(t: string): TPeg =
|
||||||
result.kind = pkChar
|
result.kind = pkChar
|
||||||
result.ch = t[0]
|
result.ch = t[0]
|
||||||
|
|
||||||
proc termIgnoreCase*(t: string): TPeg =
|
proc termIgnoreCase*(t: string): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG from a terminal string; ignore case for matching
|
## constructs a PEG from a terminal string; ignore case for matching
|
||||||
result.kind = pkTerminalIgnoreCase
|
result.kind = pkTerminalIgnoreCase
|
||||||
result.term = t
|
result.term = t
|
||||||
|
|
||||||
proc termIgnoreStyle*(t: string): TPeg =
|
proc termIgnoreStyle*(t: string): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG from a terminal string; ignore style for matching
|
## constructs a PEG from a terminal string; ignore style for matching
|
||||||
result.kind = pkTerminalIgnoreStyle
|
result.kind = pkTerminalIgnoreStyle
|
||||||
result.term = t
|
result.term = t
|
||||||
|
|
||||||
proc term*(t: char): TPeg =
|
proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} =
|
||||||
## constructs a PEG from a terminal char
|
## constructs a PEG from a terminal char
|
||||||
assert t != '\0'
|
assert t != '\0'
|
||||||
result.kind = pkChar
|
result.kind = pkChar
|
||||||
result.ch = t
|
result.ch = t
|
||||||
|
|
||||||
proc charSet*(s: set[char]): TPeg =
|
proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG from a character set `s`
|
## constructs a PEG from a character set `s`
|
||||||
assert '\0' notin s
|
assert '\0' notin s
|
||||||
result.kind = pkCharChoice
|
result.kind = pkCharChoice
|
||||||
|
|
@ -136,7 +140,8 @@ template multipleOp(k: TPegKind, localOpt: expr) =
|
||||||
if result.len == 1:
|
if result.len == 1:
|
||||||
result = result.sons[0]
|
result = result.sons[0]
|
||||||
|
|
||||||
proc `/`*(a: openArray[TPeg]): TPeg =
|
proc `/`*(a: openArray[TPeg]): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegsOrderedChoice".} =
|
||||||
## constructs an ordered choice with the PEGs in `a`
|
## constructs an ordered choice with the PEGs in `a`
|
||||||
multipleOp(pkOrderedChoice, addChoice)
|
multipleOp(pkOrderedChoice, addChoice)
|
||||||
|
|
||||||
|
|
@ -149,11 +154,12 @@ proc addSequence(dest: var TPeg, elem: TPeg) =
|
||||||
else: add(dest, elem)
|
else: add(dest, elem)
|
||||||
else: add(dest, elem)
|
else: add(dest, elem)
|
||||||
|
|
||||||
proc sequence*(a: openArray[TPeg]): TPeg =
|
proc sequence*(a: openArray[TPeg]): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a sequence with all the PEGs from `a`
|
## constructs a sequence with all the PEGs from `a`
|
||||||
multipleOp(pkSequence, addSequence)
|
multipleOp(pkSequence, addSequence)
|
||||||
|
|
||||||
proc `?`*(a: TPeg): TPeg =
|
proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
||||||
## constructs an optional for the PEG `a`
|
## constructs an optional for the PEG `a`
|
||||||
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
|
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
|
||||||
pkGreedyRepSet}:
|
pkGreedyRepSet}:
|
||||||
|
|
@ -164,7 +170,7 @@ proc `?`*(a: TPeg): TPeg =
|
||||||
result.kind = pkOption
|
result.kind = pkOption
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc `*`*(a: TPeg): TPeg =
|
proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
||||||
## constructs a "greedy repetition" for the PEG `a`
|
## constructs a "greedy repetition" for the PEG `a`
|
||||||
case a.kind
|
case a.kind
|
||||||
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
|
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
|
||||||
|
|
@ -182,7 +188,7 @@ proc `*`*(a: TPeg): TPeg =
|
||||||
result.kind = pkGreedyRep
|
result.kind = pkGreedyRep
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc `@`*(a: TPeg): TPeg =
|
proc `@`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
|
||||||
## constructs a "search" for the PEG `a`
|
## constructs a "search" for the PEG `a`
|
||||||
result.kind = pkSearch
|
result.kind = pkSearch
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
@ -199,16 +205,16 @@ when false:
|
||||||
for i in 0..a.sons.len-1:
|
for i in 0..a.sons.len-1:
|
||||||
if contains(a.sons[i], k): return true
|
if contains(a.sons[i], k): return true
|
||||||
|
|
||||||
proc `+`*(a: TPeg): TPeg =
|
proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
|
||||||
## constructs a "greedy positive repetition" with the PEG `a`
|
## constructs a "greedy positive repetition" with the PEG `a`
|
||||||
return sequence(a, *a)
|
return sequence(a, *a)
|
||||||
|
|
||||||
proc `&`*(a: TPeg): TPeg =
|
proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
|
||||||
## constructs an "and predicate" with the PEG `a`
|
## constructs an "and predicate" with the PEG `a`
|
||||||
result.kind = pkAndPredicate
|
result.kind = pkAndPredicate
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc `!`*(a: TPeg): TPeg =
|
proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
|
||||||
## constructs a "not predicate" with the PEG `a`
|
## constructs a "not predicate" with the PEG `a`
|
||||||
result.kind = pkNotPredicate
|
result.kind = pkNotPredicate
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
@ -225,24 +231,27 @@ proc newLine*: TPeg {.inline.} =
|
||||||
## constructs the PEG `newline`:idx: (``\n``)
|
## constructs the PEG `newline`:idx: (``\n``)
|
||||||
result.kind = pkNewline
|
result.kind = pkNewline
|
||||||
|
|
||||||
proc capture*(a: TPeg): TPeg =
|
proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
|
||||||
## constructs a capture with the PEG `a`
|
## constructs a capture with the PEG `a`
|
||||||
result.kind = pkCapture
|
result.kind = pkCapture
|
||||||
result.sons = @[a]
|
result.sons = @[a]
|
||||||
|
|
||||||
proc backref*(index: range[1..MaxSubPatterns]): TPeg =
|
proc backref*(index: range[1..MaxSubPatterns]): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a back reference of the given `index`. `index` starts counting
|
## constructs a back reference of the given `index`. `index` starts counting
|
||||||
## from 1.
|
## from 1.
|
||||||
result.kind = pkBackRef
|
result.kind = pkBackRef
|
||||||
result.index = index-1
|
result.index = index-1
|
||||||
|
|
||||||
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg =
|
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a back reference of the given `index`. `index` starts counting
|
## constructs a back reference of the given `index`. `index` starts counting
|
||||||
## from 1. Ignores case for matching.
|
## from 1. Ignores case for matching.
|
||||||
result.kind = pkBackRefIgnoreCase
|
result.kind = pkBackRefIgnoreCase
|
||||||
result.index = index-1
|
result.index = index-1
|
||||||
|
|
||||||
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg =
|
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".}=
|
||||||
## constructs a back reference of the given `index`. `index` starts counting
|
## constructs a back reference of the given `index`. `index` starts counting
|
||||||
## from 1. Ignores style for matching.
|
## from 1. Ignores style for matching.
|
||||||
result.kind = pkBackRefIgnoreStyle
|
result.kind = pkBackRefIgnoreStyle
|
||||||
|
|
@ -263,7 +272,8 @@ proc spaceCost(n: TPeg): int =
|
||||||
inc(result, spaceCost(n.sons[i]))
|
inc(result, spaceCost(n.sons[i]))
|
||||||
if result >= InlineThreshold: break
|
if result >= InlineThreshold: break
|
||||||
|
|
||||||
proc nonterminal*(n: PNonTerminal): TPeg =
|
proc nonterminal*(n: PNonTerminal): TPeg {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a PEG that consists of the nonterminal symbol
|
## constructs a PEG that consists of the nonterminal symbol
|
||||||
assert n != nil
|
assert n != nil
|
||||||
if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
|
if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
|
||||||
|
|
@ -273,7 +283,8 @@ proc nonterminal*(n: PNonTerminal): TPeg =
|
||||||
result.kind = pkNonTerminal
|
result.kind = pkNonTerminal
|
||||||
result.nt = n
|
result.nt = n
|
||||||
|
|
||||||
proc newNonTerminal*(name: string, line, column: int): PNonTerminal =
|
proc newNonTerminal*(name: string, line, column: int): PNonTerminal {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## constructs a nonterminal symbol
|
## constructs a nonterminal symbol
|
||||||
new(result)
|
new(result)
|
||||||
result.name = name
|
result.name = name
|
||||||
|
|
@ -432,7 +443,7 @@ proc toStrAux(r: TPeg, res: var string) =
|
||||||
toStrAux(r.sons[i], res)
|
toStrAux(r.sons[i], res)
|
||||||
add(res, "\n")
|
add(res, "\n")
|
||||||
|
|
||||||
proc `$` *(r: TPeg): string =
|
proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
||||||
## converts a PEG to its string representation
|
## converts a PEG to its string representation
|
||||||
result = ""
|
result = ""
|
||||||
toStrAux(r, result)
|
toStrAux(r, result)
|
||||||
|
|
@ -598,7 +609,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
|
||||||
of pkRule, pkList: assert false
|
of pkRule, pkList: assert false
|
||||||
|
|
||||||
proc match*(s: string, pattern: TPeg, matches: var openarray[string],
|
proc match*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||||
start = 0): bool =
|
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
|
||||||
## the captured substrings in the array ``matches``. If it does not
|
## the captured substrings in the array ``matches``. If it does not
|
||||||
## match, nothing is written into ``matches`` and ``false`` is
|
## match, nothing is written into ``matches`` and ``false`` is
|
||||||
|
|
@ -609,13 +620,14 @@ proc match*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||||
for i in 0..c.ml-1:
|
for i in 0..c.ml-1:
|
||||||
matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
|
matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
|
||||||
|
|
||||||
proc match*(s: string, pattern: TPeg, start = 0): bool =
|
proc match*(s: string, pattern: TPeg,
|
||||||
|
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
|
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
|
||||||
var c: TMatchClosure
|
var c: TMatchClosure
|
||||||
result = m(s, pattern, start, c) == len(s)-start
|
result = m(s, pattern, start, c) == len(s)-start
|
||||||
|
|
||||||
proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
|
proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||||
start = 0): int =
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## the same as ``match``, but it returns the length of the match,
|
## the same as ``match``, but it returns the length of the match,
|
||||||
## if there is no match, -1 is returned. Note that a match length
|
## if there is no match, -1 is returned. Note that a match length
|
||||||
## of zero can happen. It's possible that a suffix of `s` remains
|
## of zero can happen. It's possible that a suffix of `s` remains
|
||||||
|
|
@ -626,7 +638,8 @@ proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||||
for i in 0..c.ml-1:
|
for i in 0..c.ml-1:
|
||||||
matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
|
matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
|
||||||
|
|
||||||
proc matchLen*(s: string, pattern: TPeg, start = 0): int =
|
proc matchLen*(s: string, pattern: TPeg,
|
||||||
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## the same as ``match``, but it returns the length of the match,
|
## the same as ``match``, but it returns the length of the match,
|
||||||
## if there is no match, -1 is returned. Note that a match length
|
## if there is no match, -1 is returned. Note that a match length
|
||||||
## of zero can happen. It's possible that a suffix of `s` remains
|
## of zero can happen. It's possible that a suffix of `s` remains
|
||||||
|
|
@ -635,7 +648,7 @@ proc matchLen*(s: string, pattern: TPeg, start = 0): int =
|
||||||
result = m(s, pattern, start, c)
|
result = m(s, pattern, start, c)
|
||||||
|
|
||||||
proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||||
start = 0): int =
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## returns the starting position of ``pattern`` in ``s`` and the captured
|
## returns the starting position of ``pattern`` in ``s`` and the captured
|
||||||
## substrings in the array ``matches``. If it does not match, nothing
|
## substrings in the array ``matches``. If it does not match, nothing
|
||||||
## is written into ``matches`` and -1 is returned.
|
## is written into ``matches`` and -1 is returned.
|
||||||
|
|
@ -644,7 +657,8 @@ proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||||
return -1
|
return -1
|
||||||
# could also use the pattern here: (!P .)* P
|
# could also use the pattern here: (!P .)* P
|
||||||
|
|
||||||
proc find*(s: string, pattern: TPeg, start = 0): int =
|
proc find*(s: string, pattern: TPeg,
|
||||||
|
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns the starting position of ``pattern`` in ``s``. If it does not
|
## returns the starting position of ``pattern`` in ``s``. If it does not
|
||||||
## match, -1 is returned.
|
## match, -1 is returned.
|
||||||
for i in 0 .. s.len-1:
|
for i in 0 .. s.len-1:
|
||||||
|
|
@ -675,25 +689,29 @@ template `=~`*(s: string, pattern: TPeg): expr =
|
||||||
|
|
||||||
# ------------------------- more string handling ------------------------------
|
# ------------------------- more string handling ------------------------------
|
||||||
|
|
||||||
proc contains*(s: string, pattern: TPeg, start = 0): bool =
|
proc contains*(s: string, pattern: TPeg, start = 0): bool {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## same as ``find(s, pattern, start) >= 0``
|
## same as ``find(s, pattern, start) >= 0``
|
||||||
return find(s, pattern, start) >= 0
|
return find(s, pattern, start) >= 0
|
||||||
|
|
||||||
proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
|
proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
|
||||||
start = 0): bool =
|
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
|
||||||
## same as ``find(s, pattern, matches, start) >= 0``
|
## same as ``find(s, pattern, matches, start) >= 0``
|
||||||
return find(s, pattern, matches, start) >= 0
|
return find(s, pattern, matches, start) >= 0
|
||||||
|
|
||||||
proc startsWith*(s: string, prefix: TPeg): bool =
|
proc startsWith*(s: string, prefix: TPeg): bool {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns true if `s` starts with the pattern `prefix`
|
## returns true if `s` starts with the pattern `prefix`
|
||||||
result = matchLen(s, prefix) >= 0
|
result = matchLen(s, prefix) >= 0
|
||||||
|
|
||||||
proc endsWith*(s: string, suffix: TPeg): bool =
|
proc endsWith*(s: string, suffix: TPeg): bool {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## returns true if `s` ends with the pattern `prefix`
|
## returns true if `s` ends with the pattern `prefix`
|
||||||
for i in 0 .. s.len-1:
|
for i in 0 .. s.len-1:
|
||||||
if matchLen(s, suffix, i) == s.len - i: return true
|
if matchLen(s, suffix, i) == s.len - i: return true
|
||||||
|
|
||||||
proc replace*(s: string, sub: TPeg, by: string): string =
|
proc replace*(s: string, sub: TPeg, by: string): string {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
|
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
|
||||||
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
|
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
|
||||||
##
|
##
|
||||||
|
|
@ -720,7 +738,8 @@ proc replace*(s: string, sub: TPeg, by: string): string =
|
||||||
add(result, copy(s, i))
|
add(result, copy(s, i))
|
||||||
|
|
||||||
proc parallelReplace*(s: string, subs: openArray[
|
proc parallelReplace*(s: string, subs: openArray[
|
||||||
tuple[pattern: TPeg, repl: string]]): string =
|
tuple[pattern: TPeg, repl: string]]): string {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## Returns a modified copy of `s` with the substitutions in `subs`
|
## Returns a modified copy of `s` with the substitutions in `subs`
|
||||||
## applied in parallel.
|
## applied in parallel.
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -740,7 +759,8 @@ proc parallelReplace*(s: string, subs: openArray[
|
||||||
add(result, copy(s, i))
|
add(result, copy(s, i))
|
||||||
|
|
||||||
proc transformFile*(infile, outfile: string,
|
proc transformFile*(infile, outfile: string,
|
||||||
subs: openArray[tuple[pattern: TPeg, repl: string]]) =
|
subs: openArray[tuple[pattern: TPeg, repl: string]]) {.
|
||||||
|
rtl, extern: "npegs$1".} =
|
||||||
## reads in the file `infile`, performs a parallel replacement (calls
|
## reads in the file `infile`, performs a parallel replacement (calls
|
||||||
## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
|
## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
|
||||||
## error occurs. This is supposed to be used for quick scripting.
|
## error occurs. This is supposed to be used for quick scripting.
|
||||||
|
|
@ -787,7 +807,8 @@ iterator split*(s: string, sep: TPeg): string =
|
||||||
if first < last:
|
if first < last:
|
||||||
yield copy(s, first, last-1)
|
yield copy(s, first, last-1)
|
||||||
|
|
||||||
proc split*(s: string, sep: TPeg): seq[string] {.noSideEffect.} =
|
proc split*(s: string, sep: TPeg): seq[string] {.
|
||||||
|
nosideEffect, rtl, extern: "npegs$1".} =
|
||||||
## Splits the string `s` into substrings.
|
## Splits the string `s` into substrings.
|
||||||
accumulateResult(split(s, sep))
|
accumulateResult(split(s, sep))
|
||||||
|
|
||||||
|
|
@ -1265,6 +1286,8 @@ proc primary(p: var TPegParser): TPeg =
|
||||||
of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
|
of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
|
||||||
of "w": result = charset({'a'..'z', 'A'..'Z', '_', '0'..'9'})
|
of "w": result = charset({'a'..'z', 'A'..'Z', '_', '0'..'9'})
|
||||||
of "W": result = charset({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
|
of "W": result = charset({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
|
||||||
|
of "a": result = charset({'a'..'z', 'A'..'Z'})
|
||||||
|
of "A": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
|
||||||
of "ident": result = pegs.ident
|
of "ident": result = pegs.ident
|
||||||
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
||||||
getTok(p)
|
getTok(p)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
## Leaves can be cached for better memory efficiency at the cost of
|
## Leaves can be cached for better memory efficiency at the cost of
|
||||||
## runtime efficiency.
|
## runtime efficiency.
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
{.deadCodeElim: on.}
|
{.deadCodeElim: on.}
|
||||||
|
|
||||||
{.push debugger:off .} # the user does not want to trace a part
|
{.push debugger:off .} # the user does not want to trace a part
|
||||||
|
|
@ -44,7 +46,7 @@ proc isConc(r: PRope): bool {.inline.} = return isNil(r.data)
|
||||||
# performance. But for the caching tree we use the leaf's left and right
|
# performance. But for the caching tree we use the leaf's left and right
|
||||||
# pointers.
|
# pointers.
|
||||||
|
|
||||||
proc len*(a: PRope): int =
|
proc len*(a: PRope): int {.rtl, extern: "nro$1".} =
|
||||||
## the rope's length
|
## the rope's length
|
||||||
if a == nil: result = 0
|
if a == nil: result = 0
|
||||||
else: result = a.length
|
else: result = a.length
|
||||||
|
|
@ -126,7 +128,7 @@ proc insertInCache(s: string, tree: PRope): PRope =
|
||||||
result.left = t
|
result.left = t
|
||||||
t.right = nil
|
t.right = nil
|
||||||
|
|
||||||
proc rope*(s: string): PRope =
|
proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
|
||||||
## Converts a string to a rope.
|
## Converts a string to a rope.
|
||||||
if s.len == 0:
|
if s.len == 0:
|
||||||
result = nil
|
result = nil
|
||||||
|
|
@ -136,25 +138,25 @@ proc rope*(s: string): PRope =
|
||||||
else:
|
else:
|
||||||
result = newRope(s)
|
result = newRope(s)
|
||||||
|
|
||||||
proc rope*(i: BiggestInt): PRope =
|
proc rope*(i: BiggestInt): PRope {.rtl, extern: "nro$1BiggestInt".} =
|
||||||
## Converts an int to a rope.
|
## Converts an int to a rope.
|
||||||
result = rope($i)
|
result = rope($i)
|
||||||
|
|
||||||
proc rope*(f: BiggestFloat): PRope =
|
proc rope*(f: BiggestFloat): PRope {.rtl, extern: "nro$1BiggestFloat".} =
|
||||||
## Converts a float to a rope.
|
## Converts a float to a rope.
|
||||||
result = rope($f)
|
result = rope($f)
|
||||||
|
|
||||||
proc disableCache*() =
|
proc disableCache*() {.rtl, extern: "nro$1".} =
|
||||||
## the cache is discarded and disabled. The GC will reuse its used memory.
|
## the cache is discarded and disabled. The GC will reuse its used memory.
|
||||||
cache = nil
|
cache = nil
|
||||||
cacheEnabled = false
|
cacheEnabled = false
|
||||||
|
|
||||||
proc enableCache*() =
|
proc enableCache*() {.rtl, extern: "nro$1".} =
|
||||||
## Enables the caching of leaves. This reduces the memory footprint at
|
## Enables the caching of leaves. This reduces the memory footprint at
|
||||||
## the cost of runtime efficiency.
|
## the cost of runtime efficiency.
|
||||||
cacheEnabled = true
|
cacheEnabled = true
|
||||||
|
|
||||||
proc `&`*(a, b: PRope): PRope =
|
proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
|
||||||
## the concatenation operator for ropes.
|
## the concatenation operator for ropes.
|
||||||
if a == nil:
|
if a == nil:
|
||||||
result = b
|
result = b
|
||||||
|
|
@ -174,27 +176,27 @@ proc `&`*(a, b: PRope): PRope =
|
||||||
result.left = a
|
result.left = a
|
||||||
result.right = b
|
result.right = b
|
||||||
|
|
||||||
proc `&`*(a: PRope, b: string): PRope =
|
proc `&`*(a: PRope, b: string): PRope {.rtl, extern: "nroConcRopeStr".} =
|
||||||
## the concatenation operator for ropes.
|
## the concatenation operator for ropes.
|
||||||
result = a & rope(b)
|
result = a & rope(b)
|
||||||
|
|
||||||
proc `&`*(a: string, b: PRope): PRope =
|
proc `&`*(a: string, b: PRope): PRope {.rtl, extern: "nroConcStrRope".} =
|
||||||
## the concatenation operator for ropes.
|
## the concatenation operator for ropes.
|
||||||
result = rope(a) & b
|
result = rope(a) & b
|
||||||
|
|
||||||
proc `&`*(a: openarray[PRope]): PRope =
|
proc `&`*(a: openarray[PRope]): PRope {.rtl, extern: "nroConcOpenArray".} =
|
||||||
## the concatenation operator for an openarray of ropes.
|
## the concatenation operator for an openarray of ropes.
|
||||||
for i in countup(0, high(a)): result = result & a[i]
|
for i in countup(0, high(a)): result = result & a[i]
|
||||||
|
|
||||||
proc add*(a: var PRope, b: PRope) =
|
proc add*(a: var PRope, b: PRope) {.rtl, extern: "nro$1Rope".} =
|
||||||
## adds `b` to the rope `a`.
|
## adds `b` to the rope `a`.
|
||||||
a = a & b
|
a = a & b
|
||||||
|
|
||||||
proc add*(a: var PRope, b: string) =
|
proc add*(a: var PRope, b: string) {.rtl, extern: "nro$1Str".} =
|
||||||
## adds `b` to the rope `a`.
|
## adds `b` to the rope `a`.
|
||||||
a = a & b
|
a = a & b
|
||||||
|
|
||||||
proc `[]`*(r: PRope, i: int): char =
|
proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
|
||||||
## returns the character at position `i` in the rope `r`. This is quite
|
## returns the character at position `i` in the rope `r`. This is quite
|
||||||
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
|
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
|
||||||
var x = r
|
var x = r
|
||||||
|
|
@ -229,11 +231,11 @@ iterator items*(r: PRope): char =
|
||||||
for s in leaves(r):
|
for s in leaves(r):
|
||||||
for c in items(s): yield c
|
for c in items(s): yield c
|
||||||
|
|
||||||
proc write*(f: TFile, r: PRope) =
|
proc write*(f: TFile, r: PRope) {.rtl, extern: "nro$1".} =
|
||||||
## writes a rope to a file.
|
## writes a rope to a file.
|
||||||
for s in leaves(r): write(f, s)
|
for s in leaves(r): write(f, s)
|
||||||
|
|
||||||
proc `$`*(r: PRope): string =
|
proc `$`*(r: PRope): string {.rtl, extern: "nroToString".}=
|
||||||
## converts a rope back to a string.
|
## converts a rope back to a string.
|
||||||
result = newString(r.len)
|
result = newString(r.len)
|
||||||
setLen(result, 0)
|
setLen(result, 0)
|
||||||
|
|
@ -289,7 +291,8 @@ when false:
|
||||||
if i - 1 >= start:
|
if i - 1 >= start:
|
||||||
add(result, copy(frmt, start, i-1))
|
add(result, copy(frmt, start, i-1))
|
||||||
|
|
||||||
proc `%`*(frmt: string, args: openarray[PRope]): PRope =
|
proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
|
||||||
|
rtl, extern: "nroFormat".} =
|
||||||
## `%` substitution operator for ropes. Does not support the ``$identifier``
|
## `%` substitution operator for ropes. Does not support the ``$identifier``
|
||||||
## nor ``${identifier}`` notations.
|
## nor ``${identifier}`` notations.
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -333,11 +336,12 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope =
|
||||||
if i - 1 >= start:
|
if i - 1 >= start:
|
||||||
add(result, copy(frmt, start, i - 1))
|
add(result, copy(frmt, start, i - 1))
|
||||||
|
|
||||||
proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) =
|
proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) {.
|
||||||
|
rtl, extern: "nro$1".} =
|
||||||
## shortcut for ``add(c, frmt % args)``.
|
## shortcut for ``add(c, frmt % args)``.
|
||||||
add(c, frmt % args)
|
add(c, frmt % args)
|
||||||
|
|
||||||
proc equalsFile*(r: PRope, f: TFile): bool =
|
proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} =
|
||||||
## returns true if the contents of the file `f` equal `r`.
|
## returns true if the contents of the file `f` equal `r`.
|
||||||
var bufSize = 1024 # reasonable start value
|
var bufSize = 1024 # reasonable start value
|
||||||
var buf = alloc(BufSize)
|
var buf = alloc(BufSize)
|
||||||
|
|
@ -352,7 +356,7 @@ proc equalsFile*(r: PRope, f: TFile): bool =
|
||||||
result = readBuffer(f, buf, 1) == 0 # really at the end of file?
|
result = readBuffer(f, buf, 1) == 0 # really at the end of file?
|
||||||
dealloc(buf)
|
dealloc(buf)
|
||||||
|
|
||||||
proc equalsFile*(r: PRope, f: string): bool =
|
proc equalsFile*(r: PRope, f: string): bool {.rtl, extern: "nro$1Str".} =
|
||||||
## returns true if the contents of the file `f` equal `r`. If `f` does not
|
## returns true if the contents of the file `f` equal `r`. If `f` does not
|
||||||
## exist, false is returned.
|
## exist, false is returned.
|
||||||
var bin: TFile
|
var bin: TFile
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
import
|
import
|
||||||
os, hashes, strutils
|
os, hashes, strutils
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TStringTableMode* = enum ## describes the tables operation mode
|
TStringTableMode* = enum ## describes the tables operation mode
|
||||||
modeCaseSensitive, ## the table is case sensitive
|
modeCaseSensitive, ## the table is case sensitive
|
||||||
|
|
@ -29,28 +31,7 @@ type
|
||||||
|
|
||||||
PStringTable* = ref TStringTable ## use this type to declare string tables
|
PStringTable* = ref TStringTable ## use this type to declare string tables
|
||||||
|
|
||||||
proc newStringTable*(keyValuePairs: openarray[string],
|
proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} =
|
||||||
mode: TStringTableMode = modeCaseSensitive): PStringTable
|
|
||||||
## creates a new string table with given key value pairs.
|
|
||||||
## Example::
|
|
||||||
## var mytab = newStringTable("key1", "val1", "key2", "val2",
|
|
||||||
## modeCaseInsensitive)
|
|
||||||
|
|
||||||
proc newStringTable*(mode: TStringTableMode): PStringTable
|
|
||||||
## creates a new string table that is empty.
|
|
||||||
|
|
||||||
proc `[]=`*(t: PStringTable, key, val: string)
|
|
||||||
## puts a (key, value)-pair into `t`.
|
|
||||||
|
|
||||||
proc `[]`*(t: PStringTable, key: string): string
|
|
||||||
## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned
|
|
||||||
## and no exception is raised. One can check with ``hasKey`` whether the key
|
|
||||||
## exists.
|
|
||||||
|
|
||||||
proc hasKey*(t: PStringTable, key: string): bool
|
|
||||||
## returns true iff `key` is in the table `t`.
|
|
||||||
|
|
||||||
proc len*(t: PStringTable): int =
|
|
||||||
## returns the number of keys in `t`.
|
## returns the number of keys in `t`.
|
||||||
result = t.counter
|
result = t.counter
|
||||||
|
|
||||||
|
|
@ -70,29 +51,12 @@ type
|
||||||
useKey ## do not replace ``$key`` if it is not found
|
useKey ## do not replace ``$key`` if it is not found
|
||||||
## in the table (or in the environment)
|
## in the table (or in the environment)
|
||||||
|
|
||||||
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string
|
|
||||||
## The `%` operator for string tables.
|
|
||||||
|
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
const
|
const
|
||||||
growthFactor = 2
|
growthFactor = 2
|
||||||
startSize = 64
|
startSize = 64
|
||||||
|
|
||||||
proc newStringTable(mode: TStringTableMode): PStringTable =
|
|
||||||
new(result)
|
|
||||||
result.mode = mode
|
|
||||||
result.counter = 0
|
|
||||||
newSeq(result.data, startSize)
|
|
||||||
|
|
||||||
proc newStringTable(keyValuePairs: openarray[string],
|
|
||||||
mode: TStringTableMode = modeCaseSensitive): PStringTable =
|
|
||||||
result = newStringTable(mode)
|
|
||||||
var i = 0
|
|
||||||
while i < high(keyValuePairs):
|
|
||||||
result[keyValuePairs[i]] = keyValuePairs[i + 1]
|
|
||||||
inc(i, 2)
|
|
||||||
|
|
||||||
proc myhash(t: PStringTable, key: string): THash =
|
proc myhash(t: PStringTable, key: string): THash =
|
||||||
case t.mode
|
case t.mode
|
||||||
of modeCaseSensitive: result = hashes.hash(key)
|
of modeCaseSensitive: result = hashes.hash(key)
|
||||||
|
|
@ -121,13 +85,17 @@ proc RawGet(t: PStringTable, key: string): int =
|
||||||
h = nextTry(h, high(t.data))
|
h = nextTry(h, high(t.data))
|
||||||
result = - 1
|
result = - 1
|
||||||
|
|
||||||
proc `[]`(t: PStringTable, key: string): string =
|
proc `[]`*(t: PStringTable, key: string): string {.rtl, extern: "nstGet".} =
|
||||||
|
## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned
|
||||||
|
## and no exception is raised. One can check with ``hasKey`` whether the key
|
||||||
|
## exists.
|
||||||
var index: int
|
var index: int
|
||||||
index = RawGet(t, key)
|
index = RawGet(t, key)
|
||||||
if index >= 0: result = t.data[index].val
|
if index >= 0: result = t.data[index].val
|
||||||
else: result = ""
|
else: result = ""
|
||||||
|
|
||||||
proc hasKey(t: PStringTable, key: string): bool =
|
proc hasKey*(t: PStringTable, key: string): bool {.rtl, extern: "nst$1".} =
|
||||||
|
## returns true iff `key` is in the table `t`.
|
||||||
result = rawGet(t, key) >= 0
|
result = rawGet(t, key) >= 0
|
||||||
|
|
||||||
proc RawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) =
|
proc RawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) =
|
||||||
|
|
@ -145,7 +113,8 @@ proc Enlarge(t: PStringTable) =
|
||||||
if not isNil(t.data[i].key): RawInsert(t, n, t.data[i].key, t.data[i].val)
|
if not isNil(t.data[i].key): RawInsert(t, n, t.data[i].key, t.data[i].val)
|
||||||
swap(t.data, n)
|
swap(t.data, n)
|
||||||
|
|
||||||
proc `[]=`(t: PStringTable, key, val: string) =
|
proc `[]=`*(t: PStringTable, key, val: string) {.rtl, extern: "nstPut".} =
|
||||||
|
## puts a (key, value)-pair into `t`.
|
||||||
var index = RawGet(t, key)
|
var index = RawGet(t, key)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
t.data[index].val = val
|
t.data[index].val = val
|
||||||
|
|
@ -168,7 +137,30 @@ proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string =
|
||||||
if useKey in flags: result = '$' & key
|
if useKey in flags: result = '$' & key
|
||||||
elif not (useEmpty in flags): raiseFormatException(key)
|
elif not (useEmpty in flags): raiseFormatException(key)
|
||||||
|
|
||||||
proc `%`(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string =
|
proc newStringTable*(mode: TStringTableMode): PStringTable {.
|
||||||
|
rtl, extern: "nst$1".} =
|
||||||
|
## creates a new string table that is empty.
|
||||||
|
new(result)
|
||||||
|
result.mode = mode
|
||||||
|
result.counter = 0
|
||||||
|
newSeq(result.data, startSize)
|
||||||
|
|
||||||
|
proc newStringTable*(keyValuePairs: openarray[string],
|
||||||
|
mode: TStringTableMode = modeCaseSensitive): PStringTable {.
|
||||||
|
rtl, extern: "nst$1WithPairs".} =
|
||||||
|
## creates a new string table with given key value pairs.
|
||||||
|
## Example::
|
||||||
|
## var mytab = newStringTable("key1", "val1", "key2", "val2",
|
||||||
|
## modeCaseInsensitive)
|
||||||
|
result = newStringTable(mode)
|
||||||
|
var i = 0
|
||||||
|
while i < high(keyValuePairs):
|
||||||
|
result[keyValuePairs[i]] = keyValuePairs[i + 1]
|
||||||
|
inc(i, 2)
|
||||||
|
|
||||||
|
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string {.
|
||||||
|
rtl, extern: "nstFormat".} =
|
||||||
|
## The `%` operator for string tables.
|
||||||
const
|
const
|
||||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||||
result = ""
|
result = ""
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module contains various string utility routines.
|
## This module contains various string utility routines.
|
||||||
## See the module `regexprs` for regular expression support.
|
## See the module `re` for regular expression support.
|
||||||
## See the module `pegs` for PEG support.
|
## See the module `pegs` for PEG support.
|
||||||
|
|
||||||
import parseutils
|
import parseutils
|
||||||
|
|
@ -18,6 +18,8 @@ import parseutils
|
||||||
{.push debugger:off .} # the user does not want to trace a part
|
{.push debugger:off .} # the user does not want to trace a part
|
||||||
# of the standard library!
|
# of the standard library!
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TCharSet* = set[char] # for compatibility with Nim
|
TCharSet* = set[char] # for compatibility with Nim
|
||||||
|
|
||||||
|
|
@ -40,7 +42,151 @@ const
|
||||||
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
|
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
|
||||||
## the set of characters an identifier can start with
|
## the set of characters an identifier can start with
|
||||||
|
|
||||||
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
|
|
||||||
|
proc toLower*(c: Char): Char {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuToLowerChar".} =
|
||||||
|
## Converts `c` into lower case. This works only for the letters A-Z.
|
||||||
|
## See `unicode.toLower` for a version that works for any Unicode character.
|
||||||
|
if c in {'A'..'Z'}:
|
||||||
|
result = chr(ord(c) + (ord('a') - ord('A')))
|
||||||
|
else:
|
||||||
|
result = c
|
||||||
|
|
||||||
|
proc toLower*(s: string): string {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuToLowerStr".} =
|
||||||
|
## Converts `s` into lower case. This works only for the letters A-Z.
|
||||||
|
## See `unicode.toLower` for a version that works for any Unicode character.
|
||||||
|
result = newString(len(s))
|
||||||
|
for i in 0..len(s) - 1:
|
||||||
|
result[i] = toLower(s[i])
|
||||||
|
|
||||||
|
proc toUpper*(c: Char): Char {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuToUpperChar".} =
|
||||||
|
## Converts `c` into upper case. This works only for the letters a-z.
|
||||||
|
## See `unicode.toUpper` for a version that works for any Unicode character.
|
||||||
|
if c in {'a'..'z'}:
|
||||||
|
result = Chr(Ord(c) - (Ord('a') - Ord('A')))
|
||||||
|
else:
|
||||||
|
result = c
|
||||||
|
|
||||||
|
proc toUpper*(s: string): string {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuToUpperStr".} =
|
||||||
|
## Converts `s` into upper case. This works only for the letters a-z.
|
||||||
|
## See `unicode.toUpper` for a version that works for any Unicode character.
|
||||||
|
result = newString(len(s))
|
||||||
|
for i in 0..len(s) - 1:
|
||||||
|
result[i] = toUpper(s[i])
|
||||||
|
|
||||||
|
proc capitalize*(s: string): string {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuCapitalize".} =
|
||||||
|
## Converts the first character of `s` into upper case.
|
||||||
|
## This works only for the letters a-z.
|
||||||
|
result = toUpper(s[0]) & copy(s, 1)
|
||||||
|
|
||||||
|
proc normalize*(s: string): string {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuNormalize".} =
|
||||||
|
## Normalizes the string `s`. That means to convert it to lower case and
|
||||||
|
## remove any '_'. This is needed for Nimrod identifiers for example.
|
||||||
|
result = ""
|
||||||
|
for i in 0..len(s) - 1:
|
||||||
|
if s[i] in {'A'..'Z'}:
|
||||||
|
add result, Chr(Ord(s[i]) + (Ord('a') - Ord('A')))
|
||||||
|
elif s[i] != '_':
|
||||||
|
add result, s[i]
|
||||||
|
|
||||||
|
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
|
||||||
|
rtl, extern: "nsuCmpIgnoreCase".} =
|
||||||
|
## Compares two strings in a case insensitive manner. Returns:
|
||||||
|
##
|
||||||
|
## | 0 iff a == b
|
||||||
|
## | < 0 iff a < b
|
||||||
|
## | > 0 iff a > b
|
||||||
|
var i = 0
|
||||||
|
while i < a.len and i < b.len:
|
||||||
|
result = ord(toLower(a[i])) - ord(toLower(b[i]))
|
||||||
|
if result != 0: return
|
||||||
|
inc(i)
|
||||||
|
result = a.len - b.len
|
||||||
|
|
||||||
|
{.push checks: off, line_trace: off .} # this is a hot-spot in the compiler!
|
||||||
|
# thus we compile without checks here
|
||||||
|
|
||||||
|
proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
|
||||||
|
rtl, extern: "nsuCmpIgnoreStyle".} =
|
||||||
|
## Compares two strings normalized (i.e. case and
|
||||||
|
## underscores do not matter). Returns:
|
||||||
|
##
|
||||||
|
## | 0 iff a == b
|
||||||
|
## | < 0 iff a < b
|
||||||
|
## | > 0 iff a > b
|
||||||
|
var i = 0
|
||||||
|
var j = 0
|
||||||
|
while True:
|
||||||
|
while a[i] == '_': inc(i)
|
||||||
|
while b[j] == '_': inc(j) # BUGFIX: typo
|
||||||
|
var aa = toLower(a[i])
|
||||||
|
var bb = toLower(b[j])
|
||||||
|
result = ord(aa) - ord(bb)
|
||||||
|
if result != 0 or aa == '\0': break
|
||||||
|
inc(i)
|
||||||
|
inc(j)
|
||||||
|
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
proc findNormalized(x: string, inArray: openarray[string]): int =
|
||||||
|
var i = 0
|
||||||
|
while i < high(inArray):
|
||||||
|
if cmpIgnoreStyle(x, inArray[i]) == 0: return i
|
||||||
|
inc(i, 2) # incrementing by 1 would probably result in a
|
||||||
|
# security hole...
|
||||||
|
return -1
|
||||||
|
|
||||||
|
proc addf*(s: var string, formatstr: string, a: openarray[string]) {.
|
||||||
|
noSideEffect, rtl, extern: "nsuAddf".} =
|
||||||
|
## The same as ``add(s, formatstr % a)``, but more efficient.
|
||||||
|
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
|
||||||
|
var i = 0
|
||||||
|
var num = 0
|
||||||
|
while i < len(formatstr):
|
||||||
|
if formatstr[i] == '$':
|
||||||
|
case formatstr[i+1] # again we use the fact that strings
|
||||||
|
# are zero-terminated here
|
||||||
|
of '#':
|
||||||
|
add s, a[num]
|
||||||
|
inc i, 2
|
||||||
|
inc num
|
||||||
|
of '$':
|
||||||
|
add s, '$'
|
||||||
|
inc(i, 2)
|
||||||
|
of '1'..'9':
|
||||||
|
var j = 0
|
||||||
|
inc(i) # skip $
|
||||||
|
while formatstr[i] in Digits:
|
||||||
|
j = j * 10 + ord(formatstr[i]) - ord('0')
|
||||||
|
inc(i)
|
||||||
|
num = j
|
||||||
|
add s, a[j - 1]
|
||||||
|
of '{':
|
||||||
|
var j = i+1
|
||||||
|
while formatstr[j] notin {'\0', '}'}: inc(j)
|
||||||
|
var x = findNormalized(copy(formatstr, i+2, j-1), a)
|
||||||
|
if x >= 0 and x < high(a): add s, a[x+1]
|
||||||
|
else: raise newException(EInvalidValue, "invalid format string")
|
||||||
|
i = j+1
|
||||||
|
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
|
||||||
|
var j = i+1
|
||||||
|
while formatstr[j] in PatternChars: inc(j)
|
||||||
|
var x = findNormalized(copy(formatstr, i+1, j-1), a)
|
||||||
|
if x >= 0 and x < high(a): add s, a[x+1]
|
||||||
|
else: raise newException(EInvalidValue, "invalid format string")
|
||||||
|
i = j
|
||||||
|
else: raise newException(EInvalidValue, "invalid format string")
|
||||||
|
else:
|
||||||
|
add s, formatstr[i]
|
||||||
|
inc(i)
|
||||||
|
|
||||||
|
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuFormatOpenArray".} =
|
||||||
## The `substitution`:idx: operator performs string substitutions in
|
## The `substitution`:idx: operator performs string substitutions in
|
||||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||||
## `string interpolation`:idx:.
|
## `string interpolation`:idx:.
|
||||||
|
|
@ -77,57 +223,38 @@ proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
|
||||||
##
|
##
|
||||||
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
|
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
|
||||||
## raised if an ill-formed format string has been passed to the `%` operator.
|
## raised if an ill-formed format string has been passed to the `%` operator.
|
||||||
|
result = ""
|
||||||
|
addf(result, formatstr, a)
|
||||||
|
|
||||||
proc `%` *(formatstr, a: string): string {.noSideEffect.}
|
proc `%` *(formatstr, a: string): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuFormatSingleElem".} =
|
||||||
## This is the same as ``formatstr % [a]``.
|
## This is the same as ``formatstr % [a]``.
|
||||||
|
return formatstr % [a]
|
||||||
|
|
||||||
proc addf*(s: var string, formatstr: string, a: openarray[string])
|
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
|
||||||
## The same as ``add(s, formatstr % a)``, but more efficient.
|
rtl, extern: "nsuStrip".} =
|
||||||
|
|
||||||
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
|
|
||||||
## Strips whitespace from `s` and returns the resulting string.
|
## Strips whitespace from `s` and returns the resulting string.
|
||||||
## If `leading` is true, leading whitespace is stripped.
|
## If `leading` is true, leading whitespace is stripped.
|
||||||
## If `trailing` is true, trailing whitespace is stripped.
|
## If `trailing` is true, trailing whitespace is stripped.
|
||||||
|
const
|
||||||
|
chars: set[Char] = Whitespace
|
||||||
|
var
|
||||||
|
first = 0
|
||||||
|
last = len(s)-1
|
||||||
|
if leading:
|
||||||
|
while s[first] in chars: inc(first)
|
||||||
|
if trailing:
|
||||||
|
while last >= 0 and s[last] in chars: dec(last)
|
||||||
|
result = copy(s, first, last)
|
||||||
|
|
||||||
proc toLower*(s: string): string {.noSideEffect, procvar.}
|
proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
|
||||||
## Converts `s` into lower case. This works only for the letters A-Z.
|
|
||||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
|
||||||
|
|
||||||
proc toLower*(c: Char): Char {.noSideEffect, procvar.}
|
|
||||||
## Converts `c` into lower case. This works only for the letters A-Z.
|
|
||||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
|
||||||
|
|
||||||
proc toUpper*(s: string): string {.noSideEffect, procvar.}
|
|
||||||
## Converts `s` into upper case. This works only for the letters a-z.
|
|
||||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
|
||||||
|
|
||||||
proc toUpper*(c: Char): Char {.noSideEffect, procvar.}
|
|
||||||
## Converts `c` into upper case. This works only for the letters a-z.
|
|
||||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
|
||||||
|
|
||||||
proc capitalize*(s: string): string {.noSideEffect, procvar.}
|
|
||||||
## Converts the first character of `s` into upper case.
|
|
||||||
## This works only for the letters a-z.
|
|
||||||
|
|
||||||
proc normalize*(s: string): string {.noSideEffect, procvar.}
|
|
||||||
## Normalizes the string `s`. That means to convert it to lower case and
|
|
||||||
## remove any '_'. This is needed for Nimrod identifiers for example.
|
|
||||||
|
|
||||||
proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
|
|
||||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
|
||||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
|
||||||
|
|
||||||
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect.}
|
|
||||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
|
||||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
|
||||||
|
|
||||||
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect.}
|
|
||||||
## Searches for `chars` in `s` starting at position `start`. If `s` contains
|
|
||||||
## none of the characters in `chars`, -1 is returned.
|
|
||||||
|
|
||||||
proc toOctal*(c: char): string
|
|
||||||
## Converts a character `c` to its octal representation. The resulting
|
## Converts a character `c` to its octal representation. The resulting
|
||||||
## string may not have a leading zero. Its length is always exactly 3.
|
## string may not have a leading zero. Its length is always exactly 3.
|
||||||
|
result = newString(3)
|
||||||
|
var val = ord(c)
|
||||||
|
for i in countdown(2, 0):
|
||||||
|
result[i] = Chr(val mod 8 + ord('0'))
|
||||||
|
val = val div 8
|
||||||
|
|
||||||
iterator split*(s: string, seps: set[char] = Whitespace): string =
|
iterator split*(s: string, seps: set[char] = Whitespace): string =
|
||||||
## Splits the string `s` into substrings.
|
## Splits the string `s` into substrings.
|
||||||
|
|
@ -228,86 +355,123 @@ iterator splitLines*(s: string): string =
|
||||||
else: break # was '\0'
|
else: break # was '\0'
|
||||||
first = last
|
first = last
|
||||||
|
|
||||||
proc splitLines*(s: string): seq[string] {.noSideEffect.} =
|
proc splitLines*(s: string): seq[string] {.noSideEffect,
|
||||||
|
rtl, extern: "nsuSplitLines".} =
|
||||||
## The same as the `splitLines` iterator, but is a proc that returns a
|
## The same as the `splitLines` iterator, but is a proc that returns a
|
||||||
## sequence of substrings.
|
## sequence of substrings.
|
||||||
accumulateResult(splitLines(s))
|
accumulateResult(splitLines(s))
|
||||||
|
|
||||||
proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
|
proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
|
||||||
noSideEffect.} =
|
noSideEffect, rtl, extern: "nsuSplitCharSet".} =
|
||||||
## The same as the `split` iterator, but is a proc that returns a
|
## The same as the `split` iterator, but is a proc that returns a
|
||||||
## sequence of substrings.
|
## sequence of substrings.
|
||||||
accumulateResult(split(s, seps))
|
accumulateResult(split(s, seps))
|
||||||
|
|
||||||
proc split*(s: string, sep: char): seq[string] {.noSideEffect.} =
|
proc split*(s: string, sep: char): seq[string] {.noSideEffect,
|
||||||
|
rtl, extern: "nsuSplitChar".} =
|
||||||
## The same as the `split` iterator, but is a proc that returns a sequence
|
## The same as the `split` iterator, but is a proc that returns a sequence
|
||||||
## of substrings.
|
## of substrings.
|
||||||
accumulateResult(split(s, sep))
|
accumulateResult(split(s, sep))
|
||||||
|
|
||||||
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
|
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
## Compares two strings in a case insensitive manner. Returns:
|
rtl, extern: "nsuToHex".} =
|
||||||
##
|
|
||||||
## | 0 iff a == b
|
|
||||||
## | < 0 iff a < b
|
|
||||||
## | > 0 iff a > b
|
|
||||||
|
|
||||||
proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect.}
|
|
||||||
## Compares two strings normalized (i.e. case and
|
|
||||||
## underscores do not matter). Returns:
|
|
||||||
##
|
|
||||||
## | 0 iff a == b
|
|
||||||
## | < 0 iff a < b
|
|
||||||
## | > 0 iff a > b
|
|
||||||
|
|
||||||
proc contains*(s: string, c: char): bool {.noSideEffect.}
|
|
||||||
## Same as ``find(s, c) >= 0``.
|
|
||||||
|
|
||||||
proc contains*(s, sub: string): bool {.noSideEffect.}
|
|
||||||
## Same as ``find(s, sub) >= 0``.
|
|
||||||
|
|
||||||
proc contains*(s: string, chars: set[char]): bool {.noSideEffect.}
|
|
||||||
## Same as ``find(s, chars) >= 0``.
|
|
||||||
|
|
||||||
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.}
|
|
||||||
## Converts `x` to its hexadecimal representation. The resulting string
|
## Converts `x` to its hexadecimal representation. The resulting string
|
||||||
## will be exactly `len` characters long. No prefix like ``0x``
|
## will be exactly `len` characters long. No prefix like ``0x``
|
||||||
## is generated. `x` is treated as an unsigned value.
|
## is generated. `x` is treated as an unsigned value.
|
||||||
|
const
|
||||||
|
HexChars = "0123456789ABCDEF"
|
||||||
|
var
|
||||||
|
shift: BiggestInt
|
||||||
|
result = newString(len)
|
||||||
|
for j in countdown(len-1, 0):
|
||||||
|
result[j] = HexChars[toU32(x shr shift) and 0xF'i32]
|
||||||
|
shift = shift + 4
|
||||||
|
|
||||||
proc intToStr*(x: int, minchars: int = 1): string
|
proc intToStr*(x: int, minchars: int = 1): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuIntToStr".} =
|
||||||
## Converts `x` to its decimal representation. The resulting string
|
## Converts `x` to its decimal representation. The resulting string
|
||||||
## will be minimally `minchars` characters long. This is achieved by
|
## will be minimally `minchars` characters long. This is achieved by
|
||||||
## adding leading zeros.
|
## adding leading zeros.
|
||||||
|
result = $abs(x)
|
||||||
|
for i in 1 .. minchars - len(result):
|
||||||
|
result = '0' & result
|
||||||
|
if x < 0:
|
||||||
|
result = '-' & result
|
||||||
|
|
||||||
proc ParseInt*(s: string): int {.noSideEffect, procvar.}
|
proc ParseInt*(s: string): int {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuParseInt".} =
|
||||||
## Parses a decimal integer value contained in `s`. If `s` is not
|
## Parses a decimal integer value contained in `s`. If `s` is not
|
||||||
## a valid integer, `EInvalidValue` is raised.
|
## a valid integer, `EInvalidValue` is raised.
|
||||||
|
var L = parseutils.parseInt(s, result, 0)
|
||||||
|
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
|
||||||
|
|
||||||
proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar.}
|
proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuParseBiggestInt".} =
|
||||||
## Parses a decimal integer value contained in `s`. If `s` is not
|
## Parses a decimal integer value contained in `s`. If `s` is not
|
||||||
## a valid integer, `EInvalidValue` is raised.
|
## a valid integer, `EInvalidValue` is raised.
|
||||||
|
var L = parseutils.parseBiggestInt(s, result, 0)
|
||||||
|
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
|
||||||
|
|
||||||
proc ParseFloat*(s: string): float {.noSideEffect, procvar.}
|
proc ParseFloat*(s: string): float {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuParseFloat".} =
|
||||||
## Parses a decimal floating point value contained in `s`. If `s` is not
|
## Parses a decimal floating point value contained in `s`. If `s` is not
|
||||||
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
|
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
|
||||||
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
## ``INF``, ``-INF`` are also supported (case insensitive comparison).
|
||||||
|
var L = parseutils.parseFloat(s, result, 0)
|
||||||
|
if L != s.len: raise newException(EInvalidValue, "invalid float: " & s)
|
||||||
|
|
||||||
proc ParseHexInt*(s: string): int {.noSideEffect, procvar.}
|
proc ParseHexInt*(s: string): int {.noSideEffect, procvar,
|
||||||
|
rtl, extern: "nsuParseHexInt".} =
|
||||||
## Parses a hexadecimal integer value contained in `s`. If `s` is not
|
## Parses a hexadecimal integer value contained in `s`. If `s` is not
|
||||||
## a valid integer, `EInvalidValue` is raised. `s` can have one of the
|
## a valid integer, `EInvalidValue` is raised. `s` can have one of the
|
||||||
## following optional prefixes: ``0x``, ``0X``, ``#``.
|
## following optional prefixes: ``0x``, ``0X``, ``#``.
|
||||||
## Underscores within `s` are ignored.
|
## Underscores within `s` are ignored.
|
||||||
|
var i = 0
|
||||||
|
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
|
||||||
|
elif s[i] == '#': inc(i)
|
||||||
|
while true:
|
||||||
|
case s[i]
|
||||||
|
of '_': inc(i)
|
||||||
|
of '0'..'9':
|
||||||
|
result = result shl 4 or (ord(s[i]) - ord('0'))
|
||||||
|
inc(i)
|
||||||
|
of 'a'..'f':
|
||||||
|
result = result shl 4 or (ord(s[i]) - ord('a') + 10)
|
||||||
|
inc(i)
|
||||||
|
of 'A'..'F':
|
||||||
|
result = result shl 4 or (ord(s[i]) - ord('A') + 10)
|
||||||
|
inc(i)
|
||||||
|
of '\0': break
|
||||||
|
else: raise newException(EInvalidValue, "invalid integer: " & s)
|
||||||
|
|
||||||
proc repeatChar*(count: int, c: Char = ' '): string
|
proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuRepeatChar".} =
|
||||||
## Returns a string of length `count` consisting only of
|
## Returns a string of length `count` consisting only of
|
||||||
## the character `c`.
|
## the character `c`.
|
||||||
|
result = newString(count)
|
||||||
|
for i in 0..count-1:
|
||||||
|
result[i] = c
|
||||||
|
|
||||||
proc startsWith*(s, prefix: string): bool {.noSideEffect.}
|
proc startsWith*(s, prefix: string): bool {.noSideEffect,
|
||||||
|
rtl, extern: "nsuStartsWith".} =
|
||||||
## Returns true iff ``s`` starts with ``prefix``.
|
## Returns true iff ``s`` starts with ``prefix``.
|
||||||
## If ``prefix == ""`` true is returned.
|
## 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)
|
||||||
|
|
||||||
proc endsWith*(s, suffix: string): bool {.noSideEffect.}
|
proc endsWith*(s, suffix: string): bool {.noSideEffect,
|
||||||
|
rtl, extern: "nsuEndsWith".} =
|
||||||
## Returns true iff ``s`` ends with ``suffix``.
|
## Returns true iff ``s`` ends with ``suffix``.
|
||||||
## If ``suffix == ""`` true is returned.
|
## If ``suffix == ""`` true is returned.
|
||||||
|
var i = 0
|
||||||
|
var j = len(s) - len(suffix)
|
||||||
|
while i+j <% s.len:
|
||||||
|
if s[i+j] != suffix[i]: return false
|
||||||
|
inc(i)
|
||||||
|
if suffix[i] == '\0': return true
|
||||||
|
|
||||||
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||||
inline.} =
|
inline.} =
|
||||||
|
|
@ -335,30 +499,6 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
||||||
if c notin theSet: return false
|
if c notin theSet: return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc quoteIfContainsWhite*(s: string): string =
|
|
||||||
## returns ``'"' & s & '"'`` if `s` contains a space and does not
|
|
||||||
## start with a quote, else returns `s`
|
|
||||||
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
|
|
||||||
result = '"' & s & '"'
|
|
||||||
else:
|
|
||||||
result = s
|
|
||||||
|
|
||||||
proc startsWith(s, prefix: string): bool =
|
|
||||||
var i = 0
|
|
||||||
while true:
|
|
||||||
if prefix[i] == '\0': return true
|
|
||||||
if s[i] != prefix[i]: return false
|
|
||||||
inc(i)
|
|
||||||
|
|
||||||
proc endsWith(s, suffix: string): bool =
|
|
||||||
var
|
|
||||||
i = 0
|
|
||||||
j = len(s) - len(suffix)
|
|
||||||
while i+j <% s.len:
|
|
||||||
if s[i+j] != suffix[i]: return false
|
|
||||||
inc(i)
|
|
||||||
if suffix[i] == '\0': return true
|
|
||||||
|
|
||||||
# 012345
|
# 012345
|
||||||
# 345
|
# 345
|
||||||
|
|
||||||
|
|
@ -373,112 +513,10 @@ when false:
|
||||||
if result >= 0: return -2 # ambiguous
|
if result >= 0: return -2 # ambiguous
|
||||||
result = i
|
result = i
|
||||||
|
|
||||||
proc repeatChar(count: int, c: Char = ' '): string =
|
|
||||||
result = newString(count)
|
|
||||||
for i in 0..count-1:
|
|
||||||
result[i] = c
|
|
||||||
|
|
||||||
proc intToStr(x: int, minchars: int = 1): string =
|
|
||||||
result = $abs(x)
|
|
||||||
for i in 1 .. minchars - len(result):
|
|
||||||
result = '0' & result
|
|
||||||
if x < 0:
|
|
||||||
result = '-' & result
|
|
||||||
|
|
||||||
proc toOctal(c: char): string =
|
|
||||||
result = newString(3)
|
|
||||||
var val = ord(c)
|
|
||||||
for i in countdown(2, 0):
|
|
||||||
result[i] = Chr(val mod 8 + ord('0'))
|
|
||||||
val = val div 8
|
|
||||||
|
|
||||||
proc `%`(formatstr: string, a: string): string =
|
|
||||||
return formatstr % [a]
|
|
||||||
|
|
||||||
proc findNormalized(x: string, inArray: openarray[string]): int =
|
|
||||||
var i = 0
|
|
||||||
while i < high(inArray):
|
|
||||||
if cmpIgnoreStyle(x, inArray[i]) == 0: return i
|
|
||||||
inc(i, 2) # incrementing by 1 would probably result in a
|
|
||||||
# security hole...
|
|
||||||
return -1
|
|
||||||
|
|
||||||
proc addf(s: var string, formatstr: string, a: openarray[string]) =
|
|
||||||
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
|
|
||||||
var i = 0
|
|
||||||
var num = 0
|
|
||||||
while i < len(formatstr):
|
|
||||||
if formatstr[i] == '$':
|
|
||||||
case formatstr[i+1] # again we use the fact that strings
|
|
||||||
# are zero-terminated here
|
|
||||||
of '#':
|
|
||||||
add s, a[num]
|
|
||||||
inc i, 2
|
|
||||||
inc num
|
|
||||||
of '$':
|
|
||||||
add s, '$'
|
|
||||||
inc(i, 2)
|
|
||||||
of '1'..'9':
|
|
||||||
var j = 0
|
|
||||||
inc(i) # skip $
|
|
||||||
while formatstr[i] in Digits:
|
|
||||||
j = j * 10 + ord(formatstr[i]) - ord('0')
|
|
||||||
inc(i)
|
|
||||||
num = j
|
|
||||||
add s, a[j - 1]
|
|
||||||
of '{':
|
|
||||||
var j = i+1
|
|
||||||
while formatstr[j] notin {'\0', '}'}: inc(j)
|
|
||||||
var x = findNormalized(copy(formatstr, i+2, j-1), a)
|
|
||||||
if x >= 0 and x < high(a): add s, a[x+1]
|
|
||||||
else: raise newException(EInvalidValue, "invalid format string")
|
|
||||||
i = j+1
|
|
||||||
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
|
|
||||||
var j = i+1
|
|
||||||
while formatstr[j] in PatternChars: inc(j)
|
|
||||||
var x = findNormalized(copy(formatstr, i+1, j-1), a)
|
|
||||||
if x >= 0 and x < high(a): add s, a[x+1]
|
|
||||||
else: raise newException(EInvalidValue, "invalid format string")
|
|
||||||
i = j
|
|
||||||
else: raise newException(EInvalidValue, "invalid format string")
|
|
||||||
else:
|
|
||||||
add s, formatstr[i]
|
|
||||||
inc(i)
|
|
||||||
|
|
||||||
proc `%`(formatstr: string, a: openarray[string]): string =
|
|
||||||
result = ""
|
|
||||||
addf(result, formatstr, a)
|
|
||||||
|
|
||||||
proc cmpIgnoreCase(a, b: string): int =
|
|
||||||
var i = 0
|
|
||||||
while i < a.len and i < b.len:
|
|
||||||
result = ord(toLower(a[i])) - ord(toLower(b[i]))
|
|
||||||
if result != 0: return
|
|
||||||
inc(i)
|
|
||||||
result = a.len - b.len
|
|
||||||
|
|
||||||
|
|
||||||
{.push checks: off, line_trace: off .} # this is a hot-spot in the compiler!
|
|
||||||
# thus we compile without checks here
|
|
||||||
|
|
||||||
proc cmpIgnoreStyle(a, b: string): int =
|
|
||||||
var i = 0
|
|
||||||
var j = 0
|
|
||||||
while True:
|
|
||||||
while a[i] == '_': inc(i)
|
|
||||||
while b[j] == '_': inc(j) # BUGFIX: typo
|
|
||||||
var aa = toLower(a[i])
|
|
||||||
var bb = toLower(b[j])
|
|
||||||
result = ord(aa) - ord(bb)
|
|
||||||
if result != 0 or aa == '\0': break
|
|
||||||
inc(i)
|
|
||||||
inc(j)
|
|
||||||
|
|
||||||
{.pop.}
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
proc join*(a: openArray[string], sep: string): string =
|
proc join*(a: openArray[string], sep: string): string {.
|
||||||
|
noSideEffect, rtl, extern: "nsuJoinSep".} =
|
||||||
## concatenates all strings in `a` separating them with `sep`.
|
## concatenates all strings in `a` separating them with `sep`.
|
||||||
if len(a) > 0:
|
if len(a) > 0:
|
||||||
var L = sep.len * (a.len-1)
|
var L = sep.len * (a.len-1)
|
||||||
|
|
@ -492,7 +530,8 @@ proc join*(a: openArray[string], sep: string): string =
|
||||||
else:
|
else:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
proc join*(a: openArray[string]): string =
|
proc join*(a: openArray[string]): string {.
|
||||||
|
noSideEffect, rtl, extern: "nsuJoin".} =
|
||||||
## concatenates all strings in `a`.
|
## concatenates all strings in `a`.
|
||||||
if len(a) > 0:
|
if len(a) > 0:
|
||||||
var L = 0
|
var L = 0
|
||||||
|
|
@ -503,51 +542,6 @@ proc join*(a: openArray[string]): string =
|
||||||
else:
|
else:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
proc strip(s: string, leading = true, trailing = true): string =
|
|
||||||
const
|
|
||||||
chars: set[Char] = Whitespace
|
|
||||||
var
|
|
||||||
first = 0
|
|
||||||
last = len(s)-1
|
|
||||||
if leading:
|
|
||||||
while s[first] in chars: inc(first)
|
|
||||||
if trailing:
|
|
||||||
while last >= 0 and s[last] in chars: dec(last)
|
|
||||||
result = copy(s, first, last)
|
|
||||||
|
|
||||||
proc toLower(c: Char): Char =
|
|
||||||
if c in {'A'..'Z'}:
|
|
||||||
result = chr(ord(c) + (ord('a') - ord('A')))
|
|
||||||
else:
|
|
||||||
result = c
|
|
||||||
|
|
||||||
proc toLower(s: string): string =
|
|
||||||
result = newString(len(s))
|
|
||||||
for i in 0..len(s) - 1:
|
|
||||||
result[i] = toLower(s[i])
|
|
||||||
|
|
||||||
proc toUpper(c: Char): Char =
|
|
||||||
if c in {'a'..'z'}:
|
|
||||||
result = Chr(Ord(c) - (Ord('a') - Ord('A')))
|
|
||||||
else:
|
|
||||||
result = c
|
|
||||||
|
|
||||||
proc toUpper(s: string): string =
|
|
||||||
result = newString(len(s))
|
|
||||||
for i in 0..len(s) - 1:
|
|
||||||
result[i] = toUpper(s[i])
|
|
||||||
|
|
||||||
proc capitalize(s: string): string =
|
|
||||||
result = toUpper(s[0]) & copy(s, 1)
|
|
||||||
|
|
||||||
proc normalize(s: string): string =
|
|
||||||
result = ""
|
|
||||||
for i in 0..len(s) - 1:
|
|
||||||
if s[i] in {'A'..'Z'}:
|
|
||||||
add result, Chr(Ord(s[i]) + (Ord('a') - Ord('A')))
|
|
||||||
elif s[i] != '_':
|
|
||||||
add result, s[i]
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TSkipTable = array[Char, int]
|
TSkipTable = array[Char, int]
|
||||||
|
|
||||||
|
|
@ -571,31 +565,52 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||||
inc(j, a[s[j+m]])
|
inc(j, a[s[j+m]])
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc find(s, sub: string, start: int = 0): int =
|
proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
|
||||||
|
rtl, extern: "nsuFindStr".} =
|
||||||
|
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||||
|
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||||
var a: TSkipTable
|
var a: TSkipTable
|
||||||
preprocessSub(sub, a)
|
preprocessSub(sub, a)
|
||||||
result = findAux(s, sub, start, a)
|
result = findAux(s, sub, start, a)
|
||||||
|
|
||||||
proc find(s: string, sub: char, start: int = 0): int =
|
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect,
|
||||||
|
rtl, extern: "nsuFindChar".} =
|
||||||
|
## Searches for `sub` in `s` starting at position `start`. Searching is
|
||||||
|
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||||
for i in start..len(s)-1:
|
for i in start..len(s)-1:
|
||||||
if sub == s[i]: return i
|
if sub == s[i]: return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc find(s: string, chars: set[char], start: int = 0): int =
|
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect,
|
||||||
|
rtl, extern: "nsuFindCharSet".} =
|
||||||
|
## Searches for `chars` in `s` starting at position `start`. If `s` contains
|
||||||
|
## none of the characters in `chars`, -1 is returned.
|
||||||
for i in start..s.len-1:
|
for i in start..s.len-1:
|
||||||
if s[i] in chars: return i
|
if s[i] in chars: return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc contains(s: string, chars: set[char]): bool =
|
proc quoteIfContainsWhite*(s: string): string =
|
||||||
return find(s, chars) >= 0
|
## returns ``'"' & s & '"'`` if `s` contains a space and does not
|
||||||
|
## start with a quote, else returns `s`
|
||||||
|
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
|
||||||
|
result = '"' & s & '"'
|
||||||
|
else:
|
||||||
|
result = s
|
||||||
|
|
||||||
proc contains(s: string, c: char): bool =
|
proc contains*(s: string, c: char): bool {.noSideEffect.} =
|
||||||
|
## Same as ``find(s, c) >= 0``.
|
||||||
return find(s, c) >= 0
|
return find(s, c) >= 0
|
||||||
|
|
||||||
proc contains(s, sub: string): bool =
|
proc contains*(s, sub: string): bool {.noSideEffect.} =
|
||||||
|
## Same as ``find(s, sub) >= 0``.
|
||||||
return find(s, sub) >= 0
|
return find(s, sub) >= 0
|
||||||
|
|
||||||
proc replace*(s, sub, by: string): string =
|
proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
|
||||||
|
## Same as ``find(s, chars) >= 0``.
|
||||||
|
return find(s, chars) >= 0
|
||||||
|
|
||||||
|
proc replace*(s, sub, by: string): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuReplaceStr".} =
|
||||||
## Replaces `sub` in `s` by the string `by`.
|
## Replaces `sub` in `s` by the string `by`.
|
||||||
var a: TSkipTable
|
var a: TSkipTable
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -610,7 +625,8 @@ proc replace*(s, sub, by: string): string =
|
||||||
# copy the rest:
|
# copy the rest:
|
||||||
add result, copy(s, i)
|
add result, copy(s, i)
|
||||||
|
|
||||||
proc replace*(s: string, sub, by: char): string =
|
proc replace*(s: string, sub, by: char): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuReplaceChar".} =
|
||||||
## optimized version for characters.
|
## optimized version for characters.
|
||||||
result = newString(s.len)
|
result = newString(s.len)
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -619,7 +635,8 @@ proc replace*(s: string, sub, by: char): string =
|
||||||
else: result[i] = s[i]
|
else: result[i] = s[i]
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc delete*(s: var string, first, last: int) =
|
proc delete*(s: var string, first, last: int) {.noSideEffect,
|
||||||
|
rtl, extern: "nsuDelete".} =
|
||||||
## Deletes in `s` the characters at position `first`..`last`. This modifies
|
## Deletes in `s` the characters at position `first`..`last`. This modifies
|
||||||
## `s` itself, it does not return a copy.
|
## `s` itself, it does not return a copy.
|
||||||
var i = first
|
var i = first
|
||||||
|
|
@ -631,27 +648,12 @@ proc delete*(s: var string, first, last: int) =
|
||||||
inc(j)
|
inc(j)
|
||||||
setlen(s, newLen)
|
setlen(s, newLen)
|
||||||
|
|
||||||
# parsing numbers:
|
proc ParseOctInt*(s: string): int {.noSideEffect,
|
||||||
|
rtl, extern: "nsuParseOctInt".} =
|
||||||
proc toHex(x: BiggestInt, len: int): string =
|
## Parses an octal integer value contained in `s`. If `s` is not
|
||||||
const
|
## a valid integer, `EInvalidValue` is raised. `s` can have one of the
|
||||||
HexChars = "0123456789ABCDEF"
|
## following optional prefixes: ``0o``, ``0O``.
|
||||||
var
|
## Underscores within `s` are ignored.
|
||||||
shift: BiggestInt
|
|
||||||
result = newString(len)
|
|
||||||
for j in countdown(len-1, 0):
|
|
||||||
result[j] = HexChars[toU32(x shr shift) and 0xF'i32]
|
|
||||||
shift = shift + 4
|
|
||||||
|
|
||||||
proc parseInt(s: string): int =
|
|
||||||
var L = parseutils.parseInt(s, result, 0)
|
|
||||||
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
|
|
||||||
|
|
||||||
proc ParseBiggestInt(s: string): biggestInt =
|
|
||||||
var L = parseutils.parseBiggestInt(s, result, 0)
|
|
||||||
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
|
|
||||||
|
|
||||||
proc ParseOctInt*(s: string): int =
|
|
||||||
var i = 0
|
var i = 0
|
||||||
if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
|
if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -663,30 +665,8 @@ proc ParseOctInt*(s: string): int =
|
||||||
of '\0': break
|
of '\0': break
|
||||||
else: raise newException(EInvalidValue, "invalid integer: " & s)
|
else: raise newException(EInvalidValue, "invalid integer: " & s)
|
||||||
|
|
||||||
proc ParseHexInt(s: string): int =
|
proc toOct*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
var i = 0
|
rtl, extern: "nsuToOct".} =
|
||||||
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
|
|
||||||
elif s[i] == '#': inc(i)
|
|
||||||
while true:
|
|
||||||
case s[i]
|
|
||||||
of '_': inc(i)
|
|
||||||
of '0'..'9':
|
|
||||||
result = result shl 4 or (ord(s[i]) - ord('0'))
|
|
||||||
inc(i)
|
|
||||||
of 'a'..'f':
|
|
||||||
result = result shl 4 or (ord(s[i]) - ord('a') + 10)
|
|
||||||
inc(i)
|
|
||||||
of 'A'..'F':
|
|
||||||
result = result shl 4 or (ord(s[i]) - ord('A') + 10)
|
|
||||||
inc(i)
|
|
||||||
of '\0': break
|
|
||||||
else: raise newException(EInvalidValue, "invalid integer: " & s)
|
|
||||||
|
|
||||||
proc ParseFloat(s: string): float =
|
|
||||||
var L = parseutils.parseFloat(s, result, 0)
|
|
||||||
if L != s.len: raise newException(EInvalidValue, "invalid float: " & s)
|
|
||||||
|
|
||||||
proc toOct*(x: BiggestInt, len: int): string =
|
|
||||||
## converts `x` into its octal representation. The resulting string is
|
## converts `x` into its octal representation. The resulting string is
|
||||||
## always `len` characters long. No leading ``0o`` prefix is generated.
|
## always `len` characters long. No leading ``0o`` prefix is generated.
|
||||||
var
|
var
|
||||||
|
|
@ -699,7 +679,8 @@ proc toOct*(x: BiggestInt, len: int): string =
|
||||||
shift = shift + 3
|
shift = shift + 3
|
||||||
mask = mask shl 3
|
mask = mask shl 3
|
||||||
|
|
||||||
proc toBin*(x: BiggestInt, len: int): string =
|
proc toBin*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuToBin".} =
|
||||||
## converts `x` into its binary representation. The resulting string is
|
## converts `x` into its binary representation. The resulting string is
|
||||||
## always `len` characters long. No leading ``0b`` prefix is generated.
|
## always `len` characters long. No leading ``0b`` prefix is generated.
|
||||||
var
|
var
|
||||||
|
|
@ -712,7 +693,8 @@ proc toBin*(x: BiggestInt, len: int): string =
|
||||||
shift = shift + 1
|
shift = shift + 1
|
||||||
mask = mask shl 1
|
mask = mask shl 1
|
||||||
|
|
||||||
proc insertSep*(s: string, sep = '_', digits = 3): string =
|
proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuInsertSep".} =
|
||||||
## inserts the separator `sep` after `digits` digits from right to left.
|
## inserts the separator `sep` after `digits` digits from right to left.
|
||||||
## Even though the algorithm works with any string `s`, it is only useful
|
## Even though the algorithm works with any string `s`, it is only useful
|
||||||
## if `s` contains a number.
|
## if `s` contains a number.
|
||||||
|
|
@ -730,7 +712,8 @@ proc insertSep*(s: string, sep = '_', digits = 3): string =
|
||||||
inc(j)
|
inc(j)
|
||||||
dec(L)
|
dec(L)
|
||||||
|
|
||||||
proc escape*(s: string, prefix = "\"", suffix = "\""): string =
|
proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
|
rtl, extern: "nsuEscape".} =
|
||||||
## Escapes a string `s`. This does these operations (at the same time):
|
## Escapes a string `s`. This does these operations (at the same time):
|
||||||
## * replaces any ``\`` by ``\\``
|
## * replaces any ``\`` by ``\\``
|
||||||
## * replaces any ``'`` by ``\'``
|
## * replaces any ``'`` by ``\'``
|
||||||
|
|
@ -752,7 +735,8 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string =
|
||||||
else: add(result, c)
|
else: add(result, c)
|
||||||
add(result, suffix)
|
add(result, suffix)
|
||||||
|
|
||||||
proc validEmailAddress*(s: string): bool =
|
proc validEmailAddress*(s: string): bool {.noSideEffect,
|
||||||
|
rtl, extern: "nsuValidEmailAddress".} =
|
||||||
## returns true if `s` seems to be a valid e-mail address.
|
## returns true if `s` seems to be a valid e-mail address.
|
||||||
## The checking also uses a domain list.
|
## The checking also uses a domain list.
|
||||||
## Note: This will be moved into another module soon.
|
## Note: This will be moved into another module soon.
|
||||||
|
|
@ -779,7 +763,8 @@ proc validEmailAddress*(s: string): bool =
|
||||||
"aero", "jobs", "museum": return true
|
"aero", "jobs", "museum": return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
proc validIdentifier*(s: string): bool =
|
proc validIdentifier*(s: string): bool {.noSideEffect,
|
||||||
|
rtl, extern: "nsuValidIdentifier".} =
|
||||||
## returns true if `s` is a valid identifier. A valid identifier starts
|
## returns true if `s` is a valid identifier. A valid identifier starts
|
||||||
## with a character of the set `IdentStartChars` and is followed by any
|
## with a character of the set `IdentStartChars` and is followed by any
|
||||||
## number of characters of the set `IdentChars`.
|
## number of characters of the set `IdentChars`.
|
||||||
|
|
@ -788,7 +773,8 @@ proc validIdentifier*(s: string): bool =
|
||||||
if s[i] notin IdentChars: return false
|
if s[i] notin IdentChars: return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc editDistance*(a, b: string): int =
|
proc editDistance*(a, b: string): int {.noSideEffect,
|
||||||
|
rtl, extern: "nsuEditDistance".} =
|
||||||
## returns the edit distance between `a` and `b`. This uses the Levenshtein
|
## returns the edit distance between `a` and `b`. This uses the Levenshtein
|
||||||
## distance algorithm with only a linear memory overhead. This implementation
|
## distance algorithm with only a linear memory overhead. This implementation
|
||||||
## is highly optimized!
|
## is highly optimized!
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@
|
||||||
## This module contains routines and types for dealing with time.
|
## This module contains routines and types for dealing with time.
|
||||||
## This module is available for the ECMAScript target.
|
## This module is available for the ECMAScript target.
|
||||||
|
|
||||||
{.push debugger:off .} # the user does not want to trace a part
|
{.push debugger:off.} # the user does not want to trace a part
|
||||||
# of the standard library!
|
# of the standard library!
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils
|
strutils
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
TMonth* = enum ## represents a month
|
TMonth* = enum ## represents a month
|
||||||
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
|
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
|
||||||
|
|
@ -125,20 +127,17 @@ proc `$` *(timeInfo: TTimeInfo): string
|
||||||
proc `$` *(time: TTime): string
|
proc `$` *(time: TTime): string
|
||||||
## converts a calendar time to a string representation.
|
## converts a calendar time to a string representation.
|
||||||
|
|
||||||
proc getDateStr*(): string
|
proc `-` *(a, b: TTime): int64{.
|
||||||
## gets the current date as a string of the format ``YYYY-MM-DD``.
|
rtl, extern: "ntDiffTime".}
|
||||||
|
|
||||||
proc getClockStr*(): string
|
|
||||||
## gets the current clock time as a string of the format ``HH:MM:SS``.
|
|
||||||
|
|
||||||
proc `-` *(a, b: TTime): int64
|
|
||||||
## computes the difference of two calendar times. Result is in seconds.
|
## computes the difference of two calendar times. Result is in seconds.
|
||||||
|
|
||||||
proc `<` * (a, b: TTime): bool =
|
proc `<` * (a, b: TTime): bool {.
|
||||||
|
rtl, extern: "ntLtTime".} =
|
||||||
## returns true iff ``a < b``, that is iff a happened before b.
|
## returns true iff ``a < b``, that is iff a happened before b.
|
||||||
result = a - b < 0
|
result = a - b < 0
|
||||||
|
|
||||||
proc `<=` * (a, b: TTime): bool =
|
proc `<=` * (a, b: TTime): bool {.
|
||||||
|
rtl, extern: "ntLeTime".}=
|
||||||
## returns true iff ``a <= b``.
|
## returns true iff ``a <= b``.
|
||||||
result = a - b <= 0
|
result = a - b <= 0
|
||||||
|
|
||||||
|
|
@ -147,12 +146,12 @@ proc getStartMilsecs*(): int {.deprecated.}
|
||||||
## version 0.8.10.** Use ``realTime`` or ``cpuTime`` instead.
|
## version 0.8.10.** Use ``realTime`` or ``cpuTime`` instead.
|
||||||
|
|
||||||
when not defined(ECMAScript):
|
when not defined(ECMAScript):
|
||||||
proc epochTime*(): float
|
proc epochTime*(): float {.rtl, extern: "nt$1".}
|
||||||
## gets time after the UNIX epoch (1970) in seconds. It is a float
|
## gets time after the UNIX epoch (1970) in seconds. It is a float
|
||||||
## because sub-second resolution is likely to be supported (depending
|
## because sub-second resolution is likely to be supported (depending
|
||||||
## on the hardware/OS).
|
## on the hardware/OS).
|
||||||
|
|
||||||
proc cpuTime*(): float
|
proc cpuTime*(): float {.rtl, extern: "nt$1".}
|
||||||
## gets time spent that the CPU spent to run the current process in
|
## gets time spent that the CPU spent to run the current process in
|
||||||
## seconds. This may be more useful for benchmarking than ``epochTime``.
|
## seconds. This may be more useful for benchmarking than ``epochTime``.
|
||||||
## However, it may measure the real time instead (depending on the OS).
|
## However, it may measure the real time instead (depending on the OS).
|
||||||
|
|
@ -227,8 +226,9 @@ when not defined(ECMAScript):
|
||||||
result.yearday = t.yearday
|
result.yearday = t.yearday
|
||||||
result.isdst = -1
|
result.isdst = -1
|
||||||
|
|
||||||
proc `-` (a, b: TTime): int64 =
|
when not defined(useNimRtl):
|
||||||
return toBiggestInt(difftime(a, b))
|
proc `-` (a, b: TTime): int64 =
|
||||||
|
return toBiggestInt(difftime(a, b))
|
||||||
|
|
||||||
proc getStartMilsecs(): int =
|
proc getStartMilsecs(): int =
|
||||||
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(clock())
|
#echo "clocks per sec: ", clocksPerSec, "clock: ", int(clock())
|
||||||
|
|
@ -290,24 +290,25 @@ when not defined(ECMAScript):
|
||||||
## converts a Windows time to a UNIX `TTime` (``time_t``)
|
## converts a Windows time to a UNIX `TTime` (``time_t``)
|
||||||
result = TTime((t - epochDiff) div rateDiff)
|
result = TTime((t - epochDiff) div rateDiff)
|
||||||
|
|
||||||
proc epochTime(): float =
|
when not defined(useNimRtl):
|
||||||
when defined(posix):
|
proc epochTime(): float =
|
||||||
var a: Ttimeval
|
when defined(posix):
|
||||||
posix_gettimeofday(a)
|
var a: Ttimeval
|
||||||
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.001
|
posix_gettimeofday(a)
|
||||||
# why 0.001 instead of 0.00_0001? I don't know.
|
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.001
|
||||||
elif defined(windows):
|
# why 0.001 instead of 0.00_0001? I don't know.
|
||||||
var f: winlean.Filetime
|
elif defined(windows):
|
||||||
GetSystemTimeAsFileTime(f)
|
var f: winlean.Filetime
|
||||||
var i64 = rdFileTime(f) - epochDiff
|
GetSystemTimeAsFileTime(f)
|
||||||
var secs = i64 div rateDiff
|
var i64 = rdFileTime(f) - epochDiff
|
||||||
var subsecs = i64 mod rateDiff
|
var secs = i64 div rateDiff
|
||||||
result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
|
var subsecs = i64 mod rateDiff
|
||||||
else:
|
result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
|
||||||
{.error: "unknown OS".}
|
else:
|
||||||
|
{.error: "unknown OS".}
|
||||||
|
|
||||||
proc cpuTime(): float =
|
proc cpuTime(): float =
|
||||||
result = toFloat(int(clock())) / toFloat(clocksPerSec)
|
result = toFloat(int(clock())) / toFloat(clocksPerSec)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc getTime(): TTime {.importc: "new Date", nodecl.}
|
proc getTime(): TTime {.importc: "new Date", nodecl.}
|
||||||
|
|
@ -358,12 +359,15 @@ else:
|
||||||
## get the miliseconds from the start of the program
|
## get the miliseconds from the start of the program
|
||||||
return int(getTime() - startMilsecs)
|
return int(getTime() - startMilsecs)
|
||||||
|
|
||||||
proc getDateStr(): string =
|
|
||||||
|
proc getDateStr*(): string {.rtl, extern: "nt$1".} =
|
||||||
|
## gets the current date as a string of the format ``YYYY-MM-DD``.
|
||||||
var ti = getLocalTime(getTime())
|
var ti = getLocalTime(getTime())
|
||||||
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
|
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
|
||||||
'-' & intToStr(ti.monthDay, 2)
|
'-' & intToStr(ti.monthDay, 2)
|
||||||
|
|
||||||
proc getClockStr(): string =
|
proc getClockStr*(): string {.rtl, extern: "nt$1".} =
|
||||||
|
## gets the current clock time as a string of the format ``HH:MM:SS``.
|
||||||
var ti = getLocalTime(getTime())
|
var ti = getLocalTime(getTime())
|
||||||
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
|
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
|
||||||
':' & intToStr(ti.second, 2)
|
':' & intToStr(ti.second, 2)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
{.deadCodeElim: on.}
|
{.deadCodeElim: on.}
|
||||||
|
|
||||||
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
irune = int # underlying type of TRune
|
irune = int # underlying type of TRune
|
||||||
TRune* = distinct irune ## type that can hold any Unicode character
|
TRune* = distinct irune ## type that can hold any Unicode character
|
||||||
|
|
@ -22,7 +24,7 @@ proc `==`*(a, b: TRune): bool {.borrow.}
|
||||||
|
|
||||||
template ones(n: expr): expr = ((1 shl n)-1)
|
template ones(n: expr): expr = ((1 shl n)-1)
|
||||||
|
|
||||||
proc runeLen*(s: string): int =
|
proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
|
||||||
## returns the number of Unicode characters of the string `s`.
|
## returns the number of Unicode characters of the string `s`.
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < len(s):
|
while i < len(s):
|
||||||
|
|
@ -75,7 +77,7 @@ proc runeAt*(s: string, i: int): TRune =
|
||||||
## returns the unicode character in `s` at byte index `i`
|
## returns the unicode character in `s` at byte index `i`
|
||||||
fastRuneAt(s, i, result, false)
|
fastRuneAt(s, i, result, false)
|
||||||
|
|
||||||
proc toUTF8*(c: TRune): string =
|
proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
|
||||||
## converts a rune into its UTF8 representation
|
## converts a rune into its UTF8 representation
|
||||||
var i = irune(c)
|
var i = irune(c)
|
||||||
if i <=% 127:
|
if i <=% 127:
|
||||||
|
|
@ -1073,7 +1075,7 @@ proc binarySearch(c: irune, tab: openArray[iRune], len, stride: int): int =
|
||||||
return t
|
return t
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc toLower*(c: TRune): TRune =
|
proc toLower*(c: TRune): TRune {.rtl, extern: "nuc$1".} =
|
||||||
## Converts `c` into lower case. This works for any Unicode character.
|
## Converts `c` into lower case. This works for any Unicode character.
|
||||||
## If possible, prefer `toLower` over `toUpper`.
|
## If possible, prefer `toLower` over `toUpper`.
|
||||||
var c = irune(c)
|
var c = irune(c)
|
||||||
|
|
@ -1085,7 +1087,7 @@ proc toLower*(c: TRune): TRune =
|
||||||
return TRune(c + toLowerSinglets[p+1] - 500)
|
return TRune(c + toLowerSinglets[p+1] - 500)
|
||||||
return TRune(c)
|
return TRune(c)
|
||||||
|
|
||||||
proc toUpper*(c: TRune): TRune =
|
proc toUpper*(c: TRune): TRune {.rtl, extern: "nuc$1".} =
|
||||||
## Converts `c` into upper case. This works for any Unicode character.
|
## Converts `c` into upper case. This works for any Unicode character.
|
||||||
## If possible, prefer `toLower` over `toUpper`.
|
## If possible, prefer `toLower` over `toUpper`.
|
||||||
var c = irune(c)
|
var c = irune(c)
|
||||||
|
|
@ -1097,14 +1099,14 @@ proc toUpper*(c: TRune): TRune =
|
||||||
return TRune(c + toUpperSinglets[p+1] - 500)
|
return TRune(c + toUpperSinglets[p+1] - 500)
|
||||||
return TRune(c)
|
return TRune(c)
|
||||||
|
|
||||||
proc toTitle*(c: TRune): TRune =
|
proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1".} =
|
||||||
var c = irune(c)
|
var c = irune(c)
|
||||||
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
|
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
|
||||||
if p >= 0 and c == toTitleSinglets[p]:
|
if p >= 0 and c == toTitleSinglets[p]:
|
||||||
return TRune(c + toTitleSinglets[p+1] - 500)
|
return TRune(c + toTitleSinglets[p+1] - 500)
|
||||||
return TRune(c)
|
return TRune(c)
|
||||||
|
|
||||||
proc isLower*(c: TRune): bool =
|
proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1".} =
|
||||||
## returns true iff `c` is a lower case Unicode character
|
## returns true iff `c` is a lower case Unicode character
|
||||||
## If possible, prefer `isLower` over `isUpper`.
|
## If possible, prefer `isLower` over `isUpper`.
|
||||||
var c = irune(c)
|
var c = irune(c)
|
||||||
|
|
@ -1116,7 +1118,7 @@ proc isLower*(c: TRune): bool =
|
||||||
if p >= 0 and c == toUpperSinglets[p]:
|
if p >= 0 and c == toUpperSinglets[p]:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc isUpper*(c: TRune): bool =
|
proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1".} =
|
||||||
## returns true iff `c` is a upper case Unicode character
|
## returns true iff `c` is a upper case Unicode character
|
||||||
## If possible, prefer `isLower` over `isUpper`.
|
## If possible, prefer `isLower` over `isUpper`.
|
||||||
var c = irune(c)
|
var c = irune(c)
|
||||||
|
|
@ -1128,7 +1130,7 @@ proc isUpper*(c: TRune): bool =
|
||||||
if p >= 0 and c == toLowerSinglets[p]:
|
if p >= 0 and c == toLowerSinglets[p]:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc isAlpha*(c: TRune): bool =
|
proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1".} =
|
||||||
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
|
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
|
||||||
if isUpper(c) or isLower(c):
|
if isUpper(c) or isLower(c):
|
||||||
return true
|
return true
|
||||||
|
|
@ -1140,10 +1142,10 @@ proc isAlpha*(c: TRune): bool =
|
||||||
if p >= 0 and c == alphaSinglets[p]:
|
if p >= 0 and c == alphaSinglets[p]:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc isTitle*(c: TRune): bool =
|
proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1".} =
|
||||||
return isUpper(c) and isLower(c)
|
return isUpper(c) and isLower(c)
|
||||||
|
|
||||||
proc isWhiteSpace*(c: TRune): bool =
|
proc isWhiteSpace*(c: TRune): bool {.rtl, extern: "nuc$1".} =
|
||||||
## returns true iff `c` is a Unicode whitespace character
|
## returns true iff `c` is a Unicode whitespace character
|
||||||
var c = irune(c)
|
var c = irune(c)
|
||||||
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
|
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
|
||||||
|
|
@ -1159,7 +1161,7 @@ iterator runes*(s: string): TRune =
|
||||||
fastRuneAt(s, i, result, true)
|
fastRuneAt(s, i, result, true)
|
||||||
yield result
|
yield result
|
||||||
|
|
||||||
proc cmpRunesIgnoreCase*(a, b: string): int =
|
proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1".} =
|
||||||
## compares two UTF8 strings and ignores the case. Returns:
|
## compares two UTF8 strings and ignores the case. Returns:
|
||||||
##
|
##
|
||||||
## | 0 iff a == b
|
## | 0 iff a == b
|
||||||
|
|
@ -1175,6 +1177,3 @@ proc cmpRunesIgnoreCase*(a, b: string): int =
|
||||||
result = irune(toLower(ar)) - irune(toLower(br))
|
result = irune(toLower(ar)) - irune(toLower(br))
|
||||||
if result != 0: return
|
if result != 0: return
|
||||||
result = a.len - b.len
|
result = a.len - b.len
|
||||||
|
|
||||||
#proc substringAt*(s, sub: string, start: int): int =
|
|
||||||
#
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
## explicitly. Because of this there cannot be a user-defined module named
|
## explicitly. Because of this there cannot be a user-defined module named
|
||||||
## ``system``.
|
## ``system``.
|
||||||
|
|
||||||
{.push hints: off.}
|
{.push hints: on.}
|
||||||
|
|
||||||
type
|
type
|
||||||
int* {.magic: Int.} ## default integer type; bitwidth depends on
|
int* {.magic: Int.} ## default integer type; bitwidth depends on
|
||||||
|
|
@ -720,25 +720,6 @@ const
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
include "system/cgprocs"
|
include "system/cgprocs"
|
||||||
|
|
||||||
when not defined(ECMAScript):
|
|
||||||
{.push overflow_checks:off}
|
|
||||||
proc add* (x: var string, y: cstring) =
|
|
||||||
var i = 0
|
|
||||||
while y[i] != '\0':
|
|
||||||
add(x, y[i])
|
|
||||||
inc(i)
|
|
||||||
{.pop.}
|
|
||||||
else:
|
|
||||||
proc add* (x: var string, y: cstring) {.pure.} =
|
|
||||||
asm """
|
|
||||||
var len = `x`[0].length-1;
|
|
||||||
for (var i = 0; i < `y`.length; ++i) {
|
|
||||||
`x`[0][len] = `y`.charCodeAt(i);
|
|
||||||
++len;
|
|
||||||
}
|
|
||||||
`x`[0][len] = 0
|
|
||||||
"""
|
|
||||||
|
|
||||||
proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
|
proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
|
||||||
proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
|
proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
|
||||||
## Generic proc for adding a data item `y` to a container `x`.
|
## Generic proc for adding a data item `y` to a container `x`.
|
||||||
|
|
@ -850,10 +831,6 @@ proc toBiggestInt*(f: biggestfloat): biggestint {.
|
||||||
## rounds `f` if it does not contain an integer value. If the conversion
|
## rounds `f` if it does not contain an integer value. If the conversion
|
||||||
## fails (because `f` is infinite for example), `EInvalidValue` is raised.
|
## fails (because `f` is infinite for example), `EInvalidValue` is raised.
|
||||||
|
|
||||||
proc `/`*(x, y: int): float {.inline, noSideEffect.} =
|
|
||||||
## integer division that results in a float.
|
|
||||||
result = toFloat(x) / toFloat(y)
|
|
||||||
|
|
||||||
proc addQuitProc*(QuitProc: proc {.noconv.}) {.importc: "atexit", nodecl.}
|
proc addQuitProc*(QuitProc: proc {.noconv.}) {.importc: "atexit", nodecl.}
|
||||||
## adds/registers a quit procedure. Each call to ``addQuitProc``
|
## adds/registers a quit procedure. Each call to ``addQuitProc``
|
||||||
## registers another quit procedure. Up to 30 procedures can be
|
## registers another quit procedure. Up to 30 procedures can be
|
||||||
|
|
@ -1004,12 +981,6 @@ const
|
||||||
## and expect a reasonable result - use the `classify` procedure
|
## and expect a reasonable result - use the `classify` procedure
|
||||||
## in the module ``math`` for checking for NaN.
|
## in the module ``math`` for checking for NaN.
|
||||||
|
|
||||||
var
|
|
||||||
dbgLineHook*: proc = nil
|
|
||||||
## set this variable to provide a procedure that should be called before
|
|
||||||
## each executed instruction. This should only be used by debuggers!
|
|
||||||
## Only code compiled with the ``debugger:on`` switch calls this hook.
|
|
||||||
|
|
||||||
# GC interface:
|
# GC interface:
|
||||||
|
|
||||||
proc getOccupiedMem*(): int {.rtl.}
|
proc getOccupiedMem*(): int {.rtl.}
|
||||||
|
|
@ -1265,6 +1236,35 @@ template accumulateResult*(iter: expr) =
|
||||||
# however, stack-traces are available for most parts
|
# however, stack-traces are available for most parts
|
||||||
# of the code
|
# of the code
|
||||||
|
|
||||||
|
var
|
||||||
|
dbgLineHook*: proc = nil
|
||||||
|
## set this variable to provide a procedure that should be called before
|
||||||
|
## each executed instruction. This should only be used by debuggers!
|
||||||
|
## Only code compiled with the ``debugger:on`` switch calls this hook.
|
||||||
|
|
||||||
|
when not defined(ECMAScript):
|
||||||
|
{.push overflow_checks:off}
|
||||||
|
proc add* (x: var string, y: cstring) =
|
||||||
|
var i = 0
|
||||||
|
while y[i] != '\0':
|
||||||
|
add(x, y[i])
|
||||||
|
inc(i)
|
||||||
|
{.pop.}
|
||||||
|
else:
|
||||||
|
proc add* (x: var string, y: cstring) {.pure.} =
|
||||||
|
asm """
|
||||||
|
var len = `x`[0].length-1;
|
||||||
|
for (var i = 0; i < `y`.length; ++i) {
|
||||||
|
`x`[0][len] = `y`.charCodeAt(i);
|
||||||
|
++len;
|
||||||
|
}
|
||||||
|
`x`[0][len] = 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc `/`*(x, y: int): float {.inline, noSideEffect.} =
|
||||||
|
## integer division that results in a float.
|
||||||
|
result = toFloat(x) / toFloat(y)
|
||||||
|
|
||||||
proc echo*[Ty](x: openarray[Ty]) {.magic: "Echo".}
|
proc echo*[Ty](x: openarray[Ty]) {.magic: "Echo".}
|
||||||
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
|
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
|
||||||
## available for the ECMAScript target too!
|
## available for the ECMAScript target too!
|
||||||
|
|
|
||||||
|
|
@ -531,48 +531,49 @@ proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
|
||||||
|
|
||||||
# ---------------------- interface to programs -------------------------------
|
# ---------------------- interface to programs -------------------------------
|
||||||
|
|
||||||
proc alloc(size: int): pointer =
|
when not defined(useNimRtl):
|
||||||
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
proc alloc(size: int): pointer =
|
||||||
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
||||||
assert(not isAllocatedPtr(allocator, result))
|
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
||||||
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
|
assert(not isAllocatedPtr(allocator, result))
|
||||||
|
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
|
||||||
|
|
||||||
proc alloc0(size: int): pointer =
|
proc alloc0(size: int): pointer =
|
||||||
result = alloc(size)
|
result = alloc(size)
|
||||||
zeroMem(result, size)
|
zeroMem(result, size)
|
||||||
|
|
||||||
proc dealloc(p: pointer) =
|
proc dealloc(p: pointer) =
|
||||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||||
assert(cast[ptr TFreeCell](x).zeroField == 1)
|
assert(cast[ptr TFreeCell](x).zeroField == 1)
|
||||||
rawDealloc(allocator, x)
|
rawDealloc(allocator, x)
|
||||||
assert(not isAllocatedPtr(allocator, x))
|
assert(not isAllocatedPtr(allocator, x))
|
||||||
|
|
||||||
proc ptrSize(p: pointer): int =
|
proc ptrSize(p: pointer): int =
|
||||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||||
result = pageAddr(x).size - sizeof(TFreeCell)
|
result = pageAddr(x).size - sizeof(TFreeCell)
|
||||||
|
|
||||||
proc realloc(p: pointer, newsize: int): pointer =
|
proc realloc(p: pointer, newsize: int): pointer =
|
||||||
if newsize > 0:
|
if newsize > 0:
|
||||||
result = alloc(newsize)
|
result = alloc(newsize)
|
||||||
if p != nil:
|
if p != nil:
|
||||||
copyMem(result, p, ptrSize(p))
|
copyMem(result, p, ptrSize(p))
|
||||||
|
dealloc(p)
|
||||||
|
elif p != nil:
|
||||||
dealloc(p)
|
dealloc(p)
|
||||||
elif p != nil:
|
|
||||||
dealloc(p)
|
|
||||||
|
|
||||||
proc countFreeMem(): int =
|
proc countFreeMem(): int =
|
||||||
# only used for assertions
|
# only used for assertions
|
||||||
var it = allocator.freeChunksList
|
var it = allocator.freeChunksList
|
||||||
while it != nil:
|
while it != nil:
|
||||||
inc(result, it.size)
|
inc(result, it.size)
|
||||||
it = it.next
|
it = it.next
|
||||||
|
|
||||||
proc getFreeMem(): int =
|
proc getFreeMem(): int =
|
||||||
result = allocator.freeMem
|
result = allocator.freeMem
|
||||||
#assert(result == countFreeMem())
|
#assert(result == countFreeMem())
|
||||||
|
|
||||||
proc getTotalMem(): int = return allocator.currMem
|
proc getTotalMem(): int = return allocator.currMem
|
||||||
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
|
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
const iterations = 4000_000
|
const iterations = 4000_000
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2009 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
|
|
@ -61,11 +61,14 @@ when defined(posix):
|
||||||
proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {.
|
proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {.
|
||||||
importc, header: "<dlfcn.h>".}
|
importc, header: "<dlfcn.h>".}
|
||||||
|
|
||||||
|
proc dlerror(): cstring {.importc, header: "<dlfcn.h>".}
|
||||||
|
|
||||||
proc nimUnloadLibrary(lib: TLibHandle) =
|
proc nimUnloadLibrary(lib: TLibHandle) =
|
||||||
dlclose(lib)
|
dlclose(lib)
|
||||||
|
|
||||||
proc nimLoadLibrary(path: string): TLibHandle =
|
proc nimLoadLibrary(path: string): TLibHandle =
|
||||||
result = dlopen(path, RTLD_NOW)
|
result = dlopen(path, RTLD_NOW)
|
||||||
|
#c_fprintf(c_stdout, "%s\n", dlerror())
|
||||||
|
|
||||||
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
|
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
|
||||||
result = dlsym(lib, name)
|
result = dlsym(lib, name)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nimrod's Runtime Library
|
||||||
# (c) Copyright 2009 Andreas Rumpf
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
# Exception handling code. This is difficult because it has
|
# Exception handling code. This is difficult because it has
|
||||||
# to work if there is no more memory. Do not use ``sprintf``, etc. as they are
|
# to work if there is no more memory (but it doesn't yet!).
|
||||||
# unsafe!
|
|
||||||
|
|
||||||
when not defined(windows) or not defined(guiapp):
|
when not defined(windows) or not defined(guiapp):
|
||||||
proc writeToStdErr(msg: CString) = write(stdout, msg)
|
proc writeToStdErr(msg: CString) = write(stdout, msg)
|
||||||
|
|
|
||||||
|
|
@ -74,14 +74,6 @@ var
|
||||||
# This is wasteful but safe. This is a lock against recursive garbage
|
# This is wasteful but safe. This is a lock against recursive garbage
|
||||||
# collection, not a lock for threads!
|
# collection, not a lock for threads!
|
||||||
|
|
||||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerRtl.}
|
|
||||||
# unsureAsgnRef updates the reference counters only if dest is not on the
|
|
||||||
# stack. It is used by the code generator if it cannot decide wether a
|
|
||||||
# reference is in the stack or not (this can happen for var parameters).
|
|
||||||
|
|
||||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.}
|
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
|
||||||
|
|
||||||
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
||||||
if (c.refcount and rcZct) == 0:
|
if (c.refcount and rcZct) == 0:
|
||||||
c.refcount = c.refcount and not colorMask or rcZct
|
c.refcount = c.refcount and not colorMask or rcZct
|
||||||
|
|
@ -105,24 +97,6 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
|
||||||
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||||
result = int(usrToCell(p).refcount) shr rcShift
|
result = int(usrToCell(p).refcount) shr rcShift
|
||||||
|
|
||||||
proc GC_disable() = inc(recGcLock)
|
|
||||||
proc GC_enable() =
|
|
||||||
if recGcLock > 0: dec(recGcLock)
|
|
||||||
|
|
||||||
proc GC_setStrategy(strategy: TGC_Strategy) =
|
|
||||||
case strategy
|
|
||||||
of gcThroughput: nil
|
|
||||||
of gcResponsiveness: nil
|
|
||||||
of gcOptimizeSpace: nil
|
|
||||||
of gcOptimizeTime: nil
|
|
||||||
|
|
||||||
proc GC_enableMarkAndSweep() =
|
|
||||||
cycleThreshold = InitialCycleThreshold
|
|
||||||
|
|
||||||
proc GC_disableMarkAndSweep() =
|
|
||||||
cycleThreshold = high(cycleThreshold)-1
|
|
||||||
# set to the max value to suppress the cycle detector
|
|
||||||
|
|
||||||
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
|
||||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||||
|
|
@ -214,8 +188,13 @@ proc prepareDealloc(cell: PCell) =
|
||||||
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
|
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
|
||||||
dec(recGcLock)
|
dec(recGcLock)
|
||||||
|
|
||||||
proc PossibleRoot(gch: var TGcHeap, c: PCell) {.inline.} =
|
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||||
if canbeCycleRoot(c): incl(gch.cycleRoots, c)
|
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||||
|
incl(gch.cycleRoots, c)
|
||||||
|
|
||||||
|
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
|
||||||
|
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||||
|
addZCT(gch.zct, c)
|
||||||
|
|
||||||
proc decRef(c: PCell) {.inline.} =
|
proc decRef(c: PCell) {.inline.} =
|
||||||
when stressGC:
|
when stressGC:
|
||||||
|
|
@ -224,19 +203,19 @@ proc decRef(c: PCell) {.inline.} =
|
||||||
assert(c.refcount >=% rcIncrement)
|
assert(c.refcount >=% rcIncrement)
|
||||||
c.refcount = c.refcount -% rcIncrement
|
c.refcount = c.refcount -% rcIncrement
|
||||||
if c.refcount <% rcIncrement:
|
if c.refcount <% rcIncrement:
|
||||||
addZCT(gch.zct, c)
|
rtlAddZCT(c)
|
||||||
elif canBeCycleRoot(c):
|
elif canBeCycleRoot(c):
|
||||||
incl(gch.cycleRoots, c)
|
rtlAddCycleRoot(c)
|
||||||
|
|
||||||
proc incRef(c: PCell) {.inline.} =
|
proc incRef(c: PCell) {.inline.} =
|
||||||
c.refcount = c.refcount +% rcIncrement
|
c.refcount = c.refcount +% rcIncrement
|
||||||
if canBeCycleRoot(c):
|
if canBeCycleRoot(c):
|
||||||
incl(gch.cycleRoots, c)
|
rtlAddCycleRoot(c)
|
||||||
|
|
||||||
proc nimGCref(p: pointer) {.compilerRtl, inl.} = incRef(usrToCell(p))
|
proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(usrToCell(p))
|
||||||
proc nimGCunref(p: pointer) {.compilerRtl, inl.} = decRef(usrToCell(p))
|
proc nimGCunref(p: pointer) {.compilerProc, inline.} = decRef(usrToCell(p))
|
||||||
|
|
||||||
proc asgnRef(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
proc asgnRef(dest: ppointer, src: pointer) {.compilerProc, inline.} =
|
||||||
# the code generator calls this proc!
|
# the code generator calls this proc!
|
||||||
assert(not isOnStack(dest))
|
assert(not isOnStack(dest))
|
||||||
# BUGFIX: first incRef then decRef!
|
# BUGFIX: first incRef then decRef!
|
||||||
|
|
@ -244,7 +223,7 @@ proc asgnRef(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
||||||
if dest^ != nil: decRef(usrToCell(dest^))
|
if dest^ != nil: decRef(usrToCell(dest^))
|
||||||
dest^ = src
|
dest^ = src
|
||||||
|
|
||||||
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerProc, inline.} =
|
||||||
# the code generator calls this proc if it is known at compile time that no
|
# the code generator calls this proc if it is known at compile time that no
|
||||||
# cycle is possible.
|
# cycle is possible.
|
||||||
if src != nil:
|
if src != nil:
|
||||||
|
|
@ -254,30 +233,34 @@ proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
||||||
var c = usrToCell(dest^)
|
var c = usrToCell(dest^)
|
||||||
c.refcount = c.refcount -% rcIncrement
|
c.refcount = c.refcount -% rcIncrement
|
||||||
if c.refcount <% rcIncrement:
|
if c.refcount <% rcIncrement:
|
||||||
addZCT(gch.zct, c)
|
rtlAddZCT(c)
|
||||||
dest^ = src
|
dest^ = src
|
||||||
|
|
||||||
proc unsureAsgnRef(dest: ppointer, src: pointer) =
|
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} =
|
||||||
|
# unsureAsgnRef updates the reference counters only if dest is not on the
|
||||||
|
# stack. It is used by the code generator if it cannot decide wether a
|
||||||
|
# reference is in the stack or not (this can happen for var parameters).
|
||||||
if not IsOnStack(dest):
|
if not IsOnStack(dest):
|
||||||
if src != nil: incRef(usrToCell(src))
|
if src != nil: incRef(usrToCell(src))
|
||||||
if dest^ != nil: decRef(usrToCell(dest^))
|
if dest^ != nil: decRef(usrToCell(dest^))
|
||||||
dest^ = src
|
dest^ = src
|
||||||
|
|
||||||
proc initGC() =
|
proc initGC() =
|
||||||
when traceGC:
|
when not defined(useNimRtl):
|
||||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
when traceGC:
|
||||||
gch.stat.stackScans = 0
|
for i in low(TCellState)..high(TCellState): Init(states[i])
|
||||||
gch.stat.cycleCollections = 0
|
gch.stat.stackScans = 0
|
||||||
gch.stat.maxThreshold = 0
|
gch.stat.cycleCollections = 0
|
||||||
gch.stat.maxStackSize = 0
|
gch.stat.maxThreshold = 0
|
||||||
gch.stat.maxStackCells = 0
|
gch.stat.maxStackSize = 0
|
||||||
gch.stat.cycleTableSize = 0
|
gch.stat.maxStackCells = 0
|
||||||
# init the rt
|
gch.stat.cycleTableSize = 0
|
||||||
init(gch.zct)
|
# init the rt
|
||||||
init(gch.tempStack)
|
init(gch.zct)
|
||||||
Init(gch.cycleRoots)
|
init(gch.tempStack)
|
||||||
Init(gch.decStack)
|
Init(gch.cycleRoots)
|
||||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
Init(gch.decStack)
|
||||||
|
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||||
|
|
||||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||||
var d = cast[TAddress](dest)
|
var d = cast[TAddress](dest)
|
||||||
|
|
@ -325,7 +308,7 @@ proc checkCollection {.inline.} =
|
||||||
if recGcLock == 0:
|
if recGcLock == 0:
|
||||||
collectCT(gch)
|
collectCT(gch)
|
||||||
|
|
||||||
proc newObj(typ: PNimType, size: int): pointer =
|
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
|
||||||
# generates a new object and sets its reference counter to 0
|
# generates a new object and sets its reference counter to 0
|
||||||
assert(typ.kind in {tyRef, tyString, tySequence})
|
assert(typ.kind in {tyRef, tyString, tySequence})
|
||||||
checkCollection()
|
checkCollection()
|
||||||
|
|
@ -357,7 +340,7 @@ proc newObj(typ: PNimType, size: int): pointer =
|
||||||
gcTrace(res, csAllocated)
|
gcTrace(res, csAllocated)
|
||||||
result = cellToUsr(res)
|
result = cellToUsr(res)
|
||||||
|
|
||||||
proc newSeq(typ: PNimType, len: int): pointer =
|
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
|
||||||
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
|
||||||
cast[PGenericSeq](result).len = len
|
cast[PGenericSeq](result).len = len
|
||||||
cast[PGenericSeq](result).space = len
|
cast[PGenericSeq](result).space = len
|
||||||
|
|
@ -490,17 +473,18 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
|
||||||
else:
|
else:
|
||||||
const stackIncreases = false
|
const stackIncreases = false
|
||||||
|
|
||||||
proc setStackBottom(theStackBottom: pointer) =
|
when not defined(useNimRtl):
|
||||||
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
|
proc setStackBottom(theStackBottom: pointer) =
|
||||||
# the first init must be the one that defines the stack bottom:
|
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
|
||||||
if stackBottom == nil: stackBottom = theStackBottom
|
# the first init must be the one that defines the stack bottom:
|
||||||
else:
|
if stackBottom == nil: stackBottom = theStackBottom
|
||||||
var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2
|
|
||||||
var b = cast[TAddress](stackBottom)
|
|
||||||
when stackIncreases:
|
|
||||||
stackBottom = cast[pointer](min(a, b))
|
|
||||||
else:
|
else:
|
||||||
stackBottom = cast[pointer](max(a, b))
|
var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2
|
||||||
|
var b = cast[TAddress](stackBottom)
|
||||||
|
when stackIncreases:
|
||||||
|
stackBottom = cast[pointer](min(a, b))
|
||||||
|
else:
|
||||||
|
stackBottom = cast[pointer](max(a, b))
|
||||||
|
|
||||||
proc stackSize(): int {.noinline.} =
|
proc stackSize(): int {.noinline.} =
|
||||||
var stackTop: array[0..1, pointer]
|
var stackTop: array[0..1, pointer]
|
||||||
|
|
@ -647,22 +631,41 @@ proc collectCT(gch: var TGcHeap) =
|
||||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, cycleThreshold)
|
gch.stat.maxThreshold = max(gch.stat.maxThreshold, cycleThreshold)
|
||||||
unmarkStackAndRegisters(gch)
|
unmarkStackAndRegisters(gch)
|
||||||
|
|
||||||
proc GC_fullCollect() =
|
when not defined(useNimRtl):
|
||||||
var oldThreshold = cycleThreshold
|
proc GC_disable() = inc(recGcLock)
|
||||||
cycleThreshold = 0 # forces cycle collection
|
proc GC_enable() =
|
||||||
collectCT(gch)
|
if recGcLock > 0: dec(recGcLock)
|
||||||
cycleThreshold = oldThreshold
|
|
||||||
|
|
||||||
proc GC_getStatistics(): string =
|
proc GC_setStrategy(strategy: TGC_Strategy) =
|
||||||
GC_disable()
|
case strategy
|
||||||
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
|
of gcThroughput: nil
|
||||||
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
of gcResponsiveness: nil
|
||||||
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
of gcOptimizeSpace: nil
|
||||||
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
of gcOptimizeTime: nil
|
||||||
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
|
|
||||||
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
proc GC_enableMarkAndSweep() =
|
||||||
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
cycleThreshold = InitialCycleThreshold
|
||||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
|
||||||
"[GC] max stack size: " & $gch.stat.maxStackSize
|
proc GC_disableMarkAndSweep() =
|
||||||
when traceGC: writeLeakage()
|
cycleThreshold = high(cycleThreshold)-1
|
||||||
GC_enable()
|
# set to the max value to suppress the cycle detector
|
||||||
|
|
||||||
|
proc GC_fullCollect() =
|
||||||
|
var oldThreshold = cycleThreshold
|
||||||
|
cycleThreshold = 0 # forces cycle collection
|
||||||
|
collectCT(gch)
|
||||||
|
cycleThreshold = oldThreshold
|
||||||
|
|
||||||
|
proc GC_getStatistics(): string =
|
||||||
|
GC_disable()
|
||||||
|
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
|
||||||
|
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
|
||||||
|
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
|
||||||
|
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
|
||||||
|
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
|
||||||
|
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
|
||||||
|
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
|
||||||
|
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||||
|
"[GC] max stack size: " & $gch.stat.maxStackSize
|
||||||
|
when traceGC: writeLeakage()
|
||||||
|
GC_enable()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ when defined(createNimRtl):
|
||||||
{.error: "nimrtl must be built as a library!".}
|
{.error: "nimrtl must be built as a library!".}
|
||||||
|
|
||||||
when defined(createNimRtl):
|
when defined(createNimRtl):
|
||||||
# NOTE: compilerproc cannot make use of name mangling!
|
|
||||||
{.pragma: rtl, exportc: "nimrtl_$1", dynlib.}
|
{.pragma: rtl, exportc: "nimrtl_$1", dynlib.}
|
||||||
{.pragma: inl.}
|
{.pragma: inl.}
|
||||||
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
|
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
|
||||||
|
|
|
||||||
|
|
@ -187,28 +187,6 @@ elif defined(nogc):
|
||||||
dest^ = src
|
dest^ = src
|
||||||
|
|
||||||
include "system/cellsets"
|
include "system/cellsets"
|
||||||
elif defined(useNimRtl):
|
|
||||||
proc initGC() = nil
|
|
||||||
|
|
||||||
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.}
|
|
||||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
|
||||||
proc growObj(old: pointer, newsize: int): pointer {.rtl.}
|
|
||||||
|
|
||||||
proc nimGCref(p: pointer) {.compilerRtl.}
|
|
||||||
proc nimGCunref(p: pointer) {.compilerRtl.}
|
|
||||||
|
|
||||||
# The write barrier is performance critical!
|
|
||||||
# XXX We should ensure that they are inlined here.
|
|
||||||
# Later implementations will do this.
|
|
||||||
|
|
||||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.
|
|
||||||
compilerRtl.}
|
|
||||||
proc asgnRef(dest: ppointer, src: pointer) {.
|
|
||||||
compilerRtl.}
|
|
||||||
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.
|
|
||||||
compilerRtl.}
|
|
||||||
|
|
||||||
include "system/cellsets"
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
include "system/alloc"
|
include "system/alloc"
|
||||||
|
|
|
||||||
|
|
@ -57,18 +57,17 @@ proc evalTemplateAux(c: PContext, templ, actual: PNode, sym: PSym): PNode =
|
||||||
result.sons[i] = evalTemplateAux(c, templ.sons[i], actual, sym)
|
result.sons[i] = evalTemplateAux(c, templ.sons[i], actual, sym)
|
||||||
|
|
||||||
var evalTemplateCounter: int = 0
|
var evalTemplateCounter: int = 0
|
||||||
|
# to prevend endless recursion in templates instantation
|
||||||
|
|
||||||
proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode =
|
proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
# to prevend endless recursion in templates
|
|
||||||
# instantation
|
|
||||||
var
|
var
|
||||||
f, a: int
|
f, a: int
|
||||||
arg: PNode
|
arg: PNode
|
||||||
f = sonsLen(s.typ) # if the template has zero arguments, it can be called without ``()``
|
f = sonsLen(s.typ) # if the template has zero arguments, it can be called without ``()``
|
||||||
# `n` is then a nkSym or something similar
|
# `n` is then a nkSym or something similar
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: a = sonsLen(
|
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||||
n)
|
a = sonsLen(n)
|
||||||
else: a = 0
|
else: a = 0
|
||||||
if a > f: liMessage(n.info, errWrongNumberOfArguments)
|
if a > f: liMessage(n.info, errWrongNumberOfArguments)
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
|
@ -85,7 +84,8 @@ proc evalTemplate(c: PContext, n: PNode, sym: PSym): PNode =
|
||||||
var args: PNode
|
var args: PNode
|
||||||
inc(evalTemplateCounter)
|
inc(evalTemplateCounter)
|
||||||
if evalTemplateCounter > 100:
|
if evalTemplateCounter > 100:
|
||||||
liMessage(n.info, errTemplateInstantiationTooNested) # replace each param by the corresponding node:
|
liMessage(n.info, errTemplateInstantiationTooNested)
|
||||||
|
# replace each param by the corresponding node:
|
||||||
args = evalTemplateArgs(c, n, sym)
|
args = evalTemplateArgs(c, n, sym)
|
||||||
result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
|
result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
|
||||||
dec(evalTemplateCounter)
|
dec(evalTemplateCounter)
|
||||||
|
|
|
||||||
5
todo.txt
5
todo.txt
|
|
@ -1,6 +1,7 @@
|
||||||
For version 0.8.10
|
For version 0.8.10
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
- exception propagation across DLLs
|
||||||
- fix exception handling
|
- fix exception handling
|
||||||
- fix implicit generic routines
|
- fix implicit generic routines
|
||||||
- fix the streams implementation so that they use methods
|
- fix the streams implementation so that they use methods
|
||||||
|
|
@ -61,6 +62,7 @@ Low priority
|
||||||
- normalize for the DOM
|
- normalize for the DOM
|
||||||
- tlastmod returns wrong results on BSD (Linux, MacOS X: works)
|
- tlastmod returns wrong results on BSD (Linux, MacOS X: works)
|
||||||
- nested tuple unpacking
|
- nested tuple unpacking
|
||||||
|
- fast assignment optimization for TPeg
|
||||||
|
|
||||||
|
|
||||||
Library
|
Library
|
||||||
|
|
@ -129,4 +131,7 @@ RST
|
||||||
---
|
---
|
||||||
- footnotes; prefix :i: whitespace before :i:, _reference, `reference`__
|
- footnotes; prefix :i: whitespace before :i:, _reference, `reference`__
|
||||||
__ anonymous: www.nimrod.org
|
__ anonymous: www.nimrod.org
|
||||||
|
- rewrite the parser to use the new better look ahead technique that c2nim
|
||||||
|
uses
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue