bugfix: proper cache for generic instantiations

This commit is contained in:
Araq 2011-07-21 00:57:39 +02:00
commit 569c1ce5ec
24 changed files with 459 additions and 643 deletions

View file

@ -15,7 +15,7 @@ import
options, intsets, options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
rodutils rodutils, renderer
when options.hasTinyCBackend: when options.hasTinyCBackend:
import tccgen import tccgen

View file

@ -134,15 +134,13 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
include semtypes, semexprs, semgnrc, semstmts include semtypes, semexprs, semgnrc, semstmts
proc addCodeForGenerics(c: PContext, n: PNode) = proc addCodeForGenerics(c: PContext, n: PNode) =
for i in countup(c.lastGenericIdx, sonsLen(c.generics) - 1): for i in countup(lastGenericIdx, Len(generics) - 1):
var it = c.generics.sons[i].sons[1] var prc = generics[i].instSym
if it.kind != nkSym: InternalError("addCodeForGenerics")
var prc = it.sym
if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone: if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone:
if prc.ast == nil or prc.ast.sons[codePos] == nil: if prc.ast == nil or prc.ast.sons[codePos] == nil:
InternalError(prc.info, "no code for " & prc.name.s) InternalError(prc.info, "no code for " & prc.name.s)
addSon(n, prc.ast) addSon(n, prc.ast)
c.lastGenericIdx = sonsLen(c.generics) lastGenericIdx = Len(generics)
proc semExprNoFlags(c: PContext, n: PNode): PNode = proc semExprNoFlags(c: PContext, n: PNode): PNode =
result = semExpr(c, n, {}) result = semExpr(c, n, {})
@ -174,7 +172,7 @@ proc myOpenCached(module: PSym, filename: string,
proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semStmt(c, n) result = semStmt(c, n)
# BUGFIX: process newly generated generics here, not at the end! # BUGFIX: process newly generated generics here, not at the end!
if sonsLen(c.generics) > 0: if lastGenericIdx < Len(generics):
var a = newNodeI(nkStmtList, n.info) var a = newNodeI(nkStmtList, n.info)
addCodeForGenerics(c, a) addCodeForGenerics(c, a)
if sonsLen(a) > 0: if sonsLen(a) > 0:

View file

@ -119,12 +119,4 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
# candidateCount != 1: return explicitGenericInstError(n) # candidateCount != 1: return explicitGenericInstError(n)
else: else:
assert false assert false
when false:
var x: TCandidate
initCandidate(x, s, n)
var newInst = generateInstance(c, s, x.bindings, n.info)
markUsed(n, s)
result = newSymNode(newInst, n.info)

View file

@ -33,16 +33,17 @@ type
nestedBlockCounter*: int # whether we are in a block or not nestedBlockCounter*: int # whether we are in a block or not
next*: PProcCon # used for stacking procedure contexts next*: PProcCon # used for stacking procedure contexts
TInstantiatedSymbol* {.final.} = object
genericSym*, instSym*: PSym
concreteTypes*: seq[PType]
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context module*: PSym # the module sym belonging to the context
p*: PProcCon # procedure context p*: PProcCon # procedure context
InstCounter*: int # to prevent endless instantiations InstCounter*: int # to prevent endless instantiations
generics*: PNode # a list of the things to compile; list of
# nkExprEqExpr nodes which contain the
# generic symbol and the instantiated symbol
threadEntries*: TSymSeq # list of thread entries to check threadEntries*: TSymSeq # list of thread entries to check
lastGenericIdx*: int # used for the generics stack
tab*: TSymTab # each module has its own symbol table tab*: TSymTab # each module has its own symbol table
AmbiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot AmbiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!) # store this info in the syms themselves!)
@ -58,6 +59,8 @@ type
var gInstTypes*: TIdTable # map PType to PType var gInstTypes*: TIdTable # map PType to PType
var generics*: seq[TInstantiatedSymbol] = @[] # a list of the things to compile
var lastGenericIdx*: int # used for the generics stack
proc newContext*(module: PSym, nimfile: string): PContext proc newContext*(module: PSym, nimfile: string): PContext
@ -124,7 +127,6 @@ proc newContext(module: PSym, nimfile: string): PContext =
initLinkedList(result.libs) initLinkedList(result.libs)
append(result.optionStack, newOptionEntry()) append(result.optionStack, newOptionEntry())
result.module = module result.module = module
result.generics = newNode(nkStmtList)
result.threadEntries = @[] result.threadEntries = @[]
result.converters = @[] result.converters = @[]
result.filename = nimfile result.filename = nimfile

View file

