bugfix: proper cache for generic instantiations
This commit is contained in:
parent
81a917390b
commit
569c1ce5ec
24 changed files with 459 additions and 643 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -120,11 +120,3 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
454
doc/theindex.txt
454
doc/theindex.txt
File diff suppressed because it is too large
Load diff
|
|
@ -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
51
lib/pure/matchers.nim
Normal 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.}
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -496,36 +472,6 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1".} =
|
||||||
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.
|
||||||
|
|
|
||||||
|
|
@ -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] = [],
|
||||||
|
|
|
||||||
|
|
@ -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] =
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
@ -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``.
|
||||||
|
|
@ -828,34 +855,6 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
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".} =
|
||||||
## returns true if `s` is a valid identifier. A valid identifier starts
|
## returns true if `s` is a valid identifier. A valid identifier starts
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
19
tests/accept/run/tgenericprocvar.nim
Normal file
19
tests/accept/run/tgenericprocvar.nim
Normal 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]()
|
||||||
|
|
||||||
2
todo.txt
2
todo.txt
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
copyright © 2010 $c.authors | Last update: ${getDateStr()}
|
copyright © 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>
|
||||||
|
|
|
||||||
22
web/news.txt
22
web/news.txt
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue