Merge branch 'devel' of https://github.com/Araq/Nimrod into devel

This commit is contained in:
Michał Zieliński
2014-02-09 15:34:18 +01:00
91 changed files with 1372 additions and 1370 deletions

View File

@@ -1129,7 +1129,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
result.align = 2 # default alignment
result.id = getID()
when debugIds:
RegisterId(result)
registerId(result)
#if result.id < 2000 then
# MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
@@ -1166,7 +1166,7 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType =
if keepId:
result.id = t.id
else:
when debugIds: RegisterId(result)
when debugIds: registerId(result)
result.sym = t.sym # backend-info should not be copied
proc copySym(s: PSym, keepId: bool = false): PSym =
@@ -1177,7 +1177,7 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
result.id = s.id
else:
result.id = getID()
when debugIds: RegisterId(result)
when debugIds: registerId(result)
result.flags = s.flags
result.magic = s.magic
if s.kind == skModule:

View File

@@ -561,14 +561,31 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
proc strTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash = n.name.h and high(data)
while data[h] != nil:
if data[h] == n:
# allowed for 'export' feature:
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
return
h = nextTry(h, high(data))
assert(data[h] == nil)
data[h] = n
if sfImmediate notin n.flags:
# fast path:
while data[h] != nil:
if data[h] == n:
# allowed for 'export' feature:
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
return
h = nextTry(h, high(data))
assert(data[h] == nil)
data[h] = n
else:
# slow path; we have to ensure immediate symbols are preferred for
# symbol lookups.
# consider the chain: foo (immediate), foo, bar, bar (immediate)
# then bar (immediate) gets replaced with foo (immediate) and the non
# immediate foo is picked! Thus we need to replace it with the first
# slot that has in fact the same identifier stored in it!
var favPos = -1
while data[h] != nil:
if data[h] == n: return
if favPos < 0 and data[h].name.id == n.name.id: favPos = h
h = nextTry(h, high(data))
assert(data[h] == nil)
data[h] = n
if favPos >= 0: swap data[h], data[favPos]
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h

View File

@@ -343,7 +343,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
tyInt..tyUInt64, tyRange, tyVar:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
else: internalError("genAssignment(" & $ty.kind & ')')
else: internalError("genAssignment: " & $ty.kind)
proc getDestLoc(p: BProc, d: var TLoc, typ: PType) =
if d.k == locNone: getTemp(p, typ, d)
@@ -1049,7 +1049,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
app(tmp2.r, field.loc.r)
tmp2.k = locTemp
tmp2.t = field.loc.t
tmp2.s = OnHeap
tmp2.s = if isRef: OnHeap else: OnStack
tmp2.heapRoot = tmp.r
expr(p, it.sons[1], tmp2)
if d.k == locNone:

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -102,7 +102,7 @@ proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
of ccCDecl: result = DEFAULT_ABI
else:
GlobalError(info, "cannot map calling convention to FFI")
globalError(info, "cannot map calling convention to FFI")
template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
@@ -164,7 +164,7 @@ proc packObject(x: PNode, typ: PType, res: pointer) =
let field = getField(typ.n, i)
pack(it, field.typ, res +! field.offset)
else:
GlobalError(x.info, "cannot pack unnamed tuple")
globalError(x.info, "cannot pack unnamed tuple")
const maxPackDepth = 20
var packRecCheck = 0
@@ -193,7 +193,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of 4: awr(int32, v.intVal.int32)
of 8: awr(int64, v.intVal.int64)
else:
GlobalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
globalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
of tyFloat: awr(float, v.floatVal)
of tyFloat32: awr(float32, v.floatVal)
of tyFloat64: awr(float64, v.floatVal)
@@ -207,7 +207,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
elif v.kind in {nkStrLit..nkTripleStrLit}:
awr(cstring, cstring(v.strVal))
else:
GlobalError(v.info, "cannot map pointer/proc value to FFI")
globalError(v.info, "cannot map pointer/proc value to FFI")
of tyPtr, tyRef, tyVar:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
@@ -217,7 +217,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
else:
if packRecCheck > maxPackDepth:
packRecCheck = 0
GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
inc packRecCheck
pack(v.sons[0], typ.sons[0], res +! sizeof(pointer))
dec packRecCheck
@@ -233,7 +233,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
of tyDistinct, tyGenericInst:
pack(v, typ.sons[0], res)
else:
GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
proc unpack(x: pointer, typ: PType, n: PNode): PNode
@@ -243,7 +243,7 @@ proc unpackObjectAdd(x: pointer, n, result: PNode) =
for i in countup(0, sonsLen(n) - 1):
unpackObjectAdd(x, n.sons[i], result)
of nkRecCase:
GlobalError(result.info, "case objects cannot be unpacked")
globalError(result.info, "case objects cannot be unpacked")
of nkSym:
var pair = newNodeI(nkExprColonExpr, result.info, 2)
pair.sons[0] = n
@@ -262,14 +262,14 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
result = newNode(nkPar)
result.typ = typ
if typ.n.isNil:
InternalError("cannot unpack unnamed tuple")
internalError("cannot unpack unnamed tuple")
unpackObjectAdd(x, typ.n, result)
else:
result = n
if result.kind notin {nkObjConstr, nkPar}:
GlobalError(n.info, "cannot map value from FFI")
globalError(n.info, "cannot map value from FFI")
if typ.n.isNil:
GlobalError(n.info, "cannot unpack unnamed tuple")
globalError(n.info, "cannot unpack unnamed tuple")
for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
var it = n.sons[i]
if it.kind == nkExprColonExpr:
@@ -288,7 +288,7 @@ proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
else:
result = n
if result.kind != nkBracket:
GlobalError(n.info, "cannot map value from FFI")
globalError(n.info, "cannot map value from FFI")
let baseSize = typ.sons[1].getSize
for i in 0 .. < result.len:
result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
@@ -312,7 +312,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
#echo "expected ", k, " but got ", result.kind
#debug result
return newNodeI(nkExceptBranch, n.info)
#GlobalError(n.info, "cannot map value from FFI")
#globalError(n.info, "cannot map value from FFI")
result.field = v
template setNil() =
@@ -337,19 +337,19 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
of tyInt16: awi(nkInt16Lit, rd(int16, x))
of tyInt32: awi(nkInt32Lit, rd(int32, x))
of tyInt64: awi(nkInt64Lit, rd(int64, x))
of tyUInt: awi(nkUIntLit, rd(uint, x).biggestInt)
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).biggestInt)
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).biggestInt)
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).biggestInt)
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).biggestInt)
of tyUInt: awi(nkUIntLit, rd(uint, x).BiggestInt)
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).BiggestInt)
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).BiggestInt)
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
of tyEnum:
case typ.getSize
of 1: awi(nkIntLit, rd(uint8, x).biggestInt)
of 2: awi(nkIntLit, rd(uint16, x).biggestInt)
of 4: awi(nkIntLit, rd(int32, x).biggestInt)
of 8: awi(nkIntLit, rd(int64, x).biggestInt)
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
else:
GlobalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
globalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
of tyFloat: awf(nkFloatLit, rd(float, x))
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
@@ -374,7 +374,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
n.sons[0] = unpack(p, typ.sons[0], n.sons[0])
result = n
else:
GlobalError(n.info, "cannot map value from FFI " & typeToString(typ))
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
of tyObject, tyTuple:
result = unpackObject(x, typ, n)
of tyArray, tyArrayConstr:
@@ -391,7 +391,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
result = unpack(x, typ.sons[0], n)
else:
# XXX what to do with 'array' here?
GlobalError(n.info, "cannot map value from FFI " & typeToString(typ))
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(x: PNode, destTyp: PType): PNode =
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer,
@@ -414,7 +414,7 @@ proc fficast*(x: PNode, destTyp: PType): PNode =
dealloc a
proc callForeignFunction*(call: PNode): PNode =
InternalAssert call.sons[0].kind == nkPtrLit
internalAssert call.sons[0].kind == nkPtrLit
var cif: TCif
var sig: TParamList
@@ -422,12 +422,12 @@ proc callForeignFunction*(call: PNode): PNode =
for i in 1..call.len-1:
sig[i-1] = mapType(call.sons[i].typ)
if sig[i-1].isNil:
GlobalError(call.info, "cannot map FFI type")
globalError(call.info, "cannot map FFI type")
let typ = call.sons[0].typ
if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
mapType(typ.sons[0]), sig) != OK:
GlobalError(call.info, "error in FFI call")
globalError(call.info, "error in FFI call")
var args: TArgList
let fn = cast[pointer](call.sons[0].intVal)

View File

@@ -637,6 +637,22 @@ proc outerProcSons(o: POuterContext, n: PNode) =
let x = transformOuterProc(o, n.sons[i])
if x != nil: n.sons[i] = x
proc liftIterSym*(n: PNode): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
let iter = n.sym
assert iter.kind == skIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var env = copySym(getHiddenParam(iter))
env.kind = skLet
var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env))
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env))
result.add makeClosure(iter, env, n.info)
proc transformOuterProc(o: POuterContext, n: PNode): PNode =
if n == nil: return nil
case n.kind
@@ -649,17 +665,22 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
return indirectAccess(newSymNode(o.closureParam), local, n.info)
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
if closure != nil:
# we need to replace the lambda with '(lambda, env)':
if local.kind == skIterator and local.typ.callConv == ccClosure:
# consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the
# closure generation code again:
#if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s)
# XXX why doesn't this work?
if local.kind == skIterator and local.typ.callConv == ccClosure:
# consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the
# closure generation code again:
if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s)
# XXX why doesn't this work?
if closure.isNil:
return liftIterSym(n)
else:
let createdVar = generateIterClosureCreation(o, closure,
closure.attachedNode)
return makeClosure(local, createdVar, n.info)
if closure != nil:
# we need to replace the lambda with '(lambda, env)':
let a = closure.createdVar
if a != nil:
@@ -773,22 +794,6 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
# ------------------- iterator transformation --------------------------------
proc liftIterSym*(n: PNode): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
let iter = n.sym
assert iter.kind == skIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var env = copySym(getHiddenParam(iter))
env.kind = skLet
var v = newNodeI(nkVarSection, n.info)
addVar(v, newSymNode(env))
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env))
result.add makeClosure(iter, env, n.info)
proc liftForLoop*(body: PNode): PNode =
# problem ahead: the iterator could be invoked indirectly, but then
# we don't know what environment to create here:

View File

@@ -339,4 +339,16 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
n.sons[0].sym.name, o.inSymChoice)
if result != nil and result.kind == skStub: loadStub(result)
when false:
proc qualifiedLookUpPreferImmediate*(c: PContext, n: PNode,
flags = {checkUndeclared}): PSym =
var o: TOverloadIter
result = initOverloadIter(o, c, n)
var a = result
while a != nil:
if sfImmediate in a.flags: return a
a = nextOverloadIter(o, c, n)
if result == nil and checkUndeclared in flags:
localError(n.info, errUndeclaredIdentifier, n.considerAcc.s)
result = errorSym(c, n)

View File

@@ -135,7 +135,7 @@ proc interactivePasses =
#setTarget(osNimrodVM, cpuNimrodVM)
initDefines()
defineSymbol("nimrodvm")
when hasFFI: DefineSymbol("nimffi")
when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass)
registerPass(semPass)
registerPass(evalPass)
@@ -324,7 +324,7 @@ proc mainCommand* =
wantMainModule()
when hasTinyCBackend:
extccomp.setCC("tcc")
CommandCompileToC()
commandCompileToC()
else:
rawMessage(errInvalidCommandX, command)
of "js", "compiletojs":
@@ -450,7 +450,8 @@ proc mainCommand* =
echo " tries : ", gCacheTries
echo " misses: ", gCacheMisses
echo " int tries: ", gCacheIntTries
echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float), ffDecimal, 3)
echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
ffDecimal, 3)
when SimiluateCaasMemReset:
resetMemory()

View File

@@ -35,6 +35,7 @@ type
lex*: TLexer # the lexer that is used for parsing
tok*: TToken # the current token
inPragma: int
inSemiStmtList: int
proc parseAll*(p: var TParser): PNode
proc openParser*(p: var TParser, filename: string, inputstream: PLLStream)
@@ -455,11 +456,13 @@ proc complexOrSimpleStmt(p: var TParser): PNode
proc simpleExpr(p: var TParser, mode = pmNormal): PNode
proc semiStmtList(p: var TParser, result: PNode) =
inc p.inSemiStmtList
result.add(complexOrSimpleStmt(p))
while p.tok.tokType == tkSemiColon:
getTok(p)
optInd(p, result)
result.add(complexOrSimpleStmt(p))
dec p.inSemiStmtList
result.kind = nkStmtListExpr
proc parsePar(p: var TParser): PNode =
@@ -642,8 +645,7 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
#| | '[' optInd indexExprList optPar ']'
#| | '{' optInd indexExprList optPar '}'
#| | &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax
#| (doBlock | macroColon)?
#| | &( '`'|IDENT|literal|'cast') expr # command syntax
result = r
while p.tok.indent < 0:
case p.tok.tokType
@@ -672,16 +674,18 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
let a = result
result = newNodeP(nkCommand, p)
addSon(result, a)
while p.tok.tokType != tkEof:
let a = parseExpr(p)
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
if p.tok.tokType == tkDo:
parseDoBlocks(p, result)
else:
result = parseMacroColon(p, result)
addSon result, parseExpr(p)
when false:
while p.tok.tokType != tkEof:
let a = parseExpr(p)
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, a)
if p.tok.tokType == tkDo:
parseDoBlocks(p, result)
else:
result = parseMacroColon(p, result)
break
else:
break
@@ -1103,7 +1107,9 @@ proc parseExprStmt(p: var TParser): PNode =
#| doBlocks
#| / macroColon
#| ))?
inc p.inPragma
var a = simpleExpr(p)
dec p.inPragma
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
@@ -1878,14 +1884,18 @@ proc parseStmt(p: var TParser): PNode =
parMessage(p, errComplexStmtRequiresInd)
result = ast.emptyNode
else:
result = newNodeP(nkStmtList, p)
while true:
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
let a = simpleStmt(p)
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
result.add(a)
if p.tok.tokType != tkSemiColon: break
getTok(p)
if p.inSemiStmtList > 0:
result = simpleStmt(p)
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
else:
result = newNodeP(nkStmtList, p)
while true:
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
let a = simpleStmt(p)
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
result.add(a)
if p.tok.tokType != tkSemiColon: break
getTok(p)
proc parseAll(p: var TParser): PNode =
result = newNodeP(nkStmtList, p)

View File

@@ -83,7 +83,9 @@ proc commonType*(x, y: PType): PType =
elif a.kind == tyTypeDesc:
# turn any concrete typedesc into the abstract typedesc type
if a.sons == nil: result = a
else: result = newType(tyTypeDesc, a.owner)
else:
result = newType(tyTypeDesc, a.owner)
rawAddSon(result, newType(tyNone, a.owner))
elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and
a.kind == b.kind:
# check for seq[empty] vs. seq[int]

View File

@@ -42,7 +42,7 @@ type
TExprFlag* = enum
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
efAllowDestructor
efAllowDestructor, efWantValue
TExprFlags* = set[TExprFlag]
PContext* = ref TContext
@@ -211,6 +211,7 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ)
rawAddSon(typedesc, newTypeS(tyNone, c))
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
return newSymNode(sym, info)

View File

@@ -35,7 +35,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result.typ = errorType(c)
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags)
result = semExpr(c, n, flags+{efWantValue})
if result.isNil or result.kind == nkEmpty:
# do not produce another redundant error message:
#raiseRecoverableError("")
@@ -1706,8 +1706,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = n
result.typ = t
result.kind = nkObjConstr
t = skipTypes(t, abstractInst)
if t.kind == tyRef: t = skipTypes(t.sons[0], abstractInst)
t = skipTypes(t, {tyGenericInst})
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst})
if t.kind != tyObject:
localError(n.info, errGenerated, "object constructor needs an object type")
return
@@ -1819,6 +1819,10 @@ proc semExport(c: PContext, n: PNode): PNode =
c.module.ast.add x
result = n
proc setGenericParams(c: PContext, n: PNode) =
for i in 1 .. <n.len:
n[i].typ = semTypeNode(c, n[i], nil)
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n
if gCmd == cmdIdeTools: suggestExpr(c, n)
@@ -1924,10 +1928,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
else:
#liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags)
elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and
isSymChoice(n[0][0]):
elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]):
# indirectOp can deal with explicit instantiations; the fixes
# the 'newSeq[T](x)' bug
setGenericParams(c, n.sons[0])
result = semDirectOp(c, n, flags)
elif nfDelegate in n.flags:
elif isSymChoice(n.sons[0]) or nfDelegate in n.flags:
result = semDirectOp(c, n, flags)
else:
result = semIndirectOp(c, n, flags)
@@ -1993,7 +1999,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semStaticExpr(c, n)
of nkAsgn: result = semAsgn(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n)
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
of nkRaiseStmt: result = semRaise(c, n)
of nkVarSection: result = semVarOrLet(c, n, skVar)
of nkLetSection: result = semVarOrLet(c, n, skLet)

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -215,8 +215,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
checkMinSonsLen(a, 3)
var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc},
ctx)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
@@ -226,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3)
var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc},
ctx)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
# do not perform symbol lookup for default expressions
for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
@@ -281,8 +279,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3)
var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc},
ctx)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))

View File

@@ -740,7 +740,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
# we fill it out later. For magic generics like 'seq', it won't be filled
# so we use tyEmpty instead of nil to not crash for strange conversions
# like: mydata.seq
rawAddSon(s.typ, newTypeS(tyEmpty, c))
rawAddSon(s.typ, newTypeS(tyNone, c))
s.ast = a
inc c.inGenericContext
var body = semTypeNode(c, a.sons[2], nil)
@@ -748,7 +748,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
if body != nil:
body.sym = s
body.size = -1 # could not be computed properly
s.typ.sons[sonsLen(s.typ) - 1] = body
s.typ.sons[sonsLen(s.typ) - 1] = body
popOwner()
closeScope(c)
elif a.sons[2].kind != nkEmpty:
@@ -972,6 +972,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else:
s = n[namePos].sym
typeIsDetermined = s.typ == nil
s.ast = n
s.scope = c.currentScope
# if typeIsDetermined: assert phase == stepCompileBody
# else: assert phase == stepDetermineType
# before compiling the proc body, set as current the scope
@@ -1206,7 +1209,7 @@ proc usesResult(n: PNode): bool =
for c in n:
if usesResult(c): return true
proc semStmtList(c: PContext, n: PNode): PNode =
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# these must be last statements in a block:
const
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
@@ -1247,12 +1250,14 @@ proc semStmtList(c: PContext, n: PNode): PNode =
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true
n.typ = enforceVoidContext
if i != last or voidContext or c.inTypeClass > 0:
if i == last and efWantValue in flags:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext or c.inTypeClass > 0:
discardCheck(c, n.sons[i])
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ):
n.kind = nkStmtListExpr
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
case n.sons[i].kind
of nkVarSection, nkLetSection:
let (outer, inner) = insertDestructors(c, n.sons[i])

View File

@@ -358,7 +358,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
of nkConstSection: result = analyseConstSection(c, n)
of nkTypeSection, nkCommentStmt: result = toVoid
of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt,
nkElifBranch, nkElse, nkExceptBranch, nkOfBranch:
nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally:
for i in 0 .. <n.len: discard analyse(c, n[i])
result = toVoid
of nkBreakStmt, nkContinueStmt: result = toVoid

View File

@@ -681,12 +681,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyPtr, tyRef, tyProc:
# XXX: this is a bit strange, but proc(s: seq)
# produces tySequence(tyGenericParam, null).
# produces tySequence(tyGenericParam, tyNone).
# This also seems to be true when creating aliases
# like: type myseq = distinct seq.
# Maybe there is another better place to associate
# the seq type class with the seq identifier.
if paramType.kind == tySequence and paramType.lastSon == nil:
if paramType.kind == tySequence and paramType.lastSon.kind == tyNone:
let typ = c.newTypeWithSons(tyBuiltInTypeClass,
@[newTypeS(paramType.kind, c)])
result = addImplicitGeneric(typ)
@@ -885,8 +885,12 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
for i in countup(1, sonsLen(n)-1):
var elem = semGenericParamInInvokation(c, n.sons[i])
addToResult(elem)
elif s.typ.kind != tyGenericBody:
#we likely got code of the form TypeA[TypeB] where TypeA is
#not generic.
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
return newOrPrevType(tyError, prev, c)
else:
internalAssert s.typ.kind == tyGenericBody
var m = newCandidate(c, s, n)
matches(c, n, copyTree(n), m)
@@ -1133,15 +1137,26 @@ proc processMagicType(c: PContext, m: PSym) =
of mNil: setMagicType(m, tyNil, ptrSize)
of mExpr: setMagicType(m, tyExpr, 0)
of mStmt: setMagicType(m, tyStmt, 0)
of mTypeDesc: setMagicType(m, tyTypeDesc, 0)
of mTypeDesc:
setMagicType(m, tyTypeDesc, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mVoidType: setMagicType(m, tyEmpty, 0)
of mArray: setMagicType(m, tyArray, 0)
of mOpenArray: setMagicType(m, tyOpenArray, 0)
of mVarargs: setMagicType(m, tyVarargs, 0)
of mRange: setMagicType(m, tyRange, 0)
of mSet: setMagicType(m, tySet, 0)
of mSeq: setMagicType(m, tySequence, 0)
of mOrdinal: setMagicType(m, tyOrdinal, 0)
of mArray:
setMagicType(m, tyArray, 0)
of mOpenArray:
setMagicType(m, tyOpenArray, 0)
of mVarargs:
setMagicType(m, tyVarargs, 0)
of mRange:
setMagicType(m, tyRange, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mSet:
setMagicType(m, tySet, 0)
of mSeq:
setMagicType(m, tySequence, 0)
of mOrdinal:
setMagicType(m, tyOrdinal, 0)
rawAddSon(m.typ, newTypeS(tyNone, c))
of mPNimrodNode: discard
else: localError(m.info, errTypeExpected)
@@ -1165,8 +1180,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
typ = semTypeNode(c, constraint, nil)
if typ.kind != tyStatic or typ.len == 0:
if typ.kind == tyTypeDesc:
if typ.len == 0:
typ = newTypeS(tyTypeDesc, c)
if typ.sons[0].kind == tyNone:
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
else:
typ = semGenericConstraints(c, typ)

View File

@@ -358,7 +358,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
if lookup != nil:
result = lookup
if tfUnresolved in t.flags: result = result.base
elif t.sonsLen > 0:
elif t.sons[0].kind != tyNone:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
of tyUserTypeClass:

View File

@@ -105,6 +105,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
var bound = binding[i].typ
if bound != nil and formalTypeParam.kind != tyTypeDesc:
bound = bound.skipTypes({tyTypeDesc})
assert bound != nil
put(c.bindings, formalTypeParam, bound)
proc newCandidate*(ctx: PContext, callee: PSym,
@@ -562,10 +563,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
#if result < isGeneric: result = isNone
if result notin {isNone, isGeneric}:
result = typeRangeRel(f, a)
elif skipTypes(f, {tyRange}).kind == a.kind:
result = isIntConv
elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
result = isConvertible # a convertible to f
else:
if skipTypes(f, {tyRange}).kind == a.kind:
result = isIntConv
elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
result = isConvertible # a convertible to f
of tyInt: result = handleRange(f, a, tyInt8, tyInt32)
of tyInt8: result = handleRange(f, a, tyInt8, tyInt8)
of tyInt16: result = handleRange(f, a, tyInt8, tyInt16)
@@ -636,7 +638,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyOrdinal:
if isOrdinalType(a):
var x = if a.kind == tyOrdinal: a.sons[0] else: a
if f.sonsLen == 0:
if f.sons[0].kind == tyNone:
result = isGeneric
else:
result = typeRel(c, f.sons[0], x)
@@ -736,7 +738,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyGenericInst:
let roota = a.skipGenericAlias
let rootf = f.skipGenericAlias
if a.kind == tyGenericInst and roota.base == rootf.base :
if a.kind == tyGenericInst and roota.base == rootf.base:
for i in 1 .. rootf.sonsLen-2:
let ff = rootf.sons[i]
let aa = roota.sons[i]
@@ -845,7 +847,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# by a tyTypeDesc params. Unfortunately, this requires more substantial
# changes in semtypinst and elsewhere.
if a.kind == tyTypeDesc:
if f.sons == nil or f.sons.len == 0:
if f.sonsLen == 0:
result = isGeneric
else:
internalAssert a.sons != nil and a.sons.len > 0
@@ -854,7 +856,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else:
result = isNone
else:
if f.sonsLen > 0:
if f.sonsLen > 0 and f.sons[0].kind != tyNone:
result = typeRel(c, f.lastSon, a)
else:
result = isGeneric
@@ -883,7 +885,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
var prev = PType(idTableGet(c.bindings, f))
if prev == nil:
if a.kind == tyTypeDesc:
if f.sonsLen == 0:
if f.sons[0].kind == tyNone:
result = isGeneric
else:
result = typeRel(c, f.sons[0], a.sons[0])

View File

@@ -47,7 +47,8 @@ proc equalParams*(a, b: PNode): TParamsEquality
# returns whether the parameter lists of the procs a, b are exactly the same
proc isOrdinalType*(t: PType): bool
proc enumHasHoles*(t: PType): bool
const
const
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
tyConst, tyMutable, tyTypeDesc}
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal,
@@ -451,7 +452,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyProc: "proc"
of tyObject: "object"
of tyTuple: "tuple"
else: (internalAssert false; "")
else: (internalAssert(false); "")
of tyUserTypeClassInst:
let body = t.base
result = body.sym.name.s & "["
@@ -1257,7 +1258,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool =
return true
if t.kind == tyTypeDesc:
if t.sonsLen == 0: return true
if t.sons[0].kind == tyNone: return true
if containsGenericTypeIter(t.base, closure): return true
return false

View File

@@ -14,7 +14,7 @@ import ast except getstr
import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
parser, vmdeps, idents, trees, renderer, options
parser, vmdeps, idents, trees, renderer, options, transf
from semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate
@@ -337,7 +337,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
asgnRef(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
of opcWrGlobal:
asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
of opcLdArr:
of opcLdArr, opcLdArrRef:
# a = b[c]
let rb = instr.regB
let rc = instr.regC
@@ -348,7 +348,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
assert regs[rb].kind != nkMetaNode
let src = regs[rb]
if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
asgnComplex(regs[ra], src.sons[idx])
if instr.opcode == opcLdArrRef and false:
# XXX activate when seqs are fixed
asgnRef(regs[ra], src.sons[idx])
else:
asgnComplex(regs[ra], src.sons[idx])
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcLdStrIdx:
@@ -379,9 +383,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
# a = b.c
let rb = instr.regB
let rc = instr.regC
# XXX this creates a wrong alias
#Message(c.debug[pc], warnUser, $regs[rb].safeLen & " " & $rc)
asgnComplex(regs[ra], regs[rb].sons[rc])
of opcLdObjRef:
# a = b.c
let rb = instr.regB
let rc = instr.regC
# XXX activate when seqs are fixed
asgnComplex(regs[ra], regs[rb].sons[rc])
#asgnRef(regs[ra], regs[rb].sons[rc])
of opcWrObj:
# a.b = c
let rb = instr.regB
@@ -1037,7 +1047,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
# XXX only supports 'name' for now; we can use regC to encode the
# type trait operation
decodeB(nkStrLit)
let typ = regs[rb].sym.typ.skipTypes({tyTypeDesc})
var typ = regs[rb].typ
internalAssert typ != nil
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
regs[ra].strVal = typ.typeToString(preferExported)
of opcGlobalOnce:
let rb = instr.regBx
@@ -1066,6 +1078,7 @@ proc execute(c: PCtx, start: int): PNode =
result = rawExecute(c, start, tos)
proc evalStmt*(c: PCtx, n: PNode) =
let n = transformExpr(c.module, n)
let start = genStmt(c, n)
# execute new instructions; this redundant opcEof check saves us lots
# of allocations in 'execute':
@@ -1073,6 +1086,7 @@ proc evalStmt*(c: PCtx, n: PNode) =
discard execute(c, start)
proc evalExpr*(c: PCtx, n: PNode): PNode =
let n = transformExpr(c.module, n)
let start = genExpr(c, n)
assert c.code[start].opcode != opcEof
result = execute(c, start)
@@ -1115,6 +1129,7 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
const evalPass* = makePass(myOpen, nil, myProcess, myProcess)
proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
let n = transformExpr(module, n)
setupGlobalCtx(module)
var c = globalCtx
c.mode = mode
@@ -1168,7 +1183,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# doesn't end up in the parameter:
#InternalAssert tos.slots.len >= L
# return value:
tos.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
tos.slots[0] = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
# setup parameters:
for i in 1 .. < min(tos.slots.len, L):
tos.slots[i] = setupMacroParam(n.sons[i])

View File

@@ -34,9 +34,11 @@ type
opcAsgnComplex,
opcLdArr, # a = b[c]
opcLdArrRef,
opcWrArr, # a[b] = c
opcWrArrRef,
opcLdObj, # a = b.c
opcLdObjRef,
opcWrObj, # a.b = c
opcWrObjRef,
opcAddr,

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -13,9 +13,18 @@ import
unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef,
trees, intsets, rodread, magicsys, options
from os import splitFile
when hasFFI:
import evalffi
type
TGenFlag = enum gfNone, gfAddrOf
TGenFlags = set[TGenFlag]
proc debugInfo(info: TLineInfo): string =
result = info.toFilename.splitFile.name & ":" & $info.line
proc codeListing(c: PCtx, result: var string, start=0) =
# first iteration: compute all necessary labels:
var jumpTargets = initIntSet()
@@ -44,7 +53,7 @@ proc codeListing(c: PCtx, result: var string, start=0) =
else:
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
result.add("\t#")
result.add(toFileLine(c.debug[i]))
result.add(debugInfo(c.debug[i]))
result.add("\n")
inc i
@@ -100,7 +109,7 @@ proc patch(c: PCtx, p: TPosition) =
uint32(diff+wordExcess) shl 16'u32).TInstr
proc getSlotKind(t: PType): TSlotKind =
case t.skipTypes(abstractRange).kind
case t.skipTypes(abstractRange-{tyTypeDesc}).kind
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
slotTempInt
of tyString, tyCString:
@@ -190,20 +199,20 @@ template withBlock(labl: PSym; body: stmt) {.immediate, dirty.} =
body
popBlock(c, oldLen)
proc gen(c: PCtx; n: PNode; dest: var TDest)
proc gen(c: PCtx; n: PNode; dest: TRegister) =
proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {})
proc gen(c: PCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) =
var d: TDest = dest
gen(c, n, d)
gen(c, n, d, flags)
internalAssert d == dest
proc gen(c: PCtx; n: PNode) =
proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) =
var tmp: TDest = -1
gen(c, n, tmp)
gen(c, n, tmp, flags)
#if n.typ.isEmptyType: InternalAssert tmp < 0
proc genx(c: PCtx; n: PNode): TRegister =
proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
var tmp: TDest = -1
gen(c, n, tmp)
gen(c, n, tmp, flags)
internalAssert tmp >= 0
result = TRegister(tmp)
@@ -400,7 +409,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
let endExcept = c.xjmp(it, opcExcept, 0)
for j in countup(0, blen - 2):
assert(it.sons[j].kind == nkType)
let typ = it.sons[j].typ.skipTypes(abstractPtrs)
let typ = it.sons[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
c.gABx(it, opcExcept, 0, c.genType(typ))
if blen == 1:
# general except section:
@@ -470,15 +479,16 @@ proc genNew(c: PCtx; n: PNode) =
# we use the ref's base type here as the VM conflates 'ref object'
# and 'object' since internally we already have a pointer.
c.gABx(n, opcNew, dest,
c.genType(n.sons[1].typ.skipTypes(abstractVar).sons[0]))
c.genType(n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).sons[0]))
c.genAsgnPatch(n.sons[1], dest)
c.freeTemp(dest)
proc genNewSeq(c: PCtx; n: PNode) =
let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ)
else: c.genx(n.sons[1])
c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes(abstractVar)))
let tmp = c.genx(n.sons[2])
c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes(
abstractVar-{tyTypeDesc})))
c.gABx(n, opcNewSeq, tmp, 0)
c.freeTemp(tmp)
c.genAsgnPatch(n.sons[1], dest)
@@ -506,7 +516,7 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
c.freeTemp(tmp2)
proc genSetType(c: PCtx; n: PNode; dest: TRegister) =
let t = skipTypes(n.typ, abstractInst)
let t = skipTypes(n.typ, abstractInst-{tyTypeDesc})
if t.kind == tySet:
c.gABx(n, opcSetType, dest, c.genType(t))
@@ -528,6 +538,14 @@ proc genBinaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
c.gABC(n, opc, dest, tmp, 0)
c.freeTemp(tmp)
proc genBinaryStmtVar(c: PCtx; n: PNode; opc: TOpcode) =
let
dest = c.genx(n.sons[1], {gfAddrOf})
tmp = c.genx(n.sons[2])
c.gABC(n, opc, dest, tmp, 0)
#c.genAsgnPatch(n.sons[1], dest)
c.freeTemp(tmp)
proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
let tmp = c.genx(n.sons[1])
c.gABC(n, opc, tmp, 0, 0)
@@ -729,7 +747,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
var tmp = c.genx(n.sons[1])
var idx = c.getTemp(getSysType(tyInt))
var typ = n.sons[2].typ
if m == mOf: typ = typ.skipTypes(abstractPtrs)
if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc})
c.gABx(n, opcLdImmInt, idx, c.genType(typ))
c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx)
c.freeTemp(tmp)
@@ -739,7 +757,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
of mHigh:
if dest < 0: dest = c.getTemp(n.typ)
let tmp = c.genx(n.sons[1])
if n.sons[1].typ.skipTypes(abstractVar).kind == tyString:
if n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind == tyString:
c.gABI(n, opcLenStr, dest, tmp, 1)
else:
c.gABI(n, opcLenSeq, dest, tmp, 1)
@@ -754,13 +772,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.freeTempRange(x, n.len-1)
of mAppendStrCh:
unused(n, dest)
genBinaryStmt(c, n, opcAddStrCh)
genBinaryStmtVar(c, n, opcAddStrCh)
of mAppendStrStr:
unused(n, dest)
genBinaryStmt(c, n, opcAddStrStr)
genBinaryStmtVar(c, n, opcAddStrStr)
of mAppendSeqElem:
unused(n, dest)
genBinaryStmt(c, n, opcAddSeqElem)
genBinaryStmtVar(c, n, opcAddSeqElem)
of mParseExprToAst:
genUnaryABC(c, n, dest, opcParseExprToAst)
of mParseStmtToAst:
@@ -874,7 +892,7 @@ const
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
proc requiresCopy(n: PNode): bool =
if n.typ.skipTypes(abstractInst).kind in atomicTypes:
if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
result = false
elif n.kind in ({nkCurly, nkBracket, nkPar, nkObjConstr}+nkCallKinds):
result = false
@@ -882,7 +900,7 @@ proc requiresCopy(n: PNode): bool =
result = true
proc unneededIndirection(n: PNode): bool =
n.typ.skipTypes(abstractInst).kind == tyRef
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
proc skipDeref(n: PNode): PNode =
if n.kind in {nkDerefExpr, nkHiddenDeref} and unneededIndirection(n.sons[0]):
@@ -890,18 +908,20 @@ proc skipDeref(n: PNode): PNode =
else:
result = n
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) =
# a nop for certain types
let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags
if unneededIndirection(n.sons[0]):
gen(c, n.sons[0], dest)
gen(c, n.sons[0], dest, flags)
else:
let tmp = c.genx(n.sons[0])
let tmp = c.genx(n.sons[0], flags)
if dest < 0: dest = c.getTemp(n.typ)
gABC(c, n, opc, dest, tmp)
c.freeTemp(tmp)
proc whichAsgnOpc(n: PNode): TOpcode =
case n.typ.skipTypes(abstractRange).kind
case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
opcAsgnInt
of tyString, tyCString:
@@ -913,7 +933,7 @@ proc whichAsgnOpc(n: PNode): TOpcode =
else:
opcAsgnComplex
proc isRef(t: PType): bool = t.skipTypes(abstractRange).kind == tyRef
proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode =
if isRef(n.typ): succ(opc) else: opc
@@ -926,13 +946,22 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
proc setSlot(c: PCtx; v: PSym) =
# XXX generate type initialization here?
if v.position == 0:
v.position = c.prc.maxSlots
c.prc.slots[v.position] = (inUse: true,
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
inc c.prc.maxSlots
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
case le.kind
of nkBracketExpr:
let dest = c.genx(le.sons[0])
let idx = c.genx(le.sons[1])
let tmp = c.genx(ri)
if le.sons[0].typ.skipTypes(abstractVarRange).kind in {tyString, tyCString}:
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
tyString, tyCString}:
c.gABC(le, opcWrStrIdx, dest, idx, tmp)
else:
c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp)
@@ -952,8 +981,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
gen(c, ri, tmp)
c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position)
else:
if s.kind == skForVar and c.mode == emRepl: c.setSlot s
internalAssert s.position > 0 or (s.position == 0 and
s.kind in {skParam,skResult,skForVar})
s.kind in {skParam,skResult})
var dest: TRegister = s.position + ord(s.kind == skParam)
gen(c, ri, dest)
else:
@@ -1003,7 +1033,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
if s.isGlobal:
if sfCompileTime in s.flags or c.mode == emRepl:
discard
else:
elif s.position == 0:
cannotEval(n)
if s.position == 0:
if sfImportc in s.flags: c.importcSym(n.info, s)
@@ -1014,8 +1044,9 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
else:
c.gABx(n, opcLdGlobal, dest, s.position)
else:
if s.kind == skForVar and c.mode == emRepl: c.setSlot s
if s.position > 0 or (s.position == 0 and
s.kind in {skParam,skResult,skForVar}):
s.kind in {skParam,skResult}):
if dest < 0:
dest = s.position + ord(s.kind == skParam)
else:
@@ -1024,28 +1055,29 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
else:
# see tests/t99bott for an example that triggers it:
cannotEval(n)
#InternalError(n.info, s.name.s & " " & $s.position)
proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
let a = c.genx(n.sons[0])
let b = c.genx(n.sons[1])
proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) =
let a = c.genx(n.sons[0], flags)
let b = c.genx(n.sons[1], {})
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opc, dest, a, b)
c.gABC(n, (if gfAddrOf in flags: succ(opc) else: opc), dest, a, b)
c.freeTemp(a)
c.freeTemp(b)
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest) =
genAccess(c, n, dest, opcLdObj)
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
genAccess(c, n, dest, opcLdObj, flags)
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest) =
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
# XXX implement field checks!
genAccess(c, n.sons[0], dest, opcLdObj)
genAccess(c, n.sons[0], dest, opcLdObj, flags)
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest) =
if n.sons[0].typ.skipTypes(abstractVarRange).kind in {tyString, tyCString}:
genAccess(c, n, dest, opcLdStrIdx)
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
tyString, tyCString}:
genAccess(c, n, dest, opcLdStrIdx, {})
else:
genAccess(c, n, dest, opcLdArr)
genAccess(c, n, dest, opcLdArr, flags)
proc getNullValue*(typ: PType, info: TLineInfo): PNode
proc getNullValueAux(obj: PNode, result: PNode) =
@@ -1100,14 +1132,6 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkCurly, info, t)
else: internalError("getNullValue: " & $t.kind)
proc setSlot(c: PCtx; v: PSym) =
# XXX generate type initialization here?
if v.position == 0:
v.position = c.prc.maxSlots
c.prc.slots[v.position] = (inUse: true,
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
inc c.prc.maxSlots
proc genVarSection(c: PCtx; n: PNode) =
for a in n:
if a.kind == nkCommentStmt: continue
@@ -1187,7 +1211,7 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) =
proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ)
let t = n.typ.skipTypes(abstractRange)
let t = n.typ.skipTypes(abstractRange-{tyTypeDesc})
if t.kind == tyRef:
c.gABx(n, opcNew, dest, c.genType(t.sons[0]))
else:
@@ -1222,7 +1246,7 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
proc genProc*(c: PCtx; s: PSym): int
proc gen(c: PCtx; n: PNode; dest: var TDest) =
proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
case n.kind
of nkSym:
let s = n.sym
@@ -1271,11 +1295,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
of nkAsgn, nkFastAsgn:
unused(n, dest)
genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn)
of nkDotExpr: genObjAccess(c, n, dest)
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest)
of nkBracketExpr: genArrAccess(c, n, dest)
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref)
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr)
of nkDotExpr: genObjAccess(c, n, dest, flags)
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
of nkBracketExpr: genArrAccess(c, n, dest, flags)
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref, flags)
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr, flags)
of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
of nkCaseStmt: genCase(c, n, dest)
of nkWhileStmt:
@@ -1298,7 +1322,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
of nkStmtListExpr:
let L = n.len-1
for i in 0 .. <L: gen(c, n.sons[i])
gen(c, n.sons[L], dest)
gen(c, n.sons[L], dest, flags)
of nkDiscardStmt:
unused(n, dest)
gen(c, n.sons[0])
@@ -1460,9 +1484,9 @@ proc genProc(c: PCtx; s: PSym): int =
c.gABC(body, opcEof, eofInstr.regA)
c.optimizeJumps(result)
s.offset = c.prc.maxSlots
#if s.name.s == "rawGet":
#if s.name.s == "concatStyleInterpolation":
# c.echoCode(result)
# echo renderTree(body)
# echo renderTree(body)
c.prc = oldPrc
else:
c.prc.maxSlots = s.offset

View File

@@ -76,7 +76,7 @@ span.LongStringLit {color: blue}
span.CharLit {color: blue}
span.EscapeSequence {color: black}
span.Operator {color: black}
span.Punctation {color: black}
span.Punctuation {color: black}
span.Comment, span.LongComment {font-style:italic; color: green}
span.RegularExpression {color: DarkViolet}
span.TagStart {color: DarkViolet}

View File

@@ -98,7 +98,7 @@ doc.file = """
\newcommand{\spanCharLit}[1]{#1}
\newcommand{\spanEscapeSequence}[1]{#1}
\newcommand{\spanOperator}[1]{#1}
\newcommand{\spanPunctation}[1]{#1}
\newcommand{\spanPunctuation}[1]{#1}
\newcommand{\spanComment}[1]{\emph{#1}}
\newcommand{\spanLongComment}[1]{\emph{#1}}
\newcommand{\spanRegularExpression}[1]{#1}

View File

@@ -1,7 +1,7 @@
===============================================================================
Nimrod -- a Compiler for Nimrod. http://nimrod-code.org/
Copyright (C) 2004-2013 Andreas Rumpf. All rights reserved.
Copyright (C) 2004-2014 Andreas Rumpf. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,210 +0,0 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a simple logger. It is based on the following design:
## * Runtime log formating is a bug: Sooner or later every log file is parsed.
## * Keep it simple: If this library does not fullfill your needs, write your
## own. Trying to support every logging feature just leads to bloat.
##
## Format is::
##
## DEBUG|INFO|... (2009-11-02 00:00:00)? (Component: )? Message
##
##
import strutils, os, times
type
TLevel* = enum ## logging level
lvlAll, ## all levels active
lvlDebug, ## debug level (and any above) active
lvlInfo, ## info level (and any above) active
lvlWarn, ## warn level (and any above) active
lvlError, ## error level (and any above) active
lvlFatal, ## fatal level (and any above) active
lvlNone
const
LevelNames*: array [TLevel, string] = [
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
]
defaultFmtStr = "" ## default string between log level and message per logger
verboseFmtStr = "$date $time "
type
TLogger* = object of TObject ## abstract logger; the base type of all loggers
levelThreshold*: TLevel ## only messages of level >= levelThreshold
## should be processed
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
TConsoleLogger* = object of TLogger ## logger that writes the messages to the
## console
TFileLogger* = object of TLogger ## logger that writes the messages to a file
f: TFile
# TODO: implement rolling log, will produce filename.1, filename.2 etc.
TRollingFileLogger* = object of TFileLogger ## logger that writes the
## message to a file
maxLines: int # maximum number of lines
curLine : int
baseName: string # initial filename
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
proc substituteLog*(frmt: string): string =
## converts $date to the current date
## converts $time to the current time
## converts $app to getAppFilename()
## converts
result = newStringOfCap(frmt.len + 20)
var i = 0
while i < frmt.len:
if frmt[i] != '$':
result.add(frmt[i])
inc(i)
else:
inc(i)
var v = ""
var app = getAppFilename()
while frmt[i] in IdentChars:
v.add(toLower(frmt[i]))
inc(i)
case v
of "date": result.add(getDateStr())
of "time": result.add(getClockStr())
of "app": result.add(app)
of "appdir": result.add(app.splitFile.dir)
of "appname": result.add(app.splitFile.name)
method log*(L: ref TLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
## override this method in custom loggers. Default implementation does
## nothing.
nil
method log*(L: ref TConsoleLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
Writeln(stdout, LevelNames[level], " ", substituteLog(L.fmtStr), frmt % args)
method log*(L: ref TFileLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
Writeln(L.f, LevelNames[level], " ", substituteLog(L.fmtStr), frmt % args)
proc defaultFilename*(): string =
## returns the default filename for a logger
var (path, name, ext) = splitFile(getAppFilename())
result = changeFileExt(path / name & "_" & getDateStr(), "log")
proc newConsoleLogger*(levelThreshold = lvlAll) : ref TConsoleLogger =
new result
result.fmtStr = defaultFmtStr
result.levelThreshold = levelThreshold
proc newFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmAppend,
levelThreshold = lvlAll): ref TFileLogger =
new(result)
result.levelThreshold = levelThreshold
result.f = open(filename, mode)
result.fmtStr = defaultFmtStr
# ------
proc readLogLines(logger : ref TRollingFileLogger) = nil
#f.readLine # TODO read all lines, update curLine
proc newRollingFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmReadWrite,
levelThreshold = lvlAll,
maxLines = 1000): ref TRollingFileLogger =
new(result)
result.levelThreshold = levelThreshold
result.fmtStr = defaultFmtStr
result.maxLines = maxLines
result.f = open(filename, mode)
result.curLine = 0
# TODO count all number files
# count lines in existing filename file
# if >= maxLines then rename to next numbered file and create new file
#if mode in {fmReadWrite, fmReadWriteExisting}:
# readLogLines(result)
method log*(L: ref TRollingFileLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
# TODO
# if more than maxlines, then set cursor to zero
Writeln(L.f, LevelNames[level], " ", frmt % args)
# --------
var
level* = lvlAll ## global log filter
handlers*: seq[ref TLogger] = @[] ## handlers with their own log levels
proc logLoop(level: TLevel, frmt: string, args: varargs[string, `$`]) =
for logger in items(handlers):
if level >= logger.levelThreshold:
log(logger, level, frmt, args)
template log*(level: TLevel, frmt: string, args: varargs[string, `$`]) =
## logs a message of the given level
bind logLoop
bind `%`
bind logging.Level
if level >= logging.Level:
logLoop(level, frmt, args)
template debug*(frmt: string, args: varargs[string, `$`]) =
## logs a debug message
log(lvlDebug, frmt, args)
template info*(frmt: string, args: varargs[string, `$`]) =
## logs an info message
log(lvlInfo, frmt, args)
template warn*(frmt: string, args: varargs[string, `$`]) =
## logs a warning message
log(lvlWarn, frmt, args)
template error*(frmt: string, args: varargs[string, `$`]) =
## logs an error message
log(lvlError, frmt, args)
template fatal*(frmt: string, args: varargs[string, `$`]) =
## logs a fatal error message
log(lvlFatal, frmt, args)
# --------------
when isMainModule:
var L = newConsoleLogger()
var fL = newFileLogger("test.log")
fL.fmtStr = verboseFmtStr
handlers.add(L)
handlers.add(fL)
info("hello", [])

View File

@@ -107,3 +107,20 @@ that up to 100 objects are traversed and freed before it checks again. Thus
``workPackage`` affects the timing granularity and may need to be tweaked in
highly specialized environments or for older hardware.
Keeping track of memory
-----------------------
If you need to pass around memory allocated by Nimrod to C, you can use the
procs ``GC_ref`` and ``GC_unref`` to mark objects as referenced to avoid them
being freed by the GC. Other useful procs from `system <system.html>`_ you can
use to keep track of memory are:
* getTotalMem(): returns the amount of total memory managed by the GC.
* getOccupiedMem(): bytes reserved by the GC and used by objects.
* getFreeMem(): bytes reserved by the GC and not in use.
In addition to ``GC_ref`` and ``GC_unref`` you can avoid the GC by manually
allocating memory with procs like ``alloc``, ``allocShared``, or
``allocCStringArray``. The GC won't try to free them, you need to call their
respective *dealloc* pairs when you are done with them or they will leak.

View File

@@ -59,8 +59,7 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
| '.' optInd ('type' | 'addr' | symbol) generalizedLit?
| '[' optInd indexExprList optPar ']'
| '{' optInd indexExprList optPar '}'
| &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax
(doBlock | macroColon)?
| &( '`'|IDENT|literal|'cast') expr # command syntax
condExpr = expr colcom expr optInd
('elif' expr colcom expr optInd)*
'else' colcom expr

View File

@@ -341,6 +341,9 @@ Miscellaneous
* `endians <endians.html>`_
This module contains helpers that deal with different byte orders.
* `logging <logging.html>`_
This module implements a simple logger.
Database support
----------------

View File

@@ -12,6 +12,8 @@ Nimrod Manual
user to one/some of the other players, but the total amount seems to remain
pretty much constant for a given task. -- Ran
About this document
===================
@@ -1313,12 +1315,9 @@ Examples:
.. code-block:: nimrod
type
TCallback = proc (x: int) {.cdecl.}
proc printItem(x: int) = ...
proc printItem(x: Int) = ...
proc forEach(c: TCallback) =
proc forEach(c: proc (x: int) {.cdecl.}) =
...
forEach(printItem) # this will NOT work because calling conventions differ
@@ -1479,7 +1478,7 @@ But it seems all this boilerplate code needs to be repeated for the ``TEuro``
currency. This can be solved with templates_.
.. code-block:: nimrod
template Additive(typ: typedesc): stmt =
template additive(typ: typedesc): stmt =
proc `+` *(x, y: typ): typ {.borrow.}
proc `-` *(x, y: typ): typ {.borrow.}
@@ -1487,26 +1486,26 @@ currency. This can be solved with templates_.
proc `+` *(x: typ): typ {.borrow.}
proc `-` *(x: typ): typ {.borrow.}
template Multiplicative(typ, base: typedesc): stmt =
template multiplicative(typ, base: typedesc): stmt =
proc `*` *(x: typ, y: base): typ {.borrow.}
proc `*` *(x: base, y: typ): typ {.borrow.}
proc `div` *(x: typ, y: base): typ {.borrow.}
proc `mod` *(x: typ, y: base): typ {.borrow.}
template Comparable(typ: typedesc): stmt =
template comparable(typ: typedesc): stmt =
proc `<` * (x, y: typ): bool {.borrow.}
proc `<=` * (x, y: typ): bool {.borrow.}
proc `==` * (x, y: typ): bool {.borrow.}
template DefineCurrency(typ, base: expr): stmt =
template defineCurrency(typ, base: expr): stmt =
type
typ* = distinct base
Additive(typ)
Multiplicative(typ, base)
Comparable(typ)
additive(typ)
multiplicative(typ, base)
comparable(typ)
DefineCurrency(TDollar, int)
DefineCurrency(TEuro, int)
defineCurrency(TDollar, int)
defineCurrency(TEuro, int)
Void type
@@ -2232,6 +2231,8 @@ Instead of:
Using statement
---------------
**Warning**: The ``using`` statement is highly experimental!
The `using statement`:idx: provides syntactic convenience for procs that
heavily use a single contextual parameter. When applied to a variable or a
constant, it will instruct Nimrod to automatically consider the used symbol as
@@ -2457,6 +2458,83 @@ notation. (Thus an operator can have more than two parameters):
assert `*+`(3, 4, 6) == `*`(a, `+`(b, c))
Method call syntax
------------------
For object oriented programming, the syntax ``obj.method(args)`` can be used
instead of ``method(obj, args)``. The parentheses can be omitted if there are no
remaining arguments: ``obj.len`` (instead of ``len(obj)``).
This `method call syntax`:idx: is not restricted to objects, it can be used
to supply any type of first argument for procedures:
.. code-block:: nimrod
echo("abc".len) # is the same as echo(len("abc"))
echo("abc".toUpper())
echo({'a', 'b', 'c'}.card)
stdout.writeln("Hallo") # the same as writeln(stdout, "Hallo")
Another way to look at the method call syntax is that it provides the missing
postfix notation.
Properties
----------
Nimrod has no need for *get-properties*: Ordinary get-procedures that are called
with the *method call syntax* achieve the same. But setting a value is
different; for this a special setter syntax is needed:
.. code-block:: nimrod
type
TSocket* = object of TObject
FHost: int # cannot be accessed from the outside of the module
# the `F` prefix is a convention to avoid clashes since
# the accessors are named `host`
proc `host=`*(s: var TSocket, value: int) {.inline.} =
## setter of hostAddr
s.FHost = value
proc host*(s: TSocket): int {.inline.} =
## getter of hostAddr
return s.FHost
var
s: TSocket
s.host = 34 # same as `host=`(s, 34)
Command invocation syntax
-------------------------
Routines can be invoked without the ``()`` if the call is syntatically
a statement. This `command invocation syntax`:idx: also works for
expressions, but then only a single argument may follow. This restriction
means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as
``echo(f(1, f(2)))``. The method call syntax may be used to provide one
more argument in this case:
.. code-block:: nimrod
proc optarg(x:int, y:int = 0):int = x + y
proc singlearg(x:int):int = 20*x
echo optarg 1, " ", singlearg 2 # prints "1 40"
let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call
let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments
let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
assert x == y
The command invocation syntax also can't have complex expressions as arguments.
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
notation`_) is limited, but usable for a single proc (see the example in the
corresponding section). Function calls with no arguments still needs () to
distinguish between a call and the function itself as a first class value.
Closures
--------
@@ -2469,6 +2547,7 @@ the closure and its enclosing scope (i.e. any modifications made to them are
visible in both places). The closure environment may be allocated on the heap
or on the stack if the compiler determines that this would be safe.
Anonymous Procs
---------------
@@ -2495,6 +2574,9 @@ calls can use the ``do`` keyword:
.. code-block:: nimrod
sort(cities) do (x,y: string) -> int:
cmp(x.len, y.len)
# Less parenthesis using the method plus command syntax:
cities = cities.map do (x:string) -> string:
"City of " & x
``do`` is written after the parentheses enclosing the regular proc params.
The proc expression represented by the do block is appended to them.
@@ -2849,7 +2931,7 @@ parameters of an outer factory proc:
yield x
inc x
let foo = mycount 1, 4
let foo = mycount(1, 4)
for f in foo():
echo f
@@ -3440,13 +3522,41 @@ A symbol can be forced to be open by a `mixin`:idx: declaration:
.. code-block:: nimrod
proc create*[T](): ref T =
# there is no overloaded 'mixin' here, so we need to state that it's an
# there is no overloaded 'init' here, so we need to state that it's an
# open symbol explicitly:
mixin init
new result
init result
Bind statement
--------------
The `bind`:idx: statement is the counterpart to the ``mixin`` statement. It
can be used to explicitly declare identifiers that should be bound early (i.e.
the identifiers should be looked up in the scope of the template/generic
definition):
.. code-block:: nimrod
# Module A
var
lastId = 0
template genId*: expr =
bind lastId
inc(lastId)
lastId
.. code-block:: nimrod
# Module B
import A
echo genId()
But a ``bind`` is rarely useful because symbol binding from the definition
scope is the default.
Templates
=========
@@ -3506,28 +3616,6 @@ receive undeclared identifiers:
declareInt(x) # valid
Scoping in templates
--------------------
The template body does not open a new scope. To open a new scope a ``block``
statement can be used:
.. code-block:: nimrod
template declareInScope(x: expr, t: typedesc): stmt {.immediate.} =
var x: t
template declareInNewScope(x: expr, t: typedesc): stmt {.immediate.} =
# open a new scope:
block:
var x: t
declareInScope(a, int)
a = 42 # works, `a` is known here
declareInNewScope(b, int)
b = 42 # does not work, `b` is unknown
Passing a code block to a template
----------------------------------
@@ -3538,26 +3626,28 @@ special ``:`` syntax:
.. code-block:: nimrod
template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} =
block:
var f: TFile
if open(f, fn, mode):
try:
actions
finally:
close(f)
else:
quit("cannot open: " & fn)
var f: TFile
if open(f, fn, mode):
try:
actions
finally:
close(f)
else:
quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite):
txt.writeln("line 1")
txt.writeln("line 2")
In the example the two ``writeln`` statements are bound to the ``actions``
parameter.
parameter.
**Note:** The symbol binding rules for templates might change!
Symbol binding within templates happens after template instantiation:
Symbol binding in templates
---------------------------
A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are
bound from the definition scope of the template:
.. code-block:: nimrod
# Module A
@@ -3572,34 +3662,11 @@ Symbol binding within templates happens after template instantiation:
# Module B
import A
echo genId() # Error: undeclared identifier: 'lastId'
echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
As in generics symbol binding can be influenced via ``mixin`` or ``bind``
statements.
Bind statement
--------------
Exporting a template is a often a leaky abstraction as it can depend on
symbols that are not visible from a client module. However, to compensate for
this case, a `bind`:idx: statement can be used: It declares all identifiers
that should be bound early (i.e. when the template is parsed):
.. code-block:: nimrod
# Module A
var
lastId = 0
template genId*: expr =
bind lastId
inc(lastId)
lastId
.. code-block:: nimrod
# Module B
import A
echo genId() # Works
A ``bind`` statement can also be used in generics for the same purpose.
Identifier construction
@@ -3942,13 +4009,13 @@ Static params can also appear in the signatures of generic types:
type
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
# Please, note how `Number` is just a type constraint here, while
# Note how `Number` is just a type constraint here, while
# `static[int]` requires us to supply a compile-time int value
AffineTransform2D[T] = Matrix[3, 3, T]
AffineTransform3D[T] = Matrix[4, 4, T]
AffineTransform3D[float] # OK
AffineTransform3D[float] # OK
AffineTransform2D[string] # Error, `string` is not a `Number`
@@ -4685,6 +4752,10 @@ These rules ensure that the construction is tied to a variable and can easily
be destructed at its scope exit. Later versions of the language will improve
the support of destructors.
Be aware that destructors are not called for objects allocated with ``new``.
This may change in future versions of language, but for now use
the ``finalizer`` parameter to ``new``.
delegator pragma
----------------
@@ -5173,9 +5244,9 @@ the same feature under the same name.
Exportc pragma
--------------
The `exportc`:idx: pragma provides a means to export a type, a variable, or a
procedure to C. The optional argument is a string containing the C identifier.
If the argument is missing, the C name is the Nimrod
identifier *exactly as spelled*:
procedure to C. Enums and constants can't be exported. The optional argument
is a string containing the C identifier. If the argument is missing, the C
name is the Nimrod identifier *exactly as spelled*:
.. code-block:: Nimrod
proc callme(formatstr: cstring) {.exportc: "callMe", varargs.}

View File

@@ -1321,6 +1321,30 @@ In this example ``$`` is applied to any argument that is passed to the
parameter ``a``. Note that ``$`` applied to strings is a nop.
Slices
------
Slices look similar to subranges types in syntax but are used in a different
context. A slice is just an object of type TSlice which contains two bounds,
`a` and `b`. By itself a slice is not very useful, but other collection types
define operators which accept TSlice objects to define ranges.
.. code-block:: nimrod
var
a = "Nimrod is a progamming language"
b = "Slices are useless."
echo a[10..15] # --> 'a prog'
b[11.. -2] = "useful"
echo b # --> 'Slices are useful.'
In the previous example slices are used to modify a part of a string, and even
a negative index is used. The slice's bounds can hold any value supported by
their type, but it is the proc using the slice object which defines what values
are accepted.
Tuples
------

View File

@@ -528,7 +528,7 @@ containers:
proc newNode*[T](data: T): PBinaryTree[T] =
# constructor for a node
new(result)
result.dat = data
result.data = data
proc add*[T](root: var PBinaryTree[T], n: PBinaryTree[T]) =
# insert a node into the tree
@@ -569,7 +569,7 @@ containers:
var
root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
add(root, newNode("hallo")) # instantiates ``newNode`` and ``add``
add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
add(root, "world") # instantiates the second ``add`` proc
for str in preorder(root):
stdout.writeln(str)

View File

@@ -266,8 +266,9 @@ proc tests(args: string) =
# we compile the tester with taintMode:on to have a basic
# taint mode test :-)
exec "nimrod cc --taintMode:on tests/testament/tester"
exec quoteShell(getCurrentDir() / "tests/testament/tester".exe) & " " &
(args|"all")
let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
exec tester & " " & (args|"all")
exec tester & " html"
proc temp(args: string) =
var output = "compiler" / "nimrod".exe

View File

@@ -294,19 +294,6 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
## if not `ex`:
## echo `info` & ": Check failed: " & `expString`
when not defined(booting):
template emit*(e: static[string]): stmt =
## accepts a single string argument and treats it as nimrod code
## that should be inserted verbatim in the program
## Example:
##
## .. code-block:: nimrod
## emit("echo " & '"' & "hello world".toUpper & '"')
##
macro payload: stmt {.gensym.} =
result = e.parseStmt
payload()
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing
@@ -421,7 +408,8 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "!\"" & $n.ident & '"')
of nnkSym, nnkNone: assert false
of nnkSym: add(result, $n.symbol)
of nnkNone: assert false
else:
add(result, lispRepr(n[0]))
for j in 1..n.len-1:
@@ -745,3 +733,15 @@ proc addIdentIfAbsent*(dest: PNimrodNode, ident: string) {.compiletime.} =
else: discard
dest.add(ident(ident))
when not defined(booting):
template emit*(e: static[string]): stmt =
## accepts a single string argument and treats it as nimrod code
## that should be inserted verbatim in the program
## Example:
##
## .. code-block:: nimrod
## emit("echo " & '"' & "hello world".toUpper & '"')
##
macro payload: stmt {.gensym.} =
result = parseStmt(e)
payload()

View File

@@ -58,7 +58,7 @@ proc open*(host: string = defaultHost, port: int = defaultPort): TDbConn {.
## be established.
init(result)
let x = connect(result, host, port.cint)
let x = client(result, host, port.cint)
if x != 0'i32:
dbError(result, "cannot open: " & host)
@@ -119,7 +119,7 @@ proc insertId*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {.
## the generated OID for the ``_id`` field.
result = genOid()
var x = jsonToBSon(data, result)
insert(db, namespace, x)
insert(db, namespace, x, nil)
destroy(x)
proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {.

View File

@@ -148,7 +148,8 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
if finalize(stmt) != SQLITE_OK: dbError(db)
proc tryInsertID*(db: TDbConn, query: TSqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
args: varargs[string, `$`]): int64
{.tags: [FWriteDb], raises: [].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
@@ -157,7 +158,8 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery,
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
if step(stmt) == SQLITE_DONE:
result = last_insert_rowid(db)
if finalize(stmt) != SQLITE_OK: dbError(db)
if finalize(stmt) != SQLITE_OK:
result = -1
proc insertID*(db: TDbConn, query: TSqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =

View File

@@ -19,7 +19,7 @@ type
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
gtOperator, gtPunctation, gtComment, gtLongComment, gtRegularExpression,
gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression,
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
gtReference, gtOther
@@ -39,7 +39,7 @@ const
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
"EscapeSequence", "Operator", "Punctation", "Comment", "LongComment",
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
"RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData",
"Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
"Label", "Reference", "Other"]
@@ -258,7 +258,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
else: inc(pos)
of '(', ')', '[', ']', '{', '}', '`', ':', ',', ';':
inc(pos)
g.kind = gtPunctation
g.kind = gtPunctuation
of '\0':
g.kind = gtEof
else:
@@ -473,7 +473,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
else: inc(pos)
of '(', ')', '[', ']', '{', '}', ':', ',', ';', '.':
inc(pos)
g.kind = gtPunctation
g.kind = gtPunctuation
of '\0':
g.kind = gtEof
else:

View File

@@ -2566,3 +2566,15 @@ proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {.
proc realpath*(name, resolved: cstring): cstring {.
importc: "realpath", header: "<stdlib.h>".}
proc utimes*(path: cstring, times: ptr array [2, Ttimeval]): int {.
importc: "utimes", header: "<sys/time.h>".}
## Sets file access and modification times.
##
## Pass the filename and an array of times to set the access and modification
## times respectively. If you pass nil as the array both attributes will be
## set to the current time.
##
## Returns zero on success.
##
## For more information read http://www.unix.com/man-page/posix/3/utimes/.

View File

@@ -131,3 +131,36 @@ proc sort*[T](a: var openArray[T],
dec(m, s*2)
s = s*2
proc product*[T](x: openarray[seq[T]]): seq[seq[T]] =
## produces the Cartesian product of the array. Warning: complexity
## may explode.
result = @[]
if x.len == 0:
return
if x.len == 1:
result = @x
return
var
indexes = newSeq[int](x.len)
initial = newSeq[int](x.len)
index = 0
# replace with newSeq as soon as #853 is fixed
var next: seq[T] = @[]
next.setLen(x.len)
for i in 0..(x.len-1):
if len(x[i]) == 0: return
initial[i] = len(x[i])-1
indexes = initial
while true:
while indexes[index] == -1:
indexes[index] = initial[index]
index +=1
if index == x.len: return
indexes[index] -=1
for ni, i in indexes:
next[ni] = x[ni][i]
var res: seq[T]
shallowCopy(res, next)
result.add(res)
index = 0
indexes[index] -=1

View File

@@ -689,5 +689,5 @@ when isMainModule:
server.listen()
d.register(server)
while d.poll(-1): nil
while d.poll(-1): discard

View File

@@ -276,6 +276,38 @@ template foldr*(sequence, operation: expr): expr =
result = operation
result
template mapIt*(seq1, typ, pred: expr): expr =
## Convenience template around the ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. You also need to pass as `typ` the type of the expression,
## since the new returned sequence can have a different type than the
## original. Example:
##
## .. code-block:: nimrod
## let
## nums = @[1, 2, 3, 4]
## strings = nums.mapIt(string, $(4 * it))
var result {.gensym.}: seq[typ] = @[]
for it {.inject.} in items(seq1):
result.add(pred)
result
template mapIt*(varSeq, pred: expr) =
## Convenience template around the mutable ``map`` proc to reduce typing.
##
## The template injects the ``it`` variable which you can use directly in an
## expression. The expression has to return the same type as the sequence you
## are mutating. Example:
##
## .. code-block:: nimrod
## var nums = @[1, 2, 3, 4]
## nums.mapIt(it * 3)
## assert nums[0] + nums[3] == 15
for i in 0 .. <len(varSeq):
let it {.inject.} = varSeq[i]
varSeq[i] = pred
when isMainModule:
import strutils
block: # concat test
@@ -381,4 +413,11 @@ when isMainModule:
Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
block: # mapIt tests
var
nums = @[1, 2, 3, 4]
strings = nums.mapIt(string, $(4 * it))
nums.mapIt(it * 3)
assert nums[0] + nums[3] == 15
echo "Finished doc tests"

View File

@@ -224,3 +224,20 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
proc `$`*[A](s: TOrderedSet[A]): string =
## The `$` operator for ordered hash sets.
dollarImpl()
proc `<`*[A](s, t: TSet[A]): bool =
## Is s a strict subset of t?
s.counter != t.counter and s <= t
proc `<=`*[A](s, t: TSet[A]): bool =
## Is s a subset of t?
result = false
if s.counter > t.counter: return
result = true
for item in s:
if not(t.contains(item)):
result = false
return
proc `==`*[A](s, t: TSet[A]): bool =
s.counter == t.counter and s <= t

View File

@@ -189,6 +189,16 @@ template dollarImpl(): stmt {.dirty.} =
proc `$`*[A, B](t: TTable[A, B]): string =
## The `$` operator for hash tables.
dollarImpl()
proc `==`*[A, B](s, t: TTable[A, B]): bool =
s.counter == t.counter and s.data == t.data
proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] =
## Index the collection with the proc provided.
# TODO: As soon as supported, change collection: A to collection: A[B]
result = initTable[C, B]()
for item in collection:
result[index(item)] = item
# ------------------------------ ordered table ------------------------------

View File

@@ -14,15 +14,15 @@
type
TLibHandle* = pointer ## a handle to a dynamically loaded library
proc LoadLib*(path: string): TLibHandle
proc loadLib*(path: string): TLibHandle
## loads a library from `path`. Returns nil if the library could not
## be loaded.
proc LoadLib*(): TLibHandle
proc loadLib*(): TLibHandle
## gets the handle from the current executable. Returns nil if the
## library could not be loaded.
proc UnloadLib*(lib: TLibHandle)
proc unloadLib*(lib: TLibHandle)
## unloads the library `lib`
proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
@@ -60,9 +60,9 @@ when defined(posix):
proc dlsym(lib: TLibHandle, name: cstring): pointer {.
importc, header: "<dlfcn.h>".}
proc LoadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
proc LoadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc UnloadLib(lib: TLibHandle) = dlclose(lib)
proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc unloadLib(lib: TLibHandle) = dlclose(lib)
proc symAddr(lib: TLibHandle, name: cstring): pointer =
return dlsym(lib, name)
@@ -78,14 +78,14 @@ elif defined(windows) or defined(dos):
proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
proc winLoadLibrary(path: cstring): THINSTANCE {.
importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
proc GetProcAddress(lib: THINSTANCE, name: cstring): pointer {.
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
proc LoadLib(path: string): TLibHandle =
proc loadLib(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path))
proc LoadLib(): TLibHandle =
proc loadLib(): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(nil))
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
proc unloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
proc symAddr(lib: TLibHandle, name: cstring): pointer =
result = GetProcAddress(cast[THINSTANCE](lib), name)

View File

@@ -480,7 +480,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
if htmlTag(x.elemName) in {tagOption, tagOptgroup}:
errors.add(expected(x, result))
break
else: nil
else: discard
result.addNode(parse(x, errors))
of xmlElementEnd:
if cmpIgnoreCase(x.elemName, result.tag) == 0:
@@ -547,7 +547,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
var u = entityToUtf8(x.rawData)
if u.len != 0: result = newText(u)
next(x)
of xmlEof: nil
of xmlEof: discard
proc parseHtml*(s: PStream, filename: string,
errors: var seq[string]): PXmlNode =

View File

@@ -476,12 +476,12 @@ when isMainModule:
var client = irc("amber.tenthbit.net", nick="TestBot1234",
joinChans = @["#flood"])
client.connect()
while True:
while true:
var event: TIRCEvent
if client.poll(event):
case event.typ
of EvConnected:
nil
discard
of EvDisconnected:
break
of EvMsg:

267
lib/pure/logging.nim Normal file
View File

@@ -0,0 +1,267 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Andreas Rumpf, Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a simple logger. It has been designed to be as simple
## as possible to avoid bloat, if this library does not fullfill your needs,
## write your own.
##
## Format strings support the following variables which must be prefixed with
## the dollar operator (``$``):
##
## ============ =======================
## Operator Output
## ============ =======================
## $date Current date
## $time Current time
## $app ``os.getAppFilename()``
## ============ =======================
##
##
## The following example demonstrates logging to three different handlers
## simultaneously:
##
## .. code-block:: nimrod
##
## var L = newConsoleLogger()
## var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
## var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
## handlers.add(L)
## handlers.add(fL)
## handlers.add(rL)
## info("920410:52 accepted")
## warn("4 8 15 16 23 4-- Error")
## error("922044:16 SYSTEM FAILURE")
## fatal("SYSTEM FAILURE SYSTEM FAILURE")
import strutils, os, times
type
TLevel* = enum ## logging level
lvlAll, ## all levels active
lvlDebug, ## debug level (and any above) active
lvlInfo, ## info level (and any above) active
lvlWarn, ## warn level (and any above) active
lvlError, ## error level (and any above) active
lvlFatal, ## fatal level (and any above) active
lvlNone ## no levels active
const
LevelNames*: array [TLevel, string] = [
"DEBUG", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "NONE"
]
defaultFmtStr* = "" ## default string between log level and message per logger
verboseFmtStr* = "$date $time "
type
PLogger* = ref object of PObject ## abstract logger; the base type of all loggers
levelThreshold*: TLevel ## only messages of level >= levelThreshold
## should be processed
fmtStr: string ## = defaultFmtStr by default, see substituteLog for $date etc.
PConsoleLogger* = ref object of PLogger ## logger that writes the messages to the
## console
PFileLogger* = ref object of PLogger ## logger that writes the messages to a file
f: TFile
PRollingFileLogger* = ref object of PFileLogger ## logger that writes the
## messages to a file and
## performs log rotation
maxLines: int # maximum number of lines
curLine : int
baseName: string # initial filename
baseMode: TFileMode # initial file mode
logFiles: int # how many log files already created, e.g. basename.1, basename.2...
proc substituteLog(frmt: string): string =
## converts $date to the current date
## converts $time to the current time
## converts $app to getAppFilename()
## converts
result = newStringOfCap(frmt.len + 20)
var i = 0
while i < frmt.len:
if frmt[i] != '$':
result.add(frmt[i])
inc(i)
else:
inc(i)
var v = ""
var app = getAppFilename()
while frmt[i] in IdentChars:
v.add(toLower(frmt[i]))
inc(i)
case v
of "date": result.add(getDateStr())
of "time": result.add(getClockStr())
of "app": result.add(app)
of "appdir": result.add(app.splitFile.dir)
of "appname": result.add(app.splitFile.name)
method log*(logger: PLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) {.raises: [EBase], tags: [FTime, FWriteIO, FReadIO].} =
## Override this method in custom loggers. Default implementation does
## nothing.
discard
method log*(logger: PConsoleLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
## Logs to the console using ``logger`` only.
if level >= logger.levelThreshold:
writeln(stdout, LevelNames[level], " ", substituteLog(logger.fmtStr),
frmt % args)
method log*(logger: PFileLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
## Logs to a file using ``logger`` only.
if level >= logger.levelThreshold:
writeln(logger.f, LevelNames[level], " ",
substituteLog(logger.fmtStr), frmt % args)
proc defaultFilename*(): string =
## Returns the default filename for a logger.
var (path, name, ext) = splitFile(getAppFilename())
result = changeFileExt(path / name, "log")
proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr): PConsoleLogger =
## Creates a new console logger. This logger logs to the console.
new result
result.fmtStr = fmtStr
result.levelThreshold = levelThreshold
proc newFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmAppend,
levelThreshold = lvlAll,
fmtStr = defaultFmtStr): PFileLogger =
## Creates a new file logger. This logger logs to a file.
new(result)
result.levelThreshold = levelThreshold
result.f = open(filename, mode)
result.fmtStr = fmtStr
# ------
proc countLogLines(logger: PRollingFileLogger): int =
result = 0
for line in logger.f.lines():
result.inc()
proc countFiles(filename: string): int =
# Example: file.log.1
result = 0
let (dir, name, ext) = splitFile(filename)
for kind, path in walkDir(dir):
if kind == pcFile:
let llfn = name & ext & ExtSep
if path.extractFilename.startsWith(llfn):
let numS = path.extractFilename[llfn.len .. -1]
try:
let num = parseInt(numS)
if num > result:
result = num
except EInvalidValue: discard
proc newRollingFileLogger*(filename = defaultFilename(),
mode: TFileMode = fmReadWrite,
levelThreshold = lvlAll,
fmtStr = defaultFmtStr,
maxLines = 1000): PRollingFileLogger =
## Creates a new rolling file logger. Once a file reaches ``maxLines`` lines
## a new log file will be started and the old will be renamed.
new(result)
result.levelThreshold = levelThreshold
result.fmtStr = defaultFmtStr
result.maxLines = maxLines
result.f = open(filename, mode)
result.curLine = 0
result.baseName = filename
result.baseMode = mode
result.logFiles = countFiles(filename)
if mode == fmAppend:
# We need to get a line count because we will be appending to the file.
result.curLine = countLogLines(result)
proc rotate(logger: PRollingFileLogger) =
let (dir, name, ext) = splitFile(logger.baseName)
for i in countdown(logger.logFiles, 0):
let srcSuff = if i != 0: ExtSep & $i else: ""
moveFile(dir / (name & ext & srcSuff),
dir / (name & ext & ExtSep & $(i+1)))
method log*(logger: PRollingFileLogger, level: TLevel,
frmt: string, args: varargs[string, `$`]) =
## Logs to a file using rolling ``logger`` only.
if level >= logger.levelThreshold:
if logger.curLine >= logger.maxLines:
logger.f.close()
rotate(logger)
logger.logFiles.inc
logger.curLine = 0
logger.f = open(logger.baseName, logger.baseMode)
writeln(logger.f, LevelNames[level], " ", frmt % args)
logger.curLine.inc
# --------
var
level* = lvlAll ## global log filter
handlers*: seq[PLogger] = @[] ## handlers with their own log levels
proc logLoop(level: TLevel, frmt: string, args: varargs[string, `$`]) =
for logger in items(handlers):
if level >= logger.levelThreshold:
log(logger, level, frmt, args)
template log*(level: TLevel, frmt: string, args: varargs[string, `$`]) =
## Logs a message to all registered handlers at the given level.
bind logLoop
bind `%`
bind logging.Level
if level >= logging.Level:
logLoop(level, frmt, args)
template debug*(frmt: string, args: varargs[string, `$`]) =
## Logs a debug message to all registered handlers.
log(lvlDebug, frmt, args)
template info*(frmt: string, args: varargs[string, `$`]) =
## Logs an info message to all registered handlers.
log(lvlInfo, frmt, args)
template warn*(frmt: string, args: varargs[string, `$`]) =
## Logs a warning message to all registered handlers.
log(lvlWarn, frmt, args)
template error*(frmt: string, args: varargs[string, `$`]) =
## Logs an error message to all registered handlers.
log(lvlError, frmt, args)
template fatal*(frmt: string, args: varargs[string, `$`]) =
## Logs a fatal error message to all registered handlers.
log(lvlFatal, frmt, args)
# --------------
when isMainModule:
var L = newConsoleLogger()
var fL = newFileLogger("test.log", fmtStr = verboseFmtStr)
var rL = newRollingFileLogger("rolling.log", fmtStr = verboseFmtStr)
handlers.add(L)
handlers.add(fL)
handlers.add(rL)
for i in 0 .. 25:
info("hello" & $i, [])

View File

@@ -54,7 +54,7 @@ proc parseInt*(s: string, value: var int, validRange: TSlice[int]) {.
try:
discard parseutils.parseInt(s, x, 0)
except EOverflow:
nil
discard
if x in validRange: value = x
when isMainModule:

View File

@@ -28,7 +28,7 @@ proc hexbyte*(hex: char): int =
of '0'..'9': result = (ord(hex) - ord('0'))
of 'a'..'f': result = (ord(hex) - ord('a') + 10)
of 'A'..'F': result = (ord(hex) - ord('A') + 10)
else: nil
else: discard
proc parseOid*(str: cstring): TOid =
## parses an OID.

View File

@@ -287,12 +287,18 @@ proc osLastError*(): TOSErrorCode =
result = TOSErrorCode(errno)
{.pop.}
proc unixToNativePath*(path: string): string {.
proc unixToNativePath*(path: string, drive=""): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Converts an UNIX-like path to a native one.
##
## On an UNIX system this does nothing. Else it converts
## '/', '.', '..' to the appropriate things.
##
## On systems with a concept of "drives", `drive` is used to determine
## which drive label to use during absolute path conversion.
## `drive` defaults to the drive of the current working directory, and is
## ignored on systems that do not have a concept of "drives".
when defined(unix):
result = path
else:
@@ -300,7 +306,10 @@ proc unixToNativePath*(path: string): string {.
if path[0] == '/':
# an absolute path
when doslike:
result = r"C:\"
if drive != "":
result = drive & ":" & DirSep
else:
result = $DirSep
elif defined(macos):
result = "" # must not start with ':'
else:
@@ -387,6 +396,21 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
var res: TStat
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
tags: [FReadDir].} =
## Returns true iff the symlink `link` exists. Will return true
## regardless of whether the link points to a directory or file.
when defined(windows):
when useWinUnicode:
wrapUnary(a, getFileAttributesW, link)
else:
var a = getFileAttributesA(link)
if a != -1'i32:
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
else:
var res: TStat
return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
proc fileExists*(filename: string): bool {.inline.} =
## Synonym for existsFile
existsFile(filename)
@@ -1013,7 +1037,7 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
## the process has finished. To execute a program without having a
## shell involved, use the `execProcess` proc of the `osproc`
## module.
result = c_system(command)
result = c_system(command) shr 8
# Environment handling cannot be put into RTL, because the ``envPairs``
# iterator depends on ``environment``.
@@ -1054,9 +1078,9 @@ when defined(windows):
while true:
var eend = strEnd(e)
add(environment, $e)
e = cast[CString](cast[TAddress](eend)+1)
e = cast[cstring](cast[TAddress](eend)+1)
if eend[1] == '\0': break
discard FreeEnvironmentStringsA(env)
discard freeEnvironmentStringsA(env)
envComputed = true
else:
@@ -1221,6 +1245,8 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
if not skipFindData(f):
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDir
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
k = succ(k)
yield (k, dir / extractFilename(getFilename(f)))
if findNextFile(h, f) == 0'i32: break
findClose(h)
@@ -1245,6 +1271,10 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
tags: [FReadDir].} =
## walks over the directory `dir` and yields for each file in `dir`. The
## full path for each file is returned.
## **Warning**:
## Modifying the directory structure while the iterator
## is traversing may result in undefined behavior!
##
## Walking is recursive. `filter` controls the behaviour of the iterator:
##
## --------------------- ---------------------------------------------
@@ -1336,6 +1366,46 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
copyDir(path, dest / noSource)
else: discard
proc createSymlink*(src, dest: string) =
## Create a symbolic link at `dest` which points to the item specified
## by `src`. On most operating systems, will fail if a lonk
##
## **Warning**:
## Some OS's (such as Microsoft Windows) restrict the creation
## of symlinks to root users (administrators).
when defined(Windows):
let flag = dirExists(src).int32
when useWinUnicode:
var wSrc = newWideCString(src)
var wDst = newWideCString(dest)
if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
osError(osLastError())
else:
if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0:
osError(osLastError())
else:
if symlink(src, dest) != 0:
osError(osLastError())
proc createHardlink*(src, dest: string) =
## Create a hard link at `dest` which points to the item specified
## by `src`.
##
## **Warning**: Most OS's restrict the creation of hard links to
## root users (administrators) .
when defined(Windows):
when useWinUnicode:
var wSrc = newWideCString(src)
var wDst = newWideCString(dest)
if createHardLinkW(wDst, wSrc, nil) == 0:
osError(osLastError())
else:
if createHardLinkA(dest, src, nil) == 0:
osError(osLastError())
else:
if link(src, dest) != 0:
osError(osLastError())
proc parseCmdLine*(c: string): seq[string] {.
noSideEffect, rtl, extern: "nos$1".} =
## Splits a command line into several components;

View File

@@ -455,7 +455,7 @@ when defined(Windows) and not defined(useNimRtl):
ee, wwd, si, procInfo)
else:
success = winlean.createProcessA(nil,
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, si, procInfo)
let lastError = osLastError()
if poParentStreams notin options:
@@ -534,7 +534,7 @@ when defined(Windows) and not defined(useNimRtl):
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
else:
var res = winlean.createProcessA(nil, command, nil, nil, 0,
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
if res == 0:
osError(osLastError())
else:
@@ -791,7 +791,7 @@ elif not defined(useNimRtl):
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
proc execCmd(command: string): int =
result = csystem(command)
result = csystem(command) shr 8
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
FD_ZERO(fd)

View File

@@ -149,7 +149,7 @@ proc readRow*(my: var TCsvParser, columns = 0): bool =
of '\c': my.bufpos = handleCR(my, my.bufpos)
of '\l': my.bufpos = handleLF(my, my.bufpos)
else: break
of '\0': nil
of '\0': discard
else: error(my, my.bufpos, my.sep & " expected")
break

View File

@@ -79,7 +79,7 @@ proc handleHexChar(c: var TSqlLexer, xi: var int) =
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos)
else:
nil
discard
proc handleOctChar(c: var TSqlLexer, xi: var int) =
if c.buf[c.bufpos] in {'0'..'7'}:
@@ -373,7 +373,7 @@ proc getOperator(c: var TSqlLexer, tok: var TToken) =
of '+':
if not trailingPlusMinus and buf[pos+1] notin operators and
tok.literal.len > 0: break
of '*', '<', '>', '=': nil
of '*', '<', '>', '=': discard
else: break
add(tok.literal, buf[pos])
inc(pos)
@@ -1120,7 +1120,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
proc ra(n: PSqlNode, s: var string, indent: int) =
if n == nil: return
case n.kind
of nkNone: nil
of nkNone: discard
of nkIdent:
if allCharsInSet(n.strVal, {'\33'..'\127'}):
s.add(n.strVal)

View File

@@ -66,13 +66,15 @@ proc checkReply(smtp: var TSMTP, reply: string) =
if not line.string.startswith(reply):
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
const compiledWithSsl = defined(ssl)
proc connect*(address: string, port = 25,
ssl = false, debug = false): TSMTP =
## Establishes a connection with a SMTP server.
## May fail with EInvalidReply or with a socket error.
result.sock = socket()
if ssl:
when defined(ssl):
when compiledWithSsl:
let ctx = newContext(verifyMode = CVerifyNone)
ctx.wrapSocket(result.sock)
else:

View File

@@ -311,7 +311,8 @@ when defined(ssl):
newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
if newCTX == nil:
SSLError()
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
newCTX.loadCertificates(certFile, keyFile)
return PSSLContext(newCTX)
@@ -1291,14 +1292,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
var c: char
discard waitFor(socket, waited, timeout, 1, "readLine")
var n = recv(socket, addr(c), 1)
if n < 0: osError(osLastError())
if n < 0: socket.socketError()
elif n == 0: return
if c == '\r':
discard waitFor(socket, waited, timeout, 1, "readLine")
n = peekChar(socket, c)
if n > 0 and c == '\L':
discard recv(socket, addr(c), 1)
elif n <= 0: osError(osLastError())
elif n <= 0: socket.socketError()
addNLIfEmpty()
return
elif c == '\L':

View File

@@ -45,6 +45,16 @@ const
NewLines* = {'\13', '\10'}
## the set of characters a newline terminator can start with
AllChars* = {'\x00'..'\xFF'}
## A set with all the possible characters. Not very useful by its own, you
## can use it to create *inverted* sets to make the ``find()`` proc find
## **invalid** characters in strings. Example:
##
## .. code-block:: nimrod
## let invalid = AllChars - Digits
## doAssert "01234".find(invalid) == -1
## doAssert "01A34".find(invalid) == 2
proc toLower*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerChar".} =
## Converts `c` into lower case. This works only for the letters A-Z.

View File

@@ -211,7 +211,9 @@ proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
result.months = months
result.years = years
proc isLeapYear(year: int): bool =
proc isLeapYear*(year: int): bool =
## returns true if ``year`` is a leap year
if year mod 400 == 0:
return true
elif year mod 100 == 0:
@@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool =
else:
return false
proc getDaysInMonth(month: TMonth, year: int): int =
proc getDaysInMonth*(month: TMonth, year: int): int =
## gets the amount of days in a ``month`` of a ``year``
# http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
case month
of mFeb: result = if isLeapYear(year): 29 else: 28

View File

@@ -96,7 +96,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
## &entity;
errors.add(errorMsg(x, "unknown entity: " & x.entityName))
next(x)
of xmlEof: nil
of xmlEof: discard
proc parseXml*(s: PStream, filename: string,
errors: var seq[string]): PXmlNode =
@@ -110,7 +110,7 @@ proc parseXml*(s: PStream, filename: string,
of xmlElementOpen, xmlElementStart:
result = parse(x, errors)
break
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: nil # just skip it
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it
of xmlError:
errors.add(errorMsg(x))
else:

View File

@@ -2333,29 +2333,29 @@ when not defined(JS): #and not defined(NimrodVM):
elif defined(JS):
# Stubs:
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
proc GC_disable() = nil
proc GC_enable() = nil
proc GC_fullCollect() = nil
proc GC_setStrategy(strategy: TGC_Strategy) = nil
proc GC_enableMarkAndSweep() = nil
proc GC_disableMarkAndSweep() = nil
proc GC_disable() = discard
proc GC_enable() = discard
proc GC_fullCollect() = discard
proc GC_setStrategy(strategy: TGC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1
proc dealloc(p: pointer) = nil
proc alloc(size: int): pointer = nil
proc alloc0(size: int): pointer = nil
proc realloc(p: Pointer, newsize: int): pointer = nil
proc dealloc(p: pointer) = discard
proc alloc(size: int): pointer = discard
proc alloc0(size: int): pointer = discard
proc realloc(p: Pointer, newsize: int): pointer = discard
proc allocShared(size: int): pointer = nil
proc allocShared0(size: int): pointer = nil
proc deallocShared(p: pointer) = nil
proc reallocShared(p: pointer, newsize: int): pointer = nil
proc allocShared(size: int): pointer = discard
proc allocShared0(size: int): pointer = discard
proc deallocShared(p: pointer) = discard
proc reallocShared(p: pointer, newsize: int): pointer = discard
when defined(JS):
include "system/jssys"
@@ -2490,11 +2490,11 @@ proc staticRead*(filename: string): string {.magic: "Slurp".}
## ``slurp`` is an alias for ``staticRead``.
proc gorge*(command: string, input = ""): string {.
magic: "StaticExec".} = nil
magic: "StaticExec".} = discard
## This is an alias for ``staticExec``.
proc staticExec*(command: string, input = ""): string {.
magic: "StaticExec".} = nil
magic: "StaticExec".} = discard
## Executes an external process at compile-time.
## if `input` is not an empty string, it will be passed as a standard input
## to the executed program.
@@ -2561,7 +2561,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
## $pos.line, astToStr(code)]
## assert false, "A test expecting failure succeeded?"
## except exception:
## nil
## discard
##
## proc tester(pos: int): int =
## let

View File

@@ -23,7 +23,7 @@ else:
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
header: "<windows.h>", nodecl.}
proc writeToStdErr(msg: CString) =
proc writeToStdErr(msg: cstring) =
discard MessageBoxA(0, msg, nil, 0)
proc showErrorMessage(data: cstring) =

View File

@@ -802,7 +802,7 @@ when defined(sparc): # For SPARC architecture.
# Addresses decrease as the stack grows.
while sp <= max:
gcMark(gch, sp[])
sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer))
sp = cast[PPointer](cast[TAddress](sp) +% sizeof(pointer))
elif defined(ELATE):
{.error: "stack marking code is to be written for this architecture".}

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Andreas Rumpf
# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -59,11 +59,11 @@ var
gch {.rtlThreadVar.}: TGcHeap
when not defined(useNimRtl):
InstantiateForRegion(gch.region)
instantiateForRegion(gch.region)
template acquire(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap:
AcquireSys(HeapLock)
acquireSys(HeapLock)
template release(gch: TGcHeap) =
when hasThreadSupport and hasSharedHeap:
@@ -90,7 +90,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
# used for code generation concerning debugging
result = usrToCell(c).typ
proc unsureAsgnRef(dest: ppointer, src: pointer) {.inline.} =
proc unsureAsgnRef(dest: PPointer, src: pointer) {.inline.} =
dest[] = src
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
@@ -114,10 +114,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
# forward declarations:
proc collectCT(gch: var TGcHeap)
proc IsOnStack*(p: pointer): bool {.noinline.}
proc isOnStack*(p: pointer): bool {.noinline.}
proc forAllChildren(cell: PCell, op: TWalkOp)
proc doOperation(p: pointer, op: TWalkOp)
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp)
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp)
# we need the prototype here for debugging purposes
proc prepareDealloc(cell: PCell) =
@@ -162,19 +162,19 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
if m != nil: forAllSlotsAux(dest, m, op)
of nkNone: sysAssert(false, "forAllSlotsAux")
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
var d = cast[TAddress](dest)
if dest == nil: return # nothing to do
if ntfNoRefs notin mt.flags:
case mt.Kind
case mt.kind
of tyRef, tyString, tySequence: # leaf:
doOperation(cast[ppointer](d)[], op)
doOperation(cast[PPointer](d)[], op)
of tyObject, tyTuple:
forAllSlotsAux(dest, mt.node, op)
of tyArray, tyArrayConstr, tyOpenArray:
for i in 0..(mt.size div mt.base.size)-1:
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
else: nil
else: discard
proc forAllChildren(cell: PCell, op: TWalkOp) =
gcAssert(cell != nil, "forAllChildren: 1")
@@ -184,7 +184,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
if marker != nil:
marker(cellToUsr(cell), op.int)
else:
case cell.typ.Kind
case cell.typ.kind
of tyRef: # common case
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
of tySequence:
@@ -194,7 +194,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
for i in 0..s.len-1:
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
GenericSeqSize), cell.typ.base, op)
else: nil
else: discard
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
# generates a new object and sets its reference counter to 0
@@ -466,7 +466,7 @@ else:
sp = sp +% sizeof(pointer)*8
# last few entries:
while sp <=% max:
gcMark(gch, cast[ppointer](sp)[])
gcMark(gch, cast[PPointer](sp)[])
sp = sp +% sizeof(pointer)
# ----------------------------------------------------------------------------
@@ -505,7 +505,7 @@ when not defined(useNimRtl):
else:
dec(gch.recGcLock)
proc GC_setStrategy(strategy: TGC_Strategy) = nil
proc GC_setStrategy(strategy: TGC_Strategy) = discard
proc GC_enableMarkAndSweep() =
gch.cycleThreshold = InitialThreshold

View File

@@ -195,16 +195,31 @@ else:
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
when useWinUnicode:
proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
flags: DWORD): int32 {.
importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall.}
proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
security: pointer=nil): int32 {.
importc:"CreateHardLinkW", dynlib: "kernel32", stdcall.}
else:
proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring,
flags: DWORD): int32 {.
importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall.}
proc createHardLinkA*(lpFileName, lpExistingFileName: cstring,
security: pointer=nil): int32 {.
importc:"CreateHardLinkA", dynlib: "kernel32", stdcall.}
const
FILE_ATTRIBUTE_ARCHIVE* = 32'i32
FILE_ATTRIBUTE_COMPRESSED* = 2048'i32
@@ -212,6 +227,7 @@ const
FILE_ATTRIBUTE_DIRECTORY* = 16'i32
FILE_ATTRIBUTE_HIDDEN* = 2'i32
FILE_ATTRIBUTE_READONLY* = 1'i32
FILE_ATTRIBUTE_REPARSE_POINT* = 1024'i32
FILE_ATTRIBUTE_SYSTEM* = 4'i32
FILE_ATTRIBUTE_TEMPORARY* = 256'i32
@@ -284,7 +300,7 @@ else:
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".}

View File

@@ -109,11 +109,12 @@ type
cur*: cstring
dataSize*: cint
finished*: TBsonBool
stack*: array[0..32 - 1, cint]
ownsData*: TBsonBool
err*: cint
stackSize*: cint
stackPos*: cint
err*: cint ## Bitfield representing errors or warnings on this buffer
errstr*: cstring ## A string representation of the most recent error
## or warning.
stackPtr*: ptr csize
stack*: array[0..32 - 1, csize]
TDate* = int64
@@ -141,6 +142,7 @@ proc print*(TBson: cstring, depth: cint) {.stdcall,
importc: "bson_print_raw", dynlib: bsondll.}
## Print a string representation of a BSON object up to `depth`.
proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data",
dynlib: bsondll.}
## Return a pointer to the raw buffer stored by this bson object.
@@ -590,19 +592,30 @@ type
hosts*: ptr THostPort ## List of host/ports given by the replica set
name*: cstring ## Name of the replica set.
primary_connected*: TBsonBool ## Primary node connection status.
TWriteConcern*{.pure, final.} = object ## mongo_write_concern
w*: cint
wtimeout*: cint
j*: cint
fsync*: cint
mode*: cstring
cmd*: TBSon
TMongo*{.pure, final.} = object ## mongo
primary*: ptr THostPort ## Primary connection info.
replset*: ptr TReplSet ## replset object if connected to a replica set.
sock*: cint ## Socket file descriptor.
flags*: cint ## Flags on this connection object.
conn_timeout_ms*: cint ## Connection timeout in milliseconds.
op_timeout_ms*: cint ## Read and write timeout in milliseconds.
connected*: TBsonBool ## Connection status.
err*: TError ## Most recent driver error code.
errstr*: array[0..128 - 1, char] ## String version of most recent driver error code.
lasterrcode*: cint ## getlasterror code given by the server on error.
lasterrstr*: cstring ## getlasterror string generated by server.
primary*: ptr THostPort ## Primary connection info.
replset*: ptr TReplSet ## replset object if connected to a replica set.
sock*: cint ## Socket file descriptor.
flags*: cint ## Flags on this connection object.
conn_timeout_ms*: cint ## Connection timeout in milliseconds.
op_timeout_ms*: cint ## Read and write timeout in milliseconds.
max_bson_size*: cint ## Largest BSON object allowed on this connection.
connected*: TBsonBool ## Connection status.
write_concern*: TWriteConcern ## The default write concern.
err*: TError ## Most recent driver error code.
errcode*: cint ## Most recent errno or WSAGetLastError().
errstr*: array[0..128 - 1, char] ## String version of most recent driver error code.
lasterrcode*: cint ## getlasterror code given by the server on error.
lasterrstr*: array[0..128 - 1, char] ## getlasterror string generated by server.
TCursor*{.pure, final.} = object ## cursor
reply*: ptr TReply ## reply is owned by cursor
@@ -654,7 +667,11 @@ proc init*(conn: var TMongo){.stdcall, importc: "mongo_init", dynlib: mongodll.}
proc connect*(conn: var TMongo, host: cstring = defaultHost,
port: cint = defaultPort): cint {.stdcall,
importc: "mongo_connect", dynlib: mongodll.}
importc: "mongo_connect", dynlib: mongodll, deprecated.}
## Connect to a single MongoDB server.
proc client*(conn: var TMongo, host: cstring = defaultHost,
port: cint = defaultPort): cint {.stdcall,
importc: "mongo_client", dynlib: mongodll.}
## Connect to a single MongoDB server.
proc replsetInit*(conn: var TMongo, name: cstring){.stdcall,
@@ -714,7 +731,8 @@ proc destroy*(conn: var TMongo){.stdcall, importc: "mongo_destroy",
## You must always call this function when finished with the connection
## object.
proc insert*(conn: var TMongo, ns: cstring, data: var TBson): cint{.stdcall,
proc insert*(conn: var TMongo, ns: cstring, data: var TBson,
custom_write_concern: ptr TWriteConcern): cint{.stdcall,
importc: "mongo_insert", dynlib: mongodll, discardable.}
## Insert a BSON document into a MongoDB server. This function
## will fail if the supplied BSON struct is not UTF-8 or if

View File

@@ -268,14 +268,22 @@ proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSS
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.}
when not defined(windows):
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
dynlib: DLLSSLName, importc.}
proc CRYPTO_malloc_init*() =
when not defined(windows):
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when True:
nil
proc SSL_CTX_ctrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int =
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
when true:
discard
else:
proc SslCtxSetCipherList*(arg0: PSSL_CTX, str: cstring): cInt{.cdecl,
dynlib: DLLSSLName, importc.}
@@ -288,7 +296,6 @@ else:
proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int
proc SSLSetMode*(s: PSSL, mode: int): int
proc SSLCTXGetMode*(ctx: PSSL_CTX): int
proc SSLGetMode*(s: PSSL): int
@@ -417,15 +424,12 @@ else:
enc: cInt){.cdecl, dynlib: DLLUtilName, importc.}
# implementation
proc SSLCTXSetMode(ctx: PSSL_CTX, mode: int): int =
Result = SslCTXCtrl(ctx, SSL_CTRL_MODE, mode, nil)
proc SSLSetMode(s: PSSL, mode: int): int =
Result = SSLctrl(s, SSL_CTRL_MODE, mode, nil)
result = SSLctrl(s, SSL_CTRL_MODE, mode, nil)
proc SSLCTXGetMode(ctx: PSSL_CTX): int =
Result = SSLCTXctrl(ctx, SSL_CTRL_MODE, 0, nil)
result = SSLCTXctrl(ctx, SSL_CTRL_MODE, 0, nil)
proc SSLGetMode(s: PSSL): int =
Result = SSLctrl(s, SSL_CTRL_MODE, 0, nil)
result = SSLctrl(s, SSL_CTRL_MODE, 0, nil)

View File

@@ -61,5 +61,5 @@ allowing you to create commercial applications.
Read copying.txt for more details.
Copyright (c) 2004-2013 Andreas Rumpf.
Copyright (c) 2004-2014 Andreas Rumpf.
All rights reserved.

View File

@@ -61,5 +61,5 @@ allowing you to create commercial applications.
Read copying.txt for more details.
Copyright (c) 2004-2013 Andreas Rumpf.
Copyright (c) 2004-2014 Andreas Rumpf.
All rights reserved.

View File

@@ -0,0 +1,22 @@
import tables
doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table"
var tbl1 = initTable[int, int]()
tbl1.add(1,1)
tbl1.add(2,2)
doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table"
type
TElem = object
foo: int
bar: string
let
elem1 = TElem(foo: 1, bar: "bar")
elem2 = TElem(foo: 2, bar: "baz")
var tbl2 = initTable[string, TElem]()
tbl2.add("bar", elem1)
tbl2.add("baz", elem2)
doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table"

View File

@@ -1,5 +1,6 @@
discard """
output: '''(bar: bar)
output: '''24
(bar: bar)
1244
6
abcdefghijklmnopqrstuvwxyz
@@ -8,6 +9,10 @@ abcdefghijklmnopqrstuvwxyz
import strutils
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
echo fac4
when true:
proc test(foo: proc (x, y: int): bool) =
echo foo(5, 5)
@@ -69,3 +74,15 @@ proc semiProblem() =
if false: echo "aye"; echo "indeed"
semiProblem()
# bug #844
import json
proc parseResponse(): PJsonNode =
result = % { "key1": % { "key2": % "value" } }
for key, val in result["key1"]:
var excMsg = key & "("
if (var n=result["key2"]; n != nil):
excMsg &= n.str
raise newException(ESynch, excMsg)

