mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-08 04:44:20 +00:00
inlining of the write barrier for dlls
This commit is contained in:
@@ -169,16 +169,6 @@ in mind:
|
||||
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
|
||||
=====================
|
||||
|
||||
|
||||
@@ -70,17 +70,22 @@ 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
|
||||
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
|
||||
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
|
||||
|
||||
DLL generation
|
||||
==============
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
Additional Features
|
||||
===================
|
||||
|
||||
@@ -88,6 +88,8 @@ macro meaning
|
||||
``[^ \9-\13]``
|
||||
``\w`` any "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``
|
||||
``\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
|
||||
|
||||
@@ -11,23 +11,18 @@
|
||||
## The default Nimrtl does not only contain the ``system`` module, but these
|
||||
## too:
|
||||
##
|
||||
## * strutils
|
||||
## * parseutils
|
||||
## * strutils
|
||||
## * parseopt
|
||||
## * parsecfg
|
||||
## * strtabs
|
||||
## * times
|
||||
## * os
|
||||
## * osproc
|
||||
## * pegs
|
||||
## * unicode
|
||||
## * pegs
|
||||
## * 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":
|
||||
{.error: "This file has to be compiled as a library!".}
|
||||
@@ -35,5 +30,7 @@ when system.appType != "lib":
|
||||
when not defined(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
|
||||
assert(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]),
|
||||
(L.sentinel + 1) * chrSize) div chrSize
|
||||
s = toCopy + charsRead
|
||||
|
||||
136
lib/pure/os.nim
136
lib/pure/os.nim
@@ -14,6 +14,8 @@
|
||||
|
||||
{.push debugger: off.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import
|
||||
strutils, times
|
||||
|
||||
@@ -145,19 +147,7 @@ const
|
||||
## The character which separates the base filename from the extension;
|
||||
## for example, the '.' in ``os.nim``.
|
||||
|
||||
# procs dealing with command line arguments:
|
||||
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.} =
|
||||
proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
|
||||
## raises an EOS exception with the given message ``msg``.
|
||||
## If ``msg == ""``, the operating system's error flag
|
||||
## (``errno``) is converted to a readable error message. On Windows
|
||||
@@ -182,7 +172,8 @@ proc OSError*(msg: string = "") {.noinline.} =
|
||||
else:
|
||||
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.
|
||||
##
|
||||
## On an UNIX system this does nothing. Else it converts
|
||||
@@ -227,7 +218,7 @@ proc UnixToNativePath*(path: string): string {.noSideEffect.} =
|
||||
add result, path[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.
|
||||
when defined(windows):
|
||||
var a = GetFileAttributesA(filename)
|
||||
@@ -237,7 +228,7 @@ proc existsFile*(filename: string): bool =
|
||||
var res: TStat
|
||||
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
|
||||
## is returned.
|
||||
when defined(windows):
|
||||
@@ -248,7 +239,7 @@ proc existsDir*(dir: string): bool =
|
||||
var res: TStat
|
||||
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.
|
||||
when defined(posix):
|
||||
var res: TStat
|
||||
@@ -261,7 +252,7 @@ proc getLastModificationTime*(file: string): TTime =
|
||||
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
|
||||
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.
|
||||
when defined(posix):
|
||||
var res: TStat
|
||||
@@ -274,7 +265,7 @@ proc getLastAccessTime*(file: string): TTime =
|
||||
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
|
||||
findclose(h)
|
||||
|
||||
proc getCreationTime*(file: string): TTime =
|
||||
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
|
||||
## Returns the `file`'s creation time.
|
||||
when defined(posix):
|
||||
var res: TStat
|
||||
@@ -287,12 +278,12 @@ proc getCreationTime*(file: string): TTime =
|
||||
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
|
||||
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
|
||||
## modification time is later than `b`'s.
|
||||
result = getLastModificationTime(a) - getLastModificationTime(b) > 0
|
||||
|
||||
proc getCurrentDir*(): string =
|
||||
proc getCurrentDir*(): string {.rtl, extern: "nos$1".} =
|
||||
## Returns the current working directory.
|
||||
const bufsize = 512 # should be enough
|
||||
result = newString(bufsize)
|
||||
@@ -314,7 +305,8 @@ proc setCurrentDir*(newDir: string) {.inline.} =
|
||||
else:
|
||||
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.
|
||||
##
|
||||
## For example on Unix:
|
||||
@@ -342,7 +334,8 @@ proc JoinPath*(head, tail: string): string {.noSideEffect.} =
|
||||
else:
|
||||
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
|
||||
## of directory parts.
|
||||
result = parts[0]
|
||||
@@ -370,7 +363,8 @@ proc SplitPath*(path: string, head, tail: var string) {.noSideEffect,
|
||||
head = ""
|
||||
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
|
||||
## ``JoinPath(head, tail) == path``.
|
||||
##
|
||||
@@ -395,7 +389,8 @@ proc SplitPath*(path: string): tuple[head, tail: string] {.noSideEffect.} =
|
||||
result.head = ""
|
||||
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`.
|
||||
##
|
||||
## 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}:
|
||||
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).
|
||||
## `dir` does not end in `DirSep`.
|
||||
## `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.
|
||||
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
|
||||
## ``name & ext`` from ``splitFile(path)``.
|
||||
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
|
||||
@@ -480,7 +477,7 @@ proc extractFilename*(path: string): string {.noSideEffect.} =
|
||||
else:
|
||||
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.
|
||||
when defined(windows):
|
||||
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.
|
||||
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`.
|
||||
##
|
||||
## 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)
|
||||
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
|
||||
## `filename` already has an extension.
|
||||
##
|
||||
@@ -552,7 +551,8 @@ proc AppendFileExt*(filename, ext: string): string {.
|
||||
## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
|
||||
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.
|
||||
##
|
||||
## On a case-sensitive filesystem this is done
|
||||
@@ -566,7 +566,7 @@ proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} =
|
||||
else:
|
||||
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
|
||||
## directory (as indicated by device number and i-node number).
|
||||
## Raises an exception if an stat() call on either pathname fails.
|
||||
@@ -590,7 +590,7 @@ proc sameFile*(path1, path2: string): bool =
|
||||
else:
|
||||
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
|
||||
## binary content.
|
||||
const
|
||||
@@ -620,7 +620,7 @@ proc sameFileContent*(path1, path2: string): bool =
|
||||
close(a)
|
||||
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,
|
||||
## `EOS` is raised.
|
||||
## **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(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.
|
||||
## **Deprecated since version 0.8.8**: Use this proc with named arguments
|
||||
## only, because the order will change!
|
||||
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.
|
||||
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.
|
||||
result = csystem(command)
|
||||
|
||||
proc execShellCmd*(command: string): int =
|
||||
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} =
|
||||
## Executes a shell command.
|
||||
##
|
||||
## Command has the form 'program args' where args are the command
|
||||
@@ -675,6 +675,9 @@ proc execShellCmd*(command: string): int =
|
||||
## module.
|
||||
result = csystem(command)
|
||||
|
||||
# Environment handling cannot be put into RTL, because the ``envPairs``
|
||||
# iterator depends on ``environment``.
|
||||
|
||||
var
|
||||
envComputed: bool = false
|
||||
environment: seq[string] = @[]
|
||||
@@ -700,8 +703,8 @@ when defined(windows):
|
||||
discard FreeEnvironmentStringsA(env)
|
||||
|
||||
else:
|
||||
var
|
||||
gEnv {.importc: "gEnv".}: ptr array [0..10_000, CString]
|
||||
var gEnv {.importc: "environ".}: cstringArray
|
||||
# var gEnv {.importc: "gEnv".}: cstringArray
|
||||
|
||||
proc getEnvVarsC() =
|
||||
# retrieves the variables of char** env of C's main proc
|
||||
@@ -897,7 +900,7 @@ proc rawRemoveDir(dir: string) =
|
||||
else:
|
||||
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
|
||||
## in `dir` (recursively). If this fails, `EOS` is raised.
|
||||
for kind, path in walkDir(dir):
|
||||
@@ -914,7 +917,7 @@ proc rawCreateDir(dir: string) =
|
||||
if CreateDirectoryA(dir, nil) == 0'i32 and GetLastError() != 183'i32:
|
||||
OSError()
|
||||
|
||||
proc createDir*(dir: string) =
|
||||
proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
|
||||
## Creates the directory `dir`.
|
||||
##
|
||||
## 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))
|
||||
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;
|
||||
## This proc is only occassionally useful, better use the `parseopt` module.
|
||||
##
|
||||
@@ -1025,7 +1029,8 @@ type
|
||||
fpOthersWrite, ## write 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
|
||||
## an error. On Windows, only the ``readonly`` flag is checked, every other
|
||||
## permission is available in any case.
|
||||
@@ -1053,7 +1058,8 @@ proc getFilePermissions*(filename: string): set[TFilePermission] =
|
||||
else:
|
||||
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
|
||||
## an error. On Windows, only the ``readonly`` flag is changed, depending on
|
||||
## ``fpUserWrite``.
|
||||
@@ -1083,7 +1089,8 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) =
|
||||
OSError()
|
||||
|
||||
proc inclFilePermissions*(filename: string,
|
||||
permissions: set[TFilePermission]) =
|
||||
permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1".} =
|
||||
## a convenience procedure for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
@@ -1091,19 +1098,20 @@ proc inclFilePermissions*(filename: string,
|
||||
setFilePermissions(filename, getFilePermissions(filename)+permissions)
|
||||
|
||||
proc exclFilePermissions*(filename: string,
|
||||
permissions: set[TFilePermission]) =
|
||||
permissions: set[TFilePermission]) {.
|
||||
rtl, extern: "nos$1".} =
|
||||
## a convenience procedure for:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## 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.
|
||||
when defined(windows): return getEnv("USERPROFILE") & "\\"
|
||||
else: return getEnv("HOME") & "/"
|
||||
|
||||
proc getConfigDir*(): string =
|
||||
proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
|
||||
## Returns the config directory of the current user for applications.
|
||||
when defined(windows): return getEnv("APPDATA") & "\\"
|
||||
else: return getEnv("HOME") & "/.config/"
|
||||
@@ -1117,24 +1125,32 @@ when defined(windows):
|
||||
var
|
||||
ownArgv: seq[string]
|
||||
|
||||
proc paramStr(i: int): string =
|
||||
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
||||
return ownArgv[i]
|
||||
|
||||
proc paramCount(): int =
|
||||
proc paramCount*(): int {.rtl, extern: "nos$1".} =
|
||||
## Returns the number of command line arguments given to the
|
||||
## application.
|
||||
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
|
||||
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
|
||||
cmdCount {.importc: "cmdCount".}: cint
|
||||
cmdLine {.importc: "cmdLine".}: cstringArray
|
||||
|
||||
proc paramStr(i: int): string =
|
||||
proc paramStr*(i: int): string =
|
||||
if i < cmdCount and i >= 0: return $cmdLine[i]
|
||||
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):
|
||||
proc getApplAux(procPath: string): string =
|
||||
@@ -1153,7 +1169,7 @@ when defined(macosx):
|
||||
proc getExecPath2(c: cstring, size: var int32): bool {.
|
||||
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.
|
||||
|
||||
# Linux: /proc/<pid>/exe
|
||||
@@ -1190,11 +1206,11 @@ proc getApplicationFilename*(): string =
|
||||
var x = joinPath(p, result)
|
||||
if ExistsFile(x): return x
|
||||
|
||||
proc getApplicationDir*(): string =
|
||||
proc getApplicationDir*(): string {.rtl, extern: "nos$1".} =
|
||||
## Returns the directory of the application's executable.
|
||||
result = splitFile(getApplicationFilename()).dir
|
||||
|
||||
proc sleep*(milsecs: int) =
|
||||
proc sleep*(milsecs: int) {.rtl, extern: "nos$1".} =
|
||||
## sleeps `milsecs` milliseconds.
|
||||
when defined(windows):
|
||||
winlean.sleep(int32(milsecs))
|
||||
@@ -1204,7 +1220,7 @@ proc sleep*(milsecs: int) =
|
||||
a.tv_nsec = (milsecs mod 1000) * 1000
|
||||
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``.
|
||||
when defined(windows):
|
||||
var a: TWin32FindData
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
## This module implements an advanced facility for executing OS processes
|
||||
## and process communication.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import
|
||||
strutils, os, strtabs, streams
|
||||
|
||||
@@ -39,7 +41,8 @@ type
|
||||
|
||||
proc execProcess*(command: string,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
poUseShell}): string
|
||||
poUseShell}): string {.
|
||||
rtl, extern: "nosp$1".}
|
||||
## A convience procedure that executes ``command`` with ``startProcess``
|
||||
## and returns its output as a string.
|
||||
|
||||
@@ -50,7 +53,7 @@ proc executeProcess*(command: string,
|
||||
## **Deprecated since version 0.8.2**: Use `execProcess` instead.
|
||||
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,
|
||||
## error streams are inherited from the calling process.
|
||||
|
||||
@@ -62,8 +65,9 @@ proc executeCommand*(command: string): int {.deprecated.} =
|
||||
proc startProcess*(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||
PProcess {.rtl, extern: "nosp$1".}
|
||||
## Starts a process. `Command` is the executable file, `workingDir` is the
|
||||
## process's working directory. If ``workingDir == ""`` the current directory
|
||||
## 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,
|
||||
## 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`.
|
||||
|
||||
proc resume*(p: PProcess)
|
||||
proc resume*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||
## Resumes the process `p`.
|
||||
|
||||
proc terminate*(p: PProcess)
|
||||
proc terminate*(p: PProcess) {.rtl, extern: "nosp$1".}
|
||||
## 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.
|
||||
|
||||
proc processID*(p: PProcess): int =
|
||||
proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
|
||||
## returns `p`'s process 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.
|
||||
|
||||
proc inputStream*(p: PProcess): PStream
|
||||
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||
## 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
|
||||
|
||||
proc errorStream*(p: PProcess): PStream
|
||||
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
|
||||
## returns ``p``'s output stream for reading from
|
||||
|
||||
when defined(macosx) or defined(bsd):
|
||||
@@ -115,7 +119,7 @@ when defined(macosx) or defined(bsd):
|
||||
a: var int, b: pointer, c: int): cint {.
|
||||
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 0 if it cannot be detected.
|
||||
when defined(windows):
|
||||
@@ -150,7 +154,7 @@ proc startProcessAux(cmd: string, options: set[TProcessOption]): PProcess =
|
||||
|
||||
proc execProcesses*(cmds: openArray[string],
|
||||
options = {poStdErrToStdOut, poParentStreams},
|
||||
n = countProcessors()): int =
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1".} =
|
||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||
## that execute in parallel. The highest return value of all processes
|
||||
## is returned.
|
||||
@@ -192,27 +196,16 @@ proc execProcesses*(cmds: openArray[string],
|
||||
var p = startProcessAux(cmds[i], options=options)
|
||||
result = max(waitForExit(p), result)
|
||||
|
||||
when true:
|
||||
nil
|
||||
else:
|
||||
proc startGUIProcess*(command: string,
|
||||
workingDir: string = "",
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
x = -1,
|
||||
y = -1,
|
||||
width = -1,
|
||||
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 not defined(useNimRtl):
|
||||
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:
|
||||
proc deallocCStringArray(a: cstringArray) =
|
||||
@@ -222,7 +215,7 @@ when false:
|
||||
inc(i)
|
||||
dealloc(a)
|
||||
|
||||
when defined(Windows):
|
||||
when defined(Windows) and not defined(useNimRtl):
|
||||
# We need to implement a handle stream for Windows:
|
||||
type
|
||||
PFileHandleStream = ref TFileHandleStream
|
||||
@@ -405,7 +398,7 @@ when defined(Windows):
|
||||
result = -1
|
||||
discard CloseHandle(Process)
|
||||
|
||||
else:
|
||||
elif not defined(useNimRtl):
|
||||
const
|
||||
readIdx = 0
|
||||
writeIdx = 1
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
import
|
||||
hashes, strutils, lexbase, streams
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TCfgEventKind* = enum ## enumation of all events that may occur when parsing
|
||||
cfgEof, ## end of file reached
|
||||
@@ -65,37 +67,17 @@ type
|
||||
state: TParserState
|
||||
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
|
||||
|
||||
const
|
||||
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||
|
||||
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)
|
||||
c.filename = filename
|
||||
c.state = startState
|
||||
@@ -103,16 +85,20 @@ proc open(c: var TCfgParser, input: PStream, filename: string) =
|
||||
c.tok.literal = ""
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
proc handleHexChar(c: var TCfgParser, xi: var int) =
|
||||
@@ -300,7 +286,9 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
|
||||
tok.literal = "[EOF]"
|
||||
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",
|
||||
[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)
|
||||
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
|
||||
of tkEof:
|
||||
result.kind = cfgEof
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
{.push debugger: off.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import
|
||||
os, strutils
|
||||
|
||||
@@ -32,24 +34,28 @@ type
|
||||
## or the argument, ``value`` is not "" if
|
||||
## the option was given a value
|
||||
|
||||
proc initOptParser*(cmdline = ""): TOptParser =
|
||||
## inits the option parser. If ``cmdline == ""``, the real command line
|
||||
## (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 = ""
|
||||
when defined(os.ParamCount):
|
||||
# we cannot provide this for NimRtl creation on Posix, because we can't
|
||||
# access the command line arguments then!
|
||||
|
||||
proc init*(cmdline: string = ""): TOptParser {.deprecated.} =
|
||||
## **Deprecated since version 0.8.2**: Use `initOptParser` instead.
|
||||
result = initOptParser(cmdline)
|
||||
proc initOptParser*(cmdline = ""): TOptParser =
|
||||
## inits the option parser. If ``cmdline == ""``, the real command line
|
||||
## (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,
|
||||
delim: TCharSet = {'\x09', ' ', '\0'}): int =
|
||||
@@ -82,7 +88,8 @@ proc handleShortOption(p: var TOptParser) =
|
||||
if p.cmd[i] == '\0': p.inShortState = false
|
||||
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
|
||||
## parsed. ``p.key`` and ``p.val`` are set accordingly.
|
||||
var i = p.pos
|
||||
@@ -116,7 +123,8 @@ proc next*(p: var TOptParser) =
|
||||
p.kind = cmdArgument
|
||||
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.
|
||||
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.
|
||||
result = cmdLineRest(p)
|
||||
|
||||
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
|
||||
## This is an convenience iterator for iterating over the command line.
|
||||
## This uses the TOptParser object. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var
|
||||
## filename = ""
|
||||
## for kind, key, val in getopt():
|
||||
## case kind
|
||||
## of cmdArgument:
|
||||
## filename = key
|
||||
## of cmdLongOption, cmdShortOption:
|
||||
## case key
|
||||
## of "help", "h": writeHelp()
|
||||
## of "version", "v": writeVersion()
|
||||
## of cmdEnd: assert(false) # cannot happen
|
||||
## if filename == "":
|
||||
## # no filename has been given, so we show the help:
|
||||
## writeHelp()
|
||||
var p = initOptParser()
|
||||
while true:
|
||||
next(p)
|
||||
if p.kind == cmdEnd: break
|
||||
yield (p.kind, p.key, p.val)
|
||||
when defined(initOptParser):
|
||||
|
||||
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
|
||||
## This is an convenience iterator for iterating over the command line.
|
||||
## This uses the TOptParser object. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var
|
||||
## filename = ""
|
||||
## for kind, key, val in getopt():
|
||||
## case kind
|
||||
## of cmdArgument:
|
||||
## filename = key
|
||||
## of cmdLongOption, cmdShortOption:
|
||||
## case key
|
||||
## of "help", "h": writeHelp()
|
||||
## of "version", "v": writeVersion()
|
||||
## of cmdEnd: assert(false) # cannot happen
|
||||
## if filename == "":
|
||||
## # no filename has been given, so we show the help:
|
||||
## writeHelp()
|
||||
var p = initOptParser()
|
||||
while true:
|
||||
next(p)
|
||||
if p.kind == cmdEnd: break
|
||||
yield (p.kind, p.key, p.val)
|
||||
|
||||
{.pop.}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
{.push debugger:off .} # the user does not want to trace a part
|
||||
# of the standard library!
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
const
|
||||
Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
|
||||
@@ -23,7 +25,8 @@ const
|
||||
proc toLower(c: char): char {.inline.} =
|
||||
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
|
||||
## the number of the parsed characters or 0 in case of an error.
|
||||
var i = start
|
||||
@@ -46,7 +49,8 @@ proc parseHex*(s: string, number: var int, start = 0): int =
|
||||
inc(i)
|
||||
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
|
||||
## the number of the parsed characters or 0 in case of an error.
|
||||
var i = start
|
||||
@@ -116,13 +120,15 @@ proc rawParseInt(s: string, b: var biggestInt, start = 0): int =
|
||||
result = i - start
|
||||
{.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`.
|
||||
## Result is the number of processed chars or 0 if there is no integer.
|
||||
## `EOverflow` is raised if an overflow occurs.
|
||||
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`.
|
||||
## Result is the number of processed chars or 0 if there is no integer.
|
||||
## `EOverflow` is raised if an overflow occurs.
|
||||
@@ -134,7 +140,8 @@ proc parseInt*(s: string, number: var int, start = 0): int =
|
||||
else:
|
||||
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`.
|
||||
## Result is the number of processed chars or 0 if there occured a parsing
|
||||
## error.
|
||||
@@ -206,7 +213,8 @@ proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int =
|
||||
number = number * sign
|
||||
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`.
|
||||
## Result is the number of processed chars or 0 if there occured a parsing
|
||||
## error.
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
## .. include:: ../doc/pegdocs.txt
|
||||
##
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
const
|
||||
useUnicode = true ## change this to deactivate proper UTF-8 support
|
||||
|
||||
@@ -79,7 +81,7 @@ type
|
||||
|
||||
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
|
||||
if t.len != 1:
|
||||
result.kind = pkTerminal
|
||||
@@ -88,23 +90,25 @@ proc term*(t: string): TPeg =
|
||||
result.kind = pkChar
|
||||
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
|
||||
result.kind = pkTerminalIgnoreCase
|
||||
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
|
||||
result.kind = pkTerminalIgnoreStyle
|
||||
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
|
||||
assert t != '\0'
|
||||
result.kind = pkChar
|
||||
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`
|
||||
assert '\0' notin s
|
||||
result.kind = pkCharChoice
|
||||
@@ -136,7 +140,8 @@ template multipleOp(k: TPegKind, localOpt: expr) =
|
||||
if result.len == 1:
|
||||
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`
|
||||
multipleOp(pkOrderedChoice, addChoice)
|
||||
|
||||
@@ -149,11 +154,12 @@ proc addSequence(dest: var TPeg, elem: TPeg) =
|
||||
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`
|
||||
multipleOp(pkSequence, addSequence)
|
||||
|
||||
proc `?`*(a: TPeg): TPeg =
|
||||
proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
|
||||
## constructs an optional for the PEG `a`
|
||||
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
|
||||
pkGreedyRepSet}:
|
||||
@@ -164,7 +170,7 @@ proc `?`*(a: TPeg): TPeg =
|
||||
result.kind = pkOption
|
||||
result.sons = @[a]
|
||||
|
||||
proc `*`*(a: TPeg): TPeg =
|
||||
proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
|
||||
## constructs a "greedy repetition" for the PEG `a`
|
||||
case a.kind
|
||||
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
|
||||
@@ -182,7 +188,7 @@ proc `*`*(a: TPeg): TPeg =
|
||||
result.kind = pkGreedyRep
|
||||
result.sons = @[a]
|
||||
|
||||
proc `@`*(a: TPeg): TPeg =
|
||||
proc `@`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
|
||||
## constructs a "search" for the PEG `a`
|
||||
result.kind = pkSearch
|
||||
result.sons = @[a]
|
||||
@@ -199,16 +205,16 @@ when false:
|
||||
for i in 0..a.sons.len-1:
|
||||
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`
|
||||
return sequence(a, *a)
|
||||
|
||||
proc `&`*(a: TPeg): TPeg =
|
||||
proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
|
||||
## constructs an "and predicate" with the PEG `a`
|
||||
result.kind = pkAndPredicate
|
||||
result.sons = @[a]
|
||||
|
||||
proc `!`*(a: TPeg): TPeg =
|
||||
proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
|
||||
## constructs a "not predicate" with the PEG `a`
|
||||
result.kind = pkNotPredicate
|
||||
result.sons = @[a]
|
||||
@@ -225,24 +231,27 @@ proc newLine*: TPeg {.inline.} =
|
||||
## constructs the PEG `newline`:idx: (``\n``)
|
||||
result.kind = pkNewline
|
||||
|
||||
proc capture*(a: TPeg): TPeg =
|
||||
proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
|
||||
## constructs a capture with the PEG `a`
|
||||
result.kind = pkCapture
|
||||
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
|
||||
## from 1.
|
||||
result.kind = pkBackRef
|
||||
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
|
||||
## from 1. Ignores case for matching.
|
||||
result.kind = pkBackRefIgnoreCase
|
||||
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
|
||||
## from 1. Ignores style for matching.
|
||||
result.kind = pkBackRefIgnoreStyle
|
||||
@@ -263,7 +272,8 @@ proc spaceCost(n: TPeg): int =
|
||||
inc(result, spaceCost(n.sons[i]))
|
||||
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
|
||||
assert n != nil
|
||||
if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
|
||||
@@ -273,7 +283,8 @@ proc nonterminal*(n: PNonTerminal): TPeg =
|
||||
result.kind = pkNonTerminal
|
||||
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
|
||||
new(result)
|
||||
result.name = name
|
||||
@@ -432,7 +443,7 @@ proc toStrAux(r: TPeg, res: var string) =
|
||||
toStrAux(r.sons[i], res)
|
||||
add(res, "\n")
|
||||
|
||||
proc `$` *(r: TPeg): string =
|
||||
proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
|
||||
## converts a PEG to its string representation
|
||||
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
|
||||
|
||||
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
|
||||
## the captured substrings in the array ``matches``. If it does not
|
||||
## 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:
|
||||
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``.
|
||||
var c: TMatchClosure
|
||||
result = m(s, pattern, start, c) == len(s)-start
|
||||
|
||||
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,
|
||||
## 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
|
||||
@@ -626,7 +638,8 @@ proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
for i in 0..c.ml-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,
|
||||
## 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
|
||||
@@ -635,7 +648,7 @@ proc matchLen*(s: string, pattern: TPeg, start = 0): int =
|
||||
result = m(s, pattern, start, c)
|
||||
|
||||
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
|
||||
## substrings in the array ``matches``. If it does not match, nothing
|
||||
## is written into ``matches`` and -1 is returned.
|
||||
@@ -644,7 +657,8 @@ proc find*(s: string, pattern: TPeg, matches: var openarray[string],
|
||||
return -1
|
||||
# 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
|
||||
## match, -1 is returned.
|
||||
for i in 0 .. s.len-1:
|
||||
@@ -675,25 +689,29 @@ template `=~`*(s: string, pattern: TPeg): expr =
|
||||
|
||||
# ------------------------- 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``
|
||||
return find(s, pattern, start) >= 0
|
||||
|
||||
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``
|
||||
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`
|
||||
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`
|
||||
for i in 0 .. s.len-1:
|
||||
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`
|
||||
## 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))
|
||||
|
||||
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`
|
||||
## applied in parallel.
|
||||
result = ""
|
||||
@@ -740,7 +759,8 @@ proc parallelReplace*(s: string, subs: openArray[
|
||||
add(result, copy(s, i))
|
||||
|
||||
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
|
||||
## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
|
||||
## 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:
|
||||
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.
|
||||
accumulateResult(split(s, sep))
|
||||
|
||||
@@ -1265,6 +1286,8 @@ proc primary(p: var TPegParser): TPeg =
|
||||
of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
|
||||
of "w": result = charset({'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
|
||||
else: pegError(p, "unknown built-in: " & p.tok.literal)
|
||||
getTok(p)
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
## Leaves can be cached for better memory efficiency at the cost of
|
||||
## runtime efficiency.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
{.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
|
||||
# pointers.
|
||||
|
||||
proc len*(a: PRope): int =
|
||||
proc len*(a: PRope): int {.rtl, extern: "nro$1".} =
|
||||
## the rope's length
|
||||
if a == nil: result = 0
|
||||
else: result = a.length
|
||||
@@ -126,7 +128,7 @@ proc insertInCache(s: string, tree: PRope): PRope =
|
||||
result.left = t
|
||||
t.right = nil
|
||||
|
||||
proc rope*(s: string): PRope =
|
||||
proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
|
||||
## Converts a string to a rope.
|
||||
if s.len == 0:
|
||||
result = nil
|
||||
@@ -136,25 +138,25 @@ proc rope*(s: string): PRope =
|
||||
else:
|
||||
result = newRope(s)
|
||||
|
||||
proc rope*(i: BiggestInt): PRope =
|
||||
proc rope*(i: BiggestInt): PRope {.rtl, extern: "nro$1BiggestInt".} =
|
||||
## Converts an int to a rope.
|
||||
result = rope($i)
|
||||
|
||||
proc rope*(f: BiggestFloat): PRope =
|
||||
proc rope*(f: BiggestFloat): PRope {.rtl, extern: "nro$1BiggestFloat".} =
|
||||
## Converts a float to a rope.
|
||||
result = rope($f)
|
||||
|
||||
proc disableCache*() =
|
||||
proc disableCache*() {.rtl, extern: "nro$1".} =
|
||||
## the cache is discarded and disabled. The GC will reuse its used memory.
|
||||
cache = nil
|
||||
cacheEnabled = false
|
||||
|
||||
proc enableCache*() =
|
||||
proc enableCache*() {.rtl, extern: "nro$1".} =
|
||||
## Enables the caching of leaves. This reduces the memory footprint at
|
||||
## the cost of runtime efficiency.
|
||||
cacheEnabled = true
|
||||
|
||||
proc `&`*(a, b: PRope): PRope =
|
||||
proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
|
||||
## the concatenation operator for ropes.
|
||||
if a == nil:
|
||||
result = b
|
||||
@@ -174,27 +176,27 @@ proc `&`*(a, b: PRope): PRope =
|
||||
result.left = a
|
||||
result.right = b
|
||||
|
||||
proc `&`*(a: PRope, b: string): PRope =
|
||||
proc `&`*(a: PRope, b: string): PRope {.rtl, extern: "nroConcRopeStr".} =
|
||||
## the concatenation operator for ropes.
|
||||
result = a & rope(b)
|
||||
|
||||
proc `&`*(a: string, b: PRope): PRope =
|
||||
proc `&`*(a: string, b: PRope): PRope {.rtl, extern: "nroConcStrRope".} =
|
||||
## the concatenation operator for ropes.
|
||||
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.
|
||||
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`.
|
||||
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`.
|
||||
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
|
||||
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
|
||||
var x = r
|
||||
@@ -229,11 +231,11 @@ iterator items*(r: PRope): char =
|
||||
for s in leaves(r):
|
||||
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.
|
||||
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.
|
||||
result = newString(r.len)
|
||||
setLen(result, 0)
|
||||
@@ -289,7 +291,8 @@ when false:
|
||||
if i - 1 >= start:
|
||||
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``
|
||||
## nor ``${identifier}`` notations.
|
||||
var i = 0
|
||||
@@ -333,11 +336,12 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope =
|
||||
if i - 1 >= start:
|
||||
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)``.
|
||||
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`.
|
||||
var bufSize = 1024 # reasonable start value
|
||||
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?
|
||||
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
|
||||
## exist, false is returned.
|
||||
var bin: TFile
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
import
|
||||
os, hashes, strutils
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TStringTableMode* = enum ## describes the tables operation mode
|
||||
modeCaseSensitive, ## the table is case sensitive
|
||||
@@ -29,28 +31,7 @@ type
|
||||
|
||||
PStringTable* = ref TStringTable ## use this type to declare string tables
|
||||
|
||||
proc newStringTable*(keyValuePairs: openarray[string],
|
||||
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 =
|
||||
proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} =
|
||||
## returns the number of keys in `t`.
|
||||
result = t.counter
|
||||
|
||||
@@ -70,29 +51,12 @@ type
|
||||
useKey ## do not replace ``$key`` if it is not found
|
||||
## in the table (or in the environment)
|
||||
|
||||
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string
|
||||
## The `%` operator for string tables.
|
||||
|
||||
# implementation
|
||||
|
||||
const
|
||||
growthFactor = 2
|
||||
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 =
|
||||
case t.mode
|
||||
of modeCaseSensitive: result = hashes.hash(key)
|
||||
@@ -121,13 +85,17 @@ proc RawGet(t: PStringTable, key: string): int =
|
||||
h = nextTry(h, high(t.data))
|
||||
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
|
||||
index = RawGet(t, key)
|
||||
if index >= 0: result = t.data[index].val
|
||||
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
|
||||
|
||||
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)
|
||||
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)
|
||||
if index >= 0:
|
||||
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
|
||||
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
|
||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||
result = ""
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
|
||||
## 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.
|
||||
|
||||
import parseutils
|
||||
@@ -18,6 +18,8 @@ import parseutils
|
||||
{.push debugger:off .} # the user does not want to trace a part
|
||||
# of the standard library!
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TCharSet* = set[char] # for compatibility with Nim
|
||||
|
||||
@@ -40,7 +42,151 @@ const
|
||||
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
|
||||
## 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
|
||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||
## `string interpolation`:idx:.
|
||||
@@ -77,57 +223,38 @@ proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
|
||||
##
|
||||
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
|
||||
## 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]``.
|
||||
return formatstr % [a]
|
||||
|
||||
proc addf*(s: var string, formatstr: string, a: openarray[string])
|
||||
## The same as ``add(s, formatstr % a)``, but more efficient.
|
||||
|
||||
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
|
||||
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
|
||||
rtl, extern: "nsuStrip".} =
|
||||
## Strips whitespace from `s` and returns the resulting string.
|
||||
## If `leading` is true, leading 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.}
|
||||
## 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
|
||||
proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
|
||||
## Converts a character `c` to its octal representation. The resulting
|
||||
## 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 =
|
||||
## Splits the string `s` into substrings.
|
||||
@@ -228,86 +355,123 @@ iterator splitLines*(s: string): string =
|
||||
else: break # was '\0'
|
||||
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
|
||||
## sequence of substrings.
|
||||
accumulateResult(splitLines(s))
|
||||
|
||||
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
|
||||
## sequence of substrings.
|
||||
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
|
||||
## of substrings.
|
||||
accumulateResult(split(s, sep))
|
||||
|
||||
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.}
|
||||
## Compares two strings in a case insensitive manner. Returns:
|
||||
##
|
||||
## | 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.}
|
||||
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||
rtl, extern: "nsuToHex".} =
|
||||
## Converts `x` to its hexadecimal representation. The resulting string
|
||||
## will be exactly `len` characters long. No prefix like ``0x``
|
||||
## 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
|
||||
## will be minimally `minchars` characters long. This is achieved by
|
||||
## 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
|
||||
## 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
|
||||
## 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
|
||||
## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
|
||||
## ``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
|
||||
## a valid integer, `EInvalidValue` is raised. `s` can have one of the
|
||||
## following optional prefixes: ``0x``, ``0X``, ``#``.
|
||||
## 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
|
||||
## 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``.
|
||||
## 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``.
|
||||
## 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,
|
||||
inline.} =
|
||||
@@ -335,32 +499,8 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
||||
if c notin theSet: return false
|
||||
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
|
||||
# 345
|
||||
# 012345
|
||||
# 345
|
||||
|
||||
when false:
|
||||
proc abbrev(s: string, possibilities: openarray[string]): int =
|
||||
@@ -373,112 +513,10 @@ when false:
|
||||
if result >= 0: return -2 # ambiguous
|
||||
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`.
|
||||
if len(a) > 0:
|
||||
var L = sep.len * (a.len-1)
|
||||
@@ -492,7 +530,8 @@ proc join*(a: openArray[string], sep: string): string =
|
||||
else:
|
||||
result = ""
|
||||
|
||||
proc join*(a: openArray[string]): string =
|
||||
proc join*(a: openArray[string]): string {.
|
||||
noSideEffect, rtl, extern: "nsuJoin".} =
|
||||
## concatenates all strings in `a`.
|
||||
if len(a) > 0:
|
||||
var L = 0
|
||||
@@ -503,51 +542,6 @@ proc join*(a: openArray[string]): string =
|
||||
else:
|
||||
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
|
||||
TSkipTable = array[Char, int]
|
||||
|
||||
@@ -571,31 +565,52 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||
inc(j, a[s[j+m]])
|
||||
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
|
||||
preprocessSub(sub, 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:
|
||||
if sub == s[i]: return i
|
||||
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:
|
||||
if s[i] in chars: return i
|
||||
return -1
|
||||
|
||||
proc contains(s: string, chars: set[char]): bool =
|
||||
return find(s, chars) >= 0
|
||||
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 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
|
||||
|
||||
proc contains(s, sub: string): bool =
|
||||
proc contains*(s, sub: string): bool {.noSideEffect.} =
|
||||
## Same as ``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`.
|
||||
var a: TSkipTable
|
||||
result = ""
|
||||
@@ -610,7 +625,8 @@ proc replace*(s, sub, by: string): string =
|
||||
# copy the rest:
|
||||
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.
|
||||
result = newString(s.len)
|
||||
var i = 0
|
||||
@@ -619,7 +635,8 @@ proc replace*(s: string, sub, by: char): string =
|
||||
else: result[i] = s[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
|
||||
## `s` itself, it does not return a copy.
|
||||
var i = first
|
||||
@@ -631,27 +648,12 @@ proc delete*(s: var string, first, last: int) =
|
||||
inc(j)
|
||||
setlen(s, newLen)
|
||||
|
||||
# parsing numbers:
|
||||
|
||||
proc toHex(x: BiggestInt, len: int): string =
|
||||
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 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 =
|
||||
proc ParseOctInt*(s: string): int {.noSideEffect,
|
||||
rtl, extern: "nsuParseOctInt".} =
|
||||
## Parses an octal integer value contained in `s`. If `s` is not
|
||||
## a valid integer, `EInvalidValue` is raised. `s` can have one of the
|
||||
## following optional prefixes: ``0o``, ``0O``.
|
||||
## Underscores within `s` are ignored.
|
||||
var i = 0
|
||||
if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
|
||||
while true:
|
||||
@@ -663,30 +665,8 @@ proc ParseOctInt*(s: string): int =
|
||||
of '\0': break
|
||||
else: raise newException(EInvalidValue, "invalid integer: " & s)
|
||||
|
||||
proc ParseHexInt(s: string): int =
|
||||
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 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 =
|
||||
proc toOct*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||
rtl, extern: "nsuToOct".} =
|
||||
## converts `x` into its octal representation. The resulting string is
|
||||
## always `len` characters long. No leading ``0o`` prefix is generated.
|
||||
var
|
||||
@@ -699,7 +679,8 @@ proc toOct*(x: BiggestInt, len: int): string =
|
||||
shift = shift + 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
|
||||
## always `len` characters long. No leading ``0b`` prefix is generated.
|
||||
var
|
||||
@@ -712,7 +693,8 @@ proc toBin*(x: BiggestInt, len: int): string =
|
||||
shift = shift + 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.
|
||||
## Even though the algorithm works with any string `s`, it is only useful
|
||||
## if `s` contains a number.
|
||||
@@ -730,7 +712,8 @@ proc insertSep*(s: string, sep = '_', digits = 3): string =
|
||||
inc(j)
|
||||
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):
|
||||
## * replaces any ``\`` by ``\\``
|
||||
## * replaces any ``'`` by ``\'``
|
||||
@@ -752,7 +735,8 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string =
|
||||
else: add(result, c)
|
||||
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.
|
||||
## The checking also uses a domain list.
|
||||
## Note: This will be moved into another module soon.
|
||||
@@ -779,7 +763,8 @@ proc validEmailAddress*(s: string): bool =
|
||||
"aero", "jobs", "museum": return true
|
||||
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
|
||||
## with a character of the set `IdentStartChars` and is followed by any
|
||||
## number of characters of the set `IdentChars`.
|
||||
@@ -788,7 +773,8 @@ proc validIdentifier*(s: string): bool =
|
||||
if s[i] notin IdentChars: return false
|
||||
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
|
||||
## distance algorithm with only a linear memory overhead. This implementation
|
||||
## is highly optimized!
|
||||
|
||||
@@ -11,12 +11,14 @@
|
||||
## This module contains routines and types for dealing with time.
|
||||
## This module is available for the ECMAScript target.
|
||||
|
||||
{.push debugger:off .} # the user does not want to trace a part
|
||||
# of the standard library!
|
||||
{.push debugger:off.} # the user does not want to trace a part
|
||||
# of the standard library!
|
||||
|
||||
import
|
||||
strutils
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
TMonth* = enum ## represents a month
|
||||
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
|
||||
@@ -125,20 +127,17 @@ proc `$` *(timeInfo: TTimeInfo): string
|
||||
proc `$` *(time: TTime): string
|
||||
## converts a calendar time to a string representation.
|
||||
|
||||
proc getDateStr*(): string
|
||||
## gets the current date as a string of the format ``YYYY-MM-DD``.
|
||||
|
||||
proc getClockStr*(): string
|
||||
## gets the current clock time as a string of the format ``HH:MM:SS``.
|
||||
|
||||
proc `-` *(a, b: TTime): int64
|
||||
proc `-` *(a, b: TTime): int64{.
|
||||
rtl, extern: "ntDiffTime".}
|
||||
## 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.
|
||||
result = a - b < 0
|
||||
|
||||
proc `<=` * (a, b: TTime): bool =
|
||||
proc `<=` * (a, b: TTime): bool {.
|
||||
rtl, extern: "ntLeTime".}=
|
||||
## returns true iff ``a <= b``.
|
||||
result = a - b <= 0
|
||||
|
||||
@@ -147,12 +146,12 @@ proc getStartMilsecs*(): int {.deprecated.}
|
||||
## version 0.8.10.** Use ``realTime`` or ``cpuTime`` instead.
|
||||
|
||||
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
|
||||
## because sub-second resolution is likely to be supported (depending
|
||||
## 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
|
||||
## seconds. This may be more useful for benchmarking than ``epochTime``.
|
||||
## 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.isdst = -1
|
||||
|
||||
proc `-` (a, b: TTime): int64 =
|
||||
return toBiggestInt(difftime(a, b))
|
||||
when not defined(useNimRtl):
|
||||
proc `-` (a, b: TTime): int64 =
|
||||
return toBiggestInt(difftime(a, b))
|
||||
|
||||
proc getStartMilsecs(): int =
|
||||
#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``)
|
||||
result = TTime((t - epochDiff) div rateDiff)
|
||||
|
||||
proc epochTime(): float =
|
||||
when defined(posix):
|
||||
var a: Ttimeval
|
||||
posix_gettimeofday(a)
|
||||
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.001
|
||||
# why 0.001 instead of 0.00_0001? I don't know.
|
||||
elif defined(windows):
|
||||
var f: winlean.Filetime
|
||||
GetSystemTimeAsFileTime(f)
|
||||
var i64 = rdFileTime(f) - epochDiff
|
||||
var secs = i64 div rateDiff
|
||||
var subsecs = i64 mod rateDiff
|
||||
result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
|
||||
else:
|
||||
{.error: "unknown OS".}
|
||||
|
||||
proc cpuTime(): float =
|
||||
result = toFloat(int(clock())) / toFloat(clocksPerSec)
|
||||
when not defined(useNimRtl):
|
||||
proc epochTime(): float =
|
||||
when defined(posix):
|
||||
var a: Ttimeval
|
||||
posix_gettimeofday(a)
|
||||
result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.001
|
||||
# why 0.001 instead of 0.00_0001? I don't know.
|
||||
elif defined(windows):
|
||||
var f: winlean.Filetime
|
||||
GetSystemTimeAsFileTime(f)
|
||||
var i64 = rdFileTime(f) - epochDiff
|
||||
var secs = i64 div rateDiff
|
||||
var subsecs = i64 mod rateDiff
|
||||
result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
|
||||
else:
|
||||
{.error: "unknown OS".}
|
||||
|
||||
proc cpuTime(): float =
|
||||
result = toFloat(int(clock())) / toFloat(clocksPerSec)
|
||||
|
||||
else:
|
||||
proc getTime(): TTime {.importc: "new Date", nodecl.}
|
||||
@@ -358,12 +359,15 @@ else:
|
||||
## get the miliseconds from the start of the program
|
||||
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())
|
||||
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 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())
|
||||
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
|
||||
':' & intToStr(ti.second, 2)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
type
|
||||
irune = int # underlying type of TRune
|
||||
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)
|
||||
|
||||
proc runeLen*(s: string): int =
|
||||
proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
|
||||
## returns the number of Unicode characters of the string `s`.
|
||||
var i = 0
|
||||
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`
|
||||
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
|
||||
var i = irune(c)
|
||||
if i <=% 127:
|
||||
@@ -1073,7 +1075,7 @@ proc binarySearch(c: irune, tab: openArray[iRune], len, stride: int): int =
|
||||
return t
|
||||
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.
|
||||
## If possible, prefer `toLower` over `toUpper`.
|
||||
var c = irune(c)
|
||||
@@ -1085,7 +1087,7 @@ proc toLower*(c: TRune): TRune =
|
||||
return TRune(c + toLowerSinglets[p+1] - 500)
|
||||
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.
|
||||
## If possible, prefer `toLower` over `toUpper`.
|
||||
var c = irune(c)
|
||||
@@ -1097,14 +1099,14 @@ proc toUpper*(c: TRune): TRune =
|
||||
return TRune(c + toUpperSinglets[p+1] - 500)
|
||||
return TRune(c)
|
||||
|
||||
proc toTitle*(c: TRune): TRune =
|
||||
proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1".} =
|
||||
var c = irune(c)
|
||||
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
|
||||
if p >= 0 and c == toTitleSinglets[p]:
|
||||
return TRune(c + toTitleSinglets[p+1] - 500)
|
||||
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
|
||||
## If possible, prefer `isLower` over `isUpper`.
|
||||
var c = irune(c)
|
||||
@@ -1116,7 +1118,7 @@ proc isLower*(c: TRune): bool =
|
||||
if p >= 0 and c == toUpperSinglets[p]:
|
||||
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
|
||||
## If possible, prefer `isLower` over `isUpper`.
|
||||
var c = irune(c)
|
||||
@@ -1128,7 +1130,7 @@ proc isUpper*(c: TRune): bool =
|
||||
if p >= 0 and c == toLowerSinglets[p]:
|
||||
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)
|
||||
if isUpper(c) or isLower(c):
|
||||
return true
|
||||
@@ -1140,10 +1142,10 @@ proc isAlpha*(c: TRune): bool =
|
||||
if p >= 0 and c == alphaSinglets[p]:
|
||||
return true
|
||||
|
||||
proc isTitle*(c: TRune): bool =
|
||||
proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1".} =
|
||||
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
|
||||
var c = irune(c)
|
||||
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
|
||||
@@ -1159,7 +1161,7 @@ iterator runes*(s: string): TRune =
|
||||
fastRuneAt(s, i, result, true)
|
||||
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:
|
||||
##
|
||||
## | 0 iff a == b
|
||||
@@ -1175,6 +1177,3 @@ proc cmpRunesIgnoreCase*(a, b: string): int =
|
||||
result = irune(toLower(ar)) - irune(toLower(br))
|
||||
if result != 0: return
|
||||
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
|
||||
## ``system``.
|
||||
|
||||
{.push hints: off.}
|
||||
{.push hints: on.}
|
||||
|
||||
type
|
||||
int* {.magic: Int.} ## default integer type; bitwidth depends on
|
||||
@@ -720,25 +720,6 @@ const
|
||||
include "system/inclrtl"
|
||||
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: openArray[T]) {.noSideEffect.} =
|
||||
## 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
|
||||
## 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.}
|
||||
## adds/registers a quit procedure. Each call to ``addQuitProc``
|
||||
## registers another quit procedure. Up to 30 procedures can be
|
||||
@@ -1004,12 +981,6 @@ const
|
||||
## and expect a reasonable result - use the `classify` procedure
|
||||
## 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:
|
||||
|
||||
proc getOccupiedMem*(): int {.rtl.}
|
||||
@@ -1265,6 +1236,35 @@ template accumulateResult*(iter: expr) =
|
||||
# however, stack-traces are available for most parts
|
||||
# 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".}
|
||||
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
|
||||
## available for the ECMAScript target too!
|
||||
@@ -1342,7 +1342,7 @@ when not defined(EcmaScript) and not defined(NimrodVM):
|
||||
# we use binary mode in Windows:
|
||||
setmode(fileno(c_stdin), O_BINARY)
|
||||
setmode(fileno(c_stdout), O_BINARY)
|
||||
|
||||
|
||||
when defined(endb):
|
||||
proc endbStep()
|
||||
|
||||
|
||||
@@ -531,48 +531,49 @@ proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
|
||||
|
||||
# ---------------------- interface to programs -------------------------------
|
||||
|
||||
proc alloc(size: int): pointer =
|
||||
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
||||
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
||||
assert(not isAllocatedPtr(allocator, result))
|
||||
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
|
||||
when not defined(useNimRtl):
|
||||
proc alloc(size: int): pointer =
|
||||
result = rawAlloc(allocator, size+sizeof(TFreeCell))
|
||||
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
|
||||
assert(not isAllocatedPtr(allocator, result))
|
||||
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
|
||||
|
||||
proc alloc0(size: int): pointer =
|
||||
result = alloc(size)
|
||||
zeroMem(result, size)
|
||||
proc alloc0(size: int): pointer =
|
||||
result = alloc(size)
|
||||
zeroMem(result, size)
|
||||
|
||||
proc dealloc(p: pointer) =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
assert(cast[ptr TFreeCell](x).zeroField == 1)
|
||||
rawDealloc(allocator, x)
|
||||
assert(not isAllocatedPtr(allocator, x))
|
||||
proc dealloc(p: pointer) =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
assert(cast[ptr TFreeCell](x).zeroField == 1)
|
||||
rawDealloc(allocator, x)
|
||||
assert(not isAllocatedPtr(allocator, x))
|
||||
|
||||
proc ptrSize(p: pointer): int =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
result = pageAddr(x).size - sizeof(TFreeCell)
|
||||
proc ptrSize(p: pointer): int =
|
||||
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
|
||||
result = pageAddr(x).size - sizeof(TFreeCell)
|
||||
|
||||
proc realloc(p: pointer, newsize: int): pointer =
|
||||
if newsize > 0:
|
||||
result = alloc(newsize)
|
||||
if p != nil:
|
||||
copyMem(result, p, ptrSize(p))
|
||||
proc realloc(p: pointer, newsize: int): pointer =
|
||||
if newsize > 0:
|
||||
result = alloc(newsize)
|
||||
if p != nil:
|
||||
copyMem(result, p, ptrSize(p))
|
||||
dealloc(p)
|
||||
elif p != nil:
|
||||
dealloc(p)
|
||||
elif p != nil:
|
||||
dealloc(p)
|
||||
|
||||
proc countFreeMem(): int =
|
||||
# only used for assertions
|
||||
var it = allocator.freeChunksList
|
||||
while it != nil:
|
||||
inc(result, it.size)
|
||||
it = it.next
|
||||
proc countFreeMem(): int =
|
||||
# only used for assertions
|
||||
var it = allocator.freeChunksList
|
||||
while it != nil:
|
||||
inc(result, it.size)
|
||||
it = it.next
|
||||
|
||||
proc getFreeMem(): int =
|
||||
result = allocator.freeMem
|
||||
#assert(result == countFreeMem())
|
||||
proc getFreeMem(): int =
|
||||
result = allocator.freeMem
|
||||
#assert(result == countFreeMem())
|
||||
|
||||
proc getTotalMem(): int = return allocator.currMem
|
||||
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
|
||||
proc getTotalMem(): int = return allocator.currMem
|
||||
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
|
||||
|
||||
when isMainModule:
|
||||
const iterations = 4000_000
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -61,11 +61,14 @@ when defined(posix):
|
||||
proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {.
|
||||
importc, header: "<dlfcn.h>".}
|
||||
|
||||
proc dlerror(): cstring {.importc, header: "<dlfcn.h>".}
|
||||
|
||||
proc nimUnloadLibrary(lib: TLibHandle) =
|
||||
dlclose(lib)
|
||||
|
||||
proc nimLoadLibrary(path: string): TLibHandle =
|
||||
result = dlopen(path, RTLD_NOW)
|
||||
#c_fprintf(c_stdout, "%s\n", dlerror())
|
||||
|
||||
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
|
||||
result = dlsym(lib, name)
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2009 Andreas Rumpf
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
|
||||
# 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
|
||||
# unsafe!
|
||||
# to work if there is no more memory (but it doesn't yet!).
|
||||
|
||||
when not defined(windows) or not defined(guiapp):
|
||||
proc writeToStdErr(msg: CString) = write(stdout, msg)
|
||||
|
||||
@@ -74,14 +74,6 @@ var
|
||||
# This is wasteful but safe. This is a lock against recursive garbage
|
||||
# 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.} =
|
||||
if (c.refcount and rcZct) == 0:
|
||||
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".} =
|
||||
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:
|
||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||
@@ -214,8 +188,13 @@ proc prepareDealloc(cell: PCell) =
|
||||
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
|
||||
dec(recGcLock)
|
||||
|
||||
proc PossibleRoot(gch: var TGcHeap, c: PCell) {.inline.} =
|
||||
if canbeCycleRoot(c): incl(gch.cycleRoots, c)
|
||||
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||
# 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.} =
|
||||
when stressGC:
|
||||
@@ -224,19 +203,19 @@ proc decRef(c: PCell) {.inline.} =
|
||||
assert(c.refcount >=% rcIncrement)
|
||||
c.refcount = c.refcount -% rcIncrement
|
||||
if c.refcount <% rcIncrement:
|
||||
addZCT(gch.zct, c)
|
||||
rtlAddZCT(c)
|
||||
elif canBeCycleRoot(c):
|
||||
incl(gch.cycleRoots, c)
|
||||
rtlAddCycleRoot(c)
|
||||
|
||||
proc incRef(c: PCell) {.inline.} =
|
||||
c.refcount = c.refcount +% rcIncrement
|
||||
if canBeCycleRoot(c):
|
||||
incl(gch.cycleRoots, c)
|
||||
rtlAddCycleRoot(c)
|
||||
|
||||
proc nimGCref(p: pointer) {.compilerRtl, inl.} = incRef(usrToCell(p))
|
||||
proc nimGCunref(p: pointer) {.compilerRtl, inl.} = decRef(usrToCell(p))
|
||||
proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(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!
|
||||
assert(not isOnStack(dest))
|
||||
# BUGFIX: first incRef then decRef!
|
||||
@@ -244,7 +223,7 @@ proc asgnRef(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
||||
if dest^ != nil: decRef(usrToCell(dest^))
|
||||
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
|
||||
# cycle is possible.
|
||||
if src != nil:
|
||||
@@ -254,30 +233,34 @@ proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
|
||||
var c = usrToCell(dest^)
|
||||
c.refcount = c.refcount -% rcIncrement
|
||||
if c.refcount <% rcIncrement:
|
||||
addZCT(gch.zct, c)
|
||||
rtlAddZCT(c)
|
||||
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 src != nil: incRef(usrToCell(src))
|
||||
if dest^ != nil: decRef(usrToCell(dest^))
|
||||
dest^ = src
|
||||
|
||||
proc initGC() =
|
||||
when traceGC:
|
||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
||||
gch.stat.stackScans = 0
|
||||
gch.stat.cycleCollections = 0
|
||||
gch.stat.maxThreshold = 0
|
||||
gch.stat.maxStackSize = 0
|
||||
gch.stat.maxStackCells = 0
|
||||
gch.stat.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.tempStack)
|
||||
Init(gch.cycleRoots)
|
||||
Init(gch.decStack)
|
||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||
when not defined(useNimRtl):
|
||||
when traceGC:
|
||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
||||
gch.stat.stackScans = 0
|
||||
gch.stat.cycleCollections = 0
|
||||
gch.stat.maxThreshold = 0
|
||||
gch.stat.maxStackSize = 0
|
||||
gch.stat.maxStackCells = 0
|
||||
gch.stat.cycleTableSize = 0
|
||||
# init the rt
|
||||
init(gch.zct)
|
||||
init(gch.tempStack)
|
||||
Init(gch.cycleRoots)
|
||||
Init(gch.decStack)
|
||||
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
|
||||
|
||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||
var d = cast[TAddress](dest)
|
||||
@@ -325,7 +308,7 @@ proc checkCollection {.inline.} =
|
||||
if recGcLock == 0:
|
||||
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
|
||||
assert(typ.kind in {tyRef, tyString, tySequence})
|
||||
checkCollection()
|
||||
@@ -357,7 +340,7 @@ proc newObj(typ: PNimType, size: int): pointer =
|
||||
gcTrace(res, csAllocated)
|
||||
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))
|
||||
cast[PGenericSeq](result).len = len
|
||||
cast[PGenericSeq](result).space = len
|
||||
@@ -490,17 +473,18 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
|
||||
else:
|
||||
const stackIncreases = false
|
||||
|
||||
proc setStackBottom(theStackBottom: pointer) =
|
||||
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
|
||||
# the first init must be the one that defines the stack bottom:
|
||||
if stackBottom == nil: stackBottom = theStackBottom
|
||||
else:
|
||||
var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2
|
||||
var b = cast[TAddress](stackBottom)
|
||||
when stackIncreases:
|
||||
stackBottom = cast[pointer](min(a, b))
|
||||
when not defined(useNimRtl):
|
||||
proc setStackBottom(theStackBottom: pointer) =
|
||||
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
|
||||
# the first init must be the one that defines the stack bottom:
|
||||
if stackBottom == nil: stackBottom = theStackBottom
|
||||
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.} =
|
||||
var stackTop: array[0..1, pointer]
|
||||
@@ -647,22 +631,41 @@ proc collectCT(gch: var TGcHeap) =
|
||||
gch.stat.maxThreshold = max(gch.stat.maxThreshold, cycleThreshold)
|
||||
unmarkStackAndRegisters(gch)
|
||||
|
||||
proc GC_fullCollect() =
|
||||
var oldThreshold = cycleThreshold
|
||||
cycleThreshold = 0 # forces cycle collection
|
||||
collectCT(gch)
|
||||
cycleThreshold = oldThreshold
|
||||
when not defined(useNimRtl):
|
||||
proc GC_disable() = inc(recGcLock)
|
||||
proc GC_enable() =
|
||||
if recGcLock > 0: dec(recGcLock)
|
||||
|
||||
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()
|
||||
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
|
||||
|
||||
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!".}
|
||||
|
||||
when defined(createNimRtl):
|
||||
# NOTE: compilerproc cannot make use of name mangling!
|
||||
{.pragma: rtl, exportc: "nimrtl_$1", dynlib.}
|
||||
{.pragma: inl.}
|
||||
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
|
||||
|
||||
@@ -187,28 +187,6 @@ elif defined(nogc):
|
||||
dest^ = src
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
var evalTemplateCounter: int = 0
|
||||
# to prevend endless recursion in templates instantation
|
||||
|
||||
proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode =
|
||||
# to prevend endless recursion in templates
|
||||
# instantation
|
||||
var
|
||||
f, a: int
|
||||
arg: PNode
|
||||
f = sonsLen(s.typ) # if the template has zero arguments, it can be called without ``()``
|
||||
# `n` is then a nkSym or something similar
|
||||
case n.kind
|
||||
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: a = sonsLen(
|
||||
n)
|
||||
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||
a = sonsLen(n)
|
||||
else: a = 0
|
||||
if a > f: liMessage(n.info, errWrongNumberOfArguments)
|
||||
result = copyNode(n)
|
||||
@@ -85,7 +84,8 @@ proc evalTemplate(c: PContext, n: PNode, sym: PSym): PNode =
|
||||
var args: PNode
|
||||
inc(evalTemplateCounter)
|
||||
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)
|
||||
result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
|
||||
dec(evalTemplateCounter)
|
||||
|
||||
5
todo.txt
5
todo.txt
@@ -1,6 +1,7 @@
|
||||
For version 0.8.10
|
||||
==================
|
||||
|
||||
- exception propagation across DLLs
|
||||
- fix exception handling
|
||||
- fix implicit generic routines
|
||||
- fix the streams implementation so that they use methods
|
||||
@@ -61,6 +62,7 @@ Low priority
|
||||
- normalize for the DOM
|
||||
- tlastmod returns wrong results on BSD (Linux, MacOS X: works)
|
||||
- nested tuple unpacking
|
||||
- fast assignment optimization for TPeg
|
||||
|
||||
|
||||
Library
|
||||
@@ -129,4 +131,7 @@ RST
|
||||
---
|
||||
- footnotes; prefix :i: whitespace before :i:, _reference, `reference`__
|
||||
__ anonymous: www.nimrod.org
|
||||
- rewrite the parser to use the new better look ahead technique that c2nim
|
||||
uses
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user