inlining of the write barrier for dlls

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

View file

@ -169,16 +169,6 @@ in mind:
without the ``-w`` option helps! without the ``-w`` option helps!
Generation of dynamic link libraries
====================================
Generation of dynamic link libraries or shared libraries is not difficult; the
underlying C compiler already does all the hard work for us. The problem is the
common runtime library, especially the memory manager. Note that Borland's
Delphi had exactly the same problem. The workaround is to not link the GC with
the Dll and provide an extra runtime dll that needs to be initialized.
The Garbage Collector The Garbage Collector
===================== =====================

View file

@ -70,7 +70,7 @@ However, the generated C code is not platform independent. C code generated for
Linux does not compile on Windows, for instance. The comment on top of the Linux does not compile on Windows, for instance. The comment on top of the
C file lists the OS, CPU and CC the file has been compiled for. C file lists the OS, CPU and CC the file has been compiled for.
..
DLL generation DLL generation
============== ==============
@ -81,6 +81,11 @@ C file lists the OS, CPU and CC the file has been compiled for.
nimrod c -d:release lib/nimrtl.nim nimrod c -d:release lib/nimrtl.nim
To link to ``nimrtl.dll`` use the command::
nimrod c -d:useNimRtl myprog.nim
Additional Features Additional Features
=================== ===================

View file

@ -88,6 +88,8 @@ macro meaning
``[^ \9-\13]`` ``[^ \9-\13]``
``\w`` any "word" character: ``[a-zA-Z0-9_]`` ``\w`` any "word" character: ``[a-zA-Z0-9_]``
``\W`` any "non-word" character: ``[^a-zA-Z0-9_]`` ``\W`` any "non-word" character: ``[^a-zA-Z0-9_]``
``\a`` same as ``[a-zA-Z]``
``\A`` same as ``[^a-zA-Z]``
``\n`` any newline combination: ``\10 / \13\10 / \13`` ``\n`` any newline combination: ``\10 / \13\10 / \13``
``\i`` ignore case for matching; use this at the start of the PEG ``\i`` ignore case for matching; use this at the start of the PEG
``\y`` ignore style for matching; use this at the start of the PEG ``\y`` ignore style for matching; use this at the start of the PEG

View file

@ -11,23 +11,18 @@
## The default Nimrtl does not only contain the ``system`` module, but these ## The default Nimrtl does not only contain the ``system`` module, but these
## too: ## too:
## ##
## * strutils
## * parseutils ## * parseutils
## * strutils
## * parseopt ## * parseopt
## * parsecfg ## * parsecfg
## * strtabs ## * strtabs
## * times ## * times
## * os ## * os
## * osproc ## * osproc
## * pegs
## * unicode ## * unicode
## * pegs
## * ropes ## * ropes
## * re
## ##
## So the resulting dynamic library is quite big. However, it is very easy to
## strip modules out. Just modify the ``import`` statement in
## ``lib/nimrtl.nim`` and recompile. Note that simply *adding* a module
## here is not sufficient, though.
when system.appType != "lib": when system.appType != "lib":
{.error: "This file has to be compiled as a library!".} {.error: "This file has to be compiled as a library!".}
@ -35,5 +30,7 @@ when system.appType != "lib":
when not defined(createNimRtl): when not defined(createNimRtl):
{.error: "This file has to be compiled with '-d:createNimRtl'".} {.error: "This file has to be compiled with '-d:createNimRtl'".}
import
parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes,
os, osproc, times

View file

@ -78,7 +78,8 @@ proc FillBuffer(L: var TBaseLexer) =
toCopy = L.BufLen - L.sentinel - 1 toCopy = L.BufLen - L.sentinel - 1
assert(toCopy >= 0) assert(toCopy >= 0)
if toCopy > 0: if toCopy > 0:
MoveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize) # "moveMem" handles overlapping regions MoveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
# "moveMem" handles overlapping regions
charsRead = L.input.readData(L.input, addr(L.buf[toCopy]), charsRead = L.input.readData(L.input, addr(L.buf[toCopy]),
(L.sentinel + 1) * chrSize) div chrSize (L.sentinel + 1) * chrSize) div chrSize
s = toCopy + charsRead s = toCopy + charsRead

View file

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

View file

@ -10,6 +10,8 @@
## This module implements an advanced facility for executing OS processes ## This module implements an advanced facility for executing OS processes
## and process communication. ## and process communication.
include "system/inclrtl"
import import
strutils, os, strtabs, streams strutils, os, strtabs, streams
@ -39,7 +41,8 @@ type
proc execProcess*(command: string, proc execProcess*(command: string,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string poUseShell}): string {.
rtl, extern: "nosp$1".}
## A convience procedure that executes ``command`` with ``startProcess`` ## A convience procedure that executes ``command`` with ``startProcess``
## and returns its output as a string. ## and returns its output as a string.
@ -50,7 +53,7 @@ proc executeProcess*(command: string,
## **Deprecated since version 0.8.2**: Use `execProcess` instead. ## **Deprecated since version 0.8.2**: Use `execProcess` instead.
result = execProcess(command, options) result = execProcess(command, options)
proc execCmd*(command: string): int proc execCmd*(command: string): int {.rtl, extern: "nosp$1".}
## Executes ``command`` and returns its error code. Standard input, output, ## Executes ``command`` and returns its error code. Standard input, output,
## error streams are inherited from the calling process. ## error streams are inherited from the calling process.
@ -63,7 +66,8 @@ proc startProcess*(command: string,
workingDir: string = "", workingDir: string = "",
args: openarray[string] = [], args: openarray[string] = [],
env: PStringTable = nil, env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}): PProcess options: set[TProcessOption] = {poStdErrToStdOut}):
PProcess {.rtl, extern: "nosp$1".}
## Starts a process. `Command` is the executable file, `workingDir` is the ## Starts a process. `Command` is the executable file, `workingDir` is the
## process's working directory. If ``workingDir == ""`` the current directory ## process's working directory. If ``workingDir == ""`` the current directory
## is used. `args` are the command line arguments that are passed to the ## is used. `args` are the command line arguments that are passed to the
@ -78,32 +82,32 @@ proc startProcess*(command: string,
## Return value: The newly created process object. Nil is never returned, ## Return value: The newly created process object. Nil is never returned,
## but ``EOS`` is raised in case of an error. ## but ``EOS`` is raised in case of an error.
proc suspend*(p: PProcess) proc suspend*(p: PProcess) {.rtl, extern: "nosp$1".}
## Suspends the process `p`. ## Suspends the process `p`.
proc resume*(p: PProcess) proc resume*(p: PProcess) {.rtl, extern: "nosp$1".}
## Resumes the process `p`. ## Resumes the process `p`.
proc terminate*(p: PProcess) proc terminate*(p: PProcess) {.rtl, extern: "nosp$1".}
## Terminates the process `p`. ## Terminates the process `p`.
proc running*(p: PProcess): bool proc running*(p: PProcess): bool {.rtl, extern: "nosp$1".}
## Returns true iff the process `p` is still running. Returns immediately. ## Returns true iff the process `p` is still running. Returns immediately.
proc processID*(p: PProcess): int = proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
## returns `p`'s process ID. ## returns `p`'s process ID.
return p.id return p.id
proc waitForExit*(p: PProcess): int proc waitForExit*(p: PProcess): int {.rtl, extern: "nosp$1".}
## waits for the process to finish and returns `p`'s error code. ## waits for the process to finish and returns `p`'s error code.
proc inputStream*(p: PProcess): PStream proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
## returns ``p``'s input stream for writing to ## returns ``p``'s input stream for writing to
proc outputStream*(p: PProcess): PStream proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
## returns ``p``'s output stream for reading from ## returns ``p``'s output stream for reading from
proc errorStream*(p: PProcess): PStream proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1".}
## returns ``p``'s output stream for reading from ## returns ``p``'s output stream for reading from
when defined(macosx) or defined(bsd): when defined(macosx) or defined(bsd):
@ -115,7 +119,7 @@ when defined(macosx) or defined(bsd):
a: var int, b: pointer, c: int): cint {. a: var int, b: pointer, c: int): cint {.
importc: "sysctl", header: "<sys/sysctl.h>".} importc: "sysctl", header: "<sys/sysctl.h>".}
proc countProcessors*(): int = proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
## returns the numer of the processors/cores the machine has. ## returns the numer of the processors/cores the machine has.
## Returns 0 if it cannot be detected. ## Returns 0 if it cannot be detected.
when defined(windows): when defined(windows):
@ -150,7 +154,7 @@ proc startProcessAux(cmd: string, options: set[TProcessOption]): PProcess =
proc execProcesses*(cmds: openArray[string], proc execProcesses*(cmds: openArray[string],
options = {poStdErrToStdOut, poParentStreams}, options = {poStdErrToStdOut, poParentStreams},
n = countProcessors()): int = n = countProcessors()): int {.rtl, extern: "nosp$1".} =
## executes the commands `cmds` in parallel. Creates `n` processes ## executes the commands `cmds` in parallel. Creates `n` processes
## that execute in parallel. The highest return value of all processes ## that execute in parallel. The highest return value of all processes
## is returned. ## is returned.
@ -192,18 +196,7 @@ proc execProcesses*(cmds: openArray[string],
var p = startProcessAux(cmds[i], options=options) var p = startProcessAux(cmds[i], options=options)
result = max(waitForExit(p), result) result = max(waitForExit(p), result)
when true: when not defined(useNimRtl):
nil
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, proc execProcess(command: string,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string = poUseShell}): string =
@ -222,7 +215,7 @@ when false:
inc(i) inc(i)
dealloc(a) dealloc(a)
when defined(Windows): when defined(Windows) and not defined(useNimRtl):
# We need to implement a handle stream for Windows: # We need to implement a handle stream for Windows:
type type
PFileHandleStream = ref TFileHandleStream PFileHandleStream = ref TFileHandleStream
@ -405,7 +398,7 @@ when defined(Windows):
result = -1 result = -1
discard CloseHandle(Process) discard CloseHandle(Process)
else: elif not defined(useNimRtl):
const const
readIdx = 0 readIdx = 0
writeIdx = 1 writeIdx = 1

View file

@ -27,6 +27,8 @@
import import
hashes, strutils, lexbase, streams hashes, strutils, lexbase, streams
include "system/inclrtl"
type type
TCfgEventKind* = enum ## enumation of all events that may occur when parsing TCfgEventKind* = enum ## enumation of all events that may occur when parsing
cfgEof, ## end of file reached cfgEof, ## end of file reached
@ -65,37 +67,17 @@ type
state: TParserState state: TParserState
filename: string filename: string
proc open*(c: var TCfgParser, input: PStream, filename: string)
## initializes the parser with an input stream. `Filename` is only used
## for nice error messages.
proc close*(c: var TCfgParser)
## closes the parser `c` and its associated input stream.
proc next*(c: var TCfgParser): TCfgEvent
## retrieves the first/next event. This controls the parser.
proc getColumn*(c: TCfgParser): int
## get the current column the parser has arrived at.
proc getLine*(c: TCfgParser): int
## get the current line the parser has arrived at.
proc getFilename*(c: TCfgParser): string
## get the filename of the file that the parser processes.
proc errorStr*(c: TCfgParser, msg: string): string
## returns a properly formated error message containing current line and
## column information.
# implementation # implementation
const const
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'} SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
proc rawGetTok(c: var TCfgParser, tok: var TToken) proc rawGetTok(c: var TCfgParser, tok: var TToken)
proc open(c: var TCfgParser, input: PStream, filename: string) =
proc open*(c: var TCfgParser, input: PStream, filename: string) {.
rtl, extern: "npc$1".} =
## initializes the parser with an input stream. `Filename` is only used
## for nice error messages.
lexbase.open(c, input) lexbase.open(c, input)
c.filename = filename c.filename = filename
c.state = startState c.state = startState
@ -103,16 +85,20 @@ proc open(c: var TCfgParser, input: PStream, filename: string) =
c.tok.literal = "" c.tok.literal = ""
rawGetTok(c, c.tok) rawGetTok(c, c.tok)
proc close(c: var TCfgParser) = proc close*(c: var TCfgParser) {.rtl, extern: "npc$1".} =
## closes the parser `c` and its associated input stream.
lexbase.close(c) lexbase.close(c)
proc getColumn(c: TCfgParser): int = proc getColumn*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
## get the current column the parser has arrived at.
result = getColNumber(c, c.bufPos) result = getColNumber(c, c.bufPos)
proc getLine(c: TCfgParser): int = proc getLine*(c: TCfgParser): int {.rtl, extern: "npc$1".} =
## get the current line the parser has arrived at.
result = c.linenumber result = c.linenumber
proc getFilename(c: TCfgParser): string = proc getFilename*(c: TCfgParser): string {.rtl, extern: "npc$1".} =
## get the filename of the file that the parser processes.
result = c.filename result = c.filename
proc handleHexChar(c: var TCfgParser, xi: var int) = proc handleHexChar(c: var TCfgParser, xi: var int) =
@ -300,7 +286,9 @@ proc rawGetTok(c: var TCfgParser, tok: var TToken) =
tok.literal = "[EOF]" tok.literal = "[EOF]"
else: getSymbol(c, tok) else: getSymbol(c, tok)
proc errorStr(c: TCfgParser, msg: string): string = proc errorStr*(c: TCfgParser, msg: string): string {.rtl, extern: "npc$1".} =
## returns a properly formated error message containing current line and
## column information.
result = `%`("$1($2, $3) Error: $4", result = `%`("$1($2, $3) Error: $4",
[c.filename, $getLine(c), $getColumn(c), msg]) [c.filename, $getLine(c), $getColumn(c), msg])
@ -323,7 +311,8 @@ proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent =
result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal)
rawGetTok(c, c.tok) rawGetTok(c, c.tok)
proc next(c: var TCfgParser): TCfgEvent = proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} =
## retrieves the first/next event. This controls the parser.
case c.tok.kind case c.tok.kind
of tkEof: of tkEof:
result.kind = cfgEof result.kind = cfgEof

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -13,6 +13,8 @@
{.push debugger: off.} {.push debugger: off.}
include "system/inclrtl"
import import
os, strutils os, strutils
@ -32,6 +34,10 @@ type
## or the argument, ``value`` is not "" if ## or the argument, ``value`` is not "" if
## the option was given a value ## the option was given a value
when defined(os.ParamCount):
# we cannot provide this for NimRtl creation on Posix, because we can't
# access the command line arguments then!
proc initOptParser*(cmdline = ""): TOptParser = proc initOptParser*(cmdline = ""): TOptParser =
## inits the option parser. If ``cmdline == ""``, the real command line ## inits the option parser. If ``cmdline == ""``, the real command line
## (as provided by the ``OS`` module) is taken. ## (as provided by the ``OS`` module) is taken.
@ -82,7 +88,8 @@ proc handleShortOption(p: var TOptParser) =
if p.cmd[i] == '\0': p.inShortState = false if p.cmd[i] == '\0': p.inShortState = false
p.pos = i p.pos = i
proc next*(p: var TOptParser) = proc next*(p: var TOptParser) {.
rtl, extern: "npo$1".} =
## parses the first or next option; ``p.kind`` describes what token has been ## parses the first or next option; ``p.kind`` describes what token has been
## parsed. ``p.key`` and ``p.val`` are set accordingly. ## parsed. ``p.key`` and ``p.val`` are set accordingly.
var i = p.pos var i = p.pos
@ -116,7 +123,8 @@ proc next*(p: var TOptParser) =
p.kind = cmdArgument p.kind = cmdArgument
p.pos = parseWord(p.cmd, i, p.key) p.pos = parseWord(p.cmd, i, p.key)
proc cmdLineRest*(p: TOptParser): string = proc cmdLineRest*(p: TOptParser): string {.
rtl, extern: "npo$1".} =
## retrieves the rest of the command line that has not been parsed yet. ## retrieves the rest of the command line that has not been parsed yet.
result = strip(copy(p.cmd, p.pos, len(p.cmd) - 1)) result = strip(copy(p.cmd, p.pos, len(p.cmd) - 1))
@ -124,6 +132,8 @@ proc getRestOfCommandLine*(p: TOptParser): string {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `cmdLineRest` instead. ## **Deprecated since version 0.8.2**: Use `cmdLineRest` instead.
result = cmdLineRest(p) result = cmdLineRest(p)
when defined(initOptParser):
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] = iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =
## This is an convenience iterator for iterating over the command line. ## This is an convenience iterator for iterating over the command line.
## This uses the TOptParser object. Example: ## This uses the TOptParser object. Example:

View file

@ -14,6 +14,8 @@
{.push debugger:off .} # the user does not want to trace a part {.push debugger:off .} # the user does not want to trace a part
# of the standard library! # of the standard library!
include "system/inclrtl"
const const
Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'} Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
@ -23,7 +25,8 @@ const
proc toLower(c: char): char {.inline.} = proc toLower(c: char): char {.inline.} =
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
proc parseHex*(s: string, number: var int, start = 0): int = proc parseHex*(s: string, number: var int, start = 0): int {.
rtl, extern: "npuParseHex", noSideEffect.} =
## parses a hexadecimal number and stores its value in ``number``. Returns ## parses a hexadecimal number and stores its value in ``number``. Returns
## the number of the parsed characters or 0 in case of an error. ## the number of the parsed characters or 0 in case of an error.
var i = start var i = start
@ -46,7 +49,8 @@ proc parseHex*(s: string, number: var int, start = 0): int =
inc(i) inc(i)
if foundDigit: result = i-start if foundDigit: result = i-start
proc parseOct*(s: string, number: var int, start = 0): int = proc parseOct*(s: string, number: var int, start = 0): int {.
rtl, extern: "npuParseOct", noSideEffect.} =
## parses an octal number and stores its value in ``number``. Returns ## parses an octal number and stores its value in ``number``. Returns
## the number of the parsed characters or 0 in case of an error. ## the number of the parsed characters or 0 in case of an error.
var i = start var i = start
@ -116,13 +120,15 @@ proc rawParseInt(s: string, b: var biggestInt, start = 0): int =
result = i - start result = i - start
{.pop.} # overflowChecks {.pop.} # overflowChecks
proc parseBiggestInt*(s: string, number: var biggestInt, start = 0): int = proc parseBiggestInt*(s: string, number: var biggestInt, start = 0): int {.
rtl, extern: "npuParseBiggestInt", noSideEffect.} =
## parses an integer starting at `start` and stores the value into `number`. ## parses an integer starting at `start` and stores the value into `number`.
## Result is the number of processed chars or 0 if there is no integer. ## Result is the number of processed chars or 0 if there is no integer.
## `EOverflow` is raised if an overflow occurs. ## `EOverflow` is raised if an overflow occurs.
result = rawParseInt(s, number, start) result = rawParseInt(s, number, start)
proc parseInt*(s: string, number: var int, start = 0): int = proc parseInt*(s: string, number: var int, start = 0): int {.
rtl, extern: "npuParseInt", noSideEffect.} =
## parses an integer starting at `start` and stores the value into `number`. ## parses an integer starting at `start` and stores the value into `number`.
## Result is the number of processed chars or 0 if there is no integer. ## Result is the number of processed chars or 0 if there is no integer.
## `EOverflow` is raised if an overflow occurs. ## `EOverflow` is raised if an overflow occurs.
@ -134,7 +140,8 @@ proc parseInt*(s: string, number: var int, start = 0): int =
else: else:
number = int(res) number = int(res)
proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int = proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int {.
rtl, extern: "npuParseBiggestFloat", noSideEffect.} =
## parses a float starting at `start` and stores the value into `number`. ## parses a float starting at `start` and stores the value into `number`.
## Result is the number of processed chars or 0 if there occured a parsing ## Result is the number of processed chars or 0 if there occured a parsing
## error. ## error.
@ -206,7 +213,8 @@ proc parseBiggestFloat*(s: string, number: var biggestFloat, start = 0): int =
number = number * sign number = number * sign
result = i - start result = i - start
proc parseFloat*(s: string, number: var float, start = 0): int = proc parseFloat*(s: string, number: var float, start = 0): int {.
rtl, extern: "npuParseFloat", noSideEffect.} =
## parses a float starting at `start` and stores the value into `number`. ## parses a float starting at `start` and stores the value into `number`.
## Result is the number of processed chars or 0 if there occured a parsing ## Result is the number of processed chars or 0 if there occured a parsing
## error. ## error.

View file

