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

This commit is contained in:
Miguel 2014-02-10 01:20:42 +04:00
commit eb9964fbbb
96 changed files with 3070 additions and 1912 deletions

View file

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

View file

@ -561,14 +561,31 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
proc strTableRawInsert(data: var TSymSeq, n: PSym) = proc strTableRawInsert(data: var TSymSeq, n: PSym) =
var h: THash = n.name.h and high(data) var h: THash = n.name.h and high(data)
while data[h] != nil: if sfImmediate notin n.flags:
if data[h] == n: # fast path:
# allowed for 'export' feature: while data[h] != nil:
#InternalError(n.info, "StrTableRawInsert: " & n.name.s) if data[h] == n:
return # allowed for 'export' feature:
h = nextTry(h, high(data)) #InternalError(n.info, "StrTableRawInsert: " & n.name.s)
assert(data[h] == nil) return
data[h] = n 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) = proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h 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, of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
tyInt..tyUInt64, tyRange, tyVar: tyInt..tyUInt64, tyRange, tyVar:
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src)) 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) = proc getDestLoc(p: BProc, d: var TLoc, typ: PType) =
if d.k == locNone: getTemp(p, typ, d) 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) app(tmp2.r, field.loc.r)
tmp2.k = locTemp tmp2.k = locTemp
tmp2.t = field.loc.t tmp2.t = field.loc.t
tmp2.s = OnHeap tmp2.s = if isRef: OnHeap else: OnStack
tmp2.heapRoot = tmp.r tmp2.heapRoot = tmp.r
expr(p, it.sons[1], tmp2) expr(p, it.sons[1], tmp2)
if d.k == locNone: if d.k == locNone:

View file

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

View file

@ -116,7 +116,8 @@ type
TDep = tuple[e: PEnv, field: PSym] TDep = tuple[e: PEnv, field: PSym]
TEnv {.final.} = object of TObject TEnv {.final.} = object of TObject
attachedNode: PNode attachedNode: PNode
createdVar: PSym # if != nil it is a used environment createdVar: PSym # if != nil it is a used environment
createdVarComesFromIter: bool
capturedVars: seq[PSym] # captured variables in this environment capturedVars: seq[PSym] # captured variables in this environment
deps: seq[TDep] # dependencies deps: seq[TDep] # dependencies
up: PEnv up: PEnv
@ -571,7 +572,14 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode =
# maybe later: (sfByCopy in local.flags) # maybe later: (sfByCopy in local.flags)
# add ``env.param = param`` # add ``env.param = param``
result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info))
idNodeTablePut(o.localsToAccess, local, fieldAccess) # it can happen that we already captured 'local' in some other environment
# then we capture by copy for now. This is not entirely correct but better
# than nothing:
let existing = idNodeTableGet(o.localsToAccess, local)
if existing.isNil:
idNodeTablePut(o.localsToAccess, local, fieldAccess)
else:
result.add(newAsgnStmt(fieldAccess, existing, env.info))
# add support for 'up' references: # add support for 'up' references:
for e, field in items(scope.deps): for e, field in items(scope.deps):
# add ``env.up = env2`` # add ``env.up = env2``
@ -584,14 +592,19 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
proc generateIterClosureCreation(o: POuterContext; env: PEnv; proc generateIterClosureCreation(o: POuterContext; env: PEnv;
scope: PNode): PSym = scope: PNode): PSym =
result = newClosureCreationVar(o, env) if env.createdVarComesFromIter or env.createdVar.isNil:
let cc = rawClosureCreation(o, env, result) # we have to create a new closure:
var insertPoint = scope.sons[0] result = newClosureCreationVar(o, env)
if insertPoint.kind == nkEmpty: scope.sons[0] = cc let cc = rawClosureCreation(o, env, result)
var insertPoint = scope.sons[0]
if insertPoint.kind == nkEmpty: scope.sons[0] = cc
else:
assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList
for x in cc: insertPoint.add(x)
if env.createdVar == nil: env.createdVar = result
else: else:
assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList result = env.createdVar
for x in cc: insertPoint.add(x) env.createdVarComesFromIter = true
if env.createdVar == nil: env.createdVar = result
proc interestingIterVar(s: PSym): bool {.inline.} = proc interestingIterVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
@ -637,6 +650,22 @@ proc outerProcSons(o: POuterContext, n: PNode) =
let x = transformOuterProc(o, n.sons[i]) let x = transformOuterProc(o, n.sons[i])
if x != nil: n.sons[i] = x 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 = proc transformOuterProc(o: POuterContext, n: PNode): PNode =
if n == nil: return nil if n == nil: return nil
case n.kind case n.kind
@ -649,17 +678,22 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
return indirectAccess(newSymNode(o.closureParam), local, n.info) return indirectAccess(newSymNode(o.closureParam), local, n.info)
var closure = PEnv(idTableGet(o.lambdasToEnv, local)) 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:
if local.kind == skIterator and local.typ.callConv == ccClosure: # consider: [i1, i2, i1] Since we merged the iterator's closure
# consider: [i1, i2, i1] Since we merged the iterator's closure # with the captured owning variables, we need to generate the
# with the captured owning variables, we need to generate the # closure generation code again:
# closure generation code again: if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s)
#if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) # XXX why doesn't this work?
# XXX why doesn't this work? if closure.isNil:
return liftIterSym(n)
else:
let createdVar = generateIterClosureCreation(o, closure, let createdVar = generateIterClosureCreation(o, closure,
closure.attachedNode) closure.attachedNode)
return makeClosure(local, createdVar, n.info) return makeClosure(local, createdVar, n.info)
if closure != nil:
# we need to replace the lambda with '(lambda, env)':
let a = closure.createdVar let a = closure.createdVar
if a != nil: if a != nil:
@ -773,22 +807,6 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
# ------------------- iterator transformation -------------------------------- # ------------------- 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 = proc liftForLoop*(body: PNode): PNode =
# problem ahead: the iterator could be invoked indirectly, but then # problem ahead: the iterator could be invoked indirectly, but then
# we don't know what environment to create here: # 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) n.sons[0].sym.name, o.inSymChoice)
if result != nil and result.kind == skStub: loadStub(result) 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) #setTarget(osNimrodVM, cpuNimrodVM)
initDefines() initDefines()
defineSymbol("nimrodvm") defineSymbol("nimrodvm")
when hasFFI: DefineSymbol("nimffi") when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass) registerPass(verbosePass)
registerPass(semPass) registerPass(semPass)
registerPass(evalPass) registerPass(evalPass)
@ -324,7 +324,7 @@ proc mainCommand* =
wantMainModule() wantMainModule()
when hasTinyCBackend: when hasTinyCBackend:
extccomp.setCC("tcc") extccomp.setCC("tcc")
CommandCompileToC() commandCompileToC()
else: else:
rawMessage(errInvalidCommandX, command) rawMessage(errInvalidCommandX, command)
of "js", "compiletojs": of "js", "compiletojs":
@ -450,7 +450,8 @@ proc mainCommand* =
echo " tries : ", gCacheTries echo " tries : ", gCacheTries
echo " misses: ", gCacheMisses echo " misses: ", gCacheMisses
echo " int tries: ", gCacheIntTries 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: when SimiluateCaasMemReset:
resetMemory() resetMemory()

View file

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

View file

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

View file

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

View file