View File

@@ -6,7 +6,7 @@ when defined(GC_setMaxPause):
GC_setMaxPause 2_000
type
TExpr = object ## abstract base class for an expression
TExpr = object {.inheritable.} ## abstract base class for an expression
PLiteral = ref TLiteral
TLiteral = object of TExpr
x: int

25
tests/gc/gcleak5.nim Normal file
View File

@@ -0,0 +1,25 @@
discard """
output: "success"
"""
import os, times
proc main =
var i = 0
for ii in 0..50_000:
#while true:
var t = getTime()
var g = t.getGMTime()
#echo isOnStack(addr g)
if i mod 100 == 0:
let om = getOccupiedMem()
#echo "memory: ", om
if om > 100_000: quit "leak"
inc(i)
sleep(1)
echo "success"
main()

View File

@@ -6,6 +6,18 @@ var a: PA[string]
new(a)
a.field = "some string"
proc someOther[T](len: string): seq[T] = discard
proc someOther[T](len: int): seq[T] = echo "we"
proc foo[T](x: T) =
var s = someOther[T](34)
#newSeq[T](34)
foo 23
when false:
# Compiles unless you use var a: PA[string]
type

View File

@@ -22,11 +22,11 @@ proc factory2(a, b: int): iterator (): int =
yield x
inc x
let foo = factory 1, 4
let foo = factory(1, 4)
for f in foo():
echo f
let foo2 = factory2 1,2
let foo2 = factory2(1,2)
for f in foo2(): echo f

View File

@@ -0,0 +1,29 @@
discard """
output: '''a[0]: 42
a[1]: 45
x: some string'''
"""
import macros
macro debug(n: varargs[expr]): stmt =
# `n` is a Nimrod AST that contains the whole macro invocation
# this macro returns a list of statements:
result = newNimNode(nnkStmtList, n)
# iterate over any argument that is passed to this macro:
for i in 0..n.len-1:
# add a call to the statement list that writes the expression;
# `toStrLit` converts an AST to its string representation:
add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i])))
# add a call to the statement list that writes ": "
add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": ")))
# add a call to the statement list that writes the expressions value:
add(result, newCall("writeln", newIdentNode("stdout"), n[i]))
var
a: array [0..10, int]
x = "some string"
a[0] = 42
a[1] = 45
debug(a[0], a[1], x)

View File

@@ -2,7 +2,7 @@
import macros
template plus(a, b: expr): expr =
template plus(a, b: expr): expr {.dirty} =
a + b
macro call(e: expr): expr =

View File

@@ -7,7 +7,7 @@ proc dumpit(n: PNimrodNode): string {.compileTime.} =
result = $n.kind
add(result, "(")
case n.kind
of nnkEmpty: nil # same as nil node in this representation
of nnkEmpty: discard # same as nil node in this representation
of nnkNilLit: add(result, "nil")
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)