@ -9,15 +9,17 @@
# This module implements the instantiation of generic procs. # This module implements the instantiation of generic procs.
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable) = proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
if (n.kind != nkGenericParams): entry: var TInstantiatedSymbol) =
if n.kind != nkGenericParams:
InternalError(n.info, "instantiateGenericParamList; no generic params") InternalError(n.info, "instantiateGenericParamList; no generic params")
for i in countup(0, sonsLen(n) - 1): newSeq(entry.concreteTypes, n.len)
for i in countup(0, n.len - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind != nkSym: if a.kind != nkSym:
InternalError(a.info, "instantiateGenericParamList; no symbol") InternalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym var q = a.sym
if not (q.typ.kind in {tyTypeDesc, tyGenericParam}): continue if q.typ.kind notin {tyTypeDesc, tyGenericParam}: continue
var s = newSym(skType, q.name, getCurrOwner()) var s = newSym(skType, q.name, getCurrOwner())
s.info = q.info s.info = q.info
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -29,24 +31,23 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable) =
InternalError(a.info, "instantiateGenericParamList: " & q.name.s) InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
s.typ = t s.typ = t
addDecl(c, s) addDecl(c, s)
entry.concreteTypes[i] = t
proc GenericCacheGet(c: PContext, genericSym, instSym: PSym): PSym = proc sameInstantiation(a, b: TInstantiatedSymbol): bool =
result = nil if a.genericSym.id == b.genericSym.id and
for i in countup(0, sonsLen(c.generics) - 1): a.concreteTypes.len == b.concreteTypes.len:
if c.generics.sons[i].kind != nkExprEqExpr: for i in 0 .. < a.concreteTypes.len:
InternalError(genericSym.info, "GenericCacheGet") if not sameType(a.concreteTypes[i], b.concreteTypes[i]): return
var a = c.generics.sons[i].sons[0].sym result = true
if genericSym.id == a.id:
var b = c.generics.sons[i].sons[1].sym
if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
#echo "found in cache: ", getProcHeader(instSym)
return b
proc GenericCacheAdd(c: PContext, genericSym, instSym: PSym) = proc GenericCacheGet(c: PContext, entry: var TInstantiatedSymbol): PSym =
var n = newNode(nkExprEqExpr) for i in countup(0, Len(generics) - 1):
addSon(n, newSymNode(genericSym)) if sameInstantiation(entry, generics[i]):
addSon(n, newSymNode(instSym)) result = generics[i].instSym
addSon(c.generics, n) # checking for the concrete parameter list is wrong and unnecessary!
#if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
#echo "found in cache: ", getProcHeader(result)
return
proc removeDefaultParamValues(n: PNode) = proc removeDefaultParamValues(n: PNode) =
# we remove default params, because they cannot be instantiated properly # we remove default params, because they cannot be instantiated properly
@ -81,11 +82,14 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.ast = n result.ast = n
pushOwner(result) pushOwner(result)
openScope(c.tab) openScope(c.tab)
if (n.sons[genericParamsPos].kind == nkEmpty): if n.sons[genericParamsPos].kind == nkEmpty:
InternalError(n.info, "generateInstance") InternalError(n.info, "generateInstance")
n.sons[namePos] = newSymNode(result) n.sons[namePos] = newSymNode(result)
pushInfoContext(info) pushInfoContext(info)
instantiateGenericParamList(c, n.sons[genericParamsPos], pt) var entry: TInstantiatedSymbol
entry.instSym = result
entry.genericSym = fn
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry)
n.sons[genericParamsPos] = ast.emptyNode n.sons[genericParamsPos] = ast.emptyNode
# semantic checking for the parameters: # semantic checking for the parameters:
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
@ -96,10 +100,10 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.typ = newTypeS(tyProc, c) result.typ = newTypeS(tyProc, c)
addSon(result.typ, nil) addSon(result.typ, nil)
result.typ.callConv = fn.typ.callConv result.typ.callConv = fn.typ.callConv
var oldPrc = GenericCacheGet(c, fn, result) var oldPrc = GenericCacheGet(c, entry)
if oldPrc == nil: if oldPrc == nil:
# add it here, so that recursive generic procs are possible: # add it here, so that recursive generic procs are possible:
GenericCacheAdd(c, fn, result) generics.add(entry)
addDecl(c, result) addDecl(c, result)
if n.sons[codePos].kind != nkEmpty: if n.sons[codePos].kind != nkEmpty:
pushProcCon(c, result) pushProcCon(c, result)

View file

@ -358,6 +358,8 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef, of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef,
nkConverterDef, nkMacroDef, nkTemplateDef: nkConverterDef, nkMacroDef, nkTemplateDef:
result = toVoid result = toVoid
of nkExprColonExpr:
result = analyse(c, n.sons[1])
else: InternalError(n.info, "analysis not implemented for: " & $n.kind) else: InternalError(n.info, "analysis not implemented for: " & $n.kind)
proc analyseThreadProc*(prc: PSym) = proc analyseThreadProc*(prc: PSym) =
@ -368,17 +370,6 @@ proc analyseThreadProc*(prc: PSym) =
c.mapping[formal.id] = toTheirs # thread receives foreign data! c.mapping[formal.id] = toTheirs # thread receives foreign data!
discard analyse(c, prc.ast.sons[codePos]) discard analyse(c, prc.ast.sons[codePos])
when false:
proc analyseThreadCreationCall(n: PNode) =
# thread proc is second param of ``createThread``:
if n[2].kind != nkSym or n[2].sym.kind != skProc:
Message(n.info, warnAnalysisLoophole, renderTree(n))
return
analyseProc(n[2].sym)
proc AnalyseThread*(threadCreation: PNode) =
analyseThreadCreationCall(threadCreation)
proc needsGlobalAnalysis*: bool = proc needsGlobalAnalysis*: bool =
result = gGlobalOptions * {optThreads, optThreadAnalysis} == result = gGlobalOptions * {optThreads, optThreadAnalysis} ==
{optThreads, optThreadAnalysis} {optThreads, optThreadAnalysis}

View file

@ -12,19 +12,6 @@
import import
ast, astalgo, lexer, msgs, strutils, wordrecg ast, astalgo, lexer, msgs, strutils, wordrecg
proc getMagic*(op: PNode): TMagic
proc isConstExpr*(n: PNode): bool
proc flattenTree*(root: PNode, op: TMagic): PNode
proc TreeToSym*(t: PNode): PSym
proc SwapOperands*(op: PNode)
proc getOpSym*(op: PNode): PSym
proc getProcSym*(call: PNode): PSym
proc ExprStructuralEquivalent*(a, b: PNode): bool
proc sameTree*(a, b: PNode): bool
proc cyclicTree*(n: PNode): bool
# implementation
proc hasSon(father, son: PNode): bool = proc hasSon(father, son: PNode): bool =
for i in countup(0, sonsLen(father) - 1): for i in countup(0, sonsLen(father) - 1):
if father.sons[i] == son: if father.sons[i] == son:
@ -45,11 +32,11 @@ proc cyclicTreeAux(n, s: PNode): bool =
result = false result = false
delSon(s, m) delSon(s, m)
proc cyclicTree(n: PNode): bool = proc cyclicTree*(n: PNode): bool =
var s = newNodeI(nkEmpty, n.info) var s = newNodeI(nkEmpty, n.info)
result = cyclicTreeAux(n, s) result = cyclicTreeAux(n, s)
proc ExprStructuralEquivalent(a, b: PNode): bool = proc ExprStructuralEquivalent*(a, b: PNode): bool =
result = false result = false
if a == b: if a == b:
result = true result = true
@ -69,7 +56,7 @@ proc ExprStructuralEquivalent(a, b: PNode): bool =
if not ExprStructuralEquivalent(a.sons[i], b.sons[i]): return if not ExprStructuralEquivalent(a.sons[i], b.sons[i]): return
result = true result = true
proc sameTree(a, b: PNode): bool = proc sameTree*(a, b: PNode): bool =
result = false result = false
if a == b: if a == b:
result = true result = true
@ -93,10 +80,10 @@ proc sameTree(a, b: PNode): bool =
if not sameTree(a.sons[i], b.sons[i]): return if not sameTree(a.sons[i], b.sons[i]): return
result = true result = true
proc getProcSym(call: PNode): PSym = proc getProcSym*(call: PNode): PSym =
result = call.sons[0].sym result = call.sons[0].sym
proc getOpSym(op: PNode): PSym = proc getOpSym*(op: PNode): PSym =
if not (op.kind in {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}): if not (op.kind in {nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit}):
result = nil result = nil
else: else:
@ -104,7 +91,7 @@ proc getOpSym(op: PNode): PSym =
if op.sons[0].Kind == nkSym: result = op.sons[0].sym if op.sons[0].Kind == nkSym: result = op.sons[0].sym
else: result = nil else: result = nil
proc getMagic(op: PNode): TMagic = proc getMagic*(op: PNode): TMagic =
case op.kind case op.kind
of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix, of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit, nkPrefix, nkPostfix,
nkInfix: nkInfix:
@ -113,10 +100,10 @@ proc getMagic(op: PNode): TMagic =
else: result = mNone else: result = mNone
else: result = mNone else: result = mNone
proc TreeToSym(t: PNode): PSym = proc TreeToSym*(t: PNode): PSym =
result = t.sym result = t.sym
proc isConstExpr(n: PNode): bool = proc isConstExpr*(n: PNode): bool =
result = (n.kind in result = (n.kind in
{nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags) nkFloatLit..nkFloat64Lit, nkNilLit}) or (nfAllConst in n.flags)
@ -128,14 +115,14 @@ proc flattenTreeAux(d, a: PNode, op: TMagic) =
else: else:
addSon(d, copyTree(a)) addSon(d, copyTree(a))
proc flattenTree(root: PNode, op: TMagic): PNode = proc flattenTree*(root: PNode, op: TMagic): PNode =
result = copyNode(root) result = copyNode(root)
if (getMagic(root) == op): if (getMagic(root) == op):
# BUGFIX: forget to copy prc # BUGFIX: forget to copy prc
addSon(result, copyNode(root.sons[0])) addSon(result, copyNode(root.sons[0]))
flattenTreeAux(result, root, op) flattenTreeAux(result, root, op)
proc SwapOperands(op: PNode) = proc SwapOperands*(op: PNode) =
var tmp = op.sons[1] var tmp = op.sons[1]
op.sons[1] = op.sons[2] op.sons[1] = op.sons[2]
op.sons[2] = tmp op.sons[2] = tmp

View file

@ -99,6 +99,9 @@ String handling
It finds the sequence of ASCII characters that is the closest approximation It finds the sequence of ASCII characters that is the closest approximation
to the Unicode string. to the Unicode string.
* `matchers <matchers.html>`_
This module contains various string matchers for email addresses, etc.
Generic Operating System Services Generic Operating System Services
--------------------------------- ---------------------------------