@ -35,7 +35,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result.typ = errorType(c) result.typ = errorType(c)
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 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: if result.isNil or result.kind == nkEmpty:
# do not produce another redundant error message: # do not produce another redundant error message:
#raiseRecoverableError("") #raiseRecoverableError("")
@ -1706,8 +1706,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = n result = n
result.typ = t result.typ = t
result.kind = nkObjConstr result.kind = nkObjConstr
t = skipTypes(t, abstractInst) t = skipTypes(t, {tyGenericInst})
if t.kind == tyRef: t = skipTypes(t.sons[0], abstractInst) if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst})
if t.kind != tyObject: if t.kind != tyObject:
localError(n.info, errGenerated, "object constructor needs an object type") localError(n.info, errGenerated, "object constructor needs an object type")
return return
@ -1819,6 +1819,10 @@ proc semExport(c: PContext, n: PNode): PNode =
c.module.ast.add x c.module.ast.add x
result = n 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 = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n result = n
if gCmd == cmdIdeTools: suggestExpr(c, n) if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -1924,10 +1928,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
else: else:
#liMessage(n.info, warnUser, renderTree(n)); #liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags) result = semIndirectOp(c, n, flags)
elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]):
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) 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) result = semDirectOp(c, n, flags)
else: else:
result = semIndirectOp(c, n, flags) result = semIndirectOp(c, n, flags)
@ -1993,7 +1999,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semStaticExpr(c, n) result = semStaticExpr(c, n)
of nkAsgn: result = semAsgn(c, n) of nkAsgn: result = semAsgn(c, n)
of nkBlockStmt, nkBlockExpr: result = semBlock(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 nkRaiseStmt: result = semRaise(c, n)
of nkVarSection: result = semVarOrLet(c, n, skVar) of nkVarSection: result = semVarOrLet(c, n, skVar)
of nkLetSection: result = semVarOrLet(c, n, skLet) of nkLetSection: result = semVarOrLet(c, n, skLet)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf # (c) Copyright 2014 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -215,8 +215,7 @@ proc semGenericStmt(c: PContext, n: PNode,
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3): for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) 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) if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
ctx)
# do not perform symbol lookup for default expressions # do not perform symbol lookup for default expressions
for j in countup(0, L-3): for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) 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) if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3) checkMinSonsLen(a, 3)
var L = sonsLen(a) var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3): for j in countup(0, L-3):
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) 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 # 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 # so we use tyEmpty instead of nil to not crash for strange conversions
# like: mydata.seq # like: mydata.seq
rawAddSon(s.typ, newTypeS(tyEmpty, c)) rawAddSon(s.typ, newTypeS(tyNone, c))
s.ast = a s.ast = a
inc c.inGenericContext inc c.inGenericContext
var body = semTypeNode(c, a.sons[2], nil) var body = semTypeNode(c, a.sons[2], nil)
@ -748,7 +748,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
if body != nil: if body != nil:
body.sym = s body.sym = s
body.size = -1 # could not be computed properly 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() popOwner()
closeScope(c) closeScope(c)
elif a.sons[2].kind != nkEmpty: elif a.sons[2].kind != nkEmpty:
@ -972,6 +972,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else: else:
s = n[namePos].sym s = n[namePos].sym
typeIsDetermined = s.typ == nil typeIsDetermined = s.typ == nil
s.ast = n
s.scope = c.currentScope
# if typeIsDetermined: assert phase == stepCompileBody # if typeIsDetermined: assert phase == stepCompileBody
# else: assert phase == stepDetermineType # else: assert phase == stepDetermineType
# before compiling the proc body, set as current the scope # before compiling the proc body, set as current the scope
@ -1206,7 +1209,7 @@ proc usesResult(n: PNode): bool =
for c in n: for c in n:
if usesResult(c): return true 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: # these must be last statements in a block:
const const
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt} 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]): if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true voidContext = true
n.typ = enforceVoidContext 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]) discardCheck(c, n.sons[i])
else: else:
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): if not isEmptyType(n.typ): n.kind = nkStmtListExpr
n.kind = nkStmtListExpr
case n.sons[i].kind case n.sons[i].kind
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:
let (outer, inner) = insertDestructors(c, n.sons[i]) 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 nkConstSection: result = analyseConstSection(c, n)
of nkTypeSection, nkCommentStmt: result = toVoid of nkTypeSection, nkCommentStmt: result = toVoid
of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, 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]) for i in 0 .. <n.len: discard analyse(c, n[i])
result = toVoid result = toVoid
of nkBreakStmt, nkContinueStmt: 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, of tySequence, tySet, tyArray, tyOpenArray,
tyVar, tyPtr, tyRef, tyProc: tyVar, tyPtr, tyRef, tyProc:
# XXX: this is a bit strange, but proc(s: seq) # 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 # This also seems to be true when creating aliases
# like: type myseq = distinct seq. # like: type myseq = distinct seq.
# Maybe there is another better place to associate # Maybe there is another better place to associate
# the seq type class with the seq identifier. # 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, let typ = c.newTypeWithSons(tyBuiltInTypeClass,
@[newTypeS(paramType.kind, c)]) @[newTypeS(paramType.kind, c)])
result = addImplicitGeneric(typ) 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): for i in countup(1, sonsLen(n)-1):
var elem = semGenericParamInInvokation(c, n.sons[i]) var elem = semGenericParamInInvokation(c, n.sons[i])
addToResult(elem) 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: else:
internalAssert s.typ.kind == tyGenericBody
var m = newCandidate(c, s, n) var m = newCandidate(c, s, n)
matches(c, n, copyTree(n), m) matches(c, n, copyTree(n), m)
@ -1133,15 +1137,26 @@ proc processMagicType(c: PContext, m: PSym) =
of mNil: setMagicType(m, tyNil, ptrSize) of mNil: setMagicType(m, tyNil, ptrSize)
of mExpr: setMagicType(m, tyExpr, 0) of mExpr: setMagicType(m, tyExpr, 0)
of mStmt: setMagicType(m, tyStmt, 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 mVoidType: setMagicType(m, tyEmpty, 0)
of mArray: setMagicType(m, tyArray, 0) of mArray:
of mOpenArray: setMagicType(m, tyOpenArray, 0) setMagicType(m, tyArray, 0)
of mVarargs: setMagicType(m, tyVarargs, 0) of mOpenArray:
of mRange: setMagicType(m, tyRange, 0) setMagicType(m, tyOpenArray, 0)
of mSet: setMagicType(m, tySet, 0) of mVarargs:
of mSeq: setMagicType(m, tySequence, 0) setMagicType(m, tyVarargs, 0)
of mOrdinal: setMagicType(m, tyOrdinal, 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 of mPNimrodNode: discard
else: localError(m.info, errTypeExpected) else: localError(m.info, errTypeExpected)
@ -1165,8 +1180,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
typ = semTypeNode(c, constraint, nil) typ = semTypeNode(c, constraint, nil)
if typ.kind != tyStatic or typ.len == 0: if typ.kind != tyStatic or typ.len == 0:
if typ.kind == tyTypeDesc: if typ.kind == tyTypeDesc:
if typ.len == 0: if typ.sons[0].kind == tyNone:
typ = newTypeS(tyTypeDesc, c) typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
else: else:
typ = semGenericConstraints(c, typ) typ = semGenericConstraints(c, typ)

View file

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

View file

@ -105,6 +105,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
var bound = binding[i].typ var bound = binding[i].typ
if bound != nil and formalTypeParam.kind != tyTypeDesc: if bound != nil and formalTypeParam.kind != tyTypeDesc:
bound = bound.skipTypes({tyTypeDesc}) bound = bound.skipTypes({tyTypeDesc})
assert bound != nil
put(c.bindings, formalTypeParam, bound) put(c.bindings, formalTypeParam, bound)
proc newCandidate*(ctx: PContext, callee: PSym, 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 < isGeneric: result = isNone
if result notin {isNone, isGeneric}: if result notin {isNone, isGeneric}:
result = typeRangeRel(f, a) result = typeRangeRel(f, a)
elif skipTypes(f, {tyRange}).kind == a.kind: else:
result = isIntConv if skipTypes(f, {tyRange}).kind == a.kind:
elif isConvertibleToRange(skipTypes(f, {tyRange}), a): result = isIntConv
result = isConvertible # a convertible to f elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
result = isConvertible # a convertible to f
of tyInt: result = handleRange(f, a, tyInt8, tyInt32) of tyInt: result = handleRange(f, a, tyInt8, tyInt32)
of tyInt8: result = handleRange(f, a, tyInt8, tyInt8) of tyInt8: result = handleRange(f, a, tyInt8, tyInt8)
of tyInt16: result = handleRange(f, a, tyInt8, tyInt16) 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: of tyOrdinal:
if isOrdinalType(a): if isOrdinalType(a):
var x = if a.kind == tyOrdinal: a.sons[0] else: a var x = if a.kind == tyOrdinal: a.sons[0] else: a
if f.sonsLen == 0: if f.sons[0].kind == tyNone:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, f.sons[0], x) result = typeRel(c, f.sons[0], x)
@ -736,7 +738,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyGenericInst: of tyGenericInst:
let roota = a.skipGenericAlias let roota = a.skipGenericAlias
let rootf = f.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: for i in 1 .. rootf.sonsLen-2:
let ff = rootf.sons[i] let ff = rootf.sons[i]
let aa = roota.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 # by a tyTypeDesc params. Unfortunately, this requires more substantial
# changes in semtypinst and elsewhere. # changes in semtypinst and elsewhere.
if a.kind == tyTypeDesc: if a.kind == tyTypeDesc:
if f.sons == nil or f.sons.len == 0: if f.sonsLen == 0:
result = isGeneric result = isGeneric
else: else:
internalAssert a.sons != nil and a.sons.len > 0 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: else:
result = isNone result = isNone
else: else:
if f.sonsLen > 0: if f.sonsLen > 0 and f.sons[0].kind != tyNone:
result = typeRel(c, f.lastSon, a) result = typeRel(c, f.lastSon, a)
else: else:
result = isGeneric result = isGeneric
@ -883,7 +885,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
var prev = PType(idTableGet(c.bindings, f)) var prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:
if a.kind == tyTypeDesc: if a.kind == tyTypeDesc:
if f.sonsLen == 0: if f.sons[0].kind == tyNone:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) 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 # returns whether the parameter lists of the procs a, b are exactly the same
proc isOrdinalType*(t: PType): bool proc isOrdinalType*(t: PType): bool
proc enumHasHoles*(t: PType): bool proc enumHasHoles*(t: PType): bool
const
const
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal, abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
tyConst, tyMutable, tyTypeDesc} tyConst, tyMutable, tyTypeDesc}
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal, abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal,
@ -451,7 +452,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyProc: "proc" of tyProc: "proc"
of tyObject: "object" of tyObject: "object"
of tyTuple: "tuple" of tyTuple: "tuple"
else: (internalAssert false; "") else: (internalAssert(false); "")
of tyUserTypeClassInst: of tyUserTypeClassInst:
let body = t.base let body = t.base
result = body.sym.name.s & "[" result = body.sym.name.s & "["
@ -1257,7 +1258,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool =
return true return true
if t.kind == tyTypeDesc: 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 if containsGenericTypeIter(t.base, closure): return true
return false return false

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
=============================================================================== ===============================================================================
Nimrod -- a Compiler for Nimrod. http://nimrod-code.org/ 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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 ``workPackage`` affects the timing granularity and may need to be tweaked in
highly specialized environments or for older hardware. 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 ('type' | 'addr' | symbol) generalizedLit?
| '[' optInd indexExprList optPar ']' | '[' optInd indexExprList optPar ']'
| '{' optInd indexExprList optPar '}' | '{' optInd indexExprList optPar '}'
| &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax | &( '`'|IDENT|literal|'cast') expr # command syntax
(doBlock | macroColon)?
condExpr = expr colcom expr optInd condExpr = expr colcom expr optInd
('elif' expr colcom expr optInd)* ('elif' expr colcom expr optInd)*
'else' colcom expr 'else' colcom expr

View file

@ -341,6 +341,9 @@ Miscellaneous
* `endians <endians.html>`_ * `endians <endians.html>`_
This module contains helpers that deal with different byte orders. This module contains helpers that deal with different byte orders.
* `logging <logging.html>`_
This module implements a simple logger.
Database support 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 user to one/some of the other players, but the total amount seems to remain
pretty much constant for a given task. -- Ran pretty much constant for a given task. -- Ran
About this document About this document
=================== ===================
@ -1313,12 +1315,9 @@ Examples:
.. code-block:: nimrod .. code-block:: nimrod
type proc printItem(x: int) = ...
TCallback = proc (x: int) {.cdecl.}
proc printItem(x: Int) = ... proc forEach(c: proc (x: int) {.cdecl.}) =
proc forEach(c: TCallback) =
... ...
forEach(printItem) # this will NOT work because calling conventions differ 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_. currency. This can be solved with templates_.
.. code-block:: nimrod .. 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.}
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.}
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: typ, y: base): typ {.borrow.}
proc `*` *(x: base, y: typ): typ {.borrow.} proc `*` *(x: base, y: typ): typ {.borrow.}
proc `div` *(x: typ, y: base): typ {.borrow.} proc `div` *(x: typ, y: base): typ {.borrow.}
proc `mod` *(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.} 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 type
typ* = distinct base typ* = distinct base
Additive(typ) additive(typ)
Multiplicative(typ, base) multiplicative(typ, base)
Comparable(typ) comparable(typ)
DefineCurrency(TDollar, int) defineCurrency(TDollar, int)
DefineCurrency(TEuro, int) defineCurrency(TEuro, int)
Void type Void type
@ -2232,6 +2231,8 @@ Instead of:
Using statement Using statement
--------------- ---------------
**Warning**: The ``using`` statement is highly experimental!
The `using statement`:idx: provides syntactic convenience for procs that The `using statement`:idx: provides syntactic convenience for procs that
heavily use a single contextual parameter. When applied to a variable or a 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 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)) 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 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 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. or on the stack if the compiler determines that this would be safe.
Anonymous Procs Anonymous Procs
--------------- ---------------
@ -2495,6 +2574,9 @@ calls can use the ``do`` keyword:
.. code-block:: nimrod .. code-block:: nimrod
sort(cities) do (x,y: string) -> int: sort(cities) do (x,y: string) -> int:
cmp(x.len, y.len) 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. ``do`` is written after the parentheses enclosing the regular proc params.
The proc expression represented by the do block is appended to them. The proc expression represented by the do block is appended to them.
@ -2849,7 +2931,7 @@ parameters of an outer factory proc:
yield x yield x
inc x inc x
let foo = mycount 1, 4 let foo = mycount(1, 4)
for f in foo(): for f in foo():
echo f echo f
@ -3440,13 +3522,41 @@ A symbol can be forced to be open by a `mixin`:idx: declaration:
.. code-block:: nimrod .. code-block:: nimrod
proc create*[T](): ref T = 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: # open symbol explicitly:
mixin init mixin init
new result new result
init 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 Templates
========= =========
@ -3506,28 +3616,6 @@ receive undeclared identifiers:
declareInt(x) # valid 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 Passing a code block to a template
---------------------------------- ----------------------------------
@ -3538,26 +3626,28 @@ special ``:`` syntax:
.. code-block:: nimrod .. code-block:: nimrod
template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} = template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} =
block: var f: TFile
var f: TFile if open(f, fn, mode):
if open(f, fn, mode): try:
try: actions
actions finally:
finally: close(f)
close(f) else:
else: quit("cannot open: " & fn)
quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite): withFile(txt, "ttempl3.txt", fmWrite):
txt.writeln("line 1") txt.writeln("line 1")
txt.writeln("line 2") txt.writeln("line 2")
In the example the two ``writeln`` statements are bound to the ``actions`` 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 .. code-block:: nimrod
# Module A # Module A
@ -3572,34 +3662,11 @@ Symbol binding within templates happens after template instantiation:
# Module B # Module B
import A 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 Identifier construction
@ -3942,13 +4009,13 @@ Static params can also appear in the signatures of generic types:
type type
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T] 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 # `static[int]` requires us to supply a compile-time int value
AffineTransform2D[T] = Matrix[3, 3, T] AffineTransform2D[T] = Matrix[3, 3, T]
AffineTransform3D[T] = Matrix[4, 4, T] AffineTransform3D[T] = Matrix[4, 4, T]
AffineTransform3D[float] # OK AffineTransform3D[float] # OK
AffineTransform2D[string] # Error, `string` is not a `Number` 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 be destructed at its scope exit. Later versions of the language will improve
the support of destructors. 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 delegator pragma
---------------- ----------------
@ -5173,9 +5244,9 @@ the same feature under the same name.
Exportc pragma Exportc pragma
-------------- --------------
The `exportc`:idx: pragma provides a means to export a type, a variable, or a 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. procedure to C. Enums and constants can't be exported. The optional argument
If the argument is missing, the C name is the Nimrod is a string containing the C identifier. If the argument is missing, the C
identifier *exactly as spelled*: name is the Nimrod identifier *exactly as spelled*:
.. code-block:: Nimrod .. code-block:: Nimrod
proc callme(formatstr: cstring) {.exportc: "callMe", varargs.} 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. 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 Tuples
------ ------

View file

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

View file

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

View file

@ -294,19 +294,6 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
## if not `ex`: ## if not `ex`:
## echo `info` & ": Check failed: " & `expString` ## 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.} = proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
## checks that `n` is of kind `k`. If this is not the case, ## checks that `n` is of kind `k`. If this is not the case,
## compilation aborts with an error message. This is useful for writing ## 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 nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
of nnkIdent: add(result, "!\"" & $n.ident & '"') of nnkIdent: add(result, "!\"" & $n.ident & '"')
of nnkSym, nnkNone: assert false of nnkSym: add(result, $n.symbol)
of nnkNone: assert false
else: else:
add(result, lispRepr(n[0])) add(result, lispRepr(n[0]))
for j in 1..n.len-1: for j in 1..n.len-1:
@ -745,3 +733,15 @@ proc addIdentIfAbsent*(dest: PNimrodNode, ident: string) {.compiletime.} =
else: discard else: discard
dest.add(ident(ident)) 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. ## be established.
init(result) init(result)
let x = connect(result, host, port.cint) let x = client(result, host, port.cint)
if x != 0'i32: if x != 0'i32:
dbError(result, "cannot open: " & host) 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. ## the generated OID for the ``_id`` field.
result = genOid() result = genOid()
var x = jsonToBSon(data, result) var x = jsonToBSon(data, result)
insert(db, namespace, x) insert(db, namespace, x, nil)
destroy(x) destroy(x)
proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {. 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) if finalize(stmt) != SQLITE_OK: dbError(db)
proc tryInsertID*(db: TDbConn, query: TSqlQuery, 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 ## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. ## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args) 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 prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
if step(stmt) == SQLITE_DONE: if step(stmt) == SQLITE_DONE:
result = last_insert_rowid(db) 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, proc insertID*(db: TDbConn, query: TSqlQuery,
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} = args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =

View file

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

88
lib/posix/epoll.nim Normal file
View file

@ -0,0 +1,88 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
const
EPOLLIN* = 0x00000001
EPOLLPRI* = 0x00000002
EPOLLOUT* = 0x00000004
EPOLLERR* = 0x00000008
EPOLLHUP* = 0x00000010
EPOLLRDNORM* = 0x00000040
EPOLLRDBAND* = 0x00000080
EPOLLWRNORM* = 0x00000100
EPOLLWRBAND* = 0x00000200
EPOLLMSG* = 0x00000400
EPOLLRDHUP* = 0x00002000
EPOLLWAKEUP* = 1 shl 29
EPOLLONESHOT* = 1 shl 30
EPOLLET* = 1 shl 31
# Valid opcodes ( "op" parameter ) to issue to epoll_ctl().
const
EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface.
EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface.
EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure.
type
epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
thePtr* {.importc: "ptr".}: pointer # \
#fd*: cint
#u32*: uint32
#u64*: uint64
epoll_event* {.importc: "struct epoll_event", header: "<sys/epoll.h>", pure, final.} = object
events*: uint32 # Epoll events
data*: epoll_data # User data variable
proc epoll_create*(size: cint): cint {.importc: "epoll_create",
header: "<sys/epoll.h>".}
## Creates an epoll instance. Returns an fd for the new instance.
## The "size" parameter is a hint specifying the number of file
## descriptors to be associated with the new instance. The fd
## returned by epoll_create() should be closed with close().
proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
header: "<sys/epoll.h>".}
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped.
proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".}
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
## -1 in case of error ( the "errno" variable will contain the
## specific error code ) The "op" parameter is one of the EPOLL_CTL_*
## constants defined above. The "fd" parameter is the target of the
## operation. The "event" parameter describes which events the caller
## is interested in and any associated user data.
proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
timeout: cint): cint {.importc: "epoll_wait",
header: "<sys/epoll.h>".}
## Wait for events on an epoll instance "epfd". Returns the number of
## triggered events returned in "events" buffer. Or -1 in case of
## error with the "errno" variable set to the specific error code. The
## "events" parameter is a buffer that will contain triggered
## events. The "maxevents" is the maximum number of events to be
## returned ( usually size of "events" ). The "timeout" parameter
## specifies the maximum wait time in milliseconds (-1 == infinite).
##
## This function is a cancellation point and therefore not marked with
## __THROW.
#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint;
# timeout: cint; ss: ptr sigset_t): cint {.
# importc: "epoll_pwait", header: "<sys/epoll.h>".}
# Same as epoll_wait, but the thread's signal mask is temporarily
# and atomically replaced with the one provided as parameter.
#
# This function is a cancellation point and therefore not marked with
# __THROW.

File diff suppressed because it is too large Load diff

View file

@ -131,3 +131,36 @@ proc sort*[T](a: var openArray[T],
dec(m, s*2) dec(m, s*2)
s = 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() server.listen()
d.register(server) d.register(server)
while d.poll(-1): nil while d.poll(-1): discard

485
lib/pure/asyncio2.nim Normal file
View file

@ -0,0 +1,485 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import os, oids, tables, strutils
import winlean
import sockets2, net
## Asyncio2
## --------
##
## This module implements a brand new asyncio module based on Futures.
## IOCP is used under the hood on Windows and the selectors module is used for
## other operating systems.
# -- Futures
type
PFutureVoid* = ref object of PObject
cbVoid: proc () {.closure.}
finished: bool
PFuture*[T] = ref object of PFutureVoid
value: T
error: ref EBase
cb: proc (future: PFuture[T]) {.closure.}
proc newFuture*[T](): PFuture[T] =
## Creates a new future.
new(result)
result.finished = false
proc complete*[T](future: PFuture[T], val: T) =
## Completes ``future`` with value ``val``.
assert(not future.finished)
assert(future.error == nil)
future.value = val
future.finished = true
if future.cb != nil:
future.cb(future)
if future.cbVoid != nil:
future.cbVoid()
proc fail*[T](future: PFuture[T], error: ref EBase) =
## Completes ``future`` with ``error``.
assert(not future.finished)
future.finished = true
future.error = error
if future.cb != nil:
future.cb(future)
proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
future.cb = cb
if future.finished:
future.cb(future)
proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) =
## Sets the **void** callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: This is used for the ``await`` functionality, you most likely
## want to use ``callback``.
future.cbVoid = cb
if future.finished:
future.cbVoid()
proc read*[T](future: PFuture[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise
## this function will fail with a ``EInvalidValue`` exception.
##
## If the result of the future is an error then that error will be raised.
if future.finished:
if future.error != nil: raise future.error
return future.value
else:
# TODO: Make a custom exception type for this?
raise newException(EInvalidValue, "Future still in progress.")
proc finished*[T](future: PFuture[T]): bool =
## Determines whether ``future`` has completed.
##
## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish.
future.finished
proc failed*[T](future: PFuture[T]): bool =
## Determines whether ``future`` completed with an error.
future.error != nil
when defined(windows):
type
TCompletionKey = dword
TCompletionData* = object
sock: TSocketHandle
cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.}
PDispatcher* = ref object
ioPort: THandle
TCustomOverlapped = object
Internal*: DWORD
InternalHigh*: DWORD
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THANDLE
data*: TCompletionData
PCustomOverlapped = ptr TCustomOverlapped
proc newDispatcher*(): PDispatcher =
## Creates a new Dispatcher instance.
new result
result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
proc register*(p: PDispatcher, sock: TSocketHandle) =
## Registers ``sock`` with the dispatcher ``p``.
if CreateIOCompletionPort(sock.THandle, p.ioPort,
cast[TCompletionKey](sock), 1) == 0:
OSError(OSLastError())
proc poll*(p: PDispatcher, timeout = 500) =
## Waits for completion events and processes them.
let llTimeout =
if timeout == -1: winlean.INFINITE
else: timeout.int32
var lpNumberOfBytesTransferred: DWORD
var lpCompletionKey: ULONG
var lpOverlapped: POverlapped
let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred,
addr lpCompletionKey, addr lpOverlapped, llTimeout).bool
# http://stackoverflow.com/a/12277264/492186
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
if res:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1))
dealloc(customOverlapped)
else:
let errCode = OSLastError()
if lpOverlapped != nil:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
dealloc(customOverlapped)
customOverlapped.data.cb(customOverlapped.data.sock, errCode)
else:
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
discard
else: OSError(errCode)
var connectExPtr: pointer = nil
var acceptExPtr: pointer = nil
var getAcceptExSockAddrsPtr: pointer = nil
proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool =
# Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c
var bytesRet: DWord
func = nil
result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid,
sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD,
addr bytesRet, nil, nil) == 0
proc initAll() =
let dummySock = socket()
if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX):
OSError(OSLastError())
if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX):
OSError(OSLastError())
if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS):
OSError(OSLastError())
proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool =
if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().")
let func =
cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
lpSendBuffer: pointer, dwSendDataLength: dword,
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
lpOverlapped)
proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool =
if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().")
let func =
cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
lpOverlapped)
proc getAcceptExSockaddrs(lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD,
LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint,
RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) =
if getAcceptExSockAddrsPtr.isNil:
raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().")
let func =
cast[proc (lpOutputBuffer: pointer,
dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
RemoteSockaddr, RemoteSockaddrLength)
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
af = AF_INET): PFuture[int] =
## Connects ``socket`` to server at ``address:port``.
##
## Returns a ``PFuture`` which will complete when the connection succeeds
## or an error occurs.
var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed.
# Apparently ``ConnectEx`` expects the socket to be initially bound:
var saddr: Tsockaddr_in
saddr.sin_family = int16(toInt(af))
saddr.sin_port = 0
saddr.sin_addr.s_addr = INADDR_ANY
if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)),
sizeof(saddr).TSockLen) < 0'i32:
OSError(OSLastError())
var aiList = getAddrInfo(address, port, af)
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
# "the OVERLAPPED structure must remain valid until the I/O completes"
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
retFuture.complete(0)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
nil, 0, nil, cast[POverlapped](ol))
if ret:
# Request to connect completed immediately.
success = true
retFuture.complete(0)
dealloc(ol)
break
else:
lastError = OSLastError()
if lastError.int32 == ERROR_IO_PENDING:
# In this case ``ol`` will be deallocated in ``poll``.
success = true
break
else:
dealloc(ol)
success = false
it = it.ai_next
dealloc(aiList)
if not success:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
return retFuture
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] =
## Reads ``size`` bytes from ``socket``. Returned future will complete once
## all of the requested data is read.
var retFuture = newFuture[string]()
var dataBuf: TWSABuf
dataBuf.buf = newString(size)
dataBuf.len = size
var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
addr flags, cast[POverlapped](ol), nil)
if ret == -1:
let err = OSLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
# Request to read completed immediately.
var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
dealloc(ol)
return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
## Sends ``data`` to ``socket``. The returned future will complete once all
## data has been sent.
var retFuture = newFuture[int]()
var dataBuf: TWSABuf
dataBuf.buf = data
dataBuf.len = data.len
var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
retFuture.complete(0)
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
flags, cast[POverlapped](ol), nil)
if ret == -1:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
retFuture.complete(0)
dealloc(ol)
return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
PFuture[tuple[address: string, client: TSocketHandle]] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection and the remote address of the client.
## The future will complete when the connection is successfully accepted.
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
var clientSock = socket()
if clientSock == OSInvalidSocket: osError(osLastError())
const lpOutputLen = 1024
var lpOutputBuf = newString(lpOutputLen)
var dwBytesReceived: DWORD
let dwReceiveDataLength = 0.DWORD # We don't want any data to be read.
let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16)
let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16)
template completeAccept(): stmt {.immediate, dirty.} =
var listenSock = socket
let setoptRet = setsockopt(clientSock, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, addr listenSock,
sizeof(listenSock).TSockLen)
if setoptRet != 0: osError(osLastError())
var LocalSockaddr, RemoteSockaddr: ptr TSockAddr
var localLen, remoteLen: int32
getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength,
dwLocalAddressLength, dwRemoteAddressLength,
addr LocalSockaddr, addr localLen,
addr RemoteSockaddr, addr remoteLen)
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
retFuture.complete(
(address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr),
client: clientSock)
)
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1):
completeAccept()
else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0],
dwReceiveDataLength,
dwLocalAddressLength,
dwRemoteAddressLength,
addr dwBytesReceived, cast[POverlapped](ol))
if not ret:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
else:
completeAccept()
dealloc(ol)
return retFuture
proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
## Accepts a new connection. Returns a future containing the client socket
## corresponding to that connection.
## The future will complete when the connection is successfully accepted.
var retFut = newFuture[TSocketHandle]()
var fut = p.acceptAddr(socket)
fut.callback =
proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
assert future.finished
if future.failed:
retFut.fail(future.error)
else:
retFut.complete(future.read.client)
return retFut
initAll()
else:
# TODO: Selectors.
when isMainModule:
var p = newDispatcher()
var sock = socket()
#sock.setBlocking false
p.register(sock)
when true:
var f = p.connect(sock, "irc.freenode.org", TPort(6667))
f.callback =
proc (future: PFuture[int]) =
echo("Connected in future!")
echo(future.read)
for i in 0 .. 50:
var recvF = p.recv(sock, 10)
recvF.callback =
proc (future: PFuture[string]) =
echo("Read: ", future.read)
else:
sock.bindAddr(TPort(6667))
sock.listen()
proc onAccept(future: PFuture[TSocketHandle]) =
echo "Accepted"
var t = p.send(future.read, "test\c\L")
t.callback =
proc (future: PFuture[int]) =
echo(future.read)
var f = p.accept(sock)
f.callback = onAccept
var f = p.accept(sock)
f.callback = onAccept
while true:
p.poll()
echo "polled"

View file

@ -276,6 +276,38 @@ template foldr*(sequence, operation: expr): expr =
result = operation result = operation
result 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: when isMainModule:
import strutils import strutils
block: # concat test 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] 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]""" 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" echo "Finished doc tests"

View file

@ -224,3 +224,20 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
proc `$`*[A](s: TOrderedSet[A]): string = proc `$`*[A](s: TOrderedSet[A]): string =
## The `$` operator for ordered hash sets. ## The `$` operator for ordered hash sets.
dollarImpl() 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 = proc `$`*[A, B](t: TTable[A, B]): string =
## The `$` operator for hash tables. ## The `$` operator for hash tables.
dollarImpl() 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 ------------------------------ # ------------------------------ ordered table ------------------------------

View file

@ -14,15 +14,15 @@
type type
TLibHandle* = pointer ## a handle to a dynamically loaded library 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 ## loads a library from `path`. Returns nil if the library could not
## be loaded. ## be loaded.
proc LoadLib*(): TLibHandle proc loadLib*(): TLibHandle
## gets the handle from the current executable. Returns nil if the ## gets the handle from the current executable. Returns nil if the
## library could not be loaded. ## library could not be loaded.
proc UnloadLib*(lib: TLibHandle) proc unloadLib*(lib: TLibHandle)
## unloads the library `lib` ## unloads the library `lib`
proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} = proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
@ -60,9 +60,9 @@ when defined(posix):
proc dlsym(lib: TLibHandle, name: cstring): pointer {. proc dlsym(lib: TLibHandle, name: cstring): pointer {.
importc, header: "<dlfcn.h>".} importc, header: "<dlfcn.h>".}
proc LoadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW) proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
proc LoadLib(): TLibHandle = return dlopen(nil, RTLD_NOW) proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc UnloadLib(lib: TLibHandle) = dlclose(lib) proc unloadLib(lib: TLibHandle) = dlclose(lib)
proc symAddr(lib: TLibHandle, name: cstring): pointer = proc symAddr(lib: TLibHandle, name: cstring): pointer =
return dlsym(lib, name) return dlsym(lib, name)
@ -78,14 +78,14 @@ elif defined(windows) or defined(dos):
proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.} proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
proc winLoadLibrary(path: cstring): THINSTANCE {. proc winLoadLibrary(path: cstring): THINSTANCE {.
importc: "LoadLibraryA", header: "<windows.h>", stdcall.} 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.} importc: "GetProcAddress", header: "<windows.h>", stdcall.}
proc LoadLib(path: string): TLibHandle = proc loadLib(path: string): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path)) result = cast[TLibHandle](winLoadLibrary(path))
proc LoadLib(): TLibHandle = proc loadLib(): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(nil)) 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 = proc symAddr(lib: TLibHandle, name: cstring): pointer =
result = GetProcAddress(cast[THINSTANCE](lib), name) 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}: if htmlTag(x.elemName) in {tagOption, tagOptgroup}:
errors.add(expected(x, result)) errors.add(expected(x, result))
break break
else: nil else: discard
result.addNode(parse(x, errors)) result.addNode(parse(x, errors))
of xmlElementEnd: of xmlElementEnd:
if cmpIgnoreCase(x.elemName, result.tag) == 0: 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) var u = entityToUtf8(x.rawData)
if u.len != 0: result = newText(u) if u.len != 0: result = newText(u)
next(x) next(x)
of xmlEof: nil of xmlEof: discard
proc parseHtml*(s: PStream, filename: string, proc parseHtml*(s: PStream, filename: string,
errors: var seq[string]): PXmlNode = errors: var seq[string]): PXmlNode =

View file

@ -476,12 +476,12 @@ when isMainModule:
var client = irc("amber.tenthbit.net", nick="TestBot1234", var client = irc("amber.tenthbit.net", nick="TestBot1234",
joinChans = @["#flood"]) joinChans = @["#flood"])
client.connect() client.connect()
while True: while true:
var event: TIRCEvent var event: TIRCEvent
if client.poll(event): if client.poll(event):
case event.typ case event.typ
of EvConnected: of EvConnected:
nil discard
of EvDisconnected: of EvDisconnected:
break break
of EvMsg: 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: try:
discard parseutils.parseInt(s, x, 0) discard parseutils.parseInt(s, x, 0)
except EOverflow: except EOverflow:
nil discard
if x in validRange: value = x if x in validRange: value = x
when isMainModule: when isMainModule:

40
lib/pure/net.nim Normal file
View file

@ -0,0 +1,40 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a high-level cross-platform sockets interface.
import sockets2, os
type
TSocket* = TSocketHandle
proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
tags: [FReadIO].} =
## binds an address/port number to a socket.
## Use address string in dotted decimal form like "a.b.c.d"
## or leave "" for any address.
if address == "":
var name: TSockaddr_in
when defined(windows):
name.sin_family = toInt(AF_INET).int16
else:
name.sin_family = toInt(AF_INET)
name.sin_port = htons(int16(port))
name.sin_addr.s_addr = htonl(INADDR_ANY)
if bindAddr(socket, cast[ptr TSockAddr](addr(name)),
sizeof(name).TSocklen) < 0'i32:
osError(osLastError())
else:
var aiList = getAddrInfo(address, port, AF_INET)
if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
dealloc(aiList)
osError(osLastError())
dealloc(aiList)

View file

@ -28,7 +28,7 @@ proc hexbyte*(hex: char): int =
of '0'..'9': result = (ord(hex) - ord('0')) 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)
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 = proc parseOid*(str: cstring): TOid =
## parses an OID. ## parses an OID.

View file

@ -287,12 +287,18 @@ proc osLastError*(): TOSErrorCode =
result = TOSErrorCode(errno) result = TOSErrorCode(errno)
{.pop.} {.pop.}
proc unixToNativePath*(path: string): string {. proc unixToNativePath*(path: string, drive=""): string {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Converts an UNIX-like path to a native one. ## Converts an UNIX-like path to a native one.
## ##
## On an UNIX system this does nothing. Else it converts ## On an UNIX system this does nothing. Else it converts
## '/', '.', '..' to the appropriate things. ## '/', '.', '..' 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): when defined(unix):
result = path result = path
else: else:
@ -300,7 +306,10 @@ proc unixToNativePath*(path: string): string {.
if path[0] == '/': if path[0] == '/':
# an absolute path # an absolute path
when doslike: when doslike:
result = r"C:\" if drive != "":
result = drive & ":" & DirSep
else:
result = $DirSep
elif defined(macos): elif defined(macos):
result = "" # must not start with ':' result = "" # must not start with ':'
else: else:
@ -387,6 +396,21 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
var res: TStat var res: TStat
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
proc 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.} = proc fileExists*(filename: string): bool {.inline.} =
## Synonym for existsFile ## Synonym for existsFile
existsFile(filename) 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 ## the process has finished. To execute a program without having a
## shell involved, use the `execProcess` proc of the `osproc` ## shell involved, use the `execProcess` proc of the `osproc`
## module. ## module.
result = c_system(command) result = c_system(command) shr 8
# Environment handling cannot be put into RTL, because the ``envPairs`` # Environment handling cannot be put into RTL, because the ``envPairs``
# iterator depends on ``environment``. # iterator depends on ``environment``.
@ -1054,9 +1078,9 @@ when defined(windows):
while true: while true:
var eend = strEnd(e) var eend = strEnd(e)
add(environment, $e) add(environment, $e)
e = cast[CString](cast[TAddress](eend)+1) e = cast[cstring](cast[TAddress](eend)+1)
if eend[1] == '\0': break if eend[1] == '\0': break
discard FreeEnvironmentStringsA(env) discard freeEnvironmentStringsA(env)
envComputed = true envComputed = true
else: else:
@ -1221,6 +1245,8 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
if not skipFindData(f): if not skipFindData(f):
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
k = pcDir k = pcDir
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
k = succ(k)
yield (k, dir / extractFilename(getFilename(f))) yield (k, dir / extractFilename(getFilename(f)))
if findNextFile(h, f) == 0'i32: break if findNextFile(h, f) == 0'i32: break
findClose(h) findClose(h)
@ -1245,6 +1271,10 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
tags: [FReadDir].} = tags: [FReadDir].} =
## walks over the directory `dir` and yields for each file in `dir`. The ## walks over the directory `dir` and yields for each file in `dir`. The
## full path for each file is returned. ## 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: ## 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) copyDir(path, dest / noSource)
else: discard 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] {. proc parseCmdLine*(c: string): seq[string] {.
noSideEffect, rtl, extern: "nos$1".} = noSideEffect, rtl, extern: "nos$1".} =
## Splits a command line into several components; ## Splits a command line into several components;

View file

@ -13,7 +13,7 @@
include "system/inclrtl" include "system/inclrtl"
import import
strutils, os, strtabs, streams strutils, os, strtabs, streams, sequtils
when defined(windows): when defined(windows):
import winlean import winlean
@ -36,11 +36,17 @@ type
TProcessOption* = enum ## options that can be passed `startProcess` TProcessOption* = enum ## options that can be passed `startProcess`
poEchoCmd, ## echo the command before execution poEchoCmd, ## echo the command before execution
poUseShell, ## use the shell to execute the command; NOTE: This poUsePath, ## Asks system to search for executable using PATH environment
## often creates a security hole! ## variable.
## On Windows, this is the default.
poEvalCommand, ## Pass `command` directly to the shell, without quoting.
## Use it only if `command` comes from trused source.
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams poParentStreams ## use the parent's streams
template poUseShell*: TProcessOption {.deprecated.} = poUsePath
## Deprecated alias for poUsePath.
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
## Quote s, so it can be safely passed to Windows API. ## Quote s, so it can be safely passed to Windows API.
## Based on Python's subprocess.list2cmdline ## Based on Python's subprocess.list2cmdline
@ -94,12 +100,17 @@ proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
{.error:"quoteShell is not supported on your system".} {.error:"quoteShell is not supported on your system".}
proc execProcess*(command: string, proc execProcess*(command: string,
args: openarray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): TaintedString {. poUsePath,
poEvalCommand}): TaintedString {.
rtl, extern: "nosp$1", rtl, extern: "nosp$1",
tags: [FExecIO, FReadIO].} tags: [FExecIO, FReadIO].}
## A convenience procedure that executes ``command`` with ``startProcess`` ## A convenience procedure that executes ``command`` with ``startProcess``
## and returns its output as a string. ## and returns its output as a string.
## WARNING: this function uses poEvalCommand by default for backward compatibility.
## Make sure to pass options explicitly.
proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].} proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
## Executes ``command`` and returns its error code. Standard input, output, ## Executes ``command`` and returns its error code. Standard input, output,
@ -109,8 +120,8 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", tags: [FExecIO].}
proc startProcess*(command: string, proc startProcess*(command: string,
workingDir: string = "", workingDir: string = "",
args: openArray[string] = [], args: openArray[string] = [],
env: PStringTable = nil, env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut}): options: set[TProcessOption] = {poStdErrToStdOut}):
PProcess {.rtl, extern: "nosp$1", tags: [FExecIO, FReadEnv].} PProcess {.rtl, extern: "nosp$1", tags: [FExecIO, FReadEnv].}
## Starts a process. `Command` is the executable file, `workingDir` is the ## Starts a process. `Command` is the executable file, `workingDir` is the
## process's working directory. If ``workingDir == ""`` the current directory ## process's working directory. If ``workingDir == ""`` the current directory
@ -127,16 +138,10 @@ proc startProcess*(command: string,
## but ``EOS`` is raised in case of an error. ## but ``EOS`` is raised in case of an error.
proc startCmd*(command: string, options: set[TProcessOption] = { proc startCmd*(command: string, options: set[TProcessOption] = {
poStdErrToStdOut, poUseShell}): PProcess {. poStdErrToStdOut, poUsePath}): PProcess {.
tags: [FExecIO, FReadEnv].} = tags: [FExecIO, FReadEnv], deprecated.} =
## a simpler version of `startProcess` that parses the command line into ## Deprecated - use `startProcess` directly.
## program and arguments and then calls `startProcess` with the empty string result = startProcess(command=command, options=options + {poEvalCommand})
## for `workingDir` and the nil string table for `env`.
var c = parseCmdLine(command)
var a: seq[string]
newSeq(a, c.len-1) # avoid slicing for now (still unstable)
for i in 1 .. c.len-1: a[i-1] = c[i]
result = startProcess(command=c[0], args=a, options=options)
proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].} proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
## When the process has finished executing, cleanup related handles ## When the process has finished executing, cleanup related handles
@ -157,7 +162,7 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
## returns `p`'s process ID. ## returns `p`'s process ID.
return p.id return p.id
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
extern: "nosp$1", tags: [].} extern: "nosp$1", tags: [].}
## waits for the process to finish and returns `p`'s error code. ## waits for the process to finish and returns `p`'s error code.
@ -167,19 +172,19 @@ proc peekExitCode*(p: PProcess): int {.tags: [].}
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].} proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s input stream for writing to. ## returns ``p``'s input stream for writing to.
## ##
## **Warning**: The returned `PStream` should not be closed manually as it ## **Warning**: The returned `PStream` should not be closed manually as it
## is closed when closing the PProcess ``p``. ## is closed when closing the PProcess ``p``.
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].} proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s output stream for reading from. ## returns ``p``'s output stream for reading from.
## ##
## **Warning**: The returned `PStream` should not be closed manually as it ## **Warning**: The returned `PStream` should not be closed manually as it
## is closed when closing the PProcess ``p``. ## is closed when closing the PProcess ``p``.
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].} proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
## returns ``p``'s error stream for reading from. ## returns ``p``'s error stream for reading from.
## ##
## **Warning**: The returned `PStream` should not be closed manually as it ## **Warning**: The returned `PStream` should not be closed manually as it
## is closed when closing the PProcess ``p``. ## is closed when closing the PProcess ``p``.
proc inputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1", proc inputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1",
@ -245,15 +250,15 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
proc execProcesses*(cmds: openArray[string], proc execProcesses*(cmds: openArray[string],
options = {poStdErrToStdOut, poParentStreams}, options = {poStdErrToStdOut, poParentStreams},
n = countProcessors()): int {.rtl, extern: "nosp$1", n = countProcessors()): int {.rtl, extern: "nosp$1",
tags: [FExecIO, FTime, FReadEnv].} = tags: [FExecIO, FTime, FReadEnv]} =
## executes the commands `cmds` in parallel. Creates `n` processes ## executes the commands `cmds` in parallel. Creates `n` processes
## that execute in parallel. The highest return value of all processes ## that execute in parallel. The highest return value of all processes
## is returned. ## is returned.
when defined(posix): when defined(posix):
# poParentStreams causes problems on Posix, so we simply disable it: # poParentStreams causes problems on Posix, so we simply disable it:
var options = options - {poParentStreams} var options = options - {poParentStreams}
assert n > 0 assert n > 0
if n > 1: if n > 1:
var q: seq[PProcess] var q: seq[PProcess]
@ -307,13 +312,17 @@ proc select*(readfds: var seq[PProcess], timeout = 500): int
when not defined(useNimRtl): when not defined(useNimRtl):
proc execProcess(command: string, proc execProcess(command: string,
args: openarray[string] = [],
env: PStringTable = nil,
options: set[TProcessOption] = {poStdErrToStdOut, options: set[TProcessOption] = {poStdErrToStdOut,
poUseShell}): TaintedString = poUsePath,
var p = startCmd(command, options=options) poEvalCommand}): TaintedString =
var p = startProcess(command, args=args, env=env, options=options)
var outp = outputStream(p) var outp = outputStream(p)
result = TaintedString"" result = TaintedString""
var line = newStringOfCap(120).TaintedString var line = newStringOfCap(120).TaintedString
while true: while true:
# FIXME: converts CR-LF to LF.
if outp.readLine(line): if outp.readLine(line):
result.string.add(line.string) result.string.add(line.string)
result.string.add("\n") result.string.add("\n")
@ -427,8 +436,9 @@ when defined(Windows) and not defined(useNimRtl):
result.errHandle = TFileHandle(si.hStdError) result.errHandle = TFileHandle(si.hStdError)
var cmdl: cstring var cmdl: cstring
when false: # poUseShell in options: if poEvalCommand in options:
cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args) cmdl = command
assert args.len == 0
else: else:
cmdl = buildCommandLine(command, args) cmdl = buildCommandLine(command, args)
var wd: cstring = nil var wd: cstring = nil
@ -441,11 +451,11 @@ when defined(Windows) and not defined(useNimRtl):
var ee = newWideCString(e) var ee = newWideCString(e)
var wwd = newWideCString(wd) var wwd = newWideCString(wd)
success = winlean.createProcessW(nil, success = winlean.createProcessW(nil,
tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT, tmp, nil, nil, 1, NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT,
ee, wwd, si, procInfo) ee, wwd, si, procInfo)
else: else:
success = winlean.createProcessA(nil, 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() let lastError = osLastError()
if poParentStreams notin options: if poParentStreams notin options:
@ -455,7 +465,6 @@ when defined(Windows) and not defined(useNimRtl):
fileClose(si.hStdError) fileClose(si.hStdError)
if e != nil: dealloc(e) if e != nil: dealloc(e)
dealloc(cmdl)
if success == 0: osError(lastError) if success == 0: osError(lastError)
# Close the handle now so anyone waiting is woken: # Close the handle now so anyone waiting is woken:
discard closeHandle(procInfo.hThread) discard closeHandle(procInfo.hThread)
@ -495,7 +504,7 @@ when defined(Windows) and not defined(useNimRtl):
proc peekExitCode(p: PProcess): int = proc peekExitCode(p: PProcess): int =
var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT
if b: result = -1 if b: result = -1
else: else:
var res: int32 var res: int32
discard getExitCodeProcess(p.fProcessHandle, res) discard getExitCodeProcess(p.fProcessHandle, res)
return res return res
@ -525,7 +534,7 @@ when defined(Windows) and not defined(useNimRtl):
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
else: else:
var res = winlean.createProcessA(nil, command, nil, nil, 0, 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: if res == 0:
osError(osLastError()) osError(osLastError())
else: else:
@ -538,13 +547,13 @@ when defined(Windows) and not defined(useNimRtl):
result = -1 result = -1
discard closeHandle(process) discard closeHandle(process)
proc select(readfds: var seq[PProcess], timeout = 500): int = proc select(readfds: var seq[PProcess], timeout = 500): int =
assert readfds.len <= MAXIMUM_WAIT_OBJECTS assert readfds.len <= MAXIMUM_WAIT_OBJECTS
var rfds: TWOHandleArray var rfds: TWOHandleArray
for i in 0..readfds.len()-1: for i in 0..readfds.len()-1:
rfds[i] = readfds[i].fProcessHandle rfds[i] = readfds[i].fProcessHandle
var ret = waitForMultipleObjects(readfds.len.int32, var ret = waitForMultipleObjects(readfds.len.int32,
addr(rfds), 0'i32, timeout.int32) addr(rfds), 0'i32, timeout.int32)
case ret case ret
of WAIT_TIMEOUT: of WAIT_TIMEOUT:
@ -561,22 +570,7 @@ elif not defined(useNimRtl):
readIdx = 0 readIdx = 0
writeIdx = 1 writeIdx = 1
proc addCmdArgs(command: string, args: openArray[string]): string = proc envToCStringArray(t: PStringTable): cstringArray =
result = quoteShell(command)
for i in 0 .. high(args):
add(result, " ")
add(result, quoteShell(args[i]))
proc toCStringArray(b, a: openArray[string]): cstringArray =
result = cast[cstringArray](alloc0((a.len + b.len + 1) * sizeof(cstring)))
for i in 0..high(b):
result[i] = cast[cstring](alloc(b[i].len+1))
copyMem(result[i], cstring(b[i]), b[i].len+1)
for i in 0..high(a):
result[i+b.len] = cast[cstring](alloc(a[i].len+1))
copyMem(result[i+b.len], cstring(a[i]), a[i].len+1)
proc toCStringArray(t: PStringTable): cstringArray =
result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring))) result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
var i = 0 var i = 0
for key, val in pairs(t): for key, val in pairs(t):
@ -595,7 +589,7 @@ elif not defined(useNimRtl):
result[i] = cast[cstring](alloc(x.len+1)) result[i] = cast[cstring](alloc(x.len+1))
copyMem(result[i], addr(x[0]), x.len+1) copyMem(result[i], addr(x[0]), x.len+1)
inc(i) inc(i)
proc startProcess(command: string, proc startProcess(command: string,
workingDir: string = "", workingDir: string = "",
args: openArray[string] = [], args: openArray[string] = [],
@ -609,23 +603,38 @@ elif not defined(useNimRtl):
if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or
pipe(pStderr) != 0'i32: pipe(pStderr) != 0'i32:
osError(osLastError()) osError(osLastError())
var sys_command: string
var sys_args_raw: seq[string]
if poEvalCommand in options:
sys_command = "/bin/sh"
sys_args_raw = @[sys_command, "-c", command]
assert args.len == 0
else:
sys_command = command
sys_args_raw = @[command]
for arg in args.items:
sys_args_raw.add arg
var sys_args = allocCStringArray(sys_args_raw)
finally: deallocCStringArray(sys_args)
var pid: TPid var pid: TPid
when defined(posix_spawn) and not defined(useFork): when defined(posix_spawn) and not defined(useFork):
var attr: Tposix_spawnattr var attr: Tposix_spawnattr
var fops: Tposix_spawn_file_actions var fops: Tposix_spawn_file_actions
template chck(e: expr) = template chck(e: expr) =
if e != 0'i32: osError(osLastError()) if e != 0'i32: osError(osLastError())
chck posix_spawn_file_actions_init(fops) chck posix_spawn_file_actions_init(fops)
chck posix_spawnattr_init(attr) chck posix_spawnattr_init(attr)
var mask: Tsigset var mask: Tsigset
chck sigemptyset(mask) chck sigemptyset(mask)
chck posix_spawnattr_setsigmask(attr, mask) chck posix_spawnattr_setsigmask(attr, mask)
chck posix_spawnattr_setpgroup(attr, 0'i32) chck posix_spawnattr_setpgroup(attr, 0'i32)
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
POSIX_SPAWN_SETSIGMASK or POSIX_SPAWN_SETSIGMASK or
POSIX_SPAWN_SETPGROUP) POSIX_SPAWN_SETPGROUP)
@ -639,27 +648,22 @@ elif not defined(useNimRtl):
if poStdErrToStdOut in options: if poStdErrToStdOut in options:
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2) chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
else: else:
chck posix_spawn_file_actions_adddup2(fops, pStderr[writeIdx], 2) chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2)
var e = if env == nil: envToCStringArray() else: toCStringArray(env) var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env)
var a: cstringArray
var res: cint var res: cint
# This is incorrect!
if workingDir.len > 0: os.setCurrentDir(workingDir) if workingDir.len > 0: os.setCurrentDir(workingDir)
if poUseShell notin options: if poUsePath in options:
a = toCStringArray([extractFilename(command)], args) res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
res = posix_spawn(pid, command, fops, attr, a, e)
else: else:
var x = addCmdArgs(command, args) res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
a = toCStringArray(["sh", "-c"], [x]) deallocCStringArray(sys_env)
res = posix_spawn(pid, "/bin/sh", fops, attr, a, e)
deallocCStringArray(a)
deallocCStringArray(e)
discard posix_spawn_file_actions_destroy(fops) discard posix_spawn_file_actions_destroy(fops)
discard posix_spawnattr_destroy(attr) discard posix_spawnattr_destroy(attr)
chck res chck res
else: else:
pid = fork() pid = fork()
if pid < 0: osError(osLastError()) if pid < 0: osError(osLastError())
if pid == 0: if pid == 0:
@ -680,19 +684,18 @@ elif not defined(useNimRtl):
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno)) if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
if workingDir.len > 0: os.setCurrentDir(workingDir) if workingDir.len > 0: os.setCurrentDir(workingDir)
if poUseShell notin options:
var a = toCStringArray([extractFilename(command)], args) if env == nil:
if env == nil: if poUsePath in options:
discard execv(command, a) discard execvp(sys_command, sys_args)
else: else:
discard execve(command, a, toCStringArray(env)) discard execv(sys_command, sys_args)
else: else:
var x = addCmdArgs(command, args) var c_env = envToCStringArray(env)
var a = toCStringArray(["sh", "-c"], [x]) if poUsePath in options:
if env == nil: discard execvpe(sys_command, sys_args, c_env)
discard execv("/bin/sh", a)
else: else:
discard execve("/bin/sh", a, toCStringArray(env)) discard execve(sys_command, sys_args, c_env)
# too risky to raise an exception here: # too risky to raise an exception here:
quit("execve call failed: " & $strerror(errno)) quit("execve call failed: " & $strerror(errno))
# Parent process. Copy process information. # Parent process. Copy process information.
@ -788,15 +791,15 @@ elif not defined(useNimRtl):
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".} proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
proc execCmd(command: string): int = proc execCmd(command: string): int =
result = csystem(command) result = csystem(command) shr 8
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) = proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
FD_ZERO(fd) FD_ZERO(fd)
for i in items(s): for i in items(s):
m = max(m, int(i.outHandle)) m = max(m, int(i.outHandle))
FD_SET(cint(i.outHandle), fd) FD_SET(cint(i.outHandle), fd)
proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) = proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) =
var i = 0 var i = 0
var L = s.len var L = s.len
while i < L: while i < L:
@ -807,26 +810,26 @@ elif not defined(useNimRtl):
inc(i) inc(i)
setLen(s, L) setLen(s, L)
proc select(readfds: var seq[PProcess], timeout = 500): int = proc select(readfds: var seq[PProcess], timeout = 500): int =
var tv: Ttimeval var tv: TTimeVal
tv.tv_sec = 0 tv.tv_sec = 0
tv.tv_usec = timeout * 1000 tv.tv_usec = timeout * 1000
var rd: TFdSet var rd: TFdSet
var m = 0 var m = 0
createFdSet((rd), readfds, m) createFdSet((rd), readfds, m)
if timeout != -1: if timeout != -1:
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv))) result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
else: else:
result = int(select(cint(m+1), addr(rd), nil, nil, nil)) result = int(select(cint(m+1), addr(rd), nil, nil, nil))
pruneProcessSet(readfds, (rd)) pruneProcessSet(readfds, (rd))
proc execCmdEx*(command: string, options: set[TProcessOption] = { proc execCmdEx*(command: string, options: set[TProcessOption] = {
poStdErrToStdOut, poUseShell}): tuple[ poStdErrToStdOut, poUsePath}): tuple[
output: TaintedString, output: TaintedString,
exitCode: int] {.tags: [FExecIO, FReadIO].} = exitCode: int] {.tags: [FExecIO, FReadIO].} =
## a convenience proc that runs the `command`, grabs all its output and ## a convenience proc that runs the `command`, grabs all its output and
## exit code and returns both. ## exit code and returns both.