@ -15,6 +15,8 @@
## .. include:: ../doc/pegdocs.txt ## .. include:: ../doc/pegdocs.txt
## ##
include "system/inclrtl"
const const
useUnicode = true ## change this to deactivate proper UTF-8 support useUnicode = true ## change this to deactivate proper UTF-8 support
@ -79,7 +81,7 @@ type
TPeg* = TNode ## type that represents a PEG TPeg* = TNode ## type that represents a PEG
proc term*(t: string): TPeg = proc term*(t: string): TPeg {.nosideEffect, rtl, extern: "npegs$1Str".} =
## constructs a PEG from a terminal string ## constructs a PEG from a terminal string
if t.len != 1: if t.len != 1:
result.kind = pkTerminal result.kind = pkTerminal
@ -88,23 +90,25 @@ proc term*(t: string): TPeg =
result.kind = pkChar result.kind = pkChar
result.ch = t[0] result.ch = t[0]
proc termIgnoreCase*(t: string): TPeg = proc termIgnoreCase*(t: string): TPeg {.
nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG from a terminal string; ignore case for matching ## constructs a PEG from a terminal string; ignore case for matching
result.kind = pkTerminalIgnoreCase result.kind = pkTerminalIgnoreCase
result.term = t result.term = t
proc termIgnoreStyle*(t: string): TPeg = proc termIgnoreStyle*(t: string): TPeg {.
nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG from a terminal string; ignore style for matching ## constructs a PEG from a terminal string; ignore style for matching
result.kind = pkTerminalIgnoreStyle result.kind = pkTerminalIgnoreStyle
result.term = t result.term = t
proc term*(t: char): TPeg = proc term*(t: char): TPeg {.nosideEffect, rtl, extern: "npegs$1Char".} =
## constructs a PEG from a terminal char ## constructs a PEG from a terminal char
assert t != '\0' assert t != '\0'
result.kind = pkChar result.kind = pkChar
result.ch = t result.ch = t
proc charSet*(s: set[char]): TPeg = proc charSet*(s: set[char]): TPeg {.nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG from a character set `s` ## constructs a PEG from a character set `s`
assert '\0' notin s assert '\0' notin s
result.kind = pkCharChoice result.kind = pkCharChoice
@ -136,7 +140,8 @@ template multipleOp(k: TPegKind, localOpt: expr) =
if result.len == 1: if result.len == 1:
result = result.sons[0] result = result.sons[0]
proc `/`*(a: openArray[TPeg]): TPeg = proc `/`*(a: openArray[TPeg]): TPeg {.
nosideEffect, rtl, extern: "npegsOrderedChoice".} =
## constructs an ordered choice with the PEGs in `a` ## constructs an ordered choice with the PEGs in `a`
multipleOp(pkOrderedChoice, addChoice) multipleOp(pkOrderedChoice, addChoice)
@ -149,11 +154,12 @@ proc addSequence(dest: var TPeg, elem: TPeg) =
else: add(dest, elem) else: add(dest, elem)
else: add(dest, elem) else: add(dest, elem)
proc sequence*(a: openArray[TPeg]): TPeg = proc sequence*(a: openArray[TPeg]): TPeg {.
nosideEffect, rtl, extern: "npegs$1".} =
## constructs a sequence with all the PEGs from `a` ## constructs a sequence with all the PEGs from `a`
multipleOp(pkSequence, addSequence) multipleOp(pkSequence, addSequence)
proc `?`*(a: TPeg): TPeg = proc `?`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsOptional".} =
## constructs an optional for the PEG `a` ## constructs an optional for the PEG `a`
if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar,
pkGreedyRepSet}: pkGreedyRepSet}:
@ -164,7 +170,7 @@ proc `?`*(a: TPeg): TPeg =
result.kind = pkOption result.kind = pkOption
result.sons = @[a] result.sons = @[a]
proc `*`*(a: TPeg): TPeg = proc `*`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyRep".} =
## constructs a "greedy repetition" for the PEG `a` ## constructs a "greedy repetition" for the PEG `a`
case a.kind case a.kind
of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption:
@ -182,7 +188,7 @@ proc `*`*(a: TPeg): TPeg =
result.kind = pkGreedyRep result.kind = pkGreedyRep
result.sons = @[a] result.sons = @[a]
proc `@`*(a: TPeg): TPeg = proc `@`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsSearch".} =
## constructs a "search" for the PEG `a` ## constructs a "search" for the PEG `a`
result.kind = pkSearch result.kind = pkSearch
result.sons = @[a] result.sons = @[a]
@ -199,16 +205,16 @@ when false:
for i in 0..a.sons.len-1: for i in 0..a.sons.len-1:
if contains(a.sons[i], k): return true if contains(a.sons[i], k): return true
proc `+`*(a: TPeg): TPeg = proc `+`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsGreedyPosRep".} =
## constructs a "greedy positive repetition" with the PEG `a` ## constructs a "greedy positive repetition" with the PEG `a`
return sequence(a, *a) return sequence(a, *a)
proc `&`*(a: TPeg): TPeg = proc `&`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsAndPredicate".} =
## constructs an "and predicate" with the PEG `a` ## constructs an "and predicate" with the PEG `a`
result.kind = pkAndPredicate result.kind = pkAndPredicate
result.sons = @[a] result.sons = @[a]
proc `!`*(a: TPeg): TPeg = proc `!`*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsNotPredicate".} =
## constructs a "not predicate" with the PEG `a` ## constructs a "not predicate" with the PEG `a`
result.kind = pkNotPredicate result.kind = pkNotPredicate
result.sons = @[a] result.sons = @[a]
@ -225,24 +231,27 @@ proc newLine*: TPeg {.inline.} =
## constructs the PEG `newline`:idx: (``\n``) ## constructs the PEG `newline`:idx: (``\n``)
result.kind = pkNewline result.kind = pkNewline
proc capture*(a: TPeg): TPeg = proc capture*(a: TPeg): TPeg {.nosideEffect, rtl, extern: "npegsCapture".} =
## constructs a capture with the PEG `a` ## constructs a capture with the PEG `a`
result.kind = pkCapture result.kind = pkCapture
result.sons = @[a] result.sons = @[a]
proc backref*(index: range[1..MaxSubPatterns]): TPeg = proc backref*(index: range[1..MaxSubPatterns]): TPeg {.
nosideEffect, rtl, extern: "npegs$1".} =
## constructs a back reference of the given `index`. `index` starts counting ## constructs a back reference of the given `index`. `index` starts counting
## from 1. ## from 1.
result.kind = pkBackRef result.kind = pkBackRef
result.index = index-1 result.index = index-1
proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg = proc backrefIgnoreCase*(index: range[1..MaxSubPatterns]): TPeg {.
nosideEffect, rtl, extern: "npegs$1".} =
## constructs a back reference of the given `index`. `index` starts counting ## constructs a back reference of the given `index`. `index` starts counting
## from 1. Ignores case for matching. ## from 1. Ignores case for matching.
result.kind = pkBackRefIgnoreCase result.kind = pkBackRefIgnoreCase
result.index = index-1 result.index = index-1
proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg = proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
nosideEffect, rtl, extern: "npegs$1".}=
## constructs a back reference of the given `index`. `index` starts counting ## constructs a back reference of the given `index`. `index` starts counting
## from 1. Ignores style for matching. ## from 1. Ignores style for matching.
result.kind = pkBackRefIgnoreStyle result.kind = pkBackRefIgnoreStyle
@ -263,7 +272,8 @@ proc spaceCost(n: TPeg): int =
inc(result, spaceCost(n.sons[i])) inc(result, spaceCost(n.sons[i]))
if result >= InlineThreshold: break if result >= InlineThreshold: break
proc nonterminal*(n: PNonTerminal): TPeg = proc nonterminal*(n: PNonTerminal): TPeg {.
nosideEffect, rtl, extern: "npegs$1".} =
## constructs a PEG that consists of the nonterminal symbol ## constructs a PEG that consists of the nonterminal symbol
assert n != nil assert n != nil
if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold:
@ -273,7 +283,8 @@ proc nonterminal*(n: PNonTerminal): TPeg =
result.kind = pkNonTerminal result.kind = pkNonTerminal
result.nt = n result.nt = n
proc newNonTerminal*(name: string, line, column: int): PNonTerminal = proc newNonTerminal*(name: string, line, column: int): PNonTerminal {.
nosideEffect, rtl, extern: "npegs$1".} =
## constructs a nonterminal symbol ## constructs a nonterminal symbol
new(result) new(result)
result.name = name result.name = name
@ -432,7 +443,7 @@ proc toStrAux(r: TPeg, res: var string) =
toStrAux(r.sons[i], res) toStrAux(r.sons[i], res)
add(res, "\n") add(res, "\n")
proc `$` *(r: TPeg): string = proc `$` *(r: TPeg): string {.nosideEffect, rtl, extern: "npegsToString".} =
## converts a PEG to its string representation ## converts a PEG to its string representation
result = "" result = ""
toStrAux(r, result) toStrAux(r, result)
@ -598,7 +609,7 @@ proc m(s: string, p: TPeg, start: int, c: var TMatchClosure): int =
of pkRule, pkList: assert false of pkRule, pkList: assert false
proc match*(s: string, pattern: TPeg, matches: var openarray[string], proc match*(s: string, pattern: TPeg, matches: var openarray[string],
start = 0): bool = start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
## the captured substrings in the array ``matches``. If it does not ## the captured substrings in the array ``matches``. If it does not
## match, nothing is written into ``matches`` and ``false`` is ## match, nothing is written into ``matches`` and ``false`` is
@ -609,13 +620,14 @@ proc match*(s: string, pattern: TPeg, matches: var openarray[string],
for i in 0..c.ml-1: for i in 0..c.ml-1:
matches[i] = copy(s, c.matches[i][0], c.matches[i][1]) matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
proc match*(s: string, pattern: TPeg, start = 0): bool = proc match*(s: string, pattern: TPeg,
start = 0): bool {.nosideEffect, rtl, extern: "npegs$1".} =
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
var c: TMatchClosure var c: TMatchClosure
result = m(s, pattern, start, c) == len(s)-start result = m(s, pattern, start, c) == len(s)-start
proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string], proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
start = 0): int = start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## the same as ``match``, but it returns the length of the match, ## the same as ``match``, but it returns the length of the match,
## if there is no match, -1 is returned. Note that a match length ## if there is no match, -1 is returned. Note that a match length
## of zero can happen. It's possible that a suffix of `s` remains ## of zero can happen. It's possible that a suffix of `s` remains
@ -626,7 +638,8 @@ proc matchLen*(s: string, pattern: TPeg, matches: var openarray[string],
for i in 0..c.ml-1: for i in 0..c.ml-1:
matches[i] = copy(s, c.matches[i][0], c.matches[i][1]) matches[i] = copy(s, c.matches[i][0], c.matches[i][1])
proc matchLen*(s: string, pattern: TPeg, start = 0): int = proc matchLen*(s: string, pattern: TPeg,
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
## the same as ``match``, but it returns the length of the match, ## the same as ``match``, but it returns the length of the match,
## if there is no match, -1 is returned. Note that a match length ## if there is no match, -1 is returned. Note that a match length
## of zero can happen. It's possible that a suffix of `s` remains ## of zero can happen. It's possible that a suffix of `s` remains
@ -635,7 +648,7 @@ proc matchLen*(s: string, pattern: TPeg, start = 0): int =
result = m(s, pattern, start, c) result = m(s, pattern, start, c)
proc find*(s: string, pattern: TPeg, matches: var openarray[string], proc find*(s: string, pattern: TPeg, matches: var openarray[string],
start = 0): int = start = 0): int {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## returns the starting position of ``pattern`` in ``s`` and the captured ## returns the starting position of ``pattern`` in ``s`` and the captured
## substrings in the array ``matches``. If it does not match, nothing ## substrings in the array ``matches``. If it does not match, nothing
## is written into ``matches`` and -1 is returned. ## is written into ``matches`` and -1 is returned.
@ -644,7 +657,8 @@ proc find*(s: string, pattern: TPeg, matches: var openarray[string],
return -1 return -1
# could also use the pattern here: (!P .)* P # could also use the pattern here: (!P .)* P
proc find*(s: string, pattern: TPeg, start = 0): int = proc find*(s: string, pattern: TPeg,
start = 0): int {.nosideEffect, rtl, extern: "npegs$1".} =
## returns the starting position of ``pattern`` in ``s``. If it does not ## returns the starting position of ``pattern`` in ``s``. If it does not
## match, -1 is returned. ## match, -1 is returned.
for i in 0 .. s.len-1: for i in 0 .. s.len-1:
@ -675,25 +689,29 @@ template `=~`*(s: string, pattern: TPeg): expr =
# ------------------------- more string handling ------------------------------ # ------------------------- more string handling ------------------------------
proc contains*(s: string, pattern: TPeg, start = 0): bool = proc contains*(s: string, pattern: TPeg, start = 0): bool {.
nosideEffect, rtl, extern: "npegs$1".} =
## same as ``find(s, pattern, start) >= 0`` ## same as ``find(s, pattern, start) >= 0``
return find(s, pattern, start) >= 0 return find(s, pattern, start) >= 0
proc contains*(s: string, pattern: TPeg, matches: var openArray[string], proc contains*(s: string, pattern: TPeg, matches: var openArray[string],
start = 0): bool = start = 0): bool {.nosideEffect, rtl, extern: "npegs$1Capture".} =
## same as ``find(s, pattern, matches, start) >= 0`` ## same as ``find(s, pattern, matches, start) >= 0``
return find(s, pattern, matches, start) >= 0 return find(s, pattern, matches, start) >= 0
proc startsWith*(s: string, prefix: TPeg): bool = proc startsWith*(s: string, prefix: TPeg): bool {.
nosideEffect, rtl, extern: "npegs$1".} =
## returns true if `s` starts with the pattern `prefix` ## returns true if `s` starts with the pattern `prefix`
result = matchLen(s, prefix) >= 0 result = matchLen(s, prefix) >= 0
proc endsWith*(s: string, suffix: TPeg): bool = proc endsWith*(s: string, suffix: TPeg): bool {.
nosideEffect, rtl, extern: "npegs$1".} =
## returns true if `s` ends with the pattern `prefix` ## returns true if `s` ends with the pattern `prefix`
for i in 0 .. s.len-1: for i in 0 .. s.len-1:
if matchLen(s, suffix, i) == s.len - i: return true if matchLen(s, suffix, i) == s.len - i: return true
proc replace*(s: string, sub: TPeg, by: string): string = proc replace*(s: string, sub: TPeg, by: string): string {.
nosideEffect, rtl, extern: "npegs$1".} =
## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by`
## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples:
## ##
@ -720,7 +738,8 @@ proc replace*(s: string, sub: TPeg, by: string): string =
add(result, copy(s, i)) add(result, copy(s, i))
proc parallelReplace*(s: string, subs: openArray[ proc parallelReplace*(s: string, subs: openArray[
tuple[pattern: TPeg, repl: string]]): string = tuple[pattern: TPeg, repl: string]]): string {.
nosideEffect, rtl, extern: "npegs$1".} =
## Returns a modified copy of `s` with the substitutions in `subs` ## Returns a modified copy of `s` with the substitutions in `subs`
## applied in parallel. ## applied in parallel.
result = "" result = ""
@ -740,7 +759,8 @@ proc parallelReplace*(s: string, subs: openArray[
add(result, copy(s, i)) add(result, copy(s, i))
proc transformFile*(infile, outfile: string, proc transformFile*(infile, outfile: string,
subs: openArray[tuple[pattern: TPeg, repl: string]]) = subs: openArray[tuple[pattern: TPeg, repl: string]]) {.
rtl, extern: "npegs$1".} =
## reads in the file `infile`, performs a parallel replacement (calls ## reads in the file `infile`, performs a parallel replacement (calls
## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an ## `parallelReplace`) and writes back to `outfile`. Calls ``quit`` if an
## error occurs. This is supposed to be used for quick scripting. ## error occurs. This is supposed to be used for quick scripting.
@ -787,7 +807,8 @@ iterator split*(s: string, sep: TPeg): string =
if first < last: if first < last:
yield copy(s, first, last-1) yield copy(s, first, last-1)
proc split*(s: string, sep: TPeg): seq[string] {.noSideEffect.} = proc split*(s: string, sep: TPeg): seq[string] {.
nosideEffect, rtl, extern: "npegs$1".} =
## Splits the string `s` into substrings. ## Splits the string `s` into substrings.
accumulateResult(split(s, sep)) accumulateResult(split(s, sep))
@ -1265,6 +1286,8 @@ proc primary(p: var TPegParser): TPeg =
of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'}) of "S": result = charset({'\1'..'\xff'} - {' ', '\9'..'\13'})
of "w": result = charset({'a'..'z', 'A'..'Z', '_', '0'..'9'}) of "w": result = charset({'a'..'z', 'A'..'Z', '_', '0'..'9'})
of "W": result = charset({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'}) of "W": result = charset({'\1'..'\xff'} - {'a'..'z','A'..'Z','_','0'..'9'})
of "a": result = charset({'a'..'z', 'A'..'Z'})
of "A": result = charset({'\1'..'\xff'} - {'a'..'z', 'A'..'Z'})
of "ident": result = pegs.ident of "ident": result = pegs.ident
else: pegError(p, "unknown built-in: " & p.tok.literal) else: pegError(p, "unknown built-in: " & p.tok.literal)
getTok(p) getTok(p)

View file

@ -16,6 +16,8 @@
## Leaves can be cached for better memory efficiency at the cost of ## Leaves can be cached for better memory efficiency at the cost of
## runtime efficiency. ## runtime efficiency.
include "system/inclrtl"
{.deadCodeElim: on.} {.deadCodeElim: on.}
{.push debugger:off .} # the user does not want to trace a part {.push debugger:off .} # the user does not want to trace a part
@ -44,7 +46,7 @@ proc isConc(r: PRope): bool {.inline.} = return isNil(r.data)
# performance. But for the caching tree we use the leaf's left and right # performance. But for the caching tree we use the leaf's left and right
# pointers. # pointers.
proc len*(a: PRope): int = proc len*(a: PRope): int {.rtl, extern: "nro$1".} =
## the rope's length ## the rope's length
if a == nil: result = 0 if a == nil: result = 0
else: result = a.length else: result = a.length
@ -126,7 +128,7 @@ proc insertInCache(s: string, tree: PRope): PRope =
result.left = t result.left = t
t.right = nil t.right = nil
proc rope*(s: string): PRope = proc rope*(s: string): PRope {.rtl, extern: "nro$1Str".} =
## Converts a string to a rope. ## Converts a string to a rope.
if s.len == 0: if s.len == 0:
result = nil result = nil
@ -136,25 +138,25 @@ proc rope*(s: string): PRope =
else: else:
result = newRope(s) result = newRope(s)
proc rope*(i: BiggestInt): PRope = proc rope*(i: BiggestInt): PRope {.rtl, extern: "nro$1BiggestInt".} =
## Converts an int to a rope. ## Converts an int to a rope.
result = rope($i) result = rope($i)
proc rope*(f: BiggestFloat): PRope = proc rope*(f: BiggestFloat): PRope {.rtl, extern: "nro$1BiggestFloat".} =
## Converts a float to a rope. ## Converts a float to a rope.
result = rope($f) result = rope($f)
proc disableCache*() = proc disableCache*() {.rtl, extern: "nro$1".} =
## the cache is discarded and disabled. The GC will reuse its used memory. ## the cache is discarded and disabled. The GC will reuse its used memory.
cache = nil cache = nil
cacheEnabled = false cacheEnabled = false
proc enableCache*() = proc enableCache*() {.rtl, extern: "nro$1".} =
## Enables the caching of leaves. This reduces the memory footprint at ## Enables the caching of leaves. This reduces the memory footprint at
## the cost of runtime efficiency. ## the cost of runtime efficiency.
cacheEnabled = true cacheEnabled = true
proc `&`*(a, b: PRope): PRope = proc `&`*(a, b: PRope): PRope {.rtl, extern: "nroConcRopeRope".} =
## the concatenation operator for ropes. ## the concatenation operator for ropes.
if a == nil: if a == nil:
result = b result = b
@ -174,27 +176,27 @@ proc `&`*(a, b: PRope): PRope =
result.left = a result.left = a
result.right = b result.right = b
proc `&`*(a: PRope, b: string): PRope = proc `&`*(a: PRope, b: string): PRope {.rtl, extern: "nroConcRopeStr".} =
## the concatenation operator for ropes. ## the concatenation operator for ropes.
result = a & rope(b) result = a & rope(b)
proc `&`*(a: string, b: PRope): PRope = proc `&`*(a: string, b: PRope): PRope {.rtl, extern: "nroConcStrRope".} =
## the concatenation operator for ropes. ## the concatenation operator for ropes.
result = rope(a) & b result = rope(a) & b
proc `&`*(a: openarray[PRope]): PRope = proc `&`*(a: openarray[PRope]): PRope {.rtl, extern: "nroConcOpenArray".} =
## the concatenation operator for an openarray of ropes. ## the concatenation operator for an openarray of ropes.
for i in countup(0, high(a)): result = result & a[i] for i in countup(0, high(a)): result = result & a[i]
proc add*(a: var PRope, b: PRope) = proc add*(a: var PRope, b: PRope) {.rtl, extern: "nro$1Rope".} =
## adds `b` to the rope `a`. ## adds `b` to the rope `a`.
a = a & b a = a & b
proc add*(a: var PRope, b: string) = proc add*(a: var PRope, b: string) {.rtl, extern: "nro$1Str".} =
## adds `b` to the rope `a`. ## adds `b` to the rope `a`.
a = a & b a = a & b
proc `[]`*(r: PRope, i: int): char = proc `[]`*(r: PRope, i: int): char {.rtl, extern: "nroCharAt".} =
## returns the character at position `i` in the rope `r`. This is quite ## returns the character at position `i` in the rope `r`. This is quite
## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned. ## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned.
var x = r var x = r
@ -229,11 +231,11 @@ iterator items*(r: PRope): char =
for s in leaves(r): for s in leaves(r):
for c in items(s): yield c for c in items(s): yield c
proc write*(f: TFile, r: PRope) = proc write*(f: TFile, r: PRope) {.rtl, extern: "nro$1".} =
## writes a rope to a file. ## writes a rope to a file.
for s in leaves(r): write(f, s) for s in leaves(r): write(f, s)
proc `$`*(r: PRope): string = proc `$`*(r: PRope): string {.rtl, extern: "nroToString".}=
## converts a rope back to a string. ## converts a rope back to a string.
result = newString(r.len) result = newString(r.len)
setLen(result, 0) setLen(result, 0)
@ -289,7 +291,8 @@ when false:
if i - 1 >= start: if i - 1 >= start:
add(result, copy(frmt, start, i-1)) add(result, copy(frmt, start, i-1))
proc `%`*(frmt: string, args: openarray[PRope]): PRope = proc `%`*(frmt: string, args: openarray[PRope]): PRope {.
rtl, extern: "nroFormat".} =
## `%` substitution operator for ropes. Does not support the ``$identifier`` ## `%` substitution operator for ropes. Does not support the ``$identifier``
## nor ``${identifier}`` notations. ## nor ``${identifier}`` notations.
var i = 0 var i = 0
@ -333,11 +336,12 @@ proc `%`*(frmt: string, args: openarray[PRope]): PRope =
if i - 1 >= start: if i - 1 >= start:
add(result, copy(frmt, start, i - 1)) add(result, copy(frmt, start, i - 1))
proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) = proc addf*(c: var PRope, frmt: string, args: openarray[PRope]) {.
rtl, extern: "nro$1".} =
## shortcut for ``add(c, frmt % args)``. ## shortcut for ``add(c, frmt % args)``.
add(c, frmt % args) add(c, frmt % args)
proc equalsFile*(r: PRope, f: TFile): bool = proc equalsFile*(r: PRope, f: TFile): bool {.rtl, extern: "nro$1File".} =
## returns true if the contents of the file `f` equal `r`. ## returns true if the contents of the file `f` equal `r`.
var bufSize = 1024 # reasonable start value var bufSize = 1024 # reasonable start value
var buf = alloc(BufSize) var buf = alloc(BufSize)
@ -352,7 +356,7 @@ proc equalsFile*(r: PRope, f: TFile): bool =
result = readBuffer(f, buf, 1) == 0 # really at the end of file? result = readBuffer(f, buf, 1) == 0 # really at the end of file?
dealloc(buf) dealloc(buf)
proc equalsFile*(r: PRope, f: string): bool = proc equalsFile*(r: PRope, f: string): bool {.rtl, extern: "nro$1Str".} =
## returns true if the contents of the file `f` equal `r`. If `f` does not ## returns true if the contents of the file `f` equal `r`. If `f` does not
## exist, false is returned. ## exist, false is returned.
var bin: TFile var bin: TFile

View file

@ -15,6 +15,8 @@
import import
os, hashes, strutils os, hashes, strutils
include "system/inclrtl"
type type
TStringTableMode* = enum ## describes the tables operation mode TStringTableMode* = enum ## describes the tables operation mode
modeCaseSensitive, ## the table is case sensitive modeCaseSensitive, ## the table is case sensitive
@ -29,28 +31,7 @@ type
PStringTable* = ref TStringTable ## use this type to declare string tables PStringTable* = ref TStringTable ## use this type to declare string tables
proc newStringTable*(keyValuePairs: openarray[string], proc len*(t: PStringTable): int {.rtl, extern: "nst$1".} =
mode: TStringTableMode = modeCaseSensitive): PStringTable
## creates a new string table with given key value pairs.
## Example::
## var mytab = newStringTable("key1", "val1", "key2", "val2",
## modeCaseInsensitive)
proc newStringTable*(mode: TStringTableMode): PStringTable
## creates a new string table that is empty.
proc `[]=`*(t: PStringTable, key, val: string)
## puts a (key, value)-pair into `t`.
proc `[]`*(t: PStringTable, key: string): string
## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
proc hasKey*(t: PStringTable, key: string): bool
## returns true iff `key` is in the table `t`.
proc len*(t: PStringTable): int =
## returns the number of keys in `t`. ## returns the number of keys in `t`.
result = t.counter result = t.counter
@ -70,29 +51,12 @@ type
useKey ## do not replace ``$key`` if it is not found useKey ## do not replace ``$key`` if it is not found
## in the table (or in the environment) ## in the table (or in the environment)
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string
## The `%` operator for string tables.
# implementation # implementation
const const
growthFactor = 2 growthFactor = 2
startSize = 64 startSize = 64
proc newStringTable(mode: TStringTableMode): PStringTable =
new(result)
result.mode = mode
result.counter = 0
newSeq(result.data, startSize)
proc newStringTable(keyValuePairs: openarray[string],
mode: TStringTableMode = modeCaseSensitive): PStringTable =
result = newStringTable(mode)
var i = 0
while i < high(keyValuePairs):
result[keyValuePairs[i]] = keyValuePairs[i + 1]
inc(i, 2)
proc myhash(t: PStringTable, key: string): THash = proc myhash(t: PStringTable, key: string): THash =
case t.mode case t.mode
of modeCaseSensitive: result = hashes.hash(key) of modeCaseSensitive: result = hashes.hash(key)
@ -121,13 +85,17 @@ proc RawGet(t: PStringTable, key: string): int =
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))
result = - 1 result = - 1
proc `[]`(t: PStringTable, key: string): string = proc `[]`*(t: PStringTable, key: string): string {.rtl, extern: "nstGet".} =
## retrieves the value at ``t[key]``. If `key` is not in `t`, "" is returned
## and no exception is raised. One can check with ``hasKey`` whether the key
## exists.
var index: int var index: int
index = RawGet(t, key) index = RawGet(t, key)
if index >= 0: result = t.data[index].val if index >= 0: result = t.data[index].val
else: result = "" else: result = ""
proc hasKey(t: PStringTable, key: string): bool = proc hasKey*(t: PStringTable, key: string): bool {.rtl, extern: "nst$1".} =
## returns true iff `key` is in the table `t`.
result = rawGet(t, key) >= 0 result = rawGet(t, key) >= 0
proc RawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) = proc RawInsert(t: PStringTable, data: var TKeyValuePairSeq, key, val: string) =
@ -145,7 +113,8 @@ proc Enlarge(t: PStringTable) =
if not isNil(t.data[i].key): RawInsert(t, n, t.data[i].key, t.data[i].val) if not isNil(t.data[i].key): RawInsert(t, n, t.data[i].key, t.data[i].val)
swap(t.data, n) swap(t.data, n)
proc `[]=`(t: PStringTable, key, val: string) = proc `[]=`*(t: PStringTable, key, val: string) {.rtl, extern: "nstPut".} =
## puts a (key, value)-pair into `t`.
var index = RawGet(t, key) var index = RawGet(t, key)
if index >= 0: if index >= 0:
t.data[index].val = val t.data[index].val = val
@ -168,7 +137,30 @@ proc getValue(t: PStringTable, flags: set[TFormatFlag], key: string): string =
if useKey in flags: result = '$' & key if useKey in flags: result = '$' & key
elif not (useEmpty in flags): raiseFormatException(key) elif not (useEmpty in flags): raiseFormatException(key)
proc `%`(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string = proc newStringTable*(mode: TStringTableMode): PStringTable {.
rtl, extern: "nst$1".} =
## creates a new string table that is empty.
new(result)
result.mode = mode
result.counter = 0
newSeq(result.data, startSize)
proc newStringTable*(keyValuePairs: openarray[string],
mode: TStringTableMode = modeCaseSensitive): PStringTable {.
rtl, extern: "nst$1WithPairs".} =
## creates a new string table with given key value pairs.
## Example::
## var mytab = newStringTable("key1", "val1", "key2", "val2",
## modeCaseInsensitive)
result = newStringTable(mode)
var i = 0
while i < high(keyValuePairs):
result[keyValuePairs[i]] = keyValuePairs[i + 1]
inc(i, 2)
proc `%`*(f: string, t: PStringTable, flags: set[TFormatFlag] = {}): string {.
rtl, extern: "nstFormat".} =
## The `%` operator for string tables.
const const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'} PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
result = "" result = ""

View file

@ -8,7 +8,7 @@
# #
## This module contains various string utility routines. ## This module contains various string utility routines.
## See the module `regexprs` for regular expression support. ## See the module `re` for regular expression support.
## See the module `pegs` for PEG support. ## See the module `pegs` for PEG support.
import parseutils import parseutils
@ -18,6 +18,8 @@ import parseutils
{.push debugger:off .} # the user does not want to trace a part {.push debugger:off .} # the user does not want to trace a part
# of the standard library! # of the standard library!
include "system/inclrtl"
type type
TCharSet* = set[char] # for compatibility with Nim TCharSet* = set[char] # for compatibility with Nim
@ -40,7 +42,151 @@ const
IdentStartChars* = {'a'..'z', 'A'..'Z', '_'} IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
## the set of characters an identifier can start with ## the set of characters an identifier can start with
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
proc toLower*(c: Char): Char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerChar".} =
## Converts `c` into lower case. This works only for the letters A-Z.
## See `unicode.toLower` for a version that works for any Unicode character.
if c in {'A'..'Z'}:
result = chr(ord(c) + (ord('a') - ord('A')))
else:
result = c
proc toLower*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuToLowerStr".} =
## Converts `s` into lower case. This works only for the letters A-Z.
## See `unicode.toLower` for a version that works for any Unicode character.
result = newString(len(s))
for i in 0..len(s) - 1:
result[i] = toLower(s[i])
proc toUpper*(c: Char): Char {.noSideEffect, procvar,
rtl, extern: "nsuToUpperChar".} =
## Converts `c` into upper case. This works only for the letters a-z.
## See `unicode.toUpper` for a version that works for any Unicode character.
if c in {'a'..'z'}:
result = Chr(Ord(c) - (Ord('a') - Ord('A')))
else:
result = c
proc toUpper*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuToUpperStr".} =
## Converts `s` into upper case. This works only for the letters a-z.
## See `unicode.toUpper` for a version that works for any Unicode character.
result = newString(len(s))
for i in 0..len(s) - 1:
result[i] = toUpper(s[i])
proc capitalize*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuCapitalize".} =
## Converts the first character of `s` into upper case.
## This works only for the letters a-z.
result = toUpper(s[0]) & copy(s, 1)
proc normalize*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuNormalize".} =
## Normalizes the string `s`. That means to convert it to lower case and
## remove any '_'. This is needed for Nimrod identifiers for example.
result = ""
for i in 0..len(s) - 1:
if s[i] in {'A'..'Z'}:
add result, Chr(Ord(s[i]) + (Ord('a') - Ord('A')))
elif s[i] != '_':
add result, s[i]
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
rtl, extern: "nsuCmpIgnoreCase".} =
## Compares two strings in a case insensitive manner. Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
## | > 0 iff a > b
var i = 0
while i < a.len and i < b.len:
result = ord(toLower(a[i])) - ord(toLower(b[i]))
if result != 0: return
inc(i)
result = a.len - b.len
{.push checks: off, line_trace: off .} # this is a hot-spot in the compiler!
# thus we compile without checks here
proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
rtl, extern: "nsuCmpIgnoreStyle".} =
## Compares two strings normalized (i.e. case and
## underscores do not matter). Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
## | > 0 iff a > b
var i = 0
var j = 0
while True:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
{.pop.}
proc findNormalized(x: string, inArray: openarray[string]): int =
var i = 0
while i < high(inArray):
if cmpIgnoreStyle(x, inArray[i]) == 0: return i
inc(i, 2) # incrementing by 1 would probably result in a
# security hole...
return -1
proc addf*(s: var string, formatstr: string, a: openarray[string]) {.
noSideEffect, rtl, extern: "nsuAddf".} =
## The same as ``add(s, formatstr % a)``, but more efficient.
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
var i = 0
var num = 0
while i < len(formatstr):
if formatstr[i] == '$':
case formatstr[i+1] # again we use the fact that strings
# are zero-terminated here
of '#':
add s, a[num]
inc i, 2
inc num
of '$':
add s, '$'
inc(i, 2)
of '1'..'9':
var j = 0
inc(i) # skip $
while formatstr[i] in Digits:
j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i)
num = j
add s, a[j - 1]
of '{':
var j = i+1
while formatstr[j] notin {'\0', '}'}: inc(j)
var x = findNormalized(copy(formatstr, i+2, j-1), a)
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j+1
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var j = i+1
while formatstr[j] in PatternChars: inc(j)
var x = findNormalized(copy(formatstr, i+1, j-1), a)
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j
else: raise newException(EInvalidValue, "invalid format string")
else:
add s, formatstr[i]
inc(i)
proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect,
rtl, extern: "nsuFormatOpenArray".} =
## The `substitution`:idx: operator performs string substitutions in ## The `substitution`:idx: operator performs string substitutions in
## `formatstr` and returns a modified `formatstr`. This is often called ## `formatstr` and returns a modified `formatstr`. This is often called
## `string interpolation`:idx:. ## `string interpolation`:idx:.
@ -77,57 +223,38 @@ proc `%` *(formatstr: string, a: openarray[string]): string {.noSideEffect.}
## ##
## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is ## The variables are compared with `cmpIgnoreStyle`. `EInvalidValue` is
## raised if an ill-formed format string has been passed to the `%` operator. ## raised if an ill-formed format string has been passed to the `%` operator.
result = ""
addf(result, formatstr, a)
proc `%` *(formatstr, a: string): string {.noSideEffect.} proc `%` *(formatstr, a: string): string {.noSideEffect,
rtl, extern: "nsuFormatSingleElem".} =
## This is the same as ``formatstr % [a]``. ## This is the same as ``formatstr % [a]``.
return formatstr % [a]
proc addf*(s: var string, formatstr: string, a: openarray[string]) proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
## The same as ``add(s, formatstr % a)``, but more efficient. rtl, extern: "nsuStrip".} =
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect.}
## Strips whitespace from `s` and returns the resulting string. ## Strips whitespace from `s` and returns the resulting string.
## If `leading` is true, leading whitespace is stripped. ## If `leading` is true, leading whitespace is stripped.
## If `trailing` is true, trailing whitespace is stripped. ## If `trailing` is true, trailing whitespace is stripped.
const
chars: set[Char] = Whitespace
var
first = 0
last = len(s)-1
if leading:
while s[first] in chars: inc(first)
if trailing:
while last >= 0 and s[last] in chars: dec(last)
result = copy(s, first, last)
proc toLower*(s: string): string {.noSideEffect, procvar.} proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
## Converts `s` into lower case. This works only for the letters A-Z.
## See `unicode.toLower` for a version that works for any Unicode character.
proc toLower*(c: Char): Char {.noSideEffect, procvar.}
## Converts `c` into lower case. This works only for the letters A-Z.
## See `unicode.toLower` for a version that works for any Unicode character.
proc toUpper*(s: string): string {.noSideEffect, procvar.}
## Converts `s` into upper case. This works only for the letters a-z.
## See `unicode.toUpper` for a version that works for any Unicode character.
proc toUpper*(c: Char): Char {.noSideEffect, procvar.}
## Converts `c` into upper case. This works only for the letters a-z.
## See `unicode.toUpper` for a version that works for any Unicode character.
proc capitalize*(s: string): string {.noSideEffect, procvar.}
## Converts the first character of `s` into upper case.
## This works only for the letters a-z.
proc normalize*(s: string): string {.noSideEffect, procvar.}
## Normalizes the string `s`. That means to convert it to lower case and
## remove any '_'. This is needed for Nimrod identifiers for example.
proc find*(s, sub: string, start: int = 0): int {.noSideEffect.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect.}
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect.}
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
proc toOctal*(c: char): string
## Converts a character `c` to its octal representation. The resulting ## Converts a character `c` to its octal representation. The resulting
## string may not have a leading zero. Its length is always exactly 3. ## string may not have a leading zero. Its length is always exactly 3.
result = newString(3)
var val = ord(c)
for i in countdown(2, 0):
result[i] = Chr(val mod 8 + ord('0'))
val = val div 8
iterator split*(s: string, seps: set[char] = Whitespace): string = iterator split*(s: string, seps: set[char] = Whitespace): string =
## Splits the string `s` into substrings. ## Splits the string `s` into substrings.
@ -228,86 +355,123 @@ iterator splitLines*(s: string): string =
else: break # was '\0' else: break # was '\0'
first = last first = last
proc splitLines*(s: string): seq[string] {.noSideEffect.} = proc splitLines*(s: string): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitLines".} =
## The same as the `splitLines` iterator, but is a proc that returns a ## The same as the `splitLines` iterator, but is a proc that returns a
## sequence of substrings. ## sequence of substrings.
accumulateResult(splitLines(s)) accumulateResult(splitLines(s))
proc split*(s: string, seps: set[char] = Whitespace): seq[string] {. proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
noSideEffect.} = noSideEffect, rtl, extern: "nsuSplitCharSet".} =
## The same as the `split` iterator, but is a proc that returns a ## The same as the `split` iterator, but is a proc that returns a
## sequence of substrings. ## sequence of substrings.
accumulateResult(split(s, seps)) accumulateResult(split(s, seps))
proc split*(s: string, sep: char): seq[string] {.noSideEffect.} = proc split*(s: string, sep: char): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitChar".} =
## The same as the `split` iterator, but is a proc that returns a sequence ## The same as the `split` iterator, but is a proc that returns a sequence
## of substrings. ## of substrings.
accumulateResult(split(s, sep)) accumulateResult(split(s, sep))
proc cmpIgnoreCase*(a, b: string): int {.noSideEffect.} proc toHex*(x: BiggestInt, len: int): string {.noSideEffect,
## Compares two strings in a case insensitive manner. Returns: rtl, extern: "nsuToHex".} =
##
## | 0 iff a == b
## | < 0 iff a < b
## | > 0 iff a > b
proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect.}
## Compares two strings normalized (i.e. case and
## underscores do not matter). Returns:
##
## | 0 iff a == b
## | < 0 iff a < b
## | > 0 iff a > b
proc contains*(s: string, c: char): bool {.noSideEffect.}
## Same as ``find(s, c) >= 0``.
proc contains*(s, sub: string): bool {.noSideEffect.}
## Same as ``find(s, sub) >= 0``.
proc contains*(s: string, chars: set[char]): bool {.noSideEffect.}
## Same as ``find(s, chars) >= 0``.
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect.}
## Converts `x` to its hexadecimal representation. The resulting string ## Converts `x` to its hexadecimal representation. The resulting string
## will be exactly `len` characters long. No prefix like ``0x`` ## will be exactly `len` characters long. No prefix like ``0x``
## is generated. `x` is treated as an unsigned value. ## is generated. `x` is treated as an unsigned value.
const
HexChars = "0123456789ABCDEF"
var
shift: BiggestInt
result = newString(len)
for j in countdown(len-1, 0):
result[j] = HexChars[toU32(x shr shift) and 0xF'i32]
shift = shift + 4
proc intToStr*(x: int, minchars: int = 1): string proc intToStr*(x: int, minchars: int = 1): string {.noSideEffect,
rtl, extern: "nsuIntToStr".} =
## Converts `x` to its decimal representation. The resulting string ## Converts `x` to its decimal representation. The resulting string
## will be minimally `minchars` characters long. This is achieved by ## will be minimally `minchars` characters long. This is achieved by
## adding leading zeros. ## adding leading zeros.
result = $abs(x)
for i in 1 .. minchars - len(result):
result = '0' & result
if x < 0:
result = '-' & result
proc ParseInt*(s: string): int {.noSideEffect, procvar.} proc ParseInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseInt".} =
## Parses a decimal integer value contained in `s`. If `s` is not ## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised. ## a valid integer, `EInvalidValue` is raised.
var L = parseutils.parseInt(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar.} proc ParseBiggestInt*(s: string): biggestInt {.noSideEffect, procvar,
rtl, extern: "nsuParseBiggestInt".} =
## Parses a decimal integer value contained in `s`. If `s` is not ## Parses a decimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised. ## a valid integer, `EInvalidValue` is raised.
var L = parseutils.parseBiggestInt(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseFloat*(s: string): float {.noSideEffect, procvar.} proc ParseFloat*(s: string): float {.noSideEffect, procvar,
rtl, extern: "nsuParseFloat".} =
## Parses a decimal floating point value contained in `s`. If `s` is not ## Parses a decimal floating point value contained in `s`. If `s` is not
## a valid floating point number, `EInvalidValue` is raised. ``NAN``, ## a valid floating point number, `EInvalidValue` is raised. ``NAN``,
## ``INF``, ``-INF`` are also supported (case insensitive comparison). ## ``INF``, ``-INF`` are also supported (case insensitive comparison).
var L = parseutils.parseFloat(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid float: " & s)
proc ParseHexInt*(s: string): int {.noSideEffect, procvar.} proc ParseHexInt*(s: string): int {.noSideEffect, procvar,
rtl, extern: "nsuParseHexInt".} =
## Parses a hexadecimal integer value contained in `s`. If `s` is not ## Parses a hexadecimal integer value contained in `s`. If `s` is not
## a valid integer, `EInvalidValue` is raised. `s` can have one of the ## a valid integer, `EInvalidValue` is raised. `s` can have one of the
## following optional prefixes: ``0x``, ``0X``, ``#``. ## following optional prefixes: ``0x``, ``0X``, ``#``.
## Underscores within `s` are ignored. ## Underscores within `s` are ignored.
var i = 0
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
elif s[i] == '#': inc(i)
while true:
case s[i]
of '_': inc(i)
of '0'..'9':
result = result shl 4 or (ord(s[i]) - ord('0'))
inc(i)
of 'a'..'f':
result = result shl 4 or (ord(s[i]) - ord('a') + 10)
inc(i)
of 'A'..'F':
result = result shl 4 or (ord(s[i]) - ord('A') + 10)
inc(i)
of '\0': break
else: raise newException(EInvalidValue, "invalid integer: " & s)
proc repeatChar*(count: int, c: Char = ' '): string proc repeatChar*(count: int, c: Char = ' '): string {.noSideEffect,
rtl, extern: "nsuRepeatChar".} =
## Returns a string of length `count` consisting only of ## Returns a string of length `count` consisting only of
## the character `c`. ## the character `c`.
result = newString(count)
for i in 0..count-1:
result[i] = c
proc startsWith*(s, prefix: string): bool {.noSideEffect.} proc startsWith*(s, prefix: string): bool {.noSideEffect,
rtl, extern: "nsuStartsWith".} =
## Returns true iff ``s`` starts with ``prefix``. ## Returns true iff ``s`` starts with ``prefix``.
## If ``prefix == ""`` true is returned. ## If ``prefix == ""`` true is returned.
var i = 0
while true:
if prefix[i] == '\0': return true
if s[i] != prefix[i]: return false
inc(i)
proc endsWith*(s, suffix: string): bool {.noSideEffect.} proc endsWith*(s, suffix: string): bool {.noSideEffect,
rtl, extern: "nsuEndsWith".} =
## Returns true iff ``s`` ends with ``suffix``. ## Returns true iff ``s`` ends with ``suffix``.
## If ``suffix == ""`` true is returned. ## If ``suffix == ""`` true is returned.
var i = 0
var j = len(s) - len(suffix)
while i+j <% s.len:
if s[i+j] != suffix[i]: return false
inc(i)
if suffix[i] == '\0': return true
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect, proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
inline.} = inline.} =
@ -335,30 +499,6 @@ proc allCharsInSet*(s: string, theSet: TCharSet): bool =
if c notin theSet: return false if c notin theSet: return false
return true return true
proc quoteIfContainsWhite*(s: string): string =
## returns ``'"' & s & '"'`` if `s` contains a space and does not
## start with a quote, else returns `s`
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
result = '"' & s & '"'
else:
result = s
proc startsWith(s, prefix: string): bool =
var i = 0
while true:
if prefix[i] == '\0': return true
if s[i] != prefix[i]: return false
inc(i)
proc endsWith(s, suffix: string): bool =
var
i = 0
j = len(s) - len(suffix)
while i+j <% s.len:
if s[i+j] != suffix[i]: return false
inc(i)
if suffix[i] == '\0': return true
# 012345 # 012345
# 345 # 345
@ -373,112 +513,10 @@ when false:
if result >= 0: return -2 # ambiguous if result >= 0: return -2 # ambiguous
result = i result = i
proc repeatChar(count: int, c: Char = ' '): string =
result = newString(count)
for i in 0..count-1:
result[i] = c
proc intToStr(x: int, minchars: int = 1): string =
result = $abs(x)
for i in 1 .. minchars - len(result):
result = '0' & result
if x < 0:
result = '-' & result
proc toOctal(c: char): string =
result = newString(3)
var val = ord(c)
for i in countdown(2, 0):
result[i] = Chr(val mod 8 + ord('0'))
val = val div 8
proc `%`(formatstr: string, a: string): string =
return formatstr % [a]
proc findNormalized(x: string, inArray: openarray[string]): int =
var i = 0
while i < high(inArray):
if cmpIgnoreStyle(x, inArray[i]) == 0: return i
inc(i, 2) # incrementing by 1 would probably result in a
# security hole...
return -1
proc addf(s: var string, formatstr: string, a: openarray[string]) =
const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
var i = 0
var num = 0
while i < len(formatstr):
if formatstr[i] == '$':
case formatstr[i+1] # again we use the fact that strings
# are zero-terminated here
of '#':
add s, a[num]
inc i, 2
inc num
of '$':
add s, '$'
inc(i, 2)
of '1'..'9':
var j = 0
inc(i) # skip $
while formatstr[i] in Digits:
j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i)
num = j
add s, a[j - 1]
of '{':
var j = i+1
while formatstr[j] notin {'\0', '}'}: inc(j)
var x = findNormalized(copy(formatstr, i+2, j-1), a)
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j+1
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var j = i+1
while formatstr[j] in PatternChars: inc(j)
var x = findNormalized(copy(formatstr, i+1, j-1), a)
if x >= 0 and x < high(a): add s, a[x+1]
else: raise newException(EInvalidValue, "invalid format string")
i = j
else: raise newException(EInvalidValue, "invalid format string")
else:
add s, formatstr[i]
inc(i)
proc `%`(formatstr: string, a: openarray[string]): string =
result = ""
addf(result, formatstr, a)
proc cmpIgnoreCase(a, b: string): int =
var i = 0
while i < a.len and i < b.len:
result = ord(toLower(a[i])) - ord(toLower(b[i]))
if result != 0: return
inc(i)
result = a.len - b.len
{.push checks: off, line_trace: off .} # this is a hot-spot in the compiler!
# thus we compile without checks here
proc cmpIgnoreStyle(a, b: string): int =
var i = 0
var j = 0
while True:
while a[i] == '_': inc(i)
while b[j] == '_': inc(j) # BUGFIX: typo
var aa = toLower(a[i])
var bb = toLower(b[j])
result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break
inc(i)
inc(j)
{.pop.}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
proc join*(a: openArray[string], sep: string): string = proc join*(a: openArray[string], sep: string): string {.
noSideEffect, rtl, extern: "nsuJoinSep".} =
## concatenates all strings in `a` separating them with `sep`. ## concatenates all strings in `a` separating them with `sep`.
if len(a) > 0: if len(a) > 0:
var L = sep.len * (a.len-1) var L = sep.len * (a.len-1)
@ -492,7 +530,8 @@ proc join*(a: openArray[string], sep: string): string =
else: else:
result = "" result = ""
proc join*(a: openArray[string]): string = proc join*(a: openArray[string]): string {.
noSideEffect, rtl, extern: "nsuJoin".} =
## concatenates all strings in `a`. ## concatenates all strings in `a`.
if len(a) > 0: if len(a) > 0:
var L = 0 var L = 0
@ -503,51 +542,6 @@ proc join*(a: openArray[string]): string =
else: else:
result = "" result = ""
proc strip(s: string, leading = true, trailing = true): string =
const
chars: set[Char] = Whitespace
var
first = 0
last = len(s)-1
if leading:
while s[first] in chars: inc(first)
if trailing:
while last >= 0 and s[last] in chars: dec(last)
result = copy(s, first, last)
proc toLower(c: Char): Char =
if c in {'A'..'Z'}:
result = chr(ord(c) + (ord('a') - ord('A')))
else:
result = c
proc toLower(s: string): string =
result = newString(len(s))
for i in 0..len(s) - 1:
result[i] = toLower(s[i])
proc toUpper(c: Char): Char =
if c in {'a'..'z'}:
result = Chr(Ord(c) - (Ord('a') - Ord('A')))
else:
result = c
proc toUpper(s: string): string =
result = newString(len(s))
for i in 0..len(s) - 1:
result[i] = toUpper(s[i])
proc capitalize(s: string): string =
result = toUpper(s[0]) & copy(s, 1)
proc normalize(s: string): string =
result = ""
for i in 0..len(s) - 1:
if s[i] in {'A'..'Z'}:
add result, Chr(Ord(s[i]) + (Ord('a') - Ord('A')))
elif s[i] != '_':
add result, s[i]
type type
TSkipTable = array[Char, int] TSkipTable = array[Char, int]
@ -571,31 +565,52 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
inc(j, a[s[j+m]]) inc(j, a[s[j+m]])
return -1 return -1
proc find(s, sub: string, start: int = 0): int = proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
rtl, extern: "nsuFindStr".} =
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
var a: TSkipTable var a: TSkipTable
preprocessSub(sub, a) preprocessSub(sub, a)
result = findAux(s, sub, start, a) result = findAux(s, sub, start, a)
proc find(s: string, sub: char, start: int = 0): int = proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect,
rtl, extern: "nsuFindChar".} =
## Searches for `sub` in `s` starting at position `start`. Searching is
## case-sensitive. If `sub` is not in `s`, -1 is returned.
for i in start..len(s)-1: for i in start..len(s)-1:
if sub == s[i]: return i if sub == s[i]: return i
return -1 return -1
proc find(s: string, chars: set[char], start: int = 0): int = proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect,
rtl, extern: "nsuFindCharSet".} =
## Searches for `chars` in `s` starting at position `start`. If `s` contains
## none of the characters in `chars`, -1 is returned.
for i in start..s.len-1: for i in start..s.len-1:
if s[i] in chars: return i if s[i] in chars: return i
return -1 return -1
proc contains(s: string, chars: set[char]): bool = proc quoteIfContainsWhite*(s: string): string =
return find(s, chars) >= 0 ## returns ``'"' & s & '"'`` if `s` contains a space and does not
## start with a quote, else returns `s`
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
result = '"' & s & '"'
else:
result = s
proc contains(s: string, c: char): bool = proc contains*(s: string, c: char): bool {.noSideEffect.} =
## Same as ``find(s, c) >= 0``.
return find(s, c) >= 0 return find(s, c) >= 0
proc contains(s, sub: string): bool = proc contains*(s, sub: string): bool {.noSideEffect.} =
## Same as ``find(s, sub) >= 0``.
return find(s, sub) >= 0 return find(s, sub) >= 0
proc replace*(s, sub, by: string): string = proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
## Same as ``find(s, chars) >= 0``.
return find(s, chars) >= 0
proc replace*(s, sub, by: string): string {.noSideEffect,
rtl, extern: "nsuReplaceStr".} =
## Replaces `sub` in `s` by the string `by`. ## Replaces `sub` in `s` by the string `by`.
var a: TSkipTable var a: TSkipTable
result = "" result = ""
@ -610,7 +625,8 @@ proc replace*(s, sub, by: string): string =
# copy the rest: # copy the rest:
add result, copy(s, i) add result, copy(s, i)
proc replace*(s: string, sub, by: char): string = proc replace*(s: string, sub, by: char): string {.noSideEffect,
rtl, extern: "nsuReplaceChar".} =
## optimized version for characters. ## optimized version for characters.
result = newString(s.len) result = newString(s.len)
var i = 0 var i = 0
@ -619,7 +635,8 @@ proc replace*(s: string, sub, by: char): string =
else: result[i] = s[i] else: result[i] = s[i]
inc(i) inc(i)
proc delete*(s: var string, first, last: int) = proc delete*(s: var string, first, last: int) {.noSideEffect,
rtl, extern: "nsuDelete".} =
## Deletes in `s` the characters at position `first`..`last`. This modifies ## Deletes in `s` the characters at position `first`..`last`. This modifies
## `s` itself, it does not return a copy. ## `s` itself, it does not return a copy.
var i = first var i = first
@ -631,27 +648,12 @@ proc delete*(s: var string, first, last: int) =
inc(j) inc(j)
setlen(s, newLen) setlen(s, newLen)
# parsing numbers: proc ParseOctInt*(s: string): int {.noSideEffect,
rtl, extern: "nsuParseOctInt".} =
proc toHex(x: BiggestInt, len: int): string = ## Parses an octal integer value contained in `s`. If `s` is not
const ## a valid integer, `EInvalidValue` is raised. `s` can have one of the
HexChars = "0123456789ABCDEF" ## following optional prefixes: ``0o``, ``0O``.
var ## Underscores within `s` are ignored.
shift: BiggestInt
result = newString(len)
for j in countdown(len-1, 0):
result[j] = HexChars[toU32(x shr shift) and 0xF'i32]
shift = shift + 4
proc parseInt(s: string): int =
var L = parseutils.parseInt(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseBiggestInt(s: string): biggestInt =
var L = parseutils.parseBiggestInt(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseOctInt*(s: string): int =
var i = 0 var i = 0
if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
while true: while true:
@ -663,30 +665,8 @@ proc ParseOctInt*(s: string): int =
of '\0': break of '\0': break
else: raise newException(EInvalidValue, "invalid integer: " & s) else: raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseHexInt(s: string): int = proc toOct*(x: BiggestInt, len: int): string {.noSideEffect,
var i = 0 rtl, extern: "nsuToOct".} =
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
elif s[i] == '#': inc(i)
while true:
case s[i]
of '_': inc(i)
of '0'..'9':
result = result shl 4 or (ord(s[i]) - ord('0'))
inc(i)
of 'a'..'f':
result = result shl 4 or (ord(s[i]) - ord('a') + 10)
inc(i)
of 'A'..'F':
result = result shl 4 or (ord(s[i]) - ord('A') + 10)
inc(i)
of '\0': break
else: raise newException(EInvalidValue, "invalid integer: " & s)
proc ParseFloat(s: string): float =
var L = parseutils.parseFloat(s, result, 0)
if L != s.len: raise newException(EInvalidValue, "invalid float: " & s)
proc toOct*(x: BiggestInt, len: int): string =
## converts `x` into its octal representation. The resulting string is ## converts `x` into its octal representation. The resulting string is
## always `len` characters long. No leading ``0o`` prefix is generated. ## always `len` characters long. No leading ``0o`` prefix is generated.
var var
@ -699,7 +679,8 @@ proc toOct*(x: BiggestInt, len: int): string =
shift = shift + 3 shift = shift + 3
mask = mask shl 3 mask = mask shl 3
proc toBin*(x: BiggestInt, len: int): string = proc toBin*(x: BiggestInt, len: int): string {.noSideEffect,
rtl, extern: "nsuToBin".} =
## converts `x` into its binary representation. The resulting string is ## converts `x` into its binary representation. The resulting string is
## always `len` characters long. No leading ``0b`` prefix is generated. ## always `len` characters long. No leading ``0b`` prefix is generated.
var var
@ -712,7 +693,8 @@ proc toBin*(x: BiggestInt, len: int): string =
shift = shift + 1 shift = shift + 1
mask = mask shl 1 mask = mask shl 1
proc insertSep*(s: string, sep = '_', digits = 3): string = proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
rtl, extern: "nsuInsertSep".} =
## inserts the separator `sep` after `digits` digits from right to left. ## inserts the separator `sep` after `digits` digits from right to left.
## Even though the algorithm works with any string `s`, it is only useful ## Even though the algorithm works with any string `s`, it is only useful
## if `s` contains a number. ## if `s` contains a number.
@ -730,7 +712,8 @@ proc insertSep*(s: string, sep = '_', digits = 3): string =
inc(j) inc(j)
dec(L) dec(L)
proc escape*(s: string, prefix = "\"", suffix = "\""): string = proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
rtl, extern: "nsuEscape".} =
## Escapes a string `s`. This does these operations (at the same time): ## Escapes a string `s`. This does these operations (at the same time):
## * replaces any ``\`` by ``\\`` ## * replaces any ``\`` by ``\\``
## * replaces any ``'`` by ``\'`` ## * replaces any ``'`` by ``\'``
@ -752,7 +735,8 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string =
else: add(result, c) else: add(result, c)
add(result, suffix) add(result, suffix)
proc validEmailAddress*(s: string): bool = proc validEmailAddress*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidEmailAddress".} =
## returns true if `s` seems to be a valid e-mail address. ## returns true if `s` seems to be a valid e-mail address.
## The checking also uses a domain list. ## The checking also uses a domain list.
## Note: This will be moved into another module soon. ## Note: This will be moved into another module soon.
@ -779,7 +763,8 @@ proc validEmailAddress*(s: string): bool =
"aero", "jobs", "museum": return true "aero", "jobs", "museum": return true
return false return false
proc validIdentifier*(s: string): bool = proc validIdentifier*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidIdentifier".} =
## returns true if `s` is a valid identifier. A valid identifier starts ## returns true if `s` is a valid identifier. A valid identifier starts
## with a character of the set `IdentStartChars` and is followed by any ## with a character of the set `IdentStartChars` and is followed by any
## number of characters of the set `IdentChars`. ## number of characters of the set `IdentChars`.
@ -788,7 +773,8 @@ proc validIdentifier*(s: string): bool =
if s[i] notin IdentChars: return false if s[i] notin IdentChars: return false
return true return true
proc editDistance*(a, b: string): int = proc editDistance*(a, b: string): int {.noSideEffect,
rtl, extern: "nsuEditDistance".} =
## returns the edit distance between `a` and `b`. This uses the Levenshtein ## returns the edit distance between `a` and `b`. This uses the Levenshtein
## distance algorithm with only a linear memory overhead. This implementation ## distance algorithm with only a linear memory overhead. This implementation
## is highly optimized! ## is highly optimized!

View file

@ -17,6 +17,8 @@
import import
strutils strutils
include "system/inclrtl"
type type
TMonth* = enum ## represents a month TMonth* = enum ## represents a month
mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec mJan, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
@ -125,20 +127,17 @@ proc `$` *(timeInfo: TTimeInfo): string
proc `$` *(time: TTime): string proc `$` *(time: TTime): string
## converts a calendar time to a string representation. ## converts a calendar time to a string representation.
proc getDateStr*(): string proc `-` *(a, b: TTime): int64{.
## gets the current date as a string of the format ``YYYY-MM-DD``. rtl, extern: "ntDiffTime".}
proc getClockStr*(): string
## gets the current clock time as a string of the format ``HH:MM:SS``.
proc `-` *(a, b: TTime): int64
## computes the difference of two calendar times. Result is in seconds. ## computes the difference of two calendar times. Result is in seconds.
proc `<` * (a, b: TTime): bool = proc `<` * (a, b: TTime): bool {.
rtl, extern: "ntLtTime".} =
## returns true iff ``a < b``, that is iff a happened before b. ## returns true iff ``a < b``, that is iff a happened before b.
result = a - b < 0 result = a - b < 0
proc `<=` * (a, b: TTime): bool = proc `<=` * (a, b: TTime): bool {.
rtl, extern: "ntLeTime".}=
## returns true iff ``a <= b``. ## returns true iff ``a <= b``.
result = a - b <= 0 result = a - b <= 0
@ -147,12 +146,12 @@ proc getStartMilsecs*(): int {.deprecated.}
## version 0.8.10.** Use ``realTime`` or ``cpuTime`` instead. ## version 0.8.10.** Use ``realTime`` or ``cpuTime`` instead.
when not defined(ECMAScript): when not defined(ECMAScript):
proc epochTime*(): float proc epochTime*(): float {.rtl, extern: "nt$1".}
## gets time after the UNIX epoch (1970) in seconds. It is a float ## gets time after the UNIX epoch (1970) in seconds. It is a float
## because sub-second resolution is likely to be supported (depending ## because sub-second resolution is likely to be supported (depending
## on the hardware/OS). ## on the hardware/OS).
proc cpuTime*(): float proc cpuTime*(): float {.rtl, extern: "nt$1".}
## gets time spent that the CPU spent to run the current process in ## gets time spent that the CPU spent to run the current process in
## seconds. This may be more useful for benchmarking than ``epochTime``. ## seconds. This may be more useful for benchmarking than ``epochTime``.
## However, it may measure the real time instead (depending on the OS). ## However, it may measure the real time instead (depending on the OS).
@ -227,6 +226,7 @@ when not defined(ECMAScript):
result.yearday = t.yearday result.yearday = t.yearday
result.isdst = -1 result.isdst = -1
when not defined(useNimRtl):
proc `-` (a, b: TTime): int64 = proc `-` (a, b: TTime): int64 =
return toBiggestInt(difftime(a, b)) return toBiggestInt(difftime(a, b))
@ -290,6 +290,7 @@ when not defined(ECMAScript):
## converts a Windows time to a UNIX `TTime` (``time_t``) ## converts a Windows time to a UNIX `TTime` (``time_t``)
result = TTime((t - epochDiff) div rateDiff) result = TTime((t - epochDiff) div rateDiff)
when not defined(useNimRtl):
proc epochTime(): float = proc epochTime(): float =
when defined(posix): when defined(posix):
var a: Ttimeval var a: Ttimeval
@ -358,12 +359,15 @@ else:
## get the miliseconds from the start of the program ## get the miliseconds from the start of the program
return int(getTime() - startMilsecs) return int(getTime() - startMilsecs)
proc getDateStr(): string =
proc getDateStr*(): string {.rtl, extern: "nt$1".} =
## gets the current date as a string of the format ``YYYY-MM-DD``.
var ti = getLocalTime(getTime()) var ti = getLocalTime(getTime())
result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) & result = $ti.year & '-' & intToStr(ord(ti.month)+1, 2) &
'-' & intToStr(ti.monthDay, 2) '-' & intToStr(ti.monthDay, 2)
proc getClockStr(): string = proc getClockStr*(): string {.rtl, extern: "nt$1".} =
## gets the current clock time as a string of the format ``HH:MM:SS``.
var ti = getLocalTime(getTime()) var ti = getLocalTime(getTime())
result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) & result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
':' & intToStr(ti.second, 2) ':' & intToStr(ti.second, 2)

