diff options
author | Andreas Rumpf <andreas@andreas-desktop> | 2010-08-08 22:45:21 +0200 |
---|---|---|
committer | Andreas Rumpf <andreas@andreas-desktop> | 2010-08-08 22:45:21 +0200 |
commit | 8098e2a421bf26ad0f350f297f19f34619207443 (patch) | |
tree | ada62dabe6a38c7fbe47d65e2674b9070f2cef3c | |
parent | c9e011e36cf400e1a2e5466a1339f716623508f7 (diff) | |
download | Nim-8098e2a421bf26ad0f350f297f19f34619207443.tar.gz |
inlining of the write barrier for dlls
-rwxr-xr-x | doc/intern.txt | 10 | ||||
-rwxr-xr-x | doc/nimrodc.txt | 25 | ||||
-rwxr-xr-x | doc/pegdocs.txt | 2 | ||||
-rwxr-xr-x | lib/nimrtl.nim | 13 | ||||
-rwxr-xr-x | lib/pure/lexbase.nim | 3 | ||||
-rwxr-xr-x | lib/pure/os.nim | 134 | ||||
-rwxr-xr-x | lib/pure/osproc.nim | 69 | ||||
-rwxr-xr-x | lib/pure/parsecfg.nim | 51 | ||||
-rwxr-xr-x | lib/pure/parseopt.nim | 98 | ||||
-rwxr-xr-x | lib/pure/parseutils.nim | 20 | ||||
-rwxr-xr-x | lib/pure/pegs.nim | 91 | ||||
-rwxr-xr-x | lib/pure/ropes.nim | 42 | ||||
-rwxr-xr-x | lib/pure/strtabs.nim | 78 | ||||
-rwxr-xr-x | lib/pure/strutils.nim | 622 | ||||
-rwxr-xr-x | lib/pure/times.nim | 74 | ||||
-rwxr-xr-x | lib/pure/unicode.nim | 27 | ||||
-rwxr-xr-x | lib/system.nim | 62 | ||||
-rwxr-xr-x | lib/system/alloc.nim | 77 | ||||
-rwxr-xr-x | lib/system/dyncalls.nim | 5 | ||||
-rwxr-xr-x | lib/system/excpt.nim | 6 | ||||
-rwxr-xr-x | lib/system/gc.nim | 167 | ||||
-rwxr-xr-x | lib/system/inclrtl.nim | 1 | ||||
-rwxr-xr-x | lib/system/mmdisp.nim | 22 | ||||
-rwxr-xr-x | rod/semtempl.nim | 10 | ||||
-rwxr-xr-x | todo.txt | 5 |
25 files changed, 860 insertions, 854 deletions
diff --git a/doc/intern.txt b/doc/intern.txt index 50fc9b8b1..c347a498c 100755 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -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 ===================== diff --git a/doc/nimrodc.txt b/doc/nimrodc.txt index 1ada1fffb..997146ede 100755 --- a/doc/nimrodc.txt +++ b/doc/nimrodc.txt @@ -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 =================== diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt index 87b4e25bc..a97f7c765 100755 --- a/doc/pegdocs.txt +++ b/doc/pegdocs.txt @@ -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 diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index c61824f49..68b7d7bd9 100755 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -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 diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index bb207e92a..c875e10bc 100755 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -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 diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 39ff699bb..6ad39d5ca 100755 --- a/lib/pure/os.nim +++ b/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 = + proc paramCount*(): int {.rtl, extern: "nos$1".} = + ## Returns the number of command line arguments given to the + ## application. if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA()) - return ownArgv[i] + result = ownArgv.len-1 - proc paramCount(): int = + 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()) - result = ownArgv.len-1 + return ownArgv[i] -else: +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 diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 764a1f896..082851a81 100755 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -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 diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 4f8f5347f..ae151ff94 100755 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -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 diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 8f4be98f4..1b09934f2 100755 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -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.} diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 0f107793c..ea4b17b65 100755 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -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. diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 969ff8106..6942f97ef 100755 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -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) diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index df85baf92..14a01044f 100755 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -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 diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 38be1e983..9779be5ff 100755 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -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 = "" diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 9e8798043..f5adf1abb 100755 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -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! diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 8af7395dd..d3e2fe7cf 100755 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -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) diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 98d5eba2a..0939a3066 100755 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -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 = -# diff --git a/lib/system.nim b/lib/system.nim index 1addece93..da64b233e 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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() diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 0d3f52b2f..840107440 100755 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -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)) - -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 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)) +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 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 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 diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index d7be3d6ef..88870a209 100755 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -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) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 4d7b41da2..673e5a50e 100755 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -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) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 2ad22d8b6..59ddda5e7 100755 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -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 - -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() +when not defined(useNimRtl): + 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 + + 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() diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 3db6a77ca..e4644b969 100755 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -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.} diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index f0685c5c3..a85d69b0d 100755 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -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" diff --git a/rod/semtempl.nim b/rod/semtempl.nim index f866f77d2..cb4288a56 100755 --- a/rod/semtempl.nim +++ b/rod/semtempl.nim @@ -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) diff --git a/todo.txt b/todo.txt index 773b68a97..19a6a7d06 100755 --- a/todo.txt +++ b/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 + |