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

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

View File

@@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode =
if p.tok.s == "unsigned":
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

View File

@@ -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) =

View File

@@ -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]",

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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!

View File

@@ -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:

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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]))

View File

@@ -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)

View File

@@ -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 = ""

View File

@@ -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

View File

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

View File

@@ -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=`*(future: PFutureBase, cb: proc () {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: You most likely want the other ``callback`` setter which
## passes ``future`` as a param to the callback.
future.cb = cb
if future.finished:
future.cb()
proc `callback=`*[T](future: PFuture[T],
cb: proc (future: PFuture[T]) {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
future.cb = cb
if future.finished:
future.cb(future)
proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) =
## Sets the **void** callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
##
## **Note**: This is used for the ``await`` functionality, you most likely
## want to use ``callback``.
future.cbVoid = cb
if future.finished:
future.cbVoid()
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)
sock.setBlocking false
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)
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"

View File

@@ -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))

View File

@@ -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)
dealloc(aiList)
proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} =
## Sets blocking mode on socket
when defined(Windows):
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
osError(osLastError())
else: # BSD sockets
var x: int = fcntl(s, F_GETFL, 0)
if x == -1:
osError(osLastError())
else:
var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
if fcntl(s, F_SETFL, mode) == -1:
osError(osLastError())

View File

@@ -836,9 +836,11 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
while i < s.len:
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".} =

View File

@@ -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
PSelectorKey* = ref object
fd*: TSocketHandle
events*: set[TEvent] ## The events which ``fd`` listens for.
data*: PObject ## User object.
TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]]
TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
PSelector* = ref object of PObject ## Selector interface.
fds*: TTable[cint, TSelectorKey]
registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].}
unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].}
selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].}
closeImpl*: proc (s: PSelector) {.nimcall.}
template initSelector(r: expr) =
new r
r.fds = initTable[cint, TSelectorKey]()
proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject):
TSelectorKey =
if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data)
proc unregister*(s: PSelector, fd: cint): TSelectorKey =
##
## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey``
## will only have the ``fd`` field set. This is an optimisation and may
## change in the future if a viable use case is presented.
if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd)
proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] =
##
## The ``events`` field of the returned ``key`` contains the original events
## for which the ``fd`` was bound. This is contrary to the ``events`` field
## of the ``TReadyInfo`` tuple which determines which events are ready
## on the ``fd``.
if not s.selectImpl.isNil: result = s.selectImpl(s, timeout)
proc close*(s: PSelector) =
if not s.closeImpl.isNil: s.closeImpl(s)
# ---- Select() ----------------------------------------------------------------
type
PSelectSelector* = ref object of PSelector ## Implementation of select()
proc ssRegister(s: PSelector, fd: cint, events: set[TEvent],
data: PObject): TSelectorKey =
if s.fds.hasKey(fd):
raise newException(EInvalidValue, "FD already exists in selector.")
var sk = TSelectorKey(fd: fd, events: events, data: data)
s.fds[fd] = sk
result = sk
proc ssUnregister(s: PSelector, fd: cint): TSelectorKey =
result = s.fds[fd]
s.fds.del(fd)
proc ssClose(s: PSelector) = nil
proc timeValFromMilliseconds(timeout: int): TTimeVal =
if timeout != -1:
var seconds = timeout div 1000
result.tv_sec = seconds.int32
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey],
m: var int) =
FD_ZERO(rd); FD_ZERO(wr)
for k, v in pairs(fds):
if EvRead in v.events:
m = max(m, int(k))
FD_SET(k, rd)
if EvWrite in v.events:
m = max(m, int(k))
FD_SET(k, wr)
proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]):
seq[TReadyInfo] =
result = @[]
for k, v in pairs(fds):
var events: set[TEvent] = {}
if FD_ISSET(k, rd) != 0'i32:
events = events + {EvRead}
if FD_ISSET(k, wr) != 0'i32:
events = events + {EvWrite}
result.add((v, events))
proc select(fds: TTable[cint, TSelectorKey], timeout = 500):
seq[TReadyInfo] =
var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
var rd, wr: TFdSet
var m = 0
createFdSet(rd, wr, fds, m)
var retCode = 0
if timeout != -1:
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
else:
retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
if retCode < 0:
OSError(OSLastError())
elif retCode == 0:
return @[]
else:
return getReadyFDs(rd, wr, fds)
proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
result = select(s.fds, timeout)
proc newSelectSelector*(): PSelectSelector =
initSelector(result)
result.registerImpl = ssRegister
result.unregisterImpl = ssUnregister
result.selectImpl = ssSelect
result.closeImpl = ssClose
# ---- Epoll -------------------------------------------------------------------
when defined(linux):
import epoll
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
if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError())
result = TSelectorKey(fd: fd, events: events, data: data)
result.events = result.events or EPOLLOUT
result.data.fd = fd.cint
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:
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.")
var event = createEventStruct(events, fd)
if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, 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)
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())
echo("finished updating")
result = s.fds[fd]
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 esClose(s: PSelector) =
var es = PEpollSelector(s)
if es.epollFD.close() != 0: OSError(OSLastError())
dealloc(addr es.events) # TODO: Test this
proc close*(s: PSelector) =
if s.epollFD.close() != 0: OSError(OSLastError())
dealloc(addr s.events) # TODO: Test this
proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
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

View File

@@ -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())
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())

View File

@@ -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" &

View File

@@ -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.} =
result = a
proc Ze64(a: int64): int64 {.compilerproc.} =
proc ze*(a: int): int {.compilerproc.} =
result = a
proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} =
proc ze64*(a: int64): int64 {.compilerproc.} =
result = a
proc toU8*(a: int): int8 {.noStackFrame, compilerproc.} =
asm """
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 """

View File

@@ -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".}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c")
case x
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"

View File

@@ -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()

View File

@@ -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))

View File

@@ -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

View File

@@ -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)

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,28 +1,28 @@
template foo(a: int, b: string) = nil
template foo(a: int, b: string) = discard
foo(1, "test")
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")

View File

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

View File

@@ -178,14 +178,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter =
## #parsed_param3 = new_parsed_parameter(PK_INT, "231")
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

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

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

View File

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

View File

@@ -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!

View File

@@ -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

View File

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

View File

@@ -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(".")

View File

@@ -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:

View File

@@ -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

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

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

View File

@@ -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 =

View File

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

View File

@@ -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

View File

@@ -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() =

View File

@@ -74,5 +74,15 @@
<div id="legal">Copyright &copy; 2013 - Andreas Rumpf &amp; Contributors - All rights reserved - <a href="http://reign-studios.com/philipwitte/">Design by Philip Witte</a></div>
</div>
</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>

View File

@@ -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; }

View File

@@ -1,15 +1,55 @@
Discuss Nimrod in our `forum <http://forum.nimrod-code.org/>`_.
Forum
=====
Visit our project page at GitHub: http://github.com/Araq/Nimrod.
The `Nimrod forum <http://forum.nimrod-code.org/>`_ is the place where most
discussions related to the language happen. It not only includes discussions
relating to the design of Nimrod but also allows for beginners to ask questions
relating to Nimrod.
Wiki: http://github.com/Araq/Nimrod/wiki.
IRC
====
Bug reports: http://github.com/Araq/Nimrod/issues.
Many Nimrod developers are a part of the
`#nimrod IRC channel <http://webchat.freenode.net/?channels=nimrod>`_ on
Freenode. That is the place where the rest of the discussion relating to Nimrod
occurs. Be sure to join us there if you wish to discuss Nimrod in real-time.
IRC is the perfect place for people just starting to learn Nimrod and we
welcome any questions that you may have!
For quickest feedback, join our IRC channel: irc://irc.freenode.net/nimrod
(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
``````

View File

@@ -3,86 +3,103 @@ News
====
2014-XX-XX Version 0.9.4 released
=================================
..
2014-XX-XX Version 0.9.4 released
=================================
Bugfixes
--------
Bugfixes
--------
Library Additions
-----------------
Library Additions
-----------------
- Added ``macros.genSym`` builtin for AST generation.
- Added ``macros.newLit`` procs for easier AST generation.
- Added ``macros.genSym`` builtin for AST generation.
- Added ``macros.newLit`` procs for easier AST generation.
Changes affecting backwards compatibility
-----------------------------------------
Changes affecting backwards compatibility
-----------------------------------------
- The scoping rules for the ``if`` statement changed for better interaction
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.
- 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
------------------
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.
- 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
------------------
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.
- 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
------------------
Tools improvements
------------------
- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option
to activate.
- 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!

View File

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