View file

@ -11,6 +11,8 @@
{.deadCodeElim: on.} {.deadCodeElim: on.}
include "system/inclrtl"
type type
irune = int # underlying type of TRune irune = int # underlying type of TRune
TRune* = distinct irune ## type that can hold any Unicode character TRune* = distinct irune ## type that can hold any Unicode character
@ -22,7 +24,7 @@ proc `==`*(a, b: TRune): bool {.borrow.}
template ones(n: expr): expr = ((1 shl n)-1) template ones(n: expr): expr = ((1 shl n)-1)
proc runeLen*(s: string): int = proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
## returns the number of Unicode characters of the string `s`. ## returns the number of Unicode characters of the string `s`.
var i = 0 var i = 0
while i < len(s): while i < len(s):
@ -75,7 +77,7 @@ proc runeAt*(s: string, i: int): TRune =
## returns the unicode character in `s` at byte index `i` ## returns the unicode character in `s` at byte index `i`
fastRuneAt(s, i, result, false) fastRuneAt(s, i, result, false)
proc toUTF8*(c: TRune): string = proc toUTF8*(c: TRune): string {.rtl, extern: "nuc$1".} =
## converts a rune into its UTF8 representation ## converts a rune into its UTF8 representation
var i = irune(c) var i = irune(c)
if i <=% 127: if i <=% 127:
@ -1073,7 +1075,7 @@ proc binarySearch(c: irune, tab: openArray[iRune], len, stride: int): int =
return t return t
return -1 return -1
proc toLower*(c: TRune): TRune = proc toLower*(c: TRune): TRune {.rtl, extern: "nuc$1".} =
## Converts `c` into lower case. This works for any Unicode character. ## Converts `c` into lower case. This works for any Unicode character.
## If possible, prefer `toLower` over `toUpper`. ## If possible, prefer `toLower` over `toUpper`.
var c = irune(c) var c = irune(c)
@ -1085,7 +1087,7 @@ proc toLower*(c: TRune): TRune =
return TRune(c + toLowerSinglets[p+1] - 500) return TRune(c + toLowerSinglets[p+1] - 500)
return TRune(c) return TRune(c)
proc toUpper*(c: TRune): TRune = proc toUpper*(c: TRune): TRune {.rtl, extern: "nuc$1".} =
## Converts `c` into upper case. This works for any Unicode character. ## Converts `c` into upper case. This works for any Unicode character.
## If possible, prefer `toLower` over `toUpper`. ## If possible, prefer `toLower` over `toUpper`.
var c = irune(c) var c = irune(c)
@ -1097,14 +1099,14 @@ proc toUpper*(c: TRune): TRune =
return TRune(c + toUpperSinglets[p+1] - 500) return TRune(c + toUpperSinglets[p+1] - 500)
return TRune(c) return TRune(c)
proc toTitle*(c: TRune): TRune = proc toTitle*(c: TRune): TRune {.rtl, extern: "nuc$1".} =
var c = irune(c) var c = irune(c)
var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2) var p = binarySearch(c, toTitleSinglets, len(toTitleSinglets) div 2, 2)
if p >= 0 and c == toTitleSinglets[p]: if p >= 0 and c == toTitleSinglets[p]:
return TRune(c + toTitleSinglets[p+1] - 500) return TRune(c + toTitleSinglets[p+1] - 500)
return TRune(c) return TRune(c)
proc isLower*(c: TRune): bool = proc isLower*(c: TRune): bool {.rtl, extern: "nuc$1".} =
## returns true iff `c` is a lower case Unicode character ## returns true iff `c` is a lower case Unicode character
## If possible, prefer `isLower` over `isUpper`. ## If possible, prefer `isLower` over `isUpper`.
var c = irune(c) var c = irune(c)
@ -1116,7 +1118,7 @@ proc isLower*(c: TRune): bool =
if p >= 0 and c == toUpperSinglets[p]: if p >= 0 and c == toUpperSinglets[p]:
return true return true
proc isUpper*(c: TRune): bool = proc isUpper*(c: TRune): bool {.rtl, extern: "nuc$1".} =
## returns true iff `c` is a upper case Unicode character ## returns true iff `c` is a upper case Unicode character
## If possible, prefer `isLower` over `isUpper`. ## If possible, prefer `isLower` over `isUpper`.
var c = irune(c) var c = irune(c)
@ -1128,7 +1130,7 @@ proc isUpper*(c: TRune): bool =
if p >= 0 and c == toLowerSinglets[p]: if p >= 0 and c == toLowerSinglets[p]:
return true return true
proc isAlpha*(c: TRune): bool = proc isAlpha*(c: TRune): bool {.rtl, extern: "nuc$1".} =
## returns true iff `c` is an *alpha* Unicode character (i.e. a letter) ## returns true iff `c` is an *alpha* Unicode character (i.e. a letter)
if isUpper(c) or isLower(c): if isUpper(c) or isLower(c):
return true return true
@ -1140,10 +1142,10 @@ proc isAlpha*(c: TRune): bool =
if p >= 0 and c == alphaSinglets[p]: if p >= 0 and c == alphaSinglets[p]:
return true return true
proc isTitle*(c: TRune): bool = proc isTitle*(c: TRune): bool {.rtl, extern: "nuc$1".} =
return isUpper(c) and isLower(c) return isUpper(c) and isLower(c)
proc isWhiteSpace*(c: TRune): bool = proc isWhiteSpace*(c: TRune): bool {.rtl, extern: "nuc$1".} =
## returns true iff `c` is a Unicode whitespace character ## returns true iff `c` is a Unicode whitespace character
var c = irune(c) var c = irune(c)
var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2) var p = binarySearch(c, spaceRanges, len(spaceRanges) div 2, 2)
@ -1159,7 +1161,7 @@ iterator runes*(s: string): TRune =
fastRuneAt(s, i, result, true) fastRuneAt(s, i, result, true)
yield result yield result
proc cmpRunesIgnoreCase*(a, b: string): int = proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1".} =
## compares two UTF8 strings and ignores the case. Returns: ## compares two UTF8 strings and ignores the case. Returns:
## ##
## | 0 iff a == b ## | 0 iff a == b
@ -1175,6 +1177,3 @@ proc cmpRunesIgnoreCase*(a, b: string): int =
result = irune(toLower(ar)) - irune(toLower(br)) result = irune(toLower(ar)) - irune(toLower(br))
if result != 0: return if result != 0: return
result = a.len - b.len result = a.len - b.len
#proc substringAt*(s, sub: string, start: int): int =
#