View file

@ -149,7 +149,7 @@ proc readRow*(my: var TCsvParser, columns = 0): bool =
of '\c': my.bufpos = handleCR(my, my.bufpos) of '\c': my.bufpos = handleCR(my, my.bufpos)
of '\l': my.bufpos = handleLF(my, my.bufpos) of '\l': my.bufpos = handleLF(my, my.bufpos)
else: break else: break
of '\0': nil of '\0': discard
else: error(my, my.bufpos, my.sep & " expected") else: error(my, my.bufpos, my.sep & " expected")
break 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) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos) inc(c.bufpos)
else: else:
nil discard
proc handleOctChar(c: var TSqlLexer, xi: var int) = proc handleOctChar(c: var TSqlLexer, xi: var int) =
if c.buf[c.bufpos] in {'0'..'7'}: if c.buf[c.bufpos] in {'0'..'7'}:
@ -373,7 +373,7 @@ proc getOperator(c: var TSqlLexer, tok: var TToken) =
of '+': of '+':
if not trailingPlusMinus and buf[pos+1] notin operators and if not trailingPlusMinus and buf[pos+1] notin operators and
tok.literal.len > 0: break tok.literal.len > 0: break
of '*', '<', '>', '=': nil of '*', '<', '>', '=': discard
else: break else: break
add(tok.literal, buf[pos]) add(tok.literal, buf[pos])
inc(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) = proc ra(n: PSqlNode, s: var string, indent: int) =
if n == nil: return if n == nil: return
case n.kind case n.kind
of nkNone: nil of nkNone: discard
of nkIdent: of nkIdent:
if allCharsInSet(n.strVal, {'\33'..'\127'}): if allCharsInSet(n.strVal, {'\33'..'\127'}):
s.add(n.strVal) s.add(n.strVal)

249
lib/pure/selectors.nim Normal file
View file

@ -0,0 +1,249 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2013 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# TODO: Docs.
import tables, os, unsigned
when defined(windows):
import winlean
else:
import posix
type
TEvent* = enum
EvRead, EvWrite
TSelectorKey* = object
fd: cint
events: set[TEvent]
data: PObject
TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]]
PSelector* = ref object of PObject ## Selector interface.
fds*: TTable[cint, TSelectorKey]
registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].}
unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].}
selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].}
closeImpl*: proc (s: PSelector) {.nimcall.}
template initSelector(r: expr) =
new r
r.fds = initTable[cint, TSelectorKey]()
proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject):
TSelectorKey =
if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data)
proc unregister*(s: PSelector, fd: cint): TSelectorKey =
##
## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey``
## will only have the ``fd`` field set. This is an optimisation and may
## change in the future if a viable use case is presented.
if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd)
proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] =
##
## The ``events`` field of the returned ``key`` contains the original events
## for which the ``fd`` was bound. This is contrary to the ``events`` field
## of the ``TReadyInfo`` tuple which determines which events are ready
## on the ``fd``.
if not s.selectImpl.isNil: result = s.selectImpl(s, timeout)
proc close*(s: PSelector) =
if not s.closeImpl.isNil: s.closeImpl(s)
# ---- Select() ----------------------------------------------------------------
type
PSelectSelector* = ref object of PSelector ## Implementation of select()
proc ssRegister(s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey =
if s.fds.hasKey(fd):
raise newException(EInvalidValue, "FD already exists in selector.")
var sk = TSelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = sk
result = sk
proc ssUnregister(s: PSelector, fd: cint): TSelectorKey =
result = s.fds[fd]
s.fds.del(fd)
proc ssClose(s: PSelector) = nil
proc timeValFromMilliseconds(timeout: int): TTimeVal =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey],
m: var int) =
FD_ZERO(rd); FD_ZERO(wr)
for k, v in pairs(fds):
if EvRead in v.events:
m = max(m, int(k))
FD_SET(k, rd)
if EvWrite in v.events:
m = max(m, int(k))
FD_SET(k, wr)
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]):
seq[TReadyInfo] =
result = @[]
for k, v in pairs(fds):
var events: set[TEvent] = {}
if FD_ISSET(k, rd) != 0'i32:
events = events + {EvRead}
if FD_ISSET(k, wr) != 0'i32:
events = events + {EvWrite}
result.add((v, events))
proc select(fds: TTable[cint, TSelectorKey], timeout = 500):
seq[TReadyInfo] =
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
var rd, wr: TFdSet
var m = 0
createFdSet(rd, wr, fds, m)
var retCode = 0
if timeout != -1:
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
else:
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
if retCode < 0:
OSError(OSLastError())
elif retCode == 0:
return @[]
else:
return getReadyFDs(rd, wr, fds)
proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
result = select(s.fds, timeout)
proc newSelectSelector*(): PSelectSelector =
initSelector(result)
result.registerImpl = ssRegister
result.unregisterImpl = ssUnregister
result.selectImpl = ssSelect
result.closeImpl = ssClose
# ---- Epoll -------------------------------------------------------------------
when defined(linux):
import epoll
type
PEpollSelector* = ref object of PSelector
epollFD: cint
events: array[64, ptr epoll_event]
TDataWrapper = object
fd: cint
boundEvents: set[TEvent] ## The events which ``fd`` listens for.
data: PObject ## User object.
proc esRegister(s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey =
var es = PEpollSelector(s)
var event: epoll_event
if EvRead in events:
event.events = EPOLLIN
if EvWrite in events:
event.events = event.events or EPOLLOUT
var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd
dw.fd = fd
dw.boundEvents = events
dw.data = data
event.data.thePtr = dw
if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError())
result = TSelectorKey(fd: fd, events: events, data: data)
proc esUnregister(s: PSelector, fd: cint): TSelectorKey =
# We cannot find out the information about this ``fd`` from the epoll
# context. As such I will simply return an almost empty TSelectorKey.
var es = PEpollSelector(s)
if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
OSError(OSLastError())
# We could fill in the ``fds`` TTable to get the info, but that wouldn't
# be nice for our memory.
result = TSelectorKey(fd: fd, events: {}, data: nil)
proc esClose(s: PSelector) =
var es = PEpollSelector(s)
if es.epollFD.close() != 0: OSError(OSLastError())
dealloc(addr es.events) # TODO: Test this
proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
result = @[]
var es = PEpollSelector(s)
let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint)
if evNum < 0: OSError(OSLastError())
if evNum == 0: return @[]
for i in 0 .. <evNum:
var evSet: set[TEvent] = {}
if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
let dw = cast[ptr TDataWrapper](es.events[i].data.thePtr)
let selectorKey = TSelectorKey(fd: dw.fd, events: dw.boundEvents,
data: dw.data)
result.add((selectorKey, evSet))
proc newEpollSelector*(): PEpollSelector =
new result
result.epollFD = epoll_create(64)
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
if result.epollFD < 0:
OSError(OSLastError())
result.registerImpl = esRegister
result.unregisterImpl = esUnregister
result.closeImpl = esClose
result.selectImpl = esSelect
when isMainModule:
# Select()
import sockets
type
PSockWrapper = ref object of PObject
sock: TSocket
var sock = socket()
sock.connect("irc.freenode.net", TPort(6667))
var selector = newEpollSelector()
var data = PSockWrapper(sock: sock)
let key = selector.register(sock.getFD.cint, {EvRead}, data)
var i = 0
while true:
let ready = selector.select(1000)
echo ready.len
if ready.len > 0: echo ready[0].events
i.inc
if i == 6:
selector.close()
break

View file

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

View file

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

202
lib/pure/sockets2.nim Normal file
View file

@ -0,0 +1,202 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements a low-level cross-platform sockets interface. Look
## at the ``net`` module for the higher-level version.
import unsigned, os
when hostos == "solaris":
{.passl: "-lsocket -lnsl".}
when defined(Windows):
import winlean
else:
import posix
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
inet_ntoa
type
TPort* = distinct uint16 ## port type
TDomain* = enum ## domain, which specifies the protocol family of the
## created socket. Other domains than those that are listed
## here are unsupported.
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
AF_INET = 2, ## for network protocol IPv4 or
AF_INET6 = 23 ## for network protocol IPv6.
TType* = enum ## second argument to `socket` proc
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
SOCK_DGRAM = 2, ## datagram service or Datagram Sockets
SOCK_RAW = 3, ## raw protocols atop the network layer.
SOCK_SEQPACKET = 5 ## reliable sequenced packet service
TProtocol* = enum ## third argument to `socket` proc
IPPROTO_TCP = 6, ## Transmission control protocol.
IPPROTO_UDP = 17, ## User datagram protocol.
IPPROTO_IP, ## Internet protocol. Unsupported on Windows.
IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows.
IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows.
IPPROTO_ICMP ## Control message protocol. Unsupported on Windows.
TServent* {.pure, final.} = object ## information about a service
name*: string
aliases*: seq[string]
port*: TPort
proto*: string
Thostent* {.pure, final.} = object ## information about a given host
name*: string
aliases*: seq[string]
addrtype*: TDomain
length*: int
addrList*: seq[string]
when defined(windows):
let
OSInvalidSocket* = winlean.INVALID_SOCKET
else:
let
OSInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.}
## ``==`` for ports.
proc `$`*(p: TPort): string {.borrow.}
## returns the port number as a string
proc toInt*(domain: TDomain): cint
## Converts the TDomain enum to a platform-dependent ``cint``.
proc toInt*(typ: TType): cint
## Converts the TType enum to a platform-dependent ``cint``.
proc toInt*(p: TProtocol): cint
## Converts the TProtocol enum to a platform-dependent ``cint``.
when defined(posix):
proc toInt(domain: TDomain): cint =
case domain
of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6
else: nil
proc toInt(typ: TType): cint =
case typ
of SOCK_STREAM: result = posix.SOCK_STREAM
of SOCK_DGRAM: result = posix.SOCK_DGRAM
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
of SOCK_RAW: result = posix.SOCK_RAW
else: nil
proc toInt(p: TProtocol): cint =
case p
of IPPROTO_TCP: result = posix.IPPROTO_TCP
of IPPROTO_UDP: result = posix.IPPROTO_UDP
of IPPROTO_IP: result = posix.IPPROTO_IP
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
of IPPROTO_RAW: result = posix.IPPROTO_RAW
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
else: nil
else:
proc toInt(domain: TDomain): cint =
result = toU16(ord(domain))
proc toInt(typ: TType): cint =
result = cint(ord(typ))
proc toInt(p: TProtocol): cint =
result = cint(ord(p))
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
protocol: TProtocol = IPPROTO_TCP): TSocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs.
# TODO: The function which will use this will raise EOS.
socket(toInt(domain), toInt(typ), toInt(protocol))
proc close*(socket: TSocketHandle) =
## closes a socket.
when defined(windows):
discard winlean.closeSocket(socket)
else:
discard posix.close(socket)
# TODO: These values should not be discarded. An EOS should be raised.
# http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times
proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint =
result = bindSocket(socket, name, namelen)
proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} =
## Marks ``socket`` as accepting connections.
## ``Backlog`` specifies the maximum length of the
## queue of pending connections.
when defined(windows):
if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
else:
if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError())
proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM,
prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo =
##
##
## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``!
var hints: TAddrInfo
result = nil
hints.ai_family = toInt(af)
hints.ai_socktype = toInt(typ)
hints.ai_protocol = toInt(prot)
var gaiResult = getAddrInfo(address, $port, addr(hints), result)
if gaiResult != 0'i32:
when defined(windows):
OSError(OSLastError())
else:
raise newException(EOS, $gai_strerror(gaiResult))
proc dealloc*(ai: ptr TAddrInfo) =
freeaddrinfo(ai)
proc ntohl*(x: int32): int32 =
## Converts 32-bit integers from network to host byte order.
## On machines where the host byte order is the same as network byte order,
## this is a no-op; otherwise, it performs a 4-byte swap operation.
when cpuEndian == bigEndian: result = x
else: result = (x shr 24'i32) or
(x shr 8'i32 and 0xff00'i32) or
(x shl 8'i32 and 0xff0000'i32) or
(x shl 24'i32)
proc ntohs*(x: int16): int16 =
## Converts 16-bit integers from network to host byte order. On machines
## where the host byte order is the same as network byte order, this is
## a no-op; otherwise, it performs a 2-byte swap operation.
when cpuEndian == bigEndian: result = x
else: result = (x shr 8'i16) or (x shl 8'i16)
proc htonl*(x: int32): int32 =
## Converts 32-bit integers from host to network byte order. On machines
## where the host byte order is the same as network byte order, this is
## a no-op; otherwise, it performs a 4-byte swap operation.
result = sockets2.ntohl(x)
proc htons*(x: int16): int16 =
## Converts 16-bit positive integers from host to network byte order.
## On machines where the host byte order is the same as network byte
## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
result = sockets2.ntohs(x)
when defined(Windows):
var wsa: TWSADATA
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())

View file

@ -45,6 +45,16 @@ const
NewLines* = {'\13', '\10'} NewLines* = {'\13', '\10'}
## the set of characters a newline terminator can start with ## 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, proc toLower*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerChar".} = rtl, extern: "nsuToLowerChar".} =
## Converts `c` into lower case. This works only for the letters A-Z. ## 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.months = months
result.years = years 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: if year mod 400 == 0:
return true return true
elif year mod 100 == 0: elif year mod 100 == 0:
@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool =
else: else:
return false 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 # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
case month case month
of mFeb: result = if isLeapYear(year): 29 else: 28 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; ## &entity;
errors.add(errorMsg(x, "unknown entity: " & x.entityName)) errors.add(errorMsg(x, "unknown entity: " & x.entityName))
next(x) next(x)
of xmlEof: nil of xmlEof: discard
proc parseXml*(s: PStream, filename: string, proc parseXml*(s: PStream, filename: string,
errors: var seq[string]): PXmlNode = errors: var seq[string]): PXmlNode =
@ -110,7 +110,7 @@ proc parseXml*(s: PStream, filename: string,
of xmlElementOpen, xmlElementStart: of xmlElementOpen, xmlElementStart:
result = parse(x, errors) result = parse(x, errors)
break break
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: nil # just skip it of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it
of xmlError: of xmlError:
errors.add(errorMsg(x)) errors.add(errorMsg(x))
else: else:

View file

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

View file

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

View file

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

View file

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

View file

@ -16,8 +16,12 @@ const
type type
THandle* = int THandle* = int
LONG* = int32 LONG* = int32
ULONG* = int
PULONG* = ptr int
WINBOOL* = int32 WINBOOL* = int32
DWORD* = int32 DWORD* = int32
PDWORD* = ptr DWORD
LPINT* = ptr int32
HDC* = THandle HDC* = THandle
HGLRC* = THandle HGLRC* = THandle
@ -195,16 +199,31 @@ else:
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} 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.} importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {. proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} 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.} 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 const
FILE_ATTRIBUTE_ARCHIVE* = 32'i32 FILE_ATTRIBUTE_ARCHIVE* = 32'i32
FILE_ATTRIBUTE_COMPRESSED* = 2048'i32 FILE_ATTRIBUTE_COMPRESSED* = 2048'i32
@ -212,6 +231,7 @@ const
FILE_ATTRIBUTE_DIRECTORY* = 16'i32 FILE_ATTRIBUTE_DIRECTORY* = 16'i32
FILE_ATTRIBUTE_HIDDEN* = 2'i32 FILE_ATTRIBUTE_HIDDEN* = 2'i32
FILE_ATTRIBUTE_READONLY* = 1'i32 FILE_ATTRIBUTE_READONLY* = 1'i32
FILE_ATTRIBUTE_REPARSE_POINT* = 1024'i32
FILE_ATTRIBUTE_SYSTEM* = 4'i32 FILE_ATTRIBUTE_SYSTEM* = 4'i32
FILE_ATTRIBUTE_TEMPORARY* = 256'i32 FILE_ATTRIBUTE_TEMPORARY* = 256'i32
@ -284,7 +304,7 @@ else:
dwFileAttributes: int32): WINBOOL {. dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc copyFileA*(lpExistingFileName, lpNewFileName: CString, proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
bFailIfExists: cint): cint {. bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".} importc: "CopyFileA", stdcall, dynlib: "kernel32".}
@ -616,3 +636,76 @@ when not useWinUnicode:
proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
dynlib: "kernel32", importc: "UnmapViewOfFile".} dynlib: "kernel32", importc: "UnmapViewOfFile".}
type
TOVERLAPPED* {.final, pure.} = object
Internal*: DWORD
InternalHigh*: DWORD
Offset*: DWORD
OffsetHigh*: DWORD
hEvent*: THANDLE
POVERLAPPED* = ptr TOVERLAPPED
POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
para3: POVERLAPPED){.stdcall.}
TGUID* {.final, pure.} = object
D1*: int32
D2*: int16
D3*: int16
D4*: array [0..7, int8]
const
ERROR_IO_PENDING* = 997
proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE,
CompletionKey: DWORD,
NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall,
dynlib: "kernel32", importc: "CreateIoCompletionPort".}
proc GetQueuedCompletionStatus*(CompletionPort: THandle,
lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG,
lpOverlapped: ptr POverlapped,
dwMilliseconds: DWORD): WINBOOL{.stdcall,
dynlib: "kernel32", importc: "GetQueuedCompletionStatus".}
const
IOC_OUT* = 0x40000000
IOC_IN* = 0x80000000
IOC_WS2* = 0x08000000
IOC_INOUT* = IOC_IN or IOC_OUT
template WSAIORW*(x,y): expr = (IOC_INOUT or x or y)
const
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
var
WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [
0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8])
WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [
0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8])
proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer,
cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD,
lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED,
lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint
{.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".}
type
TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object
len*: ULONG
buf*: cstring
proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesReceived, flags: PDWORD, lpOverlapped: POverlapped,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}
proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}

