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

This commit is contained in:
Audun Wilhelmsen 2014-02-23 00:26:46 +01:00
commit 1250db5075
71 changed files with 1533 additions and 692 deletions

View file

@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode =
if p.tok.s == "unsigned": if p.tok.s == "unsigned":
isUnsigned = true isUnsigned = true
elif p.tok.s == "signed" or p.tok.s == "int": elif p.tok.s == "signed" or p.tok.s == "int":
nil discard
else: else:
add(x, p.tok.s) add(x, p.tok.s)
getTok(p, nil) getTok(p, nil)
@ -746,7 +746,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
result = declarator(p, a, ident) result = declarator(p, a, ident)
eat(p, pxParRi, result) eat(p, pxParRi, result)
else: else:
nil discard
return parseTypeSuffix(p, a) return parseTypeSuffix(p, a)
proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
@ -1165,7 +1165,7 @@ proc enumSpecifier(p: var TParser): PNode =
proc setBaseFlags(n: PNode, base: TNumericalBase) = proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base case base
of base10: nil of base10: discard
of base2: incl(n.flags, nfBase2) of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8) of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16) of base16: incl(n.flags, nfBase16)
@ -1686,7 +1686,7 @@ proc switchStatement(p: var TParser): PNode =
break break
of "case", "default": of "case", "default":
break break
else: nil else: discard
addSon(result, statement(p)) addSon(result, statement(p))
if sonsLen(result) == 0: if sonsLen(result) == 0:
# translate empty statement list to Nimrod's ``nil`` statement # translate empty statement list to Nimrod's ``nil`` statement

View file

@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) =
m.body.add(tok) m.body.add(tok)
of pxDirConc: of pxDirConc:
# just ignore this token: this implements token merging correctly # just ignore this token: this implements token merging correctly
nil discard
else: else:
m.body.add(p.tok) m.body.add(p.tok)
# we do not want macro expansion here: # we do not want macro expansion here:
@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxDirectiveParLe, pxDirective: of pxDirectiveParLe, pxDirective:
case p.tok.s case p.tok.s
of "else", "endif", "elif": break of "else", "endif", "elif": break
else: nil else: discard
addSon(result, statement(p)) addSon(result, statement(p))
proc eatEndif(p: var TParser) = proc eatEndif(p: var TParser) =

View file

@ -89,6 +89,7 @@ type
errInvalidIndexValueForTuple, errCommandExpectsFilename, errInvalidIndexValueForTuple, errCommandExpectsFilename,
errMainModuleMustBeSpecified, errMainModuleMustBeSpecified,
errXExpected, errXExpected,
errTIsNotAConcreteType,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
@ -103,6 +104,7 @@ type
errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
errXCannotBeClosure, errXMustBeCompileTime, errXCannotBeClosure, errXMustBeCompileTime,
errCannotInferTypeOfTheLiteral,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -312,6 +314,7 @@ const
errCommandExpectsFilename: "command expects a filename argument", errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "\'$1\' expected", errXExpected: "\'$1\' expected",
errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
errInvalidSectionStart: "invalid section start", errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented", errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error", errGeneralParseError: "general parse error",
@ -346,6 +349,7 @@ const
errIllegalCaptureX: "illegal capture '$1'", errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
errXMustBeCompileTime: "'$1' can only be used in compile-time context", errXMustBeCompileTime: "'$1' can only be used in compile-time context",
errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",

View file

@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
h = h +% ord(c) h = h +% ord(c)
h = h +% h shl 10 h = h +% h shl 10
h = h xor (h shr 6) h = h xor (h shr 6)
of '_': nil of '_': discard
else: break else: break
inc(pos) inc(pos)
h = h +% h shl 3 h = h +% h shl 3

View file

@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind,
proc setBaseFlags(n: PNode, base: TNumericalBase) = proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base case base
of base10: nil of base10: discard
of base2: incl(n.flags, nfBase2) of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8) of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16) of base16: incl(n.flags, nfBase16)
@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
eat(p, pxCurlyDirRi) eat(p, pxCurlyDirRi)
opNode.ident = getIdent("&") opNode.ident = getIdent("&")
else: else:
nil discard
of pxMinus: of pxMinus:
if p.tok.xkind == pxPer: if p.tok.xkind == pxPer:
getTok(p) getTok(p)
@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
of pxNeq: of pxNeq:
opNode.ident = getIdent("!=") opNode.ident = getIdent("!=")
else: else:
nil discard
skipCom(p, opNode) # read sub-expression with higher priority skipCom(p, opNode) # read sub-expression with higher priority
nextop = lowestExprAux(p, v2, opPred) nextop = lowestExprAux(p, v2, opPred)
addSon(node, opNode) addSon(node, opNode)
@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode =
(n.sons[2].kind in {nkCharLit, nkStrLit}): (n.sons[2].kind in {nkCharLit, nkStrLit}):
n.sons[0].ident = getIdent("&") # fix operator n.sons[0].ident = getIdent("&") # fix operator
else: else:
nil discard
if not (n.kind in {nkEmpty..nkNilLit}): if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i])
@ -603,7 +603,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxCurlyDirLe, pxStarDirLe: of pxCurlyDirLe, pxStarDirLe:
if not isHandledDirective(p): break if not isHandledDirective(p): break
else: else:
nil discard
addSon(result, parseStmt(p)) addSon(result, parseStmt(p))
if sonsLen(result) == 1: result = result.sons[0] if sonsLen(result) == 1: result = result.sons[0]
@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode =
addSon(b, c) addSon(b, c)
addSon(a, b) addSon(a, b)
if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id:
nil discard
else: else:
addSon(s, a) addSon(s, a)
addSon(result, s) addSon(result, s)
@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode =
getTok(p) getTok(p)
v = newNodeP(nkVarTy, p) v = newNodeP(nkVarTy, p)
else: else:
nil discard
while true: while true:
case p.tok.xkind case p.tok.xkind
of pxSymbol: a = createIdentNodeP(p.tok.ident, p) of pxSymbol: a = createIdentNodeP(p.tok.ident, p)
@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode =
proc exSymbol(n: var PNode) = proc exSymbol(n: var PNode) =
case n.kind case n.kind
of nkPostfix: of nkPostfix:
nil discard
of nkPragmaExpr: of nkPragmaExpr:
exSymbol(n.sons[0]) exSymbol(n.sons[0])
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) =
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
of nkIdentDefs: of nkIdentDefs:
for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i])
of nkNilLit, nkEmpty: nil of nkNilLit, nkEmpty: discard
else: internalError(n.info, "fixRecordDef(): " & $n.kind) else: internalError(n.info, "fixRecordDef(): " & $n.kind)
proc addPragmaToIdent(ident: var PNode, pragma: PNode) = proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
@ -1191,7 +1191,7 @@ proc parseRecordBody(p: var TParser, result, definition: PNode) =
if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p))
else: internalError(result.info, "anonymous record is not supported") else: internalError(result.info, "anonymous record is not supported")
else: else:
nil discard
opt(p, pxSemicolon) opt(p, pxSemicolon)
skipCom(p, result) skipCom(p, result)
@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) =
proc exSymbols(n: PNode) = proc exSymbols(n: PNode) =
case n.kind case n.kind
of nkEmpty..nkNilLit: nil of nkEmpty..nkNilLit: discard
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
of nkWhenStmt, nkStmtList: of nkWhenStmt, nkStmtList:
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
@ -1410,7 +1410,7 @@ proc exSymbols(n: PNode) =
exSymbol(n.sons[i].sons[0]) exSymbol(n.sons[i].sons[0])
if n.sons[i].sons[2].kind == nkObjectTy: if n.sons[i].sons[2].kind == nkObjectTy:
fixRecordDef(n.sons[i].sons[2]) fixRecordDef(n.sons[i].sons[2])
else: nil else: discard
proc parseBegin(p: var TParser, result: PNode) = proc parseBegin(p: var TParser, result: PNode) =
getTok(p) getTok(p)

View file

@ -97,8 +97,25 @@ proc makeExternImport(s: PSym, extname: string) =
incl(s.flags, sfImportc) incl(s.flags, sfImportc)
excl(s.flags, sfForward) excl(s.flags, sfForward)
proc makeExternExport(s: PSym, extname: string) = const invalidIdentChars = AllChars - IdentChars
proc validateExternCName(s: PSym, info: TLineInfo) =
## Validates that the symbol name in s.loc.r is a valid C identifier.
##
## Valid identifiers are those alphanumeric including the underscore not
## starting with a number. If the check fails, a generic error will be
## displayed to the user.
let target = ropeToStr(s.loc.r)
if target.len < 1 or (not (target[0] in IdentStartChars)) or
(not target.allCharsInSet(IdentChars)):
localError(info, errGenerated, "invalid exported symbol")
proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
setExternName(s, extname) setExternName(s, extname)
case gCmd
of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC:
validateExternCName(s, info)
else: discard
incl(s.flags, sfExportc) incl(s.flags, sfExportc)
proc processImportCompilerProc(s: PSym, extname: string) = proc processImportCompilerProc(s: PSym, extname: string) =
@ -515,7 +532,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if k in validPragmas: if k in validPragmas:
case k case k
of wExportc: of wExportc:
makeExternExport(sym, getOptionalStr(c, it, "$1")) makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
incl(sym.flags, sfUsed) # avoid wrong hints incl(sym.flags, sfUsed) # avoid wrong hints
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
of wImportCompilerProc: of wImportCompilerProc:
@ -601,7 +618,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
processDynLib(c, it, sym) processDynLib(c, it, sym)
of wCompilerproc: of wCompilerproc:
noVal(it) # compilerproc may not get a string! noVal(it) # compilerproc may not get a string!
makeExternExport(sym, "$1") makeExternExport(sym, "$1", it.info)
incl(sym.flags, sfCompilerProc) incl(sym.flags, sfCompilerProc)
incl(sym.flags, sfUsed) # suppress all those stupid warnings incl(sym.flags, sfUsed) # suppress all those stupid warnings
registerCompilerProc(sym) registerCompilerProc(sym)

View file

@ -333,6 +333,7 @@ proc myOpen(module: PSym): PPassContext =
c.semOperand = semOperand c.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall c.semOverloadedCall = semOverloadedCall
c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode c.semTypeNode = semTypeNode
pushProcCon(c, module) pushProcCon(c, module)

View file

@ -81,6 +81,7 @@ type
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode {.nimcall.} filter: TSymKinds): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
info: TLineInfo): PSym info: TLineInfo): PSym
includedFiles*: TIntSet # used to detect recursive include files includedFiles*: TIntSet # used to detect recursive include files

View file

@ -1829,8 +1829,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
if result.kind == nkSym: if result.kind == nkSym:
markIndirect(c, result.sym) markIndirect(c, result.sym)
if isGenericRoutine(result.sym): # if isGenericRoutine(result.sym):
localError(n.info, errInstantiateXExplicitely, s.name.s) # localError(n.info, errInstantiateXExplicitely, s.name.s)
of nkSym: of nkSym:
# because of the changed symbol binding, this does not mean that we # because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again! # don't have to check the symbol for semantics here again!

View file

@ -143,10 +143,11 @@ proc discardCheck(c: PContext, result: PNode) =
while n.kind in skipForDiscardable: while n.kind in skipForDiscardable:
n = n.lastSon n = n.lastSon
n.typ = nil n.typ = nil
elif c.inTypeClass > 0 and result.typ.kind == tyBool: elif c.inTypeClass > 0:
let verdict = semConstExpr(c, result) if result.typ.kind == tyBool:
if verdict.intVal == 0: let verdict = semConstExpr(c, result)
localError(result.info, "type class predicate failed") if verdict.intVal == 0:
localError(result.info, "type class predicate failed")
elif result.typ.kind != tyError and gCmd != cmdInteractive: elif result.typ.kind != tyError and gCmd != cmdInteractive:
if result.typ.kind == tyNil: if result.typ.kind == tyNil:
fixNilType(result) fixNilType(result)
@ -349,7 +350,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# BUGFIX: ``fitNode`` is needed here! # BUGFIX: ``fitNode`` is needed here!
# check type compability between def.typ and typ: # check type compability between def.typ and typ:
if typ != nil: def = fitNode(c, typ, def) if typ != nil: def = fitNode(c, typ, def)
else: typ = skipIntLit(def.typ) else:
typ = skipIntLit(def.typ)
if typ.kind in {tySequence, tyArray, tySet} and
typ.lastSon.kind == tyEmpty:
localError(def.info, errCannotInferTypeOfTheLiteral,
($typ.kind).substr(2).toLower)
else: else:
def = ast.emptyNode def = ast.emptyNode
if symkind == skLet: localError(a.info, errLetNeedsInit) if symkind == skLet: localError(a.info, errLetNeedsInit)
@ -764,6 +770,29 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
s.ast = a s.ast = a
popOwner() popOwner()
proc checkForMetaFields(n: PNode) =
template checkMeta(t) =
if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
localError(n.info, errTIsNotAConcreteType, t.typeToString)
case n.kind
of nkRecList, nkRecCase:
for s in n: checkForMetaFields(s)
of nkOfBranch, nkElse:
checkForMetaFields(n.lastSon)
of nkSym:
let t = n.sym.typ
case t.kind
of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
tyProc, tyGenericInvokation, tyGenericInst:
let start = ord(t.kind in {tyGenericInvokation, tyGenericInst})
for i in start .. <t.sons.len:
checkMeta(t.sons[i])
else:
checkMeta(t)
else:
internalAssert false
proc typeSectionFinalPass(c: PContext, n: PNode) = proc typeSectionFinalPass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
@ -780,6 +809,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
assignType(s.typ, t) assignType(s.typ, t)
s.typ.id = t.id # same id s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ) checkConstructedType(s.info, s.typ)
if s.typ.kind in {tyObject, tyTuple}:
checkForMetaFields(s.typ.n)
let aa = a.sons[2] let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy: aa.sons[0].kind == nkObjectTy:
@ -883,12 +914,19 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
s = n[namePos].sym s = n[namePos].sym
pushOwner(s) pushOwner(s)
openScope(c) openScope(c)
var gp: PNode
if n.sons[genericParamsPos].kind != nkEmpty: if n.sons[genericParamsPos].kind != nkEmpty:
illFormedAst(n) # process parameters: n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
gp = n.sons[genericParamsPos]
else:
gp = newNodeI(nkGenericParams, n.info)
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
var gp = newNodeI(nkGenericParams, n.info)
semParamList(c, n.sons[paramsPos], gp, s) semParamList(c, n.sons[paramsPos], gp, s)
paramsTypeCheck(c, s.typ) # paramsTypeCheck(c, s.typ)
if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
# we have a list of implicit type parameters:
n.sons[genericParamsPos] = gp
else: else:
s.typ = newTypeS(tyProc, c) s.typ = newTypeS(tyProc, c)
rawAddSon(s.typ, nil) rawAddSon(s.typ, nil)
@ -900,12 +938,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
#if efDetermineType notin flags: #if efDetermineType notin flags:
# XXX not good enough; see tnamedparamanonproc.nim # XXX not good enough; see tnamedparamanonproc.nim
pushProcCon(c, s) if n.sons[genericParamsPos].kind == nkEmpty:
addResult(c, s.typ.sons[0], n.info, skProc) pushProcCon(c, s)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) addResult(c, s.typ.sons[0], n.info, skProc)
n.sons[bodyPos] = transformBody(c.module, semBody, s) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
addResultNode(c, n) n.sons[bodyPos] = transformBody(c.module, semBody, s)
popProcCon(c) addResultNode(c, n)
popProcCon(c)
sideEffectsCheck(c, s) sideEffectsCheck(c, s)
else: else:
localError(n.info, errImplOfXexpected, s.name.s) localError(n.info, errImplOfXexpected, s.name.s)
@ -913,6 +952,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
popOwner() popOwner()
result.typ = s.typ result.typ = s.typ
proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
var n = n
n = replaceTypesInBody(c, pt, n)
result = n
n.sons[genericParamsPos] = emptyNode
n.sons[paramsPos] = n.typ.n
openScope(c)
var s = n.sons[namePos].sym
addParams(c, n.typ.n, skProc)
pushProcCon(c, s)
addResult(c, n.typ.sons[0], n.info, skProc)
let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
addResultNode(c, n)
popProcCon(c)
closeScope(c)
s.ast = result
# alternative variant (not quite working):
# var prc = arg[0].sym
# let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
# result = inferred.ast
# result.kind = arg.kind
proc activate(c: PContext, n: PNode) = proc activate(c: PContext, n: PNode) =
# XXX: This proc is part of my plan for getting rid of # XXX: This proc is part of my plan for getting rid of
# forward declarations. stay tuned. # forward declarations. stay tuned.
@ -1263,8 +1330,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
let (outer, inner) = insertDestructors(c, n.sons[i]) let (outer, inner) = insertDestructors(c, n.sons[i])
if outer != nil: if outer != nil:
n.sons[i] = outer n.sons[i] = outer
for j in countup(i+1, length-1): var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
inner.addSon(semStmt(c, n.sons[j])) inner.addSon(semStmtList(c, rest, flags))
n.sons.setLen(i+1) n.sons.setLen(i+1)
return return
of LastBlockStmts: of LastBlockStmts:

View file

@ -669,7 +669,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyTypeDesc: of tyTypeDesc:
if tfUnresolved notin paramType.flags: if tfUnresolved notin paramType.flags:
# naked typedescs are not bindOnce types # naked typedescs are not bindOnce types
if paramType.sonsLen == 0 and paramTypId != nil and if paramType.base.kind == tyNone and paramTypId != nil and
paramTypId.id == typedescId.id: paramTypId = nil paramTypId.id == typedescId.id: paramTypId = nil
result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
@ -705,7 +705,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
result = instGenericContainer(c, paramType.sym.info, result, result = instGenericContainer(c, paramType.sym.info, result,
allowMetaTypes = true) allowMetaTypes = true)
result.lastSon.shouldHaveMeta
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
result = addImplicitGeneric(result) result = addImplicitGeneric(result)
@ -1011,7 +1010,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mOrdinal: result = semOrdinal(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
of mVarargs: result = semVarargs(c, n, prev) of mVarargs: result = semVarargs(c, n, prev)
of mExpr, mTypeDesc: of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
of mExpr:
result = semTypeNode(c, n.sons[0], nil) result = semTypeNode(c, n.sons[0], nil)
if result != nil: if result != nil:
result = copyType(result, getCurrOwner(), false) result = copyType(result, getCurrOwner(), false)

View file

@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
localError(info, errVarVarTypeNotAllowed) localError(info, errVarVarTypeNotAllowed)
elif computeSize(t) == szIllegalRecursion: elif computeSize(t) == szIllegalRecursion:
localError(info, errIllegalRecursionInTypeX, typeToString(t)) localError(info, errIllegalRecursionInTypeX, typeToString(t))
when false: when false:
if t.kind == tyObject and t.sons[0] != nil: if t.kind == tyObject and t.sons[0] != nil:
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
@ -219,7 +220,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
# is difficult to handle: # is difficult to handle:
var body = t.sons[0] var body = t.sons[0]
if body.kind != tyGenericBody: internalError(cl.info, "no generic body") if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
var header: PType = nil var header: PType = t
# search for some instantiation here: # search for some instantiation here:
if cl.allowMetaTypes: if cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t)) result = PType(idTableGet(cl.localCache, t))
@ -231,11 +232,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if x.kind == tyGenericParam: if x.kind == tyGenericParam:
x = lookupTypeVar(cl, x) x = lookupTypeVar(cl, x)
if x != nil: if x != nil:
if header == nil: header = instCopyType(cl, t) if header == t: header = instCopyType(cl, t)
header.sons[i] = x header.sons[i] = x
propagateToOwner(header, x) propagateToOwner(header, x)
else:
propagateToOwner(header, x)
if header != nil: if header != t:
# search again after first pass: # search again after first pass:
result = searchInstTypes(header) result = searchInstTypes(header)
if result != nil: return if result != nil: return
@ -243,6 +246,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
header = instCopyType(cl, t) header = instCopyType(cl, t)
result = newType(tyGenericInst, t.sons[0].owner) result = newType(tyGenericInst, t.sons[0].owner)
result.flags = header.flags
# be careful not to propagate unnecessary flags here (don't use rawAddSon) # be careful not to propagate unnecessary flags here (don't use rawAddSon)
result.sons = @[header.sons[0]] result.sons = @[header.sons[0]]
# ugh need another pass for deeply recursive generic types (e.g. PActor) # ugh need another pass for deeply recursive generic types (e.g. PActor)
@ -409,14 +413,22 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard else: discard
proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
initIdTable(result.symMap)
copyIdTable(result.typeMap, pt)
initIdTable(result.localCache)
result.info = info
result.c = p
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode =
var cl = initTypeVars(p, pt, n.info)
pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n)
popInfoContext()
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType = t: PType): PType =
var cl: TReplTypeVars var cl = initTypeVars(p, pt, info)
initIdTable(cl.symMap)
copyIdTable(cl.typeMap, pt)
initIdTable(cl.localCache)
cl.info = info
cl.c = p
pushInfoContext(info) pushInfoContext(info)
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()

View file

@ -343,53 +343,57 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} =
proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
proc procParamTypeRel(c: var TCandidate, f, a: PType, proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
result: var TTypeRelation) = var f = f
var
m: TTypeRelation
f = f
if a.isMetaType: if a.isMetaType:
if f.isMetaType: if f.isMetaType:
# we are matching a generic proc (as proc param) # We are matching a generic proc (as proc param)
# to another generic type appearing in the proc # to another generic type appearing in the proc
# sigunature. there is a change that the target # signature. There is a change that the target
# type is already fully-determined, so we are # type is already fully-determined, so we are
# going to try resolve it # going to try resolve it
f = generateTypeInstance(c.c, c.bindings, c.call.info, f) f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
if f == nil or f.isMetaType: if f == nil or f.isMetaType:
# no luck resolving the type, so the inference fails # no luck resolving the type, so the inference fails
result = isNone return isNone
return
let reverseRel = typeRel(c, a, f) let reverseRel = typeRel(c, a, f)
if reverseRel == isGeneric: if reverseRel == isGeneric:
m = isInferred result = isInferred
inc c.genericMatches
else: else:
m = typeRel(c, f, a) result = typeRel(c, f, a)
if m <= isSubtype or inconsistentVarTypes(f, a): if result <= isSubtype or inconsistentVarTypes(f, a):
result = isNone result = isNone
return
else: if result == isEqual:
result = minRel(m, result) inc c.exactMatches
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind case a.kind
of tyProc: of tyProc:
if sonsLen(f) != sonsLen(a): return if sonsLen(f) != sonsLen(a): return
# Note: We have to do unification for the parameters before the
# return type!
result = isEqual # start with maximum; also correct for no result = isEqual # start with maximum; also correct for no
# params at all # params at all
for i in countup(1, sonsLen(f)-1):
procParamTypeRel(c, f.sons[i], a.sons[i], result) template checkParam(f, a) =
result = minRel(result, procParamTypeRel(c, f, a))
if result == isNone: return
# Note: We have to do unification for the parameters before the
# return type!
for i in 1 .. <f.sonsLen:
checkParam(f.sons[i], a.sons[i])
if f.sons[0] != nil: if f.sons[0] != nil:
if a.sons[0] != nil: if a.sons[0] != nil:
procParamTypeRel(c, f.sons[0], a.sons[0], result) checkParam(f.sons[0], a.sons[0])
else: else:
return isNone return isNone
elif a.sons[0] != nil: elif a.sons[0] != nil:
return isNone return isNone
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
return isNone return isNone
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}: elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
@ -896,20 +900,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
of tyTypeDesc: of tyTypeDesc:
if a.kind != tyTypeDesc: return isNone
var prev = PType(idTableGet(c.bindings, f)) var prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:
# proc foo(T: typedesc, x: T)
# when `f` is an unresolved typedesc, `a` could be any
# type, so we should not perform this check earlier
if a.kind != tyTypeDesc: return isNone
if f.base.kind == tyNone: if f.base.kind == tyNone:
result = isGeneric result = isGeneric
else: else:
result = typeRel(c, f.base, a.base) result = typeRel(c, f.base, a.base)
if result != isNone: if result != isNone:
put(c.bindings, f, a) put(c.bindings, f, a)
else: else:
let toMatch = if tfUnresolved in f.flags: a if tfUnresolved in f.flags:
else: a.base result = typeRel(c, prev.base, a)
result = typeRel(c, prev.base, toMatch) elif a.kind == tyTypeDesc:
result = typeRel(c, prev.base, a.base)
else:
result = isNone
of tyStmt: of tyStmt:
result = isGeneric result = isGeneric
@ -1035,10 +1046,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
#result = copyTree(arg) #result = copyTree(arg)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isInferred, isInferredConvertible: of isInferred, isInferredConvertible:
var prc = if arg.kind in nkLambdaKinds: arg[0].sym if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
else: arg.sym result = c.semInferredLambda(c, m.bindings, arg)
let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) else:
result = newSymNode(inferred, arg.info) let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
result = newSymNode(inferred, arg.info)
if r == isInferredConvertible: if r == isInferredConvertible:
result = implicitConv(nkHiddenStdConv, f, result, m, c) result = implicitConv(nkHiddenStdConv, f, result, m, c)
of isGeneric: of isGeneric:
@ -1101,6 +1113,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
# incorrect to simply use the first fitting match. However, to implement # incorrect to simply use the first fitting match. However, to implement
# this correctly is inefficient. We have to copy `m` here to be able to # this correctly is inefficient. We have to copy `m` here to be able to
# roll back the side effects of the unification algorithm. # roll back the side effects of the unification algorithm.
let c = m.c let c = m.c
var x, y, z: TCandidate var x, y, z: TCandidate
initCandidate(c, x, m.callee) initCandidate(c, x, m.callee)

View file

@ -1042,7 +1042,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
of tyEmpty: of tyEmpty:
result = taField in flags result = taField in flags
of tyTypeClasses: of tyTypeClasses:
result = true result = tfGenericTypeParam in t.flags or
taField notin flags
of tyGenericBody, tyGenericParam, tyGenericInvokation, of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr, tyFieldAccessor: tyNone, tyForward, tyFromExpr, tyFieldAccessor:
result = false result = false

View file

@ -1025,7 +1025,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
regs[ra].sons[0].flags.incl nfIsRef regs[ra].sons[0].flags.incl nfIsRef
of opcNCopyNimNode: of opcNCopyNimNode:
decodeB(nkMetaNode) decodeB(nkMetaNode)
setMeta(regs[ra], copyNode(regs[rb])) setMeta(regs[ra], copyNode(regs[rb].skipMeta))
of opcNCopyNimTree: of opcNCopyNimTree:
decodeB(nkMetaNode) decodeB(nkMetaNode)
setMeta(regs[ra], copyTree(regs[rb])) setMeta(regs[ra], copyTree(regs[rb]))

View file

@ -321,9 +321,18 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
c.gen(n.sons[2], dest) c.gen(n.sons[2], dest)
c.patch(L1) c.patch(L1)
proc nilLiteral(n: PNode): PNode =
if n.kind == nkNilLit and n.typ.sym != nil and
n.typ.sym.magic == mPNimrodNode:
let nilo = newNodeIT(nkNilLit, n.info, n.typ)
result = newNodeIT(nkMetaNode, n.info, n.typ)
result.add nilo
else:
result = n
proc rawGenLiteral(c: PCtx; n: PNode): int = proc rawGenLiteral(c: PCtx; n: PNode): int =
result = c.constants.len result = c.constants.len
c.constants.add n c.constants.add n.nilLiteral
internalAssert result < 0x7fff internalAssert result < 0x7fff
proc sameConstant*(a, b: PNode): bool = proc sameConstant*(a, b: PNode): bool =
@ -907,17 +916,20 @@ proc requiresCopy(n: PNode): bool =
proc unneededIndirection(n: PNode): bool = proc unneededIndirection(n: PNode): bool =
n.typ.skipTypes(abstractInst-{tyTypeDesc}).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]):
result = n.sons[0]
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) = flags: TGenFlags) =
# a nop for certain types # a nop for certain types
let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags
if unneededIndirection(n.sons[0]): # consider:
# proc foo(f: var ref int) =
# f = new(int)
# proc blah() =
# var x: ref int
# foo x
#
# The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for
# nkAddr we must not use 'unneededIndirection', but for deref we use it.
if opc != opcAddr and unneededIndirection(n.sons[0]):
gen(c, n.sons[0], dest, flags) gen(c, n.sons[0], dest, flags)
else: else:
let tmp = c.genx(n.sons[0], flags) let tmp = c.genx(n.sons[0], flags)
@ -1109,7 +1121,12 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkFloatLit, info, t) result = newNodeIT(nkFloatLit, info, t)
of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef: tyStmt, tyTypeDesc, tyStatic, tyRef:
result = newNodeIT(nkNilLit, info, t) if t.sym != nil and t.sym.magic == mPNimrodNode:
let nilo = newNodeIT(nkNilLit, info, t)
result = newNodeIT(nkMetaNode, info, t)
result.add nilo
else:
result = newNodeIT(nkNilLit, info, t)
of tyProc: of tyProc:
if t.callConv != ccClosure: if t.callConv != ccClosure:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)

View file

@ -36,7 +36,7 @@ block mainLoop:
case x.kind case x.kind
of xmlEof: break mainLoop of xmlEof: break mainLoop
of xmlElementClose: break of xmlElementClose: break
else: nil else: discard
x.next() # skip ``xmlElementClose`` x.next() # skip ``xmlElementClose``
# now we have the description for the ``a`` element # now we have the description for the ``a`` element
var desc = "" var desc = ""

View file

@ -7,6 +7,8 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
from posix import TSocketHandle
const const
EPOLLIN* = 0x00000001 EPOLLIN* = 0x00000001
EPOLLPRI* = 0x00000002 EPOLLPRI* = 0x00000002
@ -33,8 +35,8 @@ const
type type
epoll_data* {.importc: "union epoll_data", epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union. header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
thePtr* {.importc: "ptr".}: pointer # \ #thePtr* {.importc: "ptr".}: pointer
#fd*: cint fd*: cint # \
#u32*: uint32 #u32*: uint32
#u64*: uint64 #u64*: uint64
@ -54,7 +56,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
## Same as epoll_create but with an FLAGS parameter. The unused SIZE ## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped. ## parameter has been dropped.
proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {. proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".} importc: "epoll_ctl", header: "<sys/epoll.h>".}
## Manipulate an epoll instance "epfd". Returns 0 in case of success, ## Manipulate an epoll instance "epfd". Returns 0 in case of success,
## -1 in case of error ( the "errno" variable will contain the ## -1 in case of error ( the "errno" variable will contain the

View file

@ -2356,7 +2356,7 @@ proc FD_ZERO*(a1: var TFdSet) {.importc, header: "<sys/select.h>".}
proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec, proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec,
a6: var Tsigset): cint {.importc, header: "<sys/select.h>".} a6: var Tsigset): cint {.importc, header: "<sys/select.h>".}
proc select*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {. proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
importc, header: "<sys/select.h>".} importc, header: "<sys/select.h>".}
when hasSpawnH: when hasSpawnH:

View file

@ -7,9 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
import os, oids, tables, strutils import os, oids, tables, strutils, macros
import winlean
import sockets2, net import sockets2, net
@ -23,14 +21,13 @@ import sockets2, net
# -- Futures # -- Futures
type type
PFutureVoid* = ref object of PObject PFutureBase* = ref object of PObject
cbVoid: proc () {.closure.} cb: proc () {.closure.}
finished: bool finished: bool
PFuture*[T] = ref object of PFutureVoid PFuture*[T] = ref object of PFutureBase
value: T value: T
error: ref EBase error: ref EBase
cb: proc (future: PFuture[T]) {.closure.}
proc newFuture*[T](): PFuture[T] = proc newFuture*[T](): PFuture[T] =
## Creates a new future. ## Creates a new future.
@ -39,42 +36,38 @@ proc newFuture*[T](): PFuture[T] =
proc complete*[T](future: PFuture[T], val: T) = proc complete*[T](future: PFuture[T], val: T) =
## Completes ``future`` with value ``val``. ## Completes ``future`` with value ``val``.
assert(not future.finished) assert(not future.finished, "Future already finished, cannot finish twice.")
assert(future.error == nil) assert(future.error == nil)
future.value = val future.value = val
future.finished = true future.finished = true
if future.cb != nil: if future.cb != nil:
future.cb(future) future.cb()
if future.cbVoid != nil:
future.cbVoid()
proc fail*[T](future: PFuture[T], error: ref EBase) = proc fail*[T](future: PFuture[T], error: ref EBase) =
## Completes ``future`` with ``error``. ## Completes ``future`` with ``error``.
assert(not future.finished) assert(not future.finished, "Future already finished, cannot finish twice.")
future.finished = true future.finished = true
future.error = error future.error = error
if future.cb != nil: if future.cb != nil:
future.cb(future) future.cb()
proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: You most likely want the other ``callback`` setter which
## passes ``future`` as a param to the callback.
future.cb = cb
if future.finished:
future.cb()
proc `callback=`*[T](future: PFuture[T], proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure.}) = cb: proc (future: PFuture[T]) {.closure.}) =
## Sets the callback proc to be called when the future completes. ## Sets the callback proc to be called when the future completes.
## ##
## If future has already completed then ``cb`` will be called immediately. ## If future has already completed then ``cb`` will be called immediately.
future.cb = cb future.callback = proc () = cb(future)
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 = proc read*[T](future: PFuture[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise ## Retrieves the value of ``future``. Future must be finished otherwise
@ -98,16 +91,21 @@ proc failed*[T](future: PFuture[T]): bool =
## Determines whether ``future`` completed with an error. ## Determines whether ``future`` completed with an error.
future.error != nil future.error != nil
when defined(windows): # TODO: Get rid of register. Do it implicitly.
when defined(windows) or defined(nimdoc):
import winlean
type type
TCompletionKey = dword TCompletionKey = dword
TCompletionData* = object TCompletionData* = object
sock: TSocketHandle sock: TSocketHandle
cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.} cb: proc (sock: TSocketHandle, bytesTransferred: DWORD,
errcode: TOSErrorCode) {.closure.}
PDispatcher* = ref object PDispatcher* = ref object
ioPort: THandle ioPort: THandle
hasHandles: bool
TCustomOverlapped = object TCustomOverlapped = object
Internal*: DWORD Internal*: DWORD
@ -129,9 +127,13 @@ when defined(windows):
if CreateIOCompletionPort(sock.THandle, p.ioPort, if CreateIOCompletionPort(sock.THandle, p.ioPort,
cast[TCompletionKey](sock), 1) == 0: cast[TCompletionKey](sock), 1) == 0:
OSError(OSLastError()) OSError(OSLastError())
p.hasHandles = true
proc poll*(p: PDispatcher, timeout = 500) = proc poll*(p: PDispatcher, timeout = 500) =
## Waits for completion events and processes them. ## Waits for completion events and processes them.
if not p.hasHandles:
raise newException(EInvalidValue, "No handles registered in dispatcher.")
let llTimeout = let llTimeout =
if timeout == -1: winlean.INFINITE if timeout == -1: winlean.INFINITE
else: timeout.int32 else: timeout.int32
@ -145,16 +147,19 @@ when defined(windows):
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
var customOverlapped = cast[PCustomOverlapped](lpOverlapped) var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
if res: if res:
# This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1)) customOverlapped.data.cb(customOverlapped.data.sock,
lpNumberOfBytesTransferred, TOSErrorCode(-1))
dealloc(customOverlapped) dealloc(customOverlapped)
else: else:
let errCode = OSLastError() let errCode = OSLastError()
if lpOverlapped != nil: if lpOverlapped != nil:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
customOverlapped.data.cb(customOverlapped.data.sock,
lpNumberOfBytesTransferred, errCode)
dealloc(customOverlapped) dealloc(customOverlapped)
customOverlapped.data.cb(customOverlapped.data.sock, errCode)
else: else:
if errCode.int32 == WAIT_TIMEOUT: if errCode.int32 == WAIT_TIMEOUT:
# Timed out # Timed out
@ -252,11 +257,12 @@ when defined(windows):
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
retFuture.complete(0) if errcode == TOSErrorCode(-1):
else: retFuture.complete(0)
retFuture.fail(newException(EOS, osErrorMsg(errcode))) else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
) )
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
@ -265,7 +271,9 @@ when defined(windows):
# Request to connect completed immediately. # Request to connect completed immediately.
success = true success = true
retFuture.complete(0) retFuture.complete(0)
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
break break
else: else:
lastError = OSLastError() lastError = OSLastError()
@ -283,9 +291,13 @@ when defined(windows):
retFuture.fail(newException(EOS, osErrorMsg(lastError))) retFuture.fail(newException(EOS, osErrorMsg(lastError)))
return retFuture return retFuture
proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] = proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
flags: int = 0): PFuture[string] =
## Reads ``size`` bytes from ``socket``. Returned future will complete once ## Reads ``size`` bytes from ``socket``. Returned future will complete once
## all of the requested data is read. ## all of the requested data is read. If socket is disconnected during the
## recv operation then the future may complete with only a part of the
## requested data read. If socket is disconnected and no data is available
## to be read then the future will complete with a value of ``""``.
var retFuture = newFuture[string]() var retFuture = newFuture[string]()
@ -293,31 +305,50 @@ when defined(windows):
dataBuf.buf = newString(size) dataBuf.buf = newString(size)
dataBuf.len = size dataBuf.len = size
var bytesReceived, flags: DWord var bytesReceived: DWord
var flagsio = flags.dword
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
var data = newString(size) if errcode == TOSErrorCode(-1):
copyMem(addr data[0], addr dataBuf.buf[0], size) if bytesCount == 0 and dataBuf.buf[0] == '\0':
retFuture.complete($data) retFuture.complete("")
else: else:
retFuture.fail(newException(EOS, osErrorMsg(errcode))) 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, let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
addr flags, cast[POverlapped](ol), nil) addr flagsio, cast[POverlapped](ol), nil)
if ret == -1: if ret == -1:
let err = OSLastError() let err = OSLastError()
if err.int32 != ERROR_IO_PENDING: if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err))) retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol) dealloc(ol)
elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
# We have to ensure that the buffer is empty because WSARecv will tell
# us immediatelly when it was disconnected, even when there is still
# data in the buffer.
# We want to give the user as much data as we can. So we only return
# the empty string (which signals a disconnection) when there is
# nothing left to read.
retFuture.complete("")
# TODO: "For message-oriented sockets, where a zero byte message is often
# allowable, a failure with an error code of WSAEDISCON is used to
# indicate graceful closure."
# ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
else: else:
# Request to read completed immediately. # Request to read completed immediately.
var data = newString(size) var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size) copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data) retFuture.complete($data)
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
@ -332,11 +363,12 @@ when defined(windows):
var bytesReceived, flags: DWord var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
retFuture.complete(0) if errcode == TOSErrorCode(-1):
else: retFuture.complete(0)
retFuture.fail(newException(EOS, osErrorMsg(errcode))) else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
) )
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
@ -348,7 +380,9 @@ when defined(windows):
dealloc(ol) dealloc(ol)
else: else:
retFuture.complete(0) retFuture.complete(0)
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
@ -390,11 +424,12 @@ when defined(windows):
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb: ol.data = TCompletionData(sock: socket, cb:
proc (sock: TSocketHandle, errcode: TOSErrorCode) = proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
if errcode == TOSErrorCode(-1): if not retFuture.finished:
completeAccept() if errcode == TOSErrorCode(-1):
else: completeAccept()
retFuture.fail(newException(EOS, osErrorMsg(errcode))) else:
retFuture.fail(newException(EOS, osErrorMsg(errcode)))
) )
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
@ -411,70 +446,472 @@ when defined(windows):
dealloc(ol) dealloc(ol)
else: else:
completeAccept() completeAccept()
dealloc(ol) # We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture 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() initAll()
else: else:
# TODO: Selectors. import selectors
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK
type
TCallback = proc (sock: TSocketHandle): bool {.closure.}
PData* = ref object of PObject
sock: TSocketHandle
readCBs: seq[TCallback]
writeCBs: seq[TCallback]
PDispatcher* = ref object
selector: PSelector
proc newDispatcher*(): PDispatcher =
new result
result.selector = newSelector()
proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) =
assert sock in p.selector
echo("Update: ", events)
if events == {}:
discard p.selector.unregister(sock)
else:
discard p.selector.update(sock, events)
proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
if sock notin p.selector:
var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[])
p.selector.register(sock, {EvRead}, data.PObject)
else:
p.selector[sock].data.PData.readCBs.add(cb)
p.update(sock, p.selector[sock].events + {EvRead})
proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
if sock notin p.selector:
var data = PData(sock: sock, readCBs: @[], writeCBs: @[cb])
p.selector.register(sock, {EvWrite}, data.PObject)
else:
p.selector[sock].data.PData.writeCBs.add(cb)
p.update(sock, p.selector[sock].events + {EvWrite})
proc poll*(p: PDispatcher, timeout = 500) =
for info in p.selector.select(timeout):
let data = PData(info.key.data)
assert data.sock == info.key.fd
echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events)
if EvRead in info.events:
var newReadCBs: seq[TCallback] = @[]
for cb in data.readCBs:
if not cb(data.sock):
# Callback wants to be called again.
newReadCBs.add(cb)
data.readCBs = newReadCBs
if EvWrite in info.events:
var newWriteCBs: seq[TCallback] = @[]
for cb in data.writeCBs:
if not cb(data.sock):
# Callback wants to be called again.
newWriteCBs.add(cb)
data.writeCBs = newWriteCBs
var newEvents: set[TEvent]
if data.readCBs.len != 0: newEvents = {EvRead}
if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
p.update(data.sock, newEvents)
proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
af = AF_INET): PFuture[int] =
var retFuture = newFuture[int]()
proc cb(sock: TSocketHandle): bool =
# We have connected.
retFuture.complete(0)
return true
var aiList = getAddrInfo(address, port, af)
var success = false
var lastError: TOSErrorCode
var it = aiList
while it != nil:
var ret = connect(socket, it.ai_addr, it.ai_addrlen.TSocklen)
if ret == 0:
# Request to connect completed immediately.
success = true
retFuture.complete(0)
break
else:
lastError = osLastError()
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
success = true
addWrite(p, socket, cb)
break
else:
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,
flags: int = 0): PFuture[string] =
var retFuture = newFuture[string]()
var readBuffer = newString(size)
var sizeRead = 0
proc cb(sock: TSocketHandle): bool =
result = true
let netSize = size - sizeRead
let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint)
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
elif res == 0:
# Disconnected
if sizeRead == 0:
retFuture.complete("")
else:
readBuffer.setLen(sizeRead)
retFuture.complete(readBuffer)
else:
sizeRead.inc(res)
if res != netSize:
result = false # We want to read all the data requested.
else:
retFuture.complete(readBuffer)
addRead(p, socket, cb)
return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
var retFuture = newFuture[int]()
var written = 0
proc cb(sock: TSocketHandle): bool =
result = true
let netSize = data.len-written
var d = data.cstring
let res = send(sock, addr d[written], netSize, 0.cint)
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
else:
written.inc(res)
if res != netSize:
result = false # We still have data to send.
else:
retFuture.complete(0)
addWrite(p, socket, cb)
return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
PFuture[tuple[address: string, client: TSocketHandle]] =
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
proc cb(sock: TSocketHandle): bool =
result = true
var sockAddress: Tsockaddr_in
var addrLen = sizeof(sockAddress).TSocklen
var client = accept(sock, cast[ptr TSockAddr](addr(sockAddress)),
addr(addrLen))
if client == osInvalidSocket:
let lastError = osLastError()
assert lastError.int32 notin {EWOULDBLOCK, EAGAIN}
if lastError.int32 == EINTR:
return false
else:
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
else:
retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client))
addRead(p, socket, cb)
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
# -- Await Macro
template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} =
proc cbName {.closure.} =
if not varNameIterSym.finished:
var next = varNameIterSym()
if next == nil:
assert retFutureSym.finished, "Async procedure's return Future was not finished."
else:
next.callback = cbName
template createVar(futSymName: string, asyncProc: PNimrodNode,
valueReceiver: expr) {.immediate, dirty.} =
# TODO: Used template here due to bug #926
result = newNimNode(nnkStmtList)
var futSym = newIdentNode(futSymName) #genSym(nskVar, "future")
result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x>
valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} =
result = node
case node.kind
of nnkReturnStmt:
result = newNimNode(nnkStmtList)
result.add newCall(newIdentNode("complete"), retFutureSym,
if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0])
result.add newNimNode(nnkYieldStmt).add(newNilLit())
of nnkCommand:
if node[0].ident == !"await":
case node[1].kind
of nnkIdent:
# await x
result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x
of nnkCall:
# await foo(p, x)
var futureValue: PNimrodNode
createVar("future" & $node[1][0].toStrLit, node[1], futureValue)
result.add futureValue
else:
error("Invalid node kind in 'await', got: " & $node[1].kind)
elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and
node[1][0].ident == !"await":
# foo await x
var newCommand = node
createVar("future" & $node[0].ident, node[1][0], newCommand[1])
result.add newCommand
of nnkVarSection, nnkLetSection:
case node[0][2].kind
of nnkCommand:
if node[0][2][0].ident == !"await":
# var x = await y
var newVarSection = node # TODO: Should this use copyNimNode?
createVar("future" & $node[0][0].ident, node[0][2][1],
newVarSection[0][2])
result.add newVarSection
else: discard
of nnkAsgn:
case node[1].kind
of nnkCommand:
if node[1][0].ident == !"await":
# x = await y
var newAsgn = node
createVar("future" & $node[0].ident, node[1][1], newAsgn[1])
result.add newAsgn
else: discard
of nnkDiscardStmt:
# discard await x
if node[0][0].ident == !"await":
var dummy = newNimNode(nnkStmtList)
createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy)
else: discard
for i in 0 .. <result.len:
result[i] = processBody(result[i], retFutureSym)
#echo(treeRepr(result))
proc getName(node: PNimrodNode): string {.compileTime.} =
case node.kind
of nnkPostfix:
return $node[1].ident
of nnkIdent:
return $node.ident
else:
assert false
macro async*(prc: stmt): stmt {.immediate.} =
expectKind(prc, nnkProcDef)
hint("Processing " & prc[0].getName & " as an async proc.")
# Verify that the return type is a PFuture[T]
if prc[3][0].kind == nnkIdent:
error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'")
elif prc[3][0].kind == nnkBracketExpr:
if $prc[3][0][0] != "PFuture":
error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'")
# TODO: Why can't I use genSym? I get illegal capture errors for Syms.
# TODO: It seems genSym is broken. Change all usages back to genSym when fixed
var outerProcBody = newNimNode(nnkStmtList)
# -> var retFuture = newFuture[T]()
var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture")
outerProcBody.add(
newVarStmt(retFutureSym,
newCall(
newNimNode(nnkBracketExpr).add(
newIdentNode("newFuture"),
prc[3][0][1])))) # Get type from return type of this proc.
# -> iterator nameIter(): PFutureBase {.closure.} =
# -> var result: T
# -> <proc_body>
# -> complete(retFuture, result)
var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter")
var procBody = prc[6].processBody(retFutureSym)
procBody.insert(0, newNimNode(nnkVarSection).add(
newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T
procBody.add(
newCall(newIdentNode("complete"),
retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure"))
outerProcBody.add(closureIterator)
# -> var nameIterVar = nameIter
# -> var first = nameIterVar()
var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar")
var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
outerProcBody.add varNameIter
var varFirstSym = genSym(nskVar, "first")
var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
outerProcBody.add varFirst
# -> createCb(cb, nameIter, retFuture)
var cbName = newIdentNode("cb")
var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym)
outerProcBody.add procCb
# -> first.callback = cb
outerProcBody.add newAssignment(
newDotExpr(varFirstSym, newIdentNode("callback")),
cbName)
# -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym)
result = prc
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].ident == !"async":
result[4].del(i)
result[6] = outerProcBody
echo(toStrLit(result))
proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} =
## Reads a line of data from ``socket``. Returned future will complete once
## a full line is read or an error occurs.
##
## If a full line is read ``\r\L`` is not
## added to ``line``, however if solely ``\r\L`` is read then ``line``
## will be set to it.
##
## If the socket is disconnected, ``line`` will be set to ``""``.
template addNLIfEmpty(): stmt =
if result.len == 0:
result.add("\c\L")
result = ""
var c = ""
while true:
c = await p.recv(socket, 1)
if c.len == 0:
return
if c == "\r":
c = await p.recv(socket, 1, MSG_PEEK)
if c.len > 0 and c == "\L":
discard await p.recv(socket, 1)
addNLIfEmpty()
return
elif c == "\L":
addNLIfEmpty()
return
add(result.string, c)
when isMainModule: when isMainModule:
var p = newDispatcher() var p = newDispatcher()
var sock = socket() var sock = socket()
#sock.setBlocking false sock.setBlocking false
p.register(sock)
when true:
var f = p.connect(sock, "irc.freenode.org", TPort(6667)) when false:
f.callback = # Await tests
proc (future: PFuture[int]) = proc main(p: PDispatcher): PFuture[int] {.async.} =
echo("Connected in future!") discard await p.connect(sock, "irc.freenode.net", TPort(6667))
echo(future.read) while true:
for i in 0 .. 50: var line = await p.recvLine(sock)
var recvF = p.recv(sock, 10) echo("Line is: ", line.repr)
recvF.callback = if line == "":
proc (future: PFuture[string]) = echo "Disconnected"
echo("Read: ", future.read) break
proc peekTest(p: PDispatcher): PFuture[int] {.async.} =
discard await p.connect(sock, "localhost", TPort(6667))
while true:
var line = await p.recv(sock, 1, MSG_PEEK)
var line2 = await p.recv(sock, 1)
echo(line.repr)
echo(line2.repr)
echo("---")
if line2 == "": break
sleep(500)
var f = main(p)
else: else:
when false:
sock.bindAddr(TPort(6667)) var f = p.connect(sock, "irc.freenode.org", TPort(6667))
sock.listen() f.callback =
proc onAccept(future: PFuture[TSocketHandle]) =
echo "Accepted"
var t = p.send(future.read, "test\c\L")
t.callback =
proc (future: PFuture[int]) = proc (future: PFuture[int]) =
echo("Connected in future!")
echo(future.read) echo(future.read)
for i in 0 .. 50:
var recvF = p.recv(sock, 10)
recvF.callback =
proc (future: PFuture[string]) =
echo("Read ", future.read.len, ": ", future.read.repr)
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) var f = p.accept(sock)
f.callback = onAccept f.callback = onAccept
var f = p.accept(sock)
f.callback = onAccept
while true: while true:
p.poll() p.poll()
echo "polled"

View file

@ -14,7 +14,7 @@
type type
TLibHandle* = pointer ## a handle to a dynamically loaded library TLibHandle* = pointer ## a handle to a dynamically loaded library
proc loadLib*(path: string): TLibHandle proc loadLib*(path: string, global_symbols=false): TLibHandle
## loads a library from `path`. Returns nil if the library could not ## loads a library from `path`. Returns nil if the library could not
## be loaded. ## be loaded.
@ -53,6 +53,7 @@ when defined(posix):
# #
var var
RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int
proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".} proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".}
proc dlopen(path: CString, mode: int): TLibHandle {. proc dlopen(path: CString, mode: int): TLibHandle {.
@ -60,7 +61,10 @@ when defined(posix):
proc dlsym(lib: TLibHandle, name: cstring): pointer {. proc dlsym(lib: TLibHandle, name: cstring): pointer {.
importc, header: "<dlfcn.h>".} importc, header: "<dlfcn.h>".}
proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW) proc loadLib(path: string, global_symbols=false): TLibHandle =
var flags = RTLD_NOW
if global_symbols: flags = flags or RTLD_GLOBAL
return dlopen(path, flags)
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW) proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc unloadLib(lib: TLibHandle) = dlclose(lib) proc unloadLib(lib: TLibHandle) = dlclose(lib)
proc symAddr(lib: TLibHandle, name: cstring): pointer = proc symAddr(lib: TLibHandle, name: cstring): pointer =
@ -81,7 +85,7 @@ elif defined(windows) or defined(dos):
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {. proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.} importc: "GetProcAddress", header: "<windows.h>", stdcall.}
proc loadLib(path: string): TLibHandle = proc loadLib(path: string, global_symbols=false): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path)) result = cast[TLibHandle](winLoadLibrary(path))
proc loadLib(): TLibHandle = proc loadLib(): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(nil)) result = cast[TLibHandle](winLoadLibrary(nil))

View file

@ -38,3 +38,18 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
dealloc(aiList) dealloc(aiList)
osError(osLastError()) osError(osLastError())
dealloc(aiList) dealloc(aiList)
proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} =
## Sets blocking mode on socket
when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
osError(osLastError())
else: # BSD sockets
var x: int = fcntl(s, F_GETFL, 0)
if x == -1:
osError(osLastError())
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s, F_SETFL, mode) == -1:
osError(osLastError())

View file

@ -836,9 +836,11 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
while i < s.len: while i < s.len:
c.ml = 0 c.ml = 0
var L = rawMatch(s, pattern, i, c) var L = rawMatch(s, pattern, i, c)
if L < 0: break if L < 0:
yield substr(s, i, i+L-1) inc(i, 1)
inc(i, L) else:
yield substr(s, i, i+L-1)
inc(i, L)
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
nosideEffect, rtl, extern: "npegs$1".} = nosideEffect, rtl, extern: "npegs$1".} =

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2013 Dominik Picheta # (c) Copyright 2014 Dominik Picheta
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -9,212 +9,211 @@
# TODO: Docs. # TODO: Docs.
import tables, os, unsigned import tables, os, unsigned, hashes
when defined(windows):
import winlean when defined(linux): import posix, epoll
else: elif defined(windows): import winlean
import posix
proc hash*(x: TSocketHandle): THash {.borrow.}
type type
TEvent* = enum TEvent* = enum
EvRead, EvWrite EvRead, EvWrite
TSelectorKey* = object PSelectorKey* = ref object
fd: cint fd*: TSocketHandle
events: set[TEvent] events*: set[TEvent] ## The events which ``fd`` listens for.
data: PObject data*: PObject ## User object.
TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]] TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
PSelector* = ref object of PObject ## Selector interface. when defined(linux) or defined(nimdoc):
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 type
PEpollSelector* = ref object of PSelector PSelector* = ref object
epollFD: cint epollFD: cint
events: array[64, ptr epoll_event] events: array[64, ptr epoll_event]
fds: TTable[TSocketHandle, PSelectorKey]
TDataWrapper = object proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
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: if EvRead in events:
event.events = EPOLLIN result.events = EPOLLIN
if EvWrite in events: if EvWrite in events:
event.events = event.events or EPOLLOUT result.events = result.events or EPOLLOUT
result.data.fd = fd.cint
var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
dw.fd = fd data: PObject): PSelectorKey {.discardable.} =
dw.boundEvents = events ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
dw.data = data ## ``events``.
event.data.thePtr = dw if s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor already exists.")
if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: var event = createEventStruct(events, fd)
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError()) OSError(OSLastError())
result = TSelectorKey(fd: fd, events: events, data: data) var key = PSelectorKey(fd: fd, events: events, data: data)
proc esUnregister(s: PSelector, fd: cint): TSelectorKey = s.fds[fd] = key
# We cannot find out the information about this ``fd`` from the epoll result = key
# context. As such I will simply return an almost empty TSelectorKey.
var es = PEpollSelector(s) proc update*(s: PSelector, fd: TSocketHandle,
if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: events: set[TEvent]): PSelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for.
if not s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor not found.")
var event = createEventStruct(events, fd)
s.fds[fd].events = events
echo("About to update")
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
OSError(OSLastError()) OSError(OSLastError())
# We could fill in the ``fds`` TTable to get the info, but that wouldn't echo("finished updating")
# be nice for our memory. result = s.fds[fd]
result = TSelectorKey(fd: fd, events: {}, data: nil)
proc esClose(s: PSelector) = proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
var es = PEpollSelector(s) if not s.fds.hasKey(fd):
if es.epollFD.close() != 0: OSError(OSLastError()) raise newException(EInvalidValue, "File descriptor not found.")
dealloc(addr es.events) # TODO: Test this if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
OSError(OSLastError())
result = s.fds[fd]
s.fds.del(fd)
proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] = proc close*(s: PSelector) =
if s.epollFD.close() != 0: OSError(OSLastError())
dealloc(addr s.events) # TODO: Test this
proc select*(s: PSelector, timeout: int): 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``.
result = @[] result = @[]
var es = PEpollSelector(s)
let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint) let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint)
if evNum < 0: OSError(OSLastError()) if evNum < 0: OSError(OSLastError())
if evNum == 0: return @[] if evNum == 0: return @[]
for i in 0 .. <evNum: for i in 0 .. <evNum:
var evSet: set[TEvent] = {} var evSet: set[TEvent] = {}
if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead} if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite} if (s.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, let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
data: dw.data)
result.add((selectorKey, evSet)) result.add((selectorKey, evSet))
proc newEpollSelector*(): PEpollSelector = proc newSelector*(): PSelector =
new result new result
result.epollFD = epoll_create(64) result.epollFD = epoll_create(64)
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64)) result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
result.fds = initTable[TSocketHandle, PSelectorKey]()
if result.epollFD < 0: if result.epollFD < 0:
OSError(OSLastError()) OSError(OSLastError())
result.registerImpl = esRegister
result.unregisterImpl = esUnregister proc contains*(s: PSelector, fd: TSocketHandle): bool =
result.closeImpl = esClose ## Determines whether selector contains a file descriptor.
result.selectImpl = esSelect return s.fds.hasKey(fd)
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
## Retrieves the selector key for ``fd``.
return s.fds[fd]
elif defined(windows):
type
PSelector* = ref object
fds: TTable[TSocketHandle, PSelectorKey]
proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
data: PObject): PSelectorKey {.discardable.} =
if s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor already exists.")
var sk = PSelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = sk
result = sk
proc update*(s: PSelector, fd: TSocketHandle,
events: set[TEvent]): PSelectorKey {.discardable.} =
## Updates the events which ``fd`` wants notifications for.
if not s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor not found.")
s.fds[fd].events = events
result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
result = s.fds[fd]
s.fds.del(fd)
proc close*(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[TSocketHandle, PSelectorKey],
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[TSocketHandle, PSelectorKey]):
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[TSocketHandle, PSelectorKey], 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(TSocketHandle(m+1), addr(rd), addr(wr), nil, addr(tv)))
else:
retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, nil))
if retCode < 0:
OSError(OSLastError())
elif retCode == 0:
return @[]
else:
return getReadyFDs(rd, wr, fds)
proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
result = select(s.fds, timeout)
proc newSelector*(): PSelector =
new result
result.fds = initTable[TSocketHandle, PSelectorKey]()
proc contains*(s: PSelector, fd: TSocketHandle): bool =
return s.fds.hasKey(fd)
proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
return s.fds[fd]
elif defined(bsd) or defined(macosx):
# TODO: kqueue
{.error: "Sorry your platform is not supported yet.".}
else:
{.error: "Sorry your platform is not supported.".}
when isMainModule: when isMainModule:
# Select() # Select()
@ -224,11 +223,12 @@ when isMainModule:
sock: TSocket sock: TSocket
var sock = socket() var sock = socket()
sock.setBlocking(false)
sock.connect("irc.freenode.net", TPort(6667)) sock.connect("irc.freenode.net", TPort(6667))
var selector = newEpollSelector() var selector = newSelector()
var data = PSockWrapper(sock: sock) var data = PSockWrapper(sock: sock)
let key = selector.register(sock.getFD.cint, {EvRead}, data) let key = selector.register(sock.getFD, {EvWrite}, data)
var i = 0 var i = 0
while true: while true:
let ready = selector.select(1000) let ready = selector.select(1000)
@ -236,6 +236,7 @@ when isMainModule:
if ready.len > 0: echo ready[0].events if ready.len > 0: echo ready[0].events
i.inc i.inc
if i == 6: if i == 6:
assert selector.unregister(sock.getFD).fd == sock.getFD
selector.close() selector.close()
break break

View file

@ -17,11 +17,13 @@ when hostos == "solaris":
when defined(Windows): when defined(Windows):
import winlean import winlean
export ioctlsocket
else: else:
import posix import posix
export fcntl, F_GETFL, O_NONBLOCK, F_SETFL
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
inet_ntoa inet_ntoa, recv, `==`, connect, send, accept
type type
@ -63,10 +65,10 @@ type
when defined(windows): when defined(windows):
let let
OSInvalidSocket* = winlean.INVALID_SOCKET osInvalidSocket* = winlean.INVALID_SOCKET
else: else:
let let
OSInvalidSocket* = posix.INVALID_SOCKET osInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.} proc `==`*(a, b: TPort): bool {.borrow.}
## ``==`` for ports. ## ``==`` for ports.
@ -89,7 +91,7 @@ when defined(posix):
of AF_UNIX: result = posix.AF_UNIX of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6 of AF_INET6: result = posix.AF_INET6
else: nil else: discard
proc toInt(typ: TType): cint = proc toInt(typ: TType): cint =
case typ case typ
@ -97,7 +99,7 @@ when defined(posix):
of SOCK_DGRAM: result = posix.SOCK_DGRAM of SOCK_DGRAM: result = posix.SOCK_DGRAM
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
of SOCK_RAW: result = posix.SOCK_RAW of SOCK_RAW: result = posix.SOCK_RAW
else: nil else: discard
proc toInt(p: TProtocol): cint = proc toInt(p: TProtocol): cint =
case p case p
@ -107,7 +109,7 @@ when defined(posix):
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
of IPPROTO_RAW: result = posix.IPPROTO_RAW of IPPROTO_RAW: result = posix.IPPROTO_RAW
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
else: nil else: discard
else: else:
proc toInt(domain: TDomain): cint = proc toInt(domain: TDomain): cint =

View file

@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string =
"November", "December"] "November", "December"]
return lookup[m] return lookup[m]
proc format_token(info: TTimeInfo, token: string, buf: var string) =
## Helper of the format proc to parse individual tokens.
##
## Pass the found token in the user input string, and the buffer where the
## final string is being built. This has to be a var value because certain
## formatting tokens require modifying the previous characters.
case token
of "d":
buf.add($info.monthday)
of "dd":
if info.monthday < 10:
buf.add("0")
buf.add($info.monthday)
of "ddd":
buf.add(($info.weekday)[0 .. 2])
of "dddd":
buf.add($info.weekday)
of "h":
buf.add($(if info.hour > 12: info.hour - 12 else: info.hour))
of "hh":
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
if amerHour < 10:
buf.add('0')
buf.add($amerHour)
of "H":
buf.add($info.hour)
of "HH":
if info.hour < 10:
buf.add('0')
buf.add($info.hour)
of "m":
buf.add($info.minute)
of "mm":
if info.minute < 10:
buf.add('0')
buf.add($info.minute)
of "M":
buf.add($(int(info.month)+1))
of "MM":
if info.month < mOct:
buf.add('0')
buf.add($(int(info.month)+1))
of "MMM":
buf.add(($info.month)[0..2])
of "MMMM":
buf.add($info.month)
of "s":
buf.add($info.second)
of "ss":
if info.second < 10:
buf.add('0')
buf.add($info.second)
of "t":
if info.hour >= 12:
buf.add('P')
else: buf.add('A')
of "tt":
if info.hour >= 12:
buf.add("PM")
else: buf.add("AM")
of "y":
var fr = ($info.year).len()-1
if fr < 0: fr = 0
buf.add(($info.year)[fr .. ($info.year).len()-1])
of "yy":
var fr = ($info.year).len()-2
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyy":
var fr = ($info.year).len()-3
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyyy":
var fr = ($info.year).len()-4
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
buf.add(fyear)
of "yyyyy":
var fr = ($info.year).len()-5
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
buf.add(fyear)
of "z":
let hrs = (info.timezone div 60) div 60
buf.add($hrs)
of "zz":
let hrs = (info.timezone div 60) div 60
buf.add($hrs)
if hrs.abs < 10:
var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0))
buf.insert("0", atIndex)
of "zzz":
let hrs = (info.timezone div 60) div 60
buf.add($hrs & ":00")
if hrs.abs < 10:
var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
buf.insert("0", atIndex)
of "ZZZ":
buf.add(info.tzname)
of "":
discard
else:
raise newException(EInvalidValue, "Invalid format string: " & token)
proc format*(info: TTimeInfo, f: string): string = proc format*(info: TTimeInfo, f: string): string =
## This function formats `info` as specified by `f`. The following format ## This function formats `info` as specified by `f`. The following format
## specifiers are available: ## specifiers are available:
@ -591,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string =
## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST`` ## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST``
## ========== ================================================================================= ================================================ ## ========== ================================================================================= ================================================
## ##
## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``. ## Other strings can be inserted by putting them in ``''``. For example
## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,`` ## ``hh'->'mm`` will give ``01->56``. The following characters can be
## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
## ``,``. However you don't need to necessarily separate format specifiers, a
## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
result = "" result = ""
var i = 0 var i = 0
@ -600,111 +716,7 @@ proc format*(info: TTimeInfo, f: string): string =
while true: while true:
case f[i] case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
case currentF format_token(info, currentF, result)
of "d":
result.add($info.monthday)
of "dd":
if info.monthday < 10:
result.add("0")
result.add($info.monthday)
of "ddd":
result.add(($info.weekday)[0 .. 2])
of "dddd":
result.add($info.weekday)
of "h":
result.add($(if info.hour > 12: info.hour - 12 else: info.hour))
of "hh":
let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
if amerHour < 10:
result.add('0')
result.add($amerHour)
of "H":
result.add($info.hour)
of "HH":
if info.hour < 10:
result.add('0')
result.add($info.hour)
of "m":
result.add($info.minute)
of "mm":
if info.minute < 10:
result.add('0')
result.add($info.minute)
of "M":
result.add($(int(info.month)+1))
of "MM":
if info.month < mOct:
result.add('0')
result.add($(int(info.month)+1))
of "MMM":
result.add(($info.month)[0..2])
of "MMMM":
result.add($info.month)
of "s":
result.add($info.second)
of "ss":
if info.second < 10:
result.add('0')
result.add($info.second)
of "t":
if info.hour >= 12:
result.add('P')
else: result.add('A')
of "tt":
if info.hour >= 12:
result.add("PM")
else: result.add("AM")
of "y":
var fr = ($info.year).len()-1
if fr < 0: fr = 0
result.add(($info.year)[fr .. ($info.year).len()-1])
of "yy":
var fr = ($info.year).len()-2
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
result.add(fyear)
of "yyy":
var fr = ($info.year).len()-3
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
result.add(fyear)
of "yyyy":
var fr = ($info.year).len()-4
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
result.add(fyear)
of "yyyyy":
var fr = ($info.year).len()-5
if fr < 0: fr = 0
var fyear = ($info.year)[fr .. ($info.year).len()-1]
if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
result.add(fyear)
of "z":
let hrs = (info.timezone div 60) div 60
result.add($hrs)
of "zz":
let hrs = (info.timezone div 60) div 60
result.add($hrs)
if hrs.abs < 10:
var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0))
result.insert("0", atIndex)
of "zzz":
let hrs = (info.timezone div 60) div 60
result.add($hrs & ":00")
if hrs.abs < 10:
var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
result.insert("0", atIndex)
of "ZZZ":
result.add(info.tzname)
of "":
discard
else:
raise newException(EInvalidValue, "Invalid format string: " & currentF)
currentF = "" currentF = ""
if f[i] == '\0': break if f[i] == '\0': break
@ -716,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string =
inc(i) inc(i)
else: result.add(f[i]) else: result.add(f[i])
else: currentF.add(f[i]) else:
# Check if the letter being added matches previous accumulated buffer.
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
currentF.add(f[i])
else:
format_token(info, currentF, result)
dec(i) # Move position back to re-process the character separately.
currentF = ""
inc(i) inc(i)
{.pop.} {.pop.}
@ -727,12 +747,16 @@ when isMainModule:
var t = getGMTime(fromSeconds(2147483647)) var t = getGMTime(fromSeconds(2147483647))
echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
echo t.format("ddd ddMMMhhmmssZZZyyyy")
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038" assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC" "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
assert t.format("yyyyMMddhhmmss") == "20380119031407"
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==

View file

@ -418,76 +418,75 @@ proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} =
return Math.floor(`a` % `b`); return Math.floor(`a` % `b`);
""" """
proc NegInt(a: int): int {.compilerproc.} = proc negInt(a: int): int {.compilerproc.} =
result = a*(-1) result = a*(-1)
proc NegInt64(a: int64): int64 {.compilerproc.} = proc negInt64(a: int64): int64 {.compilerproc.} =
result = a*(-1) result = a*(-1)
proc AbsInt(a: int): int {.compilerproc.} = proc absInt(a: int): int {.compilerproc.} =
result = if a < 0: a*(-1) else: a result = if a < 0: a*(-1) else: a
proc AbsInt64(a: int64): int64 {.compilerproc.} = proc absInt64(a: int64): int64 {.compilerproc.} =
result = if a < 0: a*(-1) else: a result = if a < 0: a*(-1) else: a
proc LeU(a, b: int): bool {.compilerproc.} = proc leU(a, b: int): bool {.compilerproc.} =
result = abs(a) <= abs(b) result = abs(a) <= abs(b)
proc LtU(a, b: int): bool {.compilerproc.} = proc ltU(a, b: int): bool {.compilerproc.} =
result = abs(a) < abs(b) result = abs(a) < abs(b)
proc LeU64(a, b: int64): bool {.compilerproc.} = proc leU64(a, b: int64): bool {.compilerproc.} =
result = abs(a) <= abs(b) result = abs(a) <= abs(b)
proc ltU64(a, b: int64): bool {.compilerproc.} =
proc LtU64(a, b: int64): bool {.compilerproc.} =
result = abs(a) < abs(b) result = abs(a) < abs(b)
proc AddU(a, b: int): int {.compilerproc.} = proc addU(a, b: int): int {.compilerproc.} =
result = abs(a) + abs(b) result = abs(a) + abs(b)
proc AddU64(a, b: int64): int64 {.compilerproc.} = proc addU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) + abs(b) result = abs(a) + abs(b)
proc SubU(a, b: int): int {.compilerproc.} = proc subU(a, b: int): int {.compilerproc.} =
result = abs(a) - abs(b) result = abs(a) - abs(b)
proc SubU64(a, b: int64): int64 {.compilerproc.} = proc subU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) - abs(b) result = abs(a) - abs(b)
proc MulU(a, b: int): int {.compilerproc.} = proc mulU(a, b: int): int {.compilerproc.} =
result = abs(a) * abs(b) result = abs(a) * abs(b)
proc MulU64(a, b: int64): int64 {.compilerproc.} = proc mulU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) * abs(b) result = abs(a) * abs(b)
proc DivU(a, b: int): int {.compilerproc.} = proc divU(a, b: int): int {.compilerproc.} =
result = abs(a) div abs(b) result = abs(a) div abs(b)
proc DivU64(a, b: int64): int64 {.compilerproc.} = proc divU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) div abs(b) result = abs(a) div abs(b)
proc ModU(a, b: int): int {.compilerproc.} = proc modU(a, b: int): int {.compilerproc.} =
result = abs(a) mod abs(b) result = abs(a) mod abs(b)
proc ModU64(a, b: int64): int64 {.compilerproc.} = proc modU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) mod abs(b) result = abs(a) mod abs(b)
proc Ze(a: int): int {.compilerproc.} = proc ze*(a: int): int {.compilerproc.} =
result = a
proc Ze64(a: int64): int64 {.compilerproc.} =
result = a result = a
proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} = proc ze64*(a: int64): int64 {.compilerproc.} =
result = a
proc toU8*(a: int): int8 {.noStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} = proc toU16*(a: int): int16 {.noStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} = proc toU32*(a: int64): int32 {.noStackFrame, compilerproc.} =
asm """ asm """
return `a`; return `a`;
""" """
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool =
tyArray, tyArrayConstr, tyTuple, tyArray, tyArrayConstr, tyTuple,
tyOpenArray, tySet, tyVar, tyRef, tyPtr} tyOpenArray, tySet, tyVar, tyRef, tyPtr}
proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
case n.kind case n.kind
of nkNone: sysAssert(false, "NimCopyAux") of nkNone: sysAssert(false, "NimCopyAux")
of nkSlot: of nkSlot:
@ -518,7 +517,7 @@ proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
} }
""" """
proc NimCopy(x: pointer, ti: PNimType): pointer = proc nimCopy(x: pointer, ti: PNimType): pointer =
case ti.kind case ti.kind
of tyPtr, tyRef, tyVar, tyNil: of tyPtr, tyRef, tyVar, tyNil:
if not isFatPointer(ti): if not isFatPointer(ti):
@ -585,7 +584,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} =
else: else:
result = nil result = nil
proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. proc arrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
noStackFrame, compilerproc.} = noStackFrame, compilerproc.} =
# types are fake # types are fake
asm """ asm """

View file

@ -199,14 +199,14 @@ else:
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {. proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
when useWinUnicode: when useWinUnicode:
@ -304,7 +304,7 @@ else:
dwFileAttributes: int32): WINBOOL {. dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
bFailIfExists: cint): cint {. bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".} importc: "CopyFileA", stdcall, dynlib: "kernel32".}

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 21 line: 21
errormsg: "invalid type: 'TTable'" errormsg: "invalid type: 'TTable[string, proc (string)]'"
""" """
import tables import tables

View file

@ -7,4 +7,4 @@ type
proc ha() = proc ha() =
var var
x: TExport # no error x: TExport # no error
nil discard

View file

@ -4,4 +4,4 @@ type
TExport* = enum x, y, z TExport* = enum x, y, z
proc foo*(x: int) = proc foo*(x: int) =
nil discard

View file

@ -1,4 +1,4 @@
type type
TExport* = enum x, y, z # exactly the same type! TExport* = enum x, y, z # exactly the same type!
proc foo*(x: int) = nil proc foo*(x: int) = discard

View file

@ -0,0 +1,64 @@
discard """
file: "tasyncawait.nim"
cmd: "nimrod cc --hints:on $# $#"
output: "5000"
"""
import asyncio2, sockets2, net, strutils
var disp = newDispatcher()
var msgCount = 0
const
swarmSize = 50
messagesToSend = 100
var clientCount = 0
proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
for i in 0 .. <messagesToSend:
discard await disp.send(client, "Message " & $i & "\c\L")
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
for i in 0 .. <swarmSize:
var sock = socket()
#disp.register(sock)
discard await disp.connect(sock, "localhost", port)
when true:
discard await sendMessages(disp, sock)
sock.close()
else:
# Issue #932: https://github.com/Araq/Nimrod/issues/932
var msgFut = sendMessages(disp, sock)
msgFut.callback =
proc () =
sock.close()
proc readMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
while true:
var line = await disp.recvLine(client)
if line == "":
client.close()
clientCount.inc
break
else:
if line.startswith("Message "):
msgCount.inc
else:
doAssert false
proc createServer(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
var server = socket()
#disp.register(server)
server.bindAddr(port)
server.listen()
while true:
discard readMessages(disp, await disp.accept(server))
discard disp.createServer(TPort(10335))
discard disp.launchSwarm(TPort(10335))
while true:
disp.poll()
if clientCount == swarmSize: break
assert msgCount == swarmSize * messagesToSend
echo msgCount

View file

@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c")
case x case x
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!") of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister") of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
of "cc", "hash", "when": nil of "cc", "hash", "when": discard
of "will", "it", "finally", "be", "generated": nil of "will", "it", "finally", "be", "generated": discard
var z = case i var z = case i
of 1..5, 8, 9: "aa" of 1..5, 8, 9: "aa"

View file

@ -12,7 +12,7 @@ var
thr: array [0..5, TThread[tuple[a, b: int]]] thr: array [0..5, TThread[tuple[a, b: int]]]
L, M, N: TLock L, M, N: TLock
proc doNothing() = nil proc doNothing() = discard
proc threadFunc(interval: tuple[a, b: int]) {.thread.} = proc threadFunc(interval: tuple[a, b: int]) {.thread.} =
doNothing() doNothing()

View file

@ -80,13 +80,13 @@ proc mygeneric1() =
echo "mygeneric1 constructed" echo "mygeneric1 constructed"
proc mygeneric2[T](val: T) = proc mygeneric2[T](val: T) =
var var a = open()
a = open()
b = TMyGeneric2[int, T](x: 10, y: val)
c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
var b = TMyGeneric2[int, T](x: 10, y: val)
echo "mygeneric2 constructed" echo "mygeneric2 constructed"
var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
proc mygeneric3 = proc mygeneric3 =
var x = TMyGeneric3[int, string, TMyGeneric1[int]]( var x = TMyGeneric3[int, string, TMyGeneric1[int]](
x: 10, y: "test", z: TMyGeneric1[int](x: 10)) x: 10, y: "test", z: TMyGeneric1[int](x: 10))

View file

@ -6,7 +6,7 @@ type
PDict[TK, TV] = ref TDict[TK, TV] PDict[TK, TV] = ref TDict[TK, TV]
proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) = proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
nil discard
proc destroyDict[TK, TV](a: PDict[TK, TV]) = proc destroyDict[TK, TV](a: PDict[TK, TV]) =
return return

View file

@ -32,7 +32,7 @@ const
proc len[T,D] (n:PNode[T,D]): Int {.inline.} = proc len[T,D] (n:PNode[T,D]): Int {.inline.} =
return n.Count return n.Count
proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = nil proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = discard
proc clean[T: string|seq](o: var T) {.inline.} = proc clean[T: string|seq](o: var T) {.inline.} =
o = nil o = nil
@ -98,7 +98,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: Int): PNode[T,D] {.inline.} =
of cLen3 : setLen(n.slots, cLen3) of cLen3 : setLen(n.slots, cLen3)
of cLenCenter : setLen(n.slots, cLenCenter) of cLenCenter : setLen(n.slots, cLenCenter)
of cLen4 : setLen(n.slots, cLen4) of cLen4 : setLen(n.slots, cLen4)
else: nil else: discard
Result = n Result = n
else : else :
@ -232,7 +232,7 @@ proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], AKey: T, AValue: D) =
of cLen3: setLen(APath.Nd.slots, cLenCenter) of cLen3: setLen(APath.Nd.slots, cLenCenter)
of cLenCenter: setLen(APath.Nd.slots, cLen4) of cLenCenter: setLen(APath.Nd.slots, cLen4)
of cLen4: setLen(APath.Nd.slots, cLenMax) of cLen4: setLen(APath.Nd.slots, cLenMax)
else: nil else: discard
for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1]) for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1])
APath.Nd.slots[x] = setItem(AKey, AValue, ANode) APath.Nd.slots[x] = setItem(AKey, AValue, ANode)

View file

@ -0,0 +1,18 @@
discard """
output: "10\n10\n1\n2\n3"
"""
proc test(x: proc (a, b: int): int) =
echo x(5, 5)
test(proc (a, b): auto = a + b)
test do (a, b) -> auto: a + b
proc foreach[T](s: seq[T], body: proc(x: T)) =
for e in s:
body(e)
foreach(@[1,2,3]) do (x):
echo x

View file

@ -0,0 +1,18 @@
discard """
cmd: "nimrod check $# $#"
msg: "'proc' is not a concrete type"
msg: "'Foo' is not a concrete type."
msg: "invalid type: 'TBaseMed'"
"""
type
Foo[T] = object
x: T
TBaseMed = object
doSmth: proc
data: seq[Foo]
var a: TBaseMed
# issue 188

View file

@ -0,0 +1,15 @@
type
TObj*[T] = object
val*: T
var
totalGlobals* = 0
proc makeObj[T](x: T): TObj[T] =
totalGlobals += 1
result.val = x
proc globalInstance*[T]: var TObj[T] =
var g {.global.} = when T is int: makeObj(10) else: makeObj("hello")
result = g

View file

@ -0,0 +1,4 @@
import globalaux
echo "in globalaux2: ", globalInstance[int]().val

View file

@ -1,28 +1,28 @@
template foo(a: int, b: string) = nil template foo(a: int, b: string) = discard
foo(1, "test") foo(1, "test")
proc bar(a: int, b: string) = nil proc bar(a: int, b: string) = discard
bar(1, "test") bar(1, "test")
template foo(a: int, b: string) = bar(a, b) template foo(a: int, b: string) = bar(a, b)
foo(1, "test") foo(1, "test")
block: block:
proc bar(a: int, b: string) = nil proc bar(a: int, b: string) = discard
template foo(a: int, b: string) = nil template foo(a: int, b: string) = discard
foo(1, "test") foo(1, "test")
bar(1, "test") bar(1, "test")
proc baz = proc baz =
proc foo(a: int, b: string) = nil proc foo(a: int, b: string) = discard
proc foo(b: string) = proc foo(b: string) =
template bar(a: int, b: string) = nil template bar(a: int, b: string) = discard
bar(1, "test") bar(1, "test")
foo("test") foo("test")
block: block:
proc foo(b: string) = nil proc foo(b: string) = discard
foo("test") foo("test")
foo(1, "test") foo(1, "test")

View file

@ -0,0 +1,19 @@
discard """
output: 10
"""
#bug #926
import macros
proc test(f: var PNimrodNode) {.compileTime.} =
f = newNimNode(nnkStmtList)
f.add newCall(newIdentNode("echo"), newLit(10))
macro blah(prc: stmt): stmt =
result = prc
test(result)
proc test() {.blah.} =
echo 5

View file

@ -178,14 +178,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter =
## #parsed_param3 = new_parsed_parameter(PK_INT, "231") ## #parsed_param3 = new_parsed_parameter(PK_INT, "231")
var result {.gensym.}: Tparsed_parameter var result {.gensym.}: Tparsed_parameter
result.kind = tkind result.kind = tkind
when tkind == PK_EMPTY: nil when tkind == PK_EMPTY: discard
elif tkind == PK_INT: result.int_val = expr elif tkind == PK_INT: result.int_val = expr
elif tkind == PK_BIGGEST_INT: result.big_int_val = expr elif tkind == PK_BIGGEST_INT: result.big_int_val = expr
elif tkind == PK_FLOAT: result.float_val = expr elif tkind == PK_FLOAT: result.float_val = expr
elif tkind == PK_BIGGEST_FLOAT: result.big_float_val = expr elif tkind == PK_BIGGEST_FLOAT: result.big_float_val = expr
elif tkind == PK_STRING: result.str_val = expr elif tkind == PK_STRING: result.str_val = expr
elif tkind == PK_BOOL: result.bool_val = expr elif tkind == PK_BOOL: result.bool_val = expr
elif tkind == PK_HELP: nil elif tkind == PK_HELP: discard
else: {.error: "unknown kind".} else: {.error: "unknown kind".}
result result

View file

@ -1,10 +1,10 @@
discard """ discard """
msg: ''' msg: '''int int
int float float
float int int
TFoo TFoo TFoo
TFoo int float
''' TFoo TFoo'''
""" """
import typetraits import typetraits
@ -24,8 +24,7 @@ template reject(e: expr) =
proc genericParamRepeated[T: typedesc](a: T, b: T) = proc genericParamRepeated[T: typedesc](a: T, b: T) =
static: static:
echo a.name echo a.name, " ", b.name
echo b.name
accept genericParamRepeated(int, int) accept genericParamRepeated(int, int)
accept genericParamRepeated(float, float) accept genericParamRepeated(float, float)
@ -35,8 +34,7 @@ reject genericParamRepeated(int, float)
proc genericParamOnce[T: typedesc](a, b: T) = proc genericParamOnce[T: typedesc](a, b: T) =
static: static:
echo a.name echo a.name, " ", b.name
echo b.name
accept genericParamOnce(int, int) accept genericParamOnce(int, int)
accept genericParamOnce(TFoo, TFoo) accept genericParamOnce(TFoo, TFoo)
@ -68,8 +66,7 @@ reject typePairs2(string, int, TBAR, TBAR)
proc dontBind(a: typedesc, b: typedesc) = proc dontBind(a: typedesc, b: typedesc) =
static: static:
echo a.name echo a.name, " ", b.name
echo b.name
accept dontBind(int, float) accept dontBind(int, float)
accept dontBind(TFoo, TFoo) accept dontBind(TFoo, TFoo)

View file

@ -31,7 +31,8 @@ proc intval(x: int) = discard
# check real and virtual fields # check real and virtual fields
type type
TFoo = generic T TFoo = generic T
intval T.x T.x
y(T)
intval T.y intval T.y
proc y(x: TObj): int = 10 proc y(x: TObj): int = 10

View file

@ -14,8 +14,8 @@ type
TSomethingElse = object TSomethingElse = object
PSomethingElse = ref TSomethingElse PSomethingElse = ref TSomethingElse
method foo(a: PNode, b: PSomethingElse) = nil method foo(a: PNode, b: PSomethingElse) = discard
method foo(a: PNodeFoo, b: PSomethingElse) = nil method foo(a: PNodeFoo, b: PSomethingElse) = discard
var o: TObject var o: TObject
o.somethin() o.somethin()

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "tests/reject/trecincb.nim" file: "tests/reject/trecincb.nim"
line: 9 line: 9
errormsg: "recursive dependency: 'tests/reject/trecincb.nim'" errormsg: "recursive dependency: 'tests/module/trecincb.nim'"
""" """
# Test recursive includes # Test recursive includes

View file

@ -1,7 +1,7 @@
discard """ discard """
file: "trecincb.nim" file: "trecincb.nim"
line: 9 line: 9
errormsg: "recursive dependency: 'tests/reject/trecincb.nim'" errormsg: "recursive dependency: 'tests/module/trecincb.nim'"
""" """
# Test recursive includes # Test recursive includes

View file

@ -5,7 +5,7 @@ discard """
# Test the overloading resolution in connection with a qualifier # Test the overloading resolution in connection with a qualifier
proc write(t: TFile, s: string) = proc write(t: TFile, s: string) =
nil # a nop discard # a nop
system.write(stdout, "hello") system.write(stdout, "hello")
#OUT hello #OUT hello

View file

@ -2,7 +2,7 @@ discard """
output: "12false3ha" output: "12false3ha"
""" """
proc f(x: varargs[string, `$`]) = nil proc f(x: varargs[string, `$`]) = discard
template optF{f(X)}(x: varargs[expr]) = template optF{f(X)}(x: varargs[expr]) =
writeln(stdout, x) writeln(stdout, x)

View file

@ -8,7 +8,7 @@ type
TRange = range[0..40] TRange = range[0..40]
proc p(r: TRange) = proc p(r: TRange) =
nil discard
var var
r: TRange r: TRange

View file

@ -8,7 +8,7 @@ type
TRange = range[0..40] TRange = range[0..40]
proc p(r: TRange) = proc p(r: TRange) =
nil discard
var var
r: TRange r: TRange

View file

@ -51,12 +51,12 @@ else: write(stdout, "BUG: F ist nicht in s!\n")
a = {} #{'a'..'z'} a = {} #{'a'..'z'}
for x in low(TAZ) .. high(TAZ): for x in low(TAZ) .. high(TAZ):
incl(a, x) incl(a, x)
if x in a: nil if x in a: discard
else: write(stdout, "BUG: something not in a!\n") else: write(stdout, "BUG: something not in a!\n")
for x in low(TTokTypeRange) .. high(TTokTypeRange): for x in low(TTokTypeRange) .. high(TTokTypeRange):
if x in tokTypes: if x in tokTypes:
nil discard
#writeln(stdout, "the token '$1' is in the set" % repr(x)) #writeln(stdout, "the token '$1' is in the set" % repr(x))
#OUT Ha ein F ist in s! #OUT Ha ein F ist in s!

View file

@ -200,7 +200,7 @@ proc setSeen(d: TDb, s: TSeen) =
var hashToSet = @[("type", $s.kind.int), ("channel", s.channel), var hashToSet = @[("type", $s.kind.int), ("channel", s.channel),
("timestamp", $s.timestamp.int)] ("timestamp", $s.timestamp.int)]
case s.kind case s.kind
of PSeenJoin: nil of PSeenJoin: discard
of PSeenPart, PSeenMsg, PSeenQuit: of PSeenPart, PSeenMsg, PSeenQuit:
hashToSet.add(("msg", s.msg)) hashToSet.add(("msg", s.msg))
of PSeenNick: of PSeenNick:
@ -338,7 +338,7 @@ proc hubConnect(state: PState) =
proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
case event.typ case event.typ
of EvConnected: nil of EvConnected: discard
of EvDisconnected: of EvDisconnected:
while not state.ircClient.isConnected: while not state.ircClient.isConnected:
try: try:
@ -424,7 +424,7 @@ proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
seenNick.newNick = event.params[0] seenNick.newNick = event.params[0]
state.database.setSeen(seenNick) state.database.setSeen(seenNick)
else: else:
nil # TODO: ? discard # TODO: ?
proc open(port: TPort = TPort(5123)): PState = proc open(port: TPort = TPort(5123)): PState =
var res: PState var res: PState

View file

@ -1,7 +1,7 @@
# tests for the interpreter # tests for the interpreter
proc loops(a: var int) = proc loops(a: var int) =
nil discard
#var #var
# b: int # b: int
#b = glob #b = glob

View file

@ -7,6 +7,6 @@ proc walkDirTree(root: string) =
case k case k
of pcFile, pcLinkToFile: echo(f) of pcFile, pcLinkToFile: echo(f)
of pcDir: walkDirTree(f) of pcDir: walkDirTree(f)
of pcLinkToDir: nil of pcLinkToDir: discard
walkDirTree(".") walkDirTree(".")

View file

@ -72,7 +72,7 @@ type
rule: TNode ## the rule that the symbol refers to rule: TNode ## the rule that the symbol refers to
TNode {.final, shallow.} = object TNode {.final, shallow.} = object
case kind: TPegKind case kind: TPegKind
of pkEmpty..pkWhitespace: nil of pkEmpty..pkWhitespace: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
of pkChar, pkGreedyRepChar: ch: char of pkChar, pkGreedyRepChar: ch: char
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
@ -123,7 +123,7 @@ proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
proc copyPeg(a: TPeg): TPeg = proc copyPeg(a: TPeg): TPeg =
result.kind = a.kind result.kind = a.kind
case a.kind case a.kind
of pkEmpty..pkWhitespace: nil of pkEmpty..pkWhitespace: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
result.term = a.term result.term = a.term
of pkChar, pkGreedyRepChar: of pkChar, pkGreedyRepChar:
@ -229,7 +229,7 @@ when false:
case a.kind case a.kind
of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal, of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
pkCharChoice, pkGreedyRepSet: nil pkCharChoice, pkGreedyRepSet: discard
of pkNonTerminal: return true of pkNonTerminal: return true
else: else:
for i in 0..a.sons.len-1: for i in 0..a.sons.len-1:
@ -318,7 +318,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
proc spaceCost(n: TPeg): int = proc spaceCost(n: TPeg): int =
case n.kind case n.kind
of pkEmpty: nil of pkEmpty: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
pkAny..pkWhitespace, pkGreedyAny: pkAny..pkWhitespace, pkGreedyAny:
@ -1111,7 +1111,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
of 'A'..'F': of 'A'..'F':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos) inc(c.bufpos)
else: nil else: discard
proc getEscapedChar(c: var TPegLexer, tok: var TToken) = proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
inc(c.bufpos) inc(c.bufpos)
@ -1341,7 +1341,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
of "i": tok.modifier = modIgnoreCase of "i": tok.modifier = modIgnoreCase
of "y": tok.modifier = modIgnoreStyle of "y": tok.modifier = modIgnoreStyle
of "v": tok.modifier = modVerbatim of "v": tok.modifier = modVerbatim
else: nil else: discard
setLen(tok.literal, 0) setLen(tok.literal, 0)
if c.buf[c.bufpos] == '$': if c.buf[c.bufpos] == '$':
getDollar(c, tok) getDollar(c, tok)
@ -1488,7 +1488,7 @@ proc primary(p: var TPegParser): TPeg =
of tkCurlyAt: of tkCurlyAt:
getTok(p) getTok(p)
return !*\primary(p).token(p) return !*\primary(p).token(p)
else: nil else: discard
case p.tok.kind case p.tok.kind
of tkIdentifier: of tkIdentifier:
if p.identIsVerbatim: if p.identIsVerbatim:

View file

@ -1,7 +1,7 @@
# Test if the new table constructor syntax works: # Test if the new table constructor syntax works:
template ignoreExpr(e: expr): stmt {.immediate.} = template ignoreExpr(e: expr): stmt {.immediate.} =
nil discard
# test first class '..' syntactical citizen: # test first class '..' syntactical citizen:
ignoreExpr x <> 2..4 ignoreExpr x <> 2..4

1
tests/threads/nimrod.cfg Normal file
View file

@ -0,0 +1 @@
threads:on

View file

@ -1,5 +1,9 @@
discard """ discard """
output: "he, no return type;abc a string" output: '''12
empty
he, no return type;
abc a string
ha'''
""" """
proc ReturnT[T](x: T): T = proc ReturnT[T](x: T): T =

View file

@ -9,7 +9,7 @@ proc init: TYourObj =
result.y = -1 result.y = -1
proc f(x: var TYourObj) = proc f(x: var TYourObj) =
nil discard
var m: TMyObj = init() var m: TMyObj = init()
f(m) f(m)

View file

@ -1,8 +1,10 @@
version 0.9.4 version 0.9.4
============= =============
- fix macros\tstringinterp.nim:
- problem: needs another level of indirection for 'seq'
- problem: deref is not correct
- fix GC issues - fix GC issues
- fix macros\tstringinterp.nim
- test and fix showoff - test and fix showoff

View file

@ -249,7 +249,7 @@ proc walker(dir: string) =
of pcDir: of pcDir:
if optRecursive in options: if optRecursive in options:
walker(path) walker(path)
else: nil else: discard
if existsFile(dir): processFile(dir) if existsFile(dir): processFile(dir)
proc writeHelp() = proc writeHelp() =

View file

@ -74,5 +74,15 @@
<div id="legal">Copyright &copy; 2013 - Andreas Rumpf &amp; Contributors - All rights reserved - <a href="http://reign-studios.com/philipwitte/">Design by Philip Witte</a></div> <div id="legal">Copyright &copy; 2013 - Andreas Rumpf &amp; Contributors - All rights reserved - <a href="http://reign-studios.com/philipwitte/">Design by Philip Witte</a></div>
</div> </div>
</div> </div>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-48159761-1', 'nimrod-lang.org');
ga('send', 'pageview');
</script>
</body> </body>
</html> </html>

View file

@ -65,7 +65,7 @@ html, body {
#page { position:relative; float:left; padding:20px 30px 50px 50px; width:620px; color:#343739; } #page { position:relative; float:left; padding:20px 30px 50px 50px; width:620px; color:#343739; }
#page h1 { margin-top:40px; } #page h1 { margin-top:40px; line-height: 28px; }
#page h2 { margin-top:40px; } #page h2 { margin-top:40px; }
#page p { text-align:justify; } #page p { text-align:justify; }

View file

@ -1,15 +1,55 @@
Discuss Nimrod in our `forum <http://forum.nimrod-code.org/>`_. Forum
=====
Visit our project page at GitHub: http://github.com/Araq/Nimrod. The `Nimrod forum <http://forum.nimrod-code.org/>`_ is the place where most
discussions related to the language happen. It not only includes discussions
relating to the design of Nimrod but also allows for beginners to ask questions
relating to Nimrod.
Wiki: http://github.com/Araq/Nimrod/wiki. IRC
====
Bug reports: http://github.com/Araq/Nimrod/issues. Many Nimrod developers are a part of the
`#nimrod IRC channel <http://webchat.freenode.net/?channels=nimrod>`_ on
Freenode. That is the place where the rest of the discussion relating to Nimrod
occurs. Be sure to join us there if you wish to discuss Nimrod in real-time.
IRC is the perfect place for people just starting to learn Nimrod and we
welcome any questions that you may have!
For quickest feedback, join our IRC channel: irc://irc.freenode.net/nimrod You may also be interested in reading the
(logs at `<http://build.nimrod-code.org/irclogs/>`_). `IRC logs <http://build.nimrod-code.org/irclogs/>`_ which are an archive of all
of the previous discussions that took place in the IRC channel.
Check out our Twitter account for latest news and announcements: `@nimrodlang <http://twitter.com/nimrodlang>`_. Github
======
Nimrod's `source code <http://github.com/Araq/Nimrod>`_ is hosted on Github.
Together with the `wiki <http://github.com/Araq/Nimrod/wiki>`_ and
`issue tracker <http://github.com/Araq/Nimrod/issues>`_.
Github also hosts other projects relating to Nimrod. These projects are a part
of the `nimrod-code organisation <http://github.com/nimrod-code>`_.
This includes the `Babel package manager <http://github.com/nimrod-code/babel>`_
and its `package repository <http://github.com/nimrod-code/packages>`_.
Twitter
=======
Follow us `@nimrodlang <http://twitter.com/nimrodlang>`_ for latest news about
Nimrod.
Reddit
======
Subscribe to `/r/nimrod <http://reddit.com/r/nimrod>`_ for latest news about
Nimrod.
StackOverflow
=============
When asking a question relating to Nimrod, be sure to use the
`Nimrod <http://stackoverflow.com/questions/tagged/nimrod>`_ tag in your
question.
How to help How to help
=========== ===========
@ -26,7 +66,9 @@ can't find anything you fancy doing, you can always ask for inspiration on IRC
Donations Donations
--------- ---------
If you love what we do and are feeling generous then you can always donate: If you love what we do and are feeling generous then you can always donate.
Contributions of any quantity are greatly appreciated and will contribute to
making Nimrod even better!
Gittip Gittip
`````` ``````

View file

@ -3,86 +3,103 @@ News
==== ====
2014-XX-XX Version 0.9.4 released ..
================================= 2014-XX-XX Version 0.9.4 released
=================================
Bugfixes Bugfixes
-------- --------
Library Additions Library Additions
----------------- -----------------
- Added ``macros.genSym`` builtin for AST generation. - Added ``macros.genSym`` builtin for AST generation.
- Added ``macros.newLit`` procs for easier AST generation. - Added ``macros.newLit`` procs for easier AST generation.
Changes affecting backwards compatibility Changes affecting backwards compatibility
----------------------------------------- -----------------------------------------
- The scoping rules for the ``if`` statement changed for better interaction - The scoping rules for the ``if`` statement changed for better interaction
with the new syntactic construct ``(;)``. with the new syntactic construct ``(;)``.
- ``OSError`` family of procedures has been deprecated. Procedures with the same - ``OSError`` family of procedures has been deprecated. Procedures with the same
name but which take different parameters have been introduced. These procs now name but which take different parameters have been introduced. These procs now
require an error code to be passed to them. This error code can be retrieved require an error code to be passed to them. This error code can be retrieved
using the new ``OSLastError`` proc. using the new ``OSLastError`` proc.
- ``os.parentDir`` now returns "" if there is no parent dir. - ``os.parentDir`` now returns "" if there is no parent dir.
- In CGI scripts stacktraces are shown to the user only - In CGI scripts stacktraces are shown to the user only
if ``cgi.setStackTraceStdout`` is used. if ``cgi.setStackTraceStdout`` is used.
- The symbol binding rules for clean templates changed: ``bind`` for any - The symbol binding rules for clean templates changed: ``bind`` for any
symbol that's not a parameter is now the default. ``mixin`` can be used symbol that's not a parameter is now the default. ``mixin`` can be used
to require instantiation scope for a symbol. to require instantiation scope for a symbol.
- ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely - ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely
passed to shell, instead of just adding double quotes. passed to shell, instead of just adding double quotes.
- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, - ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``,
``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated.
- The ``nil`` statement has been deprecated, use an empty ``discard`` instead. - The ``nil`` statement has been deprecated, use an empty ``discard`` instead.
- ``sockets.select`` now prunes sockets that are **not** ready from the list - ``sockets.select`` now prunes sockets that are **not** ready from the list
of sockets given to it. of sockets given to it.
Compiler Additions Compiler Additions
------------------ ------------------
- The compiler can now warn about "uninitialized" variables. (There are no - The compiler can now warn about "uninitialized" variables. (There are no
real uninitialized variables in Nimrod as they are initialized to binary real uninitialized variables in Nimrod as they are initialized to binary
zero). Activate via ``{.warning[Uninit]:on.}``. zero). Activate via ``{.warning[Uninit]:on.}``.
- The compiler now enforces the ``not nil`` constraint. - The compiler now enforces the ``not nil`` constraint.
- The compiler now supports a ``codegenDecl`` pragma for even more control - The compiler now supports a ``codegenDecl`` pragma for even more control
over the generated code. over the generated code.
- The compiler now supports a ``computedGoto`` pragma to support very fast - The compiler now supports a ``computedGoto`` pragma to support very fast
dispatching for interpreters and the like. dispatching for interpreters and the like.
- The old evaluation engine has been replaced by a proper register based - The old evaluation engine has been replaced by a proper register based
virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro
evaluation. evaluation.
- ``--gc:none`` produces warnings when code uses the GC. - ``--gc:none`` produces warnings when code uses the GC.
Language Additions Language Additions
------------------ ------------------
- Arrays can now be declared with a single integer literal ``N`` instead of a - Arrays can now be declared with a single integer literal ``N`` instead of a
range; the range is then ``0..N-1``. range; the range is then ``0..N-1``.
- Added ``requiresInit`` pragma to enforce explicit initialization. - Added ``requiresInit`` pragma to enforce explicit initialization.
- Exported templates are allowed to access hidden fields. - Exported templates are allowed to access hidden fields.
- The ``using statement`` enables you to more easily author domain-specific - The ``using statement`` enables you to more easily author domain-specific
languages and libraries providing OOP-like syntactic sugar. languages and libraries providing OOP-like syntactic sugar.
- Added the possibility to override various dot operators in order to handle - Added the possibility to override various dot operators in order to handle
calls to missing procs and reads from undeclared fields at compile-time. calls to missing procs and reads from undeclared fields at compile-time.
- The overload resolution now supports ``static[T]`` params that must be - The overload resolution now supports ``static[T]`` params that must be
evaluable at compile-time. evaluable at compile-time.
- Support for user-defined type classes has been added. - Support for user-defined type classes has been added.
- The *command syntax* is supported in a lot more contexts. - The *command syntax* is supported in a lot more contexts.
- Anonymous iterators are now supported and iterators can capture variables - Anonymous iterators are now supported and iterators can capture variables
of an outer proc. of an outer proc.
Tools improvements Tools improvements
------------------ ------------------
- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option - c2nim can deal with a subset of C++. Use the ``--cpp`` command line option
to activate. to activate.
2014-02-11 Nimrod Featured in Dr. Dobb's Journal
================================================
Nimrod has been `featured<http://www.drdobbs.com/open-source/nimrod-a-new-systems-programming-languag/240165321>`_
as the cover story in the February 2014 issue of Dr. Dobb's Journal.
2014-01-15 Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online
============================================================================
Andreas Rumpf presented *Nimrod: A New Approach to Metaprogramming* at
`Strange Loop 2013<https://thestrangeloop.com/sessions/nimrod-a-new-approach-to-meta-programming>`_.
The `video and slides<http://www.infoq.com/presentations/nimrod>`_
of the talk are now available.
2013-05-20 New website design! 2013-05-20 New website design!

View file

@ -1,4 +1,14 @@
<a class="news" href="news.html#new-website-design"> <a class="news" href="news.html#Z2014-02-11-nimrod-featured-in-dr-dobb-s-journal">
<h3>Feb 11, 2014</h3>
<p>Nimrod featured in Dr. Dobb's Journal</p>
</a>
<a class="news" href="news.html#Z2014-01-15-andreas-rumpf-s-talk-on-nimrod-at-strange-loop-2013-is-now-online">
<h3>Jan 15, 2014</h3>
<p>Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online.</p>
</a>
<a class="news" href="news.html#Z2013-05-20-new-website-design">
<h3>May 20, 2013</h3> <h3>May 20, 2013</h3>
<p>New website design!</p> <p>New website design!</p>
</a> </a>