View file

@ -14,7 +14,7 @@
## explicitly. Because of this there cannot be a user-defined module named ## explicitly. Because of this there cannot be a user-defined module named
## ``system``. ## ``system``.
{.push hints: off.} {.push hints: on.}
type type
int* {.magic: Int.} ## default integer type; bitwidth depends on int* {.magic: Int.} ## default integer type; bitwidth depends on
@ -720,25 +720,6 @@ const
include "system/inclrtl" include "system/inclrtl"
include "system/cgprocs" include "system/cgprocs"
when not defined(ECMAScript):
{.push overflow_checks:off}
proc add* (x: var string, y: cstring) =
var i = 0
while y[i] != '\0':
add(x, y[i])
inc(i)
{.pop.}
else:
proc add* (x: var string, y: cstring) {.pure.} =
asm """
var len = `x`[0].length-1;
for (var i = 0; i < `y`.length; ++i) {
`x`[0][len] = `y`.charCodeAt(i);
++len;
}
`x`[0][len] = 0
"""
proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.}
proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
## Generic proc for adding a data item `y` to a container `x`. ## Generic proc for adding a data item `y` to a container `x`.
@ -850,10 +831,6 @@ proc toBiggestInt*(f: biggestfloat): biggestint {.
## rounds `f` if it does not contain an integer value. If the conversion ## rounds `f` if it does not contain an integer value. If the conversion
## fails (because `f` is infinite for example), `EInvalidValue` is raised. ## fails (because `f` is infinite for example), `EInvalidValue` is raised.
proc `/`*(x, y: int): float {.inline, noSideEffect.} =
## integer division that results in a float.
result = toFloat(x) / toFloat(y)
proc addQuitProc*(QuitProc: proc {.noconv.}) {.importc: "atexit", nodecl.} proc addQuitProc*(QuitProc: proc {.noconv.}) {.importc: "atexit", nodecl.}
## adds/registers a quit procedure. Each call to ``addQuitProc`` ## adds/registers a quit procedure. Each call to ``addQuitProc``
## registers another quit procedure. Up to 30 procedures can be ## registers another quit procedure. Up to 30 procedures can be
@ -1004,12 +981,6 @@ const
## and expect a reasonable result - use the `classify` procedure ## and expect a reasonable result - use the `classify` procedure
## in the module ``math`` for checking for NaN. ## in the module ``math`` for checking for NaN.
var
dbgLineHook*: proc = nil
## set this variable to provide a procedure that should be called before
## each executed instruction. This should only be used by debuggers!
## Only code compiled with the ``debugger:on`` switch calls this hook.
# GC interface: # GC interface:
proc getOccupiedMem*(): int {.rtl.} proc getOccupiedMem*(): int {.rtl.}
@ -1265,6 +1236,35 @@ template accumulateResult*(iter: expr) =
# however, stack-traces are available for most parts # however, stack-traces are available for most parts
# of the code # of the code
var
dbgLineHook*: proc = nil
## set this variable to provide a procedure that should be called before
## each executed instruction. This should only be used by debuggers!
## Only code compiled with the ``debugger:on`` switch calls this hook.
when not defined(ECMAScript):
{.push overflow_checks:off}
proc add* (x: var string, y: cstring) =
var i = 0
while y[i] != '\0':
add(x, y[i])
inc(i)
{.pop.}
else:
proc add* (x: var string, y: cstring) {.pure.} =
asm """
var len = `x`[0].length-1;
for (var i = 0; i < `y`.length; ++i) {
`x`[0][len] = `y`.charCodeAt(i);
++len;
}
`x`[0][len] = 0
"""
proc `/`*(x, y: int): float {.inline, noSideEffect.} =
## integer division that results in a float.
result = toFloat(x) / toFloat(y)
proc echo*[Ty](x: openarray[Ty]) {.magic: "Echo".} proc echo*[Ty](x: openarray[Ty]) {.magic: "Echo".}
## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is ## equivalent to ``writeln(stdout, x); flush(stdout)``. BUT: This is
## available for the ECMAScript target too! ## available for the ECMAScript target too!

View file

@ -531,6 +531,7 @@ proc isAllocatedPtr(a: TAllocator, p: pointer): bool =
# ---------------------- interface to programs ------------------------------- # ---------------------- interface to programs -------------------------------
when not defined(useNimRtl):
proc alloc(size: int): pointer = proc alloc(size: int): pointer =
result = rawAlloc(allocator, size+sizeof(TFreeCell)) result = rawAlloc(allocator, size+sizeof(TFreeCell))
cast[ptr TFreeCell](result).zeroField = 1 # mark it as used cast[ptr TFreeCell](result).zeroField = 1 # mark it as used

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -61,11 +61,14 @@ when defined(posix):
proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {. proc dlsym(lib: TLibHandle, name: cstring): TProcAddr {.
importc, header: "<dlfcn.h>".} importc, header: "<dlfcn.h>".}
proc dlerror(): cstring {.importc, header: "<dlfcn.h>".}
proc nimUnloadLibrary(lib: TLibHandle) = proc nimUnloadLibrary(lib: TLibHandle) =
dlclose(lib) dlclose(lib)
proc nimLoadLibrary(path: string): TLibHandle = proc nimLoadLibrary(path: string): TLibHandle =
result = dlopen(path, RTLD_NOW) result = dlopen(path, RTLD_NOW)
#c_fprintf(c_stdout, "%s\n", dlerror())
proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr = proc nimGetProcAddr(lib: TLibHandle, name: cstring): TProcAddr =
result = dlsym(lib, name) result = dlsym(lib, name)

View file

@ -1,16 +1,14 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2010 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# Exception handling code. This is difficult because it has # Exception handling code. This is difficult because it has
# to work if there is no more memory. Do not use ``sprintf``, etc. as they are # to work if there is no more memory (but it doesn't yet!).
# unsafe!
when not defined(windows) or not defined(guiapp): when not defined(windows) or not defined(guiapp):
proc writeToStdErr(msg: CString) = write(stdout, msg) proc writeToStdErr(msg: CString) = write(stdout, msg)

View file

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

View file

@ -24,7 +24,6 @@ when defined(createNimRtl):
{.error: "nimrtl must be built as a library!".} {.error: "nimrtl must be built as a library!".}
when defined(createNimRtl): when defined(createNimRtl):
# NOTE: compilerproc cannot make use of name mangling!
{.pragma: rtl, exportc: "nimrtl_$1", dynlib.} {.pragma: rtl, exportc: "nimrtl_$1", dynlib.}
{.pragma: inl.} {.pragma: inl.}
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.} {.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}

