Merge branch 'devel' of git://github.com/Araq/Nimrod
This commit is contained in:
commit
eb9964fbbb
96 changed files with 3070 additions and 1912 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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", [])
|
|
||||||
|
|
||||||
|
|
||||||
17
doc/gc.txt
17
doc/gc.txt
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
----------------
|
----------------
|
||||||
|
|
|
||||||
233
doc/manual.txt
233
doc/manual.txt
|
|
@ -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.}
|
||||||
|
|
|
||||||
24
doc/tut1.txt
24
doc/tut1.txt
|
|
@ -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
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
5
koch.nim
5
koch.nim
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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) {.
|
||||||
|
|
|
||||||
|
|
@ -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].} =
|
||||||
|
|
|
||||||
|
|
@ -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
88
lib/posix/epoll.nim
Normal 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
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
485
lib/pure/asyncio2.nim
Normal 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"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 ------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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
267
lib/pure/logging.nim
Normal 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, [])
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
40
lib/pure/net.nim
Normal 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)
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
249
lib/pure/selectors.nim
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
202
lib/pure/sockets2.nim
Normal 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())
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) =
|
||||||
|
|
|
||||||
|
|
@ -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".}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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".}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
22
tests/collections/ttables.nim
Normal file
22
tests/collections/ttables.nim
Normal 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"
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
25
tests/gc/gcleak5.nim
Normal 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()
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
29
tests/macros/tdebugstmt.nim
Normal file
29
tests/macros/tdebugstmt.nim
Normal 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)
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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.} =
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
out: '''HELLO WORLD'''
|
output: '''HELLO WORLD'''
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import macros, strutils
|
import macros, strutils
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
15
tests/sets/testequivalence.nim
Normal file
15
tests/sets/testequivalence.nim
Normal 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]))), "==")
|
||||||
|
|
@ -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)
|
|
||||||
|
|
||||||
8
tests/stdlib/talgorithm.nim
Normal file
8
tests/stdlib/talgorithm.nim
Normal 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"
|
||||||
17
tests/template/tprefer_immediate.nim
Normal file
17
tests/template/tprefer_immediate.nim
Normal 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)
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 -----------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
457
tests/tester.nim
457
tests/tester.nim
|
|
@ -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()
|
|
||||||
|
|
||||||
11
todo.txt
11
todo.txt
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue