inlining of the write barrier for dlls

This commit is contained in:
Andreas Rumpf
2010-08-08 22:45:21 +02:00
parent c9e011e36c
commit 8098e2a421
25 changed files with 856 additions and 850 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,6 +14,8 @@
{.push debugger: off.}
include "system/inclrtl"
import
strutils, times
@@ -145,19 +147,7 @@ const
## The character which separates the base filename from the extension;
## for example, the '.' in ``os.nim``.
# procs dealing with command line arguments:
proc paramCount*(): int
## Returns the number of command line arguments given to the
## application.
proc paramStr*(i: int): string
## Returns the `i`-th command line arguments given to the
## application.
##
## `i` should be in the range `1..paramCount()`, else
## the `EOutOfIndex` exception is raised.
proc OSError*(msg: string = "") {.noinline.} =
proc OSError*(msg: string = "") {.noinline, rtl, extern: "nos$1".} =
## raises an EOS exception with the given message ``msg``.
## If ``msg == ""``, the operating system's error flag
## (``errno``) is converted to a readable error message. On Windows
@@ -182,7 +172,8 @@ proc OSError*(msg: string = "") {.noinline.} =
else:
raise newException(EOS, msg)
proc UnixToNativePath*(path: string): string {.noSideEffect.} =
proc UnixToNativePath*(path: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Converts an UNIX-like path to a native one.
##
## On an UNIX system this does nothing. Else it converts
@@ -227,7 +218,7 @@ proc UnixToNativePath*(path: string): string {.noSideEffect.} =
add result, path[i]
inc(i)
proc existsFile*(filename: string): bool =
proc existsFile*(filename: string): bool {.rtl, extern: "nos$1".} =
## Returns true if the file exists, false otherwise.
when defined(windows):
var a = GetFileAttributesA(filename)
@@ -237,7 +228,7 @@ proc existsFile*(filename: string): bool =
var res: TStat
return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
proc existsDir*(dir: string): bool =
proc existsDir*(dir: string): bool {.rtl, extern: "nos$1".} =
## Returns true iff the directory `dir` exists. If `dir` is a file, false
## is returned.
when defined(windows):
@@ -248,7 +239,7 @@ proc existsDir*(dir: string): bool =
var res: TStat
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc getLastModificationTime*(file: string): TTime =
proc getLastModificationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## Returns the `file`'s last modification time.
when defined(posix):
var res: TStat
@@ -261,7 +252,7 @@ proc getLastModificationTime*(file: string): TTime =
result = winTimeToUnixTime(rdFileTime(f.ftLastWriteTime))
findclose(h)
proc getLastAccessTime*(file: string): TTime =
proc getLastAccessTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## Returns the `file`'s last read or write access time.
when defined(posix):
var res: TStat
@@ -274,7 +265,7 @@ proc getLastAccessTime*(file: string): TTime =
result = winTimeToUnixTime(rdFileTime(f.ftLastAccessTime))
findclose(h)
proc getCreationTime*(file: string): TTime =
proc getCreationTime*(file: string): TTime {.rtl, extern: "nos$1".} =
## Returns the `file`'s creation time.
when defined(posix):
var res: TStat
@@ -287,12 +278,12 @@ proc getCreationTime*(file: string): TTime =
result = winTimeToUnixTime(rdFileTime(f.ftCreationTime))
findclose(h)
proc fileNewer*(a, b: string): bool =
proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
## modification time is later than `b`'s.
result = getLastModificationTime(a) - getLastModificationTime(b) > 0
proc getCurrentDir*(): string =
proc getCurrentDir*(): string {.rtl, extern: "nos$1".} =
## Returns the current working directory.
const bufsize = 512 # should be enough
result = newString(bufsize)
@@ -314,7 +305,8 @@ proc setCurrentDir*(newDir: string) {.inline.} =
else:
if chdir(newDir) != 0'i32: OSError()
proc JoinPath*(head, tail: string): string {.noSideEffect.} =
proc JoinPath*(head, tail: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Joins two directory names to one.
##
## For example on Unix:
@@ -342,7 +334,8 @@ proc JoinPath*(head, tail: string): string {.noSideEffect.} =
else:
result = head & DirSep & tail
proc JoinPath*(parts: openarray[string]): string {.noSideEffect.} =
proc JoinPath*(parts: openarray[string]): string {.noSideEffect,
rtl, extern: "nos$1OpenArray".} =
## The same as `JoinPath(head, tail)`, but works with any number
## of directory parts.
result = parts[0]
@@ -370,7 +363,8 @@ proc SplitPath*(path: string, head, tail: var string) {.noSideEffect,
head = ""
tail = path # make a string copy here
proc SplitPath*(path: string): tuple[head, tail: string] {.noSideEffect.} =
proc SplitPath*(path: string): tuple[head, tail: string] {.
noSideEffect, rtl, extern: "nos$1".} =
## Splits a directory into (head, tail), so that
## ``JoinPath(head, tail) == path``.
##
@@ -395,7 +389,8 @@ proc SplitPath*(path: string): tuple[head, tail: string] {.noSideEffect.} =
result.head = ""
result.tail = path
proc parentDir*(path: string): string {.noSideEffect.} =
proc parentDir*(path: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Returns the parent directory of `path`.
##
## This is often the same as the ``head`` result of ``splitPath``.
@@ -434,7 +429,8 @@ proc searchExtPos(s: string): int =
elif s[i] in {dirsep, altsep}:
break # do not skip over path
proc splitFile*(path: string): tuple[dir, name, ext: string] {.noSideEffect.} =
proc splitFile*(path: string): tuple[dir, name, ext: string] {.
noSideEffect, rtl, extern: "nos$1".} =
## Splits a filename into (dir, filename, extension).
## `dir` does not end in `DirSep`.
## `extension` includes the leading dot.
@@ -472,7 +468,8 @@ proc extractDir*(path: string): string {.noSideEffect, deprecated.} =
## **Deprecated since version 0.8.2**: Use ``splitFile(path).dir`` instead.
result = splitFile(path).dir
proc extractFilename*(path: string): string {.noSideEffect.} =
proc extractFilename*(path: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Extracts the filename of a given `path`. This is the same as
## ``name & ext`` from ``splitFile(path)``.
if path.len == 0 or path[path.len-1] in {dirSep, altSep}:
@@ -480,7 +477,7 @@ proc extractFilename*(path: string): string {.noSideEffect.} =
else:
result = splitPath(path).tail
proc expandFilename*(filename: string): string =
proc expandFilename*(filename: string): string {.rtl, extern: "nos$1".} =
## Returns the full path of `filename`, raises EOS in case of an error.
when defined(windows):
var unused: cstring
@@ -524,7 +521,8 @@ proc extractFileTrunk*(filename: string): string {.noSideEffect, deprecated.} =
## **Deprecated since version 0.8.2**: Use ``splitFile(path).name`` instead.
result = splitFile(filename).name
proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
proc ChangeFileExt*(filename, ext: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Changes the file extension to `ext`.
##
## If the `filename` has no extension, `ext` will be added.
@@ -536,7 +534,8 @@ proc ChangeFileExt*(filename, ext: string): string {.noSideEffect.} =
if extPos < 0: result = filename & normExt(ext)
else: result = copy(filename, 0, extPos-1) & normExt(ext)
proc addFileExt*(filename, ext: string): string {.noSideEffect.} =
proc addFileExt*(filename, ext: string): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Adds the file extension `ext` to `filename`, unless
## `filename` already has an extension.
##
@@ -552,7 +551,8 @@ proc AppendFileExt*(filename, ext: string): string {.
## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
result = addFileExt(filename, ext)
proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} =
proc cmpPaths*(pathA, pathB: string): int {.
noSideEffect, rtl, extern: "nos$1".} =
## Compares two paths.
##
## On a case-sensitive filesystem this is done
@@ -566,7 +566,7 @@ proc cmpPaths*(pathA, pathB: string): int {.noSideEffect.} =
else:
result = cmpIgnoreCase(pathA, pathB)
proc sameFile*(path1, path2: string): bool =
proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
## Returns True if both pathname arguments refer to the same file or
## directory (as indicated by device number and i-node number).
## Raises an exception if an stat() call on either pathname fails.
@@ -590,7 +590,7 @@ proc sameFile*(path1, path2: string): bool =
else:
result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
proc sameFileContent*(path1, path2: string): bool =
proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1".} =
## Returns True if both pathname arguments refer to files with identical
## binary content.
const
@@ -620,7 +620,7 @@ proc sameFileContent*(path1, path2: string): bool =
close(a)
close(b)
proc copyFile*(dest, source: string) {.deprecated.} =
proc copyFile*(dest, source: string) {.deprecated, rtl, extern: "nos$1".} =
## Copies a file from `source` to `dest`. If this fails,
## `EOS` is raised.
## **Deprecated since version 0.8.8**: Use this proc with named arguments
@@ -650,13 +650,13 @@ proc copyFile*(dest, source: string) {.deprecated.} =
close(s)
close(d)
proc moveFile*(dest, source: string) {.deprecated.} =
proc moveFile*(dest, source: string) {.deprecated, rtl, extern: "nos$1".} =
## Moves a file from `source` to `dest`. If this fails, `EOS` is raised.
## **Deprecated since version 0.8.8**: Use this proc with named arguments
## only, because the order will change!
if crename(source, dest) != 0'i32: OSError()
proc removeFile*(file: string) =
proc removeFile*(file: string) {.rtl, extern: "nos$1".} =
## Removes the `file`. If this fails, `EOS` is raised.
if cremove(file) != 0'i32: OSError()
@@ -664,7 +664,7 @@ proc executeShellCommand*(command: string): int {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `execShellCmd` instead.
result = csystem(command)
proc execShellCmd*(command: string): int =
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} =
## Executes a shell command.
##
## Command has the form 'program args' where args are the command
@@ -675,6 +675,9 @@ proc execShellCmd*(command: string): int =
## module.
result = csystem(command)
# Environment handling cannot be put into RTL, because the ``envPairs``
# iterator depends on ``environment``.
var
envComputed: bool = false
environment: seq[string] = @[]
@@ -700,8 +703,8 @@ when defined(windows):
discard FreeEnvironmentStringsA(env)
else:
var
gEnv {.importc: "gEnv".}: ptr array [0..10_000, CString]
var gEnv {.importc: "environ".}: cstringArray
# var gEnv {.importc: "gEnv".}: cstringArray
proc getEnvVarsC() =
# retrieves the variables of char** env of C's main proc
@@ -897,7 +900,7 @@ proc rawRemoveDir(dir: string) =
else:
if rmdir(dir) != 0'i32: OSError()
proc removeDir*(dir: string) =
proc removeDir*(dir: string) {.rtl, extern: "nos$1".} =
## Removes the directory `dir` including all subdirectories and files
## in `dir` (recursively). If this fails, `EOS` is raised.
for kind, path in walkDir(dir):
@@ -914,7 +917,7 @@ proc rawCreateDir(dir: string) =
if CreateDirectoryA(dir, nil) == 0'i32 and GetLastError() != 183'i32:
OSError()
proc createDir*(dir: string) =
proc createDir*(dir: string) {.rtl, extern: "nos$1".} =
## Creates the directory `dir`.
##
## The directory may contain several subdirectories that do not exist yet.
@@ -925,7 +928,8 @@ proc createDir*(dir: string) =
if dir[i] in {dirsep, altsep}: rawCreateDir(copy(dir, 0, i-1))
rawCreateDir(dir)
proc parseCmdLine*(c: string): seq[string] =
proc parseCmdLine*(c: string): seq[string] {.
noSideEffect, rtl, extern: "nos$1".} =
## Splits a command line into several components;
## This proc is only occassionally useful, better use the `parseopt` module.
##
@@ -1025,7 +1029,8 @@ type
fpOthersWrite, ## write access for others
fpOthersRead ## read access for others
proc getFilePermissions*(filename: string): set[TFilePermission] =
proc getFilePermissions*(filename: string): set[TFilePermission] {.
rtl, extern: "nos$1".} =
## retrieves file permissions for `filename`. `OSError` is raised in case of
## an error. On Windows, only the ``readonly`` flag is checked, every other
## permission is available in any case.
@@ -1053,7 +1058,8 @@ proc getFilePermissions*(filename: string): set[TFilePermission] =
else:
result = {fpUserExec..fpOthersRead}
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) =
proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) {.
rtl, extern: "nos$1".} =
## sets the file permissions for `filename`. `OSError` is raised in case of
## an error. On Windows, only the ``readonly`` flag is changed, depending on
## ``fpUserWrite``.
@@ -1083,7 +1089,8 @@ proc setFilePermissions*(filename: string, permissions: set[TFilePermission]) =
OSError()
proc inclFilePermissions*(filename: string,
permissions: set[TFilePermission]) =
permissions: set[TFilePermission]) {.
rtl, extern: "nos$1".} =
## a convenience procedure for:
##
## .. code-block:: nimrod
@@ -1091,19 +1098,20 @@ proc inclFilePermissions*(filename: string,
setFilePermissions(filename, getFilePermissions(filename)+permissions)
proc exclFilePermissions*(filename: string,
permissions: set[TFilePermission]) =
permissions: set[TFilePermission]) {.
rtl, extern: "nos$1".} =
## a convenience procedure for:
##
## .. code-block:: nimrod
## setFilePermissions(filename, getFilePermissions(filename)-permissions)
setFilePermissions(filename, getFilePermissions(filename)-permissions)
proc getHomeDir*(): string =
proc getHomeDir*(): string {.rtl, extern: "nos$1".} =
## Returns the home directory of the current user.
when defined(windows): return getEnv("USERPROFILE") & "\\"
else: return getEnv("HOME") & "/"
proc getConfigDir*(): string =
proc getConfigDir*(): string {.rtl, extern: "nos$1".} =
## Returns the config directory of the current user for applications.
when defined(windows): return getEnv("APPDATA") & "\\"
else: return getEnv("HOME") & "/.config/"
@@ -1117,24 +1125,32 @@ when defined(windows):
var
ownArgv: seq[string]
proc paramStr(i: int): string =
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
return ownArgv[i]
proc paramCount(): int =
proc paramCount*(): int {.rtl, extern: "nos$1".} =
## Returns the number of command line arguments given to the
## application.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
result = ownArgv.len-1
else:
proc paramStr*(i: int): string {.rtl, extern: "nos$1".} =
## Returns the `i`-th command line argument given to the
## application.
##
## `i` should be in the range `1..paramCount()`, else
## the `EOutOfIndex` exception is raised.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLineA())
return ownArgv[i]
elif not defined(createNimRtl):
# On Posix, there is no portable way to get the command line from a DLL.
var
cmdCount {.importc: "cmdCount".}: cint
cmdLine {.importc: "cmdLine".}: cstringArray
proc paramStr(i: int): string =
proc paramStr*(i: int): string =
if i < cmdCount and i >= 0: return $cmdLine[i]
raise newException(EInvalidIndex, "invalid index")
proc paramCount(): int = return cmdCount-1
proc paramCount*(): int = return cmdCount-1
when defined(linux) or defined(solaris) or defined(bsd) or defined(aix):
proc getApplAux(procPath: string): string =
@@ -1153,7 +1169,7 @@ when defined(macosx):
proc getExecPath2(c: cstring, size: var int32): bool {.
importc: "_NSGetExecutablePath", header: "<mach-o/dyld.h>".}
proc getApplicationFilename*(): string =
proc getApplicationFilename*(): string {.rtl, extern: "nos$1".} =
## Returns the filename of the application's executable.
# Linux: /proc/<pid>/exe
@@ -1190,11 +1206,11 @@ proc getApplicationFilename*(): string =
var x = joinPath(p, result)
if ExistsFile(x): return x
proc getApplicationDir*(): string =
proc getApplicationDir*(): string {.rtl, extern: "nos$1".} =
## Returns the directory of the application's executable.
result = splitFile(getApplicationFilename()).dir
proc sleep*(milsecs: int) =
proc sleep*(milsecs: int) {.rtl, extern: "nos$1".} =
## sleeps `milsecs` milliseconds.
when defined(windows):
winlean.sleep(int32(milsecs))
@@ -1204,7 +1220,7 @@ proc sleep*(milsecs: int) =
a.tv_nsec = (milsecs mod 1000) * 1000
discard posix.nanosleep(a, b)
proc getFileSize*(file: string): biggestInt =
proc getFileSize*(file: string): biggestInt {.rtl, extern: "nos$1".} =
## returns the file size of `file`. Can raise ``EOS``.
when defined(windows):
var a: TWin32FindData

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -531,48 +531,49 @@ proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
# ---------------------- interface to programs -------------------------------
proc alloc(size: int): pointer =
result = rawAlloc(allocator, size+sizeof(TFreeCell))
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
assert(not isAllocatedPtr(allocator, result))
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
when not defined(useNimRtl):
proc alloc(size: int): pointer =
result = rawAlloc(allocator, size+sizeof(TFreeCell))
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used
assert(not isAllocatedPtr(allocator, result))
result = cast[pointer](cast[TAddress](result) +% sizeof(TFreeCell))
proc alloc0(size: int): pointer =
result = alloc(size)
zeroMem(result, size)
proc alloc0(size: int): pointer =
result = alloc(size)
zeroMem(result, size)
proc dealloc(p: pointer) =
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
assert(cast[ptr TFreeCell](x).zeroField == 1)
rawDealloc(allocator, x)
assert(not isAllocatedPtr(allocator, x))
proc dealloc(p: pointer) =
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
assert(cast[ptr TFreeCell](x).zeroField == 1)
rawDealloc(allocator, x)
assert(not isAllocatedPtr(allocator, x))
proc ptrSize(p: pointer): int =
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
result = pageAddr(x).size - sizeof(TFreeCell)
proc ptrSize(p: pointer): int =
var x = cast[pointer](cast[TAddress](p) -% sizeof(TFreeCell))
result = pageAddr(x).size - sizeof(TFreeCell)
proc realloc(p: pointer, newsize: int): pointer =
if newsize > 0:
result = alloc(newsize)
if p != nil:
copyMem(result, p, ptrSize(p))
proc realloc(p: pointer, newsize: int): pointer =
if newsize > 0:
result = alloc(newsize)
if p != nil:
copyMem(result, p, ptrSize(p))
dealloc(p)
elif p != nil:
dealloc(p)
elif p != nil:
dealloc(p)
proc countFreeMem(): int =
# only used for assertions
var it = allocator.freeChunksList
while it != nil:
inc(result, it.size)
it = it.next
proc countFreeMem(): int =
# only used for assertions
var it = allocator.freeChunksList
while it != nil:
inc(result, it.size)
it = it.next
proc getFreeMem(): int =
result = allocator.freeMem
#assert(result == countFreeMem())
proc getFreeMem(): int =
result = allocator.freeMem
#assert(result == countFreeMem())
proc getTotalMem(): int = return allocator.currMem
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
proc getTotalMem(): int = return allocator.currMem
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem()
when isMainModule:
const iterations = 4000_000

View File

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

View File

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

View File

@@ -74,14 +74,6 @@ var
# This is wasteful but safe. This is a lock against recursive garbage
# collection, not a lock for threads!
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerRtl.}
# unsureAsgnRef updates the reference counters only if dest is not on the
# stack. It is used by the code generator if it cannot decide wether a
# reference is in the stack or not (this can happen for var parameters).
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
if (c.refcount and rcZct) == 0:
c.refcount = c.refcount and not colorMask or rcZct
@@ -105,24 +97,6 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
result = int(usrToCell(p).refcount) shr rcShift
proc GC_disable() = inc(recGcLock)
proc GC_enable() =
if recGcLock > 0: dec(recGcLock)
proc GC_setStrategy(strategy: TGC_Strategy) =
case strategy
of gcThroughput: nil
of gcResponsiveness: nil
of gcOptimizeSpace: nil
of gcOptimizeTime: nil
proc GC_enableMarkAndSweep() =
cycleThreshold = InitialCycleThreshold
proc GC_disableMarkAndSweep() =
cycleThreshold = high(cycleThreshold)-1
# set to the max value to suppress the cycle detector
# this that has to equals zero, otherwise we have to round up UnitsPerPage:
when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
@@ -214,8 +188,13 @@ proc prepareDealloc(cell: PCell) =
(cast[TFinalizer](cell.typ.finalizer))(cellToUsr(cell))
dec(recGcLock)
proc PossibleRoot(gch: var TGcHeap, c: PCell) {.inline.} =
if canbeCycleRoot(c): incl(gch.cycleRoots, c)
proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
# we MUST access gch as a global here, because this crosses DLL boundaries!
incl(gch.cycleRoots, c)
proc rtlAddZCT(c: PCell) {.rtl, inl.} =
# we MUST access gch as a global here, because this crosses DLL boundaries!
addZCT(gch.zct, c)
proc decRef(c: PCell) {.inline.} =
when stressGC:
@@ -224,19 +203,19 @@ proc decRef(c: PCell) {.inline.} =
assert(c.refcount >=% rcIncrement)
c.refcount = c.refcount -% rcIncrement
if c.refcount <% rcIncrement:
addZCT(gch.zct, c)
rtlAddZCT(c)
elif canBeCycleRoot(c):
incl(gch.cycleRoots, c)
rtlAddCycleRoot(c)
proc incRef(c: PCell) {.inline.} =
c.refcount = c.refcount +% rcIncrement
if canBeCycleRoot(c):
incl(gch.cycleRoots, c)
rtlAddCycleRoot(c)
proc nimGCref(p: pointer) {.compilerRtl, inl.} = incRef(usrToCell(p))
proc nimGCunref(p: pointer) {.compilerRtl, inl.} = decRef(usrToCell(p))
proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(usrToCell(p))
proc nimGCunref(p: pointer) {.compilerProc, inline.} = decRef(usrToCell(p))
proc asgnRef(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
proc asgnRef(dest: ppointer, src: pointer) {.compilerProc, inline.} =
# the code generator calls this proc!
assert(not isOnStack(dest))
# BUGFIX: first incRef then decRef!
@@ -244,7 +223,7 @@ proc asgnRef(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
if dest^ != nil: decRef(usrToCell(dest^))
dest^ = src
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerProc, inline.} =
# the code generator calls this proc if it is known at compile time that no
# cycle is possible.
if src != nil:
@@ -254,30 +233,34 @@ proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerRtl, inl.} =
var c = usrToCell(dest^)
c.refcount = c.refcount -% rcIncrement
if c.refcount <% rcIncrement:
addZCT(gch.zct, c)
rtlAddZCT(c)
dest^ = src
proc unsureAsgnRef(dest: ppointer, src: pointer) =
proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} =
# unsureAsgnRef updates the reference counters only if dest is not on the
# stack. It is used by the code generator if it cannot decide wether a
# reference is in the stack or not (this can happen for var parameters).
if not IsOnStack(dest):
if src != nil: incRef(usrToCell(src))
if dest^ != nil: decRef(usrToCell(dest^))
dest^ = src
proc initGC() =
when traceGC:
for i in low(TCellState)..high(TCellState): Init(states[i])
gch.stat.stackScans = 0
gch.stat.cycleCollections = 0
gch.stat.maxThreshold = 0
gch.stat.maxStackSize = 0
gch.stat.maxStackCells = 0
gch.stat.cycleTableSize = 0
# init the rt
init(gch.zct)
init(gch.tempStack)
Init(gch.cycleRoots)
Init(gch.decStack)
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
when not defined(useNimRtl):
when traceGC:
for i in low(TCellState)..high(TCellState): Init(states[i])
gch.stat.stackScans = 0
gch.stat.cycleCollections = 0
gch.stat.maxThreshold = 0
gch.stat.maxStackSize = 0
gch.stat.maxStackCells = 0
gch.stat.cycleTableSize = 0
# init the rt
init(gch.zct)
init(gch.tempStack)
Init(gch.cycleRoots)
Init(gch.decStack)
new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
var d = cast[TAddress](dest)
@@ -325,7 +308,7 @@ proc checkCollection {.inline.} =
if recGcLock == 0:
collectCT(gch)
proc newObj(typ: PNimType, size: int): pointer =
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
# generates a new object and sets its reference counter to 0
assert(typ.kind in {tyRef, tyString, tySequence})
checkCollection()
@@ -357,7 +340,7 @@ proc newObj(typ: PNimType, size: int): pointer =
gcTrace(res, csAllocated)
result = cellToUsr(res)
proc newSeq(typ: PNimType, len: int): pointer =
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).space = len
@@ -490,17 +473,18 @@ elif defined(hppa) or defined(hp9000) or defined(hp9000s300) or
else:
const stackIncreases = false
proc setStackBottom(theStackBottom: pointer) =
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
# the first init must be the one that defines the stack bottom:
if stackBottom == nil: stackBottom = theStackBottom
else:
var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2
var b = cast[TAddress](stackBottom)
when stackIncreases:
stackBottom = cast[pointer](min(a, b))
when not defined(useNimRtl):
proc setStackBottom(theStackBottom: pointer) =
#c_fprintf(c_stdout, "stack bottom: %p;\n", theStackBottom)
# the first init must be the one that defines the stack bottom:
if stackBottom == nil: stackBottom = theStackBottom
else:
stackBottom = cast[pointer](max(a, b))
var a = cast[TAddress](theStackBottom) # and not PageMask - PageSize*2
var b = cast[TAddress](stackBottom)
when stackIncreases:
stackBottom = cast[pointer](min(a, b))
else:
stackBottom = cast[pointer](max(a, b))
proc stackSize(): int {.noinline.} =
var stackTop: array[0..1, pointer]
@@ -647,22 +631,41 @@ proc collectCT(gch: var TGcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, cycleThreshold)
unmarkStackAndRegisters(gch)
proc GC_fullCollect() =
var oldThreshold = cycleThreshold
cycleThreshold = 0 # forces cycle collection
collectCT(gch)
cycleThreshold = oldThreshold
when not defined(useNimRtl):
proc GC_disable() = inc(recGcLock)
proc GC_enable() =
if recGcLock > 0: dec(recGcLock)
proc GC_getStatistics(): string =
GC_disable()
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
"[GC] max stack size: " & $gch.stat.maxStackSize
when traceGC: writeLeakage()
GC_enable()
proc GC_setStrategy(strategy: TGC_Strategy) =
case strategy
of gcThroughput: nil
of gcResponsiveness: nil
of gcOptimizeSpace: nil
of gcOptimizeTime: nil
proc GC_enableMarkAndSweep() =
cycleThreshold = InitialCycleThreshold
proc GC_disableMarkAndSweep() =
cycleThreshold = high(cycleThreshold)-1
# set to the max value to suppress the cycle detector
proc GC_fullCollect() =
var oldThreshold = cycleThreshold
cycleThreshold = 0 # forces cycle collection
collectCT(gch)
cycleThreshold = oldThreshold
proc GC_getStatistics(): string =
GC_disable()
result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
"[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
"[GC] stack scans: " & $gch.stat.stackScans & "\n" &
"[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
"[GC] cycle collections: " & $gch.stat.cycleCollections & "\n" &
"[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
"[GC] zct capacity: " & $gch.zct.cap & "\n" &
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
"[GC] max stack size: " & $gch.stat.maxStackSize
when traceGC: writeLeakage()
GC_enable()

View File

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

View File

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

View File

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

View File

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