View file

@ -175,7 +175,7 @@ Emit pragma
The `emit`:idx: pragma can be used to directly affect the output of the The `emit`:idx: pragma can be used to directly affect the output of the
compiler's code generator. So it makes your code unportable to other code compiler's code generator. So it makes your code unportable to other code
generators/backends. Its usage is highly discouraged! However, it can be generators/backends. Its usage is highly discouraged! However, it can be
extremely useful for interfacing with C++ or Objective C code. extremely useful for interfacing with `C++`:idx: or `Objective C`:idx: code.
Example: Example:

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 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.
@ -21,10 +21,6 @@
## ##
## <h1><a href="http://force7.de/nimrod">Nimrod</a></h1> ## <h1><a href="http://force7.de/nimrod">Nimrod</a></h1>
## ##
## **Deprecated since version 0.8.8.** Use the macro ``<>`` in xmltree
## instead.
{.deprecated.}
import import
macros, strutils macros, strutils
@ -58,7 +54,6 @@ proc xmlCheckedTag*(e: PNimrodNode, tag: string,
# will be modified, so that each attribute is only given one value # will be modified, so that each attribute is only given one value
var req = split(reqAttr) var req = split(reqAttr)
var opt = split(optAttr) var opt = split(optAttr)
echo "##", optAttr, "##", opt.len
result = newNimNode(nnkBracket, e) result = newNimNode(nnkBracket, e)
result.add(newStrLitNode("<")) result.add(newStrLitNode("<"))
result.add(newStrLitNode(tag)) result.add(newStrLitNode(tag))

51
lib/pure/matchers.nim Normal file
View file

@ -0,0 +1,51 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module contains various string matchers for email addresses, etc.
{.deadCodeElim: on.}
{.push debugger:off .} # the user does not want to trace a part
# of the standard library!
include "system/inclrtl"
import strutils
proc validEmailAddress*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidEmailAddress".} =
## returns true if `s` seems to be a valid e-mail address.
## The checking also uses a domain list.
const
chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0
if s[i] notin chars or s[i] == '.': return false
while s[i] in chars:
if s[i] == '.' and s[i+1] == '.': return false
inc(i)
if s[i] != '@': return false
var j = len(s)-1
if s[j] notin letters: return false
while j >= i and s[j] in letters: dec(j)
inc(i) # skip '@'
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if s[i] != '\0': return false
var x = substr(s, j+1)
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
case toLower(x)
of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
"aero", "jobs", "museum": return true
return false
when isMainModule:
assert "wuseldusel@codehome.com".validEmailAddress
{.pop.}

View file

@ -351,23 +351,6 @@ proc `/` * (head, tail: string): string {.noSideEffect.} =
## The same as ``joinPath(head, tail)`` ## The same as ``joinPath(head, tail)``
return joinPath(head, tail) return joinPath(head, tail)
proc SplitPath*(path: string, head, tail: var string) {.noSideEffect,
deprecated.} =
## **Deprecated since version 0.8.2**: use the version that returns a tuple
## instead
var
sepPos = -1
for i in countdown(len(path)-1, 0):
if path[i] in {dirsep, altsep}:
sepPos = i
break
if sepPos >= 0:
head = substr(path, 0, sepPos-1)
tail = substr(path, sepPos+1)
else:
head = ""
tail = path # make a string copy here
proc SplitPath*(path: string): tuple[head, tail: string] {. proc SplitPath*(path: string): tuple[head, tail: string] {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Splits a directory into (head, tail), so that ## Splits a directory into (head, tail), so that
@ -466,13 +449,6 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {.
result.name = substr(path, sepPos+1, dotPos-1) result.name = substr(path, sepPos+1, dotPos-1)
result.ext = substr(path, dotPos) result.ext = substr(path, dotPos)
proc extractDir*(path: string): string {.noSideEffect, deprecated.} =
## Extracts the directory of a given path. This is almost the
## same as the `head` result of `splitPath`, except that
## ``extractDir("/usr/lib/") == "/usr/lib/"``.
## **Deprecated since version 0.8.2**: Use ``splitFile(path).dir`` instead.
result = splitFile(path).dir
proc extractFilename*(path: string): string {. proc extractFilename*(path: string): string {.
noSideEffect, rtl, extern: "nos$1".} = 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
@ -495,37 +471,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1".} =
if res == nil: OSError() if res == nil: OSError()
result = $res result = $res
c_free(res) c_free(res)
proc SplitFilename*(filename: string, name, extension: var string) {.
noSideEffect, deprecated.} =
## Splits a filename into (name, extension), so that
## ``name & extension == filename``.
##
## Example: After ``SplitFilename("usr/local/nimrodc.html", name, ext)``,
## `name` is "usr/local/nimrodc" and `ext` is ".html".
## If the file has no extension, extension is the empty string.
## **Deprecated since version 0.8.2**: Use ``splitFile(filename)`` instead.
var extPos = searchExtPos(filename)
if extPos >= 0:
name = substr(filename, 0, extPos-1)
extension = substr(filename, extPos)
else:
name = filename # make a string copy here
extension = ""
proc extractFileExt*(filename: string): string {.noSideEffect, deprecated.} =
## Extracts the file extension of a given `filename`. This is the
## same as the `extension` result of `splitFilename`.
## **Deprecated since version 0.8.2**: Use ``splitFile(filename).ext``
## instead.
result = splitFile(filename).ext
proc extractFileTrunk*(filename: string): string {.noSideEffect, deprecated.} =
## Extracts the file name of a given `filename`. This removes any
## directory information and the file extension.
## **Deprecated since version 0.8.2**: Use ``splitFile(path).name`` instead.
result = splitFile(filename).name
proc ChangeFileExt*(filename, ext: string): string {. proc ChangeFileExt*(filename, ext: string): string {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Changes the file extension to `ext`. ## Changes the file extension to `ext`.
@ -551,11 +497,6 @@ proc addFileExt*(filename, ext: string): string {.
if extPos < 0: result = filename & normExt(ext) if extPos < 0: result = filename & normExt(ext)
else: result = filename else: result = filename
proc AppendFileExt*(filename, ext: string): string {.
noSideEffect, deprecated.} =
## **Deprecated since version 0.8.2**: Use `addFileExt` instead.
result = addFileExt(filename, ext)
proc cmpPaths*(pathA, pathB: string): int {. proc cmpPaths*(pathA, pathB: string): int {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Compares two paths. ## Compares two paths.
@ -661,10 +602,6 @@ 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()
proc executeShellCommand*(command: string): int {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `execShellCmd` instead.
result = csystem(command)
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} = proc execShellCmd*(command: string): int {.rtl, extern: "nos$1".} =
## Executes a `shell command`:idx:. ## Executes a `shell command`:idx:.
## ##
@ -781,15 +718,6 @@ proc putEnv*(key, val: string) =
if SetEnvironmentVariableA(key, val) == 0'i32: if SetEnvironmentVariableA(key, val) == 0'i32:
OSError() OSError()
iterator iterOverEnvironment*(): tuple[key, value: string] {.deprecated.} =
## Iterate over all environments variables. In the first component of the
## tuple is the name of the current variable stored, in the second its value.
## **Deprecated since version 0.8.2**: Use `envPairs` instead.
getEnvVarsC()
for i in 0..high(environment):
var p = find(environment[i], '=')
yield (substr(environment[i], 0, p-1), substr(environment[i], p+1))
iterator envPairs*(): tuple[key, value: string] = iterator envPairs*(): tuple[key, value: string] =
## Iterate over all `environments variables`:idx:. In the first component ## Iterate over all `environments variables`:idx:. In the first component
## of the tuple is the name of the current variable stored, in the second ## of the tuple is the name of the current variable stored, in the second
@ -837,10 +765,6 @@ type
pcDir, ## path refers to a directory pcDir, ## path refers to a directory
pcLinkToDir ## path refers to a symbolic link to a directory pcLinkToDir ## path refers to a symbolic link to a directory
const
pcDirectory* {.deprecated.} = pcDir ## deprecated alias
pcLinkToDirectory* {.deprecated.} = pcLinkToDir ## deprecated alias
iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] = iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] =
## walks over the directory `dir` and yields for each directory or file in ## walks over the directory `dir` and yields for each directory or file in
## `dir`. The component type and full path for each item is returned. ## `dir`. The component type and full path for each item is returned.

View file

@ -46,22 +46,10 @@ proc execProcess*(command: string,
## A convenience procedure that executes ``command`` with ``startProcess`` ## A convenience procedure that executes ``command`` with ``startProcess``
## and returns its output as a string. ## and returns its output as a string.
proc executeProcess*(command: string,
options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): string {.
deprecated.} =
## **Deprecated since version 0.8.2**: Use `execProcess` instead.
result = execProcess(command, options)
proc execCmd*(command: string): int {.rtl, extern: "nosp$1".} 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.
proc executeCommand*(command: string): int {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `execCmd` instead.
result = execCmd(command)
proc startProcess*(command: string, proc startProcess*(command: string,
workingDir: string = "", workingDir: string = "",
args: openarray[string] = [], args: openarray[string] = [],

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 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.
@ -53,10 +53,6 @@ when defined(os.ParamCount):
result.key = "" result.key = ""
result.val = "" result.val = ""
proc init*(cmdline: string = ""): TOptParser {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `initOptParser` instead.
result = initOptParser(cmdline)
proc parseWord(s: string, i: int, w: var string, proc parseWord(s: string, i: int, w: var string,
delim: TCharSet = {'\x09', ' ', '\0'}): int = delim: TCharSet = {'\x09', ' ', '\0'}): int =
result = i result = i
@ -128,10 +124,6 @@ proc cmdLineRest*(p: TOptParser): string {.
## 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(substr(p.cmd, p.pos, len(p.cmd) - 1)) result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1))
proc getRestOfCommandLine*(p: TOptParser): string {.deprecated.} =
## **Deprecated since version 0.8.2**: Use `cmdLineRest` instead.
result = cmdLineRest(p)
when defined(initOptParser): when defined(initOptParser):
iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] = iterator getopt*(): tuple[kind: TCmdLineKind, key, val: string] =

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 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.

View file

@ -1,179 +0,0 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2009 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Regular expression support for Nimrod.
## Currently this module is implemented by providing a wrapper around the
## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## C library. This means that your application will depend on the PRCE
## library's licence when using this module, which should not be a problem
## though.
## PRCE's licence follows:
##
## .. include:: ../doc/regexprs.txt
##
# This is not just a convenient wrapper for the pcre library; the
# API will stay the same if the implementation should change.
{.deprecated.}
import
pcre, strutils
type
EInvalidRegEx* = object of EInvalidValue
## is raised if the pattern is no valid regular expression.
const
MaxSubpatterns* = 10
## defines the maximum number of subpatterns that can be captured.
## More subpatterns cannot be captured!
proc match*(s, pattern: string, matches: var openarray[string],
start: int = 0): bool
## returns ``true`` if ``s[start..]`` matches the ``pattern`` and
## the captured substrings in the array ``matches``. If it does not
## match, nothing is written into ``matches`` and ``false`` is
## returned.
proc match*(s, pattern: string, start: int = 0): bool
## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``.
proc matchLen*(s, pattern: string, matches: var openarray[string],
start: int = 0): int
## the same as ``match``, but it returns the length of the match,
## if there is no match, -1 is returned. Note that a match length
## of zero can happen.
proc find*(s, pattern: string, matches: var openarray[string],
start: int = 0): bool
## returns ``true`` if ``pattern`` occurs in ``s`` and the captured
## substrings in the array ``matches``. If it does not match, nothing
## is written into ``matches``.
proc find*(s, pattern: string, start: int = 0): bool
## returns ``true`` if ``pattern`` occurs in ``s``.
proc rawCompile(pattern: string, flags: cint): PPcre =
var
msg: CString
offset: cint
com = pcre.Compile(pattern, flags, addr(msg), addr(offset), nil)
if com == nil:
var e: ref EInvalidRegEx
new(e)
e.msg = $msg & "\n" & pattern & "\n" & repeatChar(offset) & "^\n"
raise e
return com
proc matchOrFind(s: string, pattern: PPcre, matches: var openarray[string],
start: cint): cint =
var
rawMatches: array [0..maxSubpatterns * 3 - 1, cint]
res = int(pcre.Exec(pattern, nil, s, len(s), start, 0,
cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3))
dealloc(pattern)
if res < 0: return res
for i in 0..res-1:
var
a = rawMatches[i * 2]
b = rawMatches[i * 2 + 1]
if a >= 0'i32: matches[i] = substr(s, a, int(b)-1)
else: matches[i] = ""
return res
proc matchOrFind(s: string, pattern: PPcre, start: cint): cint =
var
rawMatches: array [0..maxSubpatterns * 3 - 1, cint]
res = pcre.Exec(pattern, nil, s, len(s), start, 0,
cast[ptr cint](addr(rawMatches)), maxSubpatterns * 3)
dealloc(pattern)
return res
proc match(s, pattern: string, matches: var openarray[string],
start: int = 0): bool =
return matchOrFind(s, rawCompile(pattern, PCRE.ANCHORED),
matches, start) >= 0'i32
proc matchLen(s, pattern: string, matches: var openarray[string],
start: int = 0): int =
return matchOrFind(s, rawCompile(pattern, PCRE.ANCHORED), matches, start)
proc find(s, pattern: string, matches: var openarray[string],
start: int = 0): bool =
return matchOrFind(s, rawCompile(pattern, PCRE.MULTILINE),
matches, start) >= 0'i32
proc match(s, pattern: string, start: int = 0): bool =
return matchOrFind(s, rawCompile(pattern, PCRE.ANCHORED), start) >= 0'i32
proc find(s, pattern: string, start: int = 0): bool =
return matchOrFind(s, rawCompile(pattern, PCRE.MULTILINE), start) >= 0'i32
template `=~` *(s, pattern: expr): expr =
## This calls ``match`` with an implicit declared ``matches`` array that
## can be used in the scope of the ``=~`` call:
##
## .. code-block:: nimrod
##
## if line =~ r"\s*(\w+)\s*\=\s*(\w+)":
## # matches a key=value pair:
## echo("Key: ", matches[1])
## echo("Value: ", matches[2])
## elif line =~ r"\s*(\#.*)":
## # matches a comment
## # note that the implicit ``matches`` array is different from the
## # ``matches`` array of the first branch
## echo("comment: ", matches[1])
## else:
## echo("syntax error")
##
when not definedInScope(matches):
var matches: array[0..maxSubPatterns-1, string]
match(s, pattern, matches)
const ## common regular expressions
reIdentifier* = r"\b[a-zA-Z_][a-zA-Z_0-9]*\b" ## describes an identifier
reNatural* = r"\b\d+\b" ## describes a natural number
reInteger* = r"\b[-+]?\d+\b" ## describes an integer
reHex* = r"\b0[xX][0-9a-fA-F]+\b" ## describes a hexadecimal number
reBinary* = r"\b0[bB][01]+\b" ## describes a binary number (example: 0b11101)
reOctal* = r"\b0[oO][0-7]+\b" ## describes an octal number (example: 0o777)
reFloat* = r"\b[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\b"
## describes a floating point number
reEmail* = r"\b[a-zA-Z0-9!#$%&'*+/=?^_`{|}~\-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)" &
r"*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2}|com|org|" &
r"net|gov|mil|biz|info|mobi|name|aero|jobs|museum)\b"
## describes a common email address
reURL* = r"\b(http(s)?|ftp|gopher|telnet|file|notes|ms\-help):" &
r"((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-\=\\\.\&]*\b"
## describes an URL
proc verbose*(pattern: string): string {.noSideEffect.} =
## deletes whitespace from a pattern that is not escaped or in a character
## class. This is modelled after Perl's ``/x`` modifier.
result = ""
var i = 0
while i < pattern.len:
case pattern[i]
of ' ', '\t':
inc i
of '\\':
add result, '\\'
add result, pattern[i+1]
inc i, 2
of '[':
while pattern[i] != ']' and pattern[i] != '\0':
add result, pattern[i]
inc i
else:
add result, pattern[i]
inc i

View file

@ -480,40 +480,40 @@ proc align*(s: string, count: int): string {.
for i in spaces..count-1: result[i] = s[i-spaces] for i in spaces..count-1: result[i] = s[i-spaces]
else: else:
result = s result = s
iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
token: string, isSep: bool] = token: string, isSep: bool] =
## Tokenizes the string `s` into substrings. ## Tokenizes the string `s` into substrings.
## ##
## Substrings are separated by a substring containing only `seps`. ## Substrings are separated by a substring containing only `seps`.
## Examples: ## Examples:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## for word in tokenize(" this is an example "): ## for word in tokenize(" this is an example "):
## writeln(stdout, word) ## writeln(stdout, word)
## ##
## Results in: ## Results in:
## ##
## .. code-block:: nimrod ## .. code-block:: nimrod
## (" ", true) ## (" ", true)
## ("this", false) ## ("this", false)
## (" ", true) ## (" ", true)
## ("is", false) ## ("is", false)
## (" ", true) ## (" ", true)
## ("an", false) ## ("an", false)
## (" ", true) ## (" ", true)
## ("example", false) ## ("example", false)
## (" ", true) ## (" ", true)
var i = 0 var i = 0
while true: while true:
var j = i var j = i
var isSep = s[j] in seps var isSep = s[j] in seps
while j < s.len and (s[j] in seps) == isSep: inc(j) while j < s.len and (s[j] in seps) == isSep: inc(j)
if j > i: if j > i:
yield (substr(s, i, j-1), isSep) yield (substr(s, i, j-1), isSep)
else: else:
break break
i = j i = j
proc wordWrap*(s: string, maxLineWidth = 80, proc wordWrap*(s: string, maxLineWidth = 80,
splitLongWords = true, splitLongWords = true,
@ -544,6 +544,33 @@ proc wordWrap*(s: string, maxLineWidth = 80,
SpaceLeft = SpaceLeft - len(Word) SpaceLeft = SpaceLeft - len(Word)
result.add(word) result.add(word)
proc unindent*(s: string, eatAllIndent = false): string {.
noSideEffect, rtl, extern: "nsuUnindent".} =
## unindents `s`.
result = newStringOfCap(s.len)
var i = 0
var pattern = true
var indent = 0
while s[i] == ' ': inc i
var level = if i == 0: -1 else: i
while i < s.len:
if s[i] == ' ':
if i > 0 and s[i-1] in {'\l', '\c'}:
pattern = true
indent = 0
if pattern:
inc(indent)
if indent > level and not eatAllIndent:
result.add(s[i])
if level < 0: level = indent
else:
# a space somewhere: do not delete
result.add(s[i])
else:
pattern = false
result.add(s[i])
inc i
proc startsWith*(s, prefix: string): bool {.noSideEffect, proc startsWith*(s, prefix: string): bool {.noSideEffect,
rtl, extern: "nsuStartsWith".} = rtl, extern: "nsuStartsWith".} =
## Returns true iff ``s`` starts with ``prefix``. ## Returns true iff ``s`` starts with ``prefix``.
@ -827,34 +854,6 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
of '\"': add(result, "\\\"") of '\"': add(result, "\\\"")
else: add(result, c) else: add(result, c)
add(result, suffix) add(result, suffix)
proc validEmailAddress*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidEmailAddress".} =
## returns true if `s` seems to be a valid e-mail address.
## The checking also uses a domain list.
## Note: This will be moved to another module soon.
const
chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0
if s[i] notin chars or s[i] == '.': return false
while s[i] in chars:
if s[i] == '.' and s[i+1] == '.': return false
inc(i)
if s[i] != '@': return false
var j = len(s)-1
if s[j] notin letters: return false
while j >= i and s[j] in letters: dec(j)
inc(i) # skip '@'
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if s[i] != '\0': return false
var x = substr(s, j+1)
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true
case toLower(x)
of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
"aero", "jobs", "museum": return true
return false
proc validIdentifier*(s: string): bool {.noSideEffect, proc validIdentifier*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidIdentifier".} = rtl, extern: "nsuValidIdentifier".} =

View file

@ -254,7 +254,7 @@ type
## that should not be part of a message! Use ## that should not be part of a message! Use
## a ``TThreadId`` for that. ## a ``TThreadId`` for that.
emptyFn: proc () emptyFn: proc ()
dataFn: proc (p: TMsg) dataFn: proc (m: TMsg)
data: TMsg data: TMsg
TThreadId*[TMsg] = ptr TThread[TMsg] ## the current implementation uses TThreadId*[TMsg] = ptr TThread[TMsg] ## the current implementation uses
## a pointer as a thread ID. ## a pointer as a thread ID.

View file

@ -0,0 +1,19 @@
discard """
output: "0false"
"""
# Test multiple generic instantiation of generic proc vars:
proc threadProcWrapper[TMsg]() =
var x: TMsg
stdout.write($x)
#var x = threadProcWrapper[int]
#x()
#var y = threadProcWrapper[bool]
#y()
threadProcWrapper[int]()
threadProcWrapper[bool]()

View file

@ -3,9 +3,9 @@ Version 0.8.14
- ``var T`` as a return type; easy to prove that location does not escape its - ``var T`` as a return type; easy to prove that location does not escape its
stack frame stack frame
- strutils.unindent
- document Nimrod's two phase symbol lookup for generics - document Nimrod's two phase symbol lookup for generics
- make ^ available as operator - make ^ available as operator
- optional indentation for 'case' statement
- make threadvar efficient again on linux after testing - make threadvar efficient again on linux after testing
- test the sort implementation again - test the sort implementation again
- export re-entrant and non-reentrant locks and condition vars - export re-entrant and non-reentrant locks and condition vars

View file

@ -65,7 +65,7 @@
</div> </div>
</div> </div>
<div id="footer"> <div id="footer">
copyright &copy; 2010 $c.authors | Last update: ${getDateStr()} copyright &copy; 2011 $c.authors | Last update: ${getDateStr()}
| <a class="reference" href="http://validator.w3.org/check?uri=referer">XHTML 1.1</a> | <a class="reference" href="http://validator.w3.org/check?uri=referer">XHTML 1.1</a>
| <a class="reference" href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a> | <a class="reference" href="http://jigsaw.w3.org/css-validator/check/referer">CSS</a>
| <a class="reference" href="http://www.dcarter.co.uk">design by dcarter</a> | <a class="reference" href="http://www.dcarter.co.uk">design by dcarter</a>

View file

@ -2,17 +2,31 @@
News News
==== ====
2011-XX-XX Version 0.X.XX released 2011-XX-XX Version 0.8.14 released
================================== ==================================
Version 0.X.XX has been released! Get it `here <download.html>`_. Version 0.8.14 has been released! Get it `here <download.html>`_.
Bugfixes Bugfixes
-------- --------
- Boehm GC now works with ``--threads:on``.
- Fixed a serious memory corruption concerning message passing.
- Fixed a serious bug concerning different instantiations of a generic proc.
Changes affecting backwards compatibility Changes affecting backwards compatibility
----------------------------------------- -----------------------------------------
- Removed deprecated ``os.AppendFileExt``, ``os.executeShellCommand``,
``os.iterOverEnvironment``, ``os.pcDirectory``, ``os.pcLinkToDirectory``,
``os.SplitPath``, ``os.extractDir``, ``os.SplitFilename``,
``os.extractFileTrunk``, ``os.extractFileExt``, ``osproc.executeProcess``,
``osproc.executeCommand``.
- Removed deprecated ``parseopt.init``, ``parseopt.getRestOfCommandLine``.
- Moved ``strutils.validEmailAddress`` to ``matchers.validEmailAddress``.
Language Additions Language Additions
------------------ ------------------
@ -23,7 +37,11 @@ Library Additions
----------------- -----------------
- Added ``system.mainThreadId``. - Added ``system.mainThreadId``.
- Added ``system.allocShared``, ``system.allocShared0``,
``system.deallocShared``, ``system.reallocShared``.
- Added explicit channels for thread communication. - Added explicit channels for thread communication.
- Added ``matchers`` module for email address etc. matching.
- Added ``strutils.unindent``.
2011-07-10 Version 0.8.12 released 2011-07-10 Version 0.8.12 released

View file

@ -27,7 +27,7 @@ pdf: "manual;lib;tut1;tut2;nimrodc;c2nim;niminst"
srcdoc: "core/macros;pure/marshal;core/typeinfo" srcdoc: "core/macros;pure/marshal;core/typeinfo"
srcdoc: "impure/graphics;impure/re;pure/sockets" srcdoc: "impure/graphics;impure/re;pure/sockets"
srcdoc: "system.nim;system/threads.nim;system/inboxes.nim" srcdoc: "system.nim;system/threads.nim;system/inboxes.nim"
srcdoc: "pure/os;pure/strutils;pure/math" srcdoc: "pure/os;pure/strutils;pure/math;pure/matchers"
srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib" srcdoc: "pure/complex;pure/times;pure/osproc;pure/pegs;pure/dynlib"
srcdoc: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase" srcdoc: "pure/parseopt;pure/hashes;pure/strtabs;pure/lexbase"
srcdoc: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql" srcdoc: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql"