diff options
71 files changed, 1547 insertions, 706 deletions
diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index ffab05788..e4abe11a0 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode = if p.tok.s == "unsigned": isUnsigned = true elif p.tok.s == "signed" or p.tok.s == "int": - nil + discard else: add(x, p.tok.s) getTok(p, nil) @@ -746,7 +746,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = result = declarator(p, a, ident) eat(p, pxParRi, result) else: - nil + discard return parseTypeSuffix(p, a) 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) = case base - of base10: nil + of base10: discard of base2: incl(n.flags, nfBase2) of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) @@ -1686,7 +1686,7 @@ proc switchStatement(p: var TParser): PNode = break of "case", "default": break - else: nil + else: discard addSon(result, statement(p)) if sonsLen(result) == 0: # translate empty statement list to Nimrod's ``nil`` statement diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim index 1707b75db..84b4c4dfb 100644 --- a/compiler/c2nim/cpp.nim +++ b/compiler/c2nim/cpp.nim @@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) = m.body.add(tok) of pxDirConc: # just ignore this token: this implements token merging correctly - nil + discard else: m.body.add(p.tok) # we do not want macro expansion here: @@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode = of pxDirectiveParLe, pxDirective: case p.tok.s of "else", "endif", "elif": break - else: nil + else: discard addSon(result, statement(p)) proc eatEndif(p: var TParser) = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 61336aa87..0140d1ac4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -88,7 +88,8 @@ type errTemplateInstantiationTooNested, errInstantiationFrom, errInvalidIndexValueForTuple, errCommandExpectsFilename, errMainModuleMustBeSpecified, - errXExpected, + errXExpected, + errTIsNotAConcreteType, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, @@ -103,6 +104,7 @@ type errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errXCannotBeClosure, errXMustBeCompileTime, + errCannotInferTypeOfTheLiteral, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -312,6 +314,7 @@ const errCommandExpectsFilename: "command expects a filename argument", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errXExpected: "\'$1\' expected", + errTIsNotAConcreteType: "\'$1\' is not a concrete type.", errInvalidSectionStart: "invalid section start", errGridTableNotImplemented: "grid table is not implemented", errGeneralParseError: "general parse error", @@ -346,6 +349,7 @@ const errIllegalCaptureX: "illegal capture '$1'", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXMustBeCompileTime: "'$1' can only be used in compile-time context", + errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim index 67473e71f..f24b0c420 100644 --- a/compiler/pas2nim/paslex.nim +++ b/compiler/pas2nim/paslex.nim @@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) = h = h +% ord(c) h = h +% h shl 10 h = h xor (h shr 6) - of '_': nil + of '_': discard else: break inc(pos) h = h +% h shl 3 diff --git a/compiler/pas2nim/pasparse.nim b/compiler/pas2nim/pasparse.nim index 928896338..a6f8363f6 100644 --- a/compiler/pas2nim/pasparse.nim +++ b/compiler/pas2nim/pasparse.nim @@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind, proc setBaseFlags(n: PNode, base: TNumericalBase) = case base - of base10: nil + of base10: discard of base2: incl(n.flags, nfBase2) of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) @@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = eat(p, pxCurlyDirRi) opNode.ident = getIdent("&") else: - nil + discard of pxMinus: if p.tok.xkind == pxPer: getTok(p) @@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = of pxNeq: opNode.ident = getIdent("!=") else: - nil + discard skipCom(p, opNode) # read sub-expression with higher priority nextop = lowestExprAux(p, v2, opPred) addSon(node, opNode) @@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode = (n.sons[2].kind in {nkCharLit, nkStrLit}): n.sons[0].ident = getIdent("&") # fix operator else: - nil + discard if not (n.kind in {nkEmpty..nkNilLit}): 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: if not isHandledDirective(p): break else: - nil + discard addSon(result, parseStmt(p)) if sonsLen(result) == 1: result = result.sons[0] @@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode = addSon(b, c) addSon(a, b) if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: - nil + discard else: addSon(s, a) addSon(result, s) @@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode = getTok(p) v = newNodeP(nkVarTy, p) else: - nil + discard while true: case p.tok.xkind of pxSymbol: a = createIdentNodeP(p.tok.ident, p) @@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode = proc exSymbol(n: var PNode) = case n.kind of nkPostfix: - nil + discard of nkPragmaExpr: exSymbol(n.sons[0]) of nkIdent, nkAccQuoted: @@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) = for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) of nkIdentDefs: 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) 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)) else: internalError(result.info, "anonymous record is not supported") else: - nil + discard opt(p, pxSemicolon) skipCom(p, result) @@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) = proc exSymbols(n: PNode) = case n.kind - of nkEmpty..nkNilLit: nil + of nkEmpty..nkNilLit: discard of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) of nkWhenStmt, nkStmtList: 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]) if n.sons[i].sons[2].kind == nkObjectTy: fixRecordDef(n.sons[i].sons[2]) - else: nil + else: discard proc parseBegin(p: var TParser, result: PNode) = getTok(p) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d9ed50cfe..75c16f6cd 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -97,8 +97,25 @@ proc makeExternImport(s: PSym, extname: string) = incl(s.flags, sfImportc) 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) + case gCmd + of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: + validateExternCName(s, info) + else: discard incl(s.flags, sfExportc) proc processImportCompilerProc(s: PSym, extname: string) = @@ -515,7 +532,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, if k in validPragmas: case k of wExportc: - makeExternExport(sym, getOptionalStr(c, it, "$1")) + makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) incl(sym.flags, sfUsed) # avoid wrong hints of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) of wImportCompilerProc: @@ -601,7 +618,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, processDynLib(c, it, sym) of wCompilerproc: noVal(it) # compilerproc may not get a string! - makeExternExport(sym, "$1") + makeExternExport(sym, "$1", it.info) incl(sym.flags, sfCompilerProc) incl(sym.flags, sfUsed) # suppress all those stupid warnings registerCompilerProc(sym) diff --git a/compiler/sem.nim b/compiler/sem.nim index 140687721..09b2511f1 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -333,6 +333,7 @@ proc myOpen(module: PSym): PPassContext = c.semOperand = semOperand c.semConstBoolExpr = semConstBoolExpr c.semOverloadedCall = semOverloadedCall + c.semInferredLambda = semInferredLambda c.semGenerateInstance = generateInstance c.semTypeNode = semTypeNode pushProcCon(c, module) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 234526054..df58c896f 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -81,6 +81,7 @@ type semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, filter: TSymKinds): PNode {.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, info: TLineInfo): PSym includedFiles*: TIntSet # used to detect recursive include files diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f09ee1295..c271911ab 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1829,8 +1829,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = symChoice(c, n, s, scClosed) if result.kind == nkSym: markIndirect(c, result.sym) - if isGenericRoutine(result.sym): - localError(n.info, errInstantiateXExplicitely, s.name.s) + # if isGenericRoutine(result.sym): + # localError(n.info, errInstantiateXExplicitely, s.name.s) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 0871b7fb7..6c4e29f29 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -143,10 +143,11 @@ proc discardCheck(c: PContext, result: PNode) = while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil - elif c.inTypeClass > 0 and result.typ.kind == tyBool: - let verdict = semConstExpr(c, result) - if verdict.intVal == 0: - localError(result.info, "type class predicate failed") + elif c.inTypeClass > 0: + if result.typ.kind == tyBool: + let verdict = semConstExpr(c, result) + if verdict.intVal == 0: + localError(result.info, "type class predicate failed") elif result.typ.kind != tyError and gCmd != cmdInteractive: if result.typ.kind == tyNil: fixNilType(result) @@ -349,7 +350,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # BUGFIX: ``fitNode`` is needed here! # check type compability between def.typ and typ: 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: def = ast.emptyNode if symkind == skLet: localError(a.info, errLetNeedsInit) @@ -764,6 +770,29 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = s.ast = a 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) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] @@ -780,6 +809,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assignType(s.typ, t) s.typ.id = t.id # same id checkConstructedType(s.info, s.typ) + if s.typ.kind in {tyObject, tyTuple}: + checkForMetaFields(s.typ.n) let aa = a.sons[2] if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and aa.sons[0].kind == nkObjectTy: @@ -883,12 +914,19 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = s = n[namePos].sym pushOwner(s) openScope(c) - if n.sons[genericParamsPos].kind != nkEmpty: - illFormedAst(n) # process parameters: + var gp: PNode + if n.sons[genericParamsPos].kind != nkEmpty: + n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) + gp = n.sons[genericParamsPos] + else: + gp = newNodeI(nkGenericParams, n.info) + if n.sons[paramsPos].kind != nkEmpty: - var gp = newNodeI(nkGenericParams, n.info) 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: s.typ = newTypeS(tyProc, c) 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) #if efDetermineType notin flags: # XXX not good enough; see tnamedparamanonproc.nim - pushProcCon(c, s) - addResult(c, s.typ.sons[0], n.info, skProc) - let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, s) - addResultNode(c, n) - popProcCon(c) + if n.sons[genericParamsPos].kind == nkEmpty: + pushProcCon(c, s) + addResult(c, s.typ.sons[0], n.info, skProc) + let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) + n.sons[bodyPos] = transformBody(c.module, semBody, s) + addResultNode(c, n) + popProcCon(c) sideEffectsCheck(c, s) else: localError(n.info, errImplOfXexpected, s.name.s) @@ -913,6 +952,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = popOwner() 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) = # XXX: This proc is part of my plan for getting rid of # 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]) if outer != nil: n.sons[i] = outer - for j in countup(i+1, length-1): - inner.addSon(semStmt(c, n.sons[j])) + var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1]) + inner.addSon(semStmtList(c, rest, flags)) n.sons.setLen(i+1) return of LastBlockStmts: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 184aca4f8..0eba602cc 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -669,7 +669,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyTypeDesc: if tfUnresolved notin paramType.flags: # 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 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 = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) - result.lastSon.shouldHaveMeta result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, 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 mSeq: result = semContainer(c, n, tySequence, "seq", 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) if result != nil: result = copyType(result, getCurrOwner(), false) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 73b618f46..22edc6e32 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) = localError(info, errVarVarTypeNotAllowed) elif computeSize(t) == szIllegalRecursion: localError(info, errIllegalRecursionInTypeX, typeToString(t)) + when false: if t.kind == tyObject and t.sons[0] != nil: 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: var body = t.sons[0] if body.kind != tyGenericBody: internalError(cl.info, "no generic body") - var header: PType = nil + var header: PType = t # search for some instantiation here: if cl.allowMetaTypes: result = PType(idTableGet(cl.localCache, t)) @@ -231,11 +232,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = if x.kind == tyGenericParam: x = lookupTypeVar(cl, x) if x != nil: - if header == nil: header = instCopyType(cl, t) + if header == t: header = instCopyType(cl, t) header.sons[i] = x propagateToOwner(header, x) + else: + propagateToOwner(header, x) - if header != nil: + if header != t: # search again after first pass: result = searchInstTypes(header) if result != nil: return @@ -243,6 +246,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = header = instCopyType(cl, t) result = newType(tyGenericInst, t.sons[0].owner) + result.flags = header.flags # be careful not to propagate unnecessary flags here (don't use rawAddSon) result.sons = @[header.sons[0]] # 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 +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, t: PType): PType = - var cl: TReplTypeVars - initIdTable(cl.symMap) - copyIdTable(cl.typeMap, pt) - initIdTable(cl.localCache) - cl.info = info - cl.c = p + var cl = initTypeVars(p, pt, info) pushInfoContext(info) result = replaceTypeVarsT(cl, t) popInfoContext() diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index fce0bdf48..240145118 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -343,53 +343,57 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} = result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) -proc procParamTypeRel(c: var TCandidate, f, a: PType, - result: var TTypeRelation) = - var - m: TTypeRelation - f = f - +proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = + var f = f + if a.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 - # sigunature. there is a change that the target + # signature. There is a change that the target # type is already fully-determined, so we are # going to try resolve it f = generateTypeInstance(c.c, c.bindings, c.call.info, f) if f == nil or f.isMetaType: # no luck resolving the type, so the inference fails - result = isNone - return + return isNone let reverseRel = typeRel(c, a, f) if reverseRel == isGeneric: - m = isInferred + result = isInferred + inc c.genericMatches 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 - return - else: - result = minRel(m, result) - + + if result == isEqual: + inc c.exactMatches + proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: 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 # 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 a.sons[0] != nil: - procParamTypeRel(c, f.sons[0], a.sons[0], result) + checkParam(f.sons[0], a.sons[0]) else: return isNone elif a.sons[0] != nil: return isNone + if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: return isNone 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 of tyTypeDesc: - if a.kind != tyTypeDesc: return isNone - var prev = PType(idTableGet(c.bindings, f)) 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: result = isGeneric else: result = typeRel(c, f.base, a.base) + if result != isNone: put(c.bindings, f, a) else: - let toMatch = if tfUnresolved in f.flags: a - else: a.base - result = typeRel(c, prev.base, toMatch) + if tfUnresolved in f.flags: + result = typeRel(c, prev.base, a) + elif a.kind == tyTypeDesc: + result = typeRel(c, prev.base, a.base) + else: + result = isNone of tyStmt: result = isGeneric @@ -1035,10 +1046,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, #result = copyTree(arg) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isInferred, isInferredConvertible: - var prc = if arg.kind in nkLambdaKinds: arg[0].sym - else: arg.sym - let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) - result = newSymNode(inferred, arg.info) + if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: + result = c.semInferredLambda(c, m.bindings, arg) + else: + let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) + result = newSymNode(inferred, arg.info) if r == isInferredConvertible: result = implicitConv(nkHiddenStdConv, f, result, m, c) 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 # this correctly is inefficient. We have to copy `m` here to be able to # roll back the side effects of the unification algorithm. + let c = m.c var x, y, z: TCandidate initCandidate(c, x, m.callee) @@ -1122,10 +1135,10 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, x.state = csMatch of csMatch: var cmp = cmpCandidates(x, z) - if cmp < 0: + if cmp < 0: best = i x = z - elif cmp == 0: + elif cmp == 0: y = z # z is as good as x if x.state == csEmpty: result = nil diff --git a/compiler/types.nim b/compiler/types.nim index 55a89920a..d7148f110 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1042,7 +1042,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, of tyEmpty: result = taField in flags of tyTypeClasses: - result = true + result = tfGenericTypeParam in t.flags or + taField notin flags of tyGenericBody, tyGenericParam, tyGenericInvokation, tyNone, tyForward, tyFromExpr, tyFieldAccessor: result = false diff --git a/compiler/vm.nim b/compiler/vm.nim index 81e712047..0929e072b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1025,7 +1025,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = regs[ra].sons[0].flags.incl nfIsRef of opcNCopyNimNode: decodeB(nkMetaNode) - setMeta(regs[ra], copyNode(regs[rb])) + setMeta(regs[ra], copyNode(regs[rb].skipMeta)) of opcNCopyNimTree: decodeB(nkMetaNode) setMeta(regs[ra], copyTree(regs[rb])) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a6d044e24..206cca0d7 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -321,9 +321,18 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.gen(n.sons[2], dest) 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 = result = c.constants.len - c.constants.add n + c.constants.add n.nilLiteral internalAssert result < 0x7fff proc sameConstant*(a, b: PNode): bool = @@ -907,17 +916,20 @@ proc requiresCopy(n: PNode): bool = proc unneededIndirection(n: PNode): bool = 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; flags: TGenFlags) = # a nop for certain types 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) else: let tmp = c.genx(n.sons[0], flags) @@ -1109,7 +1121,12 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkFloatLit, info, t) of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, 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: if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) diff --git a/examples/htmlrefs.nim b/examples/htmlrefs.nim index 824c1d8c7..8b668325f 100644 --- a/examples/htmlrefs.nim +++ b/examples/htmlrefs.nim @@ -36,7 +36,7 @@ block mainLoop: case x.kind of xmlEof: break mainLoop of xmlElementClose: break - else: nil + else: discard x.next() # skip ``xmlElementClose`` # now we have the description for the ``a`` element var desc = "" diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index d50394f60..366521551 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -7,6 +7,8 @@ # distribution, for details about the copyright. # +from posix import TSocketHandle + const EPOLLIN* = 0x00000001 EPOLLPRI* = 0x00000002 @@ -33,8 +35,8 @@ const type epoll_data* {.importc: "union epoll_data", header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union. - thePtr* {.importc: "ptr".}: pointer # \ - #fd*: cint + #thePtr* {.importc: "ptr".}: pointer + fd*: cint # \ #u32*: uint32 #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 ## 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>".} ## Manipulate an epoll instance "epfd". Returns 0 in case of success, ## -1 in case of error ( the "errno" variable will contain the diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 41260b36f..bb4039c1b 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -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, 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>".} when hasSpawnH: diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index cdb4a6f49..12d4cb5a3 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -7,9 +7,7 @@ # distribution, for details about the copyright. # -import os, oids, tables, strutils - -import winlean +import os, oids, tables, strutils, macros import sockets2, net @@ -23,14 +21,13 @@ import sockets2, net # -- Futures type - PFutureVoid* = ref object of PObject - cbVoid: proc () {.closure.} + PFutureBase* = ref object of PObject + cb: proc () {.closure.} finished: bool - PFuture*[T] = ref object of PFutureVoid + PFuture*[T] = ref object of PFutureBase value: T error: ref EBase - cb: proc (future: PFuture[T]) {.closure.} proc newFuture*[T](): PFuture[T] = ## Creates a new future. @@ -39,42 +36,38 @@ proc newFuture*[T](): PFuture[T] = proc complete*[T](future: PFuture[T], val: T) = ## Completes ``future`` with value ``val``. - assert(not future.finished) + assert(not future.finished, "Future already finished, cannot finish twice.") assert(future.error == nil) future.value = val future.finished = true if future.cb != nil: - future.cb(future) - if future.cbVoid != nil: - future.cbVoid() + future.cb() proc fail*[T](future: PFuture[T], error: ref EBase) = ## Completes ``future`` with ``error``. - assert(not future.finished) + assert(not future.finished, "Future already finished, cannot finish twice.") future.finished = true future.error = error if future.cb != nil: - future.cb(future) + future.cb() -proc `callback=`*[T](future: PFuture[T], - cb: proc (future: PFuture[T]) {.closure.}) = +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(future) + future.cb() -proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) = - ## Sets the **void** callback proc to be called when the future completes. +proc `callback=`*[T](future: PFuture[T], + cb: proc (future: PFuture[T]) {.closure.}) = + ## Sets the callback proc to be called when the future completes. ## ## If future has already completed then ``cb`` will be called immediately. - ## - ## **Note**: This is used for the ``await`` functionality, you most likely - ## want to use ``callback``. - future.cbVoid = cb - if future.finished: - future.cbVoid() + future.callback = proc () = cb(future) proc read*[T](future: PFuture[T]): T = ## 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. future.error != nil -when defined(windows): +# TODO: Get rid of register. Do it implicitly. + +when defined(windows) or defined(nimdoc): + import winlean type TCompletionKey = dword TCompletionData* = object sock: TSocketHandle - cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.} + cb: proc (sock: TSocketHandle, bytesTransferred: DWORD, + errcode: TOSErrorCode) {.closure.} PDispatcher* = ref object ioPort: THandle + hasHandles: bool TCustomOverlapped = object Internal*: DWORD @@ -129,9 +127,13 @@ when defined(windows): if CreateIOCompletionPort(sock.THandle, p.ioPort, cast[TCompletionKey](sock), 1) == 0: OSError(OSLastError()) + p.hasHandles = true proc poll*(p: PDispatcher, timeout = 500) = ## Waits for completion events and processes them. + if not p.hasHandles: + raise newException(EInvalidValue, "No handles registered in dispatcher.") + let llTimeout = if timeout == -1: winlean.INFINITE else: timeout.int32 @@ -145,16 +147,19 @@ when defined(windows): # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html var customOverlapped = cast[PCustomOverlapped](lpOverlapped) if res: + # This is useful for ensuring the reliability of the overlapped struct. 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) else: let errCode = OSLastError() if lpOverlapped != nil: assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + customOverlapped.data.cb(customOverlapped.data.sock, + lpNumberOfBytesTransferred, errCode) dealloc(customOverlapped) - customOverlapped.data.cb(customOverlapped.data.sock, errCode) else: if errCode.int32 == WAIT_TIMEOUT: # Timed out @@ -252,11 +257,12 @@ when defined(windows): # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - retFuture.complete(0) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, @@ -265,7 +271,9 @@ when defined(windows): # Request to connect completed immediately. success = true 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 else: lastError = OSLastError() @@ -283,9 +291,13 @@ when defined(windows): retFuture.fail(newException(EOS, osErrorMsg(lastError))) 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 - ## 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]() @@ -293,31 +305,50 @@ when defined(windows): dataBuf.buf = newString(size) dataBuf.len = size - var bytesReceived, flags: DWord + var bytesReceived: DWord + var flagsio = flags.dword var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - var data = newString(size) - copyMem(addr data[0], addr dataBuf.buf[0], size) - retFuture.complete($data) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + if bytesCount == 0 and dataBuf.buf[0] == '\0': + retFuture.complete("") + else: + var data = newString(size) + copyMem(addr data[0], addr dataBuf.buf[0], size) + retFuture.complete($data) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) - + let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, - addr flags, cast[POverlapped](ol), nil) + addr flagsio, cast[POverlapped](ol), nil) if ret == -1: let err = OSLastError() if err.int32 != ERROR_IO_PENDING: retFuture.fail(newException(EOS, osErrorMsg(err))) 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: # Request to read completed immediately. var data = newString(size) copyMem(addr data[0], addr dataBuf.buf[0], size) 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 proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = @@ -332,11 +363,12 @@ when defined(windows): var bytesReceived, flags: DWord var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - retFuture.complete(0) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, @@ -348,7 +380,9 @@ when defined(windows): dealloc(ol) else: 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 proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): @@ -390,11 +424,12 @@ when defined(windows): var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - completeAccept() - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + completeAccept() + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx @@ -411,70 +446,472 @@ when defined(windows): dealloc(ol) else: 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 - proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] = - ## Accepts a new connection. Returns a future containing the client socket - ## corresponding to that connection. - ## The future will complete when the connection is successfully accepted. - var retFut = newFuture[TSocketHandle]() - var fut = p.acceptAddr(socket) - fut.callback = - proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) = - assert future.finished - if future.failed: - retFut.fail(future.error) - else: - retFut.complete(future.read.client) - return retFut - initAll() else: - # TODO: Selectors. + 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: var p = newDispatcher() var sock = socket() - #sock.setBlocking false - p.register(sock) - - when true: - - var f = p.connect(sock, "irc.freenode.org", TPort(6667)) - f.callback = - proc (future: PFuture[int]) = - echo("Connected in future!") - echo(future.read) - for i in 0 .. 50: - var recvF = p.recv(sock, 10) - recvF.callback = - proc (future: PFuture[string]) = - echo("Read: ", future.read) + sock.setBlocking false + + + when false: + # Await tests + proc main(p: PDispatcher): PFuture[int] {.async.} = + discard await p.connect(sock, "irc.freenode.net", TPort(6667)) + while true: + var line = await p.recvLine(sock) + echo("Line is: ", line.repr) + if line == "": + echo "Disconnected" + 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: + when false: - sock.bindAddr(TPort(6667)) - sock.listen() - proc onAccept(future: PFuture[TSocketHandle]) = - echo "Accepted" - var t = p.send(future.read, "test\c\L") - t.callback = + var f = p.connect(sock, "irc.freenode.org", TPort(6667)) + f.callback = proc (future: PFuture[int]) = + echo("Connected in future!") echo(future.read) - + for i in 0 .. 50: + var recvF = p.recv(sock, 10) + recvF.callback = + proc (future: PFuture[string]) = + echo("Read ", future.read.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) f.callback = onAccept - - var f = p.accept(sock) - f.callback = onAccept while true: p.poll() - echo "polled" diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index 3ed00fdb2..889912052 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -14,7 +14,7 @@ type 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 ## be loaded. @@ -53,6 +53,7 @@ when defined(posix): # var 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 dlopen(path: CString, mode: int): TLibHandle {. @@ -60,7 +61,10 @@ when defined(posix): proc dlsym(lib: TLibHandle, name: cstring): pointer {. importc, header: "<dlfcn.h>".} - proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW) + proc loadLib(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 unloadLib(lib: TLibHandle) = dlclose(lib) proc symAddr(lib: TLibHandle, name: cstring): pointer = @@ -81,7 +85,7 @@ elif defined(windows) or defined(dos): proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {. importc: "GetProcAddress", header: "<windows.h>", stdcall.} - proc loadLib(path: string): TLibHandle = + proc loadLib(path: string, global_symbols=false): TLibHandle = result = cast[TLibHandle](winLoadLibrary(path)) proc loadLib(): TLibHandle = result = cast[TLibHandle](winLoadLibrary(nil)) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index bdcae677e..0ec007009 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -37,4 +37,19 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: dealloc(aiList) osError(osLastError()) - dealloc(aiList) \ No newline at end of file + 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()) \ No newline at end of file diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 70b617393..68b1ab223 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -836,9 +836,11 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string = while i < s.len: c.ml = 0 var L = rawMatch(s, pattern, i, c) - if L < 0: break - yield substr(s, i, i+L-1) - inc(i, L) + if L < 0: + inc(i, 1) + else: + yield substr(s, i, i+L-1) + inc(i, L) proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. nosideEffect, rtl, extern: "npegs$1".} = diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 83c158da1..6482a01a6 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2013 Dominik Picheta +# (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,212 +9,211 @@ # TODO: Docs. -import tables, os, unsigned -when defined(windows): - import winlean -else: - import posix +import tables, os, unsigned, hashes + +when defined(linux): import posix, epoll +elif defined(windows): import winlean + +proc hash*(x: TSocketHandle): THash {.borrow.} type TEvent* = enum EvRead, EvWrite - TSelectorKey* = object - fd: cint - events: set[TEvent] - data: PObject - - TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]] - - PSelector* = ref object of PObject ## Selector interface. - fds*: TTable[cint, TSelectorKey] - registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent], - data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].} - unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].} - selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].} - closeImpl*: proc (s: PSelector) {.nimcall.} - -template initSelector(r: expr) = - new r - r.fds = initTable[cint, TSelectorKey]() - -proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject): - TSelectorKey = - if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data) - -proc unregister*(s: PSelector, fd: cint): TSelectorKey = - ## - ## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey`` - ## will only have the ``fd`` field set. This is an optimisation and may - ## change in the future if a viable use case is presented. - if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd) - -proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] = - ## - ## The ``events`` field of the returned ``key`` contains the original events - ## for which the ``fd`` was bound. This is contrary to the ``events`` field - ## of the ``TReadyInfo`` tuple which determines which events are ready - ## on the ``fd``. - - if not s.selectImpl.isNil: result = s.selectImpl(s, timeout) + PSelectorKey* = ref object + fd*: TSocketHandle + events*: set[TEvent] ## The events which ``fd`` listens for. + data*: PObject ## User object. -proc close*(s: PSelector) = - if not s.closeImpl.isNil: s.closeImpl(s) + TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]] -# ---- 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 +when defined(linux) or defined(nimdoc): type - PEpollSelector* = ref object of PSelector + PSelector* = ref object epollFD: cint events: array[64, ptr epoll_event] + fds: TTable[TSocketHandle, PSelectorKey] - TDataWrapper = object - fd: cint - boundEvents: set[TEvent] ## The events which ``fd`` listens for. - data: PObject ## User object. - - proc esRegister(s: PSelector, fd: cint, events: set[TEvent], - data: PObject): TSelectorKey = - var es = PEpollSelector(s) - var event: epoll_event + proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event = if EvRead in events: - event.events = EPOLLIN + result.events = EPOLLIN if EvWrite in events: - event.events = event.events or EPOLLOUT - - var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd - dw.fd = fd - dw.boundEvents = events - dw.data = data - event.data.thePtr = dw + result.events = result.events or EPOLLOUT + result.data.fd = fd.cint + + proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], + data: PObject): PSelectorKey {.discardable.} = + ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent + ## ``events``. + 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()) - - result = TSelectorKey(fd: fd, events: events, data: data) - proc esUnregister(s: PSelector, fd: cint): TSelectorKey = - # We cannot find out the information about this ``fd`` from the epoll - # context. As such I will simply return an almost empty TSelectorKey. - var es = PEpollSelector(s) - if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: + var key = PSelectorKey(fd: fd, events: events, data: data) + + s.fds[fd] = key + result = key + + 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.") + 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()) - # We could fill in the ``fds`` TTable to get the info, but that wouldn't - # be nice for our memory. - result = TSelectorKey(fd: fd, events: {}, data: nil) - - proc esClose(s: PSelector) = - var es = PEpollSelector(s) - if es.epollFD.close() != 0: OSError(OSLastError()) - dealloc(addr es.events) # TODO: Test this + echo("finished updating") + result = s.fds[fd] - proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] = + proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = + if not s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor not found.") + if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: + OSError(OSLastError()) + result = s.fds[fd] + s.fds.del(fd) + + 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 = @[] - 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: return @[] for i in 0 .. <evNum: var evSet: set[TEvent] = {} - if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead} - if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite} - let dw = cast[ptr TDataWrapper](es.events[i].data.thePtr) + if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead} + if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite} - let selectorKey = TSelectorKey(fd: dw.fd, events: dw.boundEvents, - data: dw.data) + let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle] result.add((selectorKey, evSet)) - proc newEpollSelector*(): PEpollSelector = + proc newSelector*(): PSelector = new result result.epollFD = epoll_create(64) result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64)) + result.fds = initTable[TSocketHandle, PSelectorKey]() if result.epollFD < 0: OSError(OSLastError()) - result.registerImpl = esRegister - result.unregisterImpl = esUnregister - result.closeImpl = esClose - result.selectImpl = esSelect + + proc contains*(s: PSelector, fd: TSocketHandle): bool = + ## Determines whether selector contains a file descriptor. + 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: # Select() @@ -224,11 +223,12 @@ when isMainModule: sock: TSocket var sock = socket() + sock.setBlocking(false) sock.connect("irc.freenode.net", TPort(6667)) - var selector = newEpollSelector() + var selector = newSelector() 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 while true: let ready = selector.select(1000) @@ -236,6 +236,7 @@ when isMainModule: if ready.len > 0: echo ready[0].events i.inc if i == 6: + assert selector.unregister(sock.getFD).fd == sock.getFD selector.close() break diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim index 22624bbad..031217b90 100644 --- a/lib/pure/sockets2.nim +++ b/lib/pure/sockets2.nim @@ -17,11 +17,13 @@ when hostos == "solaris": when defined(Windows): import winlean + export ioctlsocket else: import posix + export fcntl, F_GETFL, O_NONBLOCK, F_SETFL export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, - inet_ntoa + inet_ntoa, recv, `==`, connect, send, accept type @@ -63,10 +65,10 @@ type when defined(windows): let - OSInvalidSocket* = winlean.INVALID_SOCKET + osInvalidSocket* = winlean.INVALID_SOCKET else: let - OSInvalidSocket* = posix.INVALID_SOCKET + osInvalidSocket* = posix.INVALID_SOCKET proc `==`*(a, b: TPort): bool {.borrow.} ## ``==`` for ports. @@ -89,7 +91,7 @@ when defined(posix): of AF_UNIX: result = posix.AF_UNIX of AF_INET: result = posix.AF_INET of AF_INET6: result = posix.AF_INET6 - else: nil + else: discard proc toInt(typ: TType): cint = case typ @@ -97,7 +99,7 @@ when defined(posix): of SOCK_DGRAM: result = posix.SOCK_DGRAM of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET of SOCK_RAW: result = posix.SOCK_RAW - else: nil + else: discard proc toInt(p: TProtocol): cint = case p @@ -107,7 +109,7 @@ when defined(posix): of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 of IPPROTO_RAW: result = posix.IPPROTO_RAW of IPPROTO_ICMP: result = posix.IPPROTO_ICMP - else: nil + else: discard else: proc toInt(domain: TDomain): cint = @@ -199,4 +201,4 @@ proc htons*(x: int16): int16 = when defined(Windows): var wsa: TWSADATA - if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) \ No newline at end of file + if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index de6c4e4fa..2fce235e2 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string = "November", "December"] 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 = ## This function formats `info` as specified by `f`. The following format ## 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`` ## ========== ================================================================================= ================================================ ## - ## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``. - ## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,`` + ## Other strings can be inserted by putting them in ``''``. For example + ## ``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 = "" var i = 0 @@ -600,112 +716,8 @@ proc format*(info: TTimeInfo, f: string): string = while true: case f[i] of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': - case currentF - 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) - + format_token(info, currentF, result) + currentF = "" if f[i] == '\0': break @@ -716,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string = inc(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) {.pop.} @@ -727,11 +747,15 @@ when isMainModule: var t = getGMTime(fromSeconds(2147483647)) 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 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" & " 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" + + assert t.format("yyyyMMddhhmmss") == "20380119031407" 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" & diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 4fc5f479b..0714f4589 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -418,76 +418,75 @@ proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} = return Math.floor(`a` % `b`); """ -proc NegInt(a: int): int {.compilerproc.} = +proc negInt(a: int): int {.compilerproc.} = result = a*(-1) -proc NegInt64(a: int64): int64 {.compilerproc.} = +proc negInt64(a: int64): int64 {.compilerproc.} = result = a*(-1) -proc AbsInt(a: int): int {.compilerproc.} = +proc absInt(a: int): int {.compilerproc.} = 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 -proc LeU(a, b: int): bool {.compilerproc.} = +proc leU(a, b: int): bool {.compilerproc.} = result = abs(a) <= abs(b) -proc LtU(a, b: int): bool {.compilerproc.} = +proc ltU(a, b: int): bool {.compilerproc.} = result = abs(a) < abs(b) -proc LeU64(a, b: int64): bool {.compilerproc.} = +proc leU64(a, b: int64): bool {.compilerproc.} = result = abs(a) <= abs(b) - -proc LtU64(a, b: int64): bool {.compilerproc.} = +proc ltU64(a, b: int64): bool {.compilerproc.} = result = abs(a) < abs(b) -proc AddU(a, b: int): int {.compilerproc.} = +proc addU(a, b: int): int {.compilerproc.} = result = abs(a) + abs(b) -proc AddU64(a, b: int64): int64 {.compilerproc.} = +proc addU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) + abs(b) -proc SubU(a, b: int): int {.compilerproc.} = +proc subU(a, b: int): int {.compilerproc.} = result = abs(a) - abs(b) -proc SubU64(a, b: int64): int64 {.compilerproc.} = +proc subU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) - abs(b) -proc MulU(a, b: int): int {.compilerproc.} = +proc mulU(a, b: int): int {.compilerproc.} = result = abs(a) * abs(b) -proc MulU64(a, b: int64): int64 {.compilerproc.} = +proc mulU64(a, b: int64): int64 {.compilerproc.} = 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) -proc DivU64(a, b: int64): int64 {.compilerproc.} = +proc divU64(a, b: int64): int64 {.compilerproc.} = 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) -proc ModU64(a, b: int64): int64 {.compilerproc.} = +proc modU64(a, b: int64): int64 {.compilerproc.} = 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.} = + +proc ze64*(a: int64): int64 {.compilerproc.} = result = a -proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} = +proc toU8*(a: int): int8 {.noStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} = +proc toU16*(a: int): int16 {.noStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} = +proc toU32*(a: int64): int32 {.noStackFrame, compilerproc.} = asm """ return `a`; """ - 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 @@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool = tyArray, tyArrayConstr, tyTuple, 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 of nkNone: sysAssert(false, "NimCopyAux") 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 of tyPtr, tyRef, tyVar, tyNil: if not isFatPointer(ti): @@ -585,7 +584,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} = else: result = nil -proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. +proc arrayConstr(len: int, value: pointer, typ: PNimType): pointer {. noStackFrame, compilerproc.} = # types are fake asm """ diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 6c8fa4882..74ef9c9ec 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -199,14 +199,14 @@ else: importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} - proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. + proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {. importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} proc removeDirectoryA*(lpPathName: cstring): int32 {. importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} - proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. + proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {. importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} when useWinUnicode: @@ -304,7 +304,7 @@ else: dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} - proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, + proc copyFileA*(lpExistingFileName, lpNewFileName: CString, bFailIfExists: cint): cint {. importc: "CopyFileA", stdcall, dynlib: "kernel32".} diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim index 00b427603..878356321 100644 --- a/tests/actiontable/tactiontable2.nim +++ b/tests/actiontable/tactiontable2.nim @@ -1,6 +1,6 @@ discard """ line: 21 - errormsg: "invalid type: 'TTable'" + errormsg: "invalid type: 'TTable[string, proc (string)]'" """ import tables diff --git a/tests/ambsym/mambsym1.nim b/tests/ambsym/mambsym1.nim index cf8ac5242..d9d57b5e5 100644 --- a/tests/ambsym/mambsym1.nim +++ b/tests/ambsym/mambsym1.nim @@ -7,4 +7,4 @@ type proc ha() = var x: TExport # no error - nil + discard diff --git a/tests/ambsym/mambsys1.nim b/tests/ambsym/mambsys1.nim index 5472b5ae4..04f9561d3 100644 --- a/tests/ambsym/mambsys1.nim +++ b/tests/ambsym/mambsys1.nim @@ -4,4 +4,4 @@ type TExport* = enum x, y, z proc foo*(x: int) = - nil + discard diff --git a/tests/ambsym/mambsys2.nim b/tests/ambsym/mambsys2.nim index 395425b86..d59706865 100644 --- a/tests/ambsym/mambsys2.nim +++ b/tests/ambsym/mambsys2.nim @@ -1,4 +1,4 @@ type TExport* = enum x, y, z # exactly the same type! -proc foo*(x: int) = nil +proc foo*(x: int) = discard diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim new file mode 100644 index 000000000..bde5bf8c8 --- /dev/null +++ b/tests/async/tasyncawait.nim @@ -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 diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim index 003ec6e50..b033b98ec 100644 --- a/tests/casestmt/tcasestm.nim +++ b/tests/casestmt/tcasestm.nim @@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c") case x of "Andreas", "Rumpf": write(stdout, "Hallo Meister!") of "aa", "bb": write(stdout, "Du bist nicht mein Meister") -of "cc", "hash", "when": nil -of "will", "it", "finally", "be", "generated": nil +of "cc", "hash", "when": discard +of "will", "it", "finally", "be", "generated": discard var z = case i of 1..5, 8, 9: "aa" diff --git a/tests/concurrency/tnodeadlocks.nim b/tests/concurrency/tnodeadlocks.nim index 18fdca3e9..3f27e24f6 100644 --- a/tests/concurrency/tnodeadlocks.nim +++ b/tests/concurrency/tnodeadlocks.nim @@ -12,7 +12,7 @@ var thr: array [0..5, TThread[tuple[a, b: int]]] L, M, N: TLock -proc doNothing() = nil +proc doNothing() = discard proc threadFunc(interval: tuple[a, b: int]) {.thread.} = doNothing() diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index f10c0cc9c..e5236aaab 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -80,13 +80,13 @@ proc mygeneric1() = echo "mygeneric1 constructed" proc mygeneric2[T](val: T) = - var - a = open() - b = TMyGeneric2[int, T](x: 10, y: val) - c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test") - + var a = open() + + var b = TMyGeneric2[int, T](x: 10, y: val) echo "mygeneric2 constructed" + var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test") + proc mygeneric3 = var x = TMyGeneric3[int, string, TMyGeneric1[int]]( x: 10, y: "test", z: TMyGeneric1[int](x: 10)) diff --git a/tests/destructor/tdictdestruct.nim b/tests/destructor/tdictdestruct.nim index ec1084105..b7043f7b7 100644 --- a/tests/destructor/tdictdestruct.nim +++ b/tests/destructor/tdictdestruct.nim @@ -6,7 +6,7 @@ type PDict[TK, TV] = ref TDict[TK, TV] proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) = - nil + discard proc destroyDict[TK, TV](a: PDict[TK, TV]) = return diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 3c543ecfa..963d0ccfb 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -32,7 +32,7 @@ const proc len[T,D] (n:PNode[T,D]): Int {.inline.} = 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.} = 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 cLenCenter : setLen(n.slots, cLenCenter) of cLen4 : setLen(n.slots, cLen4) - else: nil + else: discard Result = n 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 cLenCenter: setLen(APath.Nd.slots, cLen4) 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]) APath.Nd.slots[x] = setItem(AKey, AValue, ANode) diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim new file mode 100644 index 000000000..f7aafe1d9 --- /dev/null +++ b/tests/generics/tgenericlambda.nim @@ -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 + diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim new file mode 100644 index 000000000..42353006d --- /dev/null +++ b/tests/generics/tmetafield.nim @@ -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 diff --git a/tests/global/globalaux.nim b/tests/global/globalaux.nim new file mode 100644 index 000000000..5f6f72721 --- /dev/null +++ b/tests/global/globalaux.nim @@ -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 + diff --git a/tests/global/globalaux2.nim b/tests/global/globalaux2.nim new file mode 100644 index 000000000..6c77f1f48 --- /dev/null +++ b/tests/global/globalaux2.nim @@ -0,0 +1,4 @@ +import globalaux + +echo "in globalaux2: ", globalInstance[int]().val + diff --git a/tests/lookups/tredef.nim b/tests/lookups/tredef.nim index 02d1f7776..a1647000c 100644 --- a/tests/lookups/tredef.nim +++ b/tests/lookups/tredef.nim @@ -1,28 +1,28 @@ -template foo(a: int, b: string) = nil +template foo(a: int, b: string) = discard foo(1, "test") -proc bar(a: int, b: string) = nil +proc bar(a: int, b: string) = discard bar(1, "test") template foo(a: int, b: string) = bar(a, b) foo(1, "test") block: - proc bar(a: int, b: string) = nil - template foo(a: int, b: string) = nil + proc bar(a: int, b: string) = discard + template foo(a: int, b: string) = discard foo(1, "test") bar(1, "test") proc baz = - proc foo(a: int, b: string) = nil + proc foo(a: int, b: string) = discard proc foo(b: string) = - template bar(a: int, b: string) = nil + template bar(a: int, b: string) = discard bar(1, "test") foo("test") block: - proc foo(b: string) = nil + proc foo(b: string) = discard foo("test") foo(1, "test") diff --git a/tests/macros/tvarnimnode.nim b/tests/macros/tvarnimnode.nim new file mode 100644 index 000000000..73fcc16ea --- /dev/null +++ b/tests/macros/tvarnimnode.nim @@ -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 diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index 95c71c08c..1edda4aa0 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -178,14 +178,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter = ## #parsed_param3 = new_parsed_parameter(PK_INT, "231") var result {.gensym.}: Tparsed_parameter result.kind = tkind - when tkind == PK_EMPTY: nil + when tkind == PK_EMPTY: discard elif tkind == PK_INT: result.int_val = expr elif tkind == PK_BIGGEST_INT: result.big_int_val = expr elif tkind == PK_FLOAT: result.float_val = expr elif tkind == PK_BIGGEST_FLOAT: result.big_float_val = expr elif tkind == PK_STRING: result.str_val = expr elif tkind == PK_BOOL: result.bool_val = expr - elif tkind == PK_HELP: nil + elif tkind == PK_HELP: discard else: {.error: "unknown kind".} result diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim index 5ea8cf063..84527362f 100644 --- a/tests/metatype/tbindtypedesc.nim +++ b/tests/metatype/tbindtypedesc.nim @@ -1,10 +1,10 @@ discard """ - msg: ''' -int -float -TFoo -TFoo -''' + msg: '''int int +float float +int int +TFoo TFoo +int float +TFoo TFoo''' """ import typetraits @@ -24,9 +24,8 @@ template reject(e: expr) = proc genericParamRepeated[T: typedesc](a: T, b: T) = static: - echo a.name - echo b.name - + echo a.name, " ", b.name + accept genericParamRepeated(int, int) accept genericParamRepeated(float, float) @@ -35,8 +34,7 @@ reject genericParamRepeated(int, float) proc genericParamOnce[T: typedesc](a, b: T) = static: - echo a.name - echo b.name + echo a.name, " ", b.name accept genericParamOnce(int, int) accept genericParamOnce(TFoo, TFoo) @@ -68,8 +66,7 @@ reject typePairs2(string, int, TBAR, TBAR) proc dontBind(a: typedesc, b: typedesc) = static: - echo a.name - echo b.name + echo a.name, " ", b.name accept dontBind(int, float) accept dontBind(TFoo, TFoo) diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim index 4c8c0fc56..5b04c490f 100644 --- a/tests/metatype/tusertypeclasses.nim +++ b/tests/metatype/tusertypeclasses.nim @@ -31,9 +31,10 @@ proc intval(x: int) = discard # check real and virtual fields type TFoo = generic T - intval T.x + T.x + y(T) intval T.y - + proc y(x: TObj): int = 10 proc testFoo(x: TFoo) = discard diff --git a/tests/method/tmethods1.nim b/tests/method/tmethods1.nim index f4add6af4..43a260bca 100644 --- a/tests/method/tmethods1.nim +++ b/tests/method/tmethods1.nim @@ -14,8 +14,8 @@ type TSomethingElse = object PSomethingElse = ref TSomethingElse -method foo(a: PNode, b: PSomethingElse) = nil -method foo(a: PNodeFoo, b: PSomethingElse) = nil +method foo(a: PNode, b: PSomethingElse) = discard +method foo(a: PNodeFoo, b: PSomethingElse) = discard var o: TObject o.somethin() diff --git a/tests/module/trecinca.nim b/tests/module/trecinca.nim index 73a0ec937..62d37783c 100644 --- a/tests/module/trecinca.nim +++ b/tests/module/trecinca.nim @@ -1,7 +1,7 @@ discard """ file: "tests/reject/trecincb.nim" line: 9 - errormsg: "recursive dependency: 'tests/reject/trecincb.nim'" + errormsg: "recursive dependency: 'tests/module/trecincb.nim'" """ # Test recursive includes diff --git a/tests/module/trecincb.nim b/tests/module/trecincb.nim index 9dd7d51de..a2934052f 100644 --- a/tests/module/trecincb.nim +++ b/tests/module/trecincb.nim @@ -1,7 +1,7 @@ discard """ file: "trecincb.nim" line: 9 - errormsg: "recursive dependency: 'tests/reject/trecincb.nim'" + errormsg: "recursive dependency: 'tests/module/trecincb.nim'" """ # Test recursive includes diff --git a/tests/overload/toverwr.nim b/tests/overload/toverwr.nim index ef25e8913..32d50faaa 100644 --- a/tests/overload/toverwr.nim +++ b/tests/overload/toverwr.nim @@ -1,13 +1,13 @@ -discard """ - file: "toverwr.nim" - output: "hello" -""" +discard """ + file: "toverwr.nim" + output: "hello" +""" # Test the overloading resolution in connection with a qualifier proc write(t: TFile, s: string) = - nil # a nop + discard # a nop system.write(stdout, "hello") #OUT hello - - + + diff --git a/tests/patterns/targlist.nim b/tests/patterns/targlist.nim index a2fa1fa48..e416edf0a 100644 --- a/tests/patterns/targlist.nim +++ b/tests/patterns/targlist.nim @@ -2,7 +2,7 @@ discard """ output: "12false3ha" """ -proc f(x: varargs[string, `$`]) = nil +proc f(x: varargs[string, `$`]) = discard template optF{f(X)}(x: varargs[expr]) = writeln(stdout, x) diff --git a/tests/range/tsubrange2.nim b/tests/range/tsubrange2.nim index 51598713b..d14111bb9 100644 --- a/tests/range/tsubrange2.nim +++ b/tests/range/tsubrange2.nim @@ -8,7 +8,7 @@ type TRange = range[0..40] proc p(r: TRange) = - nil + discard var r: TRange diff --git a/tests/range/tsubrange3.nim b/tests/range/tsubrange3.nim index b3e02fd29..9afb5018b 100644 --- a/tests/range/tsubrange3.nim +++ b/tests/range/tsubrange3.nim @@ -8,7 +8,7 @@ type TRange = range[0..40] proc p(r: TRange) = - nil + discard var r: TRange diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index 7b806f15b..e370209ed 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -1,7 +1,7 @@ -discard """ - file: "tsets.nim" - output: "Ha ein F ist in s!" -""" +discard """ + file: "tsets.nim" + output: "Ha ein F ist in s!" +""" # Test the handling of sets import @@ -38,7 +38,7 @@ type TTokTypes* = set[TTokTypeRange] const - toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), + toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), tkStrLit..tkTripleStrLit} var @@ -51,14 +51,14 @@ else: write(stdout, "BUG: F ist nicht in s!\n") a = {} #{'a'..'z'} for x in low(TAZ) .. high(TAZ): incl(a, x) - if x in a: nil + if x in a: discard else: write(stdout, "BUG: something not in a!\n") for x in low(TTokTypeRange) .. high(TTokTypeRange): if x in tokTypes: - nil + discard #writeln(stdout, "the token '$1' is in the set" % repr(x)) #OUT Ha ein F ist in s! - - + + diff --git a/tests/stdlib/tircbot.nim b/tests/stdlib/tircbot.nim index 6008838ff..f0417c7ac 100644 --- a/tests/stdlib/tircbot.nim +++ b/tests/stdlib/tircbot.nim @@ -200,7 +200,7 @@ proc setSeen(d: TDb, s: TSeen) = var hashToSet = @[("type", $s.kind.int), ("channel", s.channel), ("timestamp", $s.timestamp.int)] case s.kind - of PSeenJoin: nil + of PSeenJoin: discard of PSeenPart, PSeenMsg, PSeenQuit: hashToSet.add(("msg", s.msg)) of PSeenNick: @@ -338,7 +338,7 @@ proc hubConnect(state: PState) = proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = case event.typ - of EvConnected: nil + of EvConnected: discard of EvDisconnected: while not state.ircClient.isConnected: try: @@ -424,7 +424,7 @@ proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = seenNick.newNick = event.params[0] state.database.setSeen(seenNick) else: - nil # TODO: ? + discard # TODO: ? proc open(port: TPort = TPort(5123)): PState = var res: PState diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim index 6a1dae54d..935b08634 100644 --- a/tests/stdlib/tmath2.nim +++ b/tests/stdlib/tmath2.nim @@ -1,7 +1,7 @@ # tests for the interpreter proc loops(a: var int) = - nil + discard #var # b: int #b = glob diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index fa9993cc9..ebe577b00 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -7,6 +7,6 @@ proc walkDirTree(root: string) = case k of pcFile, pcLinkToFile: echo(f) of pcDir: walkDirTree(f) - of pcLinkToDir: nil + of pcLinkToDir: discard walkDirTree(".") diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index bdd8db0f8..7775091a1 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -72,7 +72,7 @@ type rule: TNode ## the rule that the symbol refers to TNode {.final, shallow.} = object case kind: TPegKind - of pkEmpty..pkWhitespace: nil + of pkEmpty..pkWhitespace: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkChar, pkGreedyRepChar: ch: 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 = result.kind = a.kind case a.kind - of pkEmpty..pkWhitespace: nil + of pkEmpty..pkWhitespace: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: result.term = a.term of pkChar, pkGreedyRepChar: @@ -229,7 +229,7 @@ when false: case a.kind of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, - pkCharChoice, pkGreedyRepSet: nil + pkCharChoice, pkGreedyRepSet: discard of pkNonTerminal: return true else: for i in 0..a.sons.len-1: @@ -318,7 +318,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {. proc spaceCost(n: TPeg): int = case n.kind - of pkEmpty: nil + of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny..pkWhitespace, pkGreedyAny: @@ -1111,7 +1111,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) = of 'A'..'F': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) inc(c.bufpos) - else: nil + else: discard proc getEscapedChar(c: var TPegLexer, tok: var TToken) = inc(c.bufpos) @@ -1341,7 +1341,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) = of "i": tok.modifier = modIgnoreCase of "y": tok.modifier = modIgnoreStyle of "v": tok.modifier = modVerbatim - else: nil + else: discard setLen(tok.literal, 0) if c.buf[c.bufpos] == '$': getDollar(c, tok) @@ -1488,7 +1488,7 @@ proc primary(p: var TPegParser): TPeg = of tkCurlyAt: getTok(p) return !*\primary(p).token(p) - else: nil + else: discard case p.tok.kind of tkIdentifier: if p.identIsVerbatim: diff --git a/tests/table/ttableconstr.nim b/tests/table/ttableconstr.nim index c627e68e8..1a21a18d1 100644 --- a/tests/table/ttableconstr.nim +++ b/tests/table/ttableconstr.nim @@ -1,7 +1,7 @@ # Test if the new table constructor syntax works: template ignoreExpr(e: expr): stmt {.immediate.} = - nil + discard # test first class '..' syntactical citizen: ignoreExpr x <> 2..4 diff --git a/tests/sunset.tmpl b/tests/template/sunset.tmpl index 6475bac4e..6475bac4e 100644 --- a/tests/sunset.tmpl +++ b/tests/template/sunset.tmpl diff --git a/tests/threads/nimrod.cfg b/tests/threads/nimrod.cfg new file mode 100644 index 000000000..b81c89721 --- /dev/null +++ b/tests/threads/nimrod.cfg @@ -0,0 +1 @@ +threads:on diff --git a/tests/typerel/tvoid.nim b/tests/typerel/tvoid.nim index bb569e7f8..d31936217 100644 --- a/tests/typerel/tvoid.nim +++ b/tests/typerel/tvoid.nim @@ -1,5 +1,9 @@ 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 = diff --git a/tests/typerel/typalias.nim b/tests/typerel/typalias.nim index ba9f38ed9..40ff06765 100644 --- a/tests/typerel/typalias.nim +++ b/tests/typerel/typalias.nim @@ -9,7 +9,7 @@ proc init: TYourObj = result.y = -1 proc f(x: var TYourObj) = - nil + discard var m: TMyObj = init() f(m) diff --git a/todo.txt b/todo.txt index 2e6eb708b..656bd06fc 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,10 @@ 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 macros\tstringinterp.nim - test and fix showoff diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index b20e86a68..28d92402e 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -249,7 +249,7 @@ proc walker(dir: string) = of pcDir: if optRecursive in options: walker(path) - else: nil + else: discard if existsFile(dir): processFile(dir) proc writeHelp() = diff --git a/tools/website.tmpl b/tools/website.tmpl index 091079c1c..08c0b450c 100644 --- a/tools/website.tmpl +++ b/tools/website.tmpl @@ -74,5 +74,15 @@ <div id="legal">Copyright © 2013 - Andreas Rumpf & Contributors - All rights reserved - <a href="http://reign-studios.com/philipwitte/">Design by Philip Witte</a></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> </html> diff --git a/web/assets/style.css b/web/assets/style.css index 715214a1a..5cee279fc 100644 --- a/web/assets/style.css +++ b/web/assets/style.css @@ -65,7 +65,7 @@ html, body { #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 p { text-align:justify; } diff --git a/web/community.txt b/web/community.txt index b9a0a4196..d45e7df61 100644 --- a/web/community.txt +++ b/web/community.txt @@ -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 -(logs at `<http://build.nimrod-code.org/irclogs/>`_). +You may also be interested in reading the +`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 =========== @@ -26,7 +66,9 @@ can't find anything you fancy doing, you can always ask for inspiration on IRC 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 `````` diff --git a/web/news.txt b/web/news.txt index b28e5c64a..187797b58 100644 --- a/web/news.txt +++ b/web/news.txt @@ -3,86 +3,103 @@ News ==== -2014-XX-XX Version 0.9.4 released -================================= - - -Bugfixes --------- - - -Library Additions ------------------ - -- Added ``macros.genSym`` builtin for AST generation. -- Added ``macros.newLit`` procs for easier AST generation. - - -Changes affecting backwards compatibility ------------------------------------------ - -- The scoping rules for the ``if`` statement changed for better interaction - with the new syntactic construct ``(;)``. -- ``OSError`` family of procedures has been deprecated. Procedures with the same - 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 - using the new ``OSLastError`` proc. -- ``os.parentDir`` now returns "" if there is no parent dir. -- In CGI scripts stacktraces are shown to the user only - if ``cgi.setStackTraceStdout`` is used. -- 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 - to require instantiation scope for a symbol. -- ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely - passed to shell, instead of just adding double quotes. -- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, - ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. -- The ``nil`` statement has been deprecated, use an empty ``discard`` instead. -- ``sockets.select`` now prunes sockets that are **not** ready from the list - of sockets given to it. - - -Compiler Additions ------------------- - -- The compiler can now warn about "uninitialized" variables. (There are no - real uninitialized variables in Nimrod as they are initialized to binary - zero). Activate via ``{.warning[Uninit]:on.}``. -- The compiler now enforces the ``not nil`` constraint. -- The compiler now supports a ``codegenDecl`` pragma for even more control - over the generated code. -- The compiler now supports a ``computedGoto`` pragma to support very fast - dispatching for interpreters and the like. -- The old evaluation engine has been replaced by a proper register based - virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro - evaluation. -- ``--gc:none`` produces warnings when code uses the GC. - - -Language Additions ------------------- - -- Arrays can now be declared with a single integer literal ``N`` instead of a - range; the range is then ``0..N-1``. -- Added ``requiresInit`` pragma to enforce explicit initialization. -- Exported templates are allowed to access hidden fields. -- The ``using statement`` enables you to more easily author domain-specific - languages and libraries providing OOP-like syntactic sugar. -- Added the possibility to override various dot operators in order to handle - calls to missing procs and reads from undeclared fields at compile-time. -- The overload resolution now supports ``static[T]`` params that must be - evaluable at compile-time. -- Support for user-defined type classes has been added. -- The *command syntax* is supported in a lot more contexts. -- Anonymous iterators are now supported and iterators can capture variables - of an outer proc. - - -Tools improvements ------------------- - -- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option - to activate. +.. + 2014-XX-XX Version 0.9.4 released + ================================= + + + Bugfixes + -------- + + + Library Additions + ----------------- + + - Added ``macros.genSym`` builtin for AST generation. + - Added ``macros.newLit`` procs for easier AST generation. + + + Changes affecting backwards compatibility + ----------------------------------------- + + - The scoping rules for the ``if`` statement changed for better interaction + with the new syntactic construct ``(;)``. + - ``OSError`` family of procedures has been deprecated. Procedures with the same + 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 + using the new ``OSLastError`` proc. + - ``os.parentDir`` now returns "" if there is no parent dir. + - In CGI scripts stacktraces are shown to the user only + if ``cgi.setStackTraceStdout`` is used. + - 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 + to require instantiation scope for a symbol. + - ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely + passed to shell, instead of just adding double quotes. + - ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, + ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. + - The ``nil`` statement has been deprecated, use an empty ``discard`` instead. + - ``sockets.select`` now prunes sockets that are **not** ready from the list + of sockets given to it. + + + Compiler Additions + ------------------ + + - The compiler can now warn about "uninitialized" variables. (There are no + real uninitialized variables in Nimrod as they are initialized to binary + zero). Activate via ``{.warning[Uninit]:on.}``. + - The compiler now enforces the ``not nil`` constraint. + - The compiler now supports a ``codegenDecl`` pragma for even more control + over the generated code. + - The compiler now supports a ``computedGoto`` pragma to support very fast + dispatching for interpreters and the like. + - The old evaluation engine has been replaced by a proper register based + virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro + evaluation. + - ``--gc:none`` produces warnings when code uses the GC. + + + Language Additions + ------------------ + + - Arrays can now be declared with a single integer literal ``N`` instead of a + range; the range is then ``0..N-1``. + - Added ``requiresInit`` pragma to enforce explicit initialization. + - Exported templates are allowed to access hidden fields. + - The ``using statement`` enables you to more easily author domain-specific + languages and libraries providing OOP-like syntactic sugar. + - Added the possibility to override various dot operators in order to handle + calls to missing procs and reads from undeclared fields at compile-time. + - The overload resolution now supports ``static[T]`` params that must be + evaluable at compile-time. + - Support for user-defined type classes has been added. + - The *command syntax* is supported in a lot more contexts. + - Anonymous iterators are now supported and iterators can capture variables + of an outer proc. + + + Tools improvements + ------------------ + + - c2nim can deal with a subset of C++. Use the ``--cpp`` command line option + 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! diff --git a/web/ticker.txt b/web/ticker.txt index 00bc6a125..844ed3033 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -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> <p>New website design!</p> </a> |