View file

@ -187,28 +187,6 @@ elif defined(nogc):
dest^ = src dest^ = src
include "system/cellsets" include "system/cellsets"
elif defined(useNimRtl):
proc initGC() = nil
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
proc growObj(old: pointer, newsize: int): pointer {.rtl.}
proc nimGCref(p: pointer) {.compilerRtl.}
proc nimGCunref(p: pointer) {.compilerRtl.}
# The write barrier is performance critical!
# XXX We should ensure that they are inlined here.
# Later implementations will do this.
proc unsureAsgnRef(dest: ppointer, src: pointer) {.
compilerRtl.}
proc asgnRef(dest: ppointer, src: pointer) {.
compilerRtl.}
proc asgnRefNoCycle(dest: ppointer, src: pointer) {.
compilerRtl.}
include "system/cellsets"
else: else:
include "system/alloc" include "system/alloc"

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) result.sons[i] = evalTemplateAux(c, templ.sons[i], actual, sym)
var evalTemplateCounter: int = 0 var evalTemplateCounter: int = 0
# to prevend endless recursion in templates instantation
proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode = proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode =
# to prevend endless recursion in templates
# instantation
var var
f, a: int f, a: int
arg: PNode arg: PNode
f = sonsLen(s.typ) # if the template has zero arguments, it can be called without ``()`` f = sonsLen(s.typ) # if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar # `n` is then a nkSym or something similar
case n.kind case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: a = sonsLen( of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
n) a = sonsLen(n)
else: a = 0 else: a = 0
if a > f: liMessage(n.info, errWrongNumberOfArguments) if a > f: liMessage(n.info, errWrongNumberOfArguments)
result = copyNode(n) result = copyNode(n)
@ -85,7 +84,8 @@ proc evalTemplate(c: PContext, n: PNode, sym: PSym): PNode =
var args: PNode var args: PNode
inc(evalTemplateCounter) inc(evalTemplateCounter)
if evalTemplateCounter > 100: if evalTemplateCounter > 100:
liMessage(n.info, errTemplateInstantiationTooNested) # replace each param by the corresponding node: liMessage(n.info, errTemplateInstantiationTooNested)
# replace each param by the corresponding node:
args = evalTemplateArgs(c, n, sym) args = evalTemplateArgs(c, n, sym)
result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym) result = evalTemplateAux(c, sym.ast.sons[codePos], args, sym)
dec(evalTemplateCounter) dec(evalTemplateCounter)

View file

@ -1,6 +1,7 @@
For version 0.8.10 For version 0.8.10
================== ==================
- exception propagation across DLLs
- fix exception handling - fix exception handling
- fix implicit generic routines - fix implicit generic routines
- fix the streams implementation so that they use methods - fix the streams implementation so that they use methods
@ -61,6 +62,7 @@ Low priority
- normalize for the DOM - normalize for the DOM
- tlastmod returns wrong results on BSD (Linux, MacOS X: works) - tlastmod returns wrong results on BSD (Linux, MacOS X: works)
- nested tuple unpacking - nested tuple unpacking
- fast assignment optimization for TPeg
Library Library
@ -129,4 +131,7 @@ RST
--- ---
- footnotes; prefix :i: whitespace before :i:, _reference, `reference`__ - footnotes; prefix :i: whitespace before :i:, _reference, `reference`__
__ anonymous: www.nimrod.org __ anonymous: www.nimrod.org
- rewrite the parser to use the new better look ahead technique that c2nim
uses