View file

@ -109,11 +109,12 @@ type
cur*: cstring cur*: cstring
dataSize*: cint dataSize*: cint
finished*: TBsonBool finished*: TBsonBool
stack*: array[0..32 - 1, cint] ownsData*: TBsonBool
err*: cint
stackSize*: cint
stackPos*: cint stackPos*: cint
err*: cint ## Bitfield representing errors or warnings on this buffer stackPtr*: ptr csize
errstr*: cstring ## A string representation of the most recent error stack*: array[0..32 - 1, csize]
## or warning.
TDate* = int64 TDate* = int64
@ -141,6 +142,7 @@ proc print*(TBson: cstring, depth: cint) {.stdcall,
importc: "bson_print_raw", dynlib: bsondll.} importc: "bson_print_raw", dynlib: bsondll.}
## Print a string representation of a BSON object up to `depth`. ## Print a string representation of a BSON object up to `depth`.
proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data", proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data",
dynlib: bsondll.} dynlib: bsondll.}
## Return a pointer to the raw buffer stored by this bson object. ## 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 hosts*: ptr THostPort ## List of host/ports given by the replica set
name*: cstring ## Name of the replica set. name*: cstring ## Name of the replica set.
primary_connected*: TBsonBool ## Primary node connection status. 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 TMongo*{.pure, final.} = object ## mongo
primary*: ptr THostPort ## Primary connection info. primary*: ptr THostPort ## Primary connection info.
replset*: ptr TReplSet ## replset object if connected to a replica set. replset*: ptr TReplSet ## replset object if connected to a replica set.
sock*: cint ## Socket file descriptor. sock*: cint ## Socket file descriptor.
flags*: cint ## Flags on this connection object. flags*: cint ## Flags on this connection object.
conn_timeout_ms*: cint ## Connection timeout in milliseconds. conn_timeout_ms*: cint ## Connection timeout in milliseconds.
op_timeout_ms*: cint ## Read and write timeout in milliseconds. op_timeout_ms*: cint ## Read and write timeout in milliseconds.
connected*: TBsonBool ## Connection status. max_bson_size*: cint ## Largest BSON object allowed on this connection.
err*: TError ## Most recent driver error code. connected*: TBsonBool ## Connection status.
errstr*: array[0..128 - 1, char] ## String version of most recent driver error code. write_concern*: TWriteConcern ## The default write concern.
lasterrcode*: cint ## getlasterror code given by the server on error. err*: TError ## Most recent driver error code.
lasterrstr*: cstring ## getlasterror string generated by server. 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 TCursor*{.pure, final.} = object ## cursor
reply*: ptr TReply ## reply is owned by 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, proc connect*(conn: var TMongo, host: cstring = defaultHost,
port: cint = defaultPort): cint {.stdcall, 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. ## Connect to a single MongoDB server.
proc replsetInit*(conn: var TMongo, name: cstring){.stdcall, 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 ## You must always call this function when finished with the connection
## object. ## 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.} importc: "mongo_insert", dynlib: mongodll, discardable.}
## Insert a BSON document into a MongoDB server. This function ## Insert a BSON document into a MongoDB server. This function
## will fail if the supplied BSON struct is not UTF-8 or if ## 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 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*() = proc CRYPTO_malloc_init*() =
when not defined(windows): when not defined(windows):
CRYPTO_set_mem_functions(alloc, realloc, dealloc) CRYPTO_set_mem_functions(alloc, realloc, dealloc)
when True: proc SSL_CTX_ctrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: pointer): int{.
nil 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: else:
proc SslCtxSetCipherList*(arg0: PSSL_CTX, str: cstring): cInt{.cdecl, proc SslCtxSetCipherList*(arg0: PSSL_CTX, str: cstring): cInt{.cdecl,
dynlib: DLLSSLName, importc.} dynlib: DLLSSLName, importc.}
@ -288,7 +296,6 @@ else:
proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{. proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
cdecl, dynlib: DLLSSLName, importc.} cdecl, dynlib: DLLSSLName, importc.}
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int
proc SSLSetMode*(s: PSSL, mode: int): int proc SSLSetMode*(s: PSSL, mode: int): int
proc SSLCTXGetMode*(ctx: PSSL_CTX): int proc SSLCTXGetMode*(ctx: PSSL_CTX): int
proc SSLGetMode*(s: PSSL): int proc SSLGetMode*(s: PSSL): int
@ -417,15 +424,12 @@ else:
enc: cInt){.cdecl, dynlib: DLLUtilName, importc.} enc: cInt){.cdecl, dynlib: DLLUtilName, importc.}
# implementation # implementation
proc SSLCTXSetMode(ctx: PSSL_CTX, mode: int): int =
Result = SslCTXCtrl(ctx, SSL_CTRL_MODE, mode, nil)
proc SSLSetMode(s: PSSL, mode: int): int = 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 = 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 = 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. Read copying.txt for more details.
Copyright (c) 2004-2013 Andreas Rumpf. Copyright (c) 2004-2014 Andreas Rumpf.
All rights reserved. All rights reserved.

View file

@ -61,5 +61,5 @@ allowing you to create commercial applications.
Read copying.txt for more details. Read copying.txt for more details.
Copyright (c) 2004-2013 Andreas Rumpf. Copyright (c) 2004-2014 Andreas Rumpf.
All rights reserved. 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 """ discard """
output: '''(bar: bar) output: '''24
(bar: bar)
1244 1244
6 6
abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz
@ -8,6 +9,10 @@ abcdefghijklmnopqrstuvwxyz
import strutils import strutils
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
echo fac4
when true: when true:
proc test(foo: proc (x, y: int): bool) = proc test(foo: proc (x, y: int): bool) =
echo foo(5, 5) echo foo(5, 5)
@ -69,3 +74,15 @@ proc semiProblem() =
if false: echo "aye"; echo "indeed" if false: echo "aye"; echo "indeed"
semiProblem() 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 GC_setMaxPause 2_000
type type
TExpr = object ## abstract base class for an expression TExpr = object {.inheritable.} ## abstract base class for an expression
PLiteral = ref TLiteral PLiteral = ref TLiteral
TLiteral = object of TExpr TLiteral = object of TExpr
x: int 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) new(a)
a.field = "some string" 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: when false:
# Compiles unless you use var a: PA[string] # Compiles unless you use var a: PA[string]
type type

View file

@ -22,11 +22,11 @@ proc factory2(a, b: int): iterator (): int =
yield x yield x
inc x inc x
let foo = factory 1, 4 let foo = factory(1, 4)
for f in foo(): for f in foo():
echo f echo f
let foo2 = factory2 1,2 let foo2 = factory2(1,2)
for f in foo2(): echo f 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 import macros
template plus(a, b: expr): expr = template plus(a, b: expr): expr {.dirty} =
a + b a + b
macro call(e: expr): expr = macro call(e: expr): expr =

View file

@ -7,7 +7,7 @@ proc dumpit(n: PNimrodNode): string {.compileTime.} =
result = $n.kind result = $n.kind
add(result, "(") add(result, "(")
case n.kind 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 nnkNilLit: add(result, "nil")
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,23 @@
discard """ 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: int): int = x-1
proc foo(x, y: int): int = x-y proc foo(x, y: int): int = x-y
let x = foo 7.foo, # comment here let x = optarg foo 7.foo
foo(1, foo 8) let y = singlearg foo(1, foo 8)
# 12 = 6 - -6 let z = singlearg 1.foo foo 8
echo x
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 type
MachineId = distinct int64 MachineId* = distinct int64
CommitId = distinct int64 CommitId = distinct int64
proc `$`(id: MachineId): string {.borrow.} proc `$`*(id: MachineId): string {.borrow.}
proc `$`(id: CommitId): string {.borrow.} proc `$`(id: CommitId): string {.borrow.}
var var
@ -61,7 +61,7 @@ var
proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip
proc getMachine: MachineId = proc getMachine*(db: TDbConn): MachineId =
var name = "hostname"() var name = "hostname"()
if name.len == 0: if name.len == 0:
name = when defined(posix): getenv"HOSTNAME".string 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 (?,?,?)", result = db.insertId(sql"insert into Machine(name, os, cpu) values (?,?,?)",
name, system.hostOS, system.hostCPU).MachineId name, system.hostOS, system.hostCPU).MachineId
proc getCommit: CommitId = proc getCommit(db: TDbConn): CommitId =
const commLen = "commit ".len const commLen = "commit ".len
let hash = "git log -n 1"()[commLen..commLen+10] let hash = "git log -n 1"()[commLen..commLen+10]
let branch = "git symbolic-ref --short HEAD"() let branch = "git symbolic-ref --short HEAD"()
@ -115,7 +115,7 @@ proc open*() =
db = open(connection="testament.db", user="testament", password="", db = open(connection="testament.db", user="testament", password="",
database="testament") database="testament")
createDb() createDb()
thisMachine = getMachine() thisMachine = getMachine(db)
thisCommit = getCommit() thisCommit = getCommit(db)
proc close*() = close(db) proc close*() = close(db)

View file

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

View file

@ -159,3 +159,22 @@ proc generateHtml*(filename: string, commit: int) =
outfile.write(HtmlEnd) outfile.write(HtmlEnd)
close(db) close(db)
close(outfile) 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 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) = # proc runCaasTests(r: var TResults) =
# for test, output, status, mode in caasTestsRunner(): # for test, output, status, mode in caasTestsRunner():
# r.addResult(test, "", output & "-> " & $mode, # r.addResult(test, "", output & "-> " & $mode,
@ -259,6 +245,7 @@ proc main() =
var commit = 0 var commit = 0
discard parseInt(p.cmdLineRest.string, commit) discard parseInt(p.cmdLineRest.string, commit)
generateHtml(resultsFile, commit) generateHtml(resultsFile, commit)
generateJson(jsonFile, commit)
else: else:
quit usage 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 version 0.9.4
============= =============
- better debugging support for writes to locations - fix GC issues
- document new templating symbol binding rules - fix macros\tstringinterp.nim
- fix eval in macros.nim - test and fix showoff
- fix closure iterators
Bugs Bugs
@ -16,7 +17,6 @@ Bugs
- compilation of niminst takes way too long. looks like a regression - compilation of niminst takes way too long. looks like a regression
- docgen: sometimes effects are listed twice - docgen: sometimes effects are listed twice
- 'result' is not properly cleaned for NRVO --> use uninit checking instead - '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 ... - blocks can "export" an identifier but the CCG generates {} for them ...
- osproc execProcesses can deadlock if all processes fail (as experienced - osproc execProcesses can deadlock if all processes fail (as experienced
in c++ mode) in c++ mode)
@ -25,6 +25,9 @@ Bugs
version 0.9.x 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 - ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat - optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC - 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/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite" srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
srcdoc2: "packages/docutils/rst;packages/docutils/rstast" 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/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc" webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"