View File

@@ -1,10 +1,8 @@
discard """
file: "tmacrogenerics.nim"
msg: '''
instantiation 1 with int and float
instantiation 2 with float and string
instantiation 3 with string and string
counter: 3
instantiation 1 with typedesc and typedesc
counter: 1
'''
output: "int\nfloat\nint\nstring"
"""

View File

@@ -1,3 +1,7 @@
discard """
disabled: true
"""
import macros, typetraits
macro checkType(ex, expected: expr): stmt {.immediate.} =

View File

@@ -1,5 +1,5 @@
discard """
out: '''HELLO WORLD'''
output: '''HELLO WORLD'''
"""
import macros, strutils

View File

@@ -9,7 +9,7 @@ proc concat(strings: varargs[string]): string =
result = newString(0)
for s in items(strings): result.add(s)
template ProcessInterpolations(e: expr) =
template processInterpolations(e: expr) =
var s = e[1].strVal
for f in interpolatedFragments(s):
case f.kind
@@ -35,7 +35,7 @@ macro formatStyleInterpolation(e: expr): expr =
proc addDollar() =
formatString.add("$$")
ProcessInterpolations(e)
processInterpolations(e)
result = parseExpr("\"x\" % [y]")
result[1].strVal = formatString
@@ -50,7 +50,7 @@ macro concatStyleInterpolation(e: expr): expr =
proc addExpr(e: PNimrodNode) = args.add(e)
proc addDollar() = args.add(newStrLitNode"$")
ProcessInterpolations(e)
processInterpolations(e)
result = newCall("concat", args)

