mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-15 03:52:44 +00:00
Merge branch 'devel' of git://github.com/Araq/Nimrod
This commit is contained in:
@@ -1129,7 +1129,7 @@ proc newType(kind: TTypeKind, owner: PSym): PType =
|
||||
result.align = 2 # default alignment
|
||||
result.id = getID()
|
||||
when debugIds:
|
||||
RegisterId(result)
|
||||
registerId(result)
|
||||
#if result.id < 2000 then
|
||||
# MessageOut(typeKindToStr[kind] & ' has id: ' & toString(result.id))
|
||||
|
||||
@@ -1166,7 +1166,7 @@ proc copyType(t: PType, owner: PSym, keepId: bool): PType =
|
||||
if keepId:
|
||||
result.id = t.id
|
||||
else:
|
||||
when debugIds: RegisterId(result)
|
||||
when debugIds: registerId(result)
|
||||
result.sym = t.sym # backend-info should not be copied
|
||||
|
||||
proc copySym(s: PSym, keepId: bool = false): PSym =
|
||||
@@ -1177,7 +1177,7 @@ proc copySym(s: PSym, keepId: bool = false): PSym =
|
||||
result.id = s.id
|
||||
else:
|
||||
result.id = getID()
|
||||
when debugIds: RegisterId(result)
|
||||
when debugIds: registerId(result)
|
||||
result.flags = s.flags
|
||||
result.magic = s.magic
|
||||
if s.kind == skModule:
|
||||
|
||||
@@ -561,14 +561,31 @@ proc strTableContains(t: TStrTable, n: PSym): bool =
|
||||
|
||||
proc strTableRawInsert(data: var TSymSeq, n: PSym) =
|
||||
var h: THash = n.name.h and high(data)
|
||||
while data[h] != nil:
|
||||
if data[h] == n:
|
||||
# allowed for 'export' feature:
|
||||
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
|
||||
return
|
||||
h = nextTry(h, high(data))
|
||||
assert(data[h] == nil)
|
||||
data[h] = n
|
||||
if sfImmediate notin n.flags:
|
||||
# fast path:
|
||||
while data[h] != nil:
|
||||
if data[h] == n:
|
||||
# allowed for 'export' feature:
|
||||
#InternalError(n.info, "StrTableRawInsert: " & n.name.s)
|
||||
return
|
||||
h = nextTry(h, high(data))
|
||||
assert(data[h] == nil)
|
||||
data[h] = n
|
||||
else:
|
||||
# slow path; we have to ensure immediate symbols are preferred for
|
||||
# symbol lookups.
|
||||
# consider the chain: foo (immediate), foo, bar, bar (immediate)
|
||||
# then bar (immediate) gets replaced with foo (immediate) and the non
|
||||
# immediate foo is picked! Thus we need to replace it with the first
|
||||
# slot that has in fact the same identifier stored in it!
|
||||
var favPos = -1
|
||||
while data[h] != nil:
|
||||
if data[h] == n: return
|
||||
if favPos < 0 and data[h].name.id == n.name.id: favPos = h
|
||||
h = nextTry(h, high(data))
|
||||
assert(data[h] == nil)
|
||||
data[h] = n
|
||||
if favPos >= 0: swap data[h], data[favPos]
|
||||
|
||||
proc symTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
|
||||
assert prevSym.name.h == newSym.name.h
|
||||
|
||||
@@ -343,7 +343,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
|
||||
tyInt..tyUInt64, tyRange, tyVar:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
|
||||
else: internalError("genAssignment(" & $ty.kind & ')')
|
||||
else: internalError("genAssignment: " & $ty.kind)
|
||||
|
||||
proc getDestLoc(p: BProc, d: var TLoc, typ: PType) =
|
||||
if d.k == locNone: getTemp(p, typ, d)
|
||||
@@ -1049,7 +1049,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
|
||||
app(tmp2.r, field.loc.r)
|
||||
tmp2.k = locTemp
|
||||
tmp2.t = field.loc.t
|
||||
tmp2.s = OnHeap
|
||||
tmp2.s = if isRef: OnHeap else: OnStack
|
||||
tmp2.heapRoot = tmp.r
|
||||
expr(p, it.sons[1], tmp2)
|
||||
if d.k == locNone:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -102,7 +102,7 @@ proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI =
|
||||
of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI
|
||||
of ccCDecl: result = DEFAULT_ABI
|
||||
else:
|
||||
GlobalError(info, "cannot map calling convention to FFI")
|
||||
globalError(info, "cannot map calling convention to FFI")
|
||||
|
||||
template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[]
|
||||
template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v
|
||||
@@ -164,7 +164,7 @@ proc packObject(x: PNode, typ: PType, res: pointer) =
|
||||
let field = getField(typ.n, i)
|
||||
pack(it, field.typ, res +! field.offset)
|
||||
else:
|
||||
GlobalError(x.info, "cannot pack unnamed tuple")
|
||||
globalError(x.info, "cannot pack unnamed tuple")
|
||||
|
||||
const maxPackDepth = 20
|
||||
var packRecCheck = 0
|
||||
@@ -193,7 +193,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
|
||||
of 4: awr(int32, v.intVal.int32)
|
||||
of 8: awr(int64, v.intVal.int64)
|
||||
else:
|
||||
GlobalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
|
||||
globalError(v.info, "cannot map value to FFI (tyEnum, tySet)")
|
||||
of tyFloat: awr(float, v.floatVal)
|
||||
of tyFloat32: awr(float32, v.floatVal)
|
||||
of tyFloat64: awr(float64, v.floatVal)
|
||||
@@ -207,7 +207,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
|
||||
elif v.kind in {nkStrLit..nkTripleStrLit}:
|
||||
awr(cstring, cstring(v.strVal))
|
||||
else:
|
||||
GlobalError(v.info, "cannot map pointer/proc value to FFI")
|
||||
globalError(v.info, "cannot map pointer/proc value to FFI")
|
||||
of tyPtr, tyRef, tyVar:
|
||||
if v.kind == nkNilLit:
|
||||
# nothing to do since the memory is 0 initialized anyway
|
||||
@@ -217,7 +217,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
|
||||
else:
|
||||
if packRecCheck > maxPackDepth:
|
||||
packRecCheck = 0
|
||||
GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
inc packRecCheck
|
||||
pack(v.sons[0], typ.sons[0], res +! sizeof(pointer))
|
||||
dec packRecCheck
|
||||
@@ -233,7 +233,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
|
||||
of tyDistinct, tyGenericInst:
|
||||
pack(v, typ.sons[0], res)
|
||||
else:
|
||||
GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
|
||||
|
||||
proc unpack(x: pointer, typ: PType, n: PNode): PNode
|
||||
|
||||
@@ -243,7 +243,7 @@ proc unpackObjectAdd(x: pointer, n, result: PNode) =
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
unpackObjectAdd(x, n.sons[i], result)
|
||||
of nkRecCase:
|
||||
GlobalError(result.info, "case objects cannot be unpacked")
|
||||
globalError(result.info, "case objects cannot be unpacked")
|
||||
of nkSym:
|
||||
var pair = newNodeI(nkExprColonExpr, result.info, 2)
|
||||
pair.sons[0] = n
|
||||
@@ -262,14 +262,14 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
|
||||
result = newNode(nkPar)
|
||||
result.typ = typ
|
||||
if typ.n.isNil:
|
||||
InternalError("cannot unpack unnamed tuple")
|
||||
internalError("cannot unpack unnamed tuple")
|
||||
unpackObjectAdd(x, typ.n, result)
|
||||
else:
|
||||
result = n
|
||||
if result.kind notin {nkObjConstr, nkPar}:
|
||||
GlobalError(n.info, "cannot map value from FFI")
|
||||
globalError(n.info, "cannot map value from FFI")
|
||||
if typ.n.isNil:
|
||||
GlobalError(n.info, "cannot unpack unnamed tuple")
|
||||
globalError(n.info, "cannot unpack unnamed tuple")
|
||||
for i in countup(ord(n.kind == nkObjConstr), sonsLen(n) - 1):
|
||||
var it = n.sons[i]
|
||||
if it.kind == nkExprColonExpr:
|
||||
@@ -288,7 +288,7 @@ proc unpackArray(x: pointer, typ: PType, n: PNode): PNode =
|
||||
else:
|
||||
result = n
|
||||
if result.kind != nkBracket:
|
||||
GlobalError(n.info, "cannot map value from FFI")
|
||||
globalError(n.info, "cannot map value from FFI")
|
||||
let baseSize = typ.sons[1].getSize
|
||||
for i in 0 .. < result.len:
|
||||
result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i])
|
||||
@@ -312,7 +312,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||
#echo "expected ", k, " but got ", result.kind
|
||||
#debug result
|
||||
return newNodeI(nkExceptBranch, n.info)
|
||||
#GlobalError(n.info, "cannot map value from FFI")
|
||||
#globalError(n.info, "cannot map value from FFI")
|
||||
result.field = v
|
||||
|
||||
template setNil() =
|
||||
@@ -337,19 +337,19 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||
of tyInt16: awi(nkInt16Lit, rd(int16, x))
|
||||
of tyInt32: awi(nkInt32Lit, rd(int32, x))
|
||||
of tyInt64: awi(nkInt64Lit, rd(int64, x))
|
||||
of tyUInt: awi(nkUIntLit, rd(uint, x).biggestInt)
|
||||
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).biggestInt)
|
||||
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).biggestInt)
|
||||
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).biggestInt)
|
||||
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).biggestInt)
|
||||
of tyUInt: awi(nkUIntLit, rd(uint, x).BiggestInt)
|
||||
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).BiggestInt)
|
||||
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).BiggestInt)
|
||||
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
|
||||
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
|
||||
of tyEnum:
|
||||
case typ.getSize
|
||||
of 1: awi(nkIntLit, rd(uint8, x).biggestInt)
|
||||
of 2: awi(nkIntLit, rd(uint16, x).biggestInt)
|
||||
of 4: awi(nkIntLit, rd(int32, x).biggestInt)
|
||||
of 8: awi(nkIntLit, rd(int64, x).biggestInt)
|
||||
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
|
||||
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
|
||||
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
|
||||
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
|
||||
else:
|
||||
GlobalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
|
||||
globalError(n.info, "cannot map value from FFI (tyEnum, tySet)")
|
||||
of tyFloat: awf(nkFloatLit, rd(float, x))
|
||||
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
|
||||
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
|
||||
@@ -374,7 +374,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||
n.sons[0] = unpack(p, typ.sons[0], n.sons[0])
|
||||
result = n
|
||||
else:
|
||||
GlobalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
of tyObject, tyTuple:
|
||||
result = unpackObject(x, typ, n)
|
||||
of tyArray, tyArrayConstr:
|
||||
@@ -391,7 +391,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
|
||||
result = unpack(x, typ.sons[0], n)
|
||||
else:
|
||||
# XXX what to do with 'array' here?
|
||||
GlobalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
|
||||
|
||||
proc fficast*(x: PNode, destTyp: PType): PNode =
|
||||
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer,
|
||||
@@ -414,7 +414,7 @@ proc fficast*(x: PNode, destTyp: PType): PNode =
|
||||
dealloc a
|
||||
|
||||
proc callForeignFunction*(call: PNode): PNode =
|
||||
InternalAssert call.sons[0].kind == nkPtrLit
|
||||
internalAssert call.sons[0].kind == nkPtrLit
|
||||
|
||||
var cif: TCif
|
||||
var sig: TParamList
|
||||
@@ -422,12 +422,12 @@ proc callForeignFunction*(call: PNode): PNode =
|
||||
for i in 1..call.len-1:
|
||||
sig[i-1] = mapType(call.sons[i].typ)
|
||||
if sig[i-1].isNil:
|
||||
GlobalError(call.info, "cannot map FFI type")
|
||||
globalError(call.info, "cannot map FFI type")
|
||||
|
||||
let typ = call.sons[0].typ
|
||||
if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1),
|
||||
mapType(typ.sons[0]), sig) != OK:
|
||||
GlobalError(call.info, "error in FFI call")
|
||||
globalError(call.info, "error in FFI call")
|
||||
|
||||
var args: TArgList
|
||||
let fn = cast[pointer](call.sons[0].intVal)
|
||||
|
||||
@@ -116,7 +116,8 @@ type
|
||||
TDep = tuple[e: PEnv, field: PSym]
|
||||
TEnv {.final.} = object of TObject
|
||||
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
|
||||
deps: seq[TDep] # dependencies
|
||||
up: PEnv
|
||||
@@ -571,7 +572,14 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode =
|
||||
# maybe later: (sfByCopy in local.flags)
|
||||
# add ``env.param = param``
|
||||
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:
|
||||
for e, field in items(scope.deps):
|
||||
# add ``env.up = env2``
|
||||
@@ -584,14 +592,19 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode =
|
||||
|
||||
proc generateIterClosureCreation(o: POuterContext; env: PEnv;
|
||||
scope: PNode): PSym =
|
||||
result = newClosureCreationVar(o, env)
|
||||
let cc = rawClosureCreation(o, env, result)
|
||||
var insertPoint = scope.sons[0]
|
||||
if insertPoint.kind == nkEmpty: scope.sons[0] = cc
|
||||
if env.createdVarComesFromIter or env.createdVar.isNil:
|
||||
# we have to create a new closure:
|
||||
result = newClosureCreationVar(o, env)
|
||||
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:
|
||||
assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList
|
||||
for x in cc: insertPoint.add(x)
|
||||
if env.createdVar == nil: env.createdVar = result
|
||||
result = env.createdVar
|
||||
env.createdVarComesFromIter = true
|
||||
|
||||
proc interestingIterVar(s: PSym): bool {.inline.} =
|
||||
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])
|
||||
if x != nil: n.sons[i] = x
|
||||
|
||||
proc liftIterSym*(n: PNode): PNode =
|
||||
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
||||
let iter = n.sym
|
||||
assert iter.kind == skIterator
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
var env = copySym(getHiddenParam(iter))
|
||||
env.kind = skLet
|
||||
var v = newNodeI(nkVarSection, n.info)
|
||||
addVar(v, newSymNode(env))
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
result.add(newCall(getSysSym"internalNew", env))
|
||||
result.add makeClosure(iter, env, n.info)
|
||||
|
||||
proc transformOuterProc(o: POuterContext, n: PNode): PNode =
|
||||
if n == nil: return nil
|
||||
case n.kind
|
||||
@@ -649,17 +678,22 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
|
||||
return indirectAccess(newSymNode(o.closureParam), local, n.info)
|
||||
|
||||
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
|
||||
if closure != nil:
|
||||
# we need to replace the lambda with '(lambda, env)':
|
||||
if local.kind == skIterator and local.typ.callConv == ccClosure:
|
||||
# consider: [i1, i2, i1] Since we merged the iterator's closure
|
||||
# with the captured owning variables, we need to generate the
|
||||
# closure generation code again:
|
||||
#if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s)
|
||||
# XXX why doesn't this work?
|
||||
|
||||
if local.kind == skIterator and local.typ.callConv == ccClosure:
|
||||
# consider: [i1, i2, i1] Since we merged the iterator's closure
|
||||
# with the captured owning variables, we need to generate the
|
||||
# closure generation code again:
|
||||
if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s)
|
||||
# XXX why doesn't this work?
|
||||
if closure.isNil:
|
||||
return liftIterSym(n)
|
||||
else:
|
||||
let createdVar = generateIterClosureCreation(o, closure,
|
||||
closure.attachedNode)
|
||||
return makeClosure(local, createdVar, n.info)
|
||||
|
||||
if closure != nil:
|
||||
# we need to replace the lambda with '(lambda, env)':
|
||||
|
||||
let a = closure.createdVar
|
||||
if a != nil:
|
||||
@@ -773,22 +807,6 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
|
||||
|
||||
# ------------------- iterator transformation --------------------------------
|
||||
|
||||
proc liftIterSym*(n: PNode): PNode =
|
||||
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
||||
let iter = n.sym
|
||||
assert iter.kind == skIterator
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
var env = copySym(getHiddenParam(iter))
|
||||
env.kind = skLet
|
||||
var v = newNodeI(nkVarSection, n.info)
|
||||
addVar(v, newSymNode(env))
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
result.add(newCall(getSysSym"internalNew", env))
|
||||
result.add makeClosure(iter, env, n.info)
|
||||
|
||||
proc liftForLoop*(body: PNode): PNode =
|
||||
# problem ahead: the iterator could be invoked indirectly, but then
|
||||
# we don't know what environment to create here:
|
||||
|
||||
@@ -339,4 +339,16 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
n.sons[0].sym.name, o.inSymChoice)
|
||||
|
||||
if result != nil and result.kind == skStub: loadStub(result)
|
||||
|
||||
|
||||
when false:
|
||||
proc qualifiedLookUpPreferImmediate*(c: PContext, n: PNode,
|
||||
flags = {checkUndeclared}): PSym =
|
||||
var o: TOverloadIter
|
||||
result = initOverloadIter(o, c, n)
|
||||
var a = result
|
||||
while a != nil:
|
||||
if sfImmediate in a.flags: return a
|
||||
a = nextOverloadIter(o, c, n)
|
||||
if result == nil and checkUndeclared in flags:
|
||||
localError(n.info, errUndeclaredIdentifier, n.considerAcc.s)
|
||||
result = errorSym(c, n)
|
||||
|
||||
@@ -135,7 +135,7 @@ proc interactivePasses =
|
||||
#setTarget(osNimrodVM, cpuNimrodVM)
|
||||
initDefines()
|
||||
defineSymbol("nimrodvm")
|
||||
when hasFFI: DefineSymbol("nimffi")
|
||||
when hasFFI: defineSymbol("nimffi")
|
||||
registerPass(verbosePass)
|
||||
registerPass(semPass)
|
||||
registerPass(evalPass)
|
||||
@@ -324,7 +324,7 @@ proc mainCommand* =
|
||||
wantMainModule()
|
||||
when hasTinyCBackend:
|
||||
extccomp.setCC("tcc")
|
||||
CommandCompileToC()
|
||||
commandCompileToC()
|
||||
else:
|
||||
rawMessage(errInvalidCommandX, command)
|
||||
of "js", "compiletojs":
|
||||
@@ -450,7 +450,8 @@ proc mainCommand* =
|
||||
echo " tries : ", gCacheTries
|
||||
echo " misses: ", gCacheMisses
|
||||
echo " int tries: ", gCacheIntTries
|
||||
echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float), ffDecimal, 3)
|
||||
echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
|
||||
ffDecimal, 3)
|
||||
|
||||
when SimiluateCaasMemReset:
|
||||
resetMemory()
|
||||
|
||||
@@ -35,6 +35,7 @@ type
|
||||
lex*: TLexer # the lexer that is used for parsing
|
||||
tok*: TToken # the current token
|
||||
inPragma: int
|
||||
inSemiStmtList: int
|
||||
|
||||
proc parseAll*(p: var TParser): PNode
|
||||
proc openParser*(p: var TParser, filename: string, inputstream: PLLStream)
|
||||
@@ -455,11 +456,13 @@ proc complexOrSimpleStmt(p: var TParser): PNode
|
||||
proc simpleExpr(p: var TParser, mode = pmNormal): PNode
|
||||
|
||||
proc semiStmtList(p: var TParser, result: PNode) =
|
||||
inc p.inSemiStmtList
|
||||
result.add(complexOrSimpleStmt(p))
|
||||
while p.tok.tokType == tkSemiColon:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
result.add(complexOrSimpleStmt(p))
|
||||
dec p.inSemiStmtList
|
||||
result.kind = nkStmtListExpr
|
||||
|
||||
proc parsePar(p: var TParser): PNode =
|
||||
@@ -642,8 +645,7 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
|
||||
#| | '.' optInd ('type' | 'addr' | symbol) generalizedLit?
|
||||
#| | '[' optInd indexExprList optPar ']'
|
||||
#| | '{' optInd indexExprList optPar '}'
|
||||
#| | &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax
|
||||
#| (doBlock | macroColon)?
|
||||
#| | &( '`'|IDENT|literal|'cast') expr # command syntax
|
||||
result = r
|
||||
while p.tok.indent < 0:
|
||||
case p.tok.tokType
|
||||
@@ -672,16 +674,18 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
|
||||
let a = result
|
||||
result = newNodeP(nkCommand, p)
|
||||
addSon(result, a)
|
||||
while p.tok.tokType != tkEof:
|
||||
let a = parseExpr(p)
|
||||
addSon(result, a)
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
if p.tok.tokType == tkDo:
|
||||
parseDoBlocks(p, result)
|
||||
else:
|
||||
result = parseMacroColon(p, result)
|
||||
addSon result, parseExpr(p)
|
||||
when false:
|
||||
while p.tok.tokType != tkEof:
|
||||
let a = parseExpr(p)
|
||||
addSon(result, a)
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
if p.tok.tokType == tkDo:
|
||||
parseDoBlocks(p, result)
|
||||
else:
|
||||
result = parseMacroColon(p, result)
|
||||
break
|
||||
else:
|
||||
break
|
||||
@@ -1103,7 +1107,9 @@ proc parseExprStmt(p: var TParser): PNode =
|
||||
#| doBlocks
|
||||
#| / macroColon
|
||||
#| ))?
|
||||
inc p.inPragma
|
||||
var a = simpleExpr(p)
|
||||
dec p.inPragma
|
||||
if p.tok.tokType == tkEquals:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
@@ -1878,14 +1884,18 @@ proc parseStmt(p: var TParser): PNode =
|
||||
parMessage(p, errComplexStmtRequiresInd)
|
||||
result = ast.emptyNode
|
||||
else:
|
||||
result = newNodeP(nkStmtList, p)
|
||||
while true:
|
||||
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
|
||||
let a = simpleStmt(p)
|
||||
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||
result.add(a)
|
||||
if p.tok.tokType != tkSemiColon: break
|
||||
getTok(p)
|
||||
if p.inSemiStmtList > 0:
|
||||
result = simpleStmt(p)
|
||||
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||
else:
|
||||
result = newNodeP(nkStmtList, p)
|
||||
while true:
|
||||
if p.tok.indent >= 0: parMessage(p, errInvalidIndentation)
|
||||
let a = simpleStmt(p)
|
||||
if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
|
||||
result.add(a)
|
||||
if p.tok.tokType != tkSemiColon: break
|
||||
getTok(p)
|
||||
|
||||
proc parseAll(p: var TParser): PNode =
|
||||
result = newNodeP(nkStmtList, p)
|
||||
|
||||
@@ -83,7 +83,9 @@ proc commonType*(x, y: PType): PType =
|
||||
elif a.kind == tyTypeDesc:
|
||||
# turn any concrete typedesc into the abstract typedesc type
|
||||
if a.sons == nil: result = a
|
||||
else: result = newType(tyTypeDesc, a.owner)
|
||||
else:
|
||||
result = newType(tyTypeDesc, a.owner)
|
||||
rawAddSon(result, newType(tyNone, a.owner))
|
||||
elif b.kind in {tyArray, tyArrayConstr, tySet, tySequence} and
|
||||
a.kind == b.kind:
|
||||
# check for seq[empty] vs. seq[int]
|
||||
|
||||
@@ -42,7 +42,7 @@ type
|
||||
|
||||
TExprFlag* = enum
|
||||
efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType,
|
||||
efAllowDestructor
|
||||
efAllowDestructor, efWantValue
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
PContext* = ref TContext
|
||||
@@ -211,6 +211,7 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
||||
|
||||
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
|
||||
let typedesc = makeTypeDesc(c, typ)
|
||||
rawAddSon(typedesc, newTypeS(tyNone, c))
|
||||
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
|
||||
return newSymNode(sym, info)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
result.typ = errorType(c)
|
||||
|
||||
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
result = semExpr(c, n, flags)
|
||||
result = semExpr(c, n, flags+{efWantValue})
|
||||
if result.isNil or result.kind == nkEmpty:
|
||||
# do not produce another redundant error message:
|
||||
#raiseRecoverableError("")
|
||||
@@ -1706,8 +1706,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result = n
|
||||
result.typ = t
|
||||
result.kind = nkObjConstr
|
||||
t = skipTypes(t, abstractInst)
|
||||
if t.kind == tyRef: t = skipTypes(t.sons[0], abstractInst)
|
||||
t = skipTypes(t, {tyGenericInst})
|
||||
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst})
|
||||
if t.kind != tyObject:
|
||||
localError(n.info, errGenerated, "object constructor needs an object type")
|
||||
return
|
||||
@@ -1819,6 +1819,10 @@ proc semExport(c: PContext, n: PNode): PNode =
|
||||
c.module.ast.add x
|
||||
result = n
|
||||
|
||||
proc setGenericParams(c: PContext, n: PNode) =
|
||||
for i in 1 .. <n.len:
|
||||
n[i].typ = semTypeNode(c, n[i], nil)
|
||||
|
||||
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
result = n
|
||||
if gCmd == cmdIdeTools: suggestExpr(c, n)
|
||||
@@ -1924,10 +1928,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
else:
|
||||
#liMessage(n.info, warnUser, renderTree(n));
|
||||
result = semIndirectOp(c, n, flags)
|
||||
elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and
|
||||
isSymChoice(n[0][0]):
|
||||
elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]):
|
||||
# indirectOp can deal with explicit instantiations; the fixes
|
||||
# the 'newSeq[T](x)' bug
|
||||
setGenericParams(c, n.sons[0])
|
||||
result = semDirectOp(c, n, flags)
|
||||
elif nfDelegate in n.flags:
|
||||
elif isSymChoice(n.sons[0]) or nfDelegate in n.flags:
|
||||
result = semDirectOp(c, n, flags)
|
||||
else:
|
||||
result = semIndirectOp(c, n, flags)
|
||||
@@ -1993,7 +1999,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
result = semStaticExpr(c, n)
|
||||
of nkAsgn: result = semAsgn(c, n)
|
||||
of nkBlockStmt, nkBlockExpr: result = semBlock(c, n)
|
||||
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n)
|
||||
of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags)
|
||||
of nkRaiseStmt: result = semRaise(c, n)
|
||||
of nkVarSection: result = semVarOrLet(c, n, skVar)
|
||||
of nkLetSection: result = semVarOrLet(c, n, skLet)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -215,8 +215,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
|
||||
checkMinSonsLen(a, 3)
|
||||
var L = sonsLen(a)
|
||||
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc},
|
||||
ctx)
|
||||
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
|
||||
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
|
||||
for j in countup(0, L-3):
|
||||
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
|
||||
@@ -226,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
if (a.kind != nkIdentDefs): illFormedAst(a)
|
||||
checkMinSonsLen(a, 3)
|
||||
var L = sonsLen(a)
|
||||
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc},
|
||||
ctx)
|
||||
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
|
||||
# do not perform symbol lookup for default expressions
|
||||
for j in countup(0, L-3):
|
||||
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
|
||||
@@ -281,8 +279,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
if (a.kind != nkIdentDefs): illFormedAst(a)
|
||||
checkMinSonsLen(a, 3)
|
||||
var L = sonsLen(a)
|
||||
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc},
|
||||
ctx)
|
||||
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
|
||||
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
|
||||
for j in countup(0, L-3):
|
||||
addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
|
||||
|
||||
@@ -740,7 +740,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
# we fill it out later. For magic generics like 'seq', it won't be filled
|
||||
# so we use tyEmpty instead of nil to not crash for strange conversions
|
||||
# like: mydata.seq
|
||||
rawAddSon(s.typ, newTypeS(tyEmpty, c))
|
||||
rawAddSon(s.typ, newTypeS(tyNone, c))
|
||||
s.ast = a
|
||||
inc c.inGenericContext
|
||||
var body = semTypeNode(c, a.sons[2], nil)
|
||||
@@ -748,7 +748,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
|
||||
if body != nil:
|
||||
body.sym = s
|
||||
body.size = -1 # could not be computed properly
|
||||
s.typ.sons[sonsLen(s.typ) - 1] = body
|
||||
s.typ.sons[sonsLen(s.typ) - 1] = body
|
||||
popOwner()
|
||||
closeScope(c)
|
||||
elif a.sons[2].kind != nkEmpty:
|
||||
@@ -972,6 +972,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
else:
|
||||
s = n[namePos].sym
|
||||
typeIsDetermined = s.typ == nil
|
||||
s.ast = n
|
||||
s.scope = c.currentScope
|
||||
|
||||
# if typeIsDetermined: assert phase == stepCompileBody
|
||||
# else: assert phase == stepDetermineType
|
||||
# before compiling the proc body, set as current the scope
|
||||
@@ -1206,7 +1209,7 @@ proc usesResult(n: PNode): bool =
|
||||
for c in n:
|
||||
if usesResult(c): return true
|
||||
|
||||
proc semStmtList(c: PContext, n: PNode): PNode =
|
||||
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# these must be last statements in a block:
|
||||
const
|
||||
LastBlockStmts = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
|
||||
@@ -1247,12 +1250,14 @@ proc semStmtList(c: PContext, n: PNode): PNode =
|
||||
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
|
||||
voidContext = true
|
||||
n.typ = enforceVoidContext
|
||||
if i != last or voidContext or c.inTypeClass > 0:
|
||||
if i == last and efWantValue in flags:
|
||||
n.typ = n.sons[i].typ
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
elif i != last or voidContext or c.inTypeClass > 0:
|
||||
discardCheck(c, n.sons[i])
|
||||
else:
|
||||
n.typ = n.sons[i].typ
|
||||
if not isEmptyType(n.typ):
|
||||
n.kind = nkStmtListExpr
|
||||
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
|
||||
case n.sons[i].kind
|
||||
of nkVarSection, nkLetSection:
|
||||
let (outer, inner) = insertDestructors(c, n.sons[i])
|
||||
|
||||
@@ -358,7 +358,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner =
|
||||
of nkConstSection: result = analyseConstSection(c, n)
|
||||
of nkTypeSection, nkCommentStmt: result = toVoid
|
||||
of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt,
|
||||
nkElifBranch, nkElse, nkExceptBranch, nkOfBranch:
|
||||
nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally:
|
||||
for i in 0 .. <n.len: discard analyse(c, n[i])
|
||||
result = toVoid
|
||||
of nkBreakStmt, nkContinueStmt: result = toVoid
|
||||
|
||||
@@ -681,12 +681,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
of tySequence, tySet, tyArray, tyOpenArray,
|
||||
tyVar, tyPtr, tyRef, tyProc:
|
||||
# XXX: this is a bit strange, but proc(s: seq)
|
||||
# produces tySequence(tyGenericParam, null).
|
||||
# produces tySequence(tyGenericParam, tyNone).
|
||||
# This also seems to be true when creating aliases
|
||||
# like: type myseq = distinct seq.
|
||||
# Maybe there is another better place to associate
|
||||
# the seq type class with the seq identifier.
|
||||
if paramType.kind == tySequence and paramType.lastSon == nil:
|
||||
if paramType.kind == tySequence and paramType.lastSon.kind == tyNone:
|
||||
let typ = c.newTypeWithSons(tyBuiltInTypeClass,
|
||||
@[newTypeS(paramType.kind, c)])
|
||||
result = addImplicitGeneric(typ)
|
||||
@@ -885,8 +885,12 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
for i in countup(1, sonsLen(n)-1):
|
||||
var elem = semGenericParamInInvokation(c, n.sons[i])
|
||||
addToResult(elem)
|
||||
elif s.typ.kind != tyGenericBody:
|
||||
#we likely got code of the form TypeA[TypeB] where TypeA is
|
||||
#not generic.
|
||||
localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
|
||||
return newOrPrevType(tyError, prev, c)
|
||||
else:
|
||||
internalAssert s.typ.kind == tyGenericBody
|
||||
|
||||
var m = newCandidate(c, s, n)
|
||||
matches(c, n, copyTree(n), m)
|
||||
@@ -1133,15 +1137,26 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
of mNil: setMagicType(m, tyNil, ptrSize)
|
||||
of mExpr: setMagicType(m, tyExpr, 0)
|
||||
of mStmt: setMagicType(m, tyStmt, 0)
|
||||
of mTypeDesc: setMagicType(m, tyTypeDesc, 0)
|
||||
of mTypeDesc:
|
||||
setMagicType(m, tyTypeDesc, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mVoidType: setMagicType(m, tyEmpty, 0)
|
||||
of mArray: setMagicType(m, tyArray, 0)
|
||||
of mOpenArray: setMagicType(m, tyOpenArray, 0)
|
||||
of mVarargs: setMagicType(m, tyVarargs, 0)
|
||||
of mRange: setMagicType(m, tyRange, 0)
|
||||
of mSet: setMagicType(m, tySet, 0)
|
||||
of mSeq: setMagicType(m, tySequence, 0)
|
||||
of mOrdinal: setMagicType(m, tyOrdinal, 0)
|
||||
of mArray:
|
||||
setMagicType(m, tyArray, 0)
|
||||
of mOpenArray:
|
||||
setMagicType(m, tyOpenArray, 0)
|
||||
of mVarargs:
|
||||
setMagicType(m, tyVarargs, 0)
|
||||
of mRange:
|
||||
setMagicType(m, tyRange, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mSet:
|
||||
setMagicType(m, tySet, 0)
|
||||
of mSeq:
|
||||
setMagicType(m, tySequence, 0)
|
||||
of mOrdinal:
|
||||
setMagicType(m, tyOrdinal, 0)
|
||||
rawAddSon(m.typ, newTypeS(tyNone, c))
|
||||
of mPNimrodNode: discard
|
||||
else: localError(m.info, errTypeExpected)
|
||||
|
||||
@@ -1165,8 +1180,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
typ = semTypeNode(c, constraint, nil)
|
||||
if typ.kind != tyStatic or typ.len == 0:
|
||||
if typ.kind == tyTypeDesc:
|
||||
if typ.len == 0:
|
||||
typ = newTypeS(tyTypeDesc, c)
|
||||
if typ.sons[0].kind == tyNone:
|
||||
typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
|
||||
else:
|
||||
typ = semGenericConstraints(c, typ)
|
||||
|
||||
|
||||
@@ -358,7 +358,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
if lookup != nil:
|
||||
result = lookup
|
||||
if tfUnresolved in t.flags: result = result.base
|
||||
elif t.sonsLen > 0:
|
||||
elif t.sons[0].kind != tyNone:
|
||||
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
|
||||
|
||||
of tyUserTypeClass:
|
||||
|
||||
@@ -105,6 +105,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
||||
var bound = binding[i].typ
|
||||
if bound != nil and formalTypeParam.kind != tyTypeDesc:
|
||||
bound = bound.skipTypes({tyTypeDesc})
|
||||
assert bound != nil
|
||||
put(c.bindings, formalTypeParam, bound)
|
||||
|
||||
proc newCandidate*(ctx: PContext, callee: PSym,
|
||||
@@ -562,10 +563,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
#if result < isGeneric: result = isNone
|
||||
if result notin {isNone, isGeneric}:
|
||||
result = typeRangeRel(f, a)
|
||||
elif skipTypes(f, {tyRange}).kind == a.kind:
|
||||
result = isIntConv
|
||||
elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
|
||||
result = isConvertible # a convertible to f
|
||||
else:
|
||||
if skipTypes(f, {tyRange}).kind == a.kind:
|
||||
result = isIntConv
|
||||
elif isConvertibleToRange(skipTypes(f, {tyRange}), a):
|
||||
result = isConvertible # a convertible to f
|
||||
of tyInt: result = handleRange(f, a, tyInt8, tyInt32)
|
||||
of tyInt8: result = handleRange(f, a, tyInt8, tyInt8)
|
||||
of tyInt16: result = handleRange(f, a, tyInt8, tyInt16)
|
||||
@@ -636,7 +638,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
of tyOrdinal:
|
||||
if isOrdinalType(a):
|
||||
var x = if a.kind == tyOrdinal: a.sons[0] else: a
|
||||
if f.sonsLen == 0:
|
||||
if f.sons[0].kind == tyNone:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, f.sons[0], x)
|
||||
@@ -736,7 +738,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
of tyGenericInst:
|
||||
let roota = a.skipGenericAlias
|
||||
let rootf = f.skipGenericAlias
|
||||
if a.kind == tyGenericInst and roota.base == rootf.base :
|
||||
if a.kind == tyGenericInst and roota.base == rootf.base:
|
||||
for i in 1 .. rootf.sonsLen-2:
|
||||
let ff = rootf.sons[i]
|
||||
let aa = roota.sons[i]
|
||||
@@ -845,7 +847,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
# by a tyTypeDesc params. Unfortunately, this requires more substantial
|
||||
# changes in semtypinst and elsewhere.
|
||||
if a.kind == tyTypeDesc:
|
||||
if f.sons == nil or f.sons.len == 0:
|
||||
if f.sonsLen == 0:
|
||||
result = isGeneric
|
||||
else:
|
||||
internalAssert a.sons != nil and a.sons.len > 0
|
||||
@@ -854,7 +856,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
else:
|
||||
result = isNone
|
||||
else:
|
||||
if f.sonsLen > 0:
|
||||
if f.sonsLen > 0 and f.sons[0].kind != tyNone:
|
||||
result = typeRel(c, f.lastSon, a)
|
||||
else:
|
||||
result = isGeneric
|
||||
@@ -883,7 +885,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||
var prev = PType(idTableGet(c.bindings, f))
|
||||
if prev == nil:
|
||||
if a.kind == tyTypeDesc:
|
||||
if f.sonsLen == 0:
|
||||
if f.sons[0].kind == tyNone:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, f.sons[0], a.sons[0])
|
||||
|
||||
@@ -47,7 +47,8 @@ proc equalParams*(a, b: PNode): TParamsEquality
|
||||
# returns whether the parameter lists of the procs a, b are exactly the same
|
||||
proc isOrdinalType*(t: PType): bool
|
||||
proc enumHasHoles*(t: PType): bool
|
||||
const
|
||||
|
||||
const
|
||||
abstractPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyDistinct, tyOrdinal,
|
||||
tyConst, tyMutable, tyTypeDesc}
|
||||
abstractVar* = {tyVar, tyGenericInst, tyDistinct, tyOrdinal,
|
||||
@@ -451,7 +452,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
of tyProc: "proc"
|
||||
of tyObject: "object"
|
||||
of tyTuple: "tuple"
|
||||
else: (internalAssert false; "")
|
||||
else: (internalAssert(false); "")
|
||||
of tyUserTypeClassInst:
|
||||
let body = t.base
|
||||
result = body.sym.name.s & "["
|
||||
@@ -1257,7 +1258,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool =
|
||||
return true
|
||||
|
||||
if t.kind == tyTypeDesc:
|
||||
if t.sonsLen == 0: return true
|
||||
if t.sons[0].kind == tyNone: return true
|
||||
if containsGenericTypeIter(t.base, closure): return true
|
||||
return false
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import ast except getstr
|
||||
|
||||
import
|
||||
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, unsigned,
|
||||
parser, vmdeps, idents, trees, renderer, options
|
||||
parser, vmdeps, idents, trees, renderer, options, transf
|
||||
|
||||
from semfold import leValueConv, ordinalValToString
|
||||
from evaltempl import evalTemplate
|
||||
@@ -337,7 +337,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
|
||||
asgnRef(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
|
||||
of opcWrGlobal:
|
||||
asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
|
||||
of opcLdArr:
|
||||
of opcLdArr, opcLdArrRef:
|
||||
# a = b[c]
|
||||
let rb = instr.regB
|
||||
let rc = instr.regC
|
||||
@@ -348,7 +348,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
|
||||
assert regs[rb].kind != nkMetaNode
|
||||
let src = regs[rb]
|
||||
if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
|
||||
asgnComplex(regs[ra], src.sons[idx])
|
||||
if instr.opcode == opcLdArrRef and false:
|
||||
# XXX activate when seqs are fixed
|
||||
asgnRef(regs[ra], src.sons[idx])
|
||||
else:
|
||||
asgnComplex(regs[ra], src.sons[idx])
|
||||
else:
|
||||
stackTrace(c, tos, pc, errIndexOutOfBounds)
|
||||
of opcLdStrIdx:
|
||||
@@ -379,9 +383,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
|
||||
# a = b.c
|
||||
let rb = instr.regB
|
||||
let rc = instr.regC
|
||||
# XXX this creates a wrong alias
|
||||
#Message(c.debug[pc], warnUser, $regs[rb].safeLen & " " & $rc)
|
||||
asgnComplex(regs[ra], regs[rb].sons[rc])
|
||||
of opcLdObjRef:
|
||||
# a = b.c
|
||||
let rb = instr.regB
|
||||
let rc = instr.regC
|
||||
# XXX activate when seqs are fixed
|
||||
asgnComplex(regs[ra], regs[rb].sons[rc])
|
||||
#asgnRef(regs[ra], regs[rb].sons[rc])
|
||||
of opcWrObj:
|
||||
# a.b = c
|
||||
let rb = instr.regB
|
||||
@@ -1037,7 +1047,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
|
||||
# XXX only supports 'name' for now; we can use regC to encode the
|
||||
# type trait operation
|
||||
decodeB(nkStrLit)
|
||||
let typ = regs[rb].sym.typ.skipTypes({tyTypeDesc})
|
||||
var typ = regs[rb].typ
|
||||
internalAssert typ != nil
|
||||
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
|
||||
regs[ra].strVal = typ.typeToString(preferExported)
|
||||
of opcGlobalOnce:
|
||||
let rb = instr.regBx
|
||||
@@ -1066,6 +1078,7 @@ proc execute(c: PCtx, start: int): PNode =
|
||||
result = rawExecute(c, start, tos)
|
||||
|
||||
proc evalStmt*(c: PCtx, n: PNode) =
|
||||
let n = transformExpr(c.module, n)
|
||||
let start = genStmt(c, n)
|
||||
# execute new instructions; this redundant opcEof check saves us lots
|
||||
# of allocations in 'execute':
|
||||
@@ -1073,6 +1086,7 @@ proc evalStmt*(c: PCtx, n: PNode) =
|
||||
discard execute(c, start)
|
||||
|
||||
proc evalExpr*(c: PCtx, n: PNode): PNode =
|
||||
let n = transformExpr(c.module, n)
|
||||
let start = genExpr(c, n)
|
||||
assert c.code[start].opcode != opcEof
|
||||
result = execute(c, start)
|
||||
@@ -1115,6 +1129,7 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
|
||||
const evalPass* = makePass(myOpen, nil, myProcess, myProcess)
|
||||
|
||||
proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
|
||||
let n = transformExpr(module, n)
|
||||
setupGlobalCtx(module)
|
||||
var c = globalCtx
|
||||
c.mode = mode
|
||||
@@ -1168,7 +1183,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
|
||||
# doesn't end up in the parameter:
|
||||
#InternalAssert tos.slots.len >= L
|
||||
# return value:
|
||||
tos.slots[0] = newNodeIT(nkNilLit, n.info, sym.typ.sons[0])
|
||||
tos.slots[0] = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
|
||||
# setup parameters:
|
||||
for i in 1 .. < min(tos.slots.len, L):
|
||||
tos.slots[i] = setupMacroParam(n.sons[i])
|
||||
|
||||
@@ -34,9 +34,11 @@ type
|
||||
opcAsgnComplex,
|
||||
|
||||
opcLdArr, # a = b[c]
|
||||
opcLdArrRef,
|
||||
opcWrArr, # a[b] = c
|
||||
opcWrArrRef,
|
||||
opcLdObj, # a = b.c
|
||||
opcLdObjRef,
|
||||
opcWrObj, # a.b = c
|
||||
opcWrObjRef,
|
||||
opcAddr,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -13,9 +13,18 @@ import
|
||||
unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef,
|
||||
trees, intsets, rodread, magicsys, options
|
||||
|
||||
from os import splitFile
|
||||
|
||||
when hasFFI:
|
||||
import evalffi
|
||||
|
||||
type
|
||||
TGenFlag = enum gfNone, gfAddrOf
|
||||
TGenFlags = set[TGenFlag]
|
||||
|
||||
proc debugInfo(info: TLineInfo): string =
|
||||
result = info.toFilename.splitFile.name & ":" & $info.line
|
||||
|
||||
proc codeListing(c: PCtx, result: var string, start=0) =
|
||||
# first iteration: compute all necessary labels:
|
||||
var jumpTargets = initIntSet()
|
||||
@@ -44,7 +53,7 @@ proc codeListing(c: PCtx, result: var string, start=0) =
|
||||
else:
|
||||
result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA, x.regBx-wordExcess)
|
||||
result.add("\t#")
|
||||
result.add(toFileLine(c.debug[i]))
|
||||
result.add(debugInfo(c.debug[i]))
|
||||
result.add("\n")
|
||||
inc i
|
||||
|
||||
@@ -100,7 +109,7 @@ proc patch(c: PCtx, p: TPosition) =
|
||||
uint32(diff+wordExcess) shl 16'u32).TInstr
|
||||
|
||||
proc getSlotKind(t: PType): TSlotKind =
|
||||
case t.skipTypes(abstractRange).kind
|
||||
case t.skipTypes(abstractRange-{tyTypeDesc}).kind
|
||||
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
slotTempInt
|
||||
of tyString, tyCString:
|
||||
@@ -190,20 +199,20 @@ template withBlock(labl: PSym; body: stmt) {.immediate, dirty.} =
|
||||
body
|
||||
popBlock(c, oldLen)
|
||||
|
||||
proc gen(c: PCtx; n: PNode; dest: var TDest)
|
||||
proc gen(c: PCtx; n: PNode; dest: TRegister) =
|
||||
proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {})
|
||||
proc gen(c: PCtx; n: PNode; dest: TRegister; flags: TGenFlags = {}) =
|
||||
var d: TDest = dest
|
||||
gen(c, n, d)
|
||||
gen(c, n, d, flags)
|
||||
internalAssert d == dest
|
||||
|
||||
proc gen(c: PCtx; n: PNode) =
|
||||
proc gen(c: PCtx; n: PNode; flags: TGenFlags = {}) =
|
||||
var tmp: TDest = -1
|
||||
gen(c, n, tmp)
|
||||
gen(c, n, tmp, flags)
|
||||
#if n.typ.isEmptyType: InternalAssert tmp < 0
|
||||
|
||||
proc genx(c: PCtx; n: PNode): TRegister =
|
||||
proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
|
||||
var tmp: TDest = -1
|
||||
gen(c, n, tmp)
|
||||
gen(c, n, tmp, flags)
|
||||
internalAssert tmp >= 0
|
||||
result = TRegister(tmp)
|
||||
|
||||
@@ -400,7 +409,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let endExcept = c.xjmp(it, opcExcept, 0)
|
||||
for j in countup(0, blen - 2):
|
||||
assert(it.sons[j].kind == nkType)
|
||||
let typ = it.sons[j].typ.skipTypes(abstractPtrs)
|
||||
let typ = it.sons[j].typ.skipTypes(abstractPtrs-{tyTypeDesc})
|
||||
c.gABx(it, opcExcept, 0, c.genType(typ))
|
||||
if blen == 1:
|
||||
# general except section:
|
||||
@@ -470,15 +479,16 @@ proc genNew(c: PCtx; n: PNode) =
|
||||
# we use the ref's base type here as the VM conflates 'ref object'
|
||||
# and 'object' since internally we already have a pointer.
|
||||
c.gABx(n, opcNew, dest,
|
||||
c.genType(n.sons[1].typ.skipTypes(abstractVar).sons[0]))
|
||||
c.genType(n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).sons[0]))
|
||||
c.genAsgnPatch(n.sons[1], dest)
|
||||
c.freeTemp(dest)
|
||||
|
||||
proc genNewSeq(c: PCtx; n: PNode) =
|
||||
let dest = if needsAsgnPatch(n.sons[1]): c.getTemp(n.sons[1].typ)
|
||||
else: c.genx(n.sons[1])
|
||||
c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes(abstractVar)))
|
||||
let tmp = c.genx(n.sons[2])
|
||||
c.gABx(n, opcNewSeq, dest, c.genType(n.sons[1].typ.skipTypes(
|
||||
abstractVar-{tyTypeDesc})))
|
||||
c.gABx(n, opcNewSeq, tmp, 0)
|
||||
c.freeTemp(tmp)
|
||||
c.genAsgnPatch(n.sons[1], dest)
|
||||
@@ -506,7 +516,7 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
c.freeTemp(tmp2)
|
||||
|
||||
proc genSetType(c: PCtx; n: PNode; dest: TRegister) =
|
||||
let t = skipTypes(n.typ, abstractInst)
|
||||
let t = skipTypes(n.typ, abstractInst-{tyTypeDesc})
|
||||
if t.kind == tySet:
|
||||
c.gABx(n, opcSetType, dest, c.genType(t))
|
||||
|
||||
@@ -528,6 +538,14 @@ proc genBinaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
|
||||
c.gABC(n, opc, dest, tmp, 0)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genBinaryStmtVar(c: PCtx; n: PNode; opc: TOpcode) =
|
||||
let
|
||||
dest = c.genx(n.sons[1], {gfAddrOf})
|
||||
tmp = c.genx(n.sons[2])
|
||||
c.gABC(n, opc, dest, tmp, 0)
|
||||
#c.genAsgnPatch(n.sons[1], dest)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
|
||||
let tmp = c.genx(n.sons[1])
|
||||
c.gABC(n, opc, tmp, 0, 0)
|
||||
@@ -729,7 +747,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
var tmp = c.genx(n.sons[1])
|
||||
var idx = c.getTemp(getSysType(tyInt))
|
||||
var typ = n.sons[2].typ
|
||||
if m == mOf: typ = typ.skipTypes(abstractPtrs)
|
||||
if m == mOf: typ = typ.skipTypes(abstractPtrs-{tyTypeDesc})
|
||||
c.gABx(n, opcLdImmInt, idx, c.genType(typ))
|
||||
c.gABC(n, if m == mOf: opcOf else: opcIs, dest, tmp, idx)
|
||||
c.freeTemp(tmp)
|
||||
@@ -739,7 +757,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
of mHigh:
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
let tmp = c.genx(n.sons[1])
|
||||
if n.sons[1].typ.skipTypes(abstractVar).kind == tyString:
|
||||
if n.sons[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind == tyString:
|
||||
c.gABI(n, opcLenStr, dest, tmp, 1)
|
||||
else:
|
||||
c.gABI(n, opcLenSeq, dest, tmp, 1)
|
||||
@@ -754,13 +772,13 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.freeTempRange(x, n.len-1)
|
||||
of mAppendStrCh:
|
||||
unused(n, dest)
|
||||
genBinaryStmt(c, n, opcAddStrCh)
|
||||
genBinaryStmtVar(c, n, opcAddStrCh)
|
||||
of mAppendStrStr:
|
||||
unused(n, dest)
|
||||
genBinaryStmt(c, n, opcAddStrStr)
|
||||
genBinaryStmtVar(c, n, opcAddStrStr)
|
||||
of mAppendSeqElem:
|
||||
unused(n, dest)
|
||||
genBinaryStmt(c, n, opcAddSeqElem)
|
||||
genBinaryStmtVar(c, n, opcAddSeqElem)
|
||||
of mParseExprToAst:
|
||||
genUnaryABC(c, n, dest, opcParseExprToAst)
|
||||
of mParseStmtToAst:
|
||||
@@ -874,7 +892,7 @@ const
|
||||
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
|
||||
|
||||
proc requiresCopy(n: PNode): bool =
|
||||
if n.typ.skipTypes(abstractInst).kind in atomicTypes:
|
||||
if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
|
||||
result = false
|
||||
elif n.kind in ({nkCurly, nkBracket, nkPar, nkObjConstr}+nkCallKinds):
|
||||
result = false
|
||||
@@ -882,7 +900,7 @@ proc requiresCopy(n: PNode): bool =
|
||||
result = true
|
||||
|
||||
proc unneededIndirection(n: PNode): bool =
|
||||
n.typ.skipTypes(abstractInst).kind == tyRef
|
||||
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
|
||||
|
||||
proc skipDeref(n: PNode): PNode =
|
||||
if n.kind in {nkDerefExpr, nkHiddenDeref} and unneededIndirection(n.sons[0]):
|
||||
@@ -890,18 +908,20 @@ proc skipDeref(n: PNode): PNode =
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
||||
flags: TGenFlags) =
|
||||
# a nop for certain types
|
||||
let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags
|
||||
if unneededIndirection(n.sons[0]):
|
||||
gen(c, n.sons[0], dest)
|
||||
gen(c, n.sons[0], dest, flags)
|
||||
else:
|
||||
let tmp = c.genx(n.sons[0])
|
||||
let tmp = c.genx(n.sons[0], flags)
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
gABC(c, n, opc, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc whichAsgnOpc(n: PNode): TOpcode =
|
||||
case n.typ.skipTypes(abstractRange).kind
|
||||
case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
|
||||
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
opcAsgnInt
|
||||
of tyString, tyCString:
|
||||
@@ -913,7 +933,7 @@ proc whichAsgnOpc(n: PNode): TOpcode =
|
||||
else:
|
||||
opcAsgnComplex
|
||||
|
||||
proc isRef(t: PType): bool = t.skipTypes(abstractRange).kind == tyRef
|
||||
proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef
|
||||
|
||||
proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode =
|
||||
if isRef(n.typ): succ(opc) else: opc
|
||||
@@ -926,13 +946,22 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
|
||||
|
||||
template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
|
||||
|
||||
proc setSlot(c: PCtx; v: PSym) =
|
||||
# XXX generate type initialization here?
|
||||
if v.position == 0:
|
||||
v.position = c.prc.maxSlots
|
||||
c.prc.slots[v.position] = (inUse: true,
|
||||
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
|
||||
inc c.prc.maxSlots
|
||||
|
||||
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
case le.kind
|
||||
of nkBracketExpr:
|
||||
let dest = c.genx(le.sons[0])
|
||||
let idx = c.genx(le.sons[1])
|
||||
let tmp = c.genx(ri)
|
||||
if le.sons[0].typ.skipTypes(abstractVarRange).kind in {tyString, tyCString}:
|
||||
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
|
||||
tyString, tyCString}:
|
||||
c.gABC(le, opcWrStrIdx, dest, idx, tmp)
|
||||
else:
|
||||
c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp)
|
||||
@@ -952,8 +981,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||
gen(c, ri, tmp)
|
||||
c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position)
|
||||
else:
|
||||
if s.kind == skForVar and c.mode == emRepl: c.setSlot s
|
||||
internalAssert s.position > 0 or (s.position == 0 and
|
||||
s.kind in {skParam,skResult,skForVar})
|
||||
s.kind in {skParam,skResult})
|
||||
var dest: TRegister = s.position + ord(s.kind == skParam)
|
||||
gen(c, ri, dest)
|
||||
else:
|
||||
@@ -1003,7 +1033,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
|
||||
if s.isGlobal:
|
||||
if sfCompileTime in s.flags or c.mode == emRepl:
|
||||
discard
|
||||
else:
|
||||
elif s.position == 0:
|
||||
cannotEval(n)
|
||||
if s.position == 0:
|
||||
if sfImportc in s.flags: c.importcSym(n.info, s)
|
||||
@@ -1014,8 +1044,9 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
|
||||
else:
|
||||
c.gABx(n, opcLdGlobal, dest, s.position)
|
||||
else:
|
||||
if s.kind == skForVar and c.mode == emRepl: c.setSlot s
|
||||
if s.position > 0 or (s.position == 0 and
|
||||
s.kind in {skParam,skResult,skForVar}):
|
||||
s.kind in {skParam,skResult}):
|
||||
if dest < 0:
|
||||
dest = s.position + ord(s.kind == skParam)
|
||||
else:
|
||||
@@ -1024,28 +1055,29 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
|
||||
else:
|
||||
# see tests/t99bott for an example that triggers it:
|
||||
cannotEval(n)
|
||||
#InternalError(n.info, s.name.s & " " & $s.position)
|
||||
|
||||
proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
let a = c.genx(n.sons[0])
|
||||
let b = c.genx(n.sons[1])
|
||||
proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
||||
flags: TGenFlags) =
|
||||
let a = c.genx(n.sons[0], flags)
|
||||
let b = c.genx(n.sons[1], {})
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opc, dest, a, b)
|
||||
c.gABC(n, (if gfAddrOf in flags: succ(opc) else: opc), dest, a, b)
|
||||
c.freeTemp(a)
|
||||
c.freeTemp(b)
|
||||
|
||||
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest) =
|
||||
genAccess(c, n, dest, opcLdObj)
|
||||
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
genAccess(c, n, dest, opcLdObj, flags)
|
||||
|
||||
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest) =
|
||||
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
# XXX implement field checks!
|
||||
genAccess(c, n.sons[0], dest, opcLdObj)
|
||||
genAccess(c, n.sons[0], dest, opcLdObj, flags)
|
||||
|
||||
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest) =
|
||||
if n.sons[0].typ.skipTypes(abstractVarRange).kind in {tyString, tyCString}:
|
||||
genAccess(c, n, dest, opcLdStrIdx)
|
||||
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
|
||||
tyString, tyCString}:
|
||||
genAccess(c, n, dest, opcLdStrIdx, {})
|
||||
else:
|
||||
genAccess(c, n, dest, opcLdArr)
|
||||
genAccess(c, n, dest, opcLdArr, flags)
|
||||
|
||||
proc getNullValue*(typ: PType, info: TLineInfo): PNode
|
||||
proc getNullValueAux(obj: PNode, result: PNode) =
|
||||
@@ -1100,14 +1132,6 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
|
||||
result = newNodeIT(nkCurly, info, t)
|
||||
else: internalError("getNullValue: " & $t.kind)
|
||||
|
||||
proc setSlot(c: PCtx; v: PSym) =
|
||||
# XXX generate type initialization here?
|
||||
if v.position == 0:
|
||||
v.position = c.prc.maxSlots
|
||||
c.prc.slots[v.position] = (inUse: true,
|
||||
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
|
||||
inc c.prc.maxSlots
|
||||
|
||||
proc genVarSection(c: PCtx; n: PNode) =
|
||||
for a in n:
|
||||
if a.kind == nkCommentStmt: continue
|
||||
@@ -1187,7 +1211,7 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
|
||||
proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
let t = n.typ.skipTypes(abstractRange)
|
||||
let t = n.typ.skipTypes(abstractRange-{tyTypeDesc})
|
||||
if t.kind == tyRef:
|
||||
c.gABx(n, opcNew, dest, c.genType(t.sons[0]))
|
||||
else:
|
||||
@@ -1222,7 +1246,7 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
|
||||
|
||||
proc genProc*(c: PCtx; s: PSym): int
|
||||
|
||||
proc gen(c: PCtx; n: PNode; dest: var TDest) =
|
||||
proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
let s = n.sym
|
||||
@@ -1271,11 +1295,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
|
||||
of nkAsgn, nkFastAsgn:
|
||||
unused(n, dest)
|
||||
genAsgn(c, n.sons[0], n.sons[1], n.kind == nkAsgn)
|
||||
of nkDotExpr: genObjAccess(c, n, dest)
|
||||
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest)
|
||||
of nkBracketExpr: genArrAccess(c, n, dest)
|
||||
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref)
|
||||
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr)
|
||||
of nkDotExpr: genObjAccess(c, n, dest, flags)
|
||||
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
|
||||
of nkBracketExpr: genArrAccess(c, n, dest, flags)
|
||||
of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref, flags)
|
||||
of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr, flags)
|
||||
of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
|
||||
of nkCaseStmt: genCase(c, n, dest)
|
||||
of nkWhileStmt:
|
||||
@@ -1298,7 +1322,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest) =
|
||||
of nkStmtListExpr:
|
||||
let L = n.len-1
|
||||
for i in 0 .. <L: gen(c, n.sons[i])
|
||||
gen(c, n.sons[L], dest)
|
||||
gen(c, n.sons[L], dest, flags)
|
||||
of nkDiscardStmt:
|
||||
unused(n, dest)
|
||||
gen(c, n.sons[0])
|
||||
@@ -1460,9 +1484,9 @@ proc genProc(c: PCtx; s: PSym): int =
|
||||
c.gABC(body, opcEof, eofInstr.regA)
|
||||
c.optimizeJumps(result)
|
||||
s.offset = c.prc.maxSlots
|
||||
#if s.name.s == "rawGet":
|
||||
#if s.name.s == "concatStyleInterpolation":
|
||||
# c.echoCode(result)
|
||||
# echo renderTree(body)
|
||||
# echo renderTree(body)
|
||||
c.prc = oldPrc
|
||||
else:
|
||||
c.prc.maxSlots = s.offset
|
||||
|
||||
@@ -76,7 +76,7 @@ span.LongStringLit {color: blue}
|
||||
span.CharLit {color: blue}
|
||||
span.EscapeSequence {color: black}
|
||||
span.Operator {color: black}
|
||||
span.Punctation {color: black}
|
||||
span.Punctuation {color: black}
|
||||
span.Comment, span.LongComment {font-style:italic; color: green}
|
||||
span.RegularExpression {color: DarkViolet}
|
||||
span.TagStart {color: DarkViolet}
|
||||
|
||||
@@ -98,7 +98,7 @@ doc.file = """
|
||||
\newcommand{\spanCharLit}[1]{#1}
|
||||
\newcommand{\spanEscapeSequence}[1]{#1}
|
||||
\newcommand{\spanOperator}[1]{#1}
|
||||
\newcommand{\spanPunctation}[1]{#1}
|
||||
\newcommand{\spanPunctuation}[1]{#1}
|
||||
\newcommand{\spanComment}[1]{\emph{#1}}
|
||||
\newcommand{\spanLongComment}[1]{\emph{#1}}
|
||||
\newcommand{\spanRegularExpression}[1]{#1}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
===============================================================================
|
||||
Nimrod -- a Compiler for Nimrod. http://nimrod-code.org/
|
||||
|
||||
Copyright (C) 2004-2013 Andreas Rumpf. All rights reserved.
|
||||
Copyright (C) 2004-2014 Andreas Rumpf. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -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
|
||||
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 indexExprList optPar ']'
|
||||
| '{' optInd indexExprList optPar '}'
|
||||
| &( '`'|IDENT|literal|'cast') expr ^+ ',' # command syntax
|
||||
(doBlock | macroColon)?
|
||||
| &( '`'|IDENT|literal|'cast') expr # command syntax
|
||||
condExpr = expr colcom expr optInd
|
||||
('elif' expr colcom expr optInd)*
|
||||
'else' colcom expr
|
||||
|
||||
@@ -341,6 +341,9 @@ Miscellaneous
|
||||
* `endians <endians.html>`_
|
||||
This module contains helpers that deal with different byte orders.
|
||||
|
||||
* `logging <logging.html>`_
|
||||
This module implements a simple logger.
|
||||
|
||||
|
||||
Database support
|
||||
----------------
|
||||
|
||||
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
|
||||
pretty much constant for a given task. -- Ran
|
||||
|
||||
|
||||
|
||||
About this document
|
||||
===================
|
||||
|
||||
@@ -1313,12 +1315,9 @@ Examples:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
type
|
||||
TCallback = proc (x: int) {.cdecl.}
|
||||
proc printItem(x: int) = ...
|
||||
|
||||
proc printItem(x: Int) = ...
|
||||
|
||||
proc forEach(c: TCallback) =
|
||||
proc forEach(c: proc (x: int) {.cdecl.}) =
|
||||
...
|
||||
|
||||
forEach(printItem) # this will NOT work because calling conventions differ
|
||||
@@ -1479,7 +1478,7 @@ But it seems all this boilerplate code needs to be repeated for the ``TEuro``
|
||||
currency. This can be solved with templates_.
|
||||
|
||||
.. code-block:: nimrod
|
||||
template Additive(typ: typedesc): stmt =
|
||||
template additive(typ: typedesc): stmt =
|
||||
proc `+` *(x, y: typ): typ {.borrow.}
|
||||
proc `-` *(x, y: typ): typ {.borrow.}
|
||||
|
||||
@@ -1487,26 +1486,26 @@ currency. This can be solved with templates_.
|
||||
proc `+` *(x: typ): typ {.borrow.}
|
||||
proc `-` *(x: typ): typ {.borrow.}
|
||||
|
||||
template Multiplicative(typ, base: typedesc): stmt =
|
||||
template multiplicative(typ, base: typedesc): stmt =
|
||||
proc `*` *(x: typ, y: base): typ {.borrow.}
|
||||
proc `*` *(x: base, y: typ): typ {.borrow.}
|
||||
proc `div` *(x: typ, y: base): typ {.borrow.}
|
||||
proc `mod` *(x: typ, y: base): typ {.borrow.}
|
||||
|
||||
template Comparable(typ: typedesc): stmt =
|
||||
template comparable(typ: typedesc): stmt =
|
||||
proc `<` * (x, y: typ): bool {.borrow.}
|
||||
proc `<=` * (x, y: typ): bool {.borrow.}
|
||||
proc `==` * (x, y: typ): bool {.borrow.}
|
||||
|
||||
template DefineCurrency(typ, base: expr): stmt =
|
||||
template defineCurrency(typ, base: expr): stmt =
|
||||
type
|
||||
typ* = distinct base
|
||||
Additive(typ)
|
||||
Multiplicative(typ, base)
|
||||
Comparable(typ)
|
||||
additive(typ)
|
||||
multiplicative(typ, base)
|
||||
comparable(typ)
|
||||
|
||||
DefineCurrency(TDollar, int)
|
||||
DefineCurrency(TEuro, int)
|
||||
defineCurrency(TDollar, int)
|
||||
defineCurrency(TEuro, int)
|
||||
|
||||
|
||||
Void type
|
||||
@@ -2232,6 +2231,8 @@ Instead of:
|
||||
Using statement
|
||||
---------------
|
||||
|
||||
**Warning**: The ``using`` statement is highly experimental!
|
||||
|
||||
The `using statement`:idx: provides syntactic convenience for procs that
|
||||
heavily use a single contextual parameter. When applied to a variable or a
|
||||
constant, it will instruct Nimrod to automatically consider the used symbol as
|
||||
@@ -2457,6 +2458,83 @@ notation. (Thus an operator can have more than two parameters):
|
||||
|
||||
assert `*+`(3, 4, 6) == `*`(a, `+`(b, c))
|
||||
|
||||
|
||||
Method call syntax
|
||||
------------------
|
||||
|
||||
For object oriented programming, the syntax ``obj.method(args)`` can be used
|
||||
instead of ``method(obj, args)``. The parentheses can be omitted if there are no
|
||||
remaining arguments: ``obj.len`` (instead of ``len(obj)``).
|
||||
|
||||
This `method call syntax`:idx: is not restricted to objects, it can be used
|
||||
to supply any type of first argument for procedures:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
echo("abc".len) # is the same as echo(len("abc"))
|
||||
echo("abc".toUpper())
|
||||
echo({'a', 'b', 'c'}.card)
|
||||
stdout.writeln("Hallo") # the same as writeln(stdout, "Hallo")
|
||||
|
||||
Another way to look at the method call syntax is that it provides the missing
|
||||
postfix notation.
|
||||
|
||||
|
||||
Properties
|
||||
----------
|
||||
Nimrod has no need for *get-properties*: Ordinary get-procedures that are called
|
||||
with the *method call syntax* achieve the same. But setting a value is
|
||||
different; for this a special setter syntax is needed:
|
||||
|
||||
.. code-block:: nimrod
|
||||
|
||||
type
|
||||
TSocket* = object of TObject
|
||||
FHost: int # cannot be accessed from the outside of the module
|
||||
# the `F` prefix is a convention to avoid clashes since
|
||||
# the accessors are named `host`
|
||||
|
||||
proc `host=`*(s: var TSocket, value: int) {.inline.} =
|
||||
## setter of hostAddr
|
||||
s.FHost = value
|
||||
|
||||
proc host*(s: TSocket): int {.inline.} =
|
||||
## getter of hostAddr
|
||||
return s.FHost
|
||||
|
||||
var
|
||||
s: TSocket
|
||||
s.host = 34 # same as `host=`(s, 34)
|
||||
|
||||
|
||||
Command invocation syntax
|
||||
-------------------------
|
||||
|
||||
Routines can be invoked without the ``()`` if the call is syntatically
|
||||
a statement. This `command invocation syntax`:idx: also works for
|
||||
expressions, but then only a single argument may follow. This restriction
|
||||
means ``echo f 1, f 2`` is parsed as ``echo(f(1), f(2))`` and not as
|
||||
``echo(f(1, f(2)))``. The method call syntax may be used to provide one
|
||||
more argument in this case:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc optarg(x:int, y:int = 0):int = x + y
|
||||
proc singlearg(x:int):int = 20*x
|
||||
|
||||
echo optarg 1, " ", singlearg 2 # prints "1 40"
|
||||
|
||||
let fail = optarg 1, optarg 8 # Wrong. Too many arguments for a command call
|
||||
let x = optarg(1, optarg 8) # traditional procedure call with 2 arguments
|
||||
let y = 1.optarg optarg 8 # same thing as above, w/o the parenthesis
|
||||
assert x == y
|
||||
|
||||
The command invocation syntax also can't have complex expressions as arguments.
|
||||
For example: (`anonymous procs`_), ``if``, ``case`` or ``try``. The (`do
|
||||
notation`_) is limited, but usable for a single proc (see the example in the
|
||||
corresponding section). Function calls with no arguments still needs () to
|
||||
distinguish between a call and the function itself as a first class value.
|
||||
|
||||
|
||||
Closures
|
||||
--------
|
||||
|
||||
@@ -2469,6 +2547,7 @@ the closure and its enclosing scope (i.e. any modifications made to them are
|
||||
visible in both places). The closure environment may be allocated on the heap
|
||||
or on the stack if the compiler determines that this would be safe.
|
||||
|
||||
|
||||
Anonymous Procs
|
||||
---------------
|
||||
|
||||
@@ -2495,6 +2574,9 @@ calls can use the ``do`` keyword:
|
||||
.. code-block:: nimrod
|
||||
sort(cities) do (x,y: string) -> int:
|
||||
cmp(x.len, y.len)
|
||||
# Less parenthesis using the method plus command syntax:
|
||||
cities = cities.map do (x:string) -> string:
|
||||
"City of " & x
|
||||
|
||||
``do`` is written after the parentheses enclosing the regular proc params.
|
||||
The proc expression represented by the do block is appended to them.
|
||||
@@ -2849,7 +2931,7 @@ parameters of an outer factory proc:
|
||||
yield x
|
||||
inc x
|
||||
|
||||
let foo = mycount 1, 4
|
||||
let foo = mycount(1, 4)
|
||||
|
||||
for f in foo():
|
||||
echo f
|
||||
@@ -3440,13 +3522,41 @@ A symbol can be forced to be open by a `mixin`:idx: declaration:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc create*[T](): ref T =
|
||||
# there is no overloaded 'mixin' here, so we need to state that it's an
|
||||
# there is no overloaded 'init' here, so we need to state that it's an
|
||||
# open symbol explicitly:
|
||||
mixin init
|
||||
new result
|
||||
init result
|
||||
|
||||
|
||||
Bind statement
|
||||
--------------
|
||||
|
||||
The `bind`:idx: statement is the counterpart to the ``mixin`` statement. It
|
||||
can be used to explicitly declare identifiers that should be bound early (i.e.
|
||||
the identifiers should be looked up in the scope of the template/generic
|
||||
definition):
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module A
|
||||
var
|
||||
lastId = 0
|
||||
|
||||
template genId*: expr =
|
||||
bind lastId
|
||||
inc(lastId)
|
||||
lastId
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module B
|
||||
import A
|
||||
|
||||
echo genId()
|
||||
|
||||
But a ``bind`` is rarely useful because symbol binding from the definition
|
||||
scope is the default.
|
||||
|
||||
|
||||
Templates
|
||||
=========
|
||||
|
||||
@@ -3506,28 +3616,6 @@ receive undeclared identifiers:
|
||||
declareInt(x) # valid
|
||||
|
||||
|
||||
Scoping in templates
|
||||
--------------------
|
||||
|
||||
The template body does not open a new scope. To open a new scope a ``block``
|
||||
statement can be used:
|
||||
|
||||
.. code-block:: nimrod
|
||||
template declareInScope(x: expr, t: typedesc): stmt {.immediate.} =
|
||||
var x: t
|
||||
|
||||
template declareInNewScope(x: expr, t: typedesc): stmt {.immediate.} =
|
||||
# open a new scope:
|
||||
block:
|
||||
var x: t
|
||||
|
||||
declareInScope(a, int)
|
||||
a = 42 # works, `a` is known here
|
||||
|
||||
declareInNewScope(b, int)
|
||||
b = 42 # does not work, `b` is unknown
|
||||
|
||||
|
||||
Passing a code block to a template
|
||||
----------------------------------
|
||||
|
||||
@@ -3538,26 +3626,28 @@ special ``:`` syntax:
|
||||
.. code-block:: nimrod
|
||||
|
||||
template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} =
|
||||
block:
|
||||
var f: TFile
|
||||
if open(f, fn, mode):
|
||||
try:
|
||||
actions
|
||||
finally:
|
||||
close(f)
|
||||
else:
|
||||
quit("cannot open: " & fn)
|
||||
var f: TFile
|
||||
if open(f, fn, mode):
|
||||
try:
|
||||
actions
|
||||
finally:
|
||||
close(f)
|
||||
else:
|
||||
quit("cannot open: " & fn)
|
||||
|
||||
withFile(txt, "ttempl3.txt", fmWrite):
|
||||
txt.writeln("line 1")
|
||||
txt.writeln("line 2")
|
||||
|
||||
In the example the two ``writeln`` statements are bound to the ``actions``
|
||||
parameter.
|
||||
parameter.
|
||||
|
||||
**Note:** The symbol binding rules for templates might change!
|
||||
|
||||
Symbol binding within templates happens after template instantiation:
|
||||
Symbol binding in templates
|
||||
---------------------------
|
||||
|
||||
A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are
|
||||
bound from the definition scope of the template:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module A
|
||||
@@ -3572,34 +3662,11 @@ Symbol binding within templates happens after template instantiation:
|
||||
# Module B
|
||||
import A
|
||||
|
||||
echo genId() # Error: undeclared identifier: 'lastId'
|
||||
echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
|
||||
|
||||
As in generics symbol binding can be influenced via ``mixin`` or ``bind``
|
||||
statements.
|
||||
|
||||
Bind statement
|
||||
--------------
|
||||
|
||||
Exporting a template is a often a leaky abstraction as it can depend on
|
||||
symbols that are not visible from a client module. However, to compensate for
|
||||
this case, a `bind`:idx: statement can be used: It declares all identifiers
|
||||
that should be bound early (i.e. when the template is parsed):
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module A
|
||||
var
|
||||
lastId = 0
|
||||
|
||||
template genId*: expr =
|
||||
bind lastId
|
||||
inc(lastId)
|
||||
lastId
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module B
|
||||
import A
|
||||
|
||||
echo genId() # Works
|
||||
|
||||
A ``bind`` statement can also be used in generics for the same purpose.
|
||||
|
||||
|
||||
Identifier construction
|
||||
@@ -3942,13 +4009,13 @@ Static params can also appear in the signatures of generic types:
|
||||
|
||||
type
|
||||
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
|
||||
# Please, note how `Number` is just a type constraint here, while
|
||||
# Note how `Number` is just a type constraint here, while
|
||||
# `static[int]` requires us to supply a compile-time int value
|
||||
|
||||
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||
AffineTransform3D[T] = Matrix[4, 4, T]
|
||||
|
||||
AffineTransform3D[float] # OK
|
||||
AffineTransform3D[float] # OK
|
||||
AffineTransform2D[string] # Error, `string` is not a `Number`
|
||||
|
||||
|
||||
@@ -4685,6 +4752,10 @@ These rules ensure that the construction is tied to a variable and can easily
|
||||
be destructed at its scope exit. Later versions of the language will improve
|
||||
the support of destructors.
|
||||
|
||||
Be aware that destructors are not called for objects allocated with ``new``.
|
||||
This may change in future versions of language, but for now use
|
||||
the ``finalizer`` parameter to ``new``.
|
||||
|
||||
|
||||
delegator pragma
|
||||
----------------
|
||||
@@ -5173,9 +5244,9 @@ the same feature under the same name.
|
||||
Exportc pragma
|
||||
--------------
|
||||
The `exportc`:idx: pragma provides a means to export a type, a variable, or a
|
||||
procedure to C. The optional argument is a string containing the C identifier.
|
||||
If the argument is missing, the C name is the Nimrod
|
||||
identifier *exactly as spelled*:
|
||||
procedure to C. Enums and constants can't be exported. The optional argument
|
||||
is a string containing the C identifier. If the argument is missing, the C
|
||||
name is the Nimrod identifier *exactly as spelled*:
|
||||
|
||||
.. code-block:: Nimrod
|
||||
proc callme(formatstr: cstring) {.exportc: "callMe", varargs.}
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
------
|
||||
|
||||
|
||||
@@ -528,7 +528,7 @@ containers:
|
||||
proc newNode*[T](data: T): PBinaryTree[T] =
|
||||
# constructor for a node
|
||||
new(result)
|
||||
result.dat = data
|
||||
result.data = data
|
||||
|
||||
proc add*[T](root: var PBinaryTree[T], n: PBinaryTree[T]) =
|
||||
# insert a node into the tree
|
||||
@@ -569,7 +569,7 @@ containers:
|
||||
|
||||
var
|
||||
root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
|
||||
add(root, newNode("hallo")) # instantiates ``newNode`` and ``add``
|
||||
add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
|
||||
add(root, "world") # instantiates the second ``add`` proc
|
||||
for str in preorder(root):
|
||||
stdout.writeln(str)
|
||||
|
||||
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
|
||||
# taint mode test :-)
|
||||
exec "nimrod cc --taintMode:on tests/testament/tester"
|
||||
exec quoteShell(getCurrentDir() / "tests/testament/tester".exe) & " " &
|
||||
(args|"all")
|
||||
let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe)
|
||||
exec tester & " " & (args|"all")
|
||||
exec tester & " html"
|
||||
|
||||
proc temp(args: string) =
|
||||
var output = "compiler" / "nimrod".exe
|
||||
|
||||
@@ -294,19 +294,6 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
|
||||
## if not `ex`:
|
||||
## echo `info` & ": Check failed: " & `expString`
|
||||
|
||||
when not defined(booting):
|
||||
template emit*(e: static[string]): stmt =
|
||||
## accepts a single string argument and treats it as nimrod code
|
||||
## that should be inserted verbatim in the program
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## emit("echo " & '"' & "hello world".toUpper & '"')
|
||||
##
|
||||
macro payload: stmt {.gensym.} =
|
||||
result = e.parseStmt
|
||||
payload()
|
||||
|
||||
proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} =
|
||||
## checks that `n` is of kind `k`. If this is not the case,
|
||||
## compilation aborts with an error message. This is useful for writing
|
||||
@@ -421,7 +408,8 @@ proc lispRepr*(n: PNimrodNode): string {.compileTime.} =
|
||||
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
||||
of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal)
|
||||
of nnkIdent: add(result, "!\"" & $n.ident & '"')
|
||||
of nnkSym, nnkNone: assert false
|
||||
of nnkSym: add(result, $n.symbol)
|
||||
of nnkNone: assert false
|
||||
else:
|
||||
add(result, lispRepr(n[0]))
|
||||
for j in 1..n.len-1:
|
||||
@@ -745,3 +733,15 @@ proc addIdentIfAbsent*(dest: PNimrodNode, ident: string) {.compiletime.} =
|
||||
else: discard
|
||||
dest.add(ident(ident))
|
||||
|
||||
when not defined(booting):
|
||||
template emit*(e: static[string]): stmt =
|
||||
## accepts a single string argument and treats it as nimrod code
|
||||
## that should be inserted verbatim in the program
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## emit("echo " & '"' & "hello world".toUpper & '"')
|
||||
##
|
||||
macro payload: stmt {.gensym.} =
|
||||
result = parseStmt(e)
|
||||
payload()
|
||||
|
||||
@@ -58,7 +58,7 @@ proc open*(host: string = defaultHost, port: int = defaultPort): TDbConn {.
|
||||
## be established.
|
||||
init(result)
|
||||
|
||||
let x = connect(result, host, port.cint)
|
||||
let x = client(result, host, port.cint)
|
||||
if x != 0'i32:
|
||||
dbError(result, "cannot open: " & host)
|
||||
|
||||
@@ -119,7 +119,7 @@ proc insertId*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {.
|
||||
## the generated OID for the ``_id`` field.
|
||||
result = genOid()
|
||||
var x = jsonToBSon(data, result)
|
||||
insert(db, namespace, x)
|
||||
insert(db, namespace, x, nil)
|
||||
destroy(x)
|
||||
|
||||
proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {.
|
||||
|
||||
@@ -148,7 +148,8 @@ proc getValue*(db: TDbConn, query: TSqlQuery,
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
|
||||
proc tryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
args: varargs[string, `$`]): int64
|
||||
{.tags: [FWriteDb], raises: [].} =
|
||||
## executes the query (typically "INSERT") and returns the
|
||||
## generated ID for the row or -1 in case of an error.
|
||||
var q = dbFormat(query, args)
|
||||
@@ -157,7 +158,8 @@ proc tryInsertID*(db: TDbConn, query: TSqlQuery,
|
||||
if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK:
|
||||
if step(stmt) == SQLITE_DONE:
|
||||
result = last_insert_rowid(db)
|
||||
if finalize(stmt) != SQLITE_OK: dbError(db)
|
||||
if finalize(stmt) != SQLITE_OK:
|
||||
result = -1
|
||||
|
||||
proc insertID*(db: TDbConn, query: TSqlQuery,
|
||||
args: varargs[string, `$`]): int64 {.tags: [FWriteDb].} =
|
||||
|
||||
@@ -19,7 +19,7 @@ type
|
||||
gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber,
|
||||
gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit,
|
||||
gtLongStringLit, gtCharLit, gtEscapeSequence, # escape sequence like \xff
|
||||
gtOperator, gtPunctation, gtComment, gtLongComment, gtRegularExpression,
|
||||
gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression,
|
||||
gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler,
|
||||
gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel,
|
||||
gtReference, gtOther
|
||||
@@ -39,7 +39,7 @@ const
|
||||
tokenClassToStr*: array[TTokenClass, string] = ["Eof", "None", "Whitespace",
|
||||
"DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber",
|
||||
"Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit",
|
||||
"EscapeSequence", "Operator", "Punctation", "Comment", "LongComment",
|
||||
"EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment",
|
||||
"RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData",
|
||||
"Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink",
|
||||
"Label", "Reference", "Other"]
|
||||
@@ -258,7 +258,7 @@ proc nimNextToken(g: var TGeneralTokenizer) =
|
||||
else: inc(pos)
|
||||
of '(', ')', '[', ']', '{', '}', '`', ':', ',', ';':
|
||||
inc(pos)
|
||||
g.kind = gtPunctation
|
||||
g.kind = gtPunctuation
|
||||
of '\0':
|
||||
g.kind = gtEof
|
||||
else:
|
||||
@@ -473,7 +473,7 @@ proc clikeNextToken(g: var TGeneralTokenizer, keywords: openArray[string],
|
||||
else: inc(pos)
|
||||
of '(', ')', '[', ']', '{', '}', ':', ',', ';', '.':
|
||||
inc(pos)
|
||||
g.kind = gtPunctation
|
||||
g.kind = gtPunctuation
|
||||
of '\0':
|
||||
g.kind = gtEof
|
||||
else:
|
||||
|
||||
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)
|
||||
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()
|
||||
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
|
||||
|
||||
template mapIt*(seq1, typ, pred: expr): expr =
|
||||
## Convenience template around the ``map`` proc to reduce typing.
|
||||
##
|
||||
## The template injects the ``it`` variable which you can use directly in an
|
||||
## expression. You also need to pass as `typ` the type of the expression,
|
||||
## since the new returned sequence can have a different type than the
|
||||
## original. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let
|
||||
## nums = @[1, 2, 3, 4]
|
||||
## strings = nums.mapIt(string, $(4 * it))
|
||||
var result {.gensym.}: seq[typ] = @[]
|
||||
for it {.inject.} in items(seq1):
|
||||
result.add(pred)
|
||||
result
|
||||
|
||||
template mapIt*(varSeq, pred: expr) =
|
||||
## Convenience template around the mutable ``map`` proc to reduce typing.
|
||||
##
|
||||
## The template injects the ``it`` variable which you can use directly in an
|
||||
## expression. The expression has to return the same type as the sequence you
|
||||
## are mutating. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## var nums = @[1, 2, 3, 4]
|
||||
## nums.mapIt(it * 3)
|
||||
## assert nums[0] + nums[3] == 15
|
||||
for i in 0 .. <len(varSeq):
|
||||
let it {.inject.} = varSeq[i]
|
||||
varSeq[i] = pred
|
||||
|
||||
when isMainModule:
|
||||
import strutils
|
||||
block: # concat test
|
||||
@@ -381,4 +413,11 @@ when isMainModule:
|
||||
Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
|
||||
at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
|
||||
|
||||
block: # mapIt tests
|
||||
var
|
||||
nums = @[1, 2, 3, 4]
|
||||
strings = nums.mapIt(string, $(4 * it))
|
||||
nums.mapIt(it * 3)
|
||||
assert nums[0] + nums[3] == 15
|
||||
|
||||
echo "Finished doc tests"
|
||||
|
||||
@@ -224,3 +224,20 @@ proc toOrderedSet*[A](keys: openArray[A]): TOrderedSet[A] =
|
||||
proc `$`*[A](s: TOrderedSet[A]): string =
|
||||
## The `$` operator for ordered hash sets.
|
||||
dollarImpl()
|
||||
|
||||
proc `<`*[A](s, t: TSet[A]): bool =
|
||||
## Is s a strict subset of t?
|
||||
s.counter != t.counter and s <= t
|
||||
|
||||
proc `<=`*[A](s, t: TSet[A]): bool =
|
||||
## Is s a subset of t?
|
||||
result = false
|
||||
if s.counter > t.counter: return
|
||||
result = true
|
||||
for item in s:
|
||||
if not(t.contains(item)):
|
||||
result = false
|
||||
return
|
||||
|
||||
proc `==`*[A](s, t: TSet[A]): bool =
|
||||
s.counter == t.counter and s <= t
|
||||
|
||||
@@ -189,6 +189,16 @@ template dollarImpl(): stmt {.dirty.} =
|
||||
proc `$`*[A, B](t: TTable[A, B]): string =
|
||||
## The `$` operator for hash tables.
|
||||
dollarImpl()
|
||||
|
||||
proc `==`*[A, B](s, t: TTable[A, B]): bool =
|
||||
s.counter == t.counter and s.data == t.data
|
||||
|
||||
proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): TTable[C, B] =
|
||||
## Index the collection with the proc provided.
|
||||
# TODO: As soon as supported, change collection: A to collection: A[B]
|
||||
result = initTable[C, B]()
|
||||
for item in collection:
|
||||
result[index(item)] = item
|
||||
|
||||
# ------------------------------ ordered table ------------------------------
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
type
|
||||
TLibHandle* = pointer ## a handle to a dynamically loaded library
|
||||
|
||||
proc LoadLib*(path: string): TLibHandle
|
||||
proc loadLib*(path: string): TLibHandle
|
||||
## loads a library from `path`. Returns nil if the library could not
|
||||
## be loaded.
|
||||
|
||||
proc LoadLib*(): TLibHandle
|
||||
proc loadLib*(): TLibHandle
|
||||
## gets the handle from the current executable. Returns nil if the
|
||||
## library could not be loaded.
|
||||
|
||||
proc UnloadLib*(lib: TLibHandle)
|
||||
proc unloadLib*(lib: TLibHandle)
|
||||
## unloads the library `lib`
|
||||
|
||||
proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} =
|
||||
@@ -60,9 +60,9 @@ when defined(posix):
|
||||
proc dlsym(lib: TLibHandle, name: cstring): pointer {.
|
||||
importc, header: "<dlfcn.h>".}
|
||||
|
||||
proc LoadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
|
||||
proc LoadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
|
||||
proc UnloadLib(lib: TLibHandle) = dlclose(lib)
|
||||
proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
|
||||
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
|
||||
proc unloadLib(lib: TLibHandle) = dlclose(lib)
|
||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||
return dlsym(lib, name)
|
||||
|
||||
@@ -78,14 +78,14 @@ elif defined(windows) or defined(dos):
|
||||
proc FreeLibrary(lib: THINSTANCE) {.importc, header: "<windows.h>", stdcall.}
|
||||
proc winLoadLibrary(path: cstring): THINSTANCE {.
|
||||
importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
|
||||
proc GetProcAddress(lib: THINSTANCE, name: cstring): pointer {.
|
||||
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
|
||||
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
|
||||
|
||||
proc LoadLib(path: string): TLibHandle =
|
||||
proc loadLib(path: string): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(path))
|
||||
proc LoadLib(): TLibHandle =
|
||||
proc loadLib(): TLibHandle =
|
||||
result = cast[TLibHandle](winLoadLibrary(nil))
|
||||
proc UnloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||
proc unloadLib(lib: TLibHandle) = FreeLibrary(cast[THINSTANCE](lib))
|
||||
|
||||
proc symAddr(lib: TLibHandle, name: cstring): pointer =
|
||||
result = GetProcAddress(cast[THINSTANCE](lib), name)
|
||||
|
||||
@@ -480,7 +480,7 @@ proc untilElementEnd(x: var TXmlParser, result: PXmlNode,
|
||||
if htmlTag(x.elemName) in {tagOption, tagOptgroup}:
|
||||
errors.add(expected(x, result))
|
||||
break
|
||||
else: nil
|
||||
else: discard
|
||||
result.addNode(parse(x, errors))
|
||||
of xmlElementEnd:
|
||||
if cmpIgnoreCase(x.elemName, result.tag) == 0:
|
||||
@@ -547,7 +547,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
||||
var u = entityToUtf8(x.rawData)
|
||||
if u.len != 0: result = newText(u)
|
||||
next(x)
|
||||
of xmlEof: nil
|
||||
of xmlEof: discard
|
||||
|
||||
proc parseHtml*(s: PStream, filename: string,
|
||||
errors: var seq[string]): PXmlNode =
|
||||
|
||||
@@ -476,12 +476,12 @@ when isMainModule:
|
||||
var client = irc("amber.tenthbit.net", nick="TestBot1234",
|
||||
joinChans = @["#flood"])
|
||||
client.connect()
|
||||
while True:
|
||||
while true:
|
||||
var event: TIRCEvent
|
||||
if client.poll(event):
|
||||
case event.typ
|
||||
of EvConnected:
|
||||
nil
|
||||
discard
|
||||
of EvDisconnected:
|
||||
break
|
||||
of EvMsg:
|
||||
|
||||
267
lib/pure/logging.nim
Normal file
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:
|
||||
discard parseutils.parseInt(s, x, 0)
|
||||
except EOverflow:
|
||||
nil
|
||||
discard
|
||||
if x in validRange: value = x
|
||||
|
||||
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 'a'..'f': result = (ord(hex) - ord('a') + 10)
|
||||
of 'A'..'F': result = (ord(hex) - ord('A') + 10)
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc parseOid*(str: cstring): TOid =
|
||||
## parses an OID.
|
||||
|
||||
@@ -287,12 +287,18 @@ proc osLastError*(): TOSErrorCode =
|
||||
result = TOSErrorCode(errno)
|
||||
{.pop.}
|
||||
|
||||
proc unixToNativePath*(path: string): string {.
|
||||
proc unixToNativePath*(path: string, drive=""): string {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Converts an UNIX-like path to a native one.
|
||||
##
|
||||
## On an UNIX system this does nothing. Else it converts
|
||||
## '/', '.', '..' to the appropriate things.
|
||||
##
|
||||
## On systems with a concept of "drives", `drive` is used to determine
|
||||
## which drive label to use during absolute path conversion.
|
||||
## `drive` defaults to the drive of the current working directory, and is
|
||||
## ignored on systems that do not have a concept of "drives".
|
||||
|
||||
when defined(unix):
|
||||
result = path
|
||||
else:
|
||||
@@ -300,7 +306,10 @@ proc unixToNativePath*(path: string): string {.
|
||||
if path[0] == '/':
|
||||
# an absolute path
|
||||
when doslike:
|
||||
result = r"C:\"
|
||||
if drive != "":
|
||||
result = drive & ":" & DirSep
|
||||
else:
|
||||
result = $DirSep
|
||||
elif defined(macos):
|
||||
result = "" # must not start with ':'
|
||||
else:
|
||||
@@ -387,6 +396,21 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [FReadDir].} =
|
||||
var res: TStat
|
||||
return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
|
||||
|
||||
proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
|
||||
tags: [FReadDir].} =
|
||||
## Returns true iff the symlink `link` exists. Will return true
|
||||
## regardless of whether the link points to a directory or file.
|
||||
when defined(windows):
|
||||
when useWinUnicode:
|
||||
wrapUnary(a, getFileAttributesW, link)
|
||||
else:
|
||||
var a = getFileAttributesA(link)
|
||||
if a != -1'i32:
|
||||
result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
|
||||
else:
|
||||
var res: TStat
|
||||
return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
|
||||
|
||||
proc fileExists*(filename: string): bool {.inline.} =
|
||||
## Synonym for existsFile
|
||||
existsFile(filename)
|
||||
@@ -1013,7 +1037,7 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
|
||||
## the process has finished. To execute a program without having a
|
||||
## shell involved, use the `execProcess` proc of the `osproc`
|
||||
## module.
|
||||
result = c_system(command)
|
||||
result = c_system(command) shr 8
|
||||
|
||||
# Environment handling cannot be put into RTL, because the ``envPairs``
|
||||
# iterator depends on ``environment``.
|
||||
@@ -1054,9 +1078,9 @@ when defined(windows):
|
||||
while true:
|
||||
var eend = strEnd(e)
|
||||
add(environment, $e)
|
||||
e = cast[CString](cast[TAddress](eend)+1)
|
||||
e = cast[cstring](cast[TAddress](eend)+1)
|
||||
if eend[1] == '\0': break
|
||||
discard FreeEnvironmentStringsA(env)
|
||||
discard freeEnvironmentStringsA(env)
|
||||
envComputed = true
|
||||
|
||||
else:
|
||||
@@ -1221,6 +1245,8 @@ iterator walkDir*(dir: string): tuple[kind: TPathComponent, path: string] {.
|
||||
if not skipFindData(f):
|
||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
|
||||
k = pcDir
|
||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
||||
k = succ(k)
|
||||
yield (k, dir / extractFilename(getFilename(f)))
|
||||
if findNextFile(h, f) == 0'i32: break
|
||||
findClose(h)
|
||||
@@ -1245,6 +1271,10 @@ iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
||||
tags: [FReadDir].} =
|
||||
## walks over the directory `dir` and yields for each file in `dir`. The
|
||||
## full path for each file is returned.
|
||||
## **Warning**:
|
||||
## Modifying the directory structure while the iterator
|
||||
## is traversing may result in undefined behavior!
|
||||
##
|
||||
## Walking is recursive. `filter` controls the behaviour of the iterator:
|
||||
##
|
||||
## --------------------- ---------------------------------------------
|
||||
@@ -1336,6 +1366,46 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
|
||||
copyDir(path, dest / noSource)
|
||||
else: discard
|
||||
|
||||
proc createSymlink*(src, dest: string) =
|
||||
## Create a symbolic link at `dest` which points to the item specified
|
||||
## by `src`. On most operating systems, will fail if a lonk
|
||||
##
|
||||
## **Warning**:
|
||||
## Some OS's (such as Microsoft Windows) restrict the creation
|
||||
## of symlinks to root users (administrators).
|
||||
when defined(Windows):
|
||||
let flag = dirExists(src).int32
|
||||
when useWinUnicode:
|
||||
var wSrc = newWideCString(src)
|
||||
var wDst = newWideCString(dest)
|
||||
if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
|
||||
osError(osLastError())
|
||||
else:
|
||||
if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0:
|
||||
osError(osLastError())
|
||||
else:
|
||||
if symlink(src, dest) != 0:
|
||||
osError(osLastError())
|
||||
|
||||
proc createHardlink*(src, dest: string) =
|
||||
## Create a hard link at `dest` which points to the item specified
|
||||
## by `src`.
|
||||
##
|
||||
## **Warning**: Most OS's restrict the creation of hard links to
|
||||
## root users (administrators) .
|
||||
when defined(Windows):
|
||||
when useWinUnicode:
|
||||
var wSrc = newWideCString(src)
|
||||
var wDst = newWideCString(dest)
|
||||
if createHardLinkW(wDst, wSrc, nil) == 0:
|
||||
osError(osLastError())
|
||||
else:
|
||||
if createHardLinkA(dest, src, nil) == 0:
|
||||
osError(osLastError())
|
||||
else:
|
||||
if link(src, dest) != 0:
|
||||
osError(osLastError())
|
||||
|
||||
proc parseCmdLine*(c: string): seq[string] {.
|
||||
noSideEffect, rtl, extern: "nos$1".} =
|
||||
## Splits a command line into several components;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
include "system/inclrtl"
|
||||
|
||||
import
|
||||
strutils, os, strtabs, streams
|
||||
strutils, os, strtabs, streams, sequtils
|
||||
|
||||
when defined(windows):
|
||||
import winlean
|
||||
@@ -36,11 +36,17 @@ type
|
||||
|
||||
TProcessOption* = enum ## options that can be passed `startProcess`
|
||||
poEchoCmd, ## echo the command before execution
|
||||
poUseShell, ## use the shell to execute the command; NOTE: This
|
||||
## often creates a security hole!
|
||||
poUsePath, ## Asks system to search for executable using PATH environment
|
||||
## 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
|
||||
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".} =
|
||||
## Quote s, so it can be safely passed to Windows API.
|
||||
## 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".}
|
||||
|
||||
proc execProcess*(command: string,
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
poUseShell}): TaintedString {.
|
||||
poUsePath,
|
||||
poEvalCommand}): TaintedString {.
|
||||
rtl, extern: "nosp$1",
|
||||
tags: [FExecIO, FReadIO].}
|
||||
## A convenience procedure that executes ``command`` with ``startProcess``
|
||||
## 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].}
|
||||
## 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,
|
||||
workingDir: string = "",
|
||||
args: openArray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut}):
|
||||
PProcess {.rtl, extern: "nosp$1", tags: [FExecIO, FReadEnv].}
|
||||
## Starts a process. `Command` is the executable file, `workingDir` is the
|
||||
## 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.
|
||||
|
||||
proc startCmd*(command: string, options: set[TProcessOption] = {
|
||||
poStdErrToStdOut, poUseShell}): PProcess {.
|
||||
tags: [FExecIO, FReadEnv].} =
|
||||
## a simpler version of `startProcess` that parses the command line into
|
||||
## program and arguments and then calls `startProcess` with the empty string
|
||||
## 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)
|
||||
poStdErrToStdOut, poUsePath}): PProcess {.
|
||||
tags: [FExecIO, FReadEnv], deprecated.} =
|
||||
## Deprecated - use `startProcess` directly.
|
||||
result = startProcess(command=command, options=options + {poEvalCommand})
|
||||
|
||||
proc close*(p: PProcess) {.rtl, extern: "nosp$1", tags: [].}
|
||||
## 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.
|
||||
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: [].}
|
||||
## 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: [].}
|
||||
## 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``.
|
||||
|
||||
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## 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``.
|
||||
|
||||
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## 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``.
|
||||
|
||||
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],
|
||||
options = {poStdErrToStdOut, poParentStreams},
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
||||
tags: [FExecIO, FTime, FReadEnv].} =
|
||||
n = countProcessors()): int {.rtl, extern: "nosp$1",
|
||||
tags: [FExecIO, FTime, FReadEnv]} =
|
||||
## executes the commands `cmds` in parallel. Creates `n` processes
|
||||
## that execute in parallel. The highest return value of all processes
|
||||
## is returned.
|
||||
when defined(posix):
|
||||
# poParentStreams causes problems on Posix, so we simply disable it:
|
||||
var options = options - {poParentStreams}
|
||||
|
||||
|
||||
assert n > 0
|
||||
if n > 1:
|
||||
var q: seq[PProcess]
|
||||
@@ -307,13 +312,17 @@ proc select*(readfds: var seq[PProcess], timeout = 500): int
|
||||
|
||||
when not defined(useNimRtl):
|
||||
proc execProcess(command: string,
|
||||
args: openarray[string] = [],
|
||||
env: PStringTable = nil,
|
||||
options: set[TProcessOption] = {poStdErrToStdOut,
|
||||
poUseShell}): TaintedString =
|
||||
var p = startCmd(command, options=options)
|
||||
poUsePath,
|
||||
poEvalCommand}): TaintedString =
|
||||
var p = startProcess(command, args=args, env=env, options=options)
|
||||
var outp = outputStream(p)
|
||||
result = TaintedString""
|
||||
var line = newStringOfCap(120).TaintedString
|
||||
while true:
|
||||
# FIXME: converts CR-LF to LF.
|
||||
if outp.readLine(line):
|
||||
result.string.add(line.string)
|
||||
result.string.add("\n")
|
||||
@@ -427,8 +436,9 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
result.errHandle = TFileHandle(si.hStdError)
|
||||
|
||||
var cmdl: cstring
|
||||
when false: # poUseShell in options:
|
||||
cmdl = buildCommandLine(getEnv("COMSPEC"), @["/c", command] & args)
|
||||
if poEvalCommand in options:
|
||||
cmdl = command
|
||||
assert args.len == 0
|
||||
else:
|
||||
cmdl = buildCommandLine(command, args)
|
||||
var wd: cstring = nil
|
||||
@@ -441,11 +451,11 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
var ee = newWideCString(e)
|
||||
var wwd = newWideCString(wd)
|
||||
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)
|
||||
else:
|
||||
success = winlean.createProcessA(nil,
|
||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, SI, ProcInfo)
|
||||
cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, e, wd, si, procInfo)
|
||||
let lastError = osLastError()
|
||||
|
||||
if poParentStreams notin options:
|
||||
@@ -455,7 +465,6 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
fileClose(si.hStdError)
|
||||
|
||||
if e != nil: dealloc(e)
|
||||
dealloc(cmdl)
|
||||
if success == 0: osError(lastError)
|
||||
# Close the handle now so anyone waiting is woken:
|
||||
discard closeHandle(procInfo.hThread)
|
||||
@@ -495,7 +504,7 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
proc peekExitCode(p: PProcess): int =
|
||||
var b = waitForSingleObject(p.fProcessHandle, 50) == WAIT_TIMEOUT
|
||||
if b: result = -1
|
||||
else:
|
||||
else:
|
||||
var res: int32
|
||||
discard getExitCodeProcess(p.fProcessHandle, res)
|
||||
return res
|
||||
@@ -525,7 +534,7 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
|
||||
else:
|
||||
var res = winlean.createProcessA(nil, command, nil, nil, 0,
|
||||
NORMAL_PRIORITY_CLASS, nil, nil, SI, ProcInfo)
|
||||
NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
|
||||
if res == 0:
|
||||
osError(osLastError())
|
||||
else:
|
||||
@@ -538,13 +547,13 @@ when defined(Windows) and not defined(useNimRtl):
|
||||
result = -1
|
||||
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
|
||||
var rfds: TWOHandleArray
|
||||
for i in 0..readfds.len()-1:
|
||||
rfds[i] = readfds[i].fProcessHandle
|
||||
|
||||
var ret = waitForMultipleObjects(readfds.len.int32,
|
||||
|
||||
var ret = waitForMultipleObjects(readfds.len.int32,
|
||||
addr(rfds), 0'i32, timeout.int32)
|
||||
case ret
|
||||
of WAIT_TIMEOUT:
|
||||
@@ -561,22 +570,7 @@ elif not defined(useNimRtl):
|
||||
readIdx = 0
|
||||
writeIdx = 1
|
||||
|
||||
proc addCmdArgs(command: string, args: openArray[string]): string =
|
||||
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 =
|
||||
proc envToCStringArray(t: PStringTable): cstringArray =
|
||||
result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
|
||||
var i = 0
|
||||
for key, val in pairs(t):
|
||||
@@ -595,7 +589,7 @@ elif not defined(useNimRtl):
|
||||
result[i] = cast[cstring](alloc(x.len+1))
|
||||
copyMem(result[i], addr(x[0]), x.len+1)
|
||||
inc(i)
|
||||
|
||||
|
||||
proc startProcess(command: string,
|
||||
workingDir: string = "",
|
||||
args: openArray[string] = [],
|
||||
@@ -609,23 +603,38 @@ elif not defined(useNimRtl):
|
||||
if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or
|
||||
pipe(pStderr) != 0'i32:
|
||||
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
|
||||
when defined(posix_spawn) and not defined(useFork):
|
||||
var attr: Tposix_spawnattr
|
||||
var fops: Tposix_spawn_file_actions
|
||||
|
||||
template chck(e: expr) =
|
||||
template chck(e: expr) =
|
||||
if e != 0'i32: osError(osLastError())
|
||||
|
||||
chck posix_spawn_file_actions_init(fops)
|
||||
chck posix_spawnattr_init(attr)
|
||||
|
||||
|
||||
var mask: Tsigset
|
||||
chck sigemptyset(mask)
|
||||
chck posix_spawnattr_setsigmask(attr, mask)
|
||||
chck posix_spawnattr_setpgroup(attr, 0'i32)
|
||||
|
||||
|
||||
chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
|
||||
POSIX_SPAWN_SETSIGMASK or
|
||||
POSIX_SPAWN_SETPGROUP)
|
||||
@@ -639,27 +648,22 @@ elif not defined(useNimRtl):
|
||||
if poStdErrToStdOut in options:
|
||||
chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
|
||||
else:
|
||||
chck posix_spawn_file_actions_adddup2(fops, pStderr[writeIdx], 2)
|
||||
|
||||
var e = if env == nil: envToCStringArray() else: toCStringArray(env)
|
||||
var a: cstringArray
|
||||
chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2)
|
||||
|
||||
var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env)
|
||||
var res: cint
|
||||
# This is incorrect!
|
||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
||||
if poUseShell notin options:
|
||||
a = toCStringArray([extractFilename(command)], args)
|
||||
res = posix_spawn(pid, command, fops, attr, a, e)
|
||||
if poUsePath in options:
|
||||
res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
|
||||
else:
|
||||
var x = addCmdArgs(command, args)
|
||||
a = toCStringArray(["sh", "-c"], [x])
|
||||
res = posix_spawn(pid, "/bin/sh", fops, attr, a, e)
|
||||
deallocCStringArray(a)
|
||||
deallocCStringArray(e)
|
||||
res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
|
||||
deallocCStringArray(sys_env)
|
||||
discard posix_spawn_file_actions_destroy(fops)
|
||||
discard posix_spawnattr_destroy(attr)
|
||||
chck res
|
||||
|
||||
else:
|
||||
|
||||
pid = fork()
|
||||
if pid < 0: osError(osLastError())
|
||||
if pid == 0:
|
||||
@@ -680,19 +684,18 @@ elif not defined(useNimRtl):
|
||||
if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
|
||||
|
||||
if workingDir.len > 0: os.setCurrentDir(workingDir)
|
||||
if poUseShell notin options:
|
||||
var a = toCStringArray([extractFilename(command)], args)
|
||||
if env == nil:
|
||||
discard execv(command, a)
|
||||
|
||||
if env == nil:
|
||||
if poUsePath in options:
|
||||
discard execvp(sys_command, sys_args)
|
||||
else:
|
||||
discard execve(command, a, toCStringArray(env))
|
||||
discard execv(sys_command, sys_args)
|
||||
else:
|
||||
var x = addCmdArgs(command, args)
|
||||
var a = toCStringArray(["sh", "-c"], [x])
|
||||
if env == nil:
|
||||
discard execv("/bin/sh", a)
|
||||
var c_env = envToCStringArray(env)
|
||||
if poUsePath in options:
|
||||
discard execvpe(sys_command, sys_args, c_env)
|
||||
else:
|
||||
discard execve("/bin/sh", a, toCStringArray(env))
|
||||
discard execve(sys_command, sys_args, c_env)
|
||||
# too risky to raise an exception here:
|
||||
quit("execve call failed: " & $strerror(errno))
|
||||
# Parent process. Copy process information.
|
||||
@@ -788,15 +791,15 @@ elif not defined(useNimRtl):
|
||||
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
|
||||
|
||||
proc execCmd(command: string): int =
|
||||
result = csystem(command)
|
||||
result = csystem(command) shr 8
|
||||
|
||||
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
|
||||
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
|
||||
FD_ZERO(fd)
|
||||
for i in items(s):
|
||||
for i in items(s):
|
||||
m = max(m, int(i.outHandle))
|
||||
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 L = s.len
|
||||
while i < L:
|
||||
@@ -807,26 +810,26 @@ elif not defined(useNimRtl):
|
||||
inc(i)
|
||||
setLen(s, L)
|
||||
|
||||
proc select(readfds: var seq[PProcess], timeout = 500): int =
|
||||
var tv: Ttimeval
|
||||
proc select(readfds: var seq[PProcess], timeout = 500): int =
|
||||
var tv: TTimeVal
|
||||
tv.tv_sec = 0
|
||||
tv.tv_usec = timeout * 1000
|
||||
|
||||
|
||||
var rd: TFdSet
|
||||
var m = 0
|
||||
createFdSet((rd), readfds, m)
|
||||
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
|
||||
|
||||
|
||||
pruneProcessSet(readfds, (rd))
|
||||
|
||||
|
||||
proc execCmdEx*(command: string, options: set[TProcessOption] = {
|
||||
poStdErrToStdOut, poUseShell}): tuple[
|
||||
output: TaintedString,
|
||||
poStdErrToStdOut, poUsePath}): tuple[
|
||||
output: TaintedString,
|
||||
exitCode: int] {.tags: [FExecIO, FReadIO].} =
|
||||
## a convenience proc that runs the `command`, grabs all its output and
|
||||
## 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 '\l': my.bufpos = handleLF(my, my.bufpos)
|
||||
else: break
|
||||
of '\0': nil
|
||||
of '\0': discard
|
||||
else: error(my, my.bufpos, my.sep & " expected")
|
||||
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)
|
||||
inc(c.bufpos)
|
||||
else:
|
||||
nil
|
||||
discard
|
||||
|
||||
proc handleOctChar(c: var TSqlLexer, xi: var int) =
|
||||
if c.buf[c.bufpos] in {'0'..'7'}:
|
||||
@@ -373,7 +373,7 @@ proc getOperator(c: var TSqlLexer, tok: var TToken) =
|
||||
of '+':
|
||||
if not trailingPlusMinus and buf[pos+1] notin operators and
|
||||
tok.literal.len > 0: break
|
||||
of '*', '<', '>', '=': nil
|
||||
of '*', '<', '>', '=': discard
|
||||
else: break
|
||||
add(tok.literal, buf[pos])
|
||||
inc(pos)
|
||||
@@ -1120,7 +1120,7 @@ proc rs(n: PSqlNode, s: var string, indent: int,
|
||||
proc ra(n: PSqlNode, s: var string, indent: int) =
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of nkNone: nil
|
||||
of nkNone: discard
|
||||
of nkIdent:
|
||||
if allCharsInSet(n.strVal, {'\33'..'\127'}):
|
||||
s.add(n.strVal)
|
||||
|
||||
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):
|
||||
quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string)
|
||||
|
||||
const compiledWithSsl = defined(ssl)
|
||||
|
||||
proc connect*(address: string, port = 25,
|
||||
ssl = false, debug = false): TSMTP =
|
||||
## Establishes a connection with a SMTP server.
|
||||
## May fail with EInvalidReply or with a socket error.
|
||||
result.sock = socket()
|
||||
if ssl:
|
||||
when defined(ssl):
|
||||
when compiledWithSsl:
|
||||
let ctx = newContext(verifyMode = CVerifyNone)
|
||||
ctx.wrapSocket(result.sock)
|
||||
else:
|
||||
|
||||
@@ -311,7 +311,8 @@ when defined(ssl):
|
||||
newCTX.SSLCTXSetVerify(SSLVerifyNone, nil)
|
||||
if newCTX == nil:
|
||||
SSLError()
|
||||
|
||||
|
||||
discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY)
|
||||
newCTX.loadCertificates(certFile, keyFile)
|
||||
return PSSLContext(newCTX)
|
||||
|
||||
@@ -1291,14 +1292,14 @@ proc readLine*(socket: TSocket, line: var TaintedString, timeout = -1) {.
|
||||
var c: char
|
||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||
var n = recv(socket, addr(c), 1)
|
||||
if n < 0: osError(osLastError())
|
||||
if n < 0: socket.socketError()
|
||||
elif n == 0: return
|
||||
if c == '\r':
|
||||
discard waitFor(socket, waited, timeout, 1, "readLine")
|
||||
n = peekChar(socket, c)
|
||||
if n > 0 and c == '\L':
|
||||
discard recv(socket, addr(c), 1)
|
||||
elif n <= 0: osError(osLastError())
|
||||
elif n <= 0: socket.socketError()
|
||||
addNLIfEmpty()
|
||||
return
|
||||
elif c == '\L':
|
||||
|
||||
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'}
|
||||
## the set of characters a newline terminator can start with
|
||||
|
||||
AllChars* = {'\x00'..'\xFF'}
|
||||
## A set with all the possible characters. Not very useful by its own, you
|
||||
## can use it to create *inverted* sets to make the ``find()`` proc find
|
||||
## **invalid** characters in strings. Example:
|
||||
##
|
||||
## .. code-block:: nimrod
|
||||
## let invalid = AllChars - Digits
|
||||
## doAssert "01234".find(invalid) == -1
|
||||
## doAssert "01A34".find(invalid) == 2
|
||||
|
||||
proc toLower*(c: char): char {.noSideEffect, procvar,
|
||||
rtl, extern: "nsuToLowerChar".} =
|
||||
## Converts `c` into lower case. This works only for the letters A-Z.
|
||||
|
||||
@@ -211,7 +211,9 @@ proc initInterval*(miliseconds, seconds, minutes, hours, days, months,
|
||||
result.months = months
|
||||
result.years = years
|
||||
|
||||
proc isLeapYear(year: int): bool =
|
||||
proc isLeapYear*(year: int): bool =
|
||||
## returns true if ``year`` is a leap year
|
||||
|
||||
if year mod 400 == 0:
|
||||
return true
|
||||
elif year mod 100 == 0:
|
||||
@@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool =
|
||||
else:
|
||||
return false
|
||||
|
||||
proc getDaysInMonth(month: TMonth, year: int): int =
|
||||
proc getDaysInMonth*(month: TMonth, year: int): int =
|
||||
## gets the amount of days in a ``month`` of a ``year``
|
||||
|
||||
# http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month
|
||||
case month
|
||||
of mFeb: result = if isLeapYear(year): 29 else: 28
|
||||
|
||||
@@ -96,7 +96,7 @@ proc parse(x: var TXmlParser, errors: var seq[string]): PXmlNode =
|
||||
## &entity;
|
||||
errors.add(errorMsg(x, "unknown entity: " & x.entityName))
|
||||
next(x)
|
||||
of xmlEof: nil
|
||||
of xmlEof: discard
|
||||
|
||||
proc parseXml*(s: PStream, filename: string,
|
||||
errors: var seq[string]): PXmlNode =
|
||||
@@ -110,7 +110,7 @@ proc parseXml*(s: PStream, filename: string,
|
||||
of xmlElementOpen, xmlElementStart:
|
||||
result = parse(x, errors)
|
||||
break
|
||||
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: nil # just skip it
|
||||
of xmlComment, xmlWhitespace, xmlSpecial, xmlPI: discard # just skip it
|
||||
of xmlError:
|
||||
errors.add(errorMsg(x))
|
||||
else:
|
||||
|
||||
@@ -2333,29 +2333,29 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||
|
||||
elif defined(JS):
|
||||
# Stubs:
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = nil
|
||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
|
||||
|
||||
proc GC_disable() = nil
|
||||
proc GC_enable() = nil
|
||||
proc GC_fullCollect() = nil
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
||||
proc GC_enableMarkAndSweep() = nil
|
||||
proc GC_disableMarkAndSweep() = nil
|
||||
proc GC_disable() = discard
|
||||
proc GC_enable() = discard
|
||||
proc GC_fullCollect() = discard
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) = discard
|
||||
proc GC_enableMarkAndSweep() = discard
|
||||
proc GC_disableMarkAndSweep() = discard
|
||||
proc GC_getStatistics(): string = return ""
|
||||
|
||||
proc getOccupiedMem(): int = return -1
|
||||
proc getFreeMem(): int = return -1
|
||||
proc getTotalMem(): int = return -1
|
||||
|
||||
proc dealloc(p: pointer) = nil
|
||||
proc alloc(size: int): pointer = nil
|
||||
proc alloc0(size: int): pointer = nil
|
||||
proc realloc(p: Pointer, newsize: int): pointer = nil
|
||||
proc dealloc(p: pointer) = discard
|
||||
proc alloc(size: int): pointer = discard
|
||||
proc alloc0(size: int): pointer = discard
|
||||
proc realloc(p: Pointer, newsize: int): pointer = discard
|
||||
|
||||
proc allocShared(size: int): pointer = nil
|
||||
proc allocShared0(size: int): pointer = nil
|
||||
proc deallocShared(p: pointer) = nil
|
||||
proc reallocShared(p: pointer, newsize: int): pointer = nil
|
||||
proc allocShared(size: int): pointer = discard
|
||||
proc allocShared0(size: int): pointer = discard
|
||||
proc deallocShared(p: pointer) = discard
|
||||
proc reallocShared(p: pointer, newsize: int): pointer = discard
|
||||
|
||||
when defined(JS):
|
||||
include "system/jssys"
|
||||
@@ -2490,11 +2490,11 @@ proc staticRead*(filename: string): string {.magic: "Slurp".}
|
||||
## ``slurp`` is an alias for ``staticRead``.
|
||||
|
||||
proc gorge*(command: string, input = ""): string {.
|
||||
magic: "StaticExec".} = nil
|
||||
magic: "StaticExec".} = discard
|
||||
## This is an alias for ``staticExec``.
|
||||
|
||||
proc staticExec*(command: string, input = ""): string {.
|
||||
magic: "StaticExec".} = nil
|
||||
magic: "StaticExec".} = discard
|
||||
## Executes an external process at compile-time.
|
||||
## if `input` is not an empty string, it will be passed as a standard input
|
||||
## to the executed program.
|
||||
@@ -2561,7 +2561,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
|
||||
## $pos.line, astToStr(code)]
|
||||
## assert false, "A test expecting failure succeeded?"
|
||||
## except exception:
|
||||
## nil
|
||||
## discard
|
||||
##
|
||||
## proc tester(pos: int): int =
|
||||
## let
|
||||
|
||||
@@ -23,7 +23,7 @@ else:
|
||||
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
|
||||
header: "<windows.h>", nodecl.}
|
||||
|
||||
proc writeToStdErr(msg: CString) =
|
||||
proc writeToStdErr(msg: cstring) =
|
||||
discard MessageBoxA(0, msg, nil, 0)
|
||||
|
||||
proc showErrorMessage(data: cstring) =
|
||||
|
||||
@@ -802,7 +802,7 @@ when defined(sparc): # For SPARC architecture.
|
||||
# Addresses decrease as the stack grows.
|
||||
while sp <= max:
|
||||
gcMark(gch, sp[])
|
||||
sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer))
|
||||
sp = cast[PPointer](cast[TAddress](sp) +% sizeof(pointer))
|
||||
|
||||
elif defined(ELATE):
|
||||
{.error: "stack marking code is to be written for this architecture".}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
# (c) Copyright 2014 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -59,11 +59,11 @@ var
|
||||
gch {.rtlThreadVar.}: TGcHeap
|
||||
|
||||
when not defined(useNimRtl):
|
||||
InstantiateForRegion(gch.region)
|
||||
instantiateForRegion(gch.region)
|
||||
|
||||
template acquire(gch: TGcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
AcquireSys(HeapLock)
|
||||
acquireSys(HeapLock)
|
||||
|
||||
template release(gch: TGcHeap) =
|
||||
when hasThreadSupport and hasSharedHeap:
|
||||
@@ -90,7 +90,7 @@ proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
|
||||
# used for code generation concerning debugging
|
||||
result = usrToCell(c).typ
|
||||
|
||||
proc unsureAsgnRef(dest: ppointer, src: pointer) {.inline.} =
|
||||
proc unsureAsgnRef(dest: PPointer, src: pointer) {.inline.} =
|
||||
dest[] = src
|
||||
|
||||
proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||
@@ -114,10 +114,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||
|
||||
# forward declarations:
|
||||
proc collectCT(gch: var TGcHeap)
|
||||
proc IsOnStack*(p: pointer): bool {.noinline.}
|
||||
proc isOnStack*(p: pointer): bool {.noinline.}
|
||||
proc forAllChildren(cell: PCell, op: TWalkOp)
|
||||
proc doOperation(p: pointer, op: TWalkOp)
|
||||
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp)
|
||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp)
|
||||
# we need the prototype here for debugging purposes
|
||||
|
||||
proc prepareDealloc(cell: PCell) =
|
||||
@@ -162,19 +162,19 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||
if m != nil: forAllSlotsAux(dest, m, op)
|
||||
of nkNone: sysAssert(false, "forAllSlotsAux")
|
||||
|
||||
proc forAllChildrenAux(dest: Pointer, mt: PNimType, op: TWalkOp) =
|
||||
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: TWalkOp) =
|
||||
var d = cast[TAddress](dest)
|
||||
if dest == nil: return # nothing to do
|
||||
if ntfNoRefs notin mt.flags:
|
||||
case mt.Kind
|
||||
case mt.kind
|
||||
of tyRef, tyString, tySequence: # leaf:
|
||||
doOperation(cast[ppointer](d)[], op)
|
||||
doOperation(cast[PPointer](d)[], op)
|
||||
of tyObject, tyTuple:
|
||||
forAllSlotsAux(dest, mt.node, op)
|
||||
of tyArray, tyArrayConstr, tyOpenArray:
|
||||
for i in 0..(mt.size div mt.base.size)-1:
|
||||
forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc forAllChildren(cell: PCell, op: TWalkOp) =
|
||||
gcAssert(cell != nil, "forAllChildren: 1")
|
||||
@@ -184,7 +184,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
|
||||
if marker != nil:
|
||||
marker(cellToUsr(cell), op.int)
|
||||
else:
|
||||
case cell.typ.Kind
|
||||
case cell.typ.kind
|
||||
of tyRef: # common case
|
||||
forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
|
||||
of tySequence:
|
||||
@@ -194,7 +194,7 @@ proc forAllChildren(cell: PCell, op: TWalkOp) =
|
||||
for i in 0..s.len-1:
|
||||
forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +%
|
||||
GenericSeqSize), cell.typ.base, op)
|
||||
else: nil
|
||||
else: discard
|
||||
|
||||
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
|
||||
# generates a new object and sets its reference counter to 0
|
||||
@@ -466,7 +466,7 @@ else:
|
||||
sp = sp +% sizeof(pointer)*8
|
||||
# last few entries:
|
||||
while sp <=% max:
|
||||
gcMark(gch, cast[ppointer](sp)[])
|
||||
gcMark(gch, cast[PPointer](sp)[])
|
||||
sp = sp +% sizeof(pointer)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
@@ -505,7 +505,7 @@ when not defined(useNimRtl):
|
||||
else:
|
||||
dec(gch.recGcLock)
|
||||
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) = nil
|
||||
proc GC_setStrategy(strategy: TGC_Strategy) = discard
|
||||
|
||||
proc GC_enableMarkAndSweep() =
|
||||
gch.cycleThreshold = InitialThreshold
|
||||
|
||||
@@ -16,8 +16,12 @@ const
|
||||
type
|
||||
THandle* = int
|
||||
LONG* = int32
|
||||
ULONG* = int
|
||||
PULONG* = ptr int
|
||||
WINBOOL* = int32
|
||||
DWORD* = int32
|
||||
PDWORD* = ptr DWORD
|
||||
LPINT* = ptr int32
|
||||
HDC* = THandle
|
||||
HGLRC* = THandle
|
||||
|
||||
@@ -195,16 +199,31 @@ else:
|
||||
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
|
||||
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
|
||||
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
|
||||
proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
|
||||
proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {.
|
||||
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
|
||||
proc removeDirectoryA*(lpPathName: cstring): int32 {.
|
||||
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
|
||||
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
|
||||
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
|
||||
|
||||
proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
|
||||
proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {.
|
||||
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
|
||||
|
||||
|
||||
when useWinUnicode:
|
||||
proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
|
||||
flags: DWORD): int32 {.
|
||||
importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall.}
|
||||
proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
|
||||
security: pointer=nil): int32 {.
|
||||
importc:"CreateHardLinkW", dynlib: "kernel32", stdcall.}
|
||||
else:
|
||||
proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring,
|
||||
flags: DWORD): int32 {.
|
||||
importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall.}
|
||||
proc createHardLinkA*(lpFileName, lpExistingFileName: cstring,
|
||||
security: pointer=nil): int32 {.
|
||||
importc:"CreateHardLinkA", dynlib: "kernel32", stdcall.}
|
||||
|
||||
const
|
||||
FILE_ATTRIBUTE_ARCHIVE* = 32'i32
|
||||
FILE_ATTRIBUTE_COMPRESSED* = 2048'i32
|
||||
@@ -212,6 +231,7 @@ const
|
||||
FILE_ATTRIBUTE_DIRECTORY* = 16'i32
|
||||
FILE_ATTRIBUTE_HIDDEN* = 2'i32
|
||||
FILE_ATTRIBUTE_READONLY* = 1'i32
|
||||
FILE_ATTRIBUTE_REPARSE_POINT* = 1024'i32
|
||||
FILE_ATTRIBUTE_SYSTEM* = 4'i32
|
||||
FILE_ATTRIBUTE_TEMPORARY* = 256'i32
|
||||
|
||||
@@ -284,7 +304,7 @@ else:
|
||||
dwFileAttributes: int32): WINBOOL {.
|
||||
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
|
||||
|
||||
proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
|
||||
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
|
||||
bFailIfExists: cint): cint {.
|
||||
importc: "CopyFileA", stdcall, dynlib: "kernel32".}
|
||||
|
||||
@@ -616,3 +636,76 @@ when not useWinUnicode:
|
||||
proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
|
||||
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
|
||||
dataSize*: cint
|
||||
finished*: TBsonBool
|
||||
stack*: array[0..32 - 1, cint]
|
||||
ownsData*: TBsonBool
|
||||
err*: cint
|
||||
stackSize*: cint
|
||||
stackPos*: cint
|
||||
err*: cint ## Bitfield representing errors or warnings on this buffer
|
||||
errstr*: cstring ## A string representation of the most recent error
|
||||
## or warning.
|
||||
stackPtr*: ptr csize
|
||||
stack*: array[0..32 - 1, csize]
|
||||
|
||||
TDate* = int64
|
||||
|
||||
@@ -141,6 +142,7 @@ proc print*(TBson: cstring, depth: cint) {.stdcall,
|
||||
importc: "bson_print_raw", dynlib: bsondll.}
|
||||
## Print a string representation of a BSON object up to `depth`.
|
||||
|
||||
|
||||
proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data",
|
||||
dynlib: bsondll.}
|
||||
## Return a pointer to the raw buffer stored by this bson object.
|
||||
@@ -590,19 +592,30 @@ type
|
||||
hosts*: ptr THostPort ## List of host/ports given by the replica set
|
||||
name*: cstring ## Name of the replica set.
|
||||
primary_connected*: TBsonBool ## Primary node connection status.
|
||||
|
||||
TWriteConcern*{.pure, final.} = object ## mongo_write_concern
|
||||
w*: cint
|
||||
wtimeout*: cint
|
||||
j*: cint
|
||||
fsync*: cint
|
||||
mode*: cstring
|
||||
cmd*: TBSon
|
||||
|
||||
TMongo*{.pure, final.} = object ## mongo
|
||||
primary*: ptr THostPort ## Primary connection info.
|
||||
replset*: ptr TReplSet ## replset object if connected to a replica set.
|
||||
sock*: cint ## Socket file descriptor.
|
||||
flags*: cint ## Flags on this connection object.
|
||||
conn_timeout_ms*: cint ## Connection timeout in milliseconds.
|
||||
op_timeout_ms*: cint ## Read and write timeout in milliseconds.
|
||||
connected*: TBsonBool ## Connection status.
|
||||
err*: TError ## Most recent driver error code.
|
||||
errstr*: array[0..128 - 1, char] ## String version of most recent driver error code.
|
||||
lasterrcode*: cint ## getlasterror code given by the server on error.
|
||||
lasterrstr*: cstring ## getlasterror string generated by server.
|
||||
primary*: ptr THostPort ## Primary connection info.
|
||||
replset*: ptr TReplSet ## replset object if connected to a replica set.
|
||||
sock*: cint ## Socket file descriptor.
|
||||
flags*: cint ## Flags on this connection object.
|
||||
conn_timeout_ms*: cint ## Connection timeout in milliseconds.
|
||||
op_timeout_ms*: cint ## Read and write timeout in milliseconds.
|
||||
max_bson_size*: cint ## Largest BSON object allowed on this connection.
|
||||
connected*: TBsonBool ## Connection status.
|
||||
write_concern*: TWriteConcern ## The default write concern.
|
||||
err*: TError ## Most recent driver error code.
|
||||
errcode*: cint ## Most recent errno or WSAGetLastError().
|
||||
errstr*: array[0..128 - 1, char] ## String version of most recent driver error code.
|
||||
lasterrcode*: cint ## getlasterror code given by the server on error.
|
||||
lasterrstr*: array[0..128 - 1, char] ## getlasterror string generated by server.
|
||||
|
||||
TCursor*{.pure, final.} = object ## cursor
|
||||
reply*: ptr TReply ## reply is owned by cursor
|
||||
@@ -654,7 +667,11 @@ proc init*(conn: var TMongo){.stdcall, importc: "mongo_init", dynlib: mongodll.}
|
||||
|
||||
proc connect*(conn: var TMongo, host: cstring = defaultHost,
|
||||
port: cint = defaultPort): cint {.stdcall,
|
||||
importc: "mongo_connect", dynlib: mongodll.}
|
||||
importc: "mongo_connect", dynlib: mongodll, deprecated.}
|
||||
## Connect to a single MongoDB server.
|
||||
proc client*(conn: var TMongo, host: cstring = defaultHost,
|
||||
port: cint = defaultPort): cint {.stdcall,
|
||||
importc: "mongo_client", dynlib: mongodll.}
|
||||
## Connect to a single MongoDB server.
|
||||
|
||||
proc replsetInit*(conn: var TMongo, name: cstring){.stdcall,
|
||||
@@ -714,7 +731,8 @@ proc destroy*(conn: var TMongo){.stdcall, importc: "mongo_destroy",
|
||||
## You must always call this function when finished with the connection
|
||||
## object.
|
||||
|
||||
proc insert*(conn: var TMongo, ns: cstring, data: var TBson): cint{.stdcall,
|
||||
proc insert*(conn: var TMongo, ns: cstring, data: var TBson,
|
||||
custom_write_concern: ptr TWriteConcern): cint{.stdcall,
|
||||
importc: "mongo_insert", dynlib: mongodll, discardable.}
|
||||
## Insert a BSON document into a MongoDB server. This function
|
||||
## will fail if the supplied BSON struct is not UTF-8 or if
|
||||
|
||||
@@ -268,14 +268,22 @@ proc OpenSSL_add_all_algorithms*(){.cdecl, dynlib: DLLUtilName, importc: "OPENSS
|
||||
|
||||
proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.}
|
||||
|
||||
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLSSLName, importc.}
|
||||
when not defined(windows):
|
||||
proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
|
||||
dynlib: DLLSSLName, importc.}
|
||||
|
||||
proc CRYPTO_malloc_init*() =
|
||||
when not defined(windows):
|
||||
CRYPTO_set_mem_functions(alloc, realloc, dealloc)
|
||||
|
||||
when True:
|
||||
nil
|
||||
proc SSL_CTX_ctrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: pointer): int{.
|
||||
cdecl, dynlib: DLLSSLName, importc.}
|
||||
|
||||
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int =
|
||||
result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil)
|
||||
|
||||
when true:
|
||||
discard
|
||||
else:
|
||||
proc SslCtxSetCipherList*(arg0: PSSL_CTX, str: cstring): cInt{.cdecl,
|
||||
dynlib: DLLSSLName, importc.}
|
||||
@@ -288,7 +296,6 @@ else:
|
||||
proc SslCTXCtrl*(ctx: PSSL_CTX, cmd: cInt, larg: int, parg: Pointer): int{.
|
||||
cdecl, dynlib: DLLSSLName, importc.}
|
||||
|
||||
proc SSLCTXSetMode*(ctx: PSSL_CTX, mode: int): int
|
||||
proc SSLSetMode*(s: PSSL, mode: int): int
|
||||
proc SSLCTXGetMode*(ctx: PSSL_CTX): int
|
||||
proc SSLGetMode*(s: PSSL): int
|
||||
@@ -417,15 +424,12 @@ else:
|
||||
enc: cInt){.cdecl, dynlib: DLLUtilName, importc.}
|
||||
# implementation
|
||||
|
||||
proc SSLCTXSetMode(ctx: PSSL_CTX, mode: int): int =
|
||||
Result = SslCTXCtrl(ctx, SSL_CTRL_MODE, mode, nil)
|
||||
|
||||
proc SSLSetMode(s: PSSL, mode: int): int =
|
||||
Result = SSLctrl(s, SSL_CTRL_MODE, mode, nil)
|
||||
result = SSLctrl(s, SSL_CTRL_MODE, mode, nil)
|
||||
|
||||
proc SSLCTXGetMode(ctx: PSSL_CTX): int =
|
||||
Result = SSLCTXctrl(ctx, SSL_CTRL_MODE, 0, nil)
|
||||
result = SSLCTXctrl(ctx, SSL_CTRL_MODE, 0, nil)
|
||||
|
||||
proc SSLGetMode(s: PSSL): int =
|
||||
Result = SSLctrl(s, SSL_CTRL_MODE, 0, nil)
|
||||
result = SSLctrl(s, SSL_CTRL_MODE, 0, nil)
|
||||
|
||||
|
||||
@@ -61,5 +61,5 @@ allowing you to create commercial applications.
|
||||
|
||||
Read copying.txt for more details.
|
||||
|
||||
Copyright (c) 2004-2013 Andreas Rumpf.
|
||||
Copyright (c) 2004-2014 Andreas Rumpf.
|
||||
All rights reserved.
|
||||
|
||||
@@ -61,5 +61,5 @@ allowing you to create commercial applications.
|
||||
|
||||
Read copying.txt for more details.
|
||||
|
||||
Copyright (c) 2004-2013 Andreas Rumpf.
|
||||
Copyright (c) 2004-2014 Andreas Rumpf.
|
||||
All rights reserved.
|
||||
|
||||
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 """
|
||||
output: '''(bar: bar)
|
||||
output: '''24
|
||||
(bar: bar)
|
||||
1244
|
||||
6
|
||||
abcdefghijklmnopqrstuvwxyz
|
||||
@@ -8,6 +9,10 @@ abcdefghijklmnopqrstuvwxyz
|
||||
|
||||
import strutils
|
||||
|
||||
const fac4 = (var x = 1; for i in 1..4: x *= i; x)
|
||||
|
||||
echo fac4
|
||||
|
||||
when true:
|
||||
proc test(foo: proc (x, y: int): bool) =
|
||||
echo foo(5, 5)
|
||||
@@ -69,3 +74,15 @@ proc semiProblem() =
|
||||
if false: echo "aye"; echo "indeed"
|
||||
|
||||
semiProblem()
|
||||
|
||||
|
||||
# bug #844
|
||||
|
||||
import json
|
||||
proc parseResponse(): PJsonNode =
|
||||
result = % { "key1": % { "key2": % "value" } }
|
||||
for key, val in result["key1"]:
|
||||
var excMsg = key & "("
|
||||
if (var n=result["key2"]; n != nil):
|
||||
excMsg &= n.str
|
||||
raise newException(ESynch, excMsg)
|
||||
|
||||
@@ -6,7 +6,7 @@ when defined(GC_setMaxPause):
|
||||
GC_setMaxPause 2_000
|
||||
|
||||
type
|
||||
TExpr = object ## abstract base class for an expression
|
||||
TExpr = object {.inheritable.} ## abstract base class for an expression
|
||||
PLiteral = ref TLiteral
|
||||
TLiteral = object of TExpr
|
||||
x: int
|
||||
|
||||
25
tests/gc/gcleak5.nim
Normal file
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)
|
||||
a.field = "some string"
|
||||
|
||||
|
||||
proc someOther[T](len: string): seq[T] = discard
|
||||
proc someOther[T](len: int): seq[T] = echo "we"
|
||||
|
||||
proc foo[T](x: T) =
|
||||
var s = someOther[T](34)
|
||||
#newSeq[T](34)
|
||||
|
||||
foo 23
|
||||
|
||||
|
||||
|
||||
when false:
|
||||
# Compiles unless you use var a: PA[string]
|
||||
type
|
||||
|
||||
@@ -22,11 +22,11 @@ proc factory2(a, b: int): iterator (): int =
|
||||
yield x
|
||||
inc x
|
||||
|
||||
let foo = factory 1, 4
|
||||
let foo = factory(1, 4)
|
||||
|
||||
for f in foo():
|
||||
echo f
|
||||
|
||||
let foo2 = factory2 1,2
|
||||
let foo2 = factory2(1,2)
|
||||
|
||||
for f in foo2(): echo f
|
||||
|
||||
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
|
||||
|
||||
template plus(a, b: expr): expr =
|
||||
template plus(a, b: expr): expr {.dirty} =
|
||||
a + b
|
||||
|
||||
macro call(e: expr): expr =
|
||||
|
||||
@@ -7,7 +7,7 @@ proc dumpit(n: PNimrodNode): string {.compileTime.} =
|
||||
result = $n.kind
|
||||
add(result, "(")
|
||||
case n.kind
|
||||
of nnkEmpty: nil # same as nil node in this representation
|
||||
of nnkEmpty: discard # same as nil node in this representation
|
||||
of nnkNilLit: add(result, "nil")
|
||||
of nnkCharLit..nnkInt64Lit: add(result, $n.intVal)
|
||||
of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal)
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
discard """
|
||||
file: "tmacrogenerics.nim"
|
||||
msg: '''
|
||||
instantiation 1 with int and float
|
||||
instantiation 2 with float and string
|
||||
instantiation 3 with string and string
|
||||
counter: 3
|
||||
instantiation 1 with typedesc and typedesc
|
||||
counter: 1
|
||||
'''
|
||||
output: "int\nfloat\nint\nstring"
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
discard """
|
||||
disabled: true
|
||||
"""
|
||||
|
||||
import macros, typetraits
|
||||
|
||||
macro checkType(ex, expected: expr): stmt {.immediate.} =
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
out: '''HELLO WORLD'''
|
||||
output: '''HELLO WORLD'''
|
||||
"""
|
||||
|
||||
import macros, strutils
|
||||
|
||||
@@ -9,7 +9,7 @@ proc concat(strings: varargs[string]): string =
|
||||
result = newString(0)
|
||||
for s in items(strings): result.add(s)
|
||||
|
||||
template ProcessInterpolations(e: expr) =
|
||||
template processInterpolations(e: expr) =
|
||||
var s = e[1].strVal
|
||||
for f in interpolatedFragments(s):
|
||||
case f.kind
|
||||
@@ -35,7 +35,7 @@ macro formatStyleInterpolation(e: expr): expr =
|
||||
proc addDollar() =
|
||||
formatString.add("$$")
|
||||
|
||||
ProcessInterpolations(e)
|
||||
processInterpolations(e)
|
||||
|
||||
result = parseExpr("\"x\" % [y]")
|
||||
result[1].strVal = formatString
|
||||
@@ -50,7 +50,7 @@ macro concatStyleInterpolation(e: expr): expr =
|
||||
proc addExpr(e: PNimrodNode) = args.add(e)
|
||||
proc addDollar() = args.add(newStrLitNode"$")
|
||||
|
||||
ProcessInterpolations(e)
|
||||
processInterpolations(e)
|
||||
|
||||
result = newCall("concat", args)
|
||||
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
discard """
|
||||
output: "12"
|
||||
output: '''140
|
||||
5-120-120
|
||||
359'''
|
||||
"""
|
||||
#import math
|
||||
|
||||
proc optarg(x:int, y:int = 0):int = x + 3 * y
|
||||
proc singlearg(x:int):int = 20*x
|
||||
echo optarg 1, singlearg 2
|
||||
|
||||
|
||||
proc foo(x: int): int = x-1
|
||||
proc foo(x, y: int): int = x-y
|
||||
|
||||
let x = foo 7.foo, # comment here
|
||||
foo(1, foo 8)
|
||||
# 12 = 6 - -6
|
||||
echo x
|
||||
let x = optarg foo 7.foo
|
||||
let y = singlearg foo(1, foo 8)
|
||||
let z = singlearg 1.foo foo 8
|
||||
|
||||
echo x, y, z
|
||||
|
||||
let a = [2,4,8].map do (d:int) -> int: d + 1
|
||||
echo a[0], a[1], a[2]
|
||||
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
|
||||
MachineId = distinct int64
|
||||
MachineId* = distinct int64
|
||||
CommitId = distinct int64
|
||||
|
||||
proc `$`(id: MachineId): string {.borrow.}
|
||||
proc `$`*(id: MachineId): string {.borrow.}
|
||||
proc `$`(id: CommitId): string {.borrow.}
|
||||
|
||||
var
|
||||
@@ -61,7 +61,7 @@ var
|
||||
|
||||
proc `()`(cmd: string{lit}): string = cmd.execProcess.string.strip
|
||||
|
||||
proc getMachine: MachineId =
|
||||
proc getMachine*(db: TDbConn): MachineId =
|
||||
var name = "hostname"()
|
||||
if name.len == 0:
|
||||
name = when defined(posix): getenv"HOSTNAME".string
|
||||
@@ -76,7 +76,7 @@ proc getMachine: MachineId =
|
||||
result = db.insertId(sql"insert into Machine(name, os, cpu) values (?,?,?)",
|
||||
name, system.hostOS, system.hostCPU).MachineId
|
||||
|
||||
proc getCommit: CommitId =
|
||||
proc getCommit(db: TDbConn): CommitId =
|
||||
const commLen = "commit ".len
|
||||
let hash = "git log -n 1"()[commLen..commLen+10]
|
||||
let branch = "git symbolic-ref --short HEAD"()
|
||||
@@ -115,7 +115,7 @@ proc open*() =
|
||||
db = open(connection="testament.db", user="testament", password="",
|
||||
database="testament")
|
||||
createDb()
|
||||
thisMachine = getMachine()
|
||||
thisCommit = getCommit()
|
||||
thisMachine = getMachine(db)
|
||||
thisCommit = getCommit(db)
|
||||
|
||||
proc close*() = close(db)
|
||||
|
||||
@@ -123,9 +123,14 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
|
||||
test "gcleak2"
|
||||
test "gctest"
|
||||
test "gcleak3"
|
||||
test "gcleak4"
|
||||
test "gcleak5"
|
||||
test "weakrefs"
|
||||
test "cycleleak"
|
||||
test "closureleak"
|
||||
test "refarrayleak"
|
||||
test "stackrefleak"
|
||||
|
||||
|
||||
# ------------------------- threading tests -----------------------------------
|
||||
|
||||
|
||||
@@ -159,3 +159,22 @@ proc generateHtml*(filename: string, commit: int) =
|
||||
outfile.write(HtmlEnd)
|
||||
close(db)
|
||||
close(outfile)
|
||||
|
||||
proc generateJson*(filename: string, commit: int) =
|
||||
const selRow = """select count(*),
|
||||
sum(result = 'reSuccess'),
|
||||
sum(result = 'reIgnored')
|
||||
from TestResult
|
||||
where [commit] = ? and machine = ?
|
||||
order by category"""
|
||||
var db = open(connection="testament.db", user="testament", password="",
|
||||
database="testament")
|
||||
let lastCommit = db.getCommit(commit)
|
||||
|
||||
var outfile = open(filename, fmWrite)
|
||||
|
||||
let data = db.getRow(sql(selRow), lastCommit, $backend.getMachine(db))
|
||||
|
||||
outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#}""" % data)
|
||||
close(db)
|
||||
close(outfile)
|
||||
|
||||
@@ -208,20 +208,6 @@ proc makeTest(test, options: string, cat: Category, action = actionCompile,
|
||||
|
||||
include categories
|
||||
|
||||
proc toJson(res: TResults): PJsonNode =
|
||||
result = newJObject()
|
||||
result["total"] = newJInt(res.total)
|
||||
result["passed"] = newJInt(res.passed)
|
||||
result["skipped"] = newJInt(res.skipped)
|
||||
|
||||
proc outputJson(reject, compile, run: TResults) =
|
||||
var doc = newJObject()
|
||||
doc["reject"] = toJson(reject)
|
||||
doc["compile"] = toJson(compile)
|
||||
doc["run"] = toJson(run)
|
||||
var s = pretty(doc)
|
||||
writeFile(jsonFile, s)
|
||||
|
||||
# proc runCaasTests(r: var TResults) =
|
||||
# for test, output, status, mode in caasTestsRunner():
|
||||
# r.addResult(test, "", output & "-> " & $mode,
|
||||
@@ -259,6 +245,7 @@ proc main() =
|
||||
var commit = 0
|
||||
discard parseInt(p.cmdLineRest.string, commit)
|
||||
generateHtml(resultsFile, commit)
|
||||
generateJson(jsonFile, commit)
|
||||
else:
|
||||
quit usage
|
||||
|
||||
|
||||
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
|
||||
=============
|
||||
|
||||
- better debugging support for writes to locations
|
||||
- document new templating symbol binding rules
|
||||
- fix eval in macros.nim
|
||||
- fix GC issues
|
||||
- fix macros\tstringinterp.nim
|
||||
- test and fix showoff
|
||||
- fix closure iterators
|
||||
|
||||
|
||||
Bugs
|
||||
@@ -16,7 +17,6 @@ Bugs
|
||||
- compilation of niminst takes way too long. looks like a regression
|
||||
- docgen: sometimes effects are listed twice
|
||||
- 'result' is not properly cleaned for NRVO --> use uninit checking instead
|
||||
- sneaking with qualifiedLookup() is really broken!
|
||||
- blocks can "export" an identifier but the CCG generates {} for them ...
|
||||
- osproc execProcesses can deadlock if all processes fail (as experienced
|
||||
in c++ mode)
|
||||
@@ -25,6 +25,9 @@ Bugs
|
||||
version 0.9.x
|
||||
=============
|
||||
|
||||
- implement 'union' and 'bits' pragmas
|
||||
- fix closures
|
||||
- test and fix exception handling
|
||||
- ensure (ref T)(a, b) works as a type conversion and type constructor
|
||||
- optimize 'genericReset'; 'newException' leads to code bloat
|
||||
- stack-less GC
|
||||
|
||||
@@ -62,7 +62,7 @@ srcdoc2: "pure/ftpclient;pure/memfiles;pure/subexes;pure/collections/critbits"
|
||||
srcdoc2: "pure/asyncio;pure/actors;core/locks;pure/oids;pure/endians;pure/uri"
|
||||
srcdoc2: "pure/nimprof;pure/unittest;packages/docutils/highlite"
|
||||
srcdoc2: "packages/docutils/rst;packages/docutils/rstast"
|
||||
srcdoc2: "packages/docutils/rstgen"
|
||||
srcdoc2: "packages/docutils/rstgen;pure/logging"
|
||||
|
||||
webdoc: "wrappers/libcurl;pure/md5;wrappers/mysql;wrappers/iup"
|
||||
webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc"
|
||||
|
||||
Reference in New Issue
Block a user