View File

@@ -1,12 +1,23 @@
discard """
output: "12"
output: '''140
5-120-120
359'''
"""
#import math
proc optarg(x:int, y:int = 0):int = x + 3 * y
proc singlearg(x:int):int = 20*x
echo optarg 1, singlearg 2
proc foo(x: int): int = x-1
proc foo(x, y: int): int = x-y
let x = foo 7.foo, # comment here
foo(1, foo 8)
# 12 = 6 - -6
echo x
let x = optarg foo 7.foo
let y = singlearg foo(1, foo 8)
let z = singlearg 1.foo foo 8
echo x, y, z
let a = [2,4,8].map do (d:int) -> int: d + 1
echo a[0], a[1], a[2]

View File

@@ -0,0 +1,15 @@
discard """
output: ''''''
"""
import unittest
import sets
doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "equivalent or subset")
doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3]), "equivalent or subset")
doAssert((not(toSet(@[1,2,3]) <= toSet(@[1,2]))), "equivalent or subset")
doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "strict subset")
doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2,3]))), "strict subset")
doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2]))), "strict subset")
doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2,3,4]))), "==")
doAssert(toSet(@[1,2,3]) == toSet(@[1,2,3]), "==")
doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2]))), "==")

View File

@@ -1,238 +0,0 @@
#
#
# Nimrod Tester
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Include for the tester that contains test suites that test special features
## of the compiler.
# included from tester.nim
# ---------------- ROD file tests ---------------------------------------------
const
rodfilesDir = "tests/rodfiles"
nimcacheDir = rodfilesDir / "nimcache"
proc delNimCache() =
try:
removeDir(nimcacheDir)
except EOS:
echo "[Warning] could not delete: ", nimcacheDir
proc runRodFiles(r: var TResults, options: string) =
template test(filename: expr): stmt =
runSingleTest(r, rodfilesDir / filename, options)
delNimCache()
# test basic recompilation scheme:
test "hallo"
test "hallo"
# test incremental type information:
test "hallo2"
delNimCache()
# test type converters:
test "aconv"
test "bconv"
delNimCache()
# test G, A, B example from the documentation; test init sections:
test "deada"
test "deada2"
delNimCache()
# test method generation:
test "bmethods"
test "bmethods2"
delNimCache()
# test generics:
test "tgeneric1"
test "tgeneric2"
delNimCache()
proc compileRodFiles(r: var TResults, options: string) =
template test(filename: expr): stmt =
compileSingleTest(r, rodfilesDir / filename, options)
delNimCache()
# test DLL interfacing:
test "gtkex1"
test "gtkex2"
delNimCache()
# --------------------- DLL generation tests ----------------------------------
proc safeCopyFile(src, dest: string) =
try:
copyFile(src, dest)
except EOS:
echo "[Warning] could not copy: ", src, " to ", dest
proc runBasicDLLTest(c, r: var TResults, options: string) =
compileSingleTest c, "lib/nimrtl.nim", options & " --app:lib -d:createNimRtl"
compileSingleTest c, "tests/dll/server.nim",
options & " --app:lib -d:useNimRtl"
when defined(Windows):
# windows looks in the dir of the exe (yay!):
var nimrtlDll = DynlibFormat % "nimrtl"
safeCopyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
else:
# posix relies on crappy LD_LIBRARY_PATH (ugh!):
var libpath = getenv"LD_LIBRARY_PATH".string
if peg"\i '/nimrod' (!'/')* '/lib'" notin libpath:
echo "[Warning] insufficient LD_LIBRARY_PATH"
var serverDll = DynlibFormat % "server"
safeCopyFile("tests/dll" / serverDll, "lib" / serverDll)
runSingleTest r, "tests/dll/client.nim", options & " -d:useNimRtl"
proc runDLLTests(r: var TResults, options: string) =
# dummy compile result:
var c = initResults()
runBasicDLLTest c, r, options
runBasicDLLTest c, r, options & " -d:release"
runBasicDLLTest c, r, options & " --gc:boehm"
runBasicDLLTest c, r, options & " -d:release --gc:boehm"
proc compileDLLTests(r: var TResults, options: string) =
# dummy run result:
var c = initResults()
runBasicDLLTest r, c, options
runBasicDLLTest r, c, options & " -d:release"
runBasicDLLTest r, c, options & " --gc:boehm"
runBasicDLLTest r, c, options & " -d:release --gc:boehm"
# ------------------------------ GC tests -------------------------------------
proc runGcTests(r: var TResults, options: string) =
template test(filename: expr): stmt =
runSingleTest(r, "tests/gc" / filename, options)
runSingleTest(r, "tests/gc" / filename, options & " -d:release")
runSingleTest(r, "tests/gc" / filename, options &
" -d:release -d:useRealtimeGC")
runSingleTest(r, "tests/gc" / filename, options &
" --gc:markAndSweep")
runSingleTest(r, "tests/gc" / filename, options &
" -d:release --gc:markAndSweep")
test "gcbench"
test "gcleak"
test "gcleak2"
test "gctest"
test "gcleak3"
test "weakrefs"
test "cycleleak"
test "closureleak"
# ------------------------- threading tests -----------------------------------
proc runThreadTests(r: var TResults, options: string) =
template test(filename: expr): stmt =
runSingleTest(r, "tests/threads" / filename, options)
runSingleTest(r, "tests/threads" / filename, options & " -d:release")
runSingleTest(r, "tests/threads" / filename, options & " --tlsEmulation:on")
test "tactors"
test "tactors2"
test "threadex"
# deactivated because output capturing still causes problems sometimes:
#test "trecursive_actor"
#test "threadring"
#test "tthreadanalysis"
#test "tthreadsort"
proc rejectThreadTests(r: var TResults, options: string) =
rejectSingleTest(r, "tests/threads/tthreadanalysis2", options)
rejectSingleTest(r, "tests/threads/tthreadanalysis3", options)
rejectSingleTest(r, "tests/threads/tthreadheapviolation1", options)
# ------------------------- IO tests ------------------------------------------
proc runIOTests(r: var TResults, options: string) =
# We need readall_echo to be compiled for this test to run.
# dummy compile result:
var c = initResults()
compileSingleTest(c, "tests/system/helpers/readall_echo", options)
runSingleTest(r, "tests/system/io", options)
# ------------------------- debugger tests ------------------------------------
proc compileDebuggerTests(r: var TResults, options: string) =
compileSingleTest(r, "tools/nimgrep", options &
" --debugger:on")
# ------------------------- JS tests ------------------------------------------
proc runJsTests(r: var TResults, options: string) =
template test(filename: expr): stmt =
runSingleTest(r, filename, options & " -d:nodejs", targetJS)
runSingleTest(r, filename, options & " -d:nodejs -d:release", targetJS)
for t in os.walkFiles("tests/js/t*.nim"):
test(t)
for testfile in ["texceptions", "texcpt1", "texcsub", "tfinally",
"tfinally2", "tfinally3", "tactiontable", "tmultim1",
"tmultim3", "tmultim4"]:
test "tests/run/" & testfile & ".nim"
# ------------------------- register special tests here -----------------------
proc runSpecialTests(r: var TResults, options: string) =
runRodFiles(r, options)
#runDLLTests(r, options)
runGCTests(r, options)
runThreadTests(r, options & " --threads:on")
runIOTests(r, options)
for t in os.walkFiles("tests/patterns/t*.nim"):
runSingleTest(r, t, options)
for t in ["lib/packages/docutils/highlite"]:
runSingleTest(r, t, options)
proc rejectSpecialTests(r: var TResults, options: string) =
rejectThreadTests(r, options)
proc findMainFile(dir: string): string =
# finds the file belonging to ".nimrod.cfg"; if there is no such file
# it returns the some ".nim" file if there is only one:
const cfgExt = ".nimrod.cfg"
result = ""
var nimFiles = 0
for kind, file in os.walkDir(dir):
if kind == pcFile:
if file.endsWith(cfgExt): return file[.. -(cfgExt.len+1)] & ".nim"
elif file.endsWith(".nim"):
if result.len == 0: result = file
inc nimFiles
if nimFiles != 1: result.setlen(0)
proc compileManyLoc(r: var TResults, options: string) =
for kind, dir in os.walkDir("tests/manyloc"):
if kind == pcDir:
let mainfile = findMainFile(dir)
if mainfile != ".nim":
compileSingleTest(r, mainfile, options)
proc compileSpecialTests(r: var TResults, options: string) =
compileRodFiles(r, options)
compileSingleTest(r, "compiler/c2nim/c2nim.nim", options)
compileSingleTest(r, "compiler/pas2nim/pas2nim.nim", options)
compileDLLTests(r, options)
compileDebuggerTests(r, options)
compileManyLoc(r, options)
#var given = callCompiler("nimrod i", "nimrod i", options)
#r.addResult("nimrod i", given.msg, if given.err: reFailure else: reSuccess)
#if not given.err: inc(r.passed)

View File

@@ -0,0 +1,8 @@
import algorithm
doAssert product[int](newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input"
doAssert product[int](@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input"
doAssert product(@[@[1,2]]) == @[@[1,2]], "a simple case of one element"
doAssert product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]], "two elements"
doAssert product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]], "three elements"
doAssert product(@[@[1,2], @[]]) == newSeq[seq[int]](), "two elements, but one empty"

View File

@@ -0,0 +1,17 @@
discard """
output: '''immediate'''
"""
# Test that immediate templates are preferred over non-immediate templates
template foo(a, b: expr) = echo "foo expr"
template foo(a, b: int) = echo "foo int"
template foo(a, b: float) = echo "foo float"
template foo(a, b: string) = echo "foo string"
template foo(a, b: expr) {.immediate.} = echo "immediate"
template foo(a, b: bool) = echo "foo bool"
template foo(a, b: char) = echo "foo char"
foo(undeclaredIdentifier, undeclaredIdentifier2)

View File

@@ -49,10 +49,10 @@ proc createDb() =
# """, [])
type
MachineId = distinct int64
MachineId* = distinct int64
CommitId = distinct int64
proc `$`(id: MachineId): string {.borrow.}
proc `$`*(id: MachineId): string {.borrow.}
proc `$`(id: CommitId): string {.borrow.}
var
@@ -61,7 +61,7 @@ var
proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip
proc getMachine: MachineId =
proc getMachine*(db: TDbConn): MachineId =
var name = "hostname"()
if name.len == 0:
name = when defined(posix): getenv"HOSTNAME".string
@@ -76,7 +76,7 @@ proc getMachine: MachineId =
result = db.insertId(sql"insert into Machine(name, os, cpu) values (?,?,?)",
name, system.hostOS, system.hostCPU).MachineId
proc getCommit: CommitId =
proc getCommit(db: TDbConn): CommitId =
const commLen = "commit ".len
let hash = "git log -n 1"()[commLen..commLen+10]
let branch = "git symbolic-ref --short HEAD"()
@@ -115,7 +115,7 @@ proc open*() =
db = open(connection="testament.db", user="testament", password="",
database="testament")
createDb()
thisMachine = getMachine()
thisCommit = getCommit()
thisMachine = getMachine(db)
thisCommit = getCommit(db)
proc close*() = close(db)

View File

@@ -123,9 +123,14 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
test "gcleak2"
test "gctest"
test "gcleak3"
test "gcleak4"
test "gcleak5"
test "weakrefs"
test "cycleleak"
test "closureleak"
test "refarrayleak"
test "stackrefleak"
# ------------------------- threading tests -----------------------------------

View File

@@ -159,3 +159,22 @@ proc generateHtml*(filename: string, commit: int) =
outfile.write(HtmlEnd)
close(db)
close(outfile)
proc generateJson*(filename: string, commit: int) =
const selRow = """select count(*),
sum(result = 'reSuccess'),
sum(result = 'reIgnored')
from TestResult
where [commit] = ? and machine = ?
order by category"""
var db = open(connection="testament.db", user="testament", password="",
database="testament")
let lastCommit = db.getCommit(commit)
var outfile = open(filename, fmWrite)
let data = db.getRow(sql(selRow), lastCommit, $backend.getMachine(db))
outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#}""" % data)
close(db)
close(outfile)

View File

@@ -208,20 +208,6 @@ proc makeTest(test, options: string, cat: Category, action = actionCompile,
include categories
proc toJson(res: TResults): PJsonNode =
result = newJObject()
result["total"] = newJInt(res.total)
result["passed"] = newJInt(res.passed)
result["skipped"] = newJInt(res.skipped)
proc outputJson(reject, compile, run: TResults) =
var doc = newJObject()
doc["reject"] = toJson(reject)
doc["compile"] = toJson(compile)
doc["run"] = toJson(run)
var s = pretty(doc)
writeFile(jsonFile, s)
# proc runCaasTests(r: var TResults) =
# for test, output, status, mode in caasTestsRunner():
# r.addResult(test, "", output & "-> " & $mode,
@@ -259,6 +245,7 @@ proc main() =
var commit = 0
discard parseInt(p.cmdLineRest.string, commit)
generateHtml(resultsFile, commit)
generateJson(jsonFile, commit)
else:
quit usage

View File

@@ -1,457 +0,0 @@
#
#
# Nimrod Tester
# (c) Copyright 2013 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This program verifies Nimrod against the testcases.
import
parseutils, strutils, pegs, os, osproc, streams, parsecfg, browsers, json,
marshal, cgi, parseopt #, caas
const
cmdTemplate = r"nimrod cc --hints:on $# $#"
resultsFile = "testresults.html"
jsonFile = "testresults.json"
Usage = "usage: tester [--print] " &
"reject|compile|run|" &
"merge|special|rodfiles| [nimrod options]\n" &
" or: tester test|comp|rej singleTest"
type
TTestAction = enum
actionCompile, actionRun, actionReject
TResultEnum = enum
reNimrodcCrash, # nimrod compiler seems to have crashed
reMsgsDiffer, # error messages differ
reFilesDiffer, # expected and given filenames differ
reLinesDiffer, # expected and given line numbers differ
reOutputsDiffer,
reExitcodesDiffer,
reInvalidPeg,
reCodegenFailure,
reCodeNotFound,
reExeNotFound,
reIgnored, # test is ignored
reSuccess # test was successful
TTarget = enum
targetC, targetCpp, targetObjC, targetJS
TSpec = object
action: TTestAction
file, cmd: string
outp: string
line, exitCode: int
msg: string
ccodeCheck: string
err: TResultEnum
substr: bool
TResults = object
total, passed, skipped: int
data: string
# ----------------------- Spec parser ----------------------------------------
when not defined(parseCfgBool):
# candidate for the stdlib:
proc parseCfgBool(s: string): bool =
case normalize(s)
of "y", "yes", "true", "1", "on": result = true
of "n", "no", "false", "0", "off": result = false
else: raise newException(EInvalidValue, "cannot interpret as a bool: " & s)
proc extractSpec(filename: string): string =
const tripleQuote = "\"\"\""
var x = readFile(filename).string
var a = x.find(tripleQuote)
var b = x.find(tripleQuote, a+3)
# look for """ only in the first section
if a >= 0 and b > a and a < 40:
result = x.substr(a+3, b-1).replace("'''", tripleQuote)
else:
#echo "warning: file does not contain spec: " & filename
result = ""
when not defined(nimhygiene):
{.pragma: inject.}
template parseSpecAux(fillResult: stmt) {.immediate.} =
var ss = newStringStream(extractSpec(filename))
var p {.inject.}: TCfgParser
open(p, ss, filename, 1)
while true:
var e {.inject.} = next(p)
case e.kind
of cfgEof: break
of cfgSectionStart, cfgOption, cfgError:
echo ignoreMsg(p, e)
of cfgKeyValuePair:
fillResult
close(p)
proc parseSpec(filename: string): TSpec =
result.file = filename
result.msg = ""
result.outp = ""
result.ccodeCheck = ""
result.cmd = cmdTemplate
parseSpecAux:
case normalize(e.key)
of "action":
case e.value.normalize
of "compile": result.action = actionCompile
of "run": result.action = actionRun
of "reject": result.action = actionReject
else: echo ignoreMsg(p, e)
of "file": result.file = e.value
of "line": discard parseInt(e.value, result.line)
of "output": result.outp = e.value
of "outputsub":
result.outp = e.value
result.substr = true
of "exitcode":
discard parseInt(e.value, result.exitCode)
of "errormsg", "msg": result.msg = e.value
of "disabled":
if parseCfgBool(e.value): result.err = reIgnored
of "cmd": result.cmd = e.value
of "ccodecheck": result.ccodeCheck = e.value
else: echo ignoreMsg(p, e)
# ----------------------------------------------------------------------------
let
pegLineError =
peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Error'/'Warning') ':' \s* {.*}"
pegOtherError = peg"'Error:' \s* {.*}"
pegSuccess = peg"'Hint: operation successful'.*"
pegOfInterest = pegLineError / pegOtherError
proc callCompiler(cmdTemplate, filename, options: string): TSpec =
let c = parseCmdLine(cmdTemplate % [options, filename])
var p = startProcess(command=c[0], args=c[1.. -1],
options={poStdErrToStdOut, poUseShell})
let outp = p.outputStream
var suc = ""
var err = ""
var x = newStringOfCap(120)
while outp.readLine(x.TaintedString) or running(p):
if x =~ pegOfInterest:
# `err` should contain the last error/warning message
err = x
elif x =~ pegSuccess:
suc = x
close(p)
result.msg = ""
result.file = ""
result.outp = ""
result.line = -1
if err =~ pegLineError:
result.file = extractFilename(matches[0])
result.line = parseInt(matches[1])
result.msg = matches[2]
elif err =~ pegOtherError:
result.msg = matches[0]
elif suc =~ pegSuccess:
result.err = reSuccess
proc initResults: TResults =
result.total = 0
result.passed = 0
result.skipped = 0
result.data = ""
proc readResults(filename: string): TResults =
result = marshal.to[TResults](readFile(filename).string)
proc writeResults(filename: string, r: TResults) =
writeFile(filename, $$r)
proc `$`(x: TResults): string =
result = ("Tests passed: $1 / $3 <br />\n" &
"Tests skipped: $2 / $3 <br />\n") %
[$x.passed, $x.skipped, $x.total]
proc colorResult(r: TResultEnum): string =
case r
of reIgnored: result = "<span style=\"color:fuchsia\">ignored</span>"
of reSuccess: result = "<span style=\"color:green\">yes</span>"
else: result = "<span style=\"color:red\">no</span>"
const
TableHeader4 = "<table border=\"1\"><tr><td>Test</td><td>Expected</td>" &
"<td>Given</td><td>Success</td></tr>\n"
TableHeader3 = "<table border=\"1\"><tr><td>Test</td>" &
"<td>Given</td><td>Success</td></tr>\n"
TableFooter = "</table>\n"
HtmlBegin = """<html>
<head>
<title>Test results</title>
<style type="text/css">
<!--""" & slurp("css/boilerplate.css") & "\n" &
slurp("css/style.css") &
"""-->
</style>
</head>
<body>"""
HtmlEnd = "</body></html>"
proc td(s: string): string =
result = s.substr(0, 200).XMLEncode
proc addResult(r: var TResults, test, expected, given: string,
success: TResultEnum) =
r.data.addf("<tr><td>$#</td><td>$#</td><td>$#</td><td>$#</td></tr>\n", [
XMLEncode(test), td(expected), td(given), success.colorResult])
proc addResult(r: var TResults, test, given: string,
success: TResultEnum) =
r.data.addf("<tr><td>$#</td><td>$#</td><td>$#</td></tr>\n", [
XMLEncode(test), td(given), success.colorResult])
proc listResults(reject, compile, run: TResults) =
var s = HtmlBegin
s.add("<h1>Tests to Reject</h1>\n")
s.add($reject)
s.add(TableHeader4 & reject.data & TableFooter)
s.add("<br /><br /><br /><h1>Tests to Compile</h1>\n")
s.add($compile)
s.add(TableHeader3 & compile.data & TableFooter)
s.add("<br /><br /><br /><h1>Tests to Run</h1>\n")
s.add($run)
s.add(TableHeader4 & run.data & TableFooter)
s.add(HtmlEnd)
writeFile(resultsFile, s)
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: string) =
if strip(expected.msg) notin strip(given.msg):
r.addResult(test, expected.msg, given.msg, reMsgsDiffer)
elif extractFilename(expected.file) != extractFilename(given.file) and
"internal error:" notin expected.msg:
r.addResult(test, expected.file, given.file, reFilesDiffer)
elif expected.line != given.line and expected.line != 0:
r.addResult(test, $expected.line, $given.line, reLinesDiffer)
else:
r.addResult(test, expected.msg, given.msg, reSuccess)
inc(r.passed)
proc rejectSingleTest(r: var TResults, test, options: string) =
let test = test.addFileExt(".nim")
var t = extractFilename(test)
inc(r.total)
echo t
var expected = parseSpec(test)
if expected.err == reIgnored:
r.addResult(t, "", "", reIgnored)
inc(r.skipped)
else:
var given = callCompiler(expected.cmd, test, options)
cmpMsgs(r, expected, given, t)
proc reject(r: var TResults, dir, options: string) =
## handle all the tests that the compiler should reject
for test in os.walkFiles(dir / "t*.nim"): rejectSingleTest(r, test, options)
proc codegenCheck(test, check, ext: string, given: var TSpec) =
if check.len > 0:
try:
let (path, name, ext2) = test.splitFile
echo path / "nimcache" / name.changeFileExt(ext)
let contents = readFile(path / "nimcache" / name.changeFileExt(ext)).string
if contents.find(check.peg) < 0:
given.err = reCodegenFailure
except EInvalidValue:
given.err = reInvalidPeg
except EIO:
given.err = reCodeNotFound
proc codegenChecks(test: string, expected: TSpec, given: var TSpec) =
codegenCheck(test, expected.ccodeCheck, ".c", given)
proc compile(r: var TResults, pattern, options: string) =
for test in os.walkFiles(pattern):
let t = extractFilename(test)
echo t
inc(r.total)
let expected = parseSpec(test)
if expected.err == reIgnored:
r.addResult(t, "", reIgnored)
inc(r.skipped)
else:
var given = callCompiler(expected.cmd, test, options)
if given.err == reSuccess:
codegenChecks(test, expected, given)
r.addResult(t, given.msg, given.err)
if given.err == reSuccess: inc(r.passed)
proc compileSingleTest(r: var TResults, test, options: string) =
# does not extract the spec because the file is not supposed to have any
let test = test.addFileExt(".nim")
let t = extractFilename(test)
inc(r.total)
echo t
let given = callCompiler(cmdTemplate, test, options)
r.addResult(t, given.msg, given.err)
if given.err == reSuccess: inc(r.passed)
proc runSingleTest(r: var TResults, test, options: string, target: TTarget) =
var test = test.addFileExt(".nim")
var t = extractFilename(test)
echo t
inc(r.total)
var expected = parseSpec(test)
if expected.err == reIgnored:
r.addResult(t, "", "", reIgnored)
inc(r.skipped)
else:
var given = callCompiler(expected.cmd, test, options)
if given.err != reSuccess:
r.addResult(t, "", given.msg, given.err)
else:
var exeFile: string
if target == targetC:
exeFile = changeFileExt(test, ExeExt)
else:
let (dir, file, ext) = splitFile(test)
exeFile = dir / "nimcache" / file & ".js"
if existsFile(exeFile):
var (buf, exitCode) = execCmdEx(
(if target==targetJS: "node " else: "") & exeFile)
if exitCode != expected.ExitCode:
r.addResult(t, "exitcode: " & $expected.ExitCode,
"exitcode: " & $exitCode, reExitCodesDiffer)
else:
if strip(buf.string) != strip(expected.outp):
if not (expected.substr and expected.outp in buf.string):
given.err = reOutputsDiffer
if given.err == reSuccess:
codeGenChecks(test, expected, given)
if given.err == reSuccess: inc(r.passed)
r.addResult(t, expected.outp, buf.string, given.err)
else:
r.addResult(t, expected.outp, "executable not found", reExeNotFound)
proc runSingleTest(r: var TResults, test, options: string) =
runSingleTest(r, test, options, targetC)
proc run(r: var TResults, dir, options: string) =
for test in os.walkFiles(dir / "t*.nim"): runSingleTest(r, test, options)
include specials
proc compileExample(r: var TResults, pattern, options: string) =
for test in os.walkFiles(pattern): compileSingleTest(r, test, options)
proc toJson(res: TResults): PJsonNode =
result = newJObject()
result["total"] = newJInt(res.total)
result["passed"] = newJInt(res.passed)
result["skipped"] = newJInt(res.skipped)
proc outputJSON(reject, compile, run: TResults) =
var doc = newJObject()
doc["reject"] = toJson(reject)
doc["compile"] = toJson(compile)
doc["run"] = toJson(run)
var s = pretty(doc)
writeFile(jsonFile, s)
# proc runCaasTests(r: var TResults) =
# for test, output, status, mode in caasTestsRunner():
# r.addResult(test, "", output & "-> " & $mode,
# if status: reSuccess else: reOutputsDiffer)
proc main() =
os.putenv "NIMTEST_NO_COLOR", "1"
os.putenv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES"
const
compileJson = "compile.json"
runJson = "run.json"
rejectJson = "reject.json"
var optPrintResults = false
var p = initOptParser()
p.next()
if p.kind == cmdLongoption:
case p.key.string
of "print": optPrintResults = true
else: quit usage
p.next()
if p.kind != cmdArgument: quit usage
var action = p.key.string.normalize
p.next()
var r = initResults()
case action
of "reject":
reject(r, "tests/reject", p.cmdLineRest.string)
rejectSpecialTests(r, p.cmdLineRest.string)
writeResults(rejectJson, r)
of "compile":
compile(r, "tests/compile/t*.nim", p.cmdLineRest.string)
compile(r, "tests/ccg/t*.nim", p.cmdLineRest.string)
compile(r, "tests/js.nim", p.cmdLineRest.string)
compileExample(r, "lib/pure/*.nim", p.cmdLineRest.string)
compileExample(r, "examples/*.nim", p.cmdLineRest.string)
compileExample(r, "examples/gtk/*.nim", p.cmdLineRest.string)
compileExample(r, "examples/talk/*.nim", p.cmdLineRest.string)
compileSpecialTests(r, p.cmdLineRest.string)
writeResults(compileJson, r)
of "run":
run(r, "tests/run", p.cmdLineRest.string)
runSpecialTests(r, p.cmdLineRest.string)
writeResults(runJson, r)
of "special":
runSpecialTests(r, p.cmdLineRest.string)
# runCaasTests(r)
writeResults(runJson, r)
of "rodfiles":
runRodFiles(r, p.cmdLineRest.string)
writeResults(runJson, r)
of "js":
if existsFile(runJSon):
r = readResults(runJson)
runJsTests(r, p.cmdLineRest.string)
writeResults(runJson, r)
of "merge":
var rejectRes = readResults(rejectJson)
var compileRes = readResults(compileJson)
var runRes = readResults(runJson)
listResults(rejectRes, compileRes, runRes)
outputJSON(rejectRes, compileRes, runRes)
of "dll":
runDLLTests r, p.cmdLineRest.string
of "gc":
runGCTests(r, p.cmdLineRest.string)
of "test":
if p.kind != cmdArgument: quit usage
var testFile = p.key.string
p.next()
runSingleTest(r, testFile, p.cmdLineRest.string)
of "comp", "rej":
if p.kind != cmdArgument: quit usage
var testFile = p.key.string
p.next()
if peg"'/reject/'" in testFile or action == "rej":
rejectSingleTest(r, testFile, p.cmdLineRest.string)
elif peg"'/compile/'" in testFile or action == "comp":
compileSingleTest(r, testFile, p.cmdLineRest.string)
else:
runSingleTest(r, testFile, p.cmdLineRest.string)
else:
quit usage
if optPrintResults: echo r, r.data
if paramCount() == 0:
quit usage
main()

View File

@@ -1,9 +1,10 @@
version 0.9.4
=============
- better debugging support for writes to locations
- document new templating symbol binding rules
- fix eval in macros.nim
- fix GC issues
- fix macros\tstringinterp.nim
- test and fix showoff
- fix closure iterators
Bugs
@@ -16,7 +17,6 @@ Bugs
- compilation of niminst takes way too long. looks like a regression
- docgen: sometimes effects are listed twice
- 'result' is not properly cleaned for NRVO --> use uninit checking instead
- sneaking with qualifiedLookup() is really broken!
- blocks can "export" an identifier but the CCG generates {} for them ...
- osproc execProcesses can deadlock if all processes fail (as experienced
in c++ mode)
@@ -25,6 +25,9 @@ Bugs
version 0.9.x
=============
- implement 'union' and 'bits' pragmas
- fix closures
- test and fix exception handling
- ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC

View File

@@ -62,7 +62,7 @@ srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits"
srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
srcdoc2: "packages/docutils/rstgen"
srcdoc2: "packages/docutils/rstgen;pure/